1 /* 2 * Copyright 2019-2020 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 14 15 #define ALIGN_MEMORY_MAP 4 16 17 18 extern "C" void arch_enter_kernel(uint32_t ttbr, struct kernel_args *kernelArgs, 19 addr_t kernelEntry, addr_t kernelStackTop); 20 21 // From arch_mmu.cpp 22 extern void arch_mmu_post_efi_setup(size_t memory_map_size, 23 efi_memory_descriptor *memory_map, size_t descriptor_size, 24 uint32_t descriptor_version); 25 26 extern uint32_t arch_mmu_generate_post_efi_page_tables(size_t memory_map_size, 27 efi_memory_descriptor *memory_map, size_t descriptor_size, 28 uint32_t descriptor_version); 29 30 31 static const char* 32 memory_region_type_str(int type) 33 { 34 switch (type) { 35 case EfiReservedMemoryType: 36 return "ReservedMemoryType"; 37 case EfiLoaderCode: 38 return "LoaderCode"; 39 case EfiLoaderData: 40 return "LoaderData"; 41 case EfiBootServicesCode: 42 return "BootServicesCode"; 43 case EfiBootServicesData: 44 return "BootServicesData"; 45 case EfiRuntimeServicesCode: 46 return "RuntimeServicesCode"; 47 case EfiRuntimeServicesData: 48 return "RuntimeServicesData"; 49 case EfiConventionalMemory: 50 return "ConventionalMemory"; 51 case EfiUnusableMemory: 52 return "UnusableMemory"; 53 case EfiACPIReclaimMemory: 54 return "ACPIReclaimMemory"; 55 case EfiACPIMemoryNVS: 56 return "ACPIMemoryNVS"; 57 case EfiMemoryMappedIO: 58 return "MMIO"; 59 case EfiMemoryMappedIOPortSpace: 60 return "MMIOPortSpace"; 61 case EfiPalCode: 62 return "PalCode"; 63 case EfiPersistentMemory: 64 return "PersistentMemory"; 65 default: 66 return "unknown"; 67 } 68 } 69 70 71 void 72 arch_start_kernel(addr_t kernelEntry) 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 memory_map_size = 0; 78 efi_memory_descriptor dummy; 79 efi_memory_descriptor *memory_map; 80 size_t map_key; 81 size_t descriptor_size; 82 uint32_t descriptor_version; 83 if (kBootServices->GetMemoryMap(&memory_map_size, &dummy, &map_key, 84 &descriptor_size, &descriptor_version) != EFI_BUFFER_TOO_SMALL) { 85 panic("Unable to determine size of system memory map"); 86 } 87 88 // Allocate a buffer twice as large as needed just in case it gets bigger 89 // between calls to ExitBootServices. 90 size_t actual_memory_map_size = memory_map_size * 2; 91 memory_map 92 = (efi_memory_descriptor *)kernel_args_malloc(actual_memory_map_size + 93 ALIGN_MEMORY_MAP); 94 95 // align memory_map to 4-byte boundary 96 // otherwise we get alignment exception when calling GetMemoryMap below 97 memory_map = (efi_memory_descriptor *)ROUNDUP((uint32_t)memory_map, ALIGN_MEMORY_MAP); 98 99 if (memory_map == NULL) 100 panic("Unable to allocate memory map."); 101 102 // Read (and print) the memory map. 103 memory_map_size = actual_memory_map_size; 104 if (kBootServices->GetMemoryMap(&memory_map_size, memory_map, &map_key, 105 &descriptor_size, &descriptor_version) != EFI_SUCCESS) { 106 panic("Unable to fetch system memory map."); 107 } 108 109 addr_t addr = (addr_t)memory_map; 110 dprintf("System provided memory map:\n"); 111 for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) { 112 efi_memory_descriptor *entry 113 = (efi_memory_descriptor *)(addr + i * descriptor_size); 114 dprintf(" phys: 0x%08llx-0x%08llx, virt: 0x%08llx-0x%08llx, type: %s (%#x), attr: %#llx\n", 115 entry->PhysicalStart, 116 entry->PhysicalStart + entry->NumberOfPages * B_PAGE_SIZE, 117 entry->VirtualStart, 118 entry->VirtualStart + entry->NumberOfPages * B_PAGE_SIZE, 119 memory_region_type_str(entry->Type), entry->Type, 120 entry->Attribute); 121 } 122 123 // Generate page tables for use after ExitBootServices. 124 uint32_t final_ttbr0 = arch_mmu_generate_post_efi_page_tables( 125 memory_map_size, memory_map, descriptor_size, descriptor_version); 126 127 // Attempt to fetch the memory map and exit boot services. 128 // This needs to be done in a loop, as ExitBootServices can change the 129 // memory map. 130 // Even better: Only GetMemoryMap and ExitBootServices can be called after 131 // the first call to ExitBootServices, as the firmware is permitted to 132 // partially exit. This is why twice as much space was allocated for the 133 // memory map, as it's impossible to allocate more now. 134 // A changing memory map shouldn't affect the generated page tables, as 135 // they only needed to know about the maximum address, not any specific 136 // entry. 137 dprintf("Calling ExitBootServices. So long, EFI!\n"); 138 while (true) { 139 if (kBootServices->ExitBootServices(kImage, map_key) == EFI_SUCCESS) { 140 // The console was provided by boot services, disable it. 141 stdout = NULL; 142 stderr = NULL; 143 // Can we adjust gKernelArgs.platform_args.serial_base_ports[0] 144 // to something fixed in qemu for debugging? 145 break; 146 } 147 148 memory_map_size = actual_memory_map_size; 149 if (kBootServices->GetMemoryMap(&memory_map_size, memory_map, &map_key, 150 &descriptor_size, &descriptor_version) != EFI_SUCCESS) { 151 panic("Unable to fetch system memory map."); 152 } 153 } 154 155 // Update EFI, generate final kernel physical memory map, etc. 156 arch_mmu_post_efi_setup(memory_map_size, memory_map, 157 descriptor_size, descriptor_version); 158 159 //smp_boot_other_cpus(final_pml4, kernelEntry); 160 161 // Enter the kernel! 162 dprintf("arch_enter_kernel(ttbr0: 0x%08x, kernelArgs: 0x%08x, " 163 "kernelEntry: 0x%08x, sp: 0x%08x)\n", 164 final_ttbr0, (uint32_t)&gKernelArgs, (uint32_t)&kernelEntry, 165 (uint32_t)(gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size)); 166 167 arch_enter_kernel(final_ttbr0, &gKernelArgs, kernelEntry, 168 gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size); 169 } 170