1 /* 2 * Copyright 2021-2022 Haiku, Inc. All rights reserved. 3 * Released under the terms of the MIT License. 4 */ 5 6 7 #include <algorithm> 8 9 #include <kernel.h> 10 #include <arch_kernel.h> 11 #include <arch/cpu.h> 12 #include <arch/x86/descriptors.h> 13 #include <boot/platform.h> 14 #include <boot/stage2.h> 15 #include <efi/types.h> 16 #include <efi/boot-services.h> 17 18 #include "efi_platform.h" 19 #include "generic_mmu.h" 20 #include "mmu.h" 21 22 23 //#define TRACE_MMU 24 #ifdef TRACE_MMU 25 # define TRACE(x...) dprintf(x) 26 #else 27 # define TRACE(x...) ; 28 #endif 29 30 31 //#define TRACE_MEMORY_MAP 32 //#define TRACE_PAGE_DIRECTORY 33 34 // Ignore memory below 1M and above 64GB (maximum amount of physical memory on x86 with PAE) 35 #define PHYSICAL_MEMORY_LOW 0x00100000 36 #define PHYSICAL_MEMORY_HIGH 0x1000000000ull 37 38 #define VADDR_TO_PDENT(va) (((va) / B_PAGE_SIZE) / 1024) 39 #define VADDR_TO_PTENT(va) (((va) / B_PAGE_SIZE) % 1024) 40 #define X86_PDE_ADDRESS_MASK 0xfffff000 41 #define X86_PTE_ADDRESS_MASK 0xfffff000 42 43 #define ALIGN_PAGEDIR B_PAGE_SIZE 44 45 46 struct gdt_idt_descr { 47 uint16_t limit; 48 uint32_t base; 49 } _PACKED; 50 51 52 static const uint32_t kDefaultPageTableFlags = 0x07; // present, user, R/W 53 54 55 static uint32_t *sPageDirectory = NULL; 56 57 58 #ifdef TRACE_PAGE_DIRECTORY 59 static void 60 dump_page_dir(void) 61 { 62 dprintf("=== Page Directory ===\n"); 63 for (uint32_t i = 0; i < 1024; i++) { 64 uint32_t directoryEntry = sPageDirectory[i]; 65 if (directoryEntry != 0) { 66 dprintf("virt 0x%08x --> page table 0x%08x type 0x%08x\n", 67 i << 22, directoryEntry & X86_PDE_ADDRESS_MASK, 68 directoryEntry & (~X86_PDE_ADDRESS_MASK)); 69 uint32_t *pageTable = (uint32_t *)(directoryEntry & X86_PDE_ADDRESS_MASK); 70 for (uint32_t j = 0; j < 1024; j++) { 71 uint32_t tableEntry = pageTable[j]; 72 if (tableEntry != 0) { 73 dprintf("virt 0x%08x --> page 0x%08x type+flags 0x%08x\n", 74 (i << 22) | (j << 12), 75 tableEntry & X86_PTE_ADDRESS_MASK, 76 tableEntry & (~X86_PTE_ADDRESS_MASK)); 77 } 78 } 79 } 80 } 81 } 82 #endif 83 84 85 static uint32_t * 86 get_next_page_table(void) 87 { 88 uint32_t *pageTable = (uint32_t *)mmu_allocate_page(); 89 memset(pageTable, 0, B_PAGE_SIZE); 90 return pageTable; 91 } 92 93 94 void 95 arch_mmu_init_gdt(gdt_idt_descr &bootGDTDescriptor) 96 { 97 segment_descriptor *bootGDT = NULL; 98 99 if (platform_allocate_region((void **)&bootGDT, 100 BOOT_GDT_SEGMENT_COUNT * sizeof(segment_descriptor), 0, false) != B_OK) { 101 panic("Failed to allocate GDT.\n"); 102 } 103 104 STATIC_ASSERT(BOOT_GDT_SEGMENT_COUNT > KERNEL_CODE_SEGMENT 105 && BOOT_GDT_SEGMENT_COUNT > KERNEL_DATA_SEGMENT 106 && BOOT_GDT_SEGMENT_COUNT > USER_CODE_SEGMENT 107 && BOOT_GDT_SEGMENT_COUNT > USER_DATA_SEGMENT); 108 109 // set up a new gdt 110 111 // put standard segment descriptors in GDT 112 clear_segment_descriptor(&bootGDT[0]); 113 114 // seg 0x08 - kernel 4GB code 115 set_segment_descriptor(&bootGDT[KERNEL_CODE_SEGMENT], 0, 0xffffffff, 116 DT_CODE_READABLE, DPL_KERNEL); 117 118 // seg 0x10 - kernel 4GB data 119 set_segment_descriptor(&bootGDT[KERNEL_DATA_SEGMENT], 0, 0xffffffff, 120 DT_DATA_WRITEABLE, DPL_KERNEL); 121 122 // seg 0x1b - ring 3 user 4GB code 123 set_segment_descriptor(&bootGDT[USER_CODE_SEGMENT], 0, 0xffffffff, 124 DT_CODE_READABLE, DPL_USER); 125 126 // seg 0x23 - ring 3 user 4GB data 127 set_segment_descriptor(&bootGDT[USER_DATA_SEGMENT], 0, 0xffffffff, 128 DT_DATA_WRITEABLE, DPL_USER); 129 130 addr_t virtualGDT; 131 platform_bootloader_address_to_kernel_address(bootGDT, &virtualGDT); 132 133 bootGDTDescriptor.limit = BOOT_GDT_SEGMENT_COUNT * sizeof(segment_descriptor); 134 bootGDTDescriptor.base = (uint32_t)virtualGDT; 135 136 TRACE("gdt phys 0x%08x virt 0x%08" B_PRIxADDR " desc 0x%08x\n", 137 (uint32_t)bootGDT, virtualGDT, 138 (uint32_t)&gBootGDTDescriptor); 139 TRACE("gdt limit=%d base=0x%08x\n", 140 bootGDTDescriptor.limit, bootGDTDescriptor.base); 141 } 142 143 144 static void 145 map_page(addr_t virtAddr, phys_addr_t physAddr, uint32_t flags) 146 { 147 physAddr &= ~(B_PAGE_SIZE - 1); 148 149 uint32_t *pageTable = NULL; 150 uint32_t pageDirectoryIndex = VADDR_TO_PDENT(virtAddr); 151 uint32_t pageDirectoryEntry = sPageDirectory[pageDirectoryIndex]; 152 153 if (pageDirectoryEntry == 0) { 154 //TRACE("get next page table for address 0x%08" B_PRIxADDR "\n", 155 // virtAddr); 156 pageTable = get_next_page_table(); 157 sPageDirectory[pageDirectoryIndex] = (uint32_t)pageTable | kDefaultPageTableFlags; 158 } else { 159 pageTable = (uint32_t *)(pageDirectoryEntry & X86_PDE_ADDRESS_MASK); 160 } 161 162 uint32_t pageTableIndex = VADDR_TO_PTENT(virtAddr); 163 pageTable[pageTableIndex] = physAddr | flags; 164 } 165 166 167 static void 168 map_range(addr_t virtAddr, phys_addr_t physAddr, size_t size, uint32_t flags) 169 { 170 //TRACE("map 0x%08" B_PRIxADDR " --> 0x%08" B_PRIxPHYSADDR 171 // ", len=0x%08" B_PRIxSIZE ", flags=0x%08" PRIx32 "\n", 172 // virtAddr, physAddr, size, flags); 173 174 for (addr_t offset = 0; offset < size; offset += B_PAGE_SIZE) { 175 map_page(virtAddr + offset, physAddr + offset, flags); 176 } 177 178 if (virtAddr >= KERNEL_LOAD_BASE) 179 ASSERT_ALWAYS(insert_virtual_allocated_range(virtAddr, size) >= B_OK); 180 } 181 182 183 void 184 arch_mmu_post_efi_setup(size_t memoryMapSize, 185 efi_memory_descriptor *memoryMap, size_t descriptorSize, 186 uint32_t descriptorVersion) 187 { 188 build_physical_allocated_list(memoryMapSize, memoryMap, 189 descriptorSize, descriptorVersion); 190 191 // Switch EFI to virtual mode, using the kernel pmap. 192 kRuntimeServices->SetVirtualAddressMap(memoryMapSize, descriptorSize, 193 descriptorVersion, memoryMap); 194 195 #ifdef TRACE_MEMORY_MAP 196 dprintf("phys memory ranges:\n"); 197 for (uint32_t i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) { 198 uint64 start = gKernelArgs.physical_memory_range[i].start; 199 uint64 size = gKernelArgs.physical_memory_range[i].size; 200 dprintf(" 0x%08" B_PRIx64 "-0x%08" B_PRIx64 ", length 0x%08" B_PRIx64 "\n", 201 start, start + size, size); 202 } 203 204 dprintf("allocated phys memory ranges:\n"); 205 for (uint32_t i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) { 206 uint64 start = gKernelArgs.physical_allocated_range[i].start; 207 uint64 size = gKernelArgs.physical_allocated_range[i].size; 208 dprintf(" 0x%08" B_PRIx64 "-0x%08" B_PRIx64 ", length 0x%08" B_PRIx64 "\n", 209 start, start + size, size); 210 } 211 212 dprintf("allocated virt memory ranges:\n"); 213 for (uint32_t i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) { 214 uint64 start = gKernelArgs.virtual_allocated_range[i].start; 215 uint64 size = gKernelArgs.virtual_allocated_range[i].size; 216 dprintf(" 0x%08" B_PRIx64 "-0x%08" B_PRIx64 ", length 0x%08" B_PRIx64 "\n", 217 start, start + size, size); 218 } 219 #endif 220 } 221 222 223 static void 224 arch_mmu_allocate_page_directory(void) 225 { 226 if (platform_allocate_region((void **)&sPageDirectory, 227 B_PAGE_SIZE + ALIGN_PAGEDIR, 0, false) != B_OK) 228 panic("Failed to allocate page directory."); 229 sPageDirectory = (uint32_t *)ROUNDUP((uint32_t)sPageDirectory, ALIGN_PAGEDIR); 230 memset(sPageDirectory, 0, B_PAGE_SIZE); 231 232 TRACE("sPageDirectory = 0x%08x\n", (uint32_t)sPageDirectory); 233 } 234 235 236 uint32_t 237 arch_mmu_generate_post_efi_page_tables(size_t memoryMapSize, 238 efi_memory_descriptor *memoryMap, size_t descriptorSize, 239 uint32_t descriptorVersion) 240 { 241 build_physical_memory_list(memoryMapSize, memoryMap, 242 descriptorSize, descriptorVersion, 243 PHYSICAL_MEMORY_LOW, PHYSICAL_MEMORY_HIGH); 244 245 //TODO: find out how to map EFI runtime services 246 //they are not mapped for now because the kernel doesn't use them anyway 247 #if 0 248 addr_t memoryMapAddr = (addr_t)memoryMap; 249 for (size_t i = 0; i < memoryMapSize / descriptorSize; ++i) { 250 efi_memory_descriptor* entry = 251 (efi_memory_descriptor *)(memoryMapAddr + i * descriptorSize); 252 if ((entry->Attribute & EFI_MEMORY_RUNTIME) != 0) 253 map_range(entry->VirtualStart, entry->PhysicalStart, 254 entry->NumberOfPages * B_PAGE_SIZE, 255 kDefaultPageFlags); 256 } 257 #endif 258 259 void* cookie = NULL; 260 addr_t vaddr; 261 phys_addr_t paddr; 262 size_t size; 263 while (mmu_next_region(&cookie, &vaddr, &paddr, &size)) { 264 map_range(vaddr, paddr, size, 265 kDefaultPageFlags); 266 } 267 268 // identity mapping for first 1MB 269 map_range((addr_t)0, (phys_addr_t)0, 1024*1024, kDefaultPageFlags); 270 271 sort_address_ranges(gKernelArgs.virtual_allocated_range, 272 gKernelArgs.num_virtual_allocated_ranges); 273 274 // Map the page directory into kernel space at 0xffc00000-0xffffffff 275 // this enables a mmu trick where the 4 MB region that this pgdir entry 276 // represents now maps the 4MB of potential pagetables that the pgdir 277 // points to. Thrown away later in VM bringup, but useful for now. 278 sPageDirectory[1023] = (uint32_t)sPageDirectory | kDefaultPageFlags; 279 280 addr_t virtPageDirectory; 281 platform_bootloader_address_to_kernel_address((void*)sPageDirectory, &virtPageDirectory); 282 283 gKernelArgs.arch_args.phys_pgdir = (uint32_t)sPageDirectory; 284 gKernelArgs.arch_args.vir_pgdir = (uint32_t)virtPageDirectory; 285 gKernelArgs.arch_args.page_hole = 0xffc00000; 286 gKernelArgs.arch_args.virtual_end 287 = gKernelArgs.virtual_allocated_range[gKernelArgs.num_virtual_allocated_ranges-1].start 288 + gKernelArgs.virtual_allocated_range[gKernelArgs.num_virtual_allocated_ranges-1].size; 289 290 TRACE("gKernelArgs.arch_args.phys_pgdir = 0x%08" B_PRIx32 "\n", 291 gKernelArgs.arch_args.phys_pgdir); 292 TRACE("gKernelArgs.arch_args.vir_pgdir = 0x%08" B_PRIx64 "\n", 293 gKernelArgs.arch_args.vir_pgdir); 294 TRACE("gKernelArgs.arch_args.page_hole = 0x%08" B_PRIx64 "\n", 295 gKernelArgs.arch_args.page_hole); 296 TRACE("gKernelArgs.arch_args.virtual_end = 0x%08" B_PRIx64 "\n", 297 gKernelArgs.arch_args.virtual_end); 298 299 #ifdef TRACE_PAGE_DIRECTORY 300 dump_page_dir(); 301 #endif 302 303 return (uint32_t)sPageDirectory; 304 } 305 306 307 void 308 arch_mmu_init(void) 309 { 310 arch_mmu_allocate_page_directory(); 311 } 312