--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/mmu/d_demandpaging.cpp Thu Dec 17 09:24:54 2009 +0200
@@ -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
+ }
+