kerneltest/e32test/mmu/t_sharedchunk.cpp
changeset 0 a41df078684a
child 31 56f325a607ea
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/mmu/t_sharedchunk.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1349 @@
+// 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 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 <e32test.h>
+#include <e32hal.h>
+#include "u32std.h"
+#include <u32hal.h>
+#include <e32svr.h>
+#include <f32dbg.h>
+#include <e32def.h>
+#include <e32def_private.h>
+#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();
+	}
+
+
+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<KTestString2().Size()-(TInt)sizeof(TInt)+1; n+=sizeof(TInt))
+		{
+		Ldd.ReadMemory(n,value);
+		CHECK(*(TInt*)&KTestString2()[n],==,value);
+		CHECK(KErrNone,==,Ldd.WriteMemory(n,*(TInt*)&KTestString()[n]));
+		Ldd.ReadMemory(n,value);
+		CHECK(*(TInt*)&KTestString()[n],==,value);
+		}
+	CHECK(KErrNone,==,message.Read(0,buf));
+	CHECK(ETrue,==,buf==KTestString());
+	buf.Close();
+
+	test.Next(_L("Check read/write from DFC"));
+	CHECK(KErrNone,==,Ldd.WriteMemory(0,KTestValue));
+	value = KTestValue2;
+	CHECK(KErrNone,==,Ldd.DfcReadWrite(0,value));
+	CHECK(KTestValue,==,value);
+	CHECK(KErrNone,==,Ldd.ReadMemory(0,value));
+	CHECK(KTestValue2,==,value);
+	
+	test.Next(_L("Check read/write from ISR"));
+	CHECK(KErrNone,==,Ldd.WriteMemory(0,KTestValue));
+	value = KTestValue2;
+	CHECK(KErrNone,==,Ldd.IsrReadWrite(0,value));
+	CHECK(KTestValue,==,value);
+	CHECK(KErrNone,==,Ldd.ReadMemory(0,value));
+	CHECK(KTestValue2,==,value);
+
+	test.Next(_L("Cleanup resources"));
+	server.Close();
+	User::WaitForRequest(logon);
+	CLOSE_AND_WAIT(process);
+
+	test.End();
+	}
+
+
+RProcess OtherProcess;
+
+TInt HandleShare(TAny* aHandle)
+	{
+	TInt r=OtherProcess.Create(KOtherProcessName,KNullDesC,EOwnerProcess);
+	if(r==KErrNone)
+		{
+		r = OtherProcess.SetParameter(1,(RChunk&)aHandle);
+		}
+	return r;
+	}
+
+void TestRestrictions(TInt aAttrib)
+	{
+	TUint ChunkAttribs = ChunkSize|aAttrib|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("Try changing restrictions"));
+	CHECK(KErrAccessDenied,==,TheChunk.SetRestrictions(0));
+
+	test.Next(_L("Check allocation restrictions"));
+	CHECK(KErrAccessDenied,==,TheChunk.Adjust(ChunkSize/2));
+	CHECK(KErrAccessDenied,==,TheChunk.AdjustDoubleEnded(PageSize,ChunkSize));
+	CHECK(KErrAccessDenied,==,TheChunk.Commit(PageSize,PageSize));
+	CHECK(KErrAccessDenied,==,TheChunk.Allocate(PageSize));
+	CHECK(KErrAccessDenied,==,TheChunk.Decommit(PageSize,PageSize));
+
+	test.Next(_L("Duplicate handle in same process"));
+	RChunk chunk2;
+	chunk2.SetHandle(TheChunk.Handle());
+	CHECK(KErrNone,==,chunk2.Duplicate(RThread(),EOwnerProcess));
+
+	test.Next(_L("Try passing handle to other process"));
+	if(aAttrib==EMultiple)
+		{
+		CHECK(0,==,CheckPlatSecPanic(HandleShare,*(TInt*)&chunk2));
+		}
+	else
+		{
+		CHECK(1,==,CheckPlatSecPanic(HandleShare,*(TInt*)&chunk2));
+		}
+	// Cleanup leftover process
+	OtherProcess.Kill(0);
+	OtherProcess.Close();
+
+	test.Next(_L("Close handles"));
+	chunk2.Close();
+	CHECK(0,==,Ldd.CloseChunk());
+	TheChunk.Close();
+
+	test.End();
+	}
+
+
+class TCommitRegion
+	{
+public:
+	TInt iOffset;
+	TInt iSize;
+	TInt iExpectedResult;
+	};
+
+void CheckRegion(TCommitRegion aRegion, TBool aCheckClear)
+	{
+	TUint8* Base = TheChunk.Base();
+	TInt offset = aRegion.iOffset*PageSize;
+	TInt limit = offset+aRegion.iSize*PageSize;
+	while(offset<limit)
+		{
+		KCHECK_MEMORY(1,offset);
+		UCHECK_MEMORY(1,offset);
+		offset += PageSize;
+		}
+	if(!aCheckClear)
+		return;
+
+	TUint32* ptr = (TUint32*)(Base+aRegion.iOffset*PageSize);
+	TUint32* end = (TUint32*)((TInt)ptr+aRegion.iSize*PageSize);
+	while(ptr<end)
+		if(*ptr++!=0x03030303u)
+			{
+			CHECK(0x03030303u,==,*ptr);
+			}
+	};
+
+void SetTags(TCommitRegion aRegion)
+	{
+	TUint8* Base = TheChunk.Base();
+	TInt offset = aRegion.iOffset*PageSize;
+	TInt limit = offset+aRegion.iSize*PageSize;
+	while(offset<limit)
+		{
+		*(TUint*)(Base+offset) = Tag(offset);
+		offset += sizeof(TUint);
+		}
+	};
+
+void CheckTags(const TCommitRegion& aRegion)
+	{
+	TUint8* Base = TheChunk.Base();
+	TInt offset = aRegion.iOffset*PageSize;
+	TInt limit = offset+aRegion.iSize*PageSize;
+	while(offset<limit)
+		{
+		KCHECK_MEMORY(1,offset);	// Check page exists
+		TInt limit2 = offset+PageSize;
+		while(offset<limit2)
+			{
+			UREAD_MEMORY(offset,Tag(offset));	// Check contents match tags we set previousely
+			offset += sizeof(TUint);
+			}
+		}
+	};
+
+void CheckOldTags(const TCommitRegion& aRegion, TInt aNewOffset)
+	{
+	TUint8* Base = TheChunk.Base();
+	TInt oldOffset = aRegion.iOffset*PageSize;
+	TInt offset = aNewOffset;
+	TInt limit = offset+aRegion.iSize*PageSize;
+	while(offset<limit)
+		{
+		KCHECK_MEMORY(1,offset);	// Check page exists
+		TInt limit2 = offset+PageSize;
+		while(offset<limit2)
+			{
+			UREAD_MEMORY(offset,Tag(oldOffset));	// Check contents matched old tags
+			offset += sizeof(TUint);
+			oldOffset += sizeof(TUint);
+			}
+		}
+	};
+
+// Following assumes 4k pages...
+static const TCommitRegion CommitList[] =
+	{
+		{0,0},		// zero size commit
+		{1,1},		// page 1
+
+		// Regions which overlap previous commit
+		{1,1,KErrAlreadyExists},
+		{0,2,KErrAlreadyExists},
+		{1,2,KErrAlreadyExists},
+		{0,3,KErrAlreadyExists},
+
+		{0,1},		// page 0
+		{2,1},		// page 2
+
+		{250,6},	// pages at end of chunk boundary
+
+		{768,256},	// whole 1meg chunk
+
+		{400,512,KErrAlreadyExists},	// Monster commit which overlaps previous regions
+
+		{256,257},	// big commit which straddles more than one 1meg chunk
+
+		{-1,-1} // End marker
+	};
+
+void CheckCommitedContents(TInt aIndex)
+	{
+	while(--aIndex>=0)
+		if(CommitList[aIndex].iExpectedResult==KErrNone)
+			CheckTags(CommitList[aIndex]);
+	};
+
+void CheckCommitState(TInt aIndex)
+	{
+	TInt page=0;
+	TInt lastPage=TheChunk.MaxSize()/PageSize;
+	while(page<lastPage)
+		{
+		TInt i=aIndex;
+		while(--i>=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 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;
+	}