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