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
arch_convert_kernel_args(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
arch_start_kernel(addr_t kernelEntry)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