1/* 2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7/*! Implementation of a physical page mapping strategy (PhysicalPageMapper, 8 TranslationMapPhysicalPageMapper) suitable for machines with a lot of 9 memory, i.e. more than we can afford to completely map into the kernel 10 address space. 11 12 m68k: WRITEME: we use more than 1 pgtable/page 13 x86: 14 We allocate a single page table (one page) that can map 1024 pages and 15 a corresponding virtual address space region (4 MB). Each of those 1024 16 slots can map a physical page. We reserve a fixed amount of slots per CPU. 17 They will be used for physical operations on that CPU (memset()/memcpy() 18 and {get,put}_physical_page_current_cpu()). A few slots we reserve for each 19 translation map (TranslationMapPhysicalPageMapper). Those will only be used 20 with the translation map locked, mapping a page table page. The remaining 21 slots remain in the global pool and are given out by get_physical_page(). 22 23 When we run out of slots, we allocate another page table (and virtual 24 address space region). 25*/ 26 27 28#include "paging/m68k_physical_page_mapper_large_memory.h" 29 30#include <new> 31 32#include <AutoDeleter.h> 33 34#include <cpu.h> 35#include <lock.h> 36#include <smp.h> 37#include <util/AutoLock.h> 38#include <util/ThreadAutoLock.h> 39#include <vm/vm.h> 40#include <vm/vm_types.h> 41#include <vm/VMAddressSpace.h> 42 43#include "paging/m68k_physical_page_mapper.h" 44#include "paging/M68KPagingStructures.h" 45#include "paging/M68KVMTranslationMap.h" 46 47 48// The number of slots we reserve per translation map from mapping page tables. 49// One slot would suffice, since the map is locked while mapping a page table, 50// but we re-use several slots on a LRU-basis so that we can keep the mappings 51// a little longer, thus avoiding re-mapping. 52#define SLOTS_PER_TRANSLATION_MAP 4 53 54#define USER_SLOTS_PER_CPU 16 55#define KERNEL_SLOTS_PER_CPU 16 56#define TOTAL_SLOTS_PER_CPU (USER_SLOTS_PER_CPU \ 57 + KERNEL_SLOTS_PER_CPU + 1) 58 // one slot is for use in interrupts 59 60 61using M68KLargePhysicalPageMapper::PhysicalPageSlot; 62using M68KLargePhysicalPageMapper::PhysicalPageSlotPool; 63 64 65class PhysicalPageSlotQueue { 66public: 67 PhysicalPageSlotQueue(); 68 69 inline PhysicalPageSlot* GetSlot(); 70 inline void GetSlots(PhysicalPageSlot*& slot1, 71 PhysicalPageSlot*& slot2); 72 inline void PutSlot(PhysicalPageSlot* slot); 73 inline void PutSlots(PhysicalPageSlot* slot1, 74 PhysicalPageSlot* slot2); 75 76private: 77 PhysicalPageSlot* fSlots; 78 ConditionVariable fFreeSlotCondition; 79 ConditionVariable fFreeSlotsCondition; 80}; 81 82 83struct PhysicalPageOpsCPUData { 84 PhysicalPageSlotQueue user; 85 // Used when copying from/to user memory. This can cause a page fault 86 // which might need to memcpy()/memset() a page when being handled. 87 PhysicalPageSlotQueue kernel; 88 // Used when memset()ing or when memcpy()ing memory non-user memory. 89 PhysicalPageSlot* interruptSlot; 90 91 void Init(); 92 93private: 94 static PhysicalPageSlot* _GetInitialSlot(); 95}; 96 97 98// #pragma mark - 99 100 101class LargeMemoryTranslationMapPhysicalPageMapper 102 : public TranslationMapPhysicalPageMapper { 103public: 104 LargeMemoryTranslationMapPhysicalPageMapper(); 105 virtual ~LargeMemoryTranslationMapPhysicalPageMapper(); 106 107 status_t Init(); 108 109 virtual void Delete(); 110 111 virtual void* GetPageTableAt(phys_addr_t physicalAddress); 112 113private: 114 struct page_slot { 115 PhysicalPageSlot* slot; 116 phys_addr_t physicalAddress; 117 CPUSet valid; 118 }; 119 120 page_slot fSlots[SLOTS_PER_TRANSLATION_MAP]; 121 int32 fSlotCount; // must be a power of 2 122 int32 fNextSlot; 123}; 124 125 126class LargeMemoryPhysicalPageMapper : public M68KPhysicalPageMapper { 127public: 128 LargeMemoryPhysicalPageMapper(); 129 130 status_t Init(kernel_args* args, 131 PhysicalPageSlotPool* initialPool, 132 TranslationMapPhysicalPageMapper*& 133 _kernelPageMapper); 134 135 virtual status_t CreateTranslationMapPhysicalPageMapper( 136 TranslationMapPhysicalPageMapper** _mapper); 137 138 virtual void* InterruptGetPageTableAt( 139 phys_addr_t physicalAddress); 140 141 virtual status_t GetPage(phys_addr_t physicalAddress, 142 addr_t* virtualAddress, void** handle); 143 virtual status_t PutPage(addr_t virtualAddress, void* handle); 144 145 virtual status_t GetPageCurrentCPU(phys_addr_t physicalAddress, 146 addr_t* virtualAddress, void** handle); 147 virtual status_t PutPageCurrentCPU(addr_t virtualAddress, 148 void* handle); 149 150 virtual status_t GetPageDebug(phys_addr_t physicalAddress, 151 addr_t* virtualAddress, void** handle); 152 virtual status_t PutPageDebug(addr_t virtualAddress, 153 void* handle); 154 155 virtual status_t MemsetPhysical(phys_addr_t address, int value, 156 phys_size_t length); 157 virtual status_t MemcpyFromPhysical(void* to, phys_addr_t from, 158 size_t length, bool user); 159 virtual status_t MemcpyToPhysical(phys_addr_t to, 160 const void* from, size_t length, bool user); 161 virtual void MemcpyPhysicalPage(phys_addr_t to, 162 phys_addr_t from); 163 164 status_t GetSlot(bool canWait, 165 PhysicalPageSlot*& slot); 166 void PutSlot(PhysicalPageSlot* slot); 167 168 inline PhysicalPageSlotQueue* GetSlotQueue(int32 cpu, bool user); 169 170private: 171 typedef DoublyLinkedList<PhysicalPageSlotPool> PoolList; 172 173 mutex fLock; 174 PoolList fEmptyPools; 175 PoolList fNonEmptyPools; 176 PhysicalPageSlot* fDebugSlot; 177 PhysicalPageSlotPool* fInitialPool; 178 LargeMemoryTranslationMapPhysicalPageMapper fKernelMapper; 179 PhysicalPageOpsCPUData fPerCPUData[SMP_MAX_CPUS]; 180}; 181 182static LargeMemoryPhysicalPageMapper sPhysicalPageMapper; 183 184 185// #pragma mark - PhysicalPageSlot / PhysicalPageSlotPool 186 187 188inline void 189PhysicalPageSlot::Map(phys_addr_t physicalAddress) 190{ 191 pool->Map(physicalAddress, address); 192} 193 194 195PhysicalPageSlotPool::~PhysicalPageSlotPool() 196{ 197} 198 199 200inline bool 201PhysicalPageSlotPool::IsEmpty() const 202{ 203 return fSlots == NULL; 204} 205 206 207inline PhysicalPageSlot* 208PhysicalPageSlotPool::GetSlot() 209{ 210 PhysicalPageSlot* slot = fSlots; 211 fSlots = slot->next; 212 return slot; 213} 214 215 216inline void 217PhysicalPageSlotPool::PutSlot(PhysicalPageSlot* slot) 218{ 219 slot->next = fSlots; 220 fSlots = slot; 221} 222 223 224// #pragma mark - PhysicalPageSlotQueue 225 226 227PhysicalPageSlotQueue::PhysicalPageSlotQueue() 228 : 229 fSlots(NULL) 230{ 231 fFreeSlotCondition.Init(this, "physical page ops slot queue"); 232 fFreeSlotsCondition.Init(this, "physical page ops slots queue"); 233} 234 235 236PhysicalPageSlot* 237PhysicalPageSlotQueue::GetSlot() 238{ 239 InterruptsLocker locker; 240 241 // wait for a free slot to turn up 242 while (fSlots == NULL) { 243 ConditionVariableEntry entry; 244 fFreeSlotCondition.Add(&entry); 245 locker.Unlock(); 246 entry.Wait(); 247 locker.Lock(); 248 } 249 250 PhysicalPageSlot* slot = fSlots; 251 fSlots = slot->next; 252 253 return slot; 254} 255 256 257void 258PhysicalPageSlotQueue::GetSlots(PhysicalPageSlot*& slot1, 259 PhysicalPageSlot*& slot2) 260{ 261 InterruptsLocker locker; 262 263 // wait for two free slot to turn up 264 while (fSlots == NULL || fSlots->next == NULL) { 265 ConditionVariableEntry entry; 266 fFreeSlotsCondition.Add(&entry); 267 locker.Unlock(); 268 entry.Wait(); 269 locker.Lock(); 270 } 271 272 slot1 = fSlots; 273 slot2 = slot1->next; 274 fSlots = slot2->next; 275} 276 277 278void 279PhysicalPageSlotQueue::PutSlot(PhysicalPageSlot* slot) 280{ 281 InterruptsLocker locker; 282 283 slot->next = fSlots; 284 fSlots = slot; 285 286 if (slot->next == NULL) 287 fFreeSlotCondition.NotifyAll(); 288 else if (slot->next->next == NULL) 289 fFreeSlotCondition.NotifyAll(); 290} 291 292 293void 294PhysicalPageSlotQueue::PutSlots(PhysicalPageSlot* slot1, 295 PhysicalPageSlot* slot2) 296{ 297 InterruptsLocker locker; 298 299 slot1->next = slot2; 300 slot2->next = fSlots; 301 fSlots = slot1; 302 303 if (slot2->next == NULL) 304 fFreeSlotCondition.NotifyAll(); 305 else if (slot2->next->next == NULL) 306 fFreeSlotCondition.NotifyAll(); 307} 308 309 310// #pragma mark - PhysicalPageOpsCPUData 311 312 313void 314PhysicalPageOpsCPUData::Init() 315{ 316 for (int32 i = 0; i < USER_SLOTS_PER_CPU; i++) 317 user.PutSlot(_GetInitialSlot()); 318 for (int32 i = 0; i < KERNEL_SLOTS_PER_CPU; i++) 319 kernel.PutSlot(_GetInitialSlot()); 320 interruptSlot = _GetInitialSlot(); 321} 322 323 324/* static */ PhysicalPageSlot* 325PhysicalPageOpsCPUData::_GetInitialSlot() 326{ 327 PhysicalPageSlot* slot; 328 status_t error = sPhysicalPageMapper.GetSlot(false, slot); 329 if (error != B_OK) { 330 panic("PhysicalPageOpsCPUData::Init(): Failed to get initial " 331 "physical page slots! Probably too many CPUs."); 332 return NULL; 333 } 334 335 return slot; 336} 337 338 339// #pragma mark - LargeMemoryTranslationMapPhysicalPageMapper 340 341 342LargeMemoryTranslationMapPhysicalPageMapper 343 ::LargeMemoryTranslationMapPhysicalPageMapper() 344 : 345 fSlotCount(sizeof(fSlots) / sizeof(page_slot)), 346 fNextSlot(0) 347{ 348 memset(fSlots, 0, sizeof(fSlots)); 349} 350 351 352LargeMemoryTranslationMapPhysicalPageMapper 353 ::~LargeMemoryTranslationMapPhysicalPageMapper() 354{ 355 // put our slots back to the global pool 356 for (int32 i = 0; i < fSlotCount; i++) { 357 if (fSlots[i].slot != NULL) 358 sPhysicalPageMapper.PutSlot(fSlots[i].slot); 359 } 360} 361 362 363status_t 364LargeMemoryTranslationMapPhysicalPageMapper::Init() 365{ 366 // get our slots from the global pool 367 for (int32 i = 0; i < fSlotCount; i++) { 368 status_t error = sPhysicalPageMapper.GetSlot(true, fSlots[i].slot); 369 if (error != B_OK) 370 return error; 371 372 // set to invalid physical address, so it won't be used accidentally 373 fSlots[i].physicalAddress = ~(phys_addr_t)0; 374 } 375 376 return B_OK; 377} 378 379 380void 381LargeMemoryTranslationMapPhysicalPageMapper::Delete() 382{ 383 delete this; 384} 385 386 387void* 388LargeMemoryTranslationMapPhysicalPageMapper::GetPageTableAt( 389 phys_addr_t physicalAddress) 390{ 391 ASSERT(physicalAddress % B_PAGE_SIZE == 0); 392 393 int32 currentCPU = smp_get_current_cpu(); 394 395 // maybe the address is already mapped 396 for (int32 i = 0; i < fSlotCount; i++) { 397 page_slot& slot = fSlots[i]; 398 if (slot.physicalAddress == physicalAddress) { 399 fNextSlot = (i + 1) & (fSlotCount - 1); 400 if ((slot.valid.GetBit(currentCPU)) == 0) { 401 // not valid on this CPU -- invalidate the TLB entry 402 arch_cpu_invalidate_TLB_range(slot.slot->address, 403 slot.slot->address); 404 slot.valid.SetBit(currentCPU); 405 } 406 return (void*)slot.slot->address; 407 } 408 } 409 410 // not found -- need to map a fresh one 411 page_slot& slot = fSlots[fNextSlot]; 412 fNextSlot = (fNextSlot + 1) & (fSlotCount - 1); 413 414 slot.physicalAddress = physicalAddress; 415 slot.slot->Map(physicalAddress); 416 slot.valid.ClearAll(); 417 slot.valid.SetBit(currentCPU); 418 419 return (void*)slot.slot->address; 420} 421 422 423// #pragma mark - LargeMemoryPhysicalPageMapper 424 425 426LargeMemoryPhysicalPageMapper::LargeMemoryPhysicalPageMapper() 427 : 428 fInitialPool(NULL) 429{ 430 mutex_init(&fLock, "large memory physical page mapper"); 431} 432 433 434status_t 435LargeMemoryPhysicalPageMapper::Init(kernel_args* args, 436 PhysicalPageSlotPool* initialPool, 437 TranslationMapPhysicalPageMapper*& _kernelPageMapper) 438{ 439 fInitialPool = initialPool; 440 fNonEmptyPools.Add(fInitialPool); 441 442 // get the debug slot 443 GetSlot(true, fDebugSlot); 444 445 // init the kernel translation map physical page mapper 446 status_t error = fKernelMapper.Init(); 447 if (error != B_OK) { 448 panic("LargeMemoryPhysicalPageMapper::Init(): Failed to init " 449 "kernel translation map physical page mapper!"); 450 return error; 451 } 452 _kernelPageMapper = &fKernelMapper; 453 454 // init the per-CPU data 455 int32 cpuCount = smp_get_num_cpus(); 456 for (int32 i = 0; i < cpuCount; i++) 457 fPerCPUData[i].Init(); 458 459 return B_OK; 460} 461 462 463status_t 464LargeMemoryPhysicalPageMapper::CreateTranslationMapPhysicalPageMapper( 465 TranslationMapPhysicalPageMapper** _mapper) 466{ 467 LargeMemoryTranslationMapPhysicalPageMapper* mapper 468 = new(std::nothrow) LargeMemoryTranslationMapPhysicalPageMapper; 469 if (mapper == NULL) 470 return B_NO_MEMORY; 471 472 status_t error = mapper->Init(); 473 if (error != B_OK) { 474 delete mapper; 475 return error; 476 } 477 478 *_mapper = mapper; 479 return B_OK; 480} 481 482 483void* 484LargeMemoryPhysicalPageMapper::InterruptGetPageTableAt( 485 phys_addr_t physicalAddress) 486{ 487 ASSERT(physicalAddress % B_PAGE_SIZE == 0); 488 489 PhysicalPageSlot* slot = fPerCPUData[smp_get_current_cpu()].interruptSlot; 490 slot->Map(physicalAddress); 491 return (void*)slot->address; 492} 493 494 495status_t 496LargeMemoryPhysicalPageMapper::GetPage(phys_addr_t physicalAddress, 497 addr_t* virtualAddress, void** handle) 498{ 499 PhysicalPageSlot* slot; 500 status_t error = GetSlot(true, slot); 501 if (error != B_OK) 502 return error; 503 504 slot->Map(physicalAddress); 505 506 *handle = slot; 507 *virtualAddress = slot->address + physicalAddress % B_PAGE_SIZE; 508 509 smp_send_broadcast_ici(SMP_MSG_INVALIDATE_PAGE_RANGE, *virtualAddress, 510 *virtualAddress, 0, NULL, SMP_MSG_FLAG_SYNC); 511 512 return B_OK; 513} 514 515 516status_t 517LargeMemoryPhysicalPageMapper::PutPage(addr_t virtualAddress, void* handle) 518{ 519 PutSlot((PhysicalPageSlot*)handle); 520 return B_OK; 521} 522 523 524status_t 525LargeMemoryPhysicalPageMapper::GetPageCurrentCPU(phys_addr_t physicalAddress, 526 addr_t* virtualAddress, void** handle) 527{ 528 // get a slot from the per-cpu user pool 529 PhysicalPageSlotQueue& slotQueue 530 = fPerCPUData[smp_get_current_cpu()].user; 531 PhysicalPageSlot* slot = slotQueue.GetSlot(); 532 slot->Map(physicalAddress); 533 534 *virtualAddress = slot->address + physicalAddress % B_PAGE_SIZE; 535 *handle = slot; 536 537 return B_OK; 538} 539 540 541status_t 542LargeMemoryPhysicalPageMapper::PutPageCurrentCPU(addr_t virtualAddress, 543 void* handle) 544{ 545 // return the slot to the per-cpu user pool 546 PhysicalPageSlotQueue& slotQueue 547 = fPerCPUData[smp_get_current_cpu()].user; 548 slotQueue.PutSlot((PhysicalPageSlot*)handle); 549 return B_OK; 550} 551 552 553status_t 554LargeMemoryPhysicalPageMapper::GetPageDebug(phys_addr_t physicalAddress, 555 addr_t* virtualAddress, void** handle) 556{ 557 fDebugSlot->Map(physicalAddress); 558 559 *handle = fDebugSlot; 560 *virtualAddress = fDebugSlot->address + physicalAddress % B_PAGE_SIZE; 561 return B_OK; 562} 563 564 565status_t 566LargeMemoryPhysicalPageMapper::PutPageDebug(addr_t virtualAddress, void* handle) 567{ 568 return B_OK; 569} 570 571 572status_t 573LargeMemoryPhysicalPageMapper::MemsetPhysical(phys_addr_t address, int value, 574 phys_size_t length) 575{ 576 addr_t pageOffset = address % B_PAGE_SIZE; 577 578 Thread* thread = thread_get_current_thread(); 579 ThreadCPUPinner _(thread); 580 581 PhysicalPageSlotQueue* slotQueue = GetSlotQueue(thread->cpu->cpu_num, 582 false); 583 PhysicalPageSlot* slot = slotQueue->GetSlot(); 584 585 while (length > 0) { 586 slot->Map(address - pageOffset); 587 588 size_t toSet = min_c(length, B_PAGE_SIZE - pageOffset); 589 memset((void*)(slot->address + pageOffset), value, toSet); 590 591 length -= toSet; 592 address += toSet; 593 pageOffset = 0; 594 } 595 596 slotQueue->PutSlot(slot); 597 598 return B_OK; 599} 600 601 602status_t 603LargeMemoryPhysicalPageMapper::MemcpyFromPhysical(void* _to, phys_addr_t from, 604 size_t length, bool user) 605{ 606 uint8* to = (uint8*)_to; 607 addr_t pageOffset = from % B_PAGE_SIZE; 608 609 Thread* thread = thread_get_current_thread(); 610 ThreadCPUPinner _(thread); 611 612 PhysicalPageSlotQueue* slotQueue = GetSlotQueue(thread->cpu->cpu_num, user); 613 PhysicalPageSlot* slot = slotQueue->GetSlot(); 614 615 status_t error = B_OK; 616 617 while (length > 0) { 618 size_t toCopy = min_c(length, B_PAGE_SIZE - pageOffset); 619 620 slot->Map(from - pageOffset); 621 622 if (user) { 623 error = user_memcpy(to, (void*)(slot->address + pageOffset), 624 toCopy); 625 if (error != B_OK) 626 break; 627 } else 628 memcpy(to, (void*)(slot->address + pageOffset), toCopy); 629 630 to += toCopy; 631 from += toCopy; 632 length -= toCopy; 633 pageOffset = 0; 634 } 635 636 slotQueue->PutSlot(slot); 637 638 return error; 639} 640 641 642status_t 643LargeMemoryPhysicalPageMapper::MemcpyToPhysical(phys_addr_t to, 644 const void* _from, size_t length, bool user) 645{ 646 const uint8* from = (const uint8*)_from; 647 addr_t pageOffset = to % B_PAGE_SIZE; 648 649 Thread* thread = thread_get_current_thread(); 650 ThreadCPUPinner _(thread); 651 652 PhysicalPageSlotQueue* slotQueue = GetSlotQueue(thread->cpu->cpu_num, user); 653 PhysicalPageSlot* slot = slotQueue->GetSlot(); 654 655 status_t error = B_OK; 656 657 while (length > 0) { 658 size_t toCopy = min_c(length, B_PAGE_SIZE - pageOffset); 659 660 slot->Map(to - pageOffset); 661 662 if (user) { 663 error = user_memcpy((void*)(slot->address + pageOffset), from, 664 toCopy); 665 if (error != B_OK) 666 break; 667 } else 668 memcpy((void*)(slot->address + pageOffset), from, toCopy); 669 670 to += toCopy; 671 from += toCopy; 672 length -= toCopy; 673 pageOffset = 0; 674 } 675 676 slotQueue->PutSlot(slot); 677 678 return error; 679} 680 681 682void 683LargeMemoryPhysicalPageMapper::MemcpyPhysicalPage(phys_addr_t to, 684 phys_addr_t from) 685{ 686 Thread* thread = thread_get_current_thread(); 687 ThreadCPUPinner _(thread); 688 689 PhysicalPageSlotQueue* slotQueue = GetSlotQueue(thread->cpu->cpu_num, 690 false); 691 PhysicalPageSlot* fromSlot; 692 PhysicalPageSlot* toSlot; 693 slotQueue->GetSlots(fromSlot, toSlot); 694 695 fromSlot->Map(from); 696 toSlot->Map(to); 697 698 memcpy((void*)toSlot->address, (void*)fromSlot->address, B_PAGE_SIZE); 699 700 slotQueue->PutSlots(fromSlot, toSlot); 701} 702 703 704status_t 705LargeMemoryPhysicalPageMapper::GetSlot(bool canWait, PhysicalPageSlot*& slot) 706{ 707 MutexLocker locker(fLock); 708 709 PhysicalPageSlotPool* pool = fNonEmptyPools.Head(); 710 if (pool == NULL) { 711 if (!canWait) 712 return B_WOULD_BLOCK; 713 714 // allocate new pool 715 locker.Unlock(); 716 status_t error = fInitialPool->AllocatePool(pool); 717 if (error != B_OK) 718 return error; 719 locker.Lock(); 720 721 fNonEmptyPools.Add(pool); 722 pool = fNonEmptyPools.Head(); 723 } 724 725 slot = pool->GetSlot(); 726 727 if (pool->IsEmpty()) { 728 fNonEmptyPools.Remove(pool); 729 fEmptyPools.Add(pool); 730 } 731 732 return B_OK; 733} 734 735 736void 737LargeMemoryPhysicalPageMapper::PutSlot(PhysicalPageSlot* slot) 738{ 739 MutexLocker locker(fLock); 740 741 PhysicalPageSlotPool* pool = slot->pool; 742 if (pool->IsEmpty()) { 743 fEmptyPools.Remove(pool); 744 fNonEmptyPools.Add(pool); 745 } 746 747 pool->PutSlot(slot); 748} 749 750 751inline PhysicalPageSlotQueue* 752LargeMemoryPhysicalPageMapper::GetSlotQueue(int32 cpu, bool user) 753{ 754 return user ? &fPerCPUData[cpu].user : &fPerCPUData[cpu].kernel; 755} 756 757 758// #pragma mark - Initialization 759 760 761status_t 762large_memory_physical_page_ops_init(kernel_args* args, 763 M68KLargePhysicalPageMapper::PhysicalPageSlotPool* initialPool, 764 M68KPhysicalPageMapper*& _pageMapper, 765 TranslationMapPhysicalPageMapper*& _kernelPageMapper) 766{ 767 new(&sPhysicalPageMapper) LargeMemoryPhysicalPageMapper; 768 sPhysicalPageMapper.Init(args, initialPool, _kernelPageMapper); 769 770 _pageMapper = &sPhysicalPageMapper; 771 return B_OK; 772} 773