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