diff -r 000000000000 -r a41df078684a kerneltest/e32test/mmu/d_demandpaging.cpp --- /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 +#include +#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 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 + } +