kerneltest/e32test/mmu/d_demandpaging.cpp
changeset 0 a41df078684a
child 43 c1f20ce4abcf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/mmu/d_demandpaging.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,617 @@
+// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32test\mmu\d_demandpaging.cpp
+// 
+//
+
+#include <kernel/kern_priv.h>
+#include <kernel/cache.h>
+#include "d_demandpaging.h"
+
+/// Page attributes, cut-n-paste'd from mmubase.h
+enum TType
+	{
+//	EInvalid=0,			// No physical RAM exists for this page
+//	EFixed=1,			// RAM fixed at boot time
+//	EUnused=2,			// Page is unused
+//	EChunk=3,
+//	ECodeSeg=4,
+//	EHwChunk=5,
+//	EPageTable=6,
+//	EPageDir=7,
+//	EPtInfo=8,
+//	EShadow=9,
+
+	EPagedROM=10,
+	EPagedCode=11,
+	EPagedData=12,
+	EPagedCache=13,
+	EPagedFree=14,
+	};
+
+enum TState
+	{
+	EStateNormal = 0,		// no special state
+	EStatePagedYoung = 1,
+	EStatePagedOld = 2,
+	EStatePagedDead = 3,
+	EStatePagedLocked = 4
+	};
+
+//
+// Class definitions
+//
+
+class DDemandPagingTestFactory : public DLogicalDevice
+	{
+public:
+	~DDemandPagingTestFactory();
+	virtual TInt Install();
+	virtual void GetCaps(TDes8& aDes) const;
+	virtual TInt Create(DLogicalChannelBase*& aChannel);
+	};
+
+class DDemandPagingTestChannel : public DLogicalChannelBase
+	{
+public:
+	DDemandPagingTestChannel();
+	~DDemandPagingTestChannel();
+	virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
+	virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
+	TInt LockTest(const TAny* aBuffer, TInt aSize);
+	TInt LockTest2();
+	TInt DoConsumeContiguousRamTest(TInt aAlign, TInt aPages);
+	TInt DoCreatePlatHwChunk(TInt aSize, TAny* aLinAddr);
+	TInt DoDestroyPlatHwChunk();
+	TInt ReadHoldingMutexTest(TAny* aDest);
+
+	TBool CheckPagedIn(TLinAddr aAddress);
+	TBool CheckPagedOut(TLinAddr aAddress);
+	TBool CheckLocked(TLinAddr aAddress);
+
+	TInt FreeRam();
+public:
+	DDemandPagingTestFactory*	iFactory;
+	DDemandPagingLock iLock;
+
+	DPlatChunkHw* iHwChunk;
+    TInt iChunkSize;
+	TPhysAddr iPhysBase;		// This will be base physical address of the chunk
+    TLinAddr iLinearBase;		// This will be base linear address of the chunk
+	};
+
+//
+// DDemandPagingTestFactory
+//
+
+TInt DDemandPagingTestFactory::Install()
+	{
+	return SetName(&KDemandPagingTestLddName);
+	}
+
+DDemandPagingTestFactory::~DDemandPagingTestFactory()
+	{
+	}
+
+void DDemandPagingTestFactory::GetCaps(TDes8& /*aDes*/) const
+	{
+	// Not used but required as DLogicalDevice::GetCaps is pure virtual
+	}
+
+TInt DDemandPagingTestFactory::Create(DLogicalChannelBase*& aChannel)
+	{
+	aChannel = NULL;
+	DDemandPagingTestChannel* channel=new DDemandPagingTestChannel;
+	if(!channel)
+		return KErrNoMemory;
+	channel->iFactory = this;
+	aChannel = channel;
+	return KErrNone;
+	}
+
+DECLARE_STANDARD_LDD()
+	{
+	return new DDemandPagingTestFactory;
+	}
+
+//
+// DDemandPagingTestChannel
+//
+
+TInt DDemandPagingTestChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/)
+	{
+	return KErrNone;
+	}
+
+DDemandPagingTestChannel::DDemandPagingTestChannel()
+	{
+	}
+
+DDemandPagingTestChannel::~DDemandPagingTestChannel()
+	{
+	DoDestroyPlatHwChunk();
+	}
+
+TInt DDemandPagingTestChannel::Request(TInt aFunction, TAny* a1, TAny* a2)
+	{
+	switch(aFunction)
+		{
+	case RDemandPagingTestLdd::ELockTest:
+		{
+		TInt r = LockTest(a1,(TInt)a2);
+		if (r == KErrNone)
+			r = LockTest2();
+		return r;
+		}
+
+	case RDemandPagingTestLdd::ESetRealtimeTrace:
+		{
+#if defined(_DEBUG)
+		TUint32 bit = TUint32(1<<(KREALTIME&31));
+		__e32_atomic_axo_ord32(&Kern::SuperPage().iDebugMask[KREALTIME>>5], ~bit, a1?bit:0);
+#if 0 // can enable this to help debugging
+		bit = (1<<(KPAGING&31));
+		__e32_atomic_axo_ord32(&Kern::SuperPage().iDebugMask[KPAGING>>5], ~bit, a1?bit:0);
+#endif
+#endif //_DEBUG
+		}
+		return KErrNone;
+
+	case RDemandPagingTestLdd::EDoConsumeContiguousRamTest:
+		{
+		return DDemandPagingTestChannel::DoConsumeContiguousRamTest((TInt)a1, (TInt)a2);
+		}
+
+	case RDemandPagingTestLdd::ECreatePlatHwChunk:
+		{
+		return DDemandPagingTestChannel::DoCreatePlatHwChunk((TInt)a1, a2);
+		}
+
+	case RDemandPagingTestLdd::EDestroyPlatHwChunk:
+		{
+		return DDemandPagingTestChannel::DoDestroyPlatHwChunk();
+		}
+
+	case RDemandPagingTestLdd::ELock:
+		{
+		TInt r=iLock.Alloc((TInt)a2);
+		if(r!=KErrNone)
+			return r;
+		return iLock.Lock(&Kern::CurrentThread(),(TLinAddr)a1,(TInt)a2);
+		}
+
+	case RDemandPagingTestLdd::EUnlock:
+		{
+		iLock.Free();
+		return KErrNone;
+		}
+
+	case RDemandPagingTestLdd::EReadHoldingMutexTest:
+		return ReadHoldingMutexTest((TAny*)a1);
+
+	default:
+		return KErrNotSupported;
+		}
+	}
+
+// 
+// DDemandPagingTestChannel::DoCreatePlatHwChunk
+//
+// For some of the tests of IPC from demand-paged memory, we need a writable
+// globally-mapped buffer; so this function creates a suitable chunk and
+// returns its (global, virtual) address to the userland caller.  The caller
+// should call DoDestroyPlatHwChunk() to release the memory when the tests
+// are finished.
+//
+TInt DDemandPagingTestChannel::DoCreatePlatHwChunk(TInt aSize, TAny* aLinAddr)
+	{
+	TInt mapAttr = EMapAttrUserRw;		// Supervisor and user both have read/write permissions
+
+	NKern::ThreadEnterCS();
+	if (iHwChunk)						// Only one chunk at a atime
+		{
+		NKern::ThreadLeaveCS();
+		return KErrAlreadyExists;
+		}
+
+	iChunkSize = Kern::RoundToPageSize(aSize);
+
+	Kern::Printf("*** Attempting to allocate contiguous physical RAM ***");
+	TInt free = Kern::FreeRamInBytes();
+	Kern::Printf("      requested:  %08x", iChunkSize);
+	Kern::Printf("      total free: %08x", free);
+	
+	TInt r = Epoc::AllocPhysicalRam(iChunkSize, iPhysBase, 0);	// Allocate RAM; result in iPhysBase
+	if (r)
+		{
+		NKern::ThreadLeaveCS();
+		Kern::Printf("      failed with error %d", r);
+		return r;
+		}
+	else
+		Kern::Printf("      success");
+
+	r = DPlatChunkHw::New(iHwChunk, iPhysBase, iChunkSize, mapAttr);	// Create chunk
+	if (r)
+		{
+		Epoc::FreePhysicalRam(iPhysBase, iChunkSize);
+		iHwChunk = 0;
+		NKern::ThreadLeaveCS();
+		return r;
+		}
+	NKern::ThreadLeaveCS();
+
+	// Return the virtual address to userland
+	iLinearBase = iHwChunk->LinearAddress();
+	kumemput(aLinAddr, &iLinearBase, sizeof(iLinearBase));
+
+	Kern::Printf("CreatePlatHwChunk@%08x: iLinearBase %08x, iPhysBase %08x, size %d",
+		iHwChunk, iLinearBase, iPhysBase, iChunkSize);
+
+	return KErrNone;
+	}
+
+TInt DDemandPagingTestChannel::DoDestroyPlatHwChunk()
+	{
+	Kern::Printf("DestroyPlatHwChunk@%08x: iLinearBase %08x, iPhysBase %08x, size %d",
+		iHwChunk, iLinearBase, iPhysBase, iChunkSize);
+	NKern::ThreadEnterCS();
+	if (iHwChunk)
+		{
+		iHwChunk->Close(NULL);
+		Epoc::FreePhysicalRam(iPhysBase, iChunkSize);
+		iPhysBase = 0;
+		iChunkSize = 0;
+		iHwChunk = 0;
+		}
+	NKern::ThreadLeaveCS();
+	return KErrNone;
+	}
+
+// 
+// DDemandPagingTestChannel::DoConsumeContiguousRamTest
+//
+// This test attempts to consume all available Contiguous Ram until we need to ask the 
+// demand paging code to release memory for it.
+// 
+// On completion free all the memory allocated.
+//
+#define CHECK(c) { if(!(c)) { Kern::Printf("Fail  %d", __LINE__); ; retVal = __LINE__;} }
+
+TInt DDemandPagingTestChannel::DoConsumeContiguousRamTest(TInt aAlign, TInt aSize)
+	{
+	TInt retVal = KErrNone;
+	TInt initialFreeRam = FreeRam();
+	TInt totalBlocks = initialFreeRam/aSize;
+
+	NKern::ThreadEnterCS();
+	TPhysAddr*	 pAddrArray = (TPhysAddr *)Kern::Alloc(sizeof(TPhysAddr) * totalBlocks);
+	NKern::ThreadLeaveCS();
+	CHECK(pAddrArray);
+	if(!pAddrArray)
+		return retVal;
+	
+	SVMCacheInfo tempPages;
+
+	// get the initial free ram again as the heap may have grabbed a page during the alloc
+	initialFreeRam = FreeRam();
+	Kern::Printf("ConsumeContiguousRamTest: align %d size %d initialFreeRam %d", aAlign, aSize, initialFreeRam);
+
+	CHECK(Kern::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0) == KErrNone);
+	Kern::Printf("Start cache info: iMinSize %d iMaxSize %d iCurrentSize %d iMaxFreeSize %d",
+				 tempPages.iMinSize, tempPages.iMaxSize, tempPages.iCurrentSize ,tempPages.iMaxFreeSize);
+
+	TInt initialFreePages = tempPages.iMaxFreeSize;
+	CHECK(initialFreePages != 0);
+	
+	// allocate blocks to use up RAM until we fail to allocate any further...
+	TBool freedPagesToAlloc = EFalse;
+	TInt index;
+	TUint32 alignMask = (1 << aAlign) - 1;
+	for (index = 0; index < totalBlocks; )
+		{
+		CHECK(Kern::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0) == KErrNone);
+		TInt beforePages = tempPages.iMaxFreeSize;
+
+		NKern::ThreadEnterCS();
+		TInt r = Epoc::AllocPhysicalRam(aSize, pAddrArray[index], aAlign);
+		if(r==KErrNone)
+			{
+			// check the alignment of the returned pages
+			CHECK((pAddrArray[index] & alignMask) == 0);
+			++index;
+			}
+		NKern::ThreadLeaveCS();
+		if(r!=KErrNone)
+			{
+			break;
+			}
+		CHECK(Kern::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0) == KErrNone);
+		TInt afterPages = tempPages.iMaxFreeSize;
+
+		if (afterPages != beforePages)
+			freedPagesToAlloc = ETrue; // the alloc reclaimed memory from the paging cache
+		}
+
+	if (!index)
+		Kern::Printf("WARNING : DoConsumeContiguousRamTest no allocations were successful");
+	// free the memory we allocated...
+	while(--index>=0)
+		{
+		NKern::ThreadEnterCS();
+		TInt r = Epoc::FreePhysicalRam(pAddrArray[index], aSize);
+		NKern::ThreadLeaveCS();
+		CHECK(r==KErrNone);
+		}
+
+	CHECK(FreeRam() == initialFreeRam);
+
+	NKern::ThreadEnterCS();
+	Kern::Free(pAddrArray);
+	NKern::ThreadLeaveCS();
+
+	CHECK(Kern::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0) == KErrNone);
+	Kern::Printf("End cache info: iMinSize %d iMaxSize %d iCurrentSize %d iMaxFreeSize %d",
+				 tempPages.iMinSize, tempPages.iMaxSize, tempPages.iCurrentSize ,tempPages.iMaxFreeSize);
+
+	if (!freedPagesToAlloc)
+		Kern::Printf("WARNING : DoConsumeContiguousRamTest freedPagesToAlloc was eFalse");
+	//CHECK(freedPagesToAlloc);		
+
+	return retVal;
+	}
+#undef CHECK
+
+
+TUint8 ReadByte(volatile TUint8* aPtr)
+	{
+	return *aPtr;
+	}
+
+#define CHECK(c) { if(!(c)) return __LINE__; }
+
+#define READ(a) ReadByte((volatile TUint8*)(a))
+
+TInt DDemandPagingTestChannel::LockTest(const TAny* aBuffer, TInt aSize)
+	{
+	// Get page size info
+	TInt pageSize = 0;
+	CHECK(Kern::HalFunction(EHalGroupKernel,EKernelHalPageSizeInBytes,&pageSize,0)==KErrNone);
+	TInt pageMask = pageSize-1;
+
+	// See if were running of the Flexible Memory Model
+  	TUint32 memModelAttrib = (TUint32)Kern::HalFunction(EHalGroupKernel,EKernelHalMemModelInfo,0,0);	
+	TBool fmm = (memModelAttrib&EMemModelTypeMask)==EMemModelTypeFlexible;
+
+	// Round buffer to page boundaries
+	TLinAddr start = ((TLinAddr)aBuffer+pageMask)&~pageMask;
+	TLinAddr end = ((TLinAddr)aBuffer+aSize)&~pageMask;
+	aSize = end-start;
+	Kern::Printf("Test buffer is %08x, %x\n",start,aSize);
+	CHECK(aSize>pageSize*2);
+
+	// Flush all paged memory
+	Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+
+	TInt initialFreeRam;
+	TInt freeRam1;
+	TInt freeRam2;
+	TLinAddr addr;
+	TUint lockBytesUsed = fmm ? 0 : 0; // free ram change on locking (zero or aSize depending on implementation)
+
+	{ // this brace is essential for correctness
+	DDemandPagingLock lock2; // construct a lock;
+
+	Kern::Printf("Check reading from buffer pages it in\n");
+	for(addr=start; addr<end; addr+=pageSize) READ(addr);
+	for(addr=start; addr<end; addr+=pageSize) CHECK(CheckPagedIn(addr));
+	initialFreeRam = FreeRam();
+
+	Kern::Printf("Check Alloc reserves pages\n");
+	CHECK(iLock.Alloc(aSize)==KErrNone);
+	freeRam1 = FreeRam();
+
+	Kern::Printf("Check flushing pages out the buffer\n");
+	Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+	for(addr=start; addr<end; addr+=pageSize) CHECK(CheckPagedOut(addr));
+
+	Kern::Printf("Check Lock\n");
+	CHECK(iLock.Lock(&Kern::CurrentThread(),start,aSize));
+	CHECK((TUint)FreeRam()==TUint(freeRam1-lockBytesUsed));
+	for(addr=start; addr<end; addr+=pageSize) CHECK(CheckLocked(addr));
+
+	Kern::Printf("Check flushing doesn't page out the buffer\n");
+	Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+	for(addr=start; addr<end; addr+=pageSize) CHECK(CheckLocked(addr));
+	CHECK((TUint)FreeRam()==TUint(freeRam1-lockBytesUsed));
+
+	Kern::Printf("Check second Alloc\n");
+	CHECK(lock2.Alloc(aSize)==KErrNone);
+	freeRam2 = FreeRam();
+
+	Kern::Printf("Check second Lock\n");
+	CHECK(lock2.Lock(&Kern::CurrentThread(),start,aSize));
+	CHECK(FreeRam()==freeRam2);
+	for(addr=start; addr<end; addr+=pageSize) CHECK(CheckLocked(addr));
+
+	Kern::Printf("Check deleting second lock\n");
+	// lock2 is deleted here because it goes out of scope...
+	} // this brace is essential for correctness
+	CHECK((TUint)FreeRam()==TUint(freeRam1-lockBytesUsed));
+	for(addr=start; addr<end; addr+=pageSize) CHECK(CheckLocked(addr));
+
+	Kern::Printf("Check Unlock\n");
+	iLock.Unlock();
+	CHECK(FreeRam()==freeRam1);
+	for(addr=start; addr<end; addr+=pageSize) CHECK(CheckPagedIn(addr));
+	iLock.Unlock();
+	CHECK(FreeRam()==initialFreeRam);
+
+	Kern::Printf("Check Free\n");
+	iLock.Free();
+	CHECK(FreeRam()==initialFreeRam);
+	iLock.Free();
+	CHECK(FreeRam()==initialFreeRam);
+
+	return KErrNone;
+	}
+
+#undef CHECK
+#define CHECK(c) { if(!(c)) { r = __LINE__; goto cleanup; } }
+
+TInt DDemandPagingTestChannel::LockTest2()
+	{
+	Kern::Printf("Check allocating locks eventually increases size of live list\n");
+	TInt r = KErrNone;
+
+	DDemandPagingLock* lock = NULL;
+	RPointerArray<DDemandPagingLock> lockArray;
+	
+	const TInt KLockMax = 1000; // make this a bit bigger than current min page count?
+	TInt i;
+	
+	NKern::ThreadEnterCS();
+	for (i = 0 ; i < KLockMax ; ++i)
+		{
+		lock = new DDemandPagingLock;
+		CHECK(lock);
+		CHECK(lockArray.Append(lock) == KErrNone);
+		lock = NULL;
+
+		TInt initialFreeRam = FreeRam();
+		CHECK(lockArray[i]->Alloc(1) == KErrNone);
+		if (FreeRam() < initialFreeRam)
+			{
+			Kern::Printf("Live list size increased after %d locks allocated", i + 1);
+			break;
+			}
+		}
+
+	CHECK(i < KLockMax);
+	
+cleanup:
+
+	delete lock;
+	lock = NULL;
+	for (i = 0 ; i < lockArray.Count() ; ++i)
+		{
+		delete lockArray[i];
+		lockArray[i] = NULL;
+		}
+	lockArray.Reset();
+
+	NKern::ThreadLeaveCS();
+
+	return r;
+	}
+
+TInt DDemandPagingTestChannel::FreeRam()
+	{
+	Kern::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0);
+	TInt freeRam = Kern::FreeRamInBytes();
+	Kern::Printf("...free RAM: %x\n",freeRam);
+	return freeRam;
+	}
+
+
+TUint32 PageState(TLinAddr aAddress)
+	{
+	TUint32 state = Kern::HalFunction(EHalGroupVM, EVMPageState, (TAny*)aAddress, 0);
+	Kern::Printf("PageState: %08x=%08x",aAddress,state);
+	return state;
+	}
+
+
+TBool DDemandPagingTestChannel::CheckPagedIn(TLinAddr aAddress)
+	{
+	TUint32 state = PageState(aAddress);
+	return (state&0xff00) == (EStatePagedYoung<<8);
+	}
+
+
+TBool DDemandPagingTestChannel::CheckPagedOut(TLinAddr aAddress)
+	{
+	TUint32 state = PageState(aAddress);
+	return (state&0xffff) == 0;
+	}
+
+
+TInt DDemandPagingTestChannel::CheckLocked(TLinAddr aAddress)
+	{
+	TUint32 state = PageState(aAddress);
+	return (state&0xff00) == (EStatePagedLocked<<8);
+	}
+
+
+TInt DDemandPagingTestChannel::ReadHoldingMutexTest(TAny* aDest)
+	{
+	_LIT(KMutexName, "DPTestMutex");
+
+	NKern::ThreadEnterCS();
+	
+	DMutex* mutex;
+	TInt r = Kern::MutexCreate(mutex, KMutexName, KMutexOrdDebug);  // Mutex order < demand paging
+	if (r != KErrNone)
+		{
+		NKern::ThreadLeaveCS();
+		return r;
+		}
+	Kern::MutexWait(*mutex);
+	
+	const TRomHeader& romHeader = Epoc::RomHeader();
+	TLinAddr unpagedRomStart = (TLinAddr)&romHeader;
+	TLinAddr unpagedRomEnd;
+	if (romHeader.iPageableRomStart)
+		unpagedRomEnd = unpagedRomStart + romHeader.iPageableRomStart;
+	else
+		unpagedRomEnd = unpagedRomStart + romHeader.iUncompressedSize;
+	
+	const TInt length = 16;
+	TUint8 localBuf[length];
+	if(!aDest)
+		aDest = localBuf;
+	Kern::Printf("Local buffer at %08x", aDest);
+
+	TAny* src1 = (TAny*)unpagedRomStart;
+	TAny* src2 = (TAny*)(unpagedRomEnd - length);
+	
+	DThread* thread = &Kern::CurrentThread();
+
+	Kern::Printf("Attempting to access %08x", src1);
+	Kern::ThreadRawWrite(thread, aDest, src1, length);
+	Kern::Printf("Attempting to access %08x", src2);
+	Kern::ThreadRawWrite(thread, aDest, src2, length);
+
+	TUint8 stackData[length];
+	Kern::Printf("Attempting to access %08x", stackData);
+	Kern::ThreadRawWrite(thread, aDest, stackData, length);
+	
+	TAny* heapData = Kern::Alloc(length);
+	if (heapData)
+		{
+		Kern::Printf("Attempting to access %08x", heapData);
+		Kern::ThreadRawWrite(thread, aDest, heapData, length);
+		Kern::Free(heapData);
+		}
+	else
+		r = KErrNoMemory;
+	
+	Kern::MutexSignal(*mutex);
+	mutex->Close(NULL);
+	
+	NKern::ThreadLeaveCS();
+	
+	return r;  // a kernel fault indicates that the test failed
+	}
+