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