1 /* 2 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. 3 * Based on code written by Travis Geiselbrecht for NewOS. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 #include "mmu.h" 10 11 #include <string.h> 12 13 #include <OS.h> 14 15 #include <arch/cpu.h> 16 #include <arch/x86/descriptors.h> 17 #include <arch_kernel.h> 18 #include <boot/platform.h> 19 #include <boot/stdio.h> 20 #include <boot/kernel_args.h> 21 #include <boot/stage2.h> 22 #include <kernel.h> 23 24 #include "bios.h" 25 #include "interrupts.h" 26 27 28 /*! The (physical) memory layout of the boot loader is currently as follows: 29 0x0500 - 0x10000 protected mode stack 30 0x0500 - 0x09000 real mode stack 31 0x10000 - ? code (up to ~500 kB) 32 0x90000 1st temporary page table (identity maps 0-4 MB) 33 0x91000 2nd (4-8 MB) 34 0x92000 - 0x92000 further page tables 35 0x9e000 - 0xa0000 SMP trampoline code 36 [0xa0000 - 0x100000 BIOS/ROM/reserved area] 37 0x100000 page directory 38 ... boot loader heap (32 kB) 39 ... free physical memory 40 41 The first 8 MB are identity mapped (0x0 - 0x0800000); paging is turned 42 on. The kernel is mapped at 0x80000000, all other stuff mapped by the 43 loader (kernel args, modules, driver settings, ...) comes after 44 0x80020000 which means that there is currently only 2 MB reserved for 45 the kernel itself (see kMaxKernelSize). 46 47 The layout in PXE mode differs a bit from this, see definitions below. 48 */ 49 50 //#define TRACE_MMU 51 #ifdef TRACE_MMU 52 # define TRACE(x...) dprintf(x) 53 #else 54 # define TRACE(x...) ; 55 #endif 56 57 58 //#define TRACE_MEMORY_MAP 59 // Define this to print the memory map to serial debug, 60 // You also need to define ENABLE_SERIAL in serial.cpp 61 // for output to work. 62 63 64 // memory structure returned by int 0x15, ax 0xe820 65 struct extended_memory { 66 uint64 base_addr; 67 uint64 length; 68 uint32 type; 69 }; 70 71 72 segment_descriptor gBootGDT[BOOT_GDT_SEGMENT_COUNT]; 73 74 static const uint32 kDefaultPageTableFlags = 0x07; // present, user, R/W 75 static const size_t kMaxKernelSize = 0x1000000; // 16 MB for the kernel 76 static const size_t kIdentityMapEnd = 0x0800000; // 8 MB 77 78 // working page directory and page table 79 static uint32 *sPageDirectory = 0; 80 81 #ifdef _PXE_ENV 82 83 static addr_t sNextPhysicalAddress = 0x112000; 84 static addr_t sNextPhysicalKernelAddress = kIdentityMapEnd; 85 static addr_t sNextVirtualAddress = KERNEL_LOAD_BASE + kMaxKernelSize; 86 87 static addr_t sNextPageTableAddress = 0x7d000; 88 static const uint32 kPageTableRegionEnd = 0x8b000; 89 // we need to reserve 2 pages for the SMP trampoline code 90 91 #else 92 93 static addr_t sNextPhysicalAddress = 0x100000; 94 static addr_t sNextPhysicalKernelAddress = kIdentityMapEnd; 95 static addr_t sNextVirtualAddress = KERNEL_LOAD_BASE + kMaxKernelSize; 96 97 static addr_t sNextPageTableAddress = 0x90000; 98 static const uint32 kPageTableRegionEnd = 0x9e000; 99 // we need to reserve 2 pages for the SMP trampoline code 100 101 #endif 102 103 104 static addr_t 105 allocate_virtual(size_t size) 106 { 107 addr_t address = sNextVirtualAddress; 108 sNextVirtualAddress += size; 109 110 return address; 111 } 112 113 114 static addr_t 115 allocate_physical(size_t size, bool forKernel) 116 { 117 uint64 base; 118 if (!forKernel) { 119 base = sNextPhysicalAddress; 120 if ((base + size) > kIdentityMapEnd) { 121 panic("Out of identity-map physical memory!"); 122 return 0; 123 } 124 125 sNextPhysicalAddress += size; 126 return base; 127 } 128 129 if (!get_free_address_range(gKernelArgs.physical_allocated_range, 130 gKernelArgs.num_physical_allocated_ranges, sNextPhysicalKernelAddress, 131 size, &base)) { 132 panic("Out of physical memory!"); 133 return 0; 134 } 135 136 insert_physical_allocated_range(base, size); 137 sNextPhysicalKernelAddress = base + size; 138 // TODO: Can overflow theoretically. 139 140 return base; 141 } 142 143 144 static addr_t 145 get_next_virtual_page() 146 { 147 return allocate_virtual(B_PAGE_SIZE); 148 } 149 150 151 static addr_t 152 get_next_physical_page() 153 { 154 return allocate_physical(B_PAGE_SIZE, true); 155 } 156 157 158 static uint32 * 159 get_next_page_table() 160 { 161 TRACE("get_next_page_table, sNextPageTableAddress %#" B_PRIxADDR 162 ", kPageTableRegionEnd %#" B_PRIxADDR "\n", sNextPageTableAddress, 163 kPageTableRegionEnd); 164 165 addr_t address = sNextPageTableAddress; 166 if (address >= kPageTableRegionEnd) 167 return (uint32 *)allocate_physical(B_PAGE_SIZE, false); 168 169 sNextPageTableAddress += B_PAGE_SIZE; 170 return (uint32 *)address; 171 } 172 173 174 /*! Adds a new page table for the specified base address */ 175 static uint32* 176 add_page_table(addr_t base) 177 { 178 if (gKernelArgs.arch_args.num_pgtables == MAX_BOOT_PTABLES) { 179 panic("gKernelArgs.arch_args.pgtables overflow"); 180 return NULL; 181 } 182 183 base = ROUNDDOWN(base, B_PAGE_SIZE * 1024); 184 185 // Get new page table and clear it out 186 uint32 *pageTable = get_next_page_table(); 187 if (pageTable > (uint32 *)kIdentityMapEnd) { 188 panic("tried to add page table beyond the identity mapped 8 MB " 189 "region\n"); 190 return NULL; 191 } 192 193 TRACE("add_page_table(base = %p), got page: %p\n", (void*)base, pageTable); 194 195 gKernelArgs.arch_args.pgtables[gKernelArgs.arch_args.num_pgtables++] 196 = (uint32)pageTable; 197 198 for (int32 i = 0; i < 1024; i++) 199 pageTable[i] = 0; 200 201 // put the new page table into the page directory 202 sPageDirectory[base / (4 * 1024 * 1024)] 203 = (uint32)pageTable | kDefaultPageTableFlags; 204 205 // update the virtual end address in the kernel args 206 base += B_PAGE_SIZE * 1024; 207 if (base > gKernelArgs.arch_args.virtual_end) 208 gKernelArgs.arch_args.virtual_end = base; 209 210 return pageTable; 211 } 212 213 214 static void 215 unmap_page(addr_t virtualAddress) 216 { 217 TRACE("unmap_page(virtualAddress = %p)\n", (void *)virtualAddress); 218 219 if (virtualAddress < KERNEL_LOAD_BASE) { 220 panic("unmap_page: asked to unmap invalid page %p!\n", 221 (void *)virtualAddress); 222 } 223 224 // unmap the page from the correct page table 225 uint32 *pageTable = (uint32 *)(sPageDirectory[virtualAddress 226 / (B_PAGE_SIZE * 1024)] & 0xfffff000); 227 pageTable[(virtualAddress % (B_PAGE_SIZE * 1024)) / B_PAGE_SIZE] = 0; 228 229 asm volatile("invlpg (%0)" : : "r" (virtualAddress)); 230 } 231 232 233 /*! Creates an entry to map the specified virtualAddress to the given 234 physicalAddress. 235 If the mapping goes beyond the current page table, it will allocate 236 a new one. If it cannot map the requested page, it panics. 237 */ 238 static void 239 map_page(addr_t virtualAddress, addr_t physicalAddress, uint32 flags) 240 { 241 TRACE("map_page: vaddr 0x%lx, paddr 0x%lx\n", virtualAddress, 242 physicalAddress); 243 244 if (virtualAddress < KERNEL_LOAD_BASE) { 245 panic("map_page: asked to map invalid page %p!\n", 246 (void *)virtualAddress); 247 } 248 249 uint32 *pageTable = (uint32 *)(sPageDirectory[virtualAddress 250 / (B_PAGE_SIZE * 1024)] & 0xfffff000); 251 252 if (pageTable == NULL) { 253 // we need to add a new page table 254 pageTable = add_page_table(virtualAddress); 255 256 if (pageTable == NULL) { 257 panic("map_page: failed to allocate a page table for virtual " 258 "address %p\n", (void*)virtualAddress); 259 return; 260 } 261 } 262 263 physicalAddress &= ~(B_PAGE_SIZE - 1); 264 265 // map the page to the correct page table 266 uint32 tableEntry = (virtualAddress % (B_PAGE_SIZE * 1024)) / B_PAGE_SIZE; 267 268 TRACE("map_page: inserting pageTable %p, tableEntry %" B_PRIu32 269 ", physicalAddress %#" B_PRIxADDR "\n", pageTable, tableEntry, 270 physicalAddress); 271 272 pageTable[tableEntry] = physicalAddress | flags; 273 274 asm volatile("invlpg (%0)" : : "r" (virtualAddress)); 275 276 TRACE("map_page: done\n"); 277 } 278 279 280 #ifdef TRACE_MEMORY_MAP 281 static const char * 282 e820_memory_type(uint32 type) 283 { 284 switch (type) { 285 case 1: return "memory"; 286 case 2: return "reserved"; 287 case 3: return "ACPI reclaim"; 288 case 4: return "ACPI NVS"; 289 default: return "unknown/reserved"; 290 } 291 } 292 #endif 293 294 295 static uint32 296 get_memory_map(extended_memory **_extendedMemory) 297 { 298 extended_memory *block = (extended_memory *)kExtraSegmentScratch; 299 bios_regs regs = {0, 0, sizeof(extended_memory), 0, 0, (uint32)block, 0, 0}; 300 uint32 count = 0; 301 302 TRACE("get_memory_map()\n"); 303 304 do { 305 regs.eax = 0xe820; 306 regs.edx = 'SMAP'; 307 308 call_bios(0x15, ®s); 309 if ((regs.flags & CARRY_FLAG) != 0) 310 return 0; 311 312 regs.edi += sizeof(extended_memory); 313 count++; 314 } while (regs.ebx != 0); 315 316 *_extendedMemory = block; 317 318 #ifdef TRACE_MEMORY_MAP 319 dprintf("extended memory info (from 0xe820):\n"); 320 for (uint32 i = 0; i < count; i++) { 321 dprintf(" base 0x%08Lx, len 0x%08Lx, type %lu (%s)\n", 322 block[i].base_addr, block[i].length, 323 block[i].type, e820_memory_type(block[i].type)); 324 } 325 #endif 326 327 return count; 328 } 329 330 331 static void 332 init_page_directory(void) 333 { 334 TRACE("init_page_directory\n"); 335 336 // allocate a new pgdir 337 sPageDirectory = (uint32 *)allocate_physical(B_PAGE_SIZE, false); 338 gKernelArgs.arch_args.phys_pgdir = (uint32)sPageDirectory; 339 340 // clear out the pgdir 341 for (int32 i = 0; i < 1024; i++) { 342 sPageDirectory[i] = 0; 343 } 344 345 // Identity map the first 8 MB of memory so that their 346 // physical and virtual address are the same. 347 // These page tables won't be taken over into the kernel. 348 349 // make the first page table at the first free spot 350 uint32 *pageTable = get_next_page_table(); 351 352 for (int32 i = 0; i < 1024; i++) { 353 pageTable[i] = (i * 0x1000) | kDefaultPageFlags; 354 } 355 356 sPageDirectory[0] = (uint32)pageTable | kDefaultPageFlags; 357 358 // make the second page table 359 pageTable = get_next_page_table(); 360 361 for (int32 i = 0; i < 1024; i++) { 362 pageTable[i] = (i * 0x1000 + 0x400000) | kDefaultPageFlags; 363 } 364 365 sPageDirectory[1] = (uint32)pageTable | kDefaultPageFlags; 366 367 gKernelArgs.arch_args.num_pgtables = 0; 368 369 // switch to the new pgdir and enable paging 370 asm("movl %0, %%eax;" 371 "movl %%eax, %%cr3;" : : "m" (sPageDirectory) : "eax"); 372 // Important. Make sure supervisor threads can fault on read only pages... 373 asm("movl %%eax, %%cr0" : : "a" ((1 << 31) | (1 << 16) | (1 << 5) | 1)); 374 } 375 376 377 // #pragma mark - 378 379 380 /*! 381 Neither \a virtualAddress nor \a size need to be aligned, but the function 382 will map all pages the range intersects with. 383 If physicalAddress is not page-aligned, the returned virtual address will 384 have the same "misalignment". 385 */ 386 extern "C" addr_t 387 mmu_map_physical_memory(addr_t physicalAddress, size_t size, uint32 flags) 388 { 389 addr_t address = sNextVirtualAddress; 390 addr_t pageOffset = physicalAddress & (B_PAGE_SIZE - 1); 391 392 physicalAddress -= pageOffset; 393 size += pageOffset; 394 395 for (addr_t offset = 0; offset < size; offset += B_PAGE_SIZE) { 396 map_page(get_next_virtual_page(), physicalAddress + offset, flags); 397 } 398 399 return address + pageOffset; 400 } 401 402 403 extern "C" void * 404 mmu_allocate(void *virtualAddress, size_t size) 405 { 406 TRACE("mmu_allocate: requested vaddr: %p, next free vaddr: 0x%lx, size: " 407 "%ld\n", virtualAddress, sNextVirtualAddress, size); 408 409 size = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE; 410 // get number of pages to map 411 412 if (virtualAddress != NULL) { 413 // This special path is almost only useful for loading the 414 // kernel into memory; it will only allow you to map the 415 // 'kMaxKernelSize' bytes following the kernel base address. 416 // Also, it won't check for already mapped addresses, so 417 // you better know why you are here :) 418 addr_t address = (addr_t)virtualAddress; 419 420 // is the address within the valid range? 421 if (address < KERNEL_LOAD_BASE || address + size * B_PAGE_SIZE 422 >= KERNEL_LOAD_BASE + kMaxKernelSize) 423 return NULL; 424 425 for (uint32 i = 0; i < size; i++) { 426 map_page(address, get_next_physical_page(), kDefaultPageFlags); 427 address += B_PAGE_SIZE; 428 } 429 430 return virtualAddress; 431 } 432 433 void *address = (void *)sNextVirtualAddress; 434 435 for (uint32 i = 0; i < size; i++) { 436 map_page(get_next_virtual_page(), get_next_physical_page(), 437 kDefaultPageFlags); 438 } 439 440 return address; 441 } 442 443 444 /*! Allocates a single page and returns both its virtual and physical 445 addresses. 446 */ 447 void * 448 mmu_allocate_page(addr_t *_physicalAddress) 449 { 450 addr_t virt = get_next_virtual_page(); 451 addr_t phys = get_next_physical_page(); 452 453 map_page(virt, phys, kDefaultPageFlags); 454 455 if (_physicalAddress) 456 *_physicalAddress = phys; 457 458 return (void *)virt; 459 } 460 461 462 /*! Allocates the given physical range. 463 \return \c true, if the range could be allocated, \c false otherwise. 464 */ 465 bool 466 mmu_allocate_physical(addr_t base, size_t size) 467 { 468 // check whether the physical memory range exists at all 469 if (!is_address_range_covered(gKernelArgs.physical_memory_range, 470 gKernelArgs.num_physical_memory_ranges, base, size)) { 471 return false; 472 } 473 474 // check whether the physical range is still free 475 uint64 foundBase; 476 if (!get_free_address_range(gKernelArgs.physical_allocated_range, 477 gKernelArgs.num_physical_allocated_ranges, base, size, &foundBase) 478 || foundBase != base) { 479 return false; 480 } 481 482 return insert_physical_allocated_range(base, size) == B_OK; 483 } 484 485 486 /*! This will unmap the allocated chunk of memory from the virtual 487 address space. It might not actually free memory (as its implementation 488 is very simple), but it might. 489 Neither \a virtualAddress nor \a size need to be aligned, but the function 490 will unmap all pages the range intersects with. 491 */ 492 extern "C" void 493 mmu_free(void *virtualAddress, size_t size) 494 { 495 TRACE("mmu_free(virtualAddress = %p, size: %ld)\n", virtualAddress, size); 496 497 addr_t address = (addr_t)virtualAddress; 498 addr_t pageOffset = address % B_PAGE_SIZE; 499 address -= pageOffset; 500 size = (size + pageOffset + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE; 501 502 // is the address within the valid range? 503 if (address < KERNEL_LOAD_BASE || address + size > sNextVirtualAddress) { 504 panic("mmu_free: asked to unmap out of range region (%p, size %lx)\n", 505 (void *)address, size); 506 } 507 508 // unmap all pages within the range 509 for (size_t i = 0; i < size; i += B_PAGE_SIZE) { 510 unmap_page(address); 511 address += B_PAGE_SIZE; 512 } 513 514 if (address == sNextVirtualAddress) { 515 // we can actually reuse the virtual address space 516 sNextVirtualAddress -= size; 517 } 518 } 519 520 521 size_t 522 mmu_get_virtual_usage() 523 { 524 return sNextVirtualAddress - KERNEL_LOAD_BASE; 525 } 526 527 528 bool 529 mmu_get_virtual_mapping(addr_t virtualAddress, addr_t *_physicalAddress) 530 { 531 if (virtualAddress < KERNEL_LOAD_BASE) { 532 panic("mmu_get_virtual_mapping: asked to lookup invalid page %p!\n", 533 (void *)virtualAddress); 534 } 535 536 uint32 dirEntry = sPageDirectory[virtualAddress / (B_PAGE_SIZE * 1024)]; 537 if ((dirEntry & (1 << 0)) == 0) 538 return false; 539 540 uint32 *pageTable = (uint32 *)(dirEntry & 0xfffff000); 541 uint32 tableEntry = pageTable[(virtualAddress % (B_PAGE_SIZE * 1024)) 542 / B_PAGE_SIZE]; 543 if ((tableEntry & (1 << 0)) == 0) 544 return false; 545 546 *_physicalAddress = tableEntry & 0xfffff000; 547 return true; 548 } 549 550 551 /*! Sets up the final and kernel accessible GDT and IDT tables. 552 BIOS calls won't work any longer after this function has 553 been called. 554 */ 555 extern "C" void 556 mmu_init_for_kernel(void) 557 { 558 TRACE("mmu_init_for_kernel\n"); 559 560 STATIC_ASSERT(BOOT_GDT_SEGMENT_COUNT > KERNEL_CODE_SEGMENT 561 && BOOT_GDT_SEGMENT_COUNT > KERNEL_DATA_SEGMENT 562 && BOOT_GDT_SEGMENT_COUNT > USER_CODE_SEGMENT 563 && BOOT_GDT_SEGMENT_COUNT > USER_DATA_SEGMENT); 564 565 // set up a new gdt 566 567 // put standard segment descriptors in GDT 568 clear_segment_descriptor(&gBootGDT[0]); 569 570 // seg 0x08 - kernel 4GB code 571 set_segment_descriptor(&gBootGDT[KERNEL_CODE_SEGMENT], 0, 0xffffffff, 572 DT_CODE_READABLE, DPL_KERNEL); 573 574 // seg 0x10 - kernel 4GB data 575 set_segment_descriptor(&gBootGDT[KERNEL_DATA_SEGMENT], 0, 0xffffffff, 576 DT_DATA_WRITEABLE, DPL_KERNEL); 577 578 // seg 0x1b - ring 3 user 4GB code 579 set_segment_descriptor(&gBootGDT[USER_CODE_SEGMENT], 0, 0xffffffff, 580 DT_CODE_READABLE, DPL_USER); 581 582 // seg 0x23 - ring 3 user 4GB data 583 set_segment_descriptor(&gBootGDT[USER_DATA_SEGMENT], 0, 0xffffffff, 584 DT_DATA_WRITEABLE, DPL_USER); 585 586 // load the GDT 587 struct gdt_idt_descr gdtDescriptor; 588 gdtDescriptor.limit = sizeof(gBootGDT); 589 gdtDescriptor.base = gBootGDT; 590 591 asm("lgdt %0" : : "m" (gdtDescriptor)); 592 593 TRACE("gdt at virtual address %p\n", gBootGDT); 594 595 // Save the memory we've virtually allocated (for the kernel and other 596 // stuff) 597 gKernelArgs.virtual_allocated_range[0].start = KERNEL_LOAD_BASE; 598 gKernelArgs.virtual_allocated_range[0].size 599 = sNextVirtualAddress - KERNEL_LOAD_BASE; 600 gKernelArgs.num_virtual_allocated_ranges = 1; 601 602 // sort the address ranges 603 sort_address_ranges(gKernelArgs.physical_memory_range, 604 gKernelArgs.num_physical_memory_ranges); 605 sort_address_ranges(gKernelArgs.physical_allocated_range, 606 gKernelArgs.num_physical_allocated_ranges); 607 sort_address_ranges(gKernelArgs.virtual_allocated_range, 608 gKernelArgs.num_virtual_allocated_ranges); 609 610 #ifdef TRACE_MEMORY_MAP 611 { 612 uint32 i; 613 614 dprintf("phys memory ranges:\n"); 615 for (i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) { 616 dprintf(" base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n", 617 gKernelArgs.physical_memory_range[i].start, 618 gKernelArgs.physical_memory_range[i].size); 619 } 620 621 dprintf("allocated phys memory ranges:\n"); 622 for (i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) { 623 dprintf(" base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n", 624 gKernelArgs.physical_allocated_range[i].start, 625 gKernelArgs.physical_allocated_range[i].size); 626 } 627 628 dprintf("allocated virt memory ranges:\n"); 629 for (i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) { 630 dprintf(" base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n", 631 gKernelArgs.virtual_allocated_range[i].start, 632 gKernelArgs.virtual_allocated_range[i].size); 633 } 634 } 635 #endif 636 } 637 638 639 extern "C" void 640 mmu_init(void) 641 { 642 TRACE("mmu_init\n"); 643 644 gKernelArgs.arch_args.virtual_end = KERNEL_LOAD_BASE; 645 646 gKernelArgs.physical_allocated_range[0].start = sNextPhysicalKernelAddress; 647 gKernelArgs.physical_allocated_range[0].size = 0; 648 gKernelArgs.num_physical_allocated_ranges = 1; 649 // remember the start of the allocated physical pages 650 651 init_page_directory(); 652 653 // Map the page directory into kernel space at 0xffc00000-0xffffffff 654 // this enables a mmu trick where the 4 MB region that this pgdir entry 655 // represents now maps the 4MB of potential pagetables that the pgdir 656 // points to. Thrown away later in VM bringup, but useful for now. 657 sPageDirectory[1023] = (uint32)sPageDirectory | kDefaultPageFlags; 658 659 // also map it on the next vpage 660 gKernelArgs.arch_args.vir_pgdir = get_next_virtual_page(); 661 map_page(gKernelArgs.arch_args.vir_pgdir, (uint32)sPageDirectory, 662 kDefaultPageFlags); 663 664 // map in a kernel stack 665 gKernelArgs.cpu_kstack[0].start = (addr_t)mmu_allocate(NULL, 666 KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE); 667 gKernelArgs.cpu_kstack[0].size = KERNEL_STACK_SIZE 668 + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE; 669 670 TRACE("kernel stack at 0x%" B_PRIx64 " to 0x%" B_PRIx64 "\n", 671 gKernelArgs.cpu_kstack[0].start, gKernelArgs.cpu_kstack[0].start 672 + gKernelArgs.cpu_kstack[0].size); 673 674 extended_memory *extMemoryBlock; 675 uint32 extMemoryCount = get_memory_map(&extMemoryBlock); 676 677 // figure out the memory map 678 if (extMemoryCount > 0) { 679 gKernelArgs.num_physical_memory_ranges = 0; 680 681 // first scan: add all usable ranges 682 for (uint32 i = 0; i < extMemoryCount; i++) { 683 // Type 1 is available memory 684 if (extMemoryBlock[i].type != 1) 685 continue; 686 687 uint64 base = extMemoryBlock[i].base_addr; 688 uint64 length = extMemoryBlock[i].length; 689 uint64 end = base + length; 690 691 // round everything up to page boundaries, exclusive of pages 692 // it partially occupies 693 base = ROUNDUP(base, B_PAGE_SIZE); 694 end = ROUNDDOWN(end, B_PAGE_SIZE); 695 696 // We ignore all memory beyond 4 GB, if phys_addr_t is only 697 // 32 bit wide. 698 #if B_HAIKU_PHYSICAL_BITS == 32 699 if (end > 0x100000000ULL) 700 end = 0x100000000ULL; 701 #endif 702 703 // Also ignore memory below 1 MB. Apparently some BIOSes fail to 704 // provide the correct range type for some ranges (cf. #1925). 705 // Later in the kernel we will reserve the range 0x0 - 0xa0000 706 // and apparently 0xa0000 - 0x100000 never contain usable 707 // memory, so we don't lose anything by doing that. 708 if (base < 0x100000) 709 base = 0x100000; 710 711 gKernelArgs.ignored_physical_memory 712 += length - (max_c(end, base) - base); 713 714 if (end <= base) 715 continue; 716 717 status_t status = insert_physical_memory_range(base, end - base); 718 if (status == B_ENTRY_NOT_FOUND) { 719 panic("mmu_init(): Failed to add physical memory range " 720 "%#" B_PRIx64 " - %#" B_PRIx64 " : all %d entries are " 721 "used already!\n", base, end, MAX_PHYSICAL_MEMORY_RANGE); 722 } else if (status != B_OK) { 723 panic("mmu_init(): Failed to add physical memory range " 724 "%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end); 725 } 726 } 727 728 uint64 initialPhysicalMemory = total_physical_memory(); 729 730 // second scan: remove everything reserved that may overlap 731 for (uint32 i = 0; i < extMemoryCount; i++) { 732 if (extMemoryBlock[i].type == 1) 733 continue; 734 735 uint64 base = extMemoryBlock[i].base_addr; 736 uint64 end = ROUNDUP(base + extMemoryBlock[i].length, B_PAGE_SIZE); 737 base = ROUNDDOWN(base, B_PAGE_SIZE); 738 739 status_t status = remove_physical_memory_range(base, end - base); 740 if (status != B_OK) { 741 panic("mmu_init(): Failed to remove physical memory range " 742 "%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end); 743 } 744 } 745 746 // sort the ranges 747 sort_address_ranges(gKernelArgs.physical_memory_range, 748 gKernelArgs.num_physical_memory_ranges); 749 750 // On some machines we get several ranges that contain only a few pages 751 // (or even only one) each, which causes us to run out of MTRRs later. 752 // So we remove all ranges smaller than 64 KB, hoping that this will 753 // leave us only with a few larger contiguous ranges (ideally one). 754 for (int32 i = gKernelArgs.num_physical_memory_ranges - 1; i >= 0; 755 i--) { 756 uint64 size = gKernelArgs.physical_memory_range[i].size; 757 if (size < 64 * 1024) { 758 uint64 start = gKernelArgs.physical_memory_range[i].start; 759 remove_physical_memory_range(start, size); 760 } 761 } 762 763 gKernelArgs.ignored_physical_memory 764 += initialPhysicalMemory - total_physical_memory(); 765 } else { 766 bios_regs regs; 767 768 // We dont have an extended map, assume memory is contiguously mapped 769 // at 0x0, but leave out the BIOS range ((640k - 1 page) to 1 MB). 770 gKernelArgs.physical_memory_range[0].start = 0; 771 gKernelArgs.physical_memory_range[0].size = 0x9f000; 772 gKernelArgs.physical_memory_range[1].start = 0x100000; 773 774 regs.eax = 0xe801; // AX 775 call_bios(0x15, ®s); 776 if ((regs.flags & CARRY_FLAG) != 0) { 777 regs.eax = 0x8800; // AH 88h 778 call_bios(0x15, ®s); 779 if ((regs.flags & CARRY_FLAG) != 0) { 780 // TODO: for now! 781 dprintf("No memory size - using 64 MB (fix me!)\n"); 782 uint32 memSize = 64 * 1024 * 1024; 783 gKernelArgs.physical_memory_range[1].size = memSize - 0x100000; 784 } else { 785 dprintf("Get Extended Memory Size succeeded.\n"); 786 gKernelArgs.physical_memory_range[1].size = regs.eax * 1024; 787 } 788 gKernelArgs.num_physical_memory_ranges = 2; 789 } else { 790 dprintf("Get Memory Size for Large Configurations succeeded.\n"); 791 gKernelArgs.physical_memory_range[1].size = regs.ecx * 1024; 792 gKernelArgs.physical_memory_range[2].start = 0x1000000; 793 gKernelArgs.physical_memory_range[2].size = regs.edx * 64 * 1024; 794 gKernelArgs.num_physical_memory_ranges = 3; 795 } 796 } 797 798 gKernelArgs.arch_args.page_hole = 0xffc00000; 799 } 800 801 802 // #pragma mark - 803 804 805 extern "C" status_t 806 platform_allocate_region(void **_address, size_t size, uint8 protection, 807 bool /*exactAddress*/) 808 { 809 void *address = mmu_allocate(*_address, size); 810 if (address == NULL) 811 return B_NO_MEMORY; 812 813 *_address = address; 814 return B_OK; 815 } 816 817 818 extern "C" status_t 819 platform_free_region(void *address, size_t size) 820 { 821 mmu_free(address, size); 822 return B_OK; 823 } 824 825 826 ssize_t 827 platform_allocate_heap_region(size_t size, void **_base) 828 { 829 size = ROUNDUP(size, B_PAGE_SIZE); 830 addr_t base = allocate_physical(size, false); 831 if (base == 0) 832 return B_NO_MEMORY; 833 834 *_base = (void*)base; 835 return size; 836 } 837 838 839 void 840 platform_free_heap_region(void *_base, size_t size) 841 { 842 addr_t base = (addr_t)_base; 843 if (sNextPhysicalAddress == (base + size)) 844 sNextPhysicalAddress -= size; 845 846 // Failures don't matter very much as regions should be freed automatically, 847 // since they're in the identity map and not stored in the kernel's page tables. 848 } 849 850 851 extern "C" status_t 852 platform_bootloader_address_to_kernel_address(void *address, addr_t *_result) 853 { 854 TRACE("%s: called\n", __func__); 855 // bios_ia32 really doesn't need an address converstion 856 *_result = (addr_t)address; 857 return B_OK; 858 } 859 860 861 extern "C" status_t 862 platform_kernel_address_to_bootloader_address(addr_t address, void **_result) 863 { 864 TRACE("%s: called\n", __func__); 865 // bios_ia32 really doesn't need an address converstion 866 *_result = (void*)address; 867 return B_OK; 868 } 869