1 /* 2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001, Travis Geiselbrecht. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 /* (bonefish) Some explanatory words on how address translation is implemented 11 for the 32 bit PPC architecture. 12 13 I use the address type nomenclature as used in the PPC architecture 14 specs, i.e. 15 - effective address: An address as used by program instructions, i.e. 16 that's what elsewhere (e.g. in the VM implementation) is called 17 virtual address. 18 - virtual address: An intermediate address computed from the effective 19 address via the segment registers. 20 - physical address: An address referring to physical storage. 21 22 The hardware translates an effective address to a physical address using 23 either of two mechanisms: 1) Block Address Translation (BAT) or 24 2) segment + page translation. The first mechanism does this directly 25 using two sets (for data/instructions) of special purpose registers. 26 The latter mechanism is of more relevance here, though: 27 28 effective address (32 bit): [ 0 ESID 3 | 4 PIX 19 | 20 Byte 31 ] 29 | | | 30 (segment registers) | | 31 | | | 32 virtual address (52 bit): [ 0 VSID 23 | 24 PIX 39 | 40 Byte 51 ] 33 [ 0 VPN 39 | 40 Byte 51 ] 34 | | 35 (page table) | 36 | | 37 physical address (32 bit): [ 0 PPN 19 | 20 Byte 31 ] 38 39 40 ESID: Effective Segment ID 41 VSID: Virtual Segment ID 42 PIX: Page Index 43 VPN: Virtual Page Number 44 PPN: Physical Page Number 45 46 47 Unlike on x86 we can't just switch the context to another team by just 48 setting a register to another page directory, since we only have one 49 page table containing both kernel and user address mappings. Instead we 50 map the effective address space of kernel and *all* teams 51 non-intersectingly into the virtual address space (which fortunately is 52 20 bits wider), and use the segment registers to select the section of 53 the virtual address space for the current team. Half of the 16 segment 54 registers (8 - 15) map the kernel addresses, so they remain unchanged. 55 56 The range of the virtual address space a team's effective address space 57 is mapped to is defined by its PPCVMTranslationMap::fVSIDBase, 58 which is the first of the 8 successive VSID values used for the team. 59 60 Which fVSIDBase values are already taken is defined by the set bits in 61 the bitmap sVSIDBaseBitmap. 62 63 64 TODO: 65 * If we want to continue to use the OF services, we would need to add 66 its address mappings to the kernel space. Unfortunately some stuff 67 (especially RAM) is mapped in an address range without the kernel 68 address space. We probably need to map those into each team's address 69 space as kernel read/write areas. 70 * The current locking scheme is insufficient. The page table is a resource 71 shared by all teams. We need to synchronize access to it. Probably via a 72 spinlock. 73 */ 74 75 #include <arch/vm_translation_map.h> 76 77 #include <stdlib.h> 78 79 #include <KernelExport.h> 80 81 #include <arch/cpu.h> 82 #include <arch_mmu.h> 83 #include <boot/kernel_args.h> 84 #include <int.h> 85 #include <kernel.h> 86 #include <slab/Slab.h> 87 #include <vm/vm.h> 88 #include <vm/vm_page.h> 89 #include <vm/vm_priv.h> 90 #include <vm/VMAddressSpace.h> 91 #include <vm/VMCache.h> 92 93 #include <util/AutoLock.h> 94 95 #include "generic_vm_physical_page_mapper.h" 96 #include "generic_vm_physical_page_ops.h" 97 #include "GenericVMPhysicalPageMapper.h" 98 99 100 static struct page_table_entry_group *sPageTable; 101 static size_t sPageTableSize; 102 static uint32 sPageTableHashMask; 103 static area_id sPageTableArea; 104 105 // 64 MB of iospace 106 #define IOSPACE_SIZE (64*1024*1024) 107 // We only have small (4 KB) pages. The only reason for choosing greater chunk 108 // size is to keep the waste of memory limited, since the generic page mapper 109 // allocates structures per physical/virtual chunk. 110 // TODO: Implement a page mapper more suitable for small pages! 111 #define IOSPACE_CHUNK_SIZE (16 * B_PAGE_SIZE) 112 113 static addr_t sIOSpaceBase; 114 115 static GenericVMPhysicalPageMapper sPhysicalPageMapper; 116 117 // The VSID is a 24 bit number. The lower three bits are defined by the 118 // (effective) segment number, which leaves us with a 21 bit space of 119 // VSID bases (= 2 * 1024 * 1024). 120 #define MAX_VSID_BASES (PAGE_SIZE * 8) 121 static uint32 sVSIDBaseBitmap[MAX_VSID_BASES / (sizeof(uint32) * 8)]; 122 static spinlock sVSIDBaseBitmapLock; 123 124 #define VSID_BASE_SHIFT 3 125 #define VADDR_TO_VSID(vsidBase, vaddr) (vsidBase + ((vaddr) >> 28)) 126 127 128 struct PPCVMTranslationMap : VMTranslationMap { 129 PPCVMTranslationMap(); 130 virtual ~PPCVMTranslationMap(); 131 132 status_t Init(bool kernel); 133 134 inline int VSIDBase() const { return fVSIDBase; } 135 136 page_table_entry* LookupPageTableEntry(addr_t virtualAddress); 137 bool RemovePageTableEntry(addr_t virtualAddress); 138 139 virtual bool Lock(); 140 virtual void Unlock(); 141 142 virtual addr_t MappedSize() const; 143 virtual size_t MaxPagesNeededToMap(addr_t start, 144 addr_t end) const; 145 146 virtual status_t Map(addr_t virtualAddress, 147 phys_addr_t physicalAddress, 148 uint32 attributes, uint32 memoryType, 149 vm_page_reservation* reservation); 150 virtual status_t Unmap(addr_t start, addr_t end); 151 152 virtual status_t UnmapPage(VMArea* area, addr_t address, 153 bool updatePageQueue); 154 155 virtual status_t Query(addr_t virtualAddress, 156 phys_addr_t* _physicalAddress, 157 uint32* _flags); 158 virtual status_t QueryInterrupt(addr_t virtualAddress, 159 phys_addr_t* _physicalAddress, 160 uint32* _flags); 161 162 virtual status_t Protect(addr_t base, addr_t top, 163 uint32 attributes, uint32 memoryType); 164 virtual status_t ClearFlags(addr_t virtualAddress, 165 uint32 flags); 166 167 virtual bool ClearAccessedAndModified( 168 VMArea* area, addr_t address, 169 bool unmapIfUnaccessed, 170 bool& _modified); 171 172 virtual void Flush(); 173 174 protected: 175 int fVSIDBase; 176 }; 177 178 179 void 180 ppc_translation_map_change_asid(VMTranslationMap *map) 181 { 182 // this code depends on the kernel being at 0x80000000, fix if we change that 183 #if KERNEL_BASE != 0x80000000 184 #error fix me 185 #endif 186 int vsidBase = static_cast<PPCVMTranslationMap*>(map)->VSIDBase(); 187 188 isync(); // synchronize context 189 asm("mtsr 0,%0" : : "g"(vsidBase)); 190 asm("mtsr 1,%0" : : "g"(vsidBase + 1)); 191 asm("mtsr 2,%0" : : "g"(vsidBase + 2)); 192 asm("mtsr 3,%0" : : "g"(vsidBase + 3)); 193 asm("mtsr 4,%0" : : "g"(vsidBase + 4)); 194 asm("mtsr 5,%0" : : "g"(vsidBase + 5)); 195 asm("mtsr 6,%0" : : "g"(vsidBase + 6)); 196 asm("mtsr 7,%0" : : "g"(vsidBase + 7)); 197 isync(); // synchronize context 198 } 199 200 201 static void 202 fill_page_table_entry(page_table_entry *entry, uint32 virtualSegmentID, 203 addr_t virtualAddress, phys_addr_t physicalAddress, uint8 protection, 204 uint32 memoryType, bool secondaryHash) 205 { 206 // lower 32 bit - set at once 207 entry->physical_page_number = physicalAddress / B_PAGE_SIZE; 208 entry->_reserved0 = 0; 209 entry->referenced = false; 210 entry->changed = false; 211 entry->write_through = (memoryType == B_MTR_UC) || (memoryType == B_MTR_WT); 212 entry->caching_inhibited = (memoryType == B_MTR_UC); 213 entry->memory_coherent = false; 214 entry->guarded = false; 215 entry->_reserved1 = 0; 216 entry->page_protection = protection & 0x3; 217 eieio(); 218 // we need to make sure that the lower 32 bit were 219 // already written when the entry becomes valid 220 221 // upper 32 bit 222 entry->virtual_segment_id = virtualSegmentID; 223 entry->secondary_hash = secondaryHash; 224 entry->abbr_page_index = (virtualAddress >> 22) & 0x3f; 225 entry->valid = true; 226 227 ppc_sync(); 228 } 229 230 231 page_table_entry * 232 PPCVMTranslationMap::LookupPageTableEntry(addr_t virtualAddress) 233 { 234 // lookup the vsid based off the va 235 uint32 virtualSegmentID = VADDR_TO_VSID(fVSIDBase, virtualAddress); 236 237 // dprintf("vm_translation_map.lookup_page_table_entry: vsid %ld, va 0x%lx\n", virtualSegmentID, virtualAddress); 238 239 // Search for the page table entry using the primary hash value 240 241 uint32 hash = page_table_entry::PrimaryHash(virtualSegmentID, virtualAddress); 242 page_table_entry_group *group = &sPageTable[hash & sPageTableHashMask]; 243 244 for (int i = 0; i < 8; i++) { 245 page_table_entry *entry = &group->entry[i]; 246 247 if (entry->virtual_segment_id == virtualSegmentID 248 && entry->secondary_hash == false 249 && entry->abbr_page_index == ((virtualAddress >> 22) & 0x3f)) 250 return entry; 251 } 252 253 // didn't find it, try the secondary hash value 254 255 hash = page_table_entry::SecondaryHash(hash); 256 group = &sPageTable[hash & sPageTableHashMask]; 257 258 for (int i = 0; i < 8; i++) { 259 page_table_entry *entry = &group->entry[i]; 260 261 if (entry->virtual_segment_id == virtualSegmentID 262 && entry->secondary_hash == true 263 && entry->abbr_page_index == ((virtualAddress >> 22) & 0x3f)) 264 return entry; 265 } 266 267 return NULL; 268 } 269 270 271 bool 272 PPCVMTranslationMap::RemovePageTableEntry(addr_t virtualAddress) 273 { 274 page_table_entry *entry = LookupPageTableEntry(virtualAddress); 275 if (entry == NULL) 276 return false; 277 278 entry->valid = 0; 279 ppc_sync(); 280 tlbie(virtualAddress); 281 eieio(); 282 tlbsync(); 283 ppc_sync(); 284 285 return true; 286 } 287 288 289 static status_t 290 map_iospace_chunk(addr_t va, phys_addr_t pa, uint32 flags) 291 { 292 pa &= ~(B_PAGE_SIZE - 1); // make sure it's page aligned 293 va &= ~(B_PAGE_SIZE - 1); // make sure it's page aligned 294 if (va < sIOSpaceBase || va >= (sIOSpaceBase + IOSPACE_SIZE)) 295 panic("map_iospace_chunk: passed invalid va 0x%lx\n", va); 296 297 // map the pages 298 return ppc_map_address_range(va, pa, IOSPACE_CHUNK_SIZE); 299 } 300 301 302 // #pragma mark - 303 304 305 PPCVMTranslationMap::PPCVMTranslationMap() 306 { 307 } 308 309 310 PPCVMTranslationMap::~PPCVMTranslationMap() 311 { 312 if (fMapCount > 0) { 313 panic("vm_translation_map.destroy_tmap: map %p has positive map count %ld\n", 314 this, fMapCount); 315 } 316 317 // mark the vsid base not in use 318 int baseBit = fVSIDBase >> VSID_BASE_SHIFT; 319 atomic_and((vint32 *)&sVSIDBaseBitmap[baseBit / 32], 320 ~(1 << (baseBit % 32))); 321 } 322 323 324 status_t 325 PPCVMTranslationMap::Init(bool kernel) 326 { 327 cpu_status state = disable_interrupts(); 328 acquire_spinlock(&sVSIDBaseBitmapLock); 329 330 // allocate a VSID base for this one 331 if (kernel) { 332 // The boot loader has set up the segment registers for identical 333 // mapping. Two VSID bases are reserved for the kernel: 0 and 8. The 334 // latter one for mapping the kernel address space (0x80000000...), the 335 // former one for the lower addresses required by the Open Firmware 336 // services. 337 fVSIDBase = 0; 338 sVSIDBaseBitmap[0] |= 0x3; 339 } else { 340 int i = 0; 341 342 while (i < MAX_VSID_BASES) { 343 if (sVSIDBaseBitmap[i / 32] == 0xffffffff) { 344 i += 32; 345 continue; 346 } 347 if ((sVSIDBaseBitmap[i / 32] & (1 << (i % 32))) == 0) { 348 // we found it 349 sVSIDBaseBitmap[i / 32] |= 1 << (i % 32); 350 break; 351 } 352 i++; 353 } 354 if (i >= MAX_VSID_BASES) 355 panic("vm_translation_map_create: out of VSID bases\n"); 356 fVSIDBase = i << VSID_BASE_SHIFT; 357 } 358 359 release_spinlock(&sVSIDBaseBitmapLock); 360 restore_interrupts(state); 361 362 return B_OK; 363 } 364 365 366 bool 367 PPCVMTranslationMap::Lock() 368 { 369 recursive_lock_lock(&fLock); 370 return true; 371 } 372 373 374 void 375 PPCVMTranslationMap::Unlock() 376 { 377 recursive_lock_unlock(&fLock); 378 } 379 380 381 size_t 382 PPCVMTranslationMap::MaxPagesNeededToMap(addr_t start, addr_t end) const 383 { 384 return 0; 385 } 386 387 388 status_t 389 PPCVMTranslationMap::Map(addr_t virtualAddress, phys_addr_t physicalAddress, 390 uint32 attributes, uint32 memoryType, vm_page_reservation* reservation) 391 { 392 // lookup the vsid based off the va 393 uint32 virtualSegmentID = VADDR_TO_VSID(fVSIDBase, virtualAddress); 394 uint32 protection = 0; 395 396 // ToDo: check this 397 // all kernel mappings are R/W to supervisor code 398 if (attributes & (B_READ_AREA | B_WRITE_AREA)) 399 protection = (attributes & B_WRITE_AREA) ? PTE_READ_WRITE : PTE_READ_ONLY; 400 401 //dprintf("vm_translation_map.map_tmap: vsid %d, pa 0x%lx, va 0x%lx\n", vsid, pa, va); 402 403 // Search for a free page table slot using the primary hash value 404 405 uint32 hash = page_table_entry::PrimaryHash(virtualSegmentID, virtualAddress); 406 page_table_entry_group *group = &sPageTable[hash & sPageTableHashMask]; 407 408 for (int i = 0; i < 8; i++) { 409 page_table_entry *entry = &group->entry[i]; 410 411 if (entry->valid) 412 continue; 413 414 fill_page_table_entry(entry, virtualSegmentID, virtualAddress, physicalAddress, 415 protection, memoryType, false); 416 fMapCount++; 417 return B_OK; 418 } 419 420 // Didn't found one, try the secondary hash value 421 422 hash = page_table_entry::SecondaryHash(hash); 423 group = &sPageTable[hash & sPageTableHashMask]; 424 425 for (int i = 0; i < 8; i++) { 426 page_table_entry *entry = &group->entry[i]; 427 428 if (entry->valid) 429 continue; 430 431 fill_page_table_entry(entry, virtualSegmentID, virtualAddress, physicalAddress, 432 protection, memoryType, false); 433 fMapCount++; 434 return B_OK; 435 } 436 437 panic("vm_translation_map.map_tmap: hash table full\n"); 438 return B_ERROR; 439 } 440 441 442 status_t 443 PPCVMTranslationMap::Unmap(addr_t start, addr_t end) 444 { 445 page_table_entry *entry; 446 447 start = ROUNDDOWN(start, B_PAGE_SIZE); 448 end = ROUNDUP(end, B_PAGE_SIZE); 449 450 // dprintf("vm_translation_map.unmap_tmap: start 0x%lx, end 0x%lx\n", start, end); 451 452 while (start < end) { 453 if (RemovePageTableEntry(start)) 454 fMapCount--; 455 456 start += B_PAGE_SIZE; 457 } 458 459 return B_OK; 460 } 461 462 463 status_t 464 PPCVMTranslationMap::UnmapPage(VMArea* area, addr_t address, 465 bool updatePageQueue) 466 { 467 ASSERT(address % B_PAGE_SIZE == 0); 468 469 RecursiveLocker locker(fLock); 470 471 if (area->cache_type == CACHE_TYPE_DEVICE) { 472 if (!RemovePageTableEntry(address)) 473 return B_ENTRY_NOT_FOUND; 474 475 fMapCount--; 476 return B_OK; 477 } 478 479 page_table_entry* entry = LookupPageTableEntry(address); 480 if (entry == NULL) 481 return B_ENTRY_NOT_FOUND; 482 483 page_num_t pageNumber = entry->physical_page_number; 484 bool accessed = entry->referenced; 485 bool modified = entry->changed; 486 487 RemovePageTableEntry(address); 488 489 fMapCount--; 490 491 locker.Detach(); 492 // PageUnmapped() will unlock for us 493 494 PageUnmapped(area, pageNumber, accessed, modified, updatePageQueue); 495 496 return B_OK; 497 } 498 499 500 status_t 501 PPCVMTranslationMap::Query(addr_t va, phys_addr_t *_outPhysical, 502 uint32 *_outFlags) 503 { 504 page_table_entry *entry; 505 506 // default the flags to not present 507 *_outFlags = 0; 508 *_outPhysical = 0; 509 510 entry = LookupPageTableEntry(va); 511 if (entry == NULL) 512 return B_NO_ERROR; 513 514 // ToDo: check this! 515 if (IS_KERNEL_ADDRESS(va)) 516 *_outFlags |= B_KERNEL_READ_AREA | (entry->page_protection == PTE_READ_ONLY ? 0 : B_KERNEL_WRITE_AREA); 517 else 518 *_outFlags |= B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | (entry->page_protection == PTE_READ_ONLY ? 0 : B_WRITE_AREA); 519 520 *_outFlags |= entry->changed ? PAGE_MODIFIED : 0; 521 *_outFlags |= entry->referenced ? PAGE_ACCESSED : 0; 522 *_outFlags |= entry->valid ? PAGE_PRESENT : 0; 523 524 *_outPhysical = entry->physical_page_number * B_PAGE_SIZE; 525 526 return B_OK; 527 } 528 529 530 status_t 531 PPCVMTranslationMap::QueryInterrupt(addr_t virtualAddress, 532 phys_addr_t* _physicalAddress, uint32* _flags) 533 { 534 return PPCVMTranslationMap::Query(virtualAddress, _physicalAddress, _flags); 535 } 536 537 538 addr_t 539 PPCVMTranslationMap::MappedSize() const 540 { 541 return fMapCount; 542 } 543 544 545 status_t 546 PPCVMTranslationMap::Protect(addr_t base, addr_t top, uint32 attributes, 547 uint32 memoryType) 548 { 549 // XXX finish 550 return B_ERROR; 551 } 552 553 554 status_t 555 PPCVMTranslationMap::ClearFlags(addr_t virtualAddress, uint32 flags) 556 { 557 page_table_entry *entry = LookupPageTableEntry(virtualAddress); 558 if (entry == NULL) 559 return B_NO_ERROR; 560 561 bool modified = false; 562 563 // clear the bits 564 if (flags & PAGE_MODIFIED && entry->changed) { 565 entry->changed = false; 566 modified = true; 567 } 568 if (flags & PAGE_ACCESSED && entry->referenced) { 569 entry->referenced = false; 570 modified = true; 571 } 572 573 // synchronize 574 if (modified) { 575 tlbie(virtualAddress); 576 eieio(); 577 tlbsync(); 578 ppc_sync(); 579 } 580 581 return B_OK; 582 } 583 584 585 bool 586 PPCVMTranslationMap::ClearAccessedAndModified(VMArea* area, addr_t address, 587 bool unmapIfUnaccessed, bool& _modified) 588 { 589 // TODO: Implement for real! ATM this is just an approximation using 590 // Query(), ClearFlags(), and UnmapPage(). See below! 591 592 RecursiveLocker locker(fLock); 593 594 uint32 flags; 595 phys_addr_t physicalAddress; 596 if (Query(address, &physicalAddress, &flags) != B_OK 597 || (flags & PAGE_PRESENT) == 0) { 598 return false; 599 } 600 601 _modified = (flags & PAGE_MODIFIED) != 0; 602 603 if ((flags & (PAGE_ACCESSED | PAGE_MODIFIED)) != 0) 604 ClearFlags(address, flags & (PAGE_ACCESSED | PAGE_MODIFIED)); 605 606 if ((flags & PAGE_ACCESSED) != 0) 607 return true; 608 609 if (!unmapIfUnaccessed) 610 return false; 611 612 locker.Unlock(); 613 614 UnmapPage(area, address, false); 615 // TODO: Obvious race condition: Between querying and unmapping the 616 // page could have been accessed. We try to compensate by considering 617 // vm_page::{accessed,modified} (which would have been updated by 618 // UnmapPage()) below, but that doesn't quite match the required 619 // semantics of the method. 620 621 vm_page* page = vm_lookup_page(physicalAddress / B_PAGE_SIZE); 622 if (page == NULL) 623 return false; 624 625 _modified |= page->modified; 626 627 return page->accessed; 628 } 629 630 631 void 632 PPCVMTranslationMap::Flush() 633 { 634 // TODO: arch_cpu_global_TLB_invalidate() is extremely expensive and doesn't 635 // even cut it here. We are supposed to invalidate all TLB entries for this 636 // map on all CPUs. We should loop over the virtual pages and invoke tlbie 637 // instead (which marks the entry invalid on all CPUs). 638 arch_cpu_global_TLB_invalidate(); 639 } 640 641 642 static status_t 643 get_physical_page_tmap(phys_addr_t physicalAddress, addr_t *_virtualAddress, 644 void **handle) 645 { 646 return generic_get_physical_page(physicalAddress, _virtualAddress, 0); 647 } 648 649 650 static status_t 651 put_physical_page_tmap(addr_t virtualAddress, void *handle) 652 { 653 return generic_put_physical_page(virtualAddress); 654 } 655 656 657 // #pragma mark - 658 // VM API 659 660 661 status_t 662 arch_vm_translation_map_create_map(bool kernel, VMTranslationMap** _map) 663 { 664 PPCVMTranslationMap* map = new(std::nothrow) PPCVMTranslationMap; 665 if (map == NULL) 666 return B_NO_MEMORY; 667 668 status_t error = map->Init(kernel); 669 if (error != B_OK) { 670 delete map; 671 return error; 672 } 673 674 *_map = map; 675 return B_OK; 676 } 677 678 679 status_t 680 arch_vm_translation_map_init(kernel_args *args, 681 VMPhysicalPageMapper** _physicalPageMapper) 682 { 683 sPageTable = (page_table_entry_group *)args->arch_args.page_table.start; 684 sPageTableSize = args->arch_args.page_table.size; 685 sPageTableHashMask = sPageTableSize / sizeof(page_table_entry_group) - 1; 686 687 // init physical page mapper 688 status_t error = generic_vm_physical_page_mapper_init(args, 689 map_iospace_chunk, &sIOSpaceBase, IOSPACE_SIZE, IOSPACE_CHUNK_SIZE); 690 if (error != B_OK) 691 return error; 692 693 new(&sPhysicalPageMapper) GenericVMPhysicalPageMapper; 694 695 *_physicalPageMapper = &sPhysicalPageMapper; 696 return B_OK; 697 } 698 699 700 status_t 701 arch_vm_translation_map_init_post_area(kernel_args *args) 702 { 703 // If the page table doesn't lie within the kernel address space, we 704 // remap it. 705 if (!IS_KERNEL_ADDRESS(sPageTable)) { 706 addr_t newAddress = (addr_t)sPageTable; 707 status_t error = ppc_remap_address_range(&newAddress, sPageTableSize, 708 false); 709 if (error != B_OK) { 710 panic("arch_vm_translation_map_init_post_area(): Failed to remap " 711 "the page table!"); 712 return error; 713 } 714 715 // set the new page table address 716 addr_t oldVirtualBase = (addr_t)(sPageTable); 717 sPageTable = (page_table_entry_group*)newAddress; 718 719 // unmap the old pages 720 ppc_unmap_address_range(oldVirtualBase, sPageTableSize); 721 722 // TODO: We should probably map the page table via BAT. It is relatively large, 723 // and due to being a hash table the access patterns might look sporadic, which 724 // certainly isn't to the liking of the TLB. 725 } 726 727 // create an area to cover the page table 728 sPageTableArea = create_area("page_table", (void **)&sPageTable, B_EXACT_ADDRESS, 729 sPageTableSize, B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 730 731 // init physical page mapper 732 status_t error = generic_vm_physical_page_mapper_init_post_area(args); 733 if (error != B_OK) 734 return error; 735 736 return B_OK; 737 } 738 739 740 status_t 741 arch_vm_translation_map_init_post_sem(kernel_args *args) 742 { 743 // init physical page mapper 744 return generic_vm_physical_page_mapper_init_post_sem(args); 745 } 746 747 748 /** Directly maps a page without having knowledge of any kernel structures. 749 * Used only during VM setup. 750 * It currently ignores the "attributes" parameter and sets all pages 751 * read/write. 752 */ 753 754 status_t 755 arch_vm_translation_map_early_map(kernel_args *ka, addr_t virtualAddress, 756 phys_addr_t physicalAddress, uint8 attributes, 757 phys_addr_t (*get_free_page)(kernel_args *)) 758 { 759 uint32 virtualSegmentID = get_sr((void *)virtualAddress) & 0xffffff; 760 761 uint32 hash = page_table_entry::PrimaryHash(virtualSegmentID, (uint32)virtualAddress); 762 page_table_entry_group *group = &sPageTable[hash & sPageTableHashMask]; 763 764 for (int32 i = 0; i < 8; i++) { 765 // 8 entries in a group 766 if (group->entry[i].valid) 767 continue; 768 769 fill_page_table_entry(&group->entry[i], virtualSegmentID, 770 virtualAddress, physicalAddress, PTE_READ_WRITE, 0, false); 771 return B_OK; 772 } 773 774 hash = page_table_entry::SecondaryHash(hash); 775 group = &sPageTable[hash & sPageTableHashMask]; 776 777 for (int32 i = 0; i < 8; i++) { 778 if (group->entry[i].valid) 779 continue; 780 781 fill_page_table_entry(&group->entry[i], virtualSegmentID, 782 virtualAddress, physicalAddress, PTE_READ_WRITE, 0, true); 783 return B_OK; 784 } 785 786 return B_ERROR; 787 } 788 789 790 // XXX currently assumes this translation map is active 791 792 status_t 793 arch_vm_translation_map_early_query(addr_t va, phys_addr_t *out_physical) 794 { 795 //PANIC_UNIMPLEMENTED(); 796 panic("vm_translation_map_quick_query(): not yet implemented\n"); 797 return B_OK; 798 } 799 800 801 // #pragma mark - 802 803 804 status_t 805 ppc_map_address_range(addr_t virtualAddress, phys_addr_t physicalAddress, 806 size_t size) 807 { 808 addr_t virtualEnd = ROUNDUP(virtualAddress + size, B_PAGE_SIZE); 809 virtualAddress = ROUNDDOWN(virtualAddress, B_PAGE_SIZE); 810 physicalAddress = ROUNDDOWN(physicalAddress, B_PAGE_SIZE); 811 812 VMAddressSpace *addressSpace = VMAddressSpace::Kernel(); 813 PPCVMTranslationMap* map = static_cast<PPCVMTranslationMap*>( 814 addressSpace->TranslationMap()); 815 816 vm_page_reservation reservation; 817 vm_page_reserve_pages(&reservation, 0, VM_PRIORITY_USER); 818 // We don't need any pages for mapping. 819 820 // map the pages 821 for (; virtualAddress < virtualEnd; 822 virtualAddress += B_PAGE_SIZE, physicalAddress += B_PAGE_SIZE) { 823 status_t error = map->Map(virtualAddress, physicalAddress, 824 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 0, &reservation); 825 if (error != B_OK) 826 return error; 827 } 828 829 return B_OK; 830 } 831 832 833 void 834 ppc_unmap_address_range(addr_t virtualAddress, size_t size) 835 { 836 addr_t virtualEnd = ROUNDUP(virtualAddress + size, B_PAGE_SIZE); 837 virtualAddress = ROUNDDOWN(virtualAddress, B_PAGE_SIZE); 838 839 VMAddressSpace *addressSpace = VMAddressSpace::Kernel(); 840 841 PPCVMTranslationMap* map = static_cast<PPCVMTranslationMap*>( 842 addressSpace->TranslationMap()); 843 for (0; virtualAddress < virtualEnd; virtualAddress += B_PAGE_SIZE) 844 map->RemovePageTableEntry(virtualAddress); 845 } 846 847 848 status_t 849 ppc_remap_address_range(addr_t *_virtualAddress, size_t size, bool unmap) 850 { 851 addr_t virtualAddress = ROUNDDOWN(*_virtualAddress, B_PAGE_SIZE); 852 size = ROUNDUP(*_virtualAddress + size - virtualAddress, B_PAGE_SIZE); 853 854 VMAddressSpace *addressSpace = VMAddressSpace::Kernel(); 855 856 // reserve space in the address space 857 void *newAddress = NULL; 858 status_t error = vm_reserve_address_range(addressSpace->ID(), &newAddress, 859 B_ANY_KERNEL_ADDRESS, size, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 860 if (error != B_OK) 861 return error; 862 863 // get the area's first physical page 864 PPCVMTranslationMap* map = static_cast<PPCVMTranslationMap*>( 865 addressSpace->TranslationMap()); 866 page_table_entry *entry = map->LookupPageTableEntry(virtualAddress); 867 if (!entry) 868 return B_ERROR; 869 phys_addr_t physicalBase = (phys_addr_t)entry->physical_page_number << 12; 870 871 // map the pages 872 error = ppc_map_address_range((addr_t)newAddress, physicalBase, size); 873 if (error != B_OK) 874 return error; 875 876 *_virtualAddress = (addr_t)newAddress; 877 878 // unmap the old pages 879 if (unmap) 880 ppc_unmap_address_range(virtualAddress, size); 881 882 return B_OK; 883 } 884 885 886 bool 887 arch_vm_translation_map_is_kernel_page_accessible(addr_t virtualAddress, 888 uint32 protection) 889 { 890 VMAddressSpace *addressSpace = VMAddressSpace::Kernel(); 891 892 PPCVMTranslationMap* map = static_cast<PPCVMTranslationMap*>( 893 addressSpace->TranslationMap()); 894 895 phys_addr_t physicalAddress; 896 uint32 flags; 897 if (map->Query(virtualAddress, &physicalAddress, &flags) != B_OK) 898 return false; 899 900 if ((flags & PAGE_PRESENT) == 0) 901 return false; 902 903 // present means kernel-readable, so check for writable 904 return (protection & B_KERNEL_WRITE_AREA) == 0 905 || (flags & B_KERNEL_WRITE_AREA) != 0; 906 } 907