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