1 /* 2 * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2010-2011, Haiku, Inc. All Rights Reserved. 4 * All rights reserved. Distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * Axel Dörfler, axeld@pinc-software.de. 8 * Alexander von Gluck, kallisti5@unixzen.com 9 */ 10 11 12 #include <OS.h> 13 14 #include <platform_arch.h> 15 #include <boot/addr_range.h> 16 #include <boot/kernel_args.h> 17 #include <boot/platform.h> 18 #include <boot/stage2.h> 19 #include <boot/stdio.h> 20 #include <platform/openfirmware/openfirmware.h> 21 #include <arch_cpu.h> 22 #include <arch_mmu.h> 23 #include <kernel.h> 24 25 #include "support.h" 26 27 28 // set protection to WIMGNPP: -----PP 29 // PP: 00 - no access 30 // 01 - read only 31 // 10 - read/write 32 // 11 - read only 33 #define PAGE_READ_ONLY 0x01 34 #define PAGE_READ_WRITE 0x02 35 36 // NULL is actually a possible physical address... 37 //#define PHYSINVAL ((void *)-1) 38 #define PHYSINVAL NULL 39 40 //#define TRACE_MMU 41 #ifdef TRACE_MMU 42 # define TRACE(x...) dprintf(x) 43 #else 44 # define TRACE(x...) ; 45 #endif 46 47 48 segment_descriptor sSegments[16]; 49 page_table_entry_group *sPageTable; 50 uint32 sPageTableHashMask; 51 52 53 // begin and end of the boot loader 54 extern "C" uint8 __text_begin; 55 extern "C" uint8 _end; 56 57 58 static status_t 59 insert_virtual_range_to_keep(void *start, uint32 size) 60 { 61 return insert_address_range(gKernelArgs.arch_args.virtual_ranges_to_keep, 62 &gKernelArgs.arch_args.num_virtual_ranges_to_keep, 63 MAX_VIRTUAL_RANGES_TO_KEEP, (addr_t)start, size); 64 } 65 66 67 static status_t 68 remove_virtual_range_to_keep(void *start, uint32 size) 69 { 70 return remove_address_range(gKernelArgs.arch_args.virtual_ranges_to_keep, 71 &gKernelArgs.arch_args.num_virtual_ranges_to_keep, 72 MAX_VIRTUAL_RANGES_TO_KEEP, (addr_t)start, size); 73 } 74 75 76 static status_t 77 find_physical_memory_ranges(size_t &total) 78 { 79 int memory; 80 dprintf("checking for memory...\n"); 81 if (of_getprop(gChosen, "memory", &memory, sizeof(int)) == OF_FAILED) 82 return B_ERROR; 83 int package = of_instance_to_package(memory); 84 85 total = 0; 86 87 // Memory base addresses are provided in 32 or 64 bit flavors 88 // #address-cells and #size-cells matches the number of 32-bit 'cells' 89 // representing the length of the base address and size fields 90 int root = of_finddevice("/"); 91 int32 regAddressCells = of_address_cells(root); 92 int32 regSizeCells = of_size_cells(root); 93 if (regAddressCells == OF_FAILED || regSizeCells == OF_FAILED) { 94 dprintf("finding base/size length counts failed, assume 32-bit.\n"); 95 regAddressCells = 1; 96 regSizeCells = 1; 97 } 98 99 // NOTE : Size Cells of 2 is possible in theory... but I haven't seen it yet. 100 if (regAddressCells > 2 || regSizeCells > 1) { 101 panic("%s: Unsupported OpenFirmware cell count detected.\n" 102 "Address Cells: %" B_PRId32 "; Size Cells: %" B_PRId32 103 " (CPU > 64bit?).\n", __func__, regAddressCells, regSizeCells); 104 return B_ERROR; 105 } 106 107 // On 64-bit PowerPC systems (G5), our mem base range address is larger 108 if (regAddressCells == 2) { 109 struct of_region<uint64> regions[64]; 110 int count = of_getprop(package, "reg", regions, sizeof(regions)); 111 if (count == OF_FAILED) 112 count = of_getprop(memory, "reg", regions, sizeof(regions)); 113 if (count == OF_FAILED) 114 return B_ERROR; 115 count /= sizeof(regions[0]); 116 117 for (int32 i = 0; i < count; i++) { 118 if (regions[i].size <= 0) { 119 dprintf("%ld: empty region\n", i); 120 continue; 121 } 122 dprintf("%" B_PRIu32 ": base = %" B_PRIu64 "," 123 "size = %" B_PRIu32 "\n", i, regions[i].base, regions[i].size); 124 125 total += regions[i].size; 126 127 if (insert_physical_memory_range((addr_t)regions[i].base, 128 regions[i].size) != B_OK) { 129 dprintf("cannot map physical memory range " 130 "(num ranges = %" B_PRIu32 ")!\n", 131 gKernelArgs.num_physical_memory_ranges); 132 return B_ERROR; 133 } 134 } 135 return B_OK; 136 } 137 138 // Otherwise, normal 32-bit PowerPC G3 or G4 have a smaller 32-bit one 139 struct of_region<uint32> regions[64]; 140 int count = of_getprop(package, "reg", regions, sizeof(regions)); 141 if (count == OF_FAILED) 142 count = of_getprop(memory, "reg", regions, sizeof(regions)); 143 if (count == OF_FAILED) 144 return B_ERROR; 145 count /= sizeof(regions[0]); 146 147 for (int32 i = 0; i < count; i++) { 148 if (regions[i].size <= 0) { 149 dprintf("%ld: empty region\n", i); 150 continue; 151 } 152 dprintf("%" B_PRIu32 ": base = %" B_PRIu32 "," 153 "size = %" B_PRIu32 "\n", i, regions[i].base, regions[i].size); 154 155 total += regions[i].size; 156 157 if (insert_physical_memory_range((addr_t)regions[i].base, 158 regions[i].size) != B_OK) { 159 dprintf("cannot map physical memory range " 160 "(num ranges = %" B_PRIu32 ")!\n", 161 gKernelArgs.num_physical_memory_ranges); 162 return B_ERROR; 163 } 164 } 165 166 return B_OK; 167 } 168 169 170 static bool 171 is_virtual_allocated(void *address, size_t size) 172 { 173 uint64 foundBase; 174 return !get_free_address_range(gKernelArgs.virtual_allocated_range, 175 gKernelArgs.num_virtual_allocated_ranges, (addr_t)address, size, 176 &foundBase) || foundBase != (addr_t)address; 177 } 178 179 180 static bool 181 is_physical_allocated(void *address, size_t size) 182 { 183 uint64 foundBase; 184 return !get_free_address_range(gKernelArgs.physical_allocated_range, 185 gKernelArgs.num_physical_allocated_ranges, (addr_t)address, size, 186 &foundBase) || foundBase != (addr_t)address; 187 } 188 189 190 static bool 191 is_physical_memory(void *address, size_t size) 192 { 193 return is_address_range_covered(gKernelArgs.physical_memory_range, 194 gKernelArgs.num_physical_memory_ranges, (addr_t)address, size); 195 } 196 197 198 static bool 199 is_physical_memory(void *address) 200 { 201 return is_physical_memory(address, 1); 202 } 203 204 205 static void 206 fill_page_table_entry(page_table_entry *entry, uint32 virtualSegmentID, 207 void *virtualAddress, void *physicalAddress, uint8 mode, bool secondaryHash) 208 { 209 // lower 32 bit - set at once 210 ((uint32 *)entry)[1] 211 = (((uint32)physicalAddress / B_PAGE_SIZE) << 12) | mode; 212 /*entry->physical_page_number = (uint32)physicalAddress / B_PAGE_SIZE; 213 entry->_reserved0 = 0; 214 entry->referenced = false; 215 entry->changed = false; 216 entry->write_through = (mode >> 6) & 1; 217 entry->caching_inhibited = (mode >> 5) & 1; 218 entry->memory_coherent = (mode >> 4) & 1; 219 entry->guarded = (mode >> 3) & 1; 220 entry->_reserved1 = 0; 221 entry->page_protection = mode & 0x3;*/ 222 eieio(); 223 // we need to make sure that the lower 32 bit were 224 // already written when the entry becomes valid 225 226 // upper 32 bit 227 entry->virtual_segment_id = virtualSegmentID; 228 entry->secondary_hash = secondaryHash; 229 entry->abbr_page_index = ((uint32)virtualAddress >> 22) & 0x3f; 230 entry->valid = true; 231 } 232 233 234 static void 235 map_page(void *virtualAddress, void *physicalAddress, uint8 mode) 236 { 237 uint32 virtualSegmentID 238 = sSegments[addr_t(virtualAddress) >> 28].virtual_segment_id; 239 240 uint32 hash = page_table_entry::PrimaryHash(virtualSegmentID, 241 (uint32)virtualAddress); 242 page_table_entry_group *group = &sPageTable[hash & sPageTableHashMask]; 243 244 for (int32 i = 0; i < 8; i++) { 245 // 8 entries in a group 246 if (group->entry[i].valid) 247 continue; 248 249 fill_page_table_entry(&group->entry[i], virtualSegmentID, 250 virtualAddress, physicalAddress, mode, false); 251 //TRACE("map: va = %p -> %p, mode = %d, hash = %lu\n", 252 // virtualAddress, physicalAddress, mode, hash); 253 return; 254 } 255 256 hash = page_table_entry::SecondaryHash(hash); 257 group = &sPageTable[hash & sPageTableHashMask]; 258 259 for (int32 i = 0; i < 8; i++) { 260 if (group->entry[i].valid) 261 continue; 262 263 fill_page_table_entry(&group->entry[i], virtualSegmentID, 264 virtualAddress, physicalAddress, mode, true); 265 //TRACE("map: va = %p -> %p, mode = %d, second hash = %lu\n", 266 // virtualAddress, physicalAddress, mode, hash); 267 return; 268 } 269 270 panic("%s: out of page table entries!\n", __func__); 271 } 272 273 274 static void 275 map_range(void *virtualAddress, void *physicalAddress, size_t size, uint8 mode) 276 { 277 for (uint32 offset = 0; offset < size; offset += B_PAGE_SIZE) { 278 map_page((void *)(uint32(virtualAddress) + offset), 279 (void *)(uint32(physicalAddress) + offset), mode); 280 } 281 } 282 283 284 static status_t 285 find_allocated_ranges(void *oldPageTable, void *pageTable, 286 page_table_entry_group **_physicalPageTable, void **_exceptionHandlers) 287 { 288 // we have to preserve the OpenFirmware established mappings 289 // if we want to continue to use its service after we've 290 // taken over (we will probably need less translations once 291 // we have proper driver support for the target hardware). 292 int mmu; 293 if (of_getprop(gChosen, "mmu", &mmu, sizeof(int)) == OF_FAILED) { 294 dprintf("%s: Error: no OpenFirmware mmu\n", __func__); 295 return B_ERROR; 296 } 297 mmu = of_instance_to_package(mmu); 298 299 struct translation_map { 300 void *virtual_address; 301 int length; 302 void *physical_address; 303 int mode; 304 } translations[64]; 305 306 int length = of_getprop(mmu, "translations", &translations, 307 sizeof(translations)); 308 if (length == OF_FAILED) { 309 dprintf("Error: no OF translations.\n"); 310 return B_ERROR; 311 } 312 length = length / sizeof(struct translation_map); 313 uint32 total = 0; 314 dprintf("found %d translations\n", length); 315 316 for (int i = 0; i < length; i++) { 317 struct translation_map *map = &translations[i]; 318 bool keepRange = true; 319 TRACE("%i: map: %p, length %d -> physical: %p, mode %d\n", i, 320 map->virtual_address, map->length, 321 map->physical_address, map->mode); 322 323 // insert range in physical allocated, if it points to physical memory 324 325 if (is_physical_memory(map->physical_address) 326 && insert_physical_allocated_range((addr_t)map->physical_address, 327 map->length) != B_OK) { 328 dprintf("cannot map physical allocated range " 329 "(num ranges = %" B_PRIu32 ")!\n", 330 gKernelArgs.num_physical_allocated_ranges); 331 return B_ERROR; 332 } 333 334 if (map->virtual_address == pageTable) { 335 dprintf("%i: found page table at va %p\n", i, 336 map->virtual_address); 337 *_physicalPageTable 338 = (page_table_entry_group *)map->physical_address; 339 keepRange = false; 340 // we keep it explicitely anyway 341 } 342 if ((addr_t)map->physical_address <= 0x100 343 && (addr_t)map->physical_address + map->length >= 0x1000) { 344 dprintf("%i: found exception handlers at va %p\n", i, 345 map->virtual_address); 346 *_exceptionHandlers = map->virtual_address; 347 keepRange = false; 348 // we keep it explicitely anyway 349 } 350 if (map->virtual_address == oldPageTable) 351 keepRange = false; 352 353 // insert range in virtual allocated 354 355 if (insert_virtual_allocated_range((addr_t)map->virtual_address, 356 map->length) != B_OK) { 357 dprintf("cannot map virtual allocated range " 358 "(num ranges = %" B_PRIu32 ")!\n", 359 gKernelArgs.num_virtual_allocated_ranges); 360 } 361 362 // map range into the page table 363 364 map_range(map->virtual_address, map->physical_address, map->length, 365 map->mode); 366 367 // insert range in virtual ranges to keep 368 369 if (keepRange) { 370 TRACE("%i: keeping free range starting at va %p\n", i, 371 map->virtual_address); 372 373 if (insert_virtual_range_to_keep(map->virtual_address, 374 map->length) != B_OK) { 375 dprintf("cannot map virtual range to keep " 376 "(num ranges = %" B_PRIu32 ")\n", 377 gKernelArgs.num_virtual_allocated_ranges); 378 } 379 } 380 381 total += map->length; 382 } 383 dprintf("total size kept: %" B_PRIu32 "\n", total); 384 385 // remove the boot loader code from the virtual ranges to keep in the 386 // kernel 387 if (remove_virtual_range_to_keep(&__text_begin, &_end - &__text_begin) 388 != B_OK) { 389 dprintf("%s: Failed to remove boot loader range " 390 "from virtual ranges to keep.\n", __func__); 391 } 392 393 return B_OK; 394 } 395 396 397 /*! Computes the recommended minimal page table size as 398 described in table 7-22 of the PowerPC "Programming 399 Environment for 32-Bit Microprocessors". 400 The page table size ranges from 64 kB (for 8 MB RAM) 401 to 32 MB (for 4 GB RAM). 402 */ 403 static size_t 404 suggested_page_table_size(size_t total) 405 { 406 uint32 max = 23; 407 // 2^23 == 8 MB 408 409 while (max < 32) { 410 if (total <= (1UL << max)) 411 break; 412 413 max++; 414 } 415 416 return 1UL << (max - 7); 417 // 2^(23 - 7) == 64 kB 418 } 419 420 421 static void * 422 find_physical_memory_range(size_t size) 423 { 424 for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) { 425 if (gKernelArgs.physical_memory_range[i].size > size) 426 return (void *)(addr_t)gKernelArgs.physical_memory_range[i].start; 427 } 428 return PHYSINVAL; 429 } 430 431 432 static void * 433 find_free_physical_range(size_t size) 434 { 435 // just do a simple linear search at the end of the allocated 436 // ranges (dumb memory allocation) 437 if (gKernelArgs.num_physical_allocated_ranges == 0) { 438 if (gKernelArgs.num_physical_memory_ranges == 0) 439 return PHYSINVAL; 440 441 return find_physical_memory_range(size); 442 } 443 444 for (uint32 i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) { 445 void *address 446 = (void *)(addr_t)(gKernelArgs.physical_allocated_range[i].start 447 + gKernelArgs.physical_allocated_range[i].size); 448 if (!is_physical_allocated(address, size) 449 && is_physical_memory(address, size)) 450 return address; 451 } 452 return PHYSINVAL; 453 } 454 455 456 static void * 457 find_free_virtual_range(void *base, size_t size) 458 { 459 if (base && !is_virtual_allocated(base, size)) 460 return base; 461 462 void *firstFound = NULL; 463 void *firstBaseFound = NULL; 464 for (uint32 i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) { 465 void *address 466 = (void *)(addr_t)(gKernelArgs.virtual_allocated_range[i].start 467 + gKernelArgs.virtual_allocated_range[i].size); 468 if (!is_virtual_allocated(address, size)) { 469 if (!base) 470 return address; 471 472 if (firstFound == NULL) 473 firstFound = address; 474 if (address >= base 475 && (firstBaseFound == NULL || address < firstBaseFound)) { 476 firstBaseFound = address; 477 } 478 } 479 } 480 return (firstBaseFound ? firstBaseFound : firstFound); 481 } 482 483 484 extern "C" void * 485 arch_mmu_allocate(void *_virtualAddress, size_t size, uint8 _protection, 486 bool exactAddress) 487 { 488 // we only know page sizes 489 size = ROUNDUP(size, B_PAGE_SIZE); 490 491 uint8 protection = 0; 492 if (_protection & B_WRITE_AREA) 493 protection = PAGE_READ_WRITE; 494 else 495 protection = PAGE_READ_ONLY; 496 497 // If no address is given, use the KERNEL_BASE as base address, since 498 // that avoids trouble in the kernel, when we decide to keep the region. 499 void *virtualAddress = _virtualAddress; 500 if (!virtualAddress) 501 virtualAddress = (void*)KERNEL_BASE; 502 503 // find free address large enough to hold "size" 504 virtualAddress = find_free_virtual_range(virtualAddress, size); 505 if (virtualAddress == NULL) 506 return NULL; 507 508 // fail if the exact address was requested, but is not free 509 if (exactAddress && _virtualAddress && virtualAddress != _virtualAddress) { 510 dprintf("arch_mmu_allocate(): exact address requested, but virtual " 511 "range (base: %p, size: %" B_PRIuSIZE ") is not free.\n", 512 _virtualAddress, size); 513 return NULL; 514 } 515 516 // we have a free virtual range for the allocation, now 517 // have a look for free physical memory as well (we assume 518 // that a) there is enough memory, and b) failing is fatal 519 // so that we don't have to optimize for these cases :) 520 521 void *physicalAddress = find_free_physical_range(size); 522 if (physicalAddress == PHYSINVAL) { 523 dprintf("arch_mmu_allocate(base: %p, size: %" B_PRIuSIZE ") " 524 "no free physical address\n", virtualAddress, size); 525 return NULL; 526 } 527 528 // everything went fine, so lets mark the space as used. 529 530 dprintf("mmu_alloc: va %p, pa %p, size %" B_PRIuSIZE "\n", virtualAddress, 531 physicalAddress, size); 532 insert_virtual_allocated_range((addr_t)virtualAddress, size); 533 insert_physical_allocated_range((addr_t)physicalAddress, size); 534 535 map_range(virtualAddress, physicalAddress, size, protection); 536 537 return virtualAddress; 538 } 539 540 541 extern "C" status_t 542 arch_mmu_free(void *address, size_t size) 543 { 544 // TODO: implement freeing a region! 545 return B_OK; 546 } 547 548 549 static inline void 550 invalidate_tlb(void) 551 { 552 //asm volatile("tlbia"); 553 // "tlbia" is obviously not available on every CPU... 554 555 // Note: this flushes the whole 4 GB address space - it 556 // would probably be a good idea to do less here 557 558 addr_t address = 0; 559 for (uint32 i = 0; i < 0x100000; i++) { 560 asm volatile("tlbie %0" : : "r" (address)); 561 address += B_PAGE_SIZE; 562 } 563 tlbsync(); 564 } 565 566 567 // #pragma mark - OpenFirmware callbacks and public API 568 569 570 static int 571 map_callback(struct of_arguments *args) 572 { 573 void *physicalAddress = (void *)args->Argument(0); 574 void *virtualAddress = (void *)args->Argument(1); 575 int length = args->Argument(2); 576 int mode = args->Argument(3); 577 int &error = args->ReturnValue(0); 578 579 // insert range in physical allocated if needed 580 581 if (is_physical_memory(physicalAddress) 582 && insert_physical_allocated_range((addr_t)physicalAddress, length) 583 != B_OK) { 584 error = -1; 585 return OF_FAILED; 586 } 587 588 // insert range in virtual allocated 589 590 if (insert_virtual_allocated_range((addr_t)virtualAddress, length) 591 != B_OK) { 592 error = -2; 593 return OF_FAILED; 594 } 595 596 // map range into the page table 597 598 map_range(virtualAddress, physicalAddress, length, mode); 599 600 return B_OK; 601 } 602 603 604 static int 605 unmap_callback(struct of_arguments *args) 606 { 607 /* void *address = (void *)args->Argument(0); 608 int length = args->Argument(1); 609 int &error = args->ReturnValue(0); 610 */ 611 // TODO: to be implemented 612 613 return OF_FAILED; 614 } 615 616 617 static int 618 translate_callback(struct of_arguments *args) 619 { 620 addr_t virtualAddress = (addr_t)args->Argument(0); 621 int &error = args->ReturnValue(0); 622 int &physicalAddress = args->ReturnValue(1); 623 int &mode = args->ReturnValue(2); 624 625 // Find page table entry for this address 626 627 uint32 virtualSegmentID 628 = sSegments[addr_t(virtualAddress) >> 28].virtual_segment_id; 629 630 uint32 hash = page_table_entry::PrimaryHash(virtualSegmentID, 631 (uint32)virtualAddress); 632 page_table_entry_group *group = &sPageTable[hash & sPageTableHashMask]; 633 page_table_entry *entry = NULL; 634 635 for (int32 i = 0; i < 8; i++) { 636 entry = &group->entry[i]; 637 638 if (entry->valid 639 && entry->virtual_segment_id == virtualSegmentID 640 && entry->secondary_hash == false 641 && entry->abbr_page_index == ((virtualAddress >> 22) & 0x3f)) 642 goto success; 643 } 644 645 hash = page_table_entry::SecondaryHash(hash); 646 group = &sPageTable[hash & sPageTableHashMask]; 647 648 for (int32 i = 0; i < 8; i++) { 649 entry = &group->entry[i]; 650 651 if (entry->valid 652 && entry->virtual_segment_id == virtualSegmentID 653 && entry->secondary_hash == true 654 && entry->abbr_page_index == ((virtualAddress >> 22) & 0x3f)) 655 goto success; 656 } 657 658 // could not find the translation 659 error = B_ENTRY_NOT_FOUND; 660 return OF_FAILED; 661 662 success: 663 // we found the entry in question 664 physicalAddress = (int)(entry->physical_page_number * B_PAGE_SIZE); 665 mode = (entry->write_through << 6) // WIMGxPP 666 | (entry->caching_inhibited << 5) 667 | (entry->memory_coherent << 4) 668 | (entry->guarded << 3) 669 | entry->page_protection; 670 error = B_OK; 671 672 return B_OK; 673 } 674 675 676 static int 677 alloc_real_mem_callback(struct of_arguments *args) 678 { 679 /* addr_t minAddress = (addr_t)args->Argument(0); 680 addr_t maxAddress = (addr_t)args->Argument(1); 681 int length = args->Argument(2); 682 int mode = args->Argument(3); 683 int &error = args->ReturnValue(0); 684 int &physicalAddress = args->ReturnValue(1); 685 */ 686 // ToDo: to be implemented 687 688 return OF_FAILED; 689 } 690 691 692 /** Dispatches the callback to the responsible function */ 693 694 static int 695 callback(struct of_arguments *args) 696 { 697 const char *name = args->name; 698 TRACE("OF CALLBACK: %s\n", name); 699 700 if (!strcmp(name, "map")) 701 return map_callback(args); 702 else if (!strcmp(name, "unmap")) 703 return unmap_callback(args); 704 else if (!strcmp(name, "translate")) 705 return translate_callback(args); 706 else if (!strcmp(name, "alloc-real-mem")) 707 return alloc_real_mem_callback(args); 708 709 return OF_FAILED; 710 } 711 712 713 extern "C" status_t 714 arch_set_callback(void) 715 { 716 // set OpenFirmware callbacks - it will ask us for memory after that 717 // instead of maintaining it itself 718 719 void *oldCallback = NULL; 720 if (of_call_client_function("set-callback", 1, 1, &callback, &oldCallback) 721 == OF_FAILED) { 722 dprintf("Error: OpenFirmware set-callback failed\n"); 723 return B_ERROR; 724 } 725 TRACE("old callback = %p; new callback = %p\n", oldCallback, callback); 726 727 return B_OK; 728 } 729 730 731 extern "C" status_t 732 arch_mmu_init(void) 733 { 734 // get map of physical memory (fill in kernel_args structure) 735 736 size_t total; 737 if (find_physical_memory_ranges(total) != B_OK) { 738 dprintf("Error: could not find physical memory ranges!\n"); 739 return B_ERROR; 740 } 741 dprintf("total physical memory = %" B_PRId32 "MB\n", total / (1024 * 1024)); 742 743 // get OpenFirmware's current page table 744 745 page_table_entry_group *oldTable; 746 page_table_entry_group *table; 747 size_t tableSize; 748 ppc_get_page_table(&table, &tableSize); 749 750 oldTable = table; 751 752 bool realMode = false; 753 754 // TODO: read these values out of the OF settings 755 // NOTE: I've only ever seen -1 (0xffffffff) for these values in 756 // OpenFirmware.. even after loading the bootloader -- Alex 757 addr_t realBase = 0; 758 addr_t realSize = 0x400000; 759 760 // can we just keep the page table? 761 size_t suggestedTableSize = suggested_page_table_size(total); 762 dprintf("suggested page table size = %" B_PRIuSIZE "\n", 763 suggestedTableSize); 764 if (tableSize < suggestedTableSize) { 765 // nah, we need a new one! 766 dprintf("need new page table, size = %" B_PRIuSIZE "!\n", 767 suggestedTableSize); 768 table = (page_table_entry_group *)of_claim(NULL, suggestedTableSize, 769 suggestedTableSize); 770 // KERNEL_BASE would be better as virtual address, but 771 // at least with Apple's OpenFirmware, it makes no 772 // difference - we will have to remap it later 773 if (table == (void *)OF_FAILED) { 774 panic("Could not allocate new page table " 775 "(size = %" B_PRIuSIZE ")!!\n", suggestedTableSize); 776 return B_NO_MEMORY; 777 } 778 if (table == NULL) { 779 // work-around for the broken Pegasos OpenFirmware 780 dprintf("broken OpenFirmware detected (claim doesn't work)\n"); 781 realMode = true; 782 783 addr_t tableBase = 0; 784 for (int32 i = 0; tableBase < realBase + realSize * 3; i++) { 785 tableBase = suggestedTableSize * i; 786 } 787 788 table = (page_table_entry_group *)tableBase; 789 } 790 791 dprintf("new table at: %p\n", table); 792 sPageTable = table; 793 tableSize = suggestedTableSize; 794 } else { 795 // ToDo: we could check if the page table is much too large 796 // and create a smaller one in this case (in order to save 797 // memory). 798 sPageTable = table; 799 } 800 801 sPageTableHashMask = tableSize / sizeof(page_table_entry_group) - 1; 802 if (sPageTable != oldTable) 803 memset(sPageTable, 0, tableSize); 804 805 // turn off address translation via the page table/segment mechanism, 806 // identity map the first 256 MB (where our code/data reside) 807 808 dprintf("MSR: %p\n", (void *)get_msr()); 809 810 #if 0 811 block_address_translation bat; 812 813 bat.length = BAT_LENGTH_256MB; 814 bat.kernel_valid = true; 815 bat.memory_coherent = true; 816 bat.protection = BAT_READ_WRITE; 817 818 set_ibat0(&bat); 819 set_dbat0(&bat); 820 isync(); 821 #endif 822 823 // initialize segment descriptors, but don't set the registers 824 // until we're about to take over the page table - we're mapping 825 // pages into our table using these values 826 827 for (int32 i = 0; i < 16; i++) 828 sSegments[i].virtual_segment_id = i; 829 830 // find already allocated ranges of physical memory 831 // and the virtual address space 832 833 page_table_entry_group *physicalTable = NULL; 834 void *exceptionHandlers = (void *)-1; 835 if (find_allocated_ranges(oldTable, table, &physicalTable, 836 &exceptionHandlers) != B_OK) { 837 dprintf("Error: find_allocated_ranges() failed\n"); 838 return B_ERROR; 839 } 840 841 #if 0 842 block_address_translation bats[8]; 843 getibats(bats); 844 for (int32 i = 0; i < 8; i++) { 845 printf("page index %u, length %u, ppn %u\n", bats[i].page_index, 846 bats[i].length, bats[i].physical_block_number); 847 } 848 #endif 849 850 if (physicalTable == NULL) { 851 dprintf("%s: Didn't find physical address of page table\n", __func__); 852 if (!realMode) 853 return B_ERROR; 854 855 // Pegasos work-around 856 #if 0 857 map_range((void *)realBase, (void *)realBase, 858 realSize * 2, PAGE_READ_WRITE); 859 map_range((void *)(total - realSize), (void *)(total - realSize), 860 realSize, PAGE_READ_WRITE); 861 map_range((void *)table, (void *)table, tableSize, PAGE_READ_WRITE); 862 #endif 863 insert_physical_allocated_range(realBase, realSize * 2); 864 insert_virtual_allocated_range(realBase, realSize * 2); 865 insert_physical_allocated_range(total - realSize, realSize); 866 insert_virtual_allocated_range(total - realSize, realSize); 867 insert_physical_allocated_range((addr_t)table, tableSize); 868 insert_virtual_allocated_range((addr_t)table, tableSize); 869 870 // QEMU OpenHackware work-around 871 insert_physical_allocated_range(0x05800000, 0x06000000 - 0x05800000); 872 insert_virtual_allocated_range(0x05800000, 0x06000000 - 0x05800000); 873 874 physicalTable = table; 875 } 876 877 if (exceptionHandlers == (void *)-1) { 878 // TODO: create mapping for the exception handlers 879 dprintf("Error: no mapping for the exception handlers!\n"); 880 } 881 882 // Set the Open Firmware memory callback. From now on the Open Firmware 883 // will ask us for memory. 884 arch_set_callback(); 885 886 // set up new page table and turn on translation again 887 888 for (int32 i = 0; i < 16; i++) { 889 ppc_set_segment_register((void *)(i * 0x10000000), sSegments[i]); 890 // one segment describes 256 MB of memory 891 } 892 893 ppc_set_page_table(physicalTable, tableSize); 894 invalidate_tlb(); 895 896 if (!realMode) { 897 // clear BATs 898 reset_ibats(); 899 reset_dbats(); 900 ppc_sync(); 901 isync(); 902 } 903 904 set_msr(MSR_MACHINE_CHECK_ENABLED | MSR_FP_AVAILABLE 905 | MSR_INST_ADDRESS_TRANSLATION | MSR_DATA_ADDRESS_TRANSLATION); 906 907 // set kernel args 908 909 dprintf("virt_allocated: %" B_PRIu32 "\n", 910 gKernelArgs.num_virtual_allocated_ranges); 911 dprintf("phys_allocated: %" B_PRIu32 "\n", 912 gKernelArgs.num_physical_allocated_ranges); 913 dprintf("phys_memory: %" B_PRIu32 "\n", 914 gKernelArgs.num_physical_memory_ranges); 915 916 gKernelArgs.arch_args.page_table.start = (addr_t)sPageTable; 917 gKernelArgs.arch_args.page_table.size = tableSize; 918 919 gKernelArgs.arch_args.exception_handlers.start = (addr_t)exceptionHandlers; 920 gKernelArgs.arch_args.exception_handlers.size = B_PAGE_SIZE; 921 922 return B_OK; 923 } 924 925