diff -r 000000000000 -r a41df078684a kerneltest/e32test/mmu/t_demandpaging.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/mmu/t_demandpaging.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1367 @@ +// 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\t_demandpaging.cpp +// Functional tests for demand paging. The test suite covers simple +// paging operations as well as HAL configuration and tuning functions. +// 001.01 DPTest::Attributes +// 001.02 DPTest::FlushCache +// 001.03 DPTest::CacheSize +// 001.04 DPTest::SetCacheSize +// 001.04.01 Changing size of flushed VM cache +// 001.04.02 Changing size of full VM cache +// 002 Loading test drivers +// 003 Test thread realtime state +// 003.01 Enable KREALTIME tracing +// 003.02 Test ERealtimeStateOff +// 003.03 Test ERealtimeStateOn +// 003.04 Test ERealtimeStateWarn +// 003.05 Test server with ERealtimeStateOff +// 003.06 Test server with ERealtimeStateOn +// 003.07 Test server with ERealtimeStateWarn +// 003.08 Disable KREALTIME tracing +// 004 Lock Test +// 005 Lock Test again +// 006 Test writing to paged ROM +// 007 Test IPC read from paged memory +// 007.01 Create server +// 007.02 IPC read from ROM +// 007.03 Stop server +// 008 Test contiguous RAM allocation reclaims paged memory +// 008.01 Start... +// 008.02 Contiguous RAM test: alloc size = 128K align = 16 +// 008.03 Contiguous RAM test: alloc size = 128K align = 0 +// 008.04 Contiguous RAM test: alloc size = 64K align = 15 +// 008.05 Contiguous RAM test: alloc size = 64K align = 14 +// 008.06 Contiguous RAM test: alloc size = 64K align = 13 +// 008.07 Contiguous RAM test: alloc size = 64K align = 12 +// 008.08 Contiguous RAM test: alloc size = 64K align = 0 +// 008.09 Contiguous RAM test: alloc size = 8K align = 13 +// 008.10 Contiguous RAM test: alloc size = 8K align = 12 +// 008.11 Contiguous RAM test: alloc size = 8K align = 0 +// 008.12 Contiguous RAM test: alloc size = 4K align = 13 +// 008.13 Contiguous RAM test: alloc size = 4K align = 12 +// 008.14 Contiguous RAM test: alloc size = 4K align = 0 +// 009 Test no kernel faults when copying data from unpaged rom with mutex held +// 010 Close test driver +// 011 Test setting publish and subscribe properties from paged area +// 012 Rom Paging Benchmark +// 012.01 Benchmark ROM paging... +// +// + +//! @SYMTestCaseID KBASE-T_DEMANDPAGING-0334 +//! @SYMTestType UT +//! @SYMPREQ PREQ1110 +//! @SYMTestCaseDesc Demand Paging functional tests. +//! @SYMTestActions 001 Test HAL interface +//! @SYMTestExpectedResults All tests should pass. +//! @SYMTestPriority High +//! @SYMTestStatus Implemented + +#define __E32TEST_EXTENSION__ +#include +#include +#include +#include +#include +#include "d_memorytest.h" +#include "d_demandpaging.h" +#include "d_gobble.h" +#include "mmudetect.h" +#include "t_codepaging_dll.h" +#include "freeram.h" + +RTest test(_L("T_DEMANDPAGING")); + +_LIT(KTCodePagingDll4, "t_codepaging_dll4.dll"); +const TInt KMinBufferSize = 16384; +const TInt KMaxIPCSize = 256*1024; + +TInt PageSize = 0; +RDemandPagingTestLdd Ldd; +RLibrary PagedLibrary; + +// A buffer containing paged memory, contents may or may not be paged in +const TUint8* LargeBuffer = NULL; +TInt LargeBufferSize = 0; + +// A buffer containing paged memeory, contents always paged out before access +const TUint8* SmallBuffer = NULL; +TInt SmallBufferSize = 0; + +// A shared buffer mapped to the global address range +TInt SharedBufferSize = KMaxIPCSize+4096; +TLinAddr SharedBufferAddr = 0; +TUint8* SharedBuffer = NULL; + +// A descriptor whose header is in paged memory (actually just a pointer to a zero word) +TDesC8* PagedHeaderDes = NULL; + +// A data paged chunk used as a buffer, if data paging is supported +_LIT(KChunkName, "t_demandpaging chunk"); +RChunk DataPagedChunk; +TBool DataPagingSupported = EFalse; +TUint8* DataPagedBuffer = NULL; + +TUint8 ReadByte(volatile TUint8* aPtr) + { + return *aPtr; + } + +#define READ(a) ReadByte((volatile TUint8*)(a)) + +void ThrashPaging(TUint aMaxBytes=KMaxTUint) + { + TUint size = LargeBufferSize; + if(size>aMaxBytes) + size = aMaxBytes; + + // read all ROM pages about 10 times each in a random order... + TUint32 random=1; + for(TInt i=size/(PageSize/10); i>0; --i) + { + READ(LargeBuffer+((TInt64(random)*TInt64(size))>>32)); + random = random*69069+1; + } + } + +void FragmentPagingCache(TUint aMaxBytes) + { + DPTest::FlushCache(); + + TUint size = Min(LargeBufferSize, aMaxBytes); + if(size events0; + DPTest::EventInfo(events0); + + TInt KRunTime = 10*1000*1000; + timer.After(status,KRunTime); + while(status==KRequestPending) + for(const TUint8* ptr=LargeBuffer; ptr<(LargeBuffer+LargeBufferSize); ptr+=PageSize) + { + READ(ptr); + if(status!=KRequestPending) + break; + } + + TPckgBuf events1; + DPTest::EventInfo(events1); + + User::WaitForRequest(status); + + TUint pages = events1().iPageInReadCount-events0().iPageInReadCount; + test.Printf(_L("%d pages in %d seconds = %d us/page\n"),pages,KRunTime/1000/1000,KRunTime/pages); + + // restore live list to default size... + test(KErrNone==DPTest::SetCacheSize(0,0)); + + test.End(); + } + + +void TestResizeVMCache() + { + TInt r = DPTest::SetCacheSize(0,0); // restore cache size to defaults + test(r==KErrNone); + TUint sizeMin = 0; + TUint sizeMax = 0; + TUint currentSize = 0; + DPTest::CacheSize(sizeMin,sizeMax,currentSize); + TUint originalMin = sizeMin; + TUint originalMax = sizeMax; + test.Printf(_L("original min=%u, original max=%u, current=%u\n"),originalMin/PageSize,originalMax/PageSize,currentSize/PageSize); + + int K = currentSize/PageSize+4; + + struct + { + TUint iMinPages; + TUint iMaxPages; + TInt iResult; + } + testArgs[] = + { + { K, K, KErrNone}, + { K-4, K, KErrNone}, + { K, K, KErrNone}, + { K, K*2, KErrNone}, + { K, K, KErrNone}, + { K-1, K, KErrNone}, + { K, K, KErrNone}, + { K, K+1, KErrNone}, + { K, K, KErrNone}, + { K+1, K, KErrArgument}, + { K, K-1, KErrArgument}, + { KMaxTInt, KMaxTInt, KErrNoMemory}, + { K, K, KErrNone}, + + { 0, 0, KErrNone}, // restore defaults + { 0, 0, KMaxTInt} // list end marker + }; + + for(TInt j=0; j<2; ++j) + { + if(!j) + { + test.Start(_L("Changing size of flushed VM cache")); + test.Printf(_L("Original cache size min == %u, max == %u\n"),originalMin/PageSize,originalMax/PageSize); + } + else + test.Next(_L("Changing size of full VM cache")); + TInt i=0; + while(testArgs[i].iResult!=KMaxTInt) + { + TUint min=testArgs[i].iMinPages*PageSize; + TUint max=testArgs[i].iMaxPages*PageSize; + TInt result=testArgs[i].iResult; + + ThrashPaging(max*2); + if(!j) + DPTest::FlushCache(); + + test.Printf(_L("DPTest::SetCacheSize min=%u, max=%u, expected result=%d\n"),min/PageSize,max/PageSize,result); + TInt r=DPTest::SetCacheSize(min,max); + if(r!=result) + { + test.Printf(_L("result=%d\n"),r); + test(0); + } + if(r==KErrNone) + { + // we've successfully changed the cache size... + if(max) + { + sizeMin = min; + sizeMax = max; + } + else + { + sizeMin = originalMin; + sizeMax = originalMax; + } + } + if(r==KErrNoMemory) + { + // cache size after OOM is unpredictable, so reset our values + DPTest::SetCacheSize(sizeMin,sizeMax); + } + else + { + // test 'get' function returns expected cache size + r=DPTest::CacheSize(min,max,currentSize); + test.Printf(_L("DPTest::CacheSize result=%d min=%u max=%u current=%u\n"),r,min/PageSize,max/PageSize,currentSize/PageSize); + if(r!=KErrNone || min!=sizeMin || max!=sizeMax) + test(0); + test(currentSize >= min && currentSize <= max); + } + ++i; + } + } + + test.End(); + } + + + +void TestResizeVMCache2() + { + TUint originalMin = 0; + TUint originalMax = 0; + TUint currentSize = 0; + test_KErrNone(DPTest::CacheSize(originalMax, originalMax, currentSize)); + test_KErrNone(DPTest::SetCacheSize(1, originalMax)); + TUint sizeMin = 0; + TUint sizeMax = 0; + test_KErrNone(DPTest::CacheSize(sizeMin, sizeMax, currentSize)); + test(sizeMin > 1); + test_KErrNone(DPTest::SetCacheSize(originalMin, originalMax)); + } + + +void TestHAL() + { + test.Start(_L("DPTest::Attributes")); + TUint32 attr=DPTest::Attributes(); + test.Printf(_L("Attributes = %08x\n"),attr); + + test.Next(_L("DPTest::FlushCache")); + TInt r=DPTest::FlushCache(); + if(r==KErrNotSupported) + test.Printf(_L("Not Supported\n")); + else if(r<0) + { + test.Printf(_L("Error = %d\n"),r); + test(0); + } + + test.Next(_L("DPTest::CacheSize")); + TUint oldMin = 0; + TUint oldMax = 0; + TUint currentSize = 0; + r=DPTest::CacheSize(oldMin,oldMax,currentSize); + if(r==KErrNotSupported) + test.Printf(_L("Not Supported\n")); + else if(r<0) + { + test.Printf(_L("Error = %d\n"),r); + test(0); + } + else + { + test.Printf(_L("Size = %dk,%dk,%dk\n"),oldMin>>10,oldMax>>10,currentSize>>10); + } + + test.Next(_L("DPTest::SetCacheSize")); + r=DPTest::SetCacheSize(oldMin,oldMax); + if(r==KErrNotSupported) + test.Printf(_L("Not Supported\n")); + else if(r<0) + { + test.Printf(_L("Error = %d\n"),r); + test(0); + } + if(r==KErrNone) + { + TestResizeVMCache(); + TestResizeVMCache2(); + } + + test.End(); + } + +// Test IPC and realtime state + +enum TIpcDir + { + EServerRead, + EServerWrite + }; + +enum TIpcObjectPaged + { + ENothingPaged, + EDesHeaderPaged, + EDesContentPaged + }; + +enum TRealtimeOutcome + { + ENoError, + EBadDescriptor, + EServerTerminated, + ERealtimePanic + }; + +class RTestSession : public RSessionBase + { +public: + TInt Create(RServer2 aServer) + { + return CreateSession(aServer,TVersion(),-1); + } + inline TInt Send(const TIpcArgs& aArgs) + { + return RSessionBase::SendReceive(0,aArgs); + } + }; + +RServer2 TestServer; + +TInt IpcTestServerFunc(TAny* aArg) + { + TIpcDir dir = (TIpcDir)(((TInt)aArg) & 0xff); + TIpcObjectPaged paged = (TIpcObjectPaged)((((TInt)aArg) >> 8) & 0xff); + User::TRealtimeState realtime = (User::TRealtimeState)((((TInt)aArg) >> 16) & 0xff); + User::TRealtimeState clientRealtime = (User::TRealtimeState)((((TInt)aArg) >> 24) & 0xff); + + TInt r; + // We want the server to fault the client when it is realtime + // and accessing paged out memory. + r = TestServer.CreateGlobal(KNullDesC, EIpcSession_Sharable, EServerRole_Default, EServerOpt_PinClientDescriptorsDisable); + if (r != KErrNone) + return r; + RThread::Rendezvous(KErrNone); + + RMessage2 message; + TestServer.Receive(message); + if ((clientRealtime == User::ERealtimeStateOn) != message.ClientIsRealtime()) + return KErrGeneral; + message.Complete(KErrNone); // complete connection request + + TRequestStatus s; + TestServer.Receive(message,s); + User::WaitForRequest(s); + if (s != KErrNone) + return s.Int(); + + TInt32 unpagedContent; + TPtr8 unpagedDes((TUint8*)&unpagedContent, 4, 4); + TPtrC8 pagedContentBuf(SmallBuffer,sizeof(TInt)); + + TPtr8* dataPagedHeaderDes = (TPtr8*)DataPagedBuffer; + if (DataPagingSupported) + new (dataPagedHeaderDes) TPtr8((TUint8*)&unpagedContent, 4); + + TPtr8 dataPagedContentDes(DataPagedBuffer + PageSize, 4); + + r = DPTest::FlushCache(); + if(r != KErrNone) + return r; + + User::SetRealtimeState(realtime); + if (dir == EServerRead) + { + switch (paged) + { + case ENothingPaged: + r = message.Read(0,unpagedDes); + break; + + case EDesHeaderPaged: + r = DataPagingSupported ? message.Read(0,*dataPagedHeaderDes) : KErrNotSupported; + break; + + case EDesContentPaged: + r = DataPagingSupported ? message.Read(0,dataPagedContentDes) : KErrNotSupported; + break; + + default: + r = KErrArgument; + break; + } + } + else if (dir == EServerWrite) + { + switch (paged) + { + case ENothingPaged: + r = message.Write(0,unpagedDes); + break; + + case EDesHeaderPaged: + r = message.Write(0,*PagedHeaderDes); + break; + + case EDesContentPaged: + r = message.Write(0,pagedContentBuf); + break; + + default: + r = KErrArgument; + break; + } + } + else + r = KErrArgument; + User::SetRealtimeState(User::ERealtimeStateOff); + + message.Complete(KErrNone); + return r; + } + +TInt IpcTestClientFunc(TAny* aArg) + { + TIpcDir dir = (TIpcDir)(((TInt)aArg) & 0xff); + TIpcObjectPaged paged = (TIpcObjectPaged)((((TInt)aArg) >> 8) & 0xff); + User::TRealtimeState realtime = (User::TRealtimeState)((((TInt)aArg) >> 16) & 0xff); + + RTestSession session; + TInt r = session.Create(TestServer); + if(r != KErrNone) + return r; + + TInt32 unpagedContent; + TPtr8 unpagedDes((TUint8*)&unpagedContent, 4, 4); + TPtrC8 pagedContentBuf(SmallBuffer + PageSize, sizeof(TInt)); + + TPtr8* dataPagedHeaderDes = (TPtr8*)(DataPagedBuffer + (2 * PageSize)); + if (DataPagingSupported) + new (dataPagedHeaderDes) TPtr8((TUint8*)&unpagedContent, 4); + + TPtr8 dataPagedContentDes(DataPagedBuffer + (3 * PageSize), 4); + + r = DPTest::FlushCache(); + if(r != KErrNone) + return r; + + User::SetRealtimeState(realtime); + if (dir == EServerRead) + { + switch (paged) + { + case ENothingPaged: + r = session.Send(TIpcArgs(&unpagedDes)); + break; + + case EDesHeaderPaged: + r = session.Send(TIpcArgs(PagedHeaderDes)); + break; + + case EDesContentPaged: + r = session.Send(TIpcArgs(&pagedContentBuf)); + break; + + default: + r = KErrArgument; + break; + } + } + else if (dir == EServerWrite) + { + switch (paged) + { + case ENothingPaged: + r = session.Send(TIpcArgs(&unpagedDes)); + break; + + case EDesHeaderPaged: + r = DataPagingSupported ? session.Send(TIpcArgs(dataPagedHeaderDes)) : KErrNotSupported; + break; + + case EDesContentPaged: + r = DataPagingSupported ? session.Send(TIpcArgs(&dataPagedContentDes)) : KErrNotSupported; + break; + + default: + r = KErrArgument; + break; + } + } + else + r = KErrArgument; + User::SetRealtimeState(User::ERealtimeStateOff); + + session.Close(); + return r; + } + +void TestRealtimeOutcome(RThread aThread, TRealtimeOutcome aOutcome) + { + switch(aOutcome) + { + case ENoError: + test_Equal(EExitKill, aThread.ExitType()); + test_KErrNone(aThread.ExitReason()); + break; + + case EBadDescriptor: + test_Equal(EExitKill, aThread.ExitType()); + test_Equal(KErrBadDescriptor, aThread.ExitReason()); + break; + + case EServerTerminated: + test_Equal(EExitKill, aThread.ExitType()); + test_Equal(KErrServerTerminated, aThread.ExitReason()); + break; + + case ERealtimePanic: + test_Equal(EExitPanic, aThread.ExitType()); + test(aThread.ExitCategory()==_L("KERN-EXEC")); + test_Equal(EIllegalFunctionForRealtimeThread, aThread.ExitReason()); + break; + + default: + test(EFalse); + } + } + +void TestPagedIpc(TIpcDir aIpcDir, + TIpcObjectPaged aClientPaged, + TIpcObjectPaged aServerPaged, + User::TRealtimeState aClientState, + User::TRealtimeState aServerState, + TRealtimeOutcome aClientOutcome, + TRealtimeOutcome aServerOutcome) + { + test.Printf(_L("TestPagedIpc %d %d %d %d %d %d %d\n"), aIpcDir, aClientPaged, aServerPaged, + aClientState, aServerState, aClientOutcome, aServerOutcome); + + RThread serverThread; + RThread clientThread; + TRequestStatus serverStatus; + TRequestStatus clientStatus; + + TInt serverArg = aIpcDir | (aServerPaged << 8) | (aServerState << 16) | (aClientState << 24); + test_KErrNone(serverThread.Create(KNullDesC, &IpcTestServerFunc, 0x1000, NULL, (TAny*)serverArg)); + TName name; + name = serverThread.Name(); + test.Printf(_L(" server: %S\n"), &name); + serverThread.Rendezvous(serverStatus); + serverThread.Resume(); + User::WaitForRequest(serverStatus); + test_KErrNone(serverStatus.Int()); + serverThread.Logon(serverStatus); + + TInt clientArg = aIpcDir | (aClientPaged << 8) | (aClientState << 16); + test_KErrNone(clientThread.Create(KNullDesC, &IpcTestClientFunc, 0x1000, NULL, (TAny*)clientArg)); + name = clientThread.Name(); + test.Printf(_L(" client: %S\n"), &name); + clientThread.Logon(clientStatus); + clientThread.Resume(); + + User::WaitForRequest(serverStatus); + test.Printf(_L(" server exit type is %d %d\n"), serverThread.ExitType(), serverThread.ExitReason()); + TestServer.Close(); // because handle is process-relative, it's not closed if the server dies + + User::WaitForRequest(clientStatus); + test.Printf(_L(" client exit type is %d %d\n"), clientThread.ExitType(), clientThread.ExitReason()); + + TestRealtimeOutcome(serverThread, aServerOutcome); + TestRealtimeOutcome(clientThread, aClientOutcome); + + CLOSE_AND_WAIT(serverThread); + CLOSE_AND_WAIT(clientThread); + } + +TInt TestThreadFunction(TAny* aType) + { + // Ensure that pageable memory is paged out + TInt r=DPTest::FlushCache(); + if(r!=KErrNone) + return r; + + // Access pageable data whilst thread is in specified realttime state. + User::SetRealtimeState((User::TRealtimeState)(TInt)aType); + READ(SmallBuffer); + return KErrNone; + } + +TInt RunTestThread(User::TRealtimeState aType, TRealtimeOutcome aOutcome) + { + RThread thread; + TInt r=thread.Create(KNullDesC, &TestThreadFunction, 0x1000, NULL, (TAny*)aType); + if(r!=KErrNone) + return r; + TRequestStatus s; + thread.Logon(s); + if(s.Int()!=KRequestPending) + return s.Int(); + thread.Resume(); + User::WaitForRequest(s); + TestRealtimeOutcome(thread, aOutcome); + CLOSE_AND_WAIT(thread); + return KErrNone; + } + +void TestRealtimeState() + { + // make sure live list is big enough + test(KErrNone==DPTest::SetCacheSize(256*PageSize,256*PageSize)); + + test.Start(_L("Enable KREALTIME tracing")); + Ldd.SetRealtimeTrace(ETrue); + + test.Next(_L("Test ERealtimeStateOff")); + RunTestThread(User::ERealtimeStateOff, ENoError); + + test.Next(_L("Test ERealtimeStateOn")); + RunTestThread(User::ERealtimeStateOn, ERealtimePanic); + + test.Next(_L("Test ERealtimeStateWarn")); + RunTestThread(User::ERealtimeStateWarn, ENoError); + + test.Next(_L("Test combinations of IPC with realtime state")); + + // ipc dir: client paged: server paged: client state: server state: client outcome: server outcome: + TestPagedIpc(EServerRead, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + TestPagedIpc(EServerRead, EDesHeaderPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + TestPagedIpc(EServerRead, EDesContentPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + TestPagedIpc(EServerWrite, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + TestPagedIpc(EServerWrite, ENothingPaged, EDesHeaderPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + TestPagedIpc(EServerWrite, ENothingPaged, EDesContentPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + + if (DataPagingSupported) + { + TestPagedIpc(EServerRead, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + TestPagedIpc(EServerRead, ENothingPaged, EDesHeaderPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + TestPagedIpc(EServerRead, ENothingPaged, EDesContentPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + TestPagedIpc(EServerWrite, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + TestPagedIpc(EServerWrite, EDesHeaderPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + TestPagedIpc(EServerWrite, EDesContentPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, ENoError, ENoError); + } + + TestPagedIpc(EServerRead, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + TestPagedIpc(EServerRead, EDesHeaderPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + TestPagedIpc(EServerRead, EDesContentPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + TestPagedIpc(EServerWrite, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + TestPagedIpc(EServerWrite, ENothingPaged, EDesHeaderPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + TestPagedIpc(EServerWrite, ENothingPaged, EDesContentPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + + if (DataPagingSupported) + { + TestPagedIpc(EServerRead, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + TestPagedIpc(EServerRead, ENothingPaged, EDesHeaderPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + TestPagedIpc(EServerRead, ENothingPaged, EDesContentPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + TestPagedIpc(EServerWrite, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + TestPagedIpc(EServerWrite, EDesHeaderPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + TestPagedIpc(EServerWrite, EDesContentPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateWarn, ENoError, ENoError); + } + + TestPagedIpc(EServerRead, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, ENoError, ENoError); + TestPagedIpc(EServerRead, EDesHeaderPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, ENoError, ENoError); + TestPagedIpc(EServerRead, EDesContentPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, ERealtimePanic, EBadDescriptor); + TestPagedIpc(EServerWrite, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, ENoError, ENoError); + TestPagedIpc(EServerWrite, ENothingPaged, EDesHeaderPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, EServerTerminated, ERealtimePanic); + TestPagedIpc(EServerWrite, ENothingPaged, EDesContentPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, EServerTerminated, ERealtimePanic); + + if (DataPagingSupported) + { + TestPagedIpc(EServerRead, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, ENoError, ENoError); + TestPagedIpc(EServerRead, ENothingPaged, EDesHeaderPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, EServerTerminated, ERealtimePanic); + TestPagedIpc(EServerRead, ENothingPaged, EDesContentPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, EServerTerminated, ERealtimePanic); + TestPagedIpc(EServerWrite, ENothingPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, ENoError, ENoError); + TestPagedIpc(EServerWrite, EDesHeaderPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, ENoError, ENoError); + TestPagedIpc(EServerWrite, EDesContentPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOn, ERealtimePanic, EBadDescriptor); + } + + test.End(); + + // retore size of live list + test(KErrNone==DPTest::SetCacheSize(0,0)); + } + +void TestLock() + { + // make sure live list is big enough + test(KErrNone==DPTest::SetCacheSize(128 * PageSize, 256 * PageSize)); + + TInt r = Ldd.LockTest(SmallBuffer,SmallBufferSize); + if(r==KErrNone) + { + test.Next(_L("Lock Test again")); + r = Ldd.LockTest(SmallBuffer,SmallBufferSize); + } + if(r) + { + test.Printf(_L("failed at D_DEMANPAGING.CPP line %d\n"),r); + test(0); + } + + // restore live list to default size... + test(KErrNone==DPTest::SetCacheSize(0,0)); + } + +const TInt KSmallPropertySize = 512; +const TInt KLargePropertySize = 16384; + +struct SSetPropertyInfo + { + TUid iCategory; + TInt iKey; + TUint8* iData; + TInt iLength; + }; + +TInt SetPropertyThreadFunction(TAny* aArg) + { + SSetPropertyInfo* info = (SSetPropertyInfo*)aArg; + TInt r; + r = DPTest::FlushCache(); + if (r != KErrNone) + return r; + TPtrC8 data(info->iData, info->iLength); + r = RProperty::Set(info->iCategory, info->iKey, data); + if (r != KErrNone) + return r; + RBuf8 buffer; + r = buffer.Create(KLargePropertySize); + if (r != KErrNone) + return r; + r = RProperty::Get(info->iCategory, info->iKey, buffer); + if (r == KErrNone && buffer != data) + r = KErrGeneral; + buffer.Close(); + return r; + } + +void TestPublishAndSubscribe() + { + RProcess thisProcess; + TUid category = thisProcess.SecureId(); + + TSecurityPolicy alwaysPass(TSecurityPolicy::EAlwaysPass); + test(RProperty::Define(category, 0, RProperty::EByteArray, alwaysPass, alwaysPass) == KErrNone); + test(RProperty::Define(category, 1, RProperty::ELargeByteArray, alwaysPass, alwaysPass) == KErrNone); + + TPtrC8 smallData(SmallBuffer, KSmallPropertySize); + TPtrC8 largeData(SmallBuffer, KLargePropertySize); + + RBuf8 buffer; + test(buffer.Create(KLargePropertySize) == KErrNone); + + // Set small property from paged data, method 1 + test(DPTest::FlushCache() == KErrNone); + test(RProperty::Set(category, 0, smallData) == KErrNone); + test(RProperty::Get(category, 0, buffer) == KErrNone); + test(buffer == smallData); + + // Set small property from paged data, method 2 + RProperty smallProp; + test(smallProp.Attach(category, 0) == KErrNone); + test(DPTest::FlushCache() == KErrNone); + test(smallProp.Set(smallData) == KErrNone); + test(smallProp.Get(buffer) == KErrNone); + test(buffer == smallData); + + // Set large property from paged data, method 1 + test(DPTest::FlushCache() == KErrNone); + test(RProperty::Set(category, 1, largeData) == KErrNone); + test(RProperty::Get(category, 1, buffer) == KErrNone); + test(buffer == largeData); + + // Set large property from paged data, method 2 + RProperty largeProp; + test(largeProp.Attach(category, 1) == KErrNone); + test(DPTest::FlushCache() == KErrNone); + test(largeProp.Set(largeData) == KErrNone); + test(largeProp.Get(buffer) == KErrNone); + test(buffer == largeData); + + // Set small property from unmapped address + RThread thread; +#if !defined( __VC32__) + SSetPropertyInfo info = { category, 0, 0, KSmallPropertySize }; +#else + SSetPropertyInfo info = { category.iUid, 0, 0, KSmallPropertySize }; +#endif + test(thread.Create(_L("SetPropertyThread"), SetPropertyThreadFunction, 4096, NULL, &info) == KErrNone); + thread.Resume(); + TRequestStatus status; + thread.Logon(status); + User::WaitForRequest(status); + test(thread.ExitType()==EExitPanic); + test(thread.ExitCategory()==_L("KERN-EXEC")); + test(thread.ExitReason()==ECausedException); + thread.Close(); + + // Set large property from unmapped address + info.iKey = 1; + info.iLength = KLargePropertySize; + test(thread.Create(_L("SetPropertyThread"), SetPropertyThreadFunction, 4096, NULL, &info) == KErrNone); + thread.Resume(); + thread.Logon(status); + User::WaitForRequest(status); + test(thread.ExitType()==EExitPanic); + test(thread.ExitCategory()==_L("KERN-EXEC")); + test(thread.ExitReason()==ECausedException); + thread.Close(); + + test(RProperty::Delete(category, 0) == KErrNone); + test(RProperty::Delete(category, 1) == KErrNone); + } + +void TestWriteToPagedArea() + { + RMemoryTestLdd memoryTest; + test(KErrNone==memoryTest.Open()); + + TModuleMemoryInfo exeInfo; + test(KErrNone==RProcess().GetMemoryInfo(exeInfo)); + test.Printf(_L("test program code is %x+%x"),exeInfo.iCodeBase,exeInfo.iCodeSize); + + TUint8* ptr = const_cast(LargeBuffer); + TUint8* end = ptr + LargeBufferSize; + while(ptr=(TUint8*)_ALIGN_DOWN(exeInfo.iCodeBase,PageSize) && ptr<(TUint8*)_ALIGN_UP(exeInfo.iCodeBase+exeInfo.iCodeSize,PageSize)) + { + // avoid testing the ROM which contains this test program + ptr += PageSize; + continue; + } + + DPTest::FlushCache(); + + TInt stateBits = UserSvr::HalFunction(EHalGroupVM, EVMPageState, ptr, 0); + test(stateBits>=0); + // write to paged out memory should cause exception... + test(KErrBadDescriptor==memoryTest.WriteMemory(ptr,0)); + // page state should be unchanged... + TInt newStateBits = UserSvr::HalFunction(EHalGroupVM, EVMPageState, ptr, 0); + if(stateBits!=newStateBits) + { + test.Printf(_L("ptr=%x stateBits=%x newStateBits=%x"),ptr,stateBits,newStateBits); + test(0); + } + // page-in in memory... + TUint32 value = *(TUint32*)ptr; + // write to paged out memory should still cause exception... + test(KErrBadDescriptor==memoryTest.WriteMemory(ptr,~value)); + // memory should be unchanged... + test(value==*(TUint32*)ptr); + ptr += PageSize; + } + + memoryTest.Close(); + } + +void TestContiguousRamAlloc() + { + test.Start(_L("Start...")); + + const TInt KCacheSize = 1024*1024; + + DPTest::SetCacheSize(0, KCacheSize); // make sure paging cache is a reasonable size + + TInt testData[][2] = /* array of page size (in units of 'half pages') and align values */ + { + {64,5}, + {64,0}, + {32,4}, + {32,3}, + {32,2}, + {32,1}, + {32,0}, + {4,2}, + {4,1}, + {4,0}, + {2,2}, + {2,1}, + {2,0}, + {1,0}, + {0,0} + }; + TInt pageShift = 1; + while((1< title; + title.AppendFormat(_L("Contiguous RAM test: alloc size = %dK align = %d"),size>>10, align); + test.Next(title); + FragmentPagingCache(KCacheSize); + TInt r = Ldd.DoConsumeContiguousRamTest(align, size); + if(r) + { + test.Printf(_L("failed at D_DEMANPAGING.CPP line %d\n"),r); + test(0); + } + } + + DPTest::SetCacheSize(0,0); // back to defaults + test.End(); + } + +void TestReadHoldingMutex() + { + TUint8 localBuf[16]; + TUint8* localPtr = localBuf; + if(DPTest::Attributes() & DPTest::EDataPaging) // if data paging supported... + localPtr = 0; // use zero to make driver use kernel memory as data destination + test(Ldd.ReadHoldingMutexTest(localPtr) == KErrNone); + } + +#if 0 // rom dump code... +#include + RFs fs; + RFile file; + test(KErrNone==fs.Connect()); + test(KErrNone==file.Replace(fs, _L("d:\\ROMDUMP"),EFileWrite)); + TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress(); + TPtrC8 rom((TUint8*)romHeader,romHeader->iRomSize); + test(KErrNone==file.Write(rom)); + file.Close(); + fs.Close(); + return 0; +#endif + + +class RMySession : public RSessionBase + { +public: + TInt Connect(RServer2 aSrv,TRequestStatus& aStat) + { + TInt r=CreateSession(aSrv,TVersion(),-1,EIpcSession_Sharable,NULL,&aStat); + if(!r) ShareAuto(); return r; + } + TInt Send(TInt function,const TIpcArgs& args) + {return SendReceive(function,args);} + }; + + +TUint8* TestBuffer = 0; +RMySession MySession; + +LOCAL_C TInt TestServerThread(TAny* aSize) + { + TInt r = TestServer.CreateGlobal(KNullDesC, EIpcSession_GlobalSharable); + if(r==KErrNone) + { + TestBuffer = (TUint8*)User::Alloc(KMaxIPCSize); + if(!TestBuffer) + r = KErrNoMemory; + } + TPtr8 buffer(TestBuffer,KMaxIPCSize); + RThread::Rendezvous(r); + if (r != KErrNone) + return r; + + RMessage2 m; + TestServer.Receive(m); + m.Complete(KErrNone); // connect message + + TBool running = ETrue; + while (running) + { + TestServer.Receive(m); + RDebug::Printf("Server received: %d", m.Function()); + + TInt r = KErrNone; + switch(m.Function()) + { + case 0: + // Kill server + running = EFalse; + break; + + case 2: + buffer.Set(SharedBuffer,KMaxIPCSize,KMaxIPCSize); + // fall through... + case 1: + // Perform CRC of data passed + { + DPTest::FlushCache(); + r=m.Read(0,buffer); + if (r!=KErrNone) + break; + TUint32 crc=0; + Mem::Crc32(crc,buffer.Ptr(),buffer.Size()); + r = crc; + } + break; + + case 4: + buffer.Set(SharedBuffer,KMaxIPCSize,KMaxIPCSize); + // fall through... + case 3: + // Write data to client descriptor + { + DPTest::FlushCache(); + RDebug::Printf("Server writing %08x+%x", m.Int1(), m.Int2()); + TPtrC8 ptr((TUint8*)m.Int1(),m.Int2()); + r=m.Write(0,ptr); + } + break; + + default: + // Just complete anything else + break; + } + m.Complete(r); + } + + RDebug::Printf("Server exiting"); + User::Free(TestBuffer); + TestBuffer = NULL; + TestServer.Close(); + return KErrNone; + } + +void TestIPC() + { + __KHEAP_MARK; + + const TUint8* start = LargeBuffer + 0x3df; // make range not page aligned + const TUint8* end = start + Min(LargeBufferSize, KMaxIPCSize * 10) - 0x130; // make range not page aligned + const TUint8* pos; + + test.Start(_L("Create server")); + RThread t; + TInt r=t.Create(KNullDesC,TestServerThread,0x1000,KMaxIPCSize+0x1000,KMaxIPCSize+0x1000,(void*)0); + test(r==KErrNone); + t.SetPriority(EPriorityMore); + TRequestStatus s; + t.Rendezvous(s); + t.Resume(); + User::WaitForRequest(s); + test(TestServer.Handle() != KNullHandle); + + test(MySession.Connect(TestServer,s) == KErrNone); + User::WaitForRequest(s); // connected + + TInt bufferType; // 0=server uses heap, 1=server uses SharedBuffer + for(bufferType=0; bufferType<=1; ++bufferType) + { + test.Next(_L("IPC read from ROM")); + pos = start; + while(posKMaxIPCSize) + size = KMaxIPCSize; + RDebug::Printf("read %x+%x",pos,size); + TPtrC8 ptr(pos,size); + TInt r = MySession.Send(1+bufferType,TIpcArgs(&ptr)); + DPTest::FlushCache(); + TUint32 crc=0; + Mem::Crc32(crc,pos,size); + RDebug::Printf("crc %08x %08x",r,crc); + if((TUint32)r!=crc) + { + RDebug::Printf("FAIL"); + DPTest::FlushCache(); + TInt count = 0; + for(TInt i=0; i 100) + break; + } + } + test((TUint32)r==crc); + pos+=size; + } + + test.Next(_L("IPC write from ROM")); + pos = start; + while(posKMaxIPCSize) + size = KMaxIPCSize; + RDebug::Printf("write %x+%x",pos,size); + memclr(TestBuffer, KMaxIPCSize); + TPtr8 ptr(TestBuffer,KMaxIPCSize); // reuse the server's buffer + TInt r = MySession.Send(3+bufferType,TIpcArgs(&ptr,pos,size)); + test_KErrNone(r); + DPTest::FlushCache(); + TUint32 crc=0; + Mem::Crc32(crc,pos,size); + TUint32 crc2=0; + Mem::Crc32(crc2,TestBuffer,size); + RDebug::Printf("crc %08x %08x",crc,crc2); + if((TUint32)crc!=crc2) + { + RDebug::Printf("FAIL"); + DPTest::FlushCache(); + TInt count = 0; + for(TInt i=0; i 100) + break; + } + } + test((TUint32)crc==crc2); + pos+=size; + } + } + + if (DPTest::Attributes() & DPTest::ERomPaging) + { + test.Next(_L("Test passing descriptor headers in paged-out memory")); + __KHEAP_MARK; + + DPTest::FlushCache(); + TInt r = MySession.Send(5,TIpcArgs(PagedHeaderDes)); + test_Equal(KErrNone, r); + + UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0); + __KHEAP_MARKEND; + } + + test.Next(_L("Stop server")); + MySession.Send(0,TIpcArgs(0)); + MySession.Close(); + t.Logon(s); + User::WaitForRequest(s); + test_Equal(EExitKill, t.ExitType()); + test_Equal(KErrNone, t.ExitReason()); + CLOSE_AND_WAIT(t); + test.End(); + + UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0); + __KHEAP_MARKEND; + } + + +TInt E32Main() + { + test.Title(); + + test_KErrNone(UserSvr::HalFunction(EHalGroupKernel,EKernelHalPageSizeInBytes,&PageSize,0)); + + if (DPTest::Attributes() & DPTest::ERomPaging) + test.Printf(_L("Rom paging supported\n")); + if (DPTest::Attributes() & DPTest::ECodePaging) + test.Printf(_L("Code paging supported\n")); + if (DPTest::Attributes() & DPTest::EDataPaging) + { + test.Printf(_L("Data paging supported\n")); + DataPagingSupported = ETrue; + TChunkCreateInfo createInfo; + createInfo.SetNormal(KMinBufferSize, KMinBufferSize); + createInfo.SetPaging(TChunkCreateInfo::EPaged); + createInfo.SetOwner(EOwnerProcess); + createInfo.SetGlobal(KChunkName); + test_KErrNone(DataPagedChunk.Create(createInfo)); + test(DataPagedChunk.IsPaged()); // this is only ever called if data paging is supported + DataPagedBuffer = (TUint8*)DataPagedChunk.Base(); + } + + test.Start(_L("Test HAL interface")); + TestHAL(); + + if (DPTest::Attributes() & DPTest::ERomPaging) + { + // Use paged part of rom for testing + TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress(); + test(romHeader->iPageableRomStart); + // todo: for some reason the first part of page of paged rom doesn't seem to get paged out + // when we flush the paging cache, hence LargeBuffer starts some way into this + LargeBuffer = (TUint8*)romHeader + romHeader->iPageableRomStart + 64 * PageSize; + LargeBufferSize = romHeader->iPageableRomSize - 64 * PageSize; + test(LargeBufferSize > 0); + // Find a zero word in rom to set PagedHeaderDes to + TUint* ptr = (TUint*)LargeBuffer; + TUint* end = (TUint*)(LargeBuffer + LargeBufferSize); + while (*ptr && ptr < end) + ++ptr; + test(*ptr == 0); + test.Printf(_L("Found zero word at %08x\n"), ptr); + PagedHeaderDes = (TDesC8*)ptr; + } + else if (DPTest::Attributes() & DPTest::ECodePaging) + { + // Use code paged DLL for testing + test_KErrNone(PagedLibrary.Load(KTCodePagingDll4)); + TGetAddressOfDataFunction func = (TGetAddressOfDataFunction)PagedLibrary.Lookup(KGetAddressOfDataFunctionOrdinal); + LargeBuffer = (TUint8*)func(LargeBufferSize); + test_NotNull(LargeBuffer); + PagedHeaderDes = (TDesC8*)LargeBuffer + 4; + } + else if (DPTest::Attributes() & DPTest::EDataPaging) + { + // Use data paged chunk for testing + LargeBuffer = DataPagedBuffer; + LargeBufferSize = KMinBufferSize; + } + else + { + test.Printf(_L("Demand Paging not supported\n")); + test.End(); + return 0; + } + + test(LargeBufferSize >= KMinBufferSize); + SmallBuffer = LargeBuffer; + SmallBufferSize = KMinBufferSize; + + test.Next(_L("Loading test drivers")); + TInt r = User::LoadLogicalDevice(KDemandPagingTestLddName); + test(r==KErrNone || r==KErrAlreadyExists); + test(Ldd.Open()==KErrNone); + + test_KErrNone(Ldd.CreatePlatHwChunk(SharedBufferSize, SharedBufferAddr)); + SharedBuffer = (TUint8*)SharedBufferAddr; + + RDebug::Printf("SmallBuffer=%x, LargeBuffer=%x, SharedBuffer=%x\n", + SmallBuffer, LargeBuffer, SharedBuffer); + + test.Next(_L("Gobble RAM")); + r = User::LoadLogicalDevice(KGobblerLddFileName); + test(r==KErrNone || r==KErrAlreadyExists); + RGobbler gobbler; + r = gobbler.Open(); + test(r==KErrNone); + TUint32 taken = gobbler.GobbleRAM(64*1024*1024); // leave 64MB of free RAM + test.Printf(_L("Gobbled: %dK\n"), taken/1024); + test.Printf(_L("Free RAM 0x%08X bytes\n"),FreeRam()); + + test.Next(_L("Test contiguous RAM allocation reclaims paged memory")); + TestContiguousRamAlloc(); + + test.Next(_L("Test thread realtime state")); + TestRealtimeState(); + + test.Next(_L("Lock Test")); + TestLock(); + + test.Next(_L("Test writing to paged area")); + TestWriteToPagedArea(); + + test.Next(_L("Test IPC read from paged memory")); + TestIPC(); + + test.Next(_L("Test no kernel faults when copying data from unpaged rom with mutex held")); + TestReadHoldingMutex(); + + test.Next(_L("Close test driver")); + Ldd.DestroyPlatHwChunk(); + Ldd.Close(); + + test.Next(_L("Test setting publish and subscribe properties from paged area")); + TestPublishAndSubscribe(); + + if (DPTest::Attributes() & DPTest::ERomPaging) + { + test.Next(_L("Rom Paging Benchmark")); + RomPagingBenchmark(); + } + + PagedLibrary.Close(); + gobbler.Close(); + test.End(); + + return 0; + }