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 dprintf("Calling ExitBootServices. So long, EFI!\n"); 134 while (true) { 135 if (kBootServices->ExitBootServices(kImage, mapKey) == EFI_SUCCESS) { 136 // The console was provided by boot services, disable it. 137 stdout = NULL; 138 stderr = NULL; 139 // Also switch to legacy serial output 140 // (may not work on all systems) 141 serial_switch_to_legacy(); 142 dprintf("Switched to legacy serial output\n"); 143 break; 144 } 145 146 memoryMapSize = actualMemoryMapSize; 147 if (kBootServices->GetMemoryMap(&memoryMapSize, memoryMap, &mapKey, 148 &descriptorSize, &descriptorVersion) != EFI_SUCCESS) { 149 panic("Unable to fetch system memory map."); 150 } 151 } 152 153 // Update EFI, generate final kernel physical memory map, etc. 154 arch_mmu_post_efi_setup(memoryMapSize, memoryMap, 155 descriptorSize, descriptorVersion); 156 157 // Copy final kernel args 158 // This should be the last step before jumping to the kernel 159 // as there are some fixups happening to kernel_args even in the last minute 160 memcpy(kernelArgs, &gKernelArgs, sizeof(struct kernel_args)); 161 162 smp_boot_other_cpus(pageDirectory, kernelEntry, virtKernelArgs); 163 164 // Enter the kernel! 165 dprintf("enter_kernel(pageDirectory: 0x%08x, kernelArgs: 0x%08x, " 166 "kernelEntry: 0x%08x, sp: 0x%08x, bootGDTDescriptor: 0x%08x)\n", 167 pageDirectory, (uint32_t)virtKernelArgs, (uint32_t)kernelEntry, 168 (uint32_t)(gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size), 169 (uint32_t)&bootGDTDescriptor); 170 171 enter_kernel(pageDirectory, virtKernelArgs, kernelEntry, 172 gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size, 173 &bootGDTDescriptor); 174 } 175