diff -r 000000000000 -r bde4ae8d615e os/kernelhwsrv/kerneltest/e32test/mmu/t_sharedchunk.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/kernelhwsrv/kerneltest/e32test/mmu/t_sharedchunk.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,1435 @@ +// Copyright (c) 2004-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_sharedchunk.cpp +// Overview: +// Test sharing an RChunk with a logical device +// API Information: +// RChunk +// Details: +// - Load and open the logical device driver ("D_SHAREDCHUNK"). Verify +// results. +// - Test and verify results of creating shared chunks under OOM conditions. Also verify +// creating a chunk with a bad type, bad size and too large all fail as +// expected. +// - Test and verify opening and closing chunk user handles work as expected. +// - Test and verify thread local and process local handles work as expected +// - Test and verify setting restrictions on RChunk if created as shared chunk are as expected. +// - Test and verify memory access for multiply and singly shared chunks, +// is as expected. Including IPC, kernel, DFC and ISR reads & writes. +// - Test and verify discontinuous memory commits for multiply and singly +// shared chunks are as expected. +// - Test and verify continuous memory commits for multiply and singly shared +// chunks are as expected. +// - Test and verify discontinuous and continuous physical memory commits for +// multiply and singly shared chunks is as expected. +// - Test Kern::OpenSharedChunk for multiply and singly shared chunks. Verify +// results are as expected. +// - Test that physical memory can be freed immediately after the chunk that mapped +// it has been closed. +// Platforms/Drives/Compatibility: +// All. +// Assumptions/Requirement/Pre-requisites: +// Failures and causes: +// Base Port information: +// +// + +//! @file +//! @SYMTestCaseID KBASE-T_SHAREDCHUNK +//! @SYMREQ 3699 +//! @SYMTestPriority High +//! @SYMTestActions Check creation, memory allocation and access to Shared Chunks +//! @SYMTestExpectedResults Test runs until this message is emitted: RTEST: SUCCESS : T_SHAREDCHUNK test completed O.K. +//! @SYMTestType UT +#define __E32TEST_EXTENSION__ + +#include "d_sharedchunk.h" +#include "d_gobble.h" +#include +#include +#include "u32std.h" +#include +#include +#include +#include +#include +#include "freeram.h" + +enum TSlaveCommand + { + ESlaveCheckChunk, + ESlaveCreateChunk, + }; + +// +// Global data +// + + +RSharedChunkLdd Ldd; +RChunk TheChunk; +TInt PageSize; +TUint32 MemModelAttributes; +TBool PhysicalCommitSupported; +TBool CachingAttributesSupported; +TUint8 BssMem[100]; + +const TUint ChunkSize = 0x400000; // 4 meg reserved space for test chunk + +_LIT(KSecondProcessName,"t_sharedchunk"); +#ifdef __T_SHAREDCHUNKF__ +_LIT(KOtherProcessName,"t_sharedchunk"); +LOCAL_D RTest test(_L("T_SHAREDCHUNKF")); +#else +_LIT(KOtherProcessName,"t_sharedchunkf"); +LOCAL_D RTest test(_L("T_SHAREDCHUNK")); +#endif + +_LIT8(KTestString, "lks4b7qeyfcea5fyaifyaefyi4flwdysuxanabxa"); +_LIT8(KTestString2,"jhfcalurnhfirlxszhrvcvduhrvndrucxnshxcsx"); +const TUint32 KTestValue = 0x12345678; +const TUint32 KTestValue2 = KTestValue^0x0f0f0f0f; + +// +// Utilitiies for use by tests +// + + +_LIT(KLitKernExec, "KERN-EXEC"); + +void CheckFailMessage(const char* n,const char* c,const char* f,TInt r) + { + TPtrC8 nn((const TUint8*)n); + TPtrC8 cc((const TUint8*)c); + TPtrC8 ff((const TUint8*)f); + RBuf8 buf; + buf.Create((nn.Size()+cc.Size()+ff.Size()+64)*2); + buf.AppendFormat(_L8("\nCHECK failed: %S == 0x%x but was tested for %S%S\n"),&ff,r,&cc,&nn); + test.Printf(buf.Expand()); + buf.Close(); + } + +#define CHECK(n,c,f) \ + { \ + TInt _r=(TInt)(f); \ + if(!((TInt)(n)c(_r))) \ + { \ + CheckFailMessage(#n,#c,#f,_r); \ + test(0); \ + } \ + } + +#define KCHECK_MEMORY(result,offset) \ + { \ + /* test.Printf(_L("check offset 0x%08x\r"),offset); */ \ + CHECK(result,==,Ldd.CheckMemory(offset)); \ + } + +#define KWRITE_MEMORY(offset,value) \ + { \ + CHECK(KErrNone,==,Ldd.WriteMemory(offset,value)); \ + } + +#define UREAD_MEMORY(offset,value) \ + { \ + CHECK(value,==,*(TUint*)(Base+offset)); \ + } + +inline TUint32 Tag(TUint32 offset) + { return (69069u*(offset*4+1)); } + +TInt MemoryAccessThread(TAny* aAddress) + { + TInt r = *(volatile TUint8*)aAddress; // read from aAddress + (void)r; + return 0; + } + +TInt CheckUMemory(TAny* aAddress) + { + RThread thread; + thread.Create(KNullDesC,MemoryAccessThread,PageSize,&User::Heap(),aAddress); + TRequestStatus status; + thread.Logon(status); + TBool jit = User::JustInTime(); + User::SetJustInTime(EFalse); + thread.Resume(); + User::WaitForRequest(status); + User::SetJustInTime(jit); + TInt r; + if(thread.ExitType()==EExitKill && thread.ExitReason()==0) + r = 1; // Memory access pass + else if(thread.ExitType()==EExitPanic && thread.ExitCategory()==KLitKernExec && thread.ExitReason()==3 ) + r = 0; // Memory access failed + else + r = -1; // Unexpected result + CLOSE_AND_WAIT(thread); + return r; + } + +#define UCHECK_MEMORY(result,offset) \ + { \ + /* test.Printf(_L("ucheck offset 0x%08x\r"),offset); */ \ + CHECK(result,==,CheckUMemory(Base+offset)); \ + } + +TInt CheckPlatSecPanic(TThreadFunction aThreadFunction,TInt aThreadArgument) + { + RThread thread; + thread.Create(KNullDesC,aThreadFunction,PageSize,&User::Heap(),(TAny*)aThreadArgument); + TRequestStatus status; + thread.Logon(status); + TBool jit = User::JustInTime(); + User::SetJustInTime(EFalse); + thread.Resume(); + User::WaitForRequest(status); + User::SetJustInTime(jit); + TInt r; + if(thread.ExitType()==EExitPanic && thread.ExitCategory()==KLitKernExec && thread.ExitReason()==46 ) + r = 1; // PlatSec panic + else if(thread.ExitType()==EExitKill && thread.ExitReason()==0) + r = 0; // Exit without error + else + r = -1; // Unexpected result + CLOSE_AND_WAIT(thread); + return r; + } + +// +// The tests +// + +void CreateWithOomCheck(TInt aCreateFlags) + { + TInt failResult=KErrGeneral; + for(TInt failCount=1; failCount<1000; failCount++) + { + test.Printf(_L("alloc fail count = %d\n"),failCount); + User::__DbgSetBurstAllocFail(ETrue,RAllocator::EFailNext,failCount,1000); + __KHEAP_MARK; + failResult = Ldd.CreateChunk(aCreateFlags); + if(failResult==KErrNone) + break; + CHECK(KErrNoMemory,==,failResult); + Ldd.IsDestroyed(); // This includes delay to let idle thread do cleanup + __KHEAP_MARKEND; + } + User::__DbgSetAllocFail(ETrue,RAllocator::ENone,0); + __KHEAP_RESET; + CHECK(KErrNone,==,failResult); + } + +void TestCreate() + { + test.Start(_L("Creating chunk type Single,OwnsMemory")); + CreateWithOomCheck(ChunkSize|ESingle|EOwnsMemory); + CHECK(0,==,Ldd.IsDestroyed()); + + test.Next(_L("Close kernel handle")); + CHECK(1,==,Ldd.CloseChunk()); // 1==DObject::EObjectDeleted + CHECK(1,==,Ldd.IsDestroyed()); + + test.Next(_L("Creating chunk type Multiple,OwnsMemory")); + CreateWithOomCheck(ChunkSize|EMultiple|EOwnsMemory); + CHECK(0,==,Ldd.IsDestroyed()); + + test.Next(_L("Close kernel handle")); + CHECK(1,==,Ldd.CloseChunk()); // 1==DObject::EObjectDeleted + CHECK(1,==,Ldd.IsDestroyed()); + + if(PhysicalCommitSupported) + { + test.Next(_L("Creating chunk type Single,!OwnsMemory")); + CreateWithOomCheck(ChunkSize|ESingle); + CHECK(0,==,Ldd.IsDestroyed()); + + test.Next(_L("Close kernel handle")); + CHECK(1,==,Ldd.CloseChunk()); // 1==DObject::EObjectDeleted + CHECK(1,==,Ldd.IsDestroyed()); + + test.Next(_L("Creating chunk type Multiple,!OwnsMemory")); + CreateWithOomCheck(ChunkSize|EMultiple); + CHECK(0,==,Ldd.IsDestroyed()); + + test.Next(_L("Close kernel handle")); + CHECK(1,==,Ldd.CloseChunk()); // 1==DObject::EObjectDeleted + CHECK(1,==,Ldd.IsDestroyed()); + } + else + { + test.Next(_L("Try creating unsupported chunk type Single,!OwnsMemory")); + CHECK(KErrNotSupported,==,Ldd.CreateChunk(ChunkSize|ESingle)); + + test.Next(_L("Try creating unsupported chunk type Multiple,!OwnsMemory")); + CHECK(KErrNotSupported,==,Ldd.CreateChunk(ChunkSize|EMultiple)); + } + + test.Next(_L("__KHEAP_MARK")); + __KHEAP_MARK; + + test.Next(_L("Creating chunk (bad type)")); + CHECK(KErrArgument,==,Ldd.CreateChunk(EBadType|EOwnsMemory|ChunkSize)); + + test.Next(_L("Creating chunk (bad size)")); + CHECK(KErrArgument,==,Ldd.CreateChunk(ESingle|EOwnsMemory|0xffffff00)); + + test.Next(_L("Creating chunk (size too big)")); + CHECK(KErrNoMemory,==,Ldd.CreateChunk(ESingle|EOwnsMemory|0x7fffff00)); + + test.Next(_L("__KHEAP_MARKEND")); + __KHEAP_MARKEND; + + test.End(); + } + + +void OpenWithOomCheck(RChunk& aChunk) + { + TInt failResult=KErrGeneral; + for(TInt failCount=1; failCount<1000; failCount++) + { + test.Printf(_L("alloc fail count = %d\n"),failCount); + User::__DbgSetBurstAllocFail(ETrue,RAllocator::EFailNext,failCount,1000); + __KHEAP_MARK; + failResult = Ldd.GetChunkHandle(aChunk); + if(failResult==KErrNone) + break; + CHECK(KErrNoMemory,==,failResult); + Ldd.IsDestroyed(); // This includes delay to let idle thread do cleanup + __KHEAP_MARKEND; + } + User::__DbgSetAllocFail(ETrue,RAllocator::ENone,0); + __KHEAP_RESET; + CHECK(KErrNone,==,failResult); + } + +void TestHandles() + { + TUint ChunkAttribs = ChunkSize|ESingle|EOwnsMemory; + + test.Start(_L("Create chunk")); + CHECK(KErrNone,==,Ldd.CreateChunk(ChunkAttribs)); + + test.Next(_L("Open user handle")); + OpenWithOomCheck(TheChunk); + + test.Next(_L("Close user handle")); + TheChunk.Close(); + + test.Next(_L("Check chunk not destroyed")); + CHECK(0,==,Ldd.IsDestroyed()); + + test.Next(_L("Close kernel handle")); + CHECK(1,==,Ldd.CloseChunk()); // 1==DObject::EObjectDeleted + + test.Next(_L("Check chunk destroyed")); + CHECK(1,==,Ldd.IsDestroyed()); + + // Another chunk - closing handles in reverse order + + test.Next(_L("Create chunk")); + CHECK(KErrNone,==,Ldd.CreateChunk(ChunkAttribs)); + + test.Next(_L("Open user handle")); + OpenWithOomCheck(TheChunk); + + test.Next(_L("Close kernel handle")); + CHECK(KErrNone,==,Ldd.CloseChunk()); + + test.Next(_L("Check chunk not destroyed")); + CHECK(0,==,Ldd.IsDestroyed()); + + test.Next(_L("Using user handle to check chunk info")); + if((MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + CHECK(0,==,TheChunk.Size()); + } + CHECK(ChunkSize,==,TheChunk.MaxSize()); + + test.Next(_L("Close user handle")); + TheChunk.Close(); + + test.Next(_L("Check chunk destroyed")); + CHECK(1,==,Ldd.IsDestroyed()); + + test.End(); + } + +TInt HandleOwnershipThread(TAny* aArg) + { + // Use existing handle and attempt to read from chunk + TInt handle = (TInt) aArg; + RChunk chunk; + chunk.SetHandle(handle); + TInt r = *(volatile TUint8*)chunk.Base(); + (void)r; + CLOSE_AND_WAIT(chunk); + return KErrNone; + } + +void TestHandleOwnership() + { + TUint ChunkAttribs = ChunkSize|ESingle|EOwnsMemory; + RThread thread; + TRequestStatus rs; + + test.Start(_L("Create chunk")); + CHECK(KErrNone,==,Ldd.CreateChunk(ChunkAttribs)); + + test.Next(_L("Commit page to chunk")); + CHECK(KErrNone,==,Ldd.CommitMemory(EDiscontiguous,PageSize)); + + test.Next(_L("Check can access memory kernel side")); + KCHECK_MEMORY(ETrue, 0); + + // Handle is thread-owned + test.Next(_L("Open user handle (thread-owned)")); + CHECK(0,<=,Ldd.GetChunkHandle(TheChunk, ETrue)); + + test.Next(_L("Get memory size info")); + if((MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + CHECK(PageSize,==,TheChunk.Size()); + } + CHECK(ChunkSize,==,TheChunk.MaxSize()); + TUint8* Base = TheChunk.Base(); + CHECK(Base,!=,0); + + test.Next(_L("Check can access memory user side")); + UCHECK_MEMORY(ETrue, 0); + + test.Next(_L("Use handle in a new thread")); + CHECK(KErrNone,==,thread.Create(_L("thread1"), HandleOwnershipThread, KDefaultStackSize, KMinHeapSize, KMinHeapSize, (TAny*)TheChunk.Handle())); + thread.Logon(rs); + thread.Resume(); + User::WaitForRequest(rs); + CHECK(EExitPanic,==,thread.ExitType()); + CHECK(0,==,thread.ExitReason()); // KERN-EXEC 0 + CLOSE_AND_WAIT(thread); + + test.Next(_L("Close user handle")); + TheChunk.Close(); + + // Handle is process-owned + test.Next(_L("Open user handle (process-owned")); + CHECK(0,<=,Ldd.GetChunkHandle(TheChunk, EFalse)); + + test.Next(_L("Check can access memory user side")); + UCHECK_MEMORY(ETrue, 0); + + test.Next(_L("Close kernel handle")); + CHECK(KErrNone,==,Ldd.CloseChunk()); + + test.Next(_L("Check chunk destroyed")); + CHECK(0,==,Ldd.IsDestroyed()); + + test.Next(_L("Use handle in a new thread")); + CHECK(KErrNone,==,thread.Create(_L("thread2"), HandleOwnershipThread, KDefaultStackSize, KMinHeapSize, KMinHeapSize, (TAny*)TheChunk.Handle())); + thread.Logon(rs); + thread.Resume(); + User::WaitForRequest(rs); + CHECK(EExitKill,==,thread.ExitType()); + CHECK(KErrNone,==,thread.ExitReason()); + CLOSE_AND_WAIT(thread); + + test.Next(_L("Check chunk destroyed")); + CHECK(1,==,Ldd.IsDestroyed()); // Object was deleted + + test.End(); + } + +void SetCreateFlags(TUint& aCreateFlags,TCommitType aCommitType) + { + if(!((TInt)aCommitType&EPhysicalMask)) + aCreateFlags |= EOwnsMemory; + else + aCreateFlags &= ~EOwnsMemory; + } + + +void TestAccess(TUint aCreateFlags,TCommitType aCommitType) + { + const TUint32 offset = 0; + const TUint32 size = PageSize; + + SetCreateFlags(aCreateFlags,aCommitType); + + test.Start(_L("Create chunk")); + TUint8* kernelAddress; + CHECK(KErrNone,==,Ldd.CreateChunk(aCreateFlags|ChunkSize,(TAny**)&kernelAddress)); + + if((MemModelAttributes&TUint32(EMemModelAttrNonExProt)) && (MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + test.Next(_L("Check can't access memory")); + KCHECK_MEMORY(EFalse,offset); + } + + test.Next(_L("Commit page to chunk")); + CHECK(KErrNone,==,Ldd.CommitMemory(aCommitType|offset,size)); + + test.Next(_L("Check can access memory kernel side")); + KCHECK_MEMORY(ETrue, offset); + + if((MemModelAttributes&EMemModelAttrKernProt) && (MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + test.Next(_L("Check user side can't access kernel memory")); + TUint8* Base = kernelAddress; + UCHECK_MEMORY(EFalse, offset); + } + + test.Next(_L("Open user handle")); + CHECK(KErrNone,==,Ldd.GetChunkHandle(TheChunk)); + + test.Next(_L("Get memory size info")); + if((MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + CHECK(PageSize,==,TheChunk.Size()); + } + CHECK(ChunkSize,==,TheChunk.MaxSize()); + TUint8* Base = TheChunk.Base(); + CHECK(Base,!=,0); + + test.Next(_L("Check can access memory user side")); + UCHECK_MEMORY(ETrue, offset); + + test.Next(_L("Check user and kernel access same memory")); + KWRITE_MEMORY(offset,~Tag(offset)); + UREAD_MEMORY(offset,~Tag(offset)); + KWRITE_MEMORY(offset,Tag(offset)); + UREAD_MEMORY(offset,Tag(offset)); + + test.Next(_L("Close user handle")); + CHECK(0,==,Ldd.CloseChunkHandle(TheChunk)); + CHECK(0,==,Ldd.IsDestroyed()); + + if((MemModelAttributes&EMemModelAttrKernProt) && (MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + test.Next(_L("Check can no-longer access memory user side")); + UCHECK_MEMORY(EFalse,offset); + } + + test.Next(_L("Check can still access memory kernel side")); + KCHECK_MEMORY(ETrue, offset); + + test.Next(_L("Open user handle again")); + CHECK(KErrNone,==,Ldd.GetChunkHandle(TheChunk)); + + test.Next(_L("Check can access chunk user side again")); + CHECK(Base,==,TheChunk.Base()); + CHECK(ChunkSize,==,TheChunk.MaxSize()); + if((MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + CHECK(size,==,TheChunk.Size()); + } + UREAD_MEMORY(offset,Tag(offset)); + + test.Next(_L("Close kernel handle")); + CHECK(0,==,Ldd.CloseChunk()); + CHECK(0,==,Ldd.IsDestroyed()); + + test.Next(_L("Check can still access chunk user side")); + CHECK(Base,==,TheChunk.Base()); + CHECK(ChunkSize,==,TheChunk.MaxSize()); + if((MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + CHECK(size,==,TheChunk.Size()); + } + UREAD_MEMORY(offset,Tag(offset)); + + test.Next(_L("Close user handle")); + TheChunk.Close(); + CHECK(1,==,Ldd.IsDestroyed()); + + test.Next(_L("Create chunk in another process")); + + // Create test server + RServer2 server; + RMessage2 message; + TRequestStatus status; + CHECK(KErrNone,==,server.CreateGlobal(KSecondProcessName)); + server.Receive(message,status); + + // Launch slave process + RProcess process; + CHECK(KErrNone,==,process.Create(KSecondProcessName,KNullDesC)); + CHECK(KErrNone,==,process.SetParameter(1,ESlaveCreateChunk)); + CHECK(KErrNone,==,process.SetParameter(2,(RBusLogicalChannel&)Ldd)); + CHECK(KErrNone,==,process.SetParameter(3,ChunkSize|aCreateFlags)); + CHECK(KErrNone,==,process.SetParameter(4,aCommitType)); + CHECK(KErrNone,==,process.SetParameter(5,PageSize)); + TRequestStatus logon; + process.Logon(logon); + process.Resume(); + + // Wait for slave to connect to test server + User::WaitForRequest(logon,status); + CHECK(KRequestPending,==,logon.Int()) + CHECK(KErrNone,==,status.Int()); + CHECK(RMessage2::EConnect,==,message.Function()); + message.Complete(KErrNone); + server.Receive(message,status); + + // Wait for message + User::WaitForRequest(logon,status); + CHECK(KRequestPending,==,logon.Int()) + CHECK(KErrNone,==,status.Int()); + CHECK(0,==,message.Function()); + + test.Next(_L("Check IPC read/write")); + RBuf8 buf; + buf.Create(KTestString().Size()); + CHECK(KErrNone,==,message.Read(0,buf)); + CHECK(ETrue,==,buf==KTestString()); + CHECK(KErrNone,==,message.Write(0,KTestString2)); + CHECK(KErrNone,==,message.Read(0,buf)); + CHECK(ETrue,==,buf==KTestString2()); + + test.Next(_L("Check Kernel read/write")); + TInt n; + TUint32 value; + for(n=0; n=0) + if(CommitList[aIndex].iExpectedResult==KErrNone) + CheckTags(CommitList[aIndex]); + }; + +void CheckCommitState(TInt aIndex) + { + TInt page=0; + TInt lastPage=TheChunk.MaxSize()/PageSize; + while(page=0) + if(CommitList[i].iExpectedResult==KErrNone) + if((TUint)(page-CommitList[i].iOffset) < (TUint)CommitList[i].iSize) + break; + TInt offset = page*PageSize; + if(i>=0) + { + KCHECK_MEMORY(1,offset); // Check page exists + } + else + { + KCHECK_MEMORY(0,offset); // Check page doesn't exists + } + ++page; + } + }; + + +void TestCommit(TUint aCreateFlags,TCommitType aCommitType) + { + SetCreateFlags(aCreateFlags,aCommitType); + TUint ChunkAttribs = ChunkSize|aCreateFlags; + + test.Start(_L("Create chunk")); + CHECK(KErrNone,==,Ldd.CreateChunk(ChunkAttribs)); + + test.Next(_L("Check wrong commit type")); + CHECK(KErrNotSupported,==,Ldd.CommitMemory((aCommitType^EPhysicalMask)|0,PageSize)); + CHECK(KErrNotSupported,==,Ldd.CommitMemory((aCommitType^EPhysicalMask^EContiguous)|0,PageSize)); + + if((TInt)aCommitType&EPhysicalMask) + { + test.Next(_L("Check commit with bad pysical address")); + CHECK(KErrArgument,==,Ldd.CommitMemory((aCommitType|EBadPhysicalAddress)|0,PageSize)); + } + + test.Next(_L("Open user handle")); + CHECK(KErrNone,==,Ldd.GetChunkHandle(TheChunk)); + + const TCommitRegion* list = CommitList; + for(;list->iOffset>=0; ++list) + { + TInt offset = list->iOffset*PageSize; + TInt size = list->iSize*PageSize; + TInt expectedResult = list->iExpectedResult; + if((MemModelAttributes&EMemModelTypeMask)==EMemModelTypeDirect && expectedResult==KErrAlreadyExists) + continue; + TBuf<100> text; + text.AppendFormat(_L("Commit pages: offset=%08x size=%08x expectedResult=%d"),offset,size,expectedResult); + test.Next(text); + + test.Start(_L("Do the Commit")); + CHECK(expectedResult,==,Ldd.CommitMemory(aCommitType|offset,size)); + + if(expectedResult==KErrNone) + { + test.Next(_L("Check new memory has been comitted")); + CheckRegion(*list,!(aCommitType&EPhysicalMask)); + } + + if((MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + test.Next(_L("Check commit state of all pages in chunk")); + CheckCommitState(list-CommitList+1); + } + + test.Next(_L("Check contents of previous commited regions are unchanged")); + CheckCommitedContents(list-CommitList); + + if(expectedResult==KErrNone) + { + test.Next(_L("Mark new memory")); + SetTags(*list); + } + test.End(); + } + + if((aCreateFlags&EMultiple) && (MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + test.Next(_L("Check another process sees same chunk state")); + TInt regionCount = list-CommitList; + + // create another process + RProcess process; + CHECK(KErrNone,==,process.Create(KOtherProcessName,KNullDesC)); + CHECK(KErrNone,==,process.SetParameter(1,ESlaveCheckChunk)); + CHECK(KErrNone,==,process.SetParameter(2,(RBusLogicalChannel&)Ldd)); + CHECK(KErrNone,==,process.SetParameter(3,(RChunk&)TheChunk)); + CHECK(KErrNone,==,process.SetParameter(4,regionCount)); + TRequestStatus status; + process.Logon(status); + process.Resume(); + + // Check chunk again in this process, concurrently with other process + CheckCommitedContents(regionCount); + CheckCommitState(regionCount); + + // wait for other process to finish + User::WaitForRequest(status); + CHECK(EExitKill,==,process.ExitType()); + CHECK(0,==,process.ExitReason()); + CLOSE_AND_WAIT(process); + } + + test.Next(_L("Close handles")); + TheChunk.Close(); + CHECK(1,==,Ldd.CloseChunk()); + + if(aCommitType&EPhysicalMask) + { + // For Physical commit tests, check correct allocation by creating a new chunk + // and checking that pages comitted contain the old TAGs placed there by the + // tests above. + + test.Next(_L("Check commit uses correct physical pages")); + + test.Start(_L("Create chunk")); + CHECK(KErrNone,==,Ldd.CreateChunk(ChunkAttribs)); + + test.Next(_L("Open user handle")); + CHECK(KErrNone,==,Ldd.GetChunkHandle(TheChunk)); + + TInt offset = 0; + for(list=CommitList; list->iOffset>=0; ++list) + { + if(list->iExpectedResult!=KErrNone) + continue; + + TInt size = list->iSize*PageSize; + TBuf<100> text; + text.AppendFormat(_L("Commit pages: offset=%08x size=%08x"),offset,size); + test.Next(text); + CHECK(KErrNone,==,Ldd.CommitMemory(aCommitType|offset,size)); + + test.Next(_L("Check RAM contents preserved from previous usage")); + CheckOldTags(*list,offset); + offset += size; + } + + test.Next(_L("Close handles")); + TheChunk.Close(); + CHECK(1,==,Ldd.CloseChunk()); + + test.End(); + } + else + { + // We don't do these OOM tests for Physical commit because we can't do it reliably + // (as only a couple of page tables come from the free pool not the whole memory + // to be comitted) + test.Next(_L("Check Out Of Memory conditions")); + + // Make sure any clean up has happened otherwise the amount of free RAM may change. + UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0); + + test.Start(_L("Gobble up most of RAM")); + test.Next(_L("Load gobbler LDD")); + TInt r = User::LoadLogicalDevice(KGobblerLddFileName); + test(r==KErrNone || r==KErrAlreadyExists); + RGobbler gobbler; + r = gobbler.Open(); + test(r==KErrNone); + TUint32 taken = gobbler.GobbleRAM(2*1024*1024); + test.Printf(_L("Gobbled: %dK\n"), taken/1024); + test.Printf(_L("Free RAM 0x%08X bytes\n"),FreeRam()); + + test.Next(_L("Get baseline free memory")); + __KHEAP_MARK; + TInt freeRam1 = FreeRam(); + + test.Next(_L("Create shared chunk")); + CHECK(KErrNone,==,Ldd.CreateChunk(ChunkAttribs)); + TInt freeRam2 = FreeRam(); + + test.Next(_L("Commit memory which will causes OOM")); + CHECK(KErrNoMemory,==,Ldd.CommitMemory(aCommitType,4096*1024)); + + test.Next(_L("Check free RAM unchanged")); + CHECK(freeRam2,==,FreeRam()); + + test.Next(_L("Check OOM during ChunkCommit")); + TInt failResult=KErrGeneral; + for(TInt failCount=1; failCount<1000; failCount++) + { + User::__DbgSetAllocFail(ETrue,RAllocator::EFailNext,failCount); + failResult = Ldd.CommitMemory(aCommitType,1); + if(failResult==KErrNone) + break; + CHECK(KErrNoMemory,==,failResult); + } + User::__DbgSetAllocFail(ETrue,RAllocator::ENone,0); + CHECK(KErrNone,==,failResult); + + test.Next(_L("Destroy shared chunk")); + CHECK(1,==,Ldd.CloseChunk()); + CHECK(1,==,Ldd.IsDestroyed()); + + test.Next(_L("Check free memory returns to baseline")); + CHECK(freeRam1,==,FreeRam()); + __KHEAP_MARKEND; + + test.Next(_L("Free gobbled RAM")); + gobbler.Close(); + + test.End(); + } + + test.End(); + } + + +void TestOpenSharedChunk(TUint aCreateFlags,TCommitType aCommitType) + { + SetCreateFlags(aCreateFlags,aCommitType); + TUint ChunkAttribs = ChunkSize|aCreateFlags; + + test.Start(_L("Create chunk")); + CHECK(KErrNone,==,Ldd.CreateChunk(ChunkAttribs)); + + test.Next(_L("Open user handle")); + CHECK(KErrNone,==,Ldd.GetChunkHandle(TheChunk)); + + test.Next(_L("Commit some memory")); + CHECK(KErrNone,==,Ldd.CommitMemory(aCommitType|1*PageSize,PageSize)); + CHECK(KErrNone,==,Ldd.CommitMemory(aCommitType|2*PageSize,PageSize)); + CHECK(KErrNone,==,Ldd.CommitMemory(aCommitType|4*PageSize,PageSize)); + + test.Next(_L("Check OpenSharedChunk with handle")); + CHECK(KErrNone,==,Ldd.TestOpenHandle(TheChunk.Handle())); + + test.Next(_L("Check OpenSharedChunk with wrong chunk handle")); + RChunk testChunk; + CHECK(KErrNone,==,testChunk.CreateLocal(PageSize,PageSize)); + CHECK(KErrNotFound,==,Ldd.TestOpenHandle(testChunk.Handle())); + testChunk.Close(); + + test.Next(_L("Check OpenSharedChunk with wrong handle type")); + CHECK(KErrNotFound,==,Ldd.TestOpenHandle(RThread().Handle())); + + test.Next(_L("Check OpenSharedChunk with bad handle")); + CHECK(KErrNotFound,==,Ldd.TestOpenHandle(0)); + + test.Next(_L("Check OpenSharedChunk with address")); + TUint8* Base = TheChunk.Base(); + CHECK(KErrNone,==,Ldd.TestOpenAddress(Base)); + CHECK(KErrNone,==,Ldd.TestOpenAddress(Base+ChunkSize-1)); + + test.Next(_L("Check OpenSharedChunk with bad address")); + CHECK(KErrNotFound,==,Ldd.TestOpenAddress(Base-1)); + CHECK(KErrNotFound,==,Ldd.TestOpenAddress(Base+ChunkSize)); + CHECK(KErrNotFound,==,Ldd.TestOpenAddress(0)); + CHECK(KErrNotFound,==,Ldd.TestOpenAddress((TAny*)~0)); + + test.Next(_L("Check OpenSharedChunk with stack memory address")); + TUint8 stackMem[100]; + CHECK(KErrNotFound,==,Ldd.TestOpenAddress(stackMem)); + + test.Next(_L("Check OpenSharedChunk with heap memory address")); + TUint8* heapMem = new TUint8[100]; + CHECK(0,!=,heapMem); + CHECK(KErrNotFound,==,Ldd.TestOpenAddress(heapMem)); + delete [] heapMem; + + test.Next(_L("Check OpenSharedChunk with BSS memory address")); + CHECK(KErrNotFound,==,Ldd.TestOpenAddress(BssMem)); + + test.Next(_L("Check OpenSharedChunk with code memory address")); + CHECK(KErrNotFound,==,Ldd.TestOpenAddress((TAny*)&TestOpenSharedChunk)); + + test.Next(_L("Check OpenSharedChunk with NULL address")); + CHECK(KErrNotFound,==,Ldd.TestOpenAddress(0)); + + test.Next(_L("Check ChunkAddress for given memory region")); + static const TCommitRegion regions[] = + { + {0,1,KErrNotFound}, + {0,2,KErrNotFound}, + {0,3,KErrNotFound}, + {1,1}, + {1,2}, + {1,3,KErrNotFound}, + {2,1}, + {2,2,KErrNotFound}, + {2,3,KErrNotFound}, + {3,1,KErrNotFound}, + {3,2,KErrNotFound}, + {3,3,KErrNotFound}, + {4,1}, + {4,2,KErrNotFound}, + {4,3,KErrNotFound}, + {0,10240,KErrArgument}, // too big + {1,0,KErrArgument}, // bad size + {1,-1,KErrArgument}, // bad size + {10240,1,KErrArgument}, // bad offset + {-2,2,KErrArgument}, // bad offset + {-1} + }; + const TCommitRegion* region = regions; + for(;region->iOffset!=-1; ++region) + { + TUint32 offset = region->iOffset*PageSize; + TUint32 size = region->iSize*PageSize; + TInt expectedResult = region->iExpectedResult; + if((MemModelAttributes&EMemModelTypeMask)==EMemModelTypeDirect && expectedResult==KErrNotFound) + continue; + TBuf<100> text; + text.AppendFormat(_L("Memory region: offset=%08x size=%08x expectedResult=%d"),offset,size,expectedResult); + test.Next(text); + CHECK(expectedResult,==,Ldd.TestAddress(offset,size)); + } + + test.Next(_L("Close handles")); + TheChunk.Close(); + CHECK(1,==,Ldd.CloseChunk()); + + test.End(); + } + + +void AccessSpeed(TCreateFlags aCreateFlags, TInt& aRead,TInt& aWrite) + { +// test.Start(_L("Create chunk")); + CHECK(KErrNone,==,Ldd.CreateChunk(ChunkSize|ESingle|EOwnsMemory|aCreateFlags)); + +// test.Next(_L("Commit some memory")); + if((MemModelAttributes&EMemModelTypeMask)==EMemModelTypeDirect) + { + CHECK(KErrNone,==,Ldd.CommitMemory(EDiscontiguous|0*PageSize,PageSize)); + } + else + { + // Allocate contiguous memory when possible so that the + // Cache::SyncMemoryBeforeXxxx calls in the test driver get exercised + CHECK(KErrNone,==,Ldd.CommitMemory(EContiguous|0*PageSize,PageSize)); + } + +// test.Next(_L("Open user handle")); + CHECK(KErrNone,==,Ldd.GetChunkHandle(TheChunk)); + volatile TUint32* p = (TUint32*)TheChunk.Base(); + + TUint32 time; + TInt itterCount=128; + do + { + itterCount *= 2; + TUint32 lastCount=User::NTickCount(); + for(TInt i=itterCount; i>0; --i) + { + TUint32 x=p[0]; x=p[1]; x=p[2]; x=p[3]; x=p[4]; x=p[5]; x=p[6]; x=p[7]; + } + time = User::NTickCount()-lastCount; + } + while(time<200); + aRead = itterCount*8/time; + + itterCount=128; + do + { + itterCount *= 2; + TUint32 lastCount=User::NTickCount(); + for(TInt i=itterCount; i>0; --i) + { + p[0]=i; p[1]=i; p[2]=i; p[3]=i; p[4]=i; p[5]=i; p[6]=i; p[7]=i; + } + time = User::NTickCount()-lastCount; + } + while(time<200); + aWrite = itterCount*8/time; + + TBuf<100> text; + text.AppendFormat(_L("Read speed=%7d Write speed=%7d\n"),aRead,aWrite); + test.Printf(text); + +// test.Next(_L("Close handles")); + TheChunk.Close(); + CHECK(1,==,Ldd.CloseChunk()); + +// test.End(); + } + +void TestMappingAttributes() + { + test.Start(_L("Fully Blocking")); + TInt blockedRead; + TInt blockedWrite; + AccessSpeed(EBlocking,blockedRead,blockedWrite); + + TInt read; + TInt write; + + test.Next(_L("Write Buffered")); + AccessSpeed(EBuffered,read,write); + CHECK(2*blockedRead,>,read); +// CHECK(2*blockedWrite,<,write); // Write buffering doesn't seem to work when cache disabled (?) + + test.Next(_L("Fully Cached")); + AccessSpeed(ECached,read,write); + CHECK(2*blockedRead,<,read); +#ifndef __X86__ // X86 seems to do always do write buffering + // Following check disabled because most dev boards only seem to be a bit faster + // and asserting a particular speed improvement is unreliable +// CHECK(2*blockedWrite,<,write); +#endif + + test.End(); + } + +class RSession : public RSessionBase + { +public: + inline TInt CreateSession(const TDesC& aServer,const TVersion& aVersion) + { return RSessionBase::CreateSession(aServer,aVersion); } + inline TInt SendReceive(TInt aFunction,const TIpcArgs& aArgs) const + { return RSessionBase::SendReceive(aFunction,aArgs); } + }; + +TInt SlaveCommand(TSlaveCommand aCommand) + { + RDebug::Print(_L("Slave Process - Command %d\n"),aCommand); + CHECK(KErrNone,==,UserHal::PageSizeInBytes(PageSize)); + CHECK(KErrNone,==,((RBusLogicalChannel&)Ldd).Open(2,EOwnerProcess)); + switch(aCommand) + { + case ESlaveCheckChunk: + { + RDebug::Print(_L("Slave Process - TheChunk.Open()\n")); + CHECK(KErrNone,==,TheChunk.Open(3)); + RDebug::Print(_L("Slave Process - Get Region Count\n")); + TInt regionCount; + CHECK(KErrNone,==,User::GetTIntParameter(4,regionCount)); + RDebug::Print(_L("Slave Process - CheckCommitedContents(%d)\n"),regionCount); + CheckCommitedContents(regionCount); + RDebug::Print(_L("Slave Process - CheckCommitState(%d)\n"),regionCount); + CheckCommitState(regionCount); + RDebug::Print(_L("Slave Process - Done\n")); + return 0; + } + + case ESlaveCreateChunk: + { + RDebug::Print(_L("Slave Process - Get parameters\n")); + TInt createFlags; + TInt commitType; + TInt commitSize; + CHECK(KErrNone,==,User::GetTIntParameter(3,createFlags)); + CHECK(KErrNone,==,User::GetTIntParameter(4,commitType)); + CHECK(KErrNone,==,User::GetTIntParameter(5,commitSize)); + + RDebug::Print(_L("Slave Process - Create Chunk\n")); + CHECK(KErrNone,==,Ldd.CreateChunk(createFlags)); + CHECK(KErrNone,==,Ldd.CommitMemory(commitType,commitSize)); + CHECK(KErrNone,==,Ldd.GetChunkHandle(TheChunk)); + TUint8* chunkBase=TheChunk.Base(); + memcpy(chunkBase,KTestString().Ptr(),KTestString().Size()); + + RDebug::Print(_L("Slave Process - Connecting to test server\n")); + RSession session; + CHECK(KErrNone,==,session.CreateSession(KSecondProcessName,TVersion())); + + RDebug::Print(_L("Slave Process - Sending message\n")); + TPtr8 ptr(chunkBase,commitSize,commitSize); + session.SendReceive(0,TIpcArgs(&ptr)); + + RDebug::Print(_L("Slave Process - Destroy Chunk\n")); + TheChunk.Close(); + CHECK(1,==,Ldd.CloseChunk()); // 1==DObject::EObjectDeleted + CHECK(1,==,Ldd.IsDestroyed()); + return 0; + } + + default: + RDebug::Print(_L("Slave Process - Bad Command\n")); + return KErrArgument; + } + } + +void TestChunkUserBase() + { + TUint ChunkAttribs = ChunkSize|ESingle|EOwnsMemory; + + test.Start(_L("Create chunk")); + CHECK(KErrNone,==,Ldd.CreateChunk(ChunkAttribs)); + + test.Next(_L("Open user handle")); + CHECK(KErrNone,==,Ldd.GetChunkHandle(TheChunk)); + + test.Next(_L("Commit some memory")); + CHECK(KErrNone,==,Ldd.CommitMemory(EDiscontiguous|1*PageSize,PageSize)); + + test.Next(_L("Check OpenSharedChunk with handle")); + CHECK(KErrNone,==,Ldd.TestOpenHandle(TheChunk.Handle())); + + test.Next(_L("Get Kernel's user base")); + TAny *kernelUserAddress; + CHECK(KErrNone,==,Ldd.GetChunkUserBase(&kernelUserAddress)); + TAny *userAddress = TheChunk.Base(); + test(kernelUserAddress == userAddress); + + TheChunk.Close(); + CHECK(1,==,Ldd.CloseChunk()); + + test.End(); + } + + +TInt E32Main() + { + // Running as slave? + TInt slaveCommand; + if(User::GetTIntParameter(1,slaveCommand)==KErrNone) + return SlaveCommand((TSlaveCommand)slaveCommand); + +// Turn off lazy dll unloading + RLoader l; + test(l.Connect()==KErrNone); + test(l.CancelLazyDllUnload()==KErrNone); + l.Close(); + + test.Title(); + + MemModelAttributes=UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL); + TUint mm=MemModelAttributes&EMemModelTypeMask; +#ifdef __T_SHAREDCHUNKF__ + if(mm!=EMemModelTypeMoving) + { + test.Start(_L("TESTS NOT RUN - Only valid on Moving Memory Model")); + test.End(); + return 0; + } +#endif + + test.Start(_L("Initialise")); + CHECK(KErrNone,==,UserHal::PageSizeInBytes(PageSize)); + PhysicalCommitSupported = mm!=EMemModelTypeDirect && mm!=EMemModelTypeEmul; + CachingAttributesSupported = mm!=EMemModelTypeDirect && mm!=EMemModelTypeEmul; + + + test.Next(_L("Loading test driver")); + TInt r = User::LoadLogicalDevice(KSharedChunkLddName); + test(r==KErrNone || r==KErrAlreadyExists); + + test.Next(_L("Opening channel")); + CHECK(KErrNone,==,Ldd.Open()); + + // now 'unload' test driver, however, it will remain loaded whilst + // we still have a channel open with it... + User::FreeLogicalDevice(KSharedChunkLddName); + + test.Next(_L("Test chunk create")); + TestCreate(); + + test.Next(_L("Test handles")); + TestHandles(); + + test.Next(_L("Test handle ownership")); + TestHandleOwnership(); + + test.Next(_L("Test restrictions for multiply shared chunks")); + TestRestrictions(EMultiple); + test.Next(_L("Test restrictions for singly shared chunks")); + TestRestrictions(ESingle); + + test.Next(_L("Test memory access for multiply shared chunks")); + TestAccess(EMultiple|EOwnsMemory,EDiscontiguous); + test.Next(_L("Test memory access for singly shared chunks")); + TestAccess(ESingle|EOwnsMemory,EDiscontiguous); + + test.Next(_L("Test Discontiguous memory commit for multiply shared chunks")); + TestCommit(EMultiple,EDiscontiguous); + test.Next(_L("Test Discontiguous memory commit for singly shared chunks")); + TestCommit(ESingle,EDiscontiguous); + + if((MemModelAttributes&EMemModelTypeMask)!=EMemModelTypeDirect) + { + test.Next(_L("Test Contiguous memory commit for multiply shared chunks")); + TestCommit(EMultiple,EContiguous); + test.Next(_L("Test Contiguous memory commit for singly shared chunks")); + TestCommit(ESingle,EContiguous); + } + + if(PhysicalCommitSupported) + { + test.Next(_L("Test Discontiguous Physical commit for multiply shared chunks")); + TestCommit(EMultiple,EDiscontiguousPhysical); + test.Next(_L("Test Discontiguous Physical commit for singly shared chunks")); + TestCommit(ESingle,EDiscontiguousPhysical); + + test.Next(_L("Test Contiguous Physical commit for multiply shared chunks")); + TestCommit(EMultiple,EContiguousPhysical); + test.Next(_L("Test Contiguous Physical commit for singly shared chunks")); + TestCommit(ESingle,EContiguousPhysical); + } + + test.Next(_L("Test Kern::OpenSharedChunk for multiply shared chunks")); + TestOpenSharedChunk(EMultiple,EDiscontiguous); + test.Next(_L("Test Kern::OpenSharedChunk for singly shared chunks")); + TestOpenSharedChunk(ESingle,EDiscontiguous); + + if(CachingAttributesSupported) + { + test.Next(_L("Test Mapping Attributes")); + TestMappingAttributes(); + } + + test.Next(_L("Testing Kern::ChunkUserBase for shared chunks")); + TestChunkUserBase(); + + if (PhysicalCommitSupported) + { + test.Next(_L("Testing Kern::ChunkClose allows immediate freeing of physical ram")); + test_KErrNone(Ldd.TestChunkCloseAndFree()); + } + + test.Next(_L("Close test driver")); + Ldd.Close(); + + test.End(); + + return 0; + }