kerneltest/e32test/mmu/t_pin.cpp
changeset 0 a41df078684a
child 13 46fffbe7b5a7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/mmu/t_pin.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,753 @@
+// Copyright (c) 2008-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_pin.cpp
+// Tests kernel APIs for logical pinning by pinning memory and using a realtime thread to check that
+// no page faults are taken while accessing it.
+// 
+//
+
+#define __E32TEST_EXTENSION__
+#include <e32test.h>
+#include <e32svr.h>
+#include <e32rom.h>
+#include <e32kpan.h>
+#include <u32hal.h>
+#include <dptest.h>
+#include "d_memorytest.h"
+#include "t_codepaging_dll.h"
+#include "mmudetect.h"
+#include "freeram.h"
+
+RTest test(_L("T_PIN"));
+
+_LIT(KTCodePagingDll4, "t_codepaging_dll4.dll");
+const TInt KMinBufferSize = 16384;
+
+RMemoryTestLdd Ldd;
+RMemoryTestLdd Ldd2;
+RLibrary PagedLibrary;
+const TUint8* PagedBuffer = NULL;
+const TUint8* UnpagedBuffer = NULL;
+TInt PageSize;
+
+TInt FreeRamNoWait()
+	{
+	TMemoryInfoV1Buf meminfo;
+	UserHal::MemoryInfo(meminfo);
+	return meminfo().iFreeRamInBytes;
+	}
+
+void CheckMemoryPresent(const TUint8* aBuffer, TInt aSize, TBool aExpected)
+	{
+	if (aExpected)
+		test.Printf(_L("  Checking memory at %08x is present\n"), aBuffer);
+	else
+		test.Printf(_L("  Checking memory at %08x is not present\n"), aBuffer);
+	for (TInt i = 0 ; i < aSize ; i += PageSize)
+		test_Equal(aExpected, Ldd.IsMemoryPresent(aBuffer + i));
+	}
+
+void FlushPagingCache()
+	{
+	test_KErrNone(DPTest::FlushCache());
+	}
+
+void TestPinVirtualMemoryUnpaged()
+	{
+	test.Printf(_L("Create logical pin object\n"));
+	test_KErrNone(Ldd.CreateVirtualPinObject());
+#ifdef __EPOC32__
+	CheckMemoryPresent(UnpagedBuffer, KMinBufferSize, ETrue);
+	test.Printf(_L("Perform logical pin operation on zero-length buffer\n"));
+	test_KErrNone(Ldd.PinVirtualMemory((TLinAddr)UnpagedBuffer, 0));
+	CheckMemoryPresent(UnpagedBuffer, KMinBufferSize, ETrue);
+	test.Printf(_L("Perform logical unpin operation\n"));
+	test_KErrNone(Ldd.UnpinVirtualMemory());
+	CheckMemoryPresent(UnpagedBuffer, KMinBufferSize, ETrue);
+	test.Printf(_L("Perform logical pin operation on whole buffer\n"));
+	test_KErrNone(Ldd.PinVirtualMemory((TLinAddr)UnpagedBuffer, KMinBufferSize));
+	CheckMemoryPresent(UnpagedBuffer, KMinBufferSize, ETrue);
+	test.Printf(_L("Perform logical unpin operation\n"));
+	test_KErrNone(Ldd.UnpinVirtualMemory());
+	CheckMemoryPresent(UnpagedBuffer, KMinBufferSize, ETrue);
+#else
+	// Don't check for memory presence on emulator as paging not supported.
+	test.Printf(_L("Perform logical pin operation on zero-length buffer\n"));
+	test_KErrNone(Ldd.PinVirtualMemory((TLinAddr)UnpagedBuffer, 0));
+	test.Printf(_L("Perform logical unpin operation\n"));
+	test_KErrNone(Ldd.UnpinVirtualMemory());
+	test.Printf(_L("Perform logical pin operation on whole buffer\n"));
+	test_KErrNone(Ldd.PinVirtualMemory((TLinAddr)UnpagedBuffer, KMinBufferSize));
+	test.Printf(_L("Perform logical unpin operation\n"));
+	test_KErrNone(Ldd.UnpinVirtualMemory());
+#endif
+	test.Printf(_L("Perform logical unpin operation (again)\n"));
+	test_KErrNone(Ldd.UnpinVirtualMemory());	// test double unpin ok
+	test.Printf(_L("Destroy logical pin object\n"));
+	test_KErrNone(Ldd.DestroyVirtualPinObject());
+	test.Printf(_L("Destroy logical pin object (again)\n"));
+	test_KErrNone(Ldd.DestroyVirtualPinObject());  // test double destroy ok
+	}
+
+void TestPinPhysicalMemory()
+	{
+	
+	TInt mm = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, 0, 0) & EMemModelTypeMask;
+	if (mm < EMemModelTypeFlexible)
+		{
+		test.Printf(_L("Memory model (%d) doesn't support physical pining\n"),mm);
+		return;
+		}
+	TInt i;
+	TInt8* UCBase;
+	RChunk chunk;
+
+	test.Printf(_L("Allocate user chunk\n"));
+	TChunkCreateInfo createInfo;
+	createInfo.SetDisconnected(0,UCPageCount*PageSize,UCPageCount*PageSize);
+	createInfo.SetPaging(TChunkCreateInfo::EPaged);
+	test_KErrNone(chunk.Create(createInfo));
+	UCBase = (TInt8*)chunk.Base();
+	
+	test.Printf(_L("Create physical pin object\n"));
+	test_KErrNone(Ldd.CreatePhysicalPinObject());
+
+	test.Printf(_L("Perform physical pin operation on zero-length buffer\n"));
+	test_KErrNone(Ldd.PinPhysicalMemory((TLinAddr)UCBase, 0));	
+
+	test.Printf(_L("Perform physical unpin operation\n"));
+	test_KErrNone(Ldd.UnpinPhysicalMemory());	
+
+	test.Printf(_L("Perform Physical pin operation on the chunk\n"));
+	test_KErrNone(Ldd.PinPhysicalMemory((TLinAddr)UCBase, UCPageCount*PageSize));	
+
+	test.Printf(_L("Test that pinned physical memory preserves its mapping when recommited\n"));
+	test_KErrNone(chunk.Decommit(0,UCPageCount*PageSize));							 //Decommit all
+	for (i=UCPageCount-1;i>=0;i--) test_KErrNone(chunk.Commit(i*PageSize,PageSize)); //Commit in reverse order
+	for (i=0;i<UCPageCount;i++) // Recommited memory is not paged in. So, write into each page, before driver 
+		{						// calls Kern::LinearToPhysical or it will get KErrInvalidMemory in return.
+		volatile TInt8* ptr = (volatile TInt8*)(UCBase+i*PageSize);
+		*ptr = 10;
+		}
+	test_KErrNone(Ldd.CheckPageList(chunk.Base())); 					// Check that the mapping is preserved. 	
+	
+	test.Printf(_L("Sync cache & memory of User Chunk\n"));//Test Cache::SyncPhysicalMemoryBeforeDmaWrite
+	test_KErrNone(Ldd.SyncPinnedPhysicalMemory(0,UCPageCount*PageSize));
+
+	test.Printf(_L("Invalidate cache of User Chunk\n"));//Test Cache::SyncPhysicalMemoryBefore/AfterDmaRead
+	test_KErrNone(Ldd.InvalidatePinnedPhysicalMemory(0,UCPageCount*PageSize));
+	
+	test.Printf(_L("Try to move pinned phys. memory...\n")); //RAM defrag should return error code here.
+	i = Ldd.MovePinnedPhysicalMemory(0);
+	test.Printf(_L("...returned %d\n"),i);
+	test(i!=KErrNone);
+
+	test.Printf(_L("Close the chunk\n")); // Phys. memory is pinned and shouldn't be ...
+	chunk.Close();						  // ... mapped to another virtual memory.
+
+	test.Printf(_L("Allocate & initilise the second chunk\n"));// Kernel sholudn't commit pinned physical memory ...
+	test_KErrNone(chunk.CreateLocal(2*PageSize,2*PageSize));   // ...that has been just decommited from the first chunk.
+	UCBase = (TInt8*)chunk.Base();
+	for (i=0;i<UCPageCount*PageSize;i++) UCBase[i]=0; //Initialise user buffer
+
+	test.Printf(_L("Invalidate cache of pinned memory\n"));//This shouldn't affect the second chunk.
+	test_KErrNone(Ldd.InvalidatePinnedPhysicalMemory(0,UCPageCount*PageSize));
+
+	test.Printf(_L("Check data in the second chunk is unaffected\n"));
+	for (i=0;i<UCPageCount*PageSize;i++) test(UCBase[i]==0);
+	
+	test.Printf(_L("Close the second chunk\n"));
+	chunk.Close();
+
+	test.Printf(_L("Perform physical unpin operation\n"));
+	test_KErrNone(Ldd.UnpinPhysicalMemory());	
+
+	test.Printf(_L("Perform physical unpin operation (again)\n"));
+	test_KErrNone(Ldd.UnpinPhysicalMemory());	// test double unpin ok
+
+	test.Printf(_L("Destroy physical pin object\n"));
+	test_KErrNone(Ldd.DestroyPhysicalPinObject());
+
+	test.Printf(_L("Destroy physical pin object (again)\n"));
+	test_KErrNone(Ldd.DestroyPhysicalPinObject());  // test double destroy ok
+
+	test.Printf(_L("Test phys. pinning and sync of kernel memory.\n"));
+	test_KErrNone(Ldd.PinKernelPhysicalMemory());// Simple test of phys. pinning of kernel memory
+	}
+
+void TestPhysicalPinOutOfMemory()
+	{
+	TInt mm = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, 0, 0) & EMemModelTypeMask;
+	if (mm < EMemModelTypeFlexible)
+		{
+		test.Printf(_L("Memory model (%d) doesn't support physical pining\n"),mm);
+		return;
+		}
+	
+	TInt8* UCBase;
+	RChunk chunk;
+
+	test.Printf(_L("Allocate user chunk\n"));
+	test_KErrNone(chunk.CreateDisconnectedLocal(0,UCPageCount*PageSize,UCPageCount*PageSize));
+	UCBase = (TInt8*)chunk.Base();
+	
+	const TInt KMaxKernelAllocations = 1024;
+	TInt r=KErrNoMemory;
+	TInt i;
+
+	__KHEAP_MARK;
+	for (i = 0; i < KMaxKernelAllocations && r == KErrNoMemory; i++)
+		{
+		__KHEAP_FAILNEXT(i);
+		test.Printf(_L("Create physical pin object\n"));
+		r = Ldd.CreatePhysicalPinObject();
+		__KHEAP_RESET;
+		}
+	test.Printf(_L("Create physical pin object took %d tries\n"),i);
+	test_KErrNone(r);
+
+	r = KErrNoMemory;
+
+	for (i = 0; i < KMaxKernelAllocations && r == KErrNoMemory; i++)
+		{
+		__KHEAP_FAILNEXT(i);
+		test.Printf(_L("Perform physical pin operation\n"));
+		r = Ldd.PinPhysicalMemory((TLinAddr)UCBase, UCPageCount*PageSize);
+		__KHEAP_RESET;
+		}
+	test.Printf(_L("Perform physical pin operation took %d tries\n"),i);
+	if (r == KErrNone)
+		{
+		test.Printf(_L("Perform physical unpin operation\n"));
+		Ldd.UnpinPhysicalMemory();
+		}
+
+	test.Printf(_L("Destroy physical pin object\n"));
+	Ldd.DestroyPhysicalPinObject();
+
+	// wait for any async cleanup in the supervisor to finish first...
+	UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0);	
+	__KHEAP_MARKEND;
+
+	test.Printf(_L("Close the chunk\n"));
+	chunk.Close();
+	
+	test_KErrNone(r);
+	}
+
+
+void TestPinVirtualMemoryInvalid()
+	{
+	test.Printf(_L("Create logical pin object\n"));
+	test_KErrNone(Ldd.CreateVirtualPinObject());
+	test.Printf(_L("Attempt logical pin on bad memory address\n"));
+	TLinAddr bad = (TLinAddr)0x10000;
+	TInt r = Ldd.PinVirtualMemory(bad,KMinBufferSize);
+	test.Printf(_L("%08x r=%d"),bad,r);
+	if(r==KErrNone)
+		test_KErrNone(Ldd.UnpinVirtualMemory());	
+	if ((MemModelAttributes() & EMemModelTypeMask) == EMemModelTypeMultiple)
+		{
+		// test unused part of code chunk...
+		bad = (TLinAddr)0x7f000000;
+		r = Ldd.PinVirtualMemory(bad,KMinBufferSize);
+		test.Printf(_L("%08x r=%d"),bad,r);
+		if(r==KErrNone)
+			test_KErrNone(Ldd.UnpinVirtualMemory());	
+		}
+	test.Printf(_L("Destroy logical pin object\n"));
+	test_KErrNone(Ldd.DestroyVirtualPinObject());
+	}
+
+void TestPinVirtualMemoryPaged()
+	{
+	test.Printf(_L("Create logical pin object\n"));
+	test_KErrNone(Ldd.CreateVirtualPinObject());
+	FlushPagingCache();
+	CheckMemoryPresent(PagedBuffer, KMinBufferSize, EFalse);
+	test.Printf(_L("Perform logical pin operation on zero-length buffer\n"));
+	test_KErrNone(Ldd.PinVirtualMemory((TLinAddr)PagedBuffer, 0));	
+	CheckMemoryPresent(PagedBuffer, KMinBufferSize, EFalse);
+	test.Printf(_L("Perform logical unpin operation\n"));
+	test_KErrNone(Ldd.UnpinVirtualMemory());	
+	CheckMemoryPresent(PagedBuffer, KMinBufferSize, EFalse);
+	test.Printf(_L("Perform logical pin operation on whole buffer\n"));
+	test_KErrNone(Ldd.PinVirtualMemory((TLinAddr)PagedBuffer, KMinBufferSize));	
+	CheckMemoryPresent(PagedBuffer, KMinBufferSize, ETrue);
+	FlushPagingCache();
+	CheckMemoryPresent(PagedBuffer, KMinBufferSize, ETrue);
+	test.Printf(_L("Perform logical unpin operation\n"));
+	test_KErrNone(Ldd.UnpinVirtualMemory());	
+	CheckMemoryPresent(PagedBuffer, KMinBufferSize, ETrue);
+	FlushPagingCache();
+	CheckMemoryPresent(PagedBuffer, KMinBufferSize, EFalse);
+	test.Printf(_L("Perform logical unpin operation (again)\n"));
+	test_KErrNone(Ldd.UnpinVirtualMemory());	// test double unpin ok
+	test.Printf(_L("Destroy logical pin object\n"));
+	test_KErrNone(Ldd.DestroyVirtualPinObject());
+	test.Printf(_L("Destroy logical pin object (again)\n"));
+	test_KErrNone(Ldd.DestroyVirtualPinObject());  // test double destroy ok
+	}
+
+
+volatile TBool SoakEnd = false;
+
+class TRandom
+	{
+public:
+	TRandom(TUint32 aSeed)
+		: iSeed(aSeed) {};
+	inline TUint32 Next()
+		{ iSeed = iSeed*69069+1; return iSeed; }
+	TUint32 operator()(TUint32 aRange)
+		{ return (TUint32)((TUint64(Next())*TUint64(aRange))>>32); }
+private:
+	TUint iSeed;
+	};
+
+#define SOAK_CHECK(r)												\
+	if(r!=KErrNone)													\
+		{															\
+		RDebug::Printf("SOAK_CHECK fail at line %d",__LINE__);		\
+		return r;													\
+		}															\
+
+TInt SoakThread(TAny*)
+	{
+	RMemoryTestLdd ldd;
+	TInt r = ldd.Open();
+	SOAK_CHECK(r)
+
+	r = ldd.CreateVirtualPinObject();
+	SOAK_CHECK(r)
+
+	TRandom random((TUint32)&ldd);
+
+	while(!SoakEnd)
+		{
+		TUint start = random(KMinBufferSize);
+		TUint end = random(KMinBufferSize);
+		if(start>end)
+			{
+			TUint temp = start;
+			start = end;
+			end = temp;
+			}
+		const TUint32 KPageMask = 0xfff;
+		start &= ~KPageMask;
+		end = (end+KPageMask)&~KPageMask;
+
+		r = ldd.PinVirtualMemory((TLinAddr)(PagedBuffer+start),end-start);
+		SOAK_CHECK(r)
+
+		r = ldd.UnpinVirtualMemory();
+		SOAK_CHECK(r)
+		}
+
+	r = ldd.DestroyVirtualPinObject();
+	SOAK_CHECK(r)
+
+	CLOSE_AND_WAIT(ldd);
+	return KErrNone;
+	}
+
+
+void TestPinVirtualMemoryPagedSoak()
+	{
+	test.Start(_L("Create timer"));
+	RTimer timer;
+	test_KErrNone(timer.CreateLocal());
+
+	test.Next(_L("Create threads"));
+	const TUint KNumThreads = 4;
+	TRequestStatus status[KNumThreads];
+	RThread thread[KNumThreads];
+	TUint i;
+	for(i=0; i<KNumThreads; i++)
+		{
+		test_KErrNone(thread[i].Create(KNullDesC, SoakThread, 0x1000, NULL, 0));
+		thread[i].Logon(status[i]);
+		test(status[i].Int()==KRequestPending);
+		}
+
+	test.Next(_L("Start threads"));
+	RThread().SetPriority(EPriorityMore); // make sure we are higher priority than soak threads
+	for(i=0; i<KNumThreads; i++)
+		thread[i].Resume();
+
+	test.Next(_L("Wait..."));
+	TRequestStatus timeoutStatus;
+	timer.After(timeoutStatus,10*1000000);
+	User::WaitForAnyRequest();
+	test_KErrNone(timeoutStatus.Int()); // we should have timed out if soak threads are still running OK
+
+	test.Next(_L("Stop threads and check results"));
+	for(i=0; i<KNumThreads; i++)
+		test_Equal(KRequestPending,status[i].Int());
+	SoakEnd = true;
+	timer.After(timeoutStatus,10*1000000);
+	for(i=0; i<KNumThreads; i++)
+		{
+		User::WaitForAnyRequest();
+		test_Equal(KRequestPending,timeoutStatus.Int());
+		}
+	timer.Cancel();
+	User::WaitForRequest(timeoutStatus);
+	RThread().SetPriority(EPriorityNormal); // restore thread priority
+
+	// cleanup...
+	CLOSE_AND_WAIT(timer);
+	for(i=0; i<KNumThreads; i++)
+		CLOSE_AND_WAIT(thread[i]);
+
+	test.End();
+	}
+
+
+void TestPinVirtualMemoryDecommit()
+	{
+	const TInt KChunk = 4*1024*1024; // offset of page table boundary on X86 and ARM
+	const TInt KPage = PageSize;
+	const TInt TestData[][2] =
+		{
+			{0,				KPage},
+			{KPage,			KPage},
+			{KPage,			2*KPage},
+			{KChunk-KPage,	KPage},
+			{KChunk-2*KPage,2*KPage},
+			{KChunk-KPage,	2*KPage},
+			{0,0} // end marker
+		};
+
+	for(TInt i=0; TestData[i][1]; ++i)
+		{
+		TInt commitOffset = TestData[i][0];
+		TInt commitSize = TestData[i][1];
+		test.Printf(_L("Create chunk 0x%x+0x%x\n"),commitOffset,commitSize);
+
+		TChunkCreateInfo createInfo;
+		createInfo.SetDisconnected(commitOffset,commitOffset+commitSize,commitOffset+commitSize);
+		createInfo.SetPaging(TChunkCreateInfo::EPaged);
+		RChunk chunk;
+		test_KErrNone(chunk.Create(createInfo));
+		TUint8* buffer = chunk.Base()+commitOffset;
+		TUint bufferSize = commitSize;
+		FlushPagingCache(); // start with blank slate as far as paged memory is concerned
+
+		test.Printf(_L("Create virtual pin object\n"));
+		test_KErrNone(Ldd.CreateVirtualPinObject());
+		test_KErrNone(Ldd2.CreateVirtualPinObject());
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		TInt initialFreeRam = FreeRam();
+
+		test.Printf(_L("Pin memory\n"));
+		test_KErrNone(Ldd.PinVirtualMemory((TLinAddr)buffer, bufferSize));	
+		CheckMemoryPresent(buffer, bufferSize, ETrue);
+		TInt pinnedFreeRam = FreeRam();
+		test_Compare(pinnedFreeRam,<,initialFreeRam);
+		TUint8 c = *buffer;
+		memset(buffer,~c,bufferSize); // invert memory
+
+		test.Printf(_L("Decommit pinned memory\n"));
+		test_KErrNone(chunk.Decommit(commitOffset,commitSize));	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(pinnedFreeRam,FreeRam()); // decommited memory should not be freed as it is pinned
+
+		test.Printf(_L("Unpin memory\n"));
+		test_KErrNone(Ldd.UnpinVirtualMemory());	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(initialFreeRam,FreeRam()); // memory should be now freed
+
+		//
+		// test recommitting decommitted pinned memory...
+		//
+
+		test.Printf(_L("Commit memory\n"));
+		test_KErrNone(chunk.Commit(commitOffset,commitSize));	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(initialFreeRam,FreeRam());
+
+		test.Printf(_L("Read memory\n"));
+		volatile TUint8* p = buffer;
+		volatile TUint8* pEnd = buffer+bufferSize;
+		while(p<pEnd)
+			test_Equal(c,*p++); // memory should have been wiped
+		test_Equal(initialFreeRam,FreeRam()); // memory now paged in
+
+		test.Printf(_L("Pin memory which is already paged in\n"));
+		test_KErrNone(Ldd.PinVirtualMemory((TLinAddr)buffer, bufferSize));	
+		CheckMemoryPresent(buffer, bufferSize, ETrue);
+		test_Equal(pinnedFreeRam,FreeRam());
+		memset(buffer,~c,bufferSize); // invert memory
+
+		test.Printf(_L("Decommit pinned memory\n"));
+		test_KErrNone(chunk.Decommit(commitOffset,commitSize));	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(pinnedFreeRam,FreeRam());
+
+		test.Printf(_L("Commit pinned memory again\n"));
+		test_KErrNone(chunk.Commit(commitOffset,commitSize));	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(pinnedFreeRam,FreeRam());
+		p = buffer;
+		pEnd = buffer+bufferSize;
+		while(p<pEnd)
+			test_Equal(c,*p++); // memory should have been wiped
+
+		test.Printf(_L("Unpin memory\n"));
+		test_KErrNone(Ldd.UnpinVirtualMemory());	
+		CheckMemoryPresent(buffer, bufferSize, ETrue);
+		test_Equal(initialFreeRam,FreeRam());
+
+		test.Printf(_L("Decommit memory\n"));
+		test_KErrNone(chunk.Decommit(commitOffset,commitSize));	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Compare(FreeRam(),<=,initialFreeRam);
+
+		//
+		// test pin twice...
+		//
+
+		test.Printf(_L("Commit memory\n"));
+		test_KErrNone(chunk.Commit(commitOffset,commitSize));	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(initialFreeRam,FreeRam());
+
+		test.Printf(_L("Pin memory\n"));
+		test_KErrNone(Ldd.PinVirtualMemory((TLinAddr)buffer, bufferSize));	
+		CheckMemoryPresent(buffer, bufferSize, ETrue);
+		test_Equal(pinnedFreeRam,FreeRam());
+
+		test.Printf(_L("Pin memory again\n"));
+		test_KErrNone(Ldd2.PinVirtualMemory((TLinAddr)buffer, bufferSize));	
+		CheckMemoryPresent(buffer, bufferSize, ETrue);
+		test_Equal(pinnedFreeRam,FreeRam());
+
+		test.Printf(_L("Decommit pinned memory\n"));
+		test_KErrNone(chunk.Decommit(commitOffset,commitSize));	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(pinnedFreeRam,FreeRam()); // decommited memory should not be freed as it is pinned
+
+		test.Printf(_L("Unpin memory\n"));
+		test_KErrNone(Ldd2.UnpinVirtualMemory());	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(pinnedFreeRam,FreeRam()); // memory shouldn't be freed as another pin exists
+
+		test.Printf(_L("Unpin memory again\n"));
+		test_KErrNone(Ldd.UnpinVirtualMemory());	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(initialFreeRam,FreeRam()); // memory should be now freed
+
+		//
+		// test page stealing of decommited memory
+		//
+
+		test.Printf(_L("Commit memory\n"));
+		test_KErrNone(chunk.Commit(commitOffset,commitSize));	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(initialFreeRam,FreeRam());
+
+		test.Printf(_L("Pin memory\n"));
+		test_KErrNone(Ldd.PinVirtualMemory((TLinAddr)buffer, bufferSize));	
+		CheckMemoryPresent(buffer, bufferSize, ETrue);
+		test_Equal(pinnedFreeRam,FreeRam());
+
+		test.Printf(_L("Decommit pinned memory\n"));
+		test_KErrNone(chunk.Decommit(commitOffset,commitSize));	
+		CheckMemoryPresent(buffer, bufferSize, EFalse);
+		test_Equal(pinnedFreeRam,FreeRam());
+
+		test.Printf(_L("Unpin memory a higher priority that supervisor thread\n"));
+		RThread().SetPriority(EPriorityRealTime);
+		test_KErrNone(Ldd.UnpinVirtualMemory());	
+		// on single core system, supervisor thread can't run and free pages yet
+		// because we're a higher priority...
+		test.Printf(_L("memory freed = %d\n"),initialFreeRam==FreeRamNoWait());
+
+		test.Printf(_L("Force decommited unpinned pages out of live list\n"));
+		FlushPagingCache();
+		RThread().SetPriority(EPriorityNormal);
+		test_Equal(initialFreeRam,FreeRam()); // memory should be now freed
+
+		//
+		// cleanup...
+		//
+
+		test.Printf(_L("Destroy pin object\n"));
+		test_KErrNone(Ldd.DestroyVirtualPinObject());
+		test_KErrNone(Ldd2.DestroyVirtualPinObject());
+		chunk.Close();
+		}
+
+	test.Printf(_L("Flush paging cache\n"));
+	FlushPagingCache(); // this is a test that has shown up bugs in the past
+	}
+
+
+void TestPinOutOfMemory()
+	{
+	// Ensure that if pinning fails with KErrNoMemory,
+	// there isn't a memory leak
+	const TInt KMaxKernelAllocations = 1024;
+	TInt r=KErrNoMemory;
+	TInt i;
+	const TUint8* buffer = NULL;
+	if (PagedBuffer)
+		{
+		buffer = PagedBuffer;
+		}
+	else
+		{
+		buffer = UnpagedBuffer;
+		}
+	test_NotNull(buffer);
+
+	__KHEAP_MARK;
+	for (i = 0; i < KMaxKernelAllocations && r == KErrNoMemory; i++)
+		{
+		__KHEAP_FAILNEXT(i);
+		test.Printf(_L("Create logical pin object\n"));
+		r = Ldd.CreateVirtualPinObject();
+		__KHEAP_RESET;
+		}
+	test.Printf(_L("Create logical pin object took %d tries\n"),i);
+	test_KErrNone(r);
+
+	r = KErrNoMemory;
+	for (i = 0; i < KMaxKernelAllocations && r == KErrNoMemory; i++)
+		{
+		__KHEAP_FAILNEXT(i);
+		test.Printf(_L("Perform logical pin operation\n"));
+		r = Ldd.PinVirtualMemory((TLinAddr)buffer, KMinBufferSize);
+		__KHEAP_RESET;
+		}
+	test.Printf(_L("Perform logical pin operation took %d tries\n"),i);
+	if (r == KErrNone)
+		{
+		test.Printf(_L("Perform logical unpin operation\n"));
+		Ldd.UnpinVirtualMemory();
+		}
+	
+	test.Printf(_L("Destroy logical pin object\n"));
+	Ldd.DestroyVirtualPinObject();
+	// wait for any async cleanup in the supervisor to finish first...
+	UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0);	
+	__KHEAP_MARKEND;
+
+	test_KErrNone(r);
+	}
+
+
+TInt E32Main()
+	{
+	test.Title();
+	test.Start(_L("Test kernel pinning APIs"));
+
+	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"));
+	
+	test.Next(_L("Loading test drivers"));
+	test_KErrNone(Ldd.Open());
+	test_KErrNone(Ldd2.Open());
+
+	test.Next(_L("Getting page size"));
+	test_KErrNone(UserSvr::HalFunction(EHalGroupKernel,EKernelHalPageSizeInBytes,&PageSize,0));
+	
+	test.Next(_L("Setting up paged and unpaged buffers"));
+
+#ifdef __EPOC32__
+	// Use unpaged rom for our unpaged buffer
+	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
+	UnpagedBuffer = (TUint8*)romHeader;
+	TInt size = romHeader->iPageableRomStart ? romHeader->iPageableRomStart : romHeader->iUncompressedSize;
+	test(size >= KMinBufferSize);
+	
+	if (DPTest::Attributes() & DPTest::ERomPaging)
+		{
+		// Use end of paged ROM for our paged buffer
+		test(romHeader->iPageableRomStart);
+		TInt offset = romHeader->iPageableRomStart + romHeader->iPageableRomSize - KMinBufferSize;
+		offset &= ~0xfff;
+		test(offset>=romHeader->iPageableRomStart);
+		PagedBuffer = (TUint8*)romHeader + offset;
+		}
+	else if (DPTest::Attributes() & DPTest::ECodePaging)
+		{
+		// Use code paged DLL for our paged buffer
+		test_KErrNone(PagedLibrary.Load(KTCodePagingDll4));		
+		TGetAddressOfDataFunction func = (TGetAddressOfDataFunction)PagedLibrary.Lookup(KGetAddressOfDataFunctionOrdinal);
+		TInt size;
+		PagedBuffer = (TUint8*)func(size);
+		test_NotNull(PagedBuffer);
+		test(size >= KMinBufferSize);
+		}
+#else
+	UnpagedBuffer = (TUint8*)User::Alloc(KMinBufferSize);
+	test_NotNull(UnpagedBuffer);
+#endif
+
+	RDebug::Printf("UnpagedBuffer=%x\n",UnpagedBuffer);
+	RDebug::Printf("PagedBuffer=%x\n",PagedBuffer);
+
+	__KHEAP_MARK;
+	
+	test.Next(_L("Logical pin unpaged memory"));
+	TestPinVirtualMemoryUnpaged();
+
+	test.Next(_L("Logical pin invalid memory"));
+	TestPinVirtualMemoryInvalid();
+
+	test.Next(_L("Physical pinning"));
+	TestPinPhysicalMemory();
+	
+	test.Next(_L("Physical pinning OOM"));
+	TestPhysicalPinOutOfMemory();
+	
+	test.Next(_L("Pin OOM Tests"));
+	TestPinOutOfMemory();
+
+	if (PagedBuffer)
+		{
+		test.Next(_L("Logical pin paged memory"));
+		TestPinVirtualMemoryPaged();
+
+		test.Next(_L("Logical pin paged memory soak test"));
+		TestPinVirtualMemoryPagedSoak();
+		}
+
+	if (DPTest::Attributes() & DPTest::EDataPaging)
+		{
+		test.Next(_L("Logical pin then decommit memory"));
+		TestPinVirtualMemoryDecommit();
+		}
+
+	// wait for any async cleanup in the supervisor to finish first...
+	UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0);	
+	__KHEAP_MARKEND;
+
+#ifndef __EPOC32__
+	User::Free((TAny*)UnpagedBuffer);
+#endif
+
+	PagedLibrary.Close();
+	Ldd.Close();
+	Ldd2.Close();
+	test.End();
+
+	return KErrNone;
+	}