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