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