xref: /haiku/src/system/boot/platform/efi/arch/x86/arch_start.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 
19 struct gdt_idt_descr {
20 	uint16  limit;
21 	void*   base;
22 } _PACKED;
23 
24 
25 extern "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
30 extern "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
34 extern void arch_mmu_init_gdt(gdt_idt_descr &bootGDTDescriptor);
35 
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 void
55 arch_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