xref: /haiku/src/system/boot/platform/efi/arch/x86/arch_start.cpp (revision 830f67ef991407f287dbc1238aa5f5906d90c991)
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 "mmu.h"
14 #include "serial.h"
15 #include "smp.h"
16 
17 
18 struct gdt_idt_descr {
19 	uint16  limit;
20 	void*   base;
21 } _PACKED;
22 
23 
24 extern gdt_idt_descr gBootGDTDescriptor;
25 
26 
27 extern "C" typedef void (*enter_kernel_t)(uint32_t, addr_t, addr_t, addr_t,
28 	struct gdt_idt_descr *);
29 
30 
31 // From entry.S
32 extern "C" void arch_enter_kernel(uint32_t pageDirectory, addr_t kernelArgs,
33 	addr_t kernelEntry, addr_t kernelStackTop, struct gdt_idt_descr *gdtDescriptor);
34 
35 // From arch_mmu.cpp
36 extern void arch_mmu_post_efi_setup(size_t memoryMapSize,
37 	efi_memory_descriptor *memoryMap, size_t descriptorSize,
38 	uint32_t descriptorVersion);
39 
40 extern 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 
45 void
46 arch_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 
54 static const char*
55 memory_region_type_str(int type)
56 {
57 	switch (type)	{
58 		case EfiReservedMemoryType:
59 			return "ReservedMemoryType";
60 		case EfiLoaderCode:
61 			return "LoaderCode";
62 		case EfiLoaderData:
63 			return "LoaderData";
64 		case EfiBootServicesCode:
65 			return "BootServicesCode";
66 		case EfiBootServicesData:
67 			return "BootServicesData";
68 		case EfiRuntimeServicesCode:
69 			return "RuntimeServicesCode";
70 		case EfiRuntimeServicesData:
71 			return "RuntimeServicesData";
72 		case EfiConventionalMemory:
73 			return "ConventionalMemory";
74 		case EfiUnusableMemory:
75 			return "UnusableMemory";
76 		case EfiACPIReclaimMemory:
77 			return "ACPIReclaimMemory";
78 		case EfiACPIMemoryNVS:
79 			return "ACPIMemoryNVS";
80 		case EfiMemoryMappedIO:
81 			return "MMIO";
82 		case EfiMemoryMappedIOPortSpace:
83 			return "MMIOPortSpace";
84 		case EfiPalCode:
85 			return "PalCode";
86 		case EfiPersistentMemory:
87 			return "PersistentMemory";
88 		default:
89 			return "unknown";
90 	}
91 }
92 
93 
94 void
95 arch_start_kernel(addr_t kernelEntry)
96 {
97 	// Copy entry.S trampoline to lower 1M
98 	enter_kernel_t enter_kernel = (enter_kernel_t)0xa000;
99 	memcpy((void *)enter_kernel, (void *)arch_enter_kernel, B_PAGE_SIZE);
100 
101 	// Allocate virtual memory for kernel args
102 	struct kernel_args *kernelArgs = NULL;
103 	if (platform_allocate_region((void **)&kernelArgs,
104 			sizeof(struct kernel_args), 0, false) != B_OK)
105 		panic("Failed to allocate kernel args.");
106 
107 	addr_t virtKernelArgs;
108 	platform_bootloader_address_to_kernel_address((void*)kernelArgs,
109 		&virtKernelArgs);
110 
111 	// Prepare to exit EFI boot services.
112 	// Read the memory map.
113 	// First call is to determine the buffer size.
114 	size_t memoryMapSize = 0;
115 	efi_memory_descriptor dummy;
116 	size_t mapKey;
117 	size_t descriptorSize;
118 	uint32_t descriptorVersion;
119 	if (kBootServices->GetMemoryMap(&memoryMapSize, &dummy, &mapKey,
120 			&descriptorSize, &descriptorVersion) != EFI_BUFFER_TOO_SMALL) {
121 		panic("Unable to determine size of system memory map");
122 	}
123 
124 	// Allocate a buffer twice as large as needed just in case it gets bigger
125 	// between calls to ExitBootServices.
126 	size_t actualMemoryMapSize = memoryMapSize * 2;
127 	efi_memory_descriptor *memoryMap
128 		= (efi_memory_descriptor *)kernel_args_malloc(actualMemoryMapSize);
129 
130 	if (memoryMap == NULL)
131 		panic("Unable to allocate memory map.");
132 
133 	// Read (and print) the memory map.
134 	memoryMapSize = actualMemoryMapSize;
135 	if (kBootServices->GetMemoryMap(&memoryMapSize, memoryMap, &mapKey,
136 			&descriptorSize, &descriptorVersion) != EFI_SUCCESS) {
137 		panic("Unable to fetch system memory map.");
138 	}
139 
140 	addr_t addr = (addr_t)memoryMap;
141 	dprintf("System provided memory map:\n");
142 	for (size_t i = 0; i < memoryMapSize / descriptorSize; i++) {
143 		efi_memory_descriptor *entry
144 			= (efi_memory_descriptor *)(addr + i * descriptorSize);
145 		dprintf("  phys: 0x%08" PRIx64 "-0x%08" PRIx64
146 			", virt: 0x%08" PRIx64 "-0x%08" PRIx64
147 			", type: %s (%#x), attr: %#" PRIx64 "\n",
148 			entry->PhysicalStart,
149 			entry->PhysicalStart + entry->NumberOfPages * B_PAGE_SIZE,
150 			entry->VirtualStart,
151 			entry->VirtualStart + entry->NumberOfPages * B_PAGE_SIZE,
152 			memory_region_type_str(entry->Type), entry->Type,
153 			entry->Attribute);
154 	}
155 
156 	// Generate page tables for use after ExitBootServices.
157 	uint32_t pageDirectory = arch_mmu_generate_post_efi_page_tables(
158 		memoryMapSize, memoryMap, descriptorSize, descriptorVersion);
159 
160 	// Attempt to fetch the memory map and exit boot services.
161 	// This needs to be done in a loop, as ExitBootServices can change the
162 	// memory map.
163 	// Even better: Only GetMemoryMap and ExitBootServices can be called after
164 	// the first call to ExitBootServices, as the firmware is permitted to
165 	// partially exit. This is why twice as much space was allocated for the
166 	// memory map, as it's impossible to allocate more now.
167 	// A changing memory map shouldn't affect the generated page tables, as
168 	// they only needed to know about the maximum address, not any specific
169 	// entry.
170 	dprintf("Calling ExitBootServices. So long, EFI!\n");
171 	while (true) {
172 		if (kBootServices->ExitBootServices(kImage, mapKey) == EFI_SUCCESS) {
173 			// The console was provided by boot services, disable it.
174 			stdout = NULL;
175 			stderr = NULL;
176 			// Also switch to legacy serial output
177 			// (may not work on all systems)
178 			serial_switch_to_legacy();
179 			dprintf("Switched to legacy serial output\n");
180 			break;
181 		}
182 
183 		memoryMapSize = actualMemoryMapSize;
184 		if (kBootServices->GetMemoryMap(&memoryMapSize, memoryMap, &mapKey,
185 				&descriptorSize, &descriptorVersion) != EFI_SUCCESS) {
186 			panic("Unable to fetch system memory map.");
187 		}
188 	}
189 
190 	// Update EFI, generate final kernel physical memory map, etc.
191 	arch_mmu_post_efi_setup(memoryMapSize, memoryMap,
192 		descriptorSize, descriptorVersion);
193 
194 	// Copy final kernel args
195 	// This should be the last step before jumping to the kernel
196 	// as there are some fixups happening to kernel_args even in the last minute
197 	memcpy(kernelArgs, &gKernelArgs, sizeof(struct kernel_args));
198 
199 	smp_boot_other_cpus(pageDirectory, kernelEntry, virtKernelArgs);
200 
201 	// Enter the kernel!
202 	dprintf("enter_kernel(pageDirectory: 0x%08x, kernelArgs: 0x%08x, "
203 		"kernelEntry: 0x%08x, sp: 0x%08x, gBootGDTDescriptor: 0x%08x)\n",
204 		pageDirectory, (uint32_t)virtKernelArgs, (uint32_t)kernelEntry,
205 		(uint32_t)(gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size),
206 		(uint32_t)&gBootGDTDescriptor);
207 
208 	enter_kernel(pageDirectory, virtKernelArgs, kernelEntry,
209 		gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size,
210 		&gBootGDTDescriptor);
211 }
212