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