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