1 /* 2 * Copyright 2019-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 "mmu.h" 14 #include "smp.h" 15 16 17 #define ALIGN_MEMORY_MAP 4 18 19 20 extern "C" typedef void (*arch_enter_kernel_t)(uint32_t, addr_t, addr_t, addr_t); 21 22 23 // From entry.S 24 extern "C" void arch_enter_kernel(uint32_t ttbr, addr_t kernelArgs, 25 addr_t kernelEntry, addr_t kernelStackTop); 26 27 // From arch_mmu.cpp 28 extern void arch_mmu_post_efi_setup(size_t memoryMapSize, 29 efi_memory_descriptor *memoryMap, size_t descriptorSize, 30 uint32_t descriptorVersion); 31 32 extern uint32_t arch_mmu_generate_post_efi_page_tables(size_t memoryMapSize, 33 efi_memory_descriptor *memoryMap, size_t descriptorSize, 34 uint32_t descriptorVersion); 35 36 37 void 38 arch_convert_kernel_args(void) 39 { 40 fix_address(gKernelArgs.arch_args.fdt); 41 } 42 43 44 static const char* 45 memory_region_type_str(int type) 46 { 47 switch (type) { 48 case EfiReservedMemoryType: 49 return "ReservedMemoryType"; 50 case EfiLoaderCode: 51 return "LoaderCode"; 52 case EfiLoaderData: 53 return "LoaderData"; 54 case EfiBootServicesCode: 55 return "BootServicesCode"; 56 case EfiBootServicesData: 57 return "BootServicesData"; 58 case EfiRuntimeServicesCode: 59 return "RuntimeServicesCode"; 60 case EfiRuntimeServicesData: 61 return "RuntimeServicesData"; 62 case EfiConventionalMemory: 63 return "ConventionalMemory"; 64 case EfiUnusableMemory: 65 return "UnusableMemory"; 66 case EfiACPIReclaimMemory: 67 return "ACPIReclaimMemory"; 68 case EfiACPIMemoryNVS: 69 return "ACPIMemoryNVS"; 70 case EfiMemoryMappedIO: 71 return "MMIO"; 72 case EfiMemoryMappedIOPortSpace: 73 return "MMIOPortSpace"; 74 case EfiPalCode: 75 return "PalCode"; 76 case EfiPersistentMemory: 77 return "PersistentMemory"; 78 default: 79 return "unknown"; 80 } 81 } 82 83 84 static void * 85 allocate_trampoline_page(void) 86 { 87 void *trampolinePage = NULL; 88 if (platform_allocate_lomem(&trampolinePage, B_PAGE_SIZE) == B_OK) 89 return trampolinePage; 90 91 trampolinePage = (void *)get_next_virtual_address(B_PAGE_SIZE); 92 if (platform_allocate_region(&trampolinePage, B_PAGE_SIZE, 0, true) == B_OK) 93 return trampolinePage; 94 95 trampolinePage = NULL; 96 if (platform_allocate_region(&trampolinePage, B_PAGE_SIZE, 0, false) != B_OK) 97 return NULL; 98 99 if (platform_free_region(trampolinePage, B_PAGE_SIZE) != B_OK) 100 return NULL; 101 102 if (platform_allocate_region(&trampolinePage, B_PAGE_SIZE, 0, true) != B_OK) 103 return NULL; 104 105 ASSERT_ALWAYS((uint32_t)trampolinePage >= 0x88000000); 106 return trampolinePage; 107 } 108 109 110 void 111 arch_start_kernel(addr_t kernelEntry) 112 { 113 // Allocate virtual memory for kernel args 114 struct kernel_args *kernelArgs = NULL; 115 if (platform_allocate_region((void **)&kernelArgs, 116 sizeof(struct kernel_args), 0, false) != B_OK) 117 panic("Failed to allocate kernel args."); 118 119 addr_t virtKernelArgs; 120 platform_bootloader_address_to_kernel_address((void*)kernelArgs, 121 &virtKernelArgs); 122 123 // Allocate identity mapped region for entry.S trampoline 124 void *trampolinePage = allocate_trampoline_page(); 125 if (trampolinePage == NULL) 126 panic("Failed to allocate trampoline page."); 127 128 memcpy(trampolinePage, (void *)arch_enter_kernel, B_PAGE_SIZE); 129 arch_enter_kernel_t enter_kernel = (arch_enter_kernel_t)trampolinePage; 130 131 // Prepare to exit EFI boot services. 132 // Read the memory map. 133 // First call is to determine the buffer size. 134 size_t memoryMapSize = 0; 135 efi_memory_descriptor dummy; 136 size_t mapKey; 137 size_t descriptorSize; 138 uint32_t descriptorVersion; 139 if (kBootServices->GetMemoryMap(&memoryMapSize, &dummy, &mapKey, 140 &descriptorSize, &descriptorVersion) != EFI_BUFFER_TOO_SMALL) { 141 panic("Unable to determine size of system memory map"); 142 } 143 144 // Allocate a buffer twice as large as needed just in case it gets bigger 145 // between calls to ExitBootServices. 146 size_t actualMemoryMapSize = memoryMapSize * 2; 147 efi_memory_descriptor *memoryMap 148 = (efi_memory_descriptor *)kernel_args_malloc(actualMemoryMapSize + 149 ALIGN_MEMORY_MAP); 150 151 // align memory_map to 4-byte boundary 152 // otherwise we get alignment exception when calling GetMemoryMap below 153 memoryMap = (efi_memory_descriptor *)ROUNDUP((uint32_t)memoryMap, ALIGN_MEMORY_MAP); 154 155 if (memoryMap == NULL) 156 panic("Unable to allocate memory map."); 157 158 // Read (and print) the memory map. 159 memoryMapSize = actualMemoryMapSize; 160 if (kBootServices->GetMemoryMap(&memoryMapSize, memoryMap, &mapKey, 161 &descriptorSize, &descriptorVersion) != EFI_SUCCESS) { 162 panic("Unable to fetch system memory map."); 163 } 164 165 addr_t addr = (addr_t)memoryMap; 166 dprintf("System provided memory map:\n"); 167 for (size_t i = 0; i < memoryMapSize / descriptorSize; ++i) { 168 efi_memory_descriptor *entry 169 = (efi_memory_descriptor *)(addr + i * descriptorSize); 170 dprintf(" phys: 0x%08" PRIx64 "-0x%08" PRIx64 171 ", virt: 0x%08" PRIx64 "-0x%08" PRIx64 172 ", type: %s (%#x), attr: %#" PRIx64 "\n", 173 entry->PhysicalStart, 174 entry->PhysicalStart + entry->NumberOfPages * B_PAGE_SIZE, 175 entry->VirtualStart, 176 entry->VirtualStart + entry->NumberOfPages * B_PAGE_SIZE, 177 memory_region_type_str(entry->Type), entry->Type, 178 entry->Attribute); 179 } 180 181 // Generate page tables for use after ExitBootServices. 182 uint32_t final_ttbr0 = arch_mmu_generate_post_efi_page_tables( 183 memoryMapSize, memoryMap, descriptorSize, descriptorVersion); 184 185 // Attempt to fetch the memory map and exit boot services. 186 // This needs to be done in a loop, as ExitBootServices can change the 187 // memory map. 188 // Even better: Only GetMemoryMap and ExitBootServices can be called after 189 // the first call to ExitBootServices, as the firmware is permitted to 190 // partially exit. This is why twice as much space was allocated for the 191 // memory map, as it's impossible to allocate more now. 192 // A changing memory map shouldn't affect the generated page tables, as 193 // they only needed to know about the maximum address, not any specific 194 // entry. 195 dprintf("Calling ExitBootServices. So long, EFI!\n"); 196 while (true) { 197 if (kBootServices->ExitBootServices(kImage, mapKey) == EFI_SUCCESS) { 198 // The console was provided by boot services, disable it. 199 stdout = NULL; 200 stderr = NULL; 201 // Can we adjust gKernelArgs.platform_args.serial_base_ports[0] 202 // to something fixed in qemu for debugging? 203 break; 204 } 205 206 memoryMapSize = actualMemoryMapSize; 207 if (kBootServices->GetMemoryMap(&memoryMapSize, memoryMap, &mapKey, 208 &descriptorSize, &descriptorVersion) != EFI_SUCCESS) { 209 panic("Unable to fetch system memory map."); 210 } 211 } 212 213 // Update EFI, generate final kernel physical memory map, etc. 214 arch_mmu_post_efi_setup(memoryMapSize, memoryMap, 215 descriptorSize, descriptorVersion); 216 217 // Copy final kernel args 218 // This should be the last step before jumping to the kernel 219 // as there are some fixups happening to kernel_args even in the last minute 220 memcpy(kernelArgs, &gKernelArgs, sizeof(struct kernel_args)); 221 222 //smp_boot_other_cpus(final_ttbr0, kernelEntry); 223 224 // Enter the kernel! 225 dprintf("enter_kernel(ttbr0: 0x%08x, kernelArgs: 0x%08x, " 226 "kernelEntry: 0x%08x, sp: 0x%08x)\n", 227 final_ttbr0, (uint32_t)virtKernelArgs, (uint32_t)kernelEntry, 228 (uint32_t)(gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size)); 229 230 enter_kernel(final_ttbr0, virtKernelArgs, kernelEntry, 231 gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size); 232 } 233