1/*
2 * Copyright 2021-2022 Haiku, Inc. All rights reserved.
3 * Released under the terms of the MIT License.
4 */
5
6
7#include <kernel.h>
8#include <boot/platform.h>
9#include <boot/stage2.h>
10#include <boot/stdio.h>
11
12#include "efi_platform.h"
13#include "generic_mmu.h"
14#include "mmu.h"
15#include "serial.h"
16#include "smp.h"
17
18
19struct gdt_idt_descr {
20	uint16  limit;
21	void*   base;
22} _PACKED;
23
24
25extern "C" typedef void (*enter_kernel_t)(uint32_t, addr_t, addr_t, addr_t,
26	struct gdt_idt_descr *);
27
28
29// From entry.S
30extern "C" void arch_enter_kernel(uint32_t pageDirectory, addr_t kernelArgs,
31	addr_t kernelEntry, addr_t kernelStackTop, struct gdt_idt_descr *gdtDescriptor);
32
33// From arch_mmu.cpp
34extern void arch_mmu_init_gdt(gdt_idt_descr &bootGDTDescriptor);
35
36extern void arch_mmu_post_efi_setup(size_t memoryMapSize,
37	efi_memory_descriptor *memoryMap, size_t descriptorSize,
38	uint32_t descriptorVersion);
39
40extern uint32_t arch_mmu_generate_post_efi_page_tables(size_t memoryMapSize,
41	efi_memory_descriptor *memoryMap, size_t descriptorSize,
42	uint32_t descriptorVersion);
43
44
45void
46arch_convert_kernel_args(void)
47{
48	fix_address(gKernelArgs.ucode_data);
49	fix_address(gKernelArgs.arch_args.apic);
50	fix_address(gKernelArgs.arch_args.hpet);
51}
52
53
54void
55arch_start_kernel(addr_t kernelEntry)
56{
57	gdt_idt_descr bootGDTDescriptor;
58	arch_mmu_init_gdt(bootGDTDescriptor);
59
60	// Copy entry.S trampoline to lower 1M
61	enter_kernel_t enter_kernel = (enter_kernel_t)0xa000;
62	memcpy((void *)enter_kernel, (void *)arch_enter_kernel, B_PAGE_SIZE);
63
64	// Allocate virtual memory for kernel args
65	struct kernel_args *kernelArgs = NULL;
66	if (platform_allocate_region((void **)&kernelArgs,
67			sizeof(struct kernel_args), 0, false) != B_OK)
68		panic("Failed to allocate kernel args.");
69
70	addr_t virtKernelArgs;
71	platform_bootloader_address_to_kernel_address((void*)kernelArgs,
72		&virtKernelArgs);
73
74	// Prepare to exit EFI boot services.
75	// Read the memory map.
76	// First call is to determine the buffer size.
77	size_t memoryMapSize = 0;
78	efi_memory_descriptor dummy;
79	size_t mapKey;
80	size_t descriptorSize;
81	uint32_t descriptorVersion;
82	if (kBootServices->GetMemoryMap(&memoryMapSize, &dummy, &mapKey,
83			&descriptorSize, &descriptorVersion) != EFI_BUFFER_TOO_SMALL) {
84		panic("Unable to determine size of system memory map");
85	}
86
87	// Allocate a buffer twice as large as needed just in case it gets bigger
88	// between calls to ExitBootServices.
89	size_t actualMemoryMapSize = memoryMapSize * 2;
90	efi_memory_descriptor *memoryMap
91		= (efi_memory_descriptor *)kernel_args_malloc(actualMemoryMapSize);
92
93	if (memoryMap == NULL)
94		panic("Unable to allocate memory map.");
95
96	// Read (and print) the memory map.
97	memoryMapSize = actualMemoryMapSize;
98	if (kBootServices->GetMemoryMap(&memoryMapSize, memoryMap, &mapKey,
99			&descriptorSize, &descriptorVersion) != EFI_SUCCESS) {
100		panic("Unable to fetch system memory map.");
101	}
102
103	addr_t addr = (addr_t)memoryMap;
104	dprintf("System provided memory map:\n");
105	for (size_t i = 0; i < memoryMapSize / descriptorSize; i++) {
106		efi_memory_descriptor *entry
107			= (efi_memory_descriptor *)(addr + i * descriptorSize);
108		dprintf("  phys: 0x%08" PRIx64 "-0x%08" PRIx64
109			", virt: 0x%08" PRIx64 "-0x%08" PRIx64
110			", type: %s (%#x), attr: %#" PRIx64 "\n",
111			entry->PhysicalStart,
112			entry->PhysicalStart + entry->NumberOfPages * B_PAGE_SIZE,
113			entry->VirtualStart,
114			entry->VirtualStart + entry->NumberOfPages * B_PAGE_SIZE,
115			memory_region_type_str(entry->Type), entry->Type,
116			entry->Attribute);
117	}
118
119	// Generate page tables for use after ExitBootServices.
120	uint32_t pageDirectory = arch_mmu_generate_post_efi_page_tables(
121		memoryMapSize, memoryMap, descriptorSize, descriptorVersion);
122
123	// Attempt to fetch the memory map and exit boot services.
124	// This needs to be done in a loop, as ExitBootServices can change the
125	// memory map.
126	// Even better: Only GetMemoryMap and ExitBootServices can be called after
127	// the first call to ExitBootServices, as the firmware is permitted to
128	// partially exit. This is why twice as much space was allocated for the
129	// memory map, as it's impossible to allocate more now.
130	// A changing memory map shouldn't affect the generated page tables, as
131	// they only needed to know about the maximum address, not any specific
132	// entry.
133
134	dprintf("Calling ExitBootServices. So long, EFI!\n");
135	serial_disable();
136	while (true) {
137		if (kBootServices->ExitBootServices(kImage, mapKey) == EFI_SUCCESS) {
138			// Disconnect from EFI serial_io / stdio services
139			serial_kernel_handoff();
140			dprintf("Unhooked from EFI serial services\n");
141			break;
142		}
143
144		memoryMapSize = actualMemoryMapSize;
145		if (kBootServices->GetMemoryMap(&memoryMapSize, memoryMap, &mapKey,
146				&descriptorSize, &descriptorVersion) != EFI_SUCCESS) {
147			panic("Unable to fetch system memory map.");
148		}
149	}
150
151	// Update EFI, generate final kernel physical memory map, etc.
152	arch_mmu_post_efi_setup(memoryMapSize, memoryMap,
153		descriptorSize, descriptorVersion);
154
155	// Re-init and activate serial in a horrific post-EFI landscape. Clowns roam the land freely.
156	serial_init();
157	serial_enable();
158
159	// Copy final kernel args
160	// This should be the last step before jumping to the kernel
161	// as there are some fixups happening to kernel_args even in the last minute
162	memcpy(kernelArgs, &gKernelArgs, sizeof(struct kernel_args));
163
164	smp_boot_other_cpus(pageDirectory, kernelEntry, virtKernelArgs);
165
166	// Enter the kernel!
167	dprintf("enter_kernel(pageDirectory: 0x%08x, kernelArgs: 0x%08x, "
168		"kernelEntry: 0x%08x, sp: 0x%08x, bootGDTDescriptor: 0x%08x)\n",
169		pageDirectory, (uint32_t)virtKernelArgs, (uint32_t)kernelEntry,
170		(uint32_t)(gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size),
171		(uint32_t)&bootGDTDescriptor);
172
173	enter_kernel(pageDirectory, virtKernelArgs, kernelEntry,
174		gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size,
175		&bootGDTDescriptor);
176}
177