xref: /haiku/src/system/boot/platform/efi/arch/x86_64/arch_start.cpp (revision 81a981f14a7e31e81575589a49de5385c353f39c)
1 /*
2  * Copyright 2014-2022 Haiku, Inc. All rights reserved.
3  * Copyright 2013-2014, Fredrik Holmqvist, fredrik.holmqvist@gmail.com.
4  * Copyright 2014, Henry Harrington, henry.harrington@gmail.com.
5  * All rights reserved.
6  * Distributed under the terms of the MIT License.
7  *
8  * Authors:
9  * 	Alexander von Gluck IV <kallisti5@unixzen.com>
10  */
11 
12 
13 #include <boot/platform.h>
14 #include <boot/stage2.h>
15 #include <boot/stdio.h>
16 
17 #include "mmu.h"
18 #include "serial.h"
19 #include "smp.h"
20 #include "efi_platform.h"
21 
22 
23 // From entry.S
24 extern "C" void arch_enter_kernel(uint64 pml4, uint64 entry_point,
25 	uint64 stackTop);
26 
27 // From arch_mmu.cpp
28 extern void arch_mmu_post_efi_setup(size_t memory_map_size,
29     efi_memory_descriptor *memory_map, size_t descriptor_size,
30     uint32_t descriptor_version);
31 
32 extern uint64_t arch_mmu_generate_post_efi_page_tables(size_t memory_map_size,
33     efi_memory_descriptor *memory_map, size_t descriptor_size,
34     uint32_t descriptor_version);
35 
36 
37 void
arch_convert_kernel_args(void)38 arch_convert_kernel_args(void)
39 {
40 	fix_address(gKernelArgs.ucode_data);
41 	fix_address(gKernelArgs.arch_args.apic);
42 	fix_address(gKernelArgs.arch_args.hpet);
43 }
44 
45 
46 static const char*
memory_region_type_str(int type)47 memory_region_type_str(int type)
48 {
49 	switch (type)	{
50 		case EfiReservedMemoryType:
51 			return "EfiReservedMemoryType";
52 		case EfiLoaderCode:
53 			return "EfiLoaderCode";
54 		case EfiLoaderData:
55 			return "EfiLoaderData";
56 		case EfiBootServicesCode:
57 			return "EfiBootServicesCode";
58 		case EfiBootServicesData:
59 			return "EfiBootServicesData";
60 		case EfiRuntimeServicesCode:
61 			return "EfiRuntimeServicesCode";
62 		case EfiRuntimeServicesData:
63 			return "EfiRuntimeServicesData";
64 		case EfiConventionalMemory:
65 			return "EfiConventionalMemory";
66 		case EfiUnusableMemory:
67 			return "EfiUnusableMemory";
68 		case EfiACPIReclaimMemory:
69 			return "EfiACPIReclaimMemory";
70 		case EfiACPIMemoryNVS:
71 			return "EfiACPIMemoryNVS";
72 		case EfiMemoryMappedIO:
73 			return "EfiMemoryMappedIO";
74 		case EfiMemoryMappedIOPortSpace:
75 			return "EfiMemoryMappedIOPortSpace";
76 		case EfiPalCode:
77 			return "EfiPalCode";
78 		case EfiPersistentMemory:
79 			return "EfiPersistentMemory";
80 		default:
81 			return "unknown";
82 	}
83 }
84 
85 
86 void
arch_start_kernel(addr_t kernelEntry)87 arch_start_kernel(addr_t kernelEntry)
88 {
89 	// Prepare to exit EFI boot services.
90 	// Read the memory map.
91 	// First call is to determine the buffer size.
92 	size_t memory_map_size = 0;
93 	efi_memory_descriptor dummy;
94 	size_t map_key;
95 	size_t descriptor_size;
96 	uint32_t descriptor_version;
97 	if (kBootServices->GetMemoryMap(&memory_map_size, &dummy, &map_key,
98 		&descriptor_size, &descriptor_version) != EFI_BUFFER_TOO_SMALL) {
99 		panic("Unable to determine size of system memory map");
100 	}
101 
102 	// Allocate a buffer twice as large as needed just in case it gets bigger
103 	// between calls to ExitBootServices.
104 	size_t actual_memory_map_size = memory_map_size * 2;
105 	efi_memory_descriptor *memory_map
106 		= (efi_memory_descriptor *)kernel_args_malloc(actual_memory_map_size);
107 
108 	if (memory_map == NULL)
109 		panic("Unable to allocate memory map.");
110 
111 	// Read (and print) the memory map.
112 	memory_map_size = actual_memory_map_size;
113 	if (kBootServices->GetMemoryMap(&memory_map_size, memory_map, &map_key,
114 		&descriptor_size, &descriptor_version) != EFI_SUCCESS) {
115 		panic("Unable to fetch system memory map.");
116 	}
117 
118 	addr_t addr = (addr_t)memory_map;
119 	dprintf("System provided memory map:\n");
120 	for (size_t i = 0; i < memory_map_size / descriptor_size; i++) {
121 		efi_memory_descriptor *entry
122 			= (efi_memory_descriptor *)(addr + i * descriptor_size);
123 		dprintf("  phys: 0x%08lx-0x%08lx, virt: 0x%08lx-0x%08lx, type: %s (%#x), attr: %#lx\n",
124 			entry->PhysicalStart,
125 			entry->PhysicalStart + entry->NumberOfPages * B_PAGE_SIZE,
126 			entry->VirtualStart,
127 			entry->VirtualStart + entry->NumberOfPages * B_PAGE_SIZE,
128 			memory_region_type_str(entry->Type), entry->Type,
129 			entry->Attribute);
130 	}
131 
132 	// Generate page tables for use after ExitBootServices.
133 	uint64_t final_pml4 = arch_mmu_generate_post_efi_page_tables(
134 		memory_map_size, memory_map, descriptor_size, descriptor_version);
135 
136 	// Attempt to fetch the memory map and exit boot services.
137 	// This needs to be done in a loop, as ExitBootServices can change the
138 	// memory map.
139 	// Even better: Only GetMemoryMap and ExitBootServices can be called after
140 	// the first call to ExitBootServices, as the firmware is permitted to
141 	// partially exit. This is why twice as much space was allocated for the
142 	// memory map, as it's impossible to allocate more now.
143 	// A changing memory map shouldn't affect the generated page tables, as
144 	// they only needed to know about the maximum address, not any specific
145 	// entry.
146 
147 	dprintf("Calling ExitBootServices. So long, EFI!\n");
148 	serial_disable();
149 
150 	while (true) {
151 		if (kBootServices->ExitBootServices(kImage, map_key) == EFI_SUCCESS) {
152 			// Disconnect from EFI serial_io / stdio services
153 			serial_kernel_handoff();
154 			dprintf("Unhooked from EFI serial services\n");
155 			break;
156 		}
157 
158 		memory_map_size = actual_memory_map_size;
159 		if (kBootServices->GetMemoryMap(&memory_map_size, memory_map, &map_key,
160 				&descriptor_size, &descriptor_version) != EFI_SUCCESS) {
161 			panic("Unable to fetch system memory map.");
162 		}
163 	}
164 
165 	// Update EFI, generate final kernel physical memory map, etc.
166 	arch_mmu_post_efi_setup(memory_map_size, memory_map,
167 		descriptor_size, descriptor_version);
168 
169 	// Re-init and activate serial in a horrific post-EFI landscape. Clowns roam the land freely.
170 	serial_init();
171 	serial_enable();
172 
173 	smp_boot_other_cpus(final_pml4, kernelEntry, (addr_t)&gKernelArgs);
174 
175 	// Enter the kernel!
176 	dprintf("arch_enter_kernel(final_pml4: 0x%08" PRIx64 ", kernelArgs: %p, "
177 		"kernelEntry: 0x%08" B_PRIxADDR ", sp: 0x%08" B_PRIx64 ")\n",
178 		final_pml4, &gKernelArgs, kernelEntry,
179 		gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size);
180 
181 	arch_enter_kernel(final_pml4, kernelEntry,
182 		gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size);
183 }
184