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 inIdentityMap = true) 116 { 117 if ((size % B_PAGE_SIZE) != 0) 118 panic("request for non-page-aligned physical memory!"); 119 120 addr_t* nextAddress = &sNextPhysicalKernelAddress; 121 if (inIdentityMap) { 122 nextAddress = &sNextPhysicalAddress; 123 if ((*nextAddress + size) > kIdentityMapEnd) { 124 panic("request too large for identity-map physical memory!"); 125 return 0; 126 } 127 } 128 129 uint64 base = *nextAddress; 130 if (!get_free_address_range(gKernelArgs.physical_allocated_range, 131 gKernelArgs.num_physical_allocated_ranges, base, size, &base)) { 132 panic("Out of physical memory!"); 133 return 0; 134 } 135 136 insert_physical_allocated_range(base, size); 137 *nextAddress = 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, false); 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); 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); 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 = HOWMANY(size, 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 += pageOffset; 501 size = ROUNDUP(size, B_PAGE_SIZE); 502 503 // is the address within the valid range? 504 if (address < KERNEL_LOAD_BASE || address + size > sNextVirtualAddress) { 505 panic("mmu_free: asked to unmap out of range region (%p, size %lx)\n", 506 (void *)address, size); 507 } 508 509 // unmap all pages within the range 510 for (size_t i = 0; i < size; i += B_PAGE_SIZE) { 511 unmap_page(address); 512 address += B_PAGE_SIZE; 513 } 514 515 if (address == sNextVirtualAddress) { 516 // we can actually reuse the virtual address space 517 sNextVirtualAddress -= size; 518 } 519 } 520 521 522 size_t 523 mmu_get_virtual_usage() 524 { 525 return sNextVirtualAddress - KERNEL_LOAD_BASE; 526 } 527 528 529 bool 530 mmu_get_virtual_mapping(addr_t virtualAddress, addr_t *_physicalAddress) 531 { 532 if (virtualAddress < KERNEL_LOAD_BASE) { 533 panic("mmu_get_virtual_mapping: asked to lookup invalid page %p!\n", 534 (void *)virtualAddress); 535 } 536 537 uint32 dirEntry = sPageDirectory[virtualAddress / (B_PAGE_SIZE * 1024)]; 538 if ((dirEntry & (1 << 0)) == 0) 539 return false; 540 541 uint32 *pageTable = (uint32 *)(dirEntry & 0xfffff000); 542 uint32 tableEntry = pageTable[(virtualAddress % (B_PAGE_SIZE * 1024)) 543 / B_PAGE_SIZE]; 544 if ((tableEntry & (1 << 0)) == 0) 545 return false; 546 547 *_physicalAddress = tableEntry & 0xfffff000; 548 return true; 549 } 550 551 552 /*! Sets up the final and kernel accessible GDT and IDT tables. 553 BIOS calls won't work any longer after this function has 554 been called. 555 */ 556 extern "C" void 557 mmu_init_for_kernel(void) 558 { 559 TRACE("mmu_init_for_kernel\n"); 560 561 STATIC_ASSERT(BOOT_GDT_SEGMENT_COUNT > KERNEL_CODE_SEGMENT 562 && BOOT_GDT_SEGMENT_COUNT > KERNEL_DATA_SEGMENT 563 && BOOT_GDT_SEGMENT_COUNT > USER_CODE_SEGMENT 564 && BOOT_GDT_SEGMENT_COUNT > USER_DATA_SEGMENT); 565 566 // set up a new gdt 567 568 // put standard segment descriptors in GDT 569 clear_segment_descriptor(&gBootGDT[0]); 570 571 // seg 0x08 - kernel 4GB code 572 set_segment_descriptor(&gBootGDT[KERNEL_CODE_SEGMENT], 0, 0xffffffff, 573 DT_CODE_READABLE, DPL_KERNEL); 574 575 // seg 0x10 - kernel 4GB data 576 set_segment_descriptor(&gBootGDT[KERNEL_DATA_SEGMENT], 0, 0xffffffff, 577 DT_DATA_WRITEABLE, DPL_KERNEL); 578 579 // seg 0x1b - ring 3 user 4GB code 580 set_segment_descriptor(&gBootGDT[USER_CODE_SEGMENT], 0, 0xffffffff, 581 DT_CODE_READABLE, DPL_USER); 582 583 // seg 0x23 - ring 3 user 4GB data 584 set_segment_descriptor(&gBootGDT[USER_DATA_SEGMENT], 0, 0xffffffff, 585 DT_DATA_WRITEABLE, DPL_USER); 586 587 // load the GDT 588 struct gdt_idt_descr gdtDescriptor; 589 gdtDescriptor.limit = sizeof(gBootGDT); 590 gdtDescriptor.base = gBootGDT; 591 592 asm("lgdt %0" : : "m" (gdtDescriptor)); 593 594 TRACE("gdt at virtual address %p\n", gBootGDT); 595 596 // Save the memory we've virtually allocated (for the kernel and other 597 // stuff) 598 gKernelArgs.virtual_allocated_range[0].start = KERNEL_LOAD_BASE; 599 gKernelArgs.virtual_allocated_range[0].size 600 = sNextVirtualAddress - KERNEL_LOAD_BASE; 601 gKernelArgs.num_virtual_allocated_ranges = 1; 602 603 // sort the address ranges 604 sort_address_ranges(gKernelArgs.physical_memory_range, 605 gKernelArgs.num_physical_memory_ranges); 606 sort_address_ranges(gKernelArgs.physical_allocated_range, 607 gKernelArgs.num_physical_allocated_ranges); 608 sort_address_ranges(gKernelArgs.virtual_allocated_range, 609 gKernelArgs.num_virtual_allocated_ranges); 610 611 #ifdef TRACE_MEMORY_MAP 612 { 613 uint32 i; 614 615 dprintf("phys memory ranges:\n"); 616 for (i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) { 617 dprintf(" base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n", 618 gKernelArgs.physical_memory_range[i].start, 619 gKernelArgs.physical_memory_range[i].size); 620 } 621 622 dprintf("allocated phys memory ranges:\n"); 623 for (i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) { 624 dprintf(" base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n", 625 gKernelArgs.physical_allocated_range[i].start, 626 gKernelArgs.physical_allocated_range[i].size); 627 } 628 629 dprintf("allocated virt memory ranges:\n"); 630 for (i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) { 631 dprintf(" base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n", 632 gKernelArgs.virtual_allocated_range[i].start, 633 gKernelArgs.virtual_allocated_range[i].size); 634 } 635 } 636 #endif 637 } 638 639 640 extern "C" void 641 mmu_init(void) 642 { 643 TRACE("mmu_init\n"); 644 645 gKernelArgs.arch_args.virtual_end = KERNEL_LOAD_BASE; 646 647 gKernelArgs.physical_allocated_range[0].start = sNextPhysicalAddress; 648 gKernelArgs.physical_allocated_range[0].size = 0; 649 gKernelArgs.physical_allocated_range[1].start = sNextPhysicalKernelAddress; 650 gKernelArgs.physical_allocated_range[1].size = 0; 651 gKernelArgs.num_physical_allocated_ranges = 2; 652 // remember the start of the allocated physical pages 653 654 init_page_directory(); 655 656 // Map the page directory into kernel space at 0xffc00000-0xffffffff 657 // this enables a mmu trick where the 4 MB region that this pgdir entry 658 // represents now maps the 4MB of potential pagetables that the pgdir 659 // points to. Thrown away later in VM bringup, but useful for now. 660 sPageDirectory[1023] = (uint32)sPageDirectory | kDefaultPageFlags; 661 662 // also map it on the next vpage 663 gKernelArgs.arch_args.vir_pgdir = get_next_virtual_page(); 664 map_page(gKernelArgs.arch_args.vir_pgdir, (uint32)sPageDirectory, 665 kDefaultPageFlags); 666 667 // map in a kernel stack 668 gKernelArgs.cpu_kstack[0].start = (addr_t)mmu_allocate(NULL, 669 KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE); 670 gKernelArgs.cpu_kstack[0].size = KERNEL_STACK_SIZE 671 + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE; 672 673 TRACE("kernel stack at 0x%" B_PRIx64 " to 0x%" B_PRIx64 "\n", 674 gKernelArgs.cpu_kstack[0].start, gKernelArgs.cpu_kstack[0].start 675 + gKernelArgs.cpu_kstack[0].size); 676 677 extended_memory *extMemoryBlock; 678 uint32 extMemoryCount = get_memory_map(&extMemoryBlock); 679 680 // figure out the memory map 681 if (extMemoryCount > 0) { 682 gKernelArgs.num_physical_memory_ranges = 0; 683 684 // first scan: add all usable ranges 685 for (uint32 i = 0; i < extMemoryCount; i++) { 686 // Type 1 is available memory 687 if (extMemoryBlock[i].type != 1) 688 continue; 689 690 uint64 base = extMemoryBlock[i].base_addr; 691 uint64 length = extMemoryBlock[i].length; 692 uint64 end = base + length; 693 694 // round everything up to page boundaries, exclusive of pages 695 // it partially occupies 696 base = ROUNDUP(base, B_PAGE_SIZE); 697 end = ROUNDDOWN(end, B_PAGE_SIZE); 698 699 // We ignore all memory beyond 4 GB, if phys_addr_t is only 700 // 32 bit wide. 701 #if B_HAIKU_PHYSICAL_BITS == 32 702 if (end > 0x100000000ULL) 703 end = 0x100000000ULL; 704 #endif 705 706 // Also ignore memory below 1 MB. Apparently some BIOSes fail to 707 // provide the correct range type for some ranges (cf. #1925). 708 // Later in the kernel we will reserve the range 0x0 - 0xa0000 709 // and apparently 0xa0000 - 0x100000 never contain usable 710 // memory, so we don't lose anything by doing that. 711 if (base < 0x100000) 712 base = 0x100000; 713 714 gKernelArgs.ignored_physical_memory 715 += length - (max_c(end, base) - base); 716 717 if (end <= base) 718 continue; 719 720 status_t status = insert_physical_memory_range(base, end - base); 721 if (status == B_ENTRY_NOT_FOUND) { 722 panic("mmu_init(): Failed to add physical memory range " 723 "%#" B_PRIx64 " - %#" B_PRIx64 " : all %d entries are " 724 "used already!\n", base, end, MAX_PHYSICAL_MEMORY_RANGE); 725 } else if (status != B_OK) { 726 panic("mmu_init(): Failed to add physical memory range " 727 "%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end); 728 } 729 } 730 731 uint64 initialPhysicalMemory = total_physical_memory(); 732 733 // second scan: remove everything reserved that may overlap 734 for (uint32 i = 0; i < extMemoryCount; i++) { 735 if (extMemoryBlock[i].type == 1) 736 continue; 737 738 uint64 base = extMemoryBlock[i].base_addr; 739 uint64 end = ROUNDUP(base + extMemoryBlock[i].length, B_PAGE_SIZE); 740 base = ROUNDDOWN(base, B_PAGE_SIZE); 741 742 status_t status = remove_physical_memory_range(base, end - base); 743 if (status != B_OK) { 744 panic("mmu_init(): Failed to remove physical memory range " 745 "%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end); 746 } 747 } 748 749 // sort the ranges 750 sort_address_ranges(gKernelArgs.physical_memory_range, 751 gKernelArgs.num_physical_memory_ranges); 752 753 // On some machines we get several ranges that contain only a few pages 754 // (or even only one) each, which causes us to run out of MTRRs later. 755 // So we remove all ranges smaller than 64 KB, hoping that this will 756 // leave us only with a few larger contiguous ranges (ideally one). 757 for (int32 i = gKernelArgs.num_physical_memory_ranges - 1; i >= 0; 758 i--) { 759 uint64 size = gKernelArgs.physical_memory_range[i].size; 760 if (size < 64 * 1024) { 761 uint64 start = gKernelArgs.physical_memory_range[i].start; 762 remove_physical_memory_range(start, size); 763 } 764 } 765 766 gKernelArgs.ignored_physical_memory 767 += initialPhysicalMemory - total_physical_memory(); 768 } else { 769 bios_regs regs; 770 771 // We dont have an extended map, assume memory is contiguously mapped 772 // at 0x0, but leave out the BIOS range ((640k - 1 page) to 1 MB). 773 gKernelArgs.physical_memory_range[0].start = 0; 774 gKernelArgs.physical_memory_range[0].size = 0x9f000; 775 gKernelArgs.physical_memory_range[1].start = 0x100000; 776 777 regs.eax = 0xe801; // AX 778 call_bios(0x15, ®s); 779 if ((regs.flags & CARRY_FLAG) != 0) { 780 regs.eax = 0x8800; // AH 88h 781 call_bios(0x15, ®s); 782 if ((regs.flags & CARRY_FLAG) != 0) { 783 // TODO: for now! 784 dprintf("No memory size - using 64 MB (fix me!)\n"); 785 uint32 memSize = 64 * 1024 * 1024; 786 gKernelArgs.physical_memory_range[1].size = memSize - 0x100000; 787 } else { 788 dprintf("Get Extended Memory Size succeeded.\n"); 789 gKernelArgs.physical_memory_range[1].size = regs.eax * 1024; 790 } 791 gKernelArgs.num_physical_memory_ranges = 2; 792 } else { 793 dprintf("Get Memory Size for Large Configurations succeeded.\n"); 794 gKernelArgs.physical_memory_range[1].size = regs.ecx * 1024; 795 gKernelArgs.physical_memory_range[2].start = 0x1000000; 796 gKernelArgs.physical_memory_range[2].size = regs.edx * 64 * 1024; 797 gKernelArgs.num_physical_memory_ranges = 3; 798 } 799 } 800 801 gKernelArgs.arch_args.page_hole = 0xffc00000; 802 } 803 804 805 // #pragma mark - 806 807 808 extern "C" status_t 809 platform_allocate_region(void **_address, size_t size, uint8 protection, 810 bool /*exactAddress*/) 811 { 812 void *address = mmu_allocate(*_address, size); 813 if (address == NULL) 814 return B_NO_MEMORY; 815 816 *_address = address; 817 return B_OK; 818 } 819 820 821 extern "C" status_t 822 platform_free_region(void *address, size_t size) 823 { 824 mmu_free(address, size); 825 return B_OK; 826 } 827 828 829 ssize_t 830 platform_allocate_heap_region(size_t size, void **_base) 831 { 832 size = ROUNDUP(size, B_PAGE_SIZE); 833 addr_t base = allocate_physical(size); 834 if (base == 0) 835 return B_NO_MEMORY; 836 837 *_base = (void*)base; 838 return size; 839 } 840 841 842 void 843 platform_free_heap_region(void *_base, size_t size) 844 { 845 addr_t base = (addr_t)_base; 846 remove_physical_allocated_range(base, size); 847 if (sNextPhysicalAddress == (base + size)) 848 sNextPhysicalAddress -= size; 849 850 // Failures don't matter very much as regions should be freed automatically, 851 // since they're in the identity map and not stored in the kernel's page tables. 852 } 853 854 855 extern "C" status_t 856 platform_bootloader_address_to_kernel_address(void *address, addr_t *_result) 857 { 858 TRACE("%s: called\n", __func__); 859 // bios_ia32 really doesn't need an address converstion 860 *_result = (addr_t)address; 861 return B_OK; 862 } 863 864 865 extern "C" status_t 866 platform_kernel_address_to_bootloader_address(addr_t address, void **_result) 867 { 868 TRACE("%s: called\n", __func__); 869 // bios_ia32 really doesn't need an address converstion 870 *_result = (void*)address; 871 return B_OK; 872 } 873