kerneltest/e32test/defrag/t_pagemove.cpp
changeset 43 96e5fb8b040d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/defrag/t_pagemove.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,1502 @@
+// Copyright (c) 2006-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\defrag\t_pagemove.cpp
+
+//
+//--------------------------------------------------------------------------------------------------
+//! @SYMTestCaseID			KBASE-T_PAGEMOVE-0572
+//! @SYMTestType			UT
+//! @SYMPREQ				PREQ308
+//! @SYMTestCaseDesc		Test physical page moving
+//!							t_pagemove loads and opens the logical device driver ("D_PAGEMOVE.LDD"). 
+//!							Following this, it requests that the driver attempt to move  
+//!							various kinds of pages directly. 
+//!
+//!							API Information:
+//!								RBusLogicalChannel
+//!
+//!							Platforms/Drives/Compatibility:
+//!								Hardware only. No defrag support on emulator. 
+//!
+//! @SYMTestActions			1  -  Move regular local data pages
+//! 						2  -  Move regular global data pages
+//! 						3  -  Move DLL writable static data pages
+//! 						4  -  Move user self-modifying code chunk pages
+//! 						5  -  Move RAM drive pages
+//!							6  -  Move kernel heap pages (*********DISABLED************)
+//! 						7  -  Move kernel stack pages
+//! 						8  -  Move kernel code pages
+//! 						9  -  Move regular code pages
+//! 						10 -  Move code whilst the page is being modified
+//! 						11 -  Move code (async) whilst the page is being modified
+//! 						12 -  Move ROM locale DLL pages
+//! 						13 -  Move RAM locale DLL pages
+//! 						14 -  Moving pages whilst they are being virtually pinned and unpinned.
+//! 						15 -  Moving pages whilst they are being physically pinned and unpinned.
+//! @SYMTestExpectedResults All tests should pass.
+//! @SYMTestPriority        High
+//! @SYMTestStatus          Implemented
+//--------------------------------------------------------------------------------------------------
+//
+#define __E32TEST_EXTENSION__
+#include <e32test.h>
+#include <e32math.h>
+#include <e32uid.h>
+#include <e32hal.h>
+#include <e32std.h>
+#include <e32std_private.h>
+#include <dptest.h>
+#include "d_pagemove.h"
+#include "t_pagemove_dll.h"
+#include "t_pmwsd.h"
+#include "..\mmu\mmudetect.h"
+#include "..\debug\d_codemodifier.h"
+#include "..\mmu\d_memorytest.h"
+
+//#define _DEBUG_MSG
+#ifdef _DEBUG_MSG
+#define _R_PRINTF(x) 	RDebug::Printf(x)
+#define _T_PRINTF(x)	test.Printf(x)
+#else
+#define _R_PRINTF(x)
+#define _T_PRINTF(x)
+#endif
+
+LOCAL_D RTest test(_L("T_PAGEMOVE"));
+
+_LIT(ELOCL_DEFAULT, "");
+_LIT(ELOCLUS, "T_LOCLUS_RAM");
+_LIT(ELOCLUS_ROM, "T_LOCLUS");
+LOCAL_C TInt E32TestLocale(TInt);
+
+RCodeModifierDevice Device;
+extern TInt TestCodeModFunc();
+
+extern TInt Increment(TInt);
+extern TUint Increment_Length();
+extern TInt Decrement(TInt);
+extern TUint Decrement_Length();
+typedef TInt (*PFI)(TInt);
+
+LOCAL_C void StartCodeModifierDriver();
+LOCAL_C void StopCodeModifierDriver();
+LOCAL_C TInt TestCodeModification(RPageMove &);
+LOCAL_C TInt TestCodeModificationAsync(RPageMove& pagemove);
+
+
+const TPtrC KLddFileName=_L("D_PAGEMOVE.LDD");
+TInt Repitions=4000;
+
+TInt PageSize;
+TUint NumberOfCpus;
+
+volatile TBool ThreadDie;
+
+TBool gDataPagingSupported;
+TBool gRomPagingSupported;
+TBool gCodePagingSupported;
+TBool gPinningSupported;
+
+// This executable is ram loaded (see mmp file) so this function will do fine
+// as a test of RAM-loaded code.
+TInt RamLoadedFunction()
+	{
+	return KArbitraryNumber;
+	}
+
+struct SPinThreadArgs
+	{
+	TLinAddr iLinAddr;
+	TTestFunction iTestFunc;
+	RThread iParentThread;
+	User::TRealtimeState iRealtimeState;
+	};
+
+
+void StartThreads(	TUint aNumThreads, RThread* aThreads, TRequestStatus* aStatus, 
+					TThreadFunction aThreadFunc, SPinThreadArgs& aThreadArgs)
+	{
+	for (TUint i = 0; i < aNumThreads; i++)
+		{
+		test_KErrNone(aThreads[i].Create(KNullDesC, aThreadFunc, KDefaultStackSize, NULL, &aThreadArgs));
+		aThreads[i].Logon(aStatus[i]);
+		TRequestStatus threadInitialised;
+		aThreads[i].Rendezvous(threadInitialised);
+		aThreads[i].Resume();
+		_T_PRINTF(_L("wait for child\n"));
+		User::WaitForRequest(threadInitialised);
+		test_KErrNone(threadInitialised.Int());
+		}
+	}
+
+void EndThreads(TUint aNumThreads, RThread* aThreads, TRequestStatus* aStatus)
+	{
+	for (TUint i = 0; i < aNumThreads; i++)
+		{
+		User::WaitForRequest(aStatus[i]);
+		test_Equal(EExitKill, aThreads[i].ExitType());
+		test_KErrNone(aThreads[i].ExitReason());
+		aThreads[i].Close();
+		}
+	}
+
+
+void Reschedule(TInt64& aSeed)
+	{
+	if (NumberOfCpus == 1)
+		{
+		TInt rand = Math::Rand(aSeed);
+		if ((rand & 0x5) == 5)
+			User::AfterHighRes(rand & 0x7);
+		}
+	}
+
+TInt ReadWriteByte(TAny* aParam)
+	{
+	SPinThreadArgs* args = (SPinThreadArgs*)aParam;
+	volatile TUint8* byte = (volatile TUint8*)args->iLinAddr;
+	TInt64 seed = Math::Random()*Math::Random();
+
+	test_KErrNone(User::SetRealtimeState(args->iRealtimeState));
+
+	// Ensure the the parentThread has moved the page at least once
+	// before we start accessing it.
+	TRequestStatus status;
+	args->iParentThread.Rendezvous(status);
+	RThread::Rendezvous(KErrNone);
+	_R_PRINTF("wait for parent");
+	User::WaitForRequest(status);
+	_R_PRINTF("acesssing page");
+
+	FOREVER
+		{
+		*byte = *byte;
+		Reschedule(seed);
+		if (ThreadDie)
+			break;
+		}
+	return KErrNone;
+	}
+
+
+TInt RunCodeThread(TAny* aParam)
+	{
+	TInt64 seed = Math::Random()*Math::Random();
+	SPinThreadArgs* args = (SPinThreadArgs*)aParam;
+
+	test_KErrNone(User::SetRealtimeState(args->iRealtimeState));
+
+	// Ensure the the parentThread has moved the page at least once
+	// before we start accessing it.
+	TRequestStatus status;
+	args->iParentThread.Rendezvous(status);
+	RThread::Rendezvous(KErrNone);
+	_R_PRINTF("wait for parent");
+	User::WaitForRequest(status);
+	_R_PRINTF("acesssing page");
+
+	FOREVER
+		{
+		TInt r = args->iTestFunc();
+		if (r != KArbitraryNumber)
+			return KErrGeneral;
+		Reschedule(seed);
+		if (ThreadDie)
+			break;
+		}
+	return KErrNone;
+	}
+
+
+TInt VirtualPinPage(TAny* aParam)
+	{
+	TInt64 seed = Math::Random()*Math::Random();
+	SPinThreadArgs* args = (SPinThreadArgs*)aParam;
+	RMemoryTestLdd ldd;
+	test_KErrNone(ldd.Open());
+
+	test_KErrNone(ldd.CreateVirtualPinObject());
+
+	TBool firstRun = ETrue;
+	FOREVER
+		{
+		// Pin the page of aParam.
+		test_KErrNone(ldd.PinVirtualMemory(args->iLinAddr, PageSize));
+		if (firstRun)
+			{// On the first run ensure that the page is definitely pinned when
+			// the parent thread first attempts to move it.
+			TRequestStatus status;
+			args->iParentThread.Rendezvous(status);
+			RThread::Rendezvous(KErrNone);
+			User::WaitForRequest(status);
+			test_KErrNone(status.Int());
+			firstRun = EFalse;
+			}
+		Reschedule(seed);
+		test_KErrNone(ldd.UnpinVirtualMemory());
+		if (ThreadDie)
+			break;
+		}
+	test_KErrNone(ldd.DestroyVirtualPinObject());
+	ldd.Close();
+	return KErrNone;
+	}
+
+
+TInt PhysicalPinPage(TAny* aParam)
+	{
+	TInt64 seed = Math::Random()*Math::Random();
+	SPinThreadArgs* args = (SPinThreadArgs*)aParam;
+
+	RMemoryTestLdd ldd;
+	test_KErrNone(ldd.Open());
+
+	test_KErrNone(ldd.CreatePhysicalPinObject());
+
+	TBool firstRun = ETrue;
+	FOREVER
+		{
+		// Pin the page of aParam, use a read only pinning so that pinning code 
+		// doesn't return KErrAccessDenied as writable mappings not allowed on code.
+		test_KErrNone(ldd.PinPhysicalMemoryRO(args->iLinAddr, PageSize));
+		if (firstRun)
+			{// On the first run ensure that the page is definitely pinned when
+			// the parent thread first attempts to move it.
+			TRequestStatus status;
+			args->iParentThread.Rendezvous(status);
+			RThread::Rendezvous(KErrNone);
+			User::WaitForRequest(status);
+			test_KErrNone(status.Int());
+			firstRun = EFalse;
+			}
+		Reschedule(seed);
+		test_KErrNone(ldd.UnpinPhysicalMemory());
+		if (ThreadDie)
+			break;
+		}
+	test_KErrNone(ldd.DestroyPhysicalPinObject());
+	ldd.Close();
+	return KErrNone;
+	}
+
+TInt ModifyCodeThread(TAny* aParam)
+	{
+	SPinThreadArgs* args = (SPinThreadArgs*)aParam;
+	TUint8* p = (TUint8*)args->iLinAddr;
+	PFI func = (PFI)p;
+
+	// Ensure the the parentThread has moved the page at least once
+	// before we start accessing it.
+	TRequestStatus status;
+	args->iParentThread.Rendezvous(status);
+	RThread::Rendezvous(KErrNone);
+	_R_PRINTF("wait for parent");
+	User::WaitForRequest(status);
+	_R_PRINTF("modifiying page");
+
+	while (!ThreadDie)
+		{
+		Mem::Copy(p, (TAny*)&Increment, Increment_Length());
+		User::IMB_Range(p, p+Increment_Length());
+		test_Equal(8, func(7));
+
+		Mem::Copy(p, (TAny*)&Decrement, Decrement_Length());
+		User::IMB_Range(p, p+Decrement_Length());
+		test_Equal(6, func(7));
+		}
+	return KErrNone;
+	}
+
+
+enum TMovingPinStage
+	{
+	ENoPinning,
+	EVirtualPinning,
+	EPhysicalPinning,
+	EMovingPinStages,
+	};
+
+void TestUserData(RPageMove& pagemove, TUint8* array, TInt size, TBool aPagedData=EFalse)
+	{
+	_T_PRINTF(_L("Fill the array with some data\n"));
+	for (TInt i=0; i<size; i++) array[i] = i*i;
+
+	TUint8* firstpage = (TUint8*)_ALIGN_DOWN((TLinAddr)array, PageSize);
+	RThread thread;
+	thread.Open(RThread().Id());
+	SPinThreadArgs threadArgs;
+	threadArgs.iLinAddr = (TLinAddr)array;
+	threadArgs.iParentThread = thread;
+	threadArgs.iRealtimeState = User::ERealtimeStateOff;
+
+	TMovingPinStage endStage = EMovingPinStages;
+	if (!gPinningSupported)
+		endStage = EVirtualPinning;
+
+	for (TUint state = ENoPinning; state < (TUint)endStage; state++)
+		{
+		TThreadFunction threadFunc = NULL;
+		switch (state)
+			{
+			case ENoPinning:
+				test.Printf(_L("Attempt to move pages while they are being modified\n"));
+				threadFunc = &ReadWriteByte;
+				break;
+			case EVirtualPinning:
+				test.Printf(_L("Attempt to move pages while they are being virtually pinned\n"));
+				threadFunc = &VirtualPinPage;
+				break;
+			case EPhysicalPinning:
+				test.Printf(_L("Attempt to move pages while they are being physically pinned\n"));
+				threadFunc = &PhysicalPinPage;
+				break;
+			}
+		ThreadDie = EFalse;
+		TUint numThreads = (NumberOfCpus > 1) ? NumberOfCpus - 1 : 1;
+		RThread* userDataThread = new RThread[numThreads];
+		TRequestStatus* s = new TRequestStatus[numThreads];
+		StartThreads(numThreads, userDataThread, s, threadFunc, threadArgs);
+
+		_T_PRINTF(_L("Move first array page repeatedly\n"));
+		TBool success=EFalse;
+		TUint inuse = 0;
+		*(volatile TUint8*)array = *array;	// Ensure the page of the first entry is paged in for the first move.
+		for (TInt i=0; i < Repitions*2; i++)
+			{
+			TInt r = pagemove.TryMovingUserPage(firstpage, ETrue);
+			if (i == 0)
+				{// If this is the first run allow the pinning threads to 
+				// unpin the memory now that we've definitely done at least 
+				// one page move with the page pinned.
+				_T_PRINTF(_L("signal to child\n"));
+				RThread::Rendezvous(KErrNone);
+				}
+			switch (r)
+				{
+				case KErrInUse:
+					inuse++;
+					break;
+				case KErrArgument:
+					// The page was paged out, this should only happen for paged data.
+					test(aPagedData);
+					break;
+				default:
+					test_KErrNone(r);
+					success=ETrue;
+					break;
+				}
+			}
+		// Can't guarantee that for paged data the page and its page tables will
+		// be paged in, in most cases it will be at least once.
+		// Pinning the page should always return KErrInUse except for virtually 
+		// pinned non-paged memory as virtual pinning is a nop for unpaged memory.
+		test.Printf(_L("inuse test removed; inuse %d\n"),inuse);
+		//test(inuse || aPagedData || state == EVirtualPinning);
+		test(success || state == EPhysicalPinning);
+
+		ThreadDie = ETrue;
+		EndThreads(numThreads, userDataThread, s);
+
+		_T_PRINTF(_L("Validate page data\n"));
+		for (TInt i=0; i<size; i++)
+			test_Equal((TUint8)(i*i), array[i]);
+		}
+	thread.Close();
+	}
+
+
+void TestMovingCode(RPageMove& aPagemove, TTestFunction aFunc, TBool aPaged=EFalse)
+	{
+	TUint8* firstpage = (TUint8*)_ALIGN_DOWN((TLinAddr)aFunc, PageSize);
+	RThread thread;
+	thread.Open(RThread().Id());
+	SPinThreadArgs threadArgs;
+	threadArgs.iLinAddr = (TLinAddr)firstpage;
+	threadArgs.iTestFunc = aFunc;
+	threadArgs.iParentThread = thread;
+	threadArgs.iRealtimeState = User::ERealtimeStateOff;
+
+	TMovingPinStage endStage = EMovingPinStages;
+	if (!gPinningSupported)
+		endStage = EVirtualPinning;
+
+	for (TUint state = ENoPinning; state < (TUint)endStage; state++)
+		{
+		TThreadFunction threadFunc = NULL;
+		switch (state)
+			{
+			case ENoPinning:
+				test.Printf(_L("Attempt to move pages while they are being executed\n"));
+				threadFunc = &RunCodeThread;
+				test_Equal(KArbitraryNumber, aFunc()); // Ensure the page is paged in.
+				break;
+			case EVirtualPinning:
+				test.Printf(_L("Attempt to move pages while they are being virtually pinned\n"));
+				threadFunc = &VirtualPinPage;
+				break;
+			case EPhysicalPinning:
+				test.Printf(_L("Attempt to move pages while they are being physically pinned\n"));
+				threadFunc = &PhysicalPinPage;
+				break;
+			}
+		ThreadDie = EFalse;
+		TUint numThreads = (NumberOfCpus > 1) ? NumberOfCpus - 1 : 1;
+		RThread* codeRunThread = new RThread[numThreads];
+		TRequestStatus* s = new TRequestStatus[numThreads];
+		StartThreads(numThreads, codeRunThread, s, threadFunc, threadArgs);
+
+		_T_PRINTF(_L("Move first code page repeatedly\n"));
+		test_Equal(KArbitraryNumber, aFunc());	
+		TBool inuse=EFalse, success=EFalse;
+		for (TInt i=0; i < Repitions; i++)
+			{
+			TInt r = aPagemove.TryMovingUserPage(firstpage, ETrue);
+			if (i == 0)
+				{// If this is the first run allow the pinning threads to 
+				// unpin the memory now that we've definitely done at least 
+				// one page move with the page pinned.
+				_T_PRINTF(_L("signal to child\n"));
+				RThread::Rendezvous(KErrNone);
+				}
+			switch (r)
+				{
+				case KErrInUse:
+					inuse=ETrue;
+					break;
+				case KErrArgument:
+					// The page was paged out, this should only happen for paged code.
+					test(aPaged);
+					break;
+				default:
+					test_KErrNone(r);
+					success=ETrue;
+					break;
+				}
+			}
+		// Physical pinning or adding a new pinning while a page is being moved
+		// should prevent code pages being moved.
+		switch (state)
+		{
+			case ENoPinning :			
+				test(!inuse || aPaged);	// Stealing may get KErrInUse but this should only happen for paged code.
+			case EVirtualPinning :
+				test(success);
+				break;
+			case EPhysicalPinning :
+				break;
+		}
+
+		ThreadDie = ETrue;
+		EndThreads(numThreads, codeRunThread, s);
+
+		_T_PRINTF(_L("Validate page data\n"));
+		test_Equal(KArbitraryNumber, aFunc());		
+		}
+	thread.Close();
+	}
+
+
+void TestMovingRealtime(RPageMove& aPagemove, TUint8* aArray, TInt aSize, TTestFunction aFunc, TBool aCode, TBool aPaged=EFalse)
+	{
+	TThreadFunction threadFunc;
+	TLinAddr pageAddr;
+	RThread thread;
+	TUint8* firstpage;
+	thread.Open(RThread().Id());
+	SPinThreadArgs threadArgs;
+	threadArgs.iParentThread = thread;
+	if (aCode)
+		{
+		pageAddr = (TLinAddr)aFunc;
+		firstpage = (TUint8*)_ALIGN_DOWN(pageAddr, PageSize);
+		threadArgs.iLinAddr = (TLinAddr)firstpage;
+		threadFunc = RunCodeThread;
+		threadArgs.iTestFunc = aFunc;
+		test_Equal(KArbitraryNumber, aFunc());
+		}
+	else
+		{
+		pageAddr = (TLinAddr)aArray;
+		firstpage = (TUint8*)_ALIGN_DOWN(pageAddr, PageSize);
+		threadArgs.iLinAddr = (TLinAddr)aArray;
+		threadFunc = ReadWriteByte;
+		_T_PRINTF(_L("Fill the array with some data\n"));
+		for (TInt i=0; i<aSize; i++) aArray[i] = i*i;
+		}
+
+	RMemoryTestLdd ldd;
+
+	TMovingPinStage endStage = EMovingPinStages;
+	if (gPinningSupported)
+		{
+		test_KErrNone(ldd.Open());
+		test_KErrNone(ldd.CreateVirtualPinObject());
+		test_KErrNone(ldd.CreatePhysicalPinObject());
+		}
+	else
+		endStage = EVirtualPinning;
+
+	for (TUint state = ENoPinning; state < (TUint)endStage; state++)
+		{
+		switch (state)
+			{
+			case ENoPinning:
+				test.Printf(_L("Attempt to move pages while they are being accessed\n"));
+				break;
+			case EVirtualPinning:
+				test.Printf(_L("Attempt to move pages while they are virtually pinned\n"));
+				test_KErrNone(ldd.PinVirtualMemory((TLinAddr)firstpage, PageSize));
+
+				break;
+			case EPhysicalPinning:
+				test.Printf(_L("Attempt to move pages while they are physically pinned\n"));
+				test_KErrNone(ldd.PinPhysicalMemoryRO((TLinAddr)firstpage, PageSize));
+				break;
+			}
+		for (	TUint realtimeState = User::ERealtimeStateOff; 
+				realtimeState <= User::ERealtimeStateWarn; 
+				realtimeState++)
+			{
+			ThreadDie = EFalse;
+			RThread accessThread;
+			TRequestStatus s;
+			threadArgs.iRealtimeState = (User::TRealtimeState)realtimeState;
+			test_KErrNone(accessThread.Create(_L("Realtime Thread"), threadFunc, KDefaultStackSize, NULL, &threadArgs));
+			accessThread.Logon(s);
+			TRequestStatus threadInitialised;
+			accessThread.Rendezvous(threadInitialised);
+			accessThread.Resume();
+
+			_T_PRINTF(_L("wait for child\n"));
+			User::WaitForRequest(threadInitialised);
+			test_KErrNone(threadInitialised.Int());
+
+			_T_PRINTF(_L("Move page repeatedly\n"));
+			TBool success=EFalse, pagedOut=EFalse;
+			TUint inuse=0;
+			if (aCode)
+				{
+				test_Equal(KArbitraryNumber, aFunc());
+				}
+			else
+				{
+				*(volatile TUint8*)aArray = *aArray;
+				}
+
+			for (TInt i=0; i < Repitions; i++)
+				{
+				TInt r = aPagemove.TryMovingUserPage(firstpage, ETrue);
+				if (i == 0)
+					{
+					_T_PRINTF(_L("signal to child\n"));
+					RThread::Rendezvous(KErrNone);
+					}
+				switch (r)
+					{
+					case KErrInUse:
+						inuse++;
+						break;
+					case KErrArgument:
+						// The page was paged out, this should only happen for paged code.
+						test(aPaged);
+						pagedOut = ETrue;
+						break;
+					default:
+						test_KErrNone(r);
+						success=ETrue;
+						break;
+					}
+				}
+			ThreadDie = ETrue;
+			User::WaitForRequest(s);
+			test.Printf(_L("inuse %d\n"),inuse);
+			switch (state)
+				{
+				case ENoPinning :
+					test(success);
+					if (EExitPanic == accessThread.ExitType())
+						{
+						test(accessThread.ExitCategory()==_L("KERN-EXEC"));
+						test_Equal(EIllegalFunctionForRealtimeThread, accessThread.ExitReason());
+						test(aPaged && realtimeState == User::ERealtimeStateOn);
+						}
+					else
+						{
+						test_Equal(EExitKill,accessThread.ExitType());
+						test_KErrNone(accessThread.ExitReason());
+						}
+					// Ensure the page is paged in before we attempt to move it again with a different realtime state.
+					if (aCode)
+						{
+						test_Equal(KArbitraryNumber, aFunc());
+						}
+					else
+						{
+						*(volatile TUint8*)aArray = *aArray;
+						}
+					break;				
+				case EVirtualPinning :
+					test(!aCode || !inuse);
+					test(success);
+					test(!pagedOut);
+					test_Equal(EExitKill,accessThread.ExitType());
+					test_KErrNone(accessThread.ExitReason());
+					break;
+				case EPhysicalPinning :
+					test(!success);
+					break;
+				}
+			accessThread.Close();
+			}
+		if (gPinningSupported)
+			{
+			// Unpin any pinned memory.
+			test_KErrNone(ldd.UnpinVirtualMemory());
+			test_KErrNone(ldd.UnpinPhysicalMemory());
+			}
+
+		_T_PRINTF(_L("Validate page data\n"));
+		if (aCode)
+			{
+			test_Equal(KArbitraryNumber, aFunc());
+			}
+		else
+			{
+			for (TInt i=0; i<aSize; i++)
+				test_Equal((TUint8)(i*i), aArray[i]);
+			}
+			
+		}
+	if (gPinningSupported)
+		{
+		test_KErrNone(ldd.DestroyVirtualPinObject());
+		test_KErrNone(ldd.DestroyPhysicalPinObject());
+		ldd.Close();
+		}
+	thread.Close();
+	}
+
+// Only commits and decommits the first page as that is the only page that is being moved.
+// Plus this ensures the page table and page directories of the chunk are always allocated
+// and therefore prevents Epoc::LinearToPhysical() from crashing the system.
+TInt CommitDecommit(TAny* aParam)
+	{
+	RChunk* chunk = (RChunk*) aParam;
+	volatile TUint8* byte = chunk->Base();
+	FOREVER
+		{
+		*byte = *byte;
+		User::AfterHighRes(0);
+		TInt r = chunk->Decommit(0, PageSize);
+		if (r != KErrNone)
+			return r;
+		User::AfterHighRes(0);
+		r = chunk->Commit(0, PageSize);
+		if (r != KErrNone)
+			return r;
+		}
+	}
+
+void TestCommitDecommit(RPageMove& pagemove, RChunk& aChunk)
+	{
+	test.Printf(_L("Attempt to move a page while it is being committed and decommited\n"));
+	RThread thread;
+	TRequestStatus s;
+	test_KErrNone(thread.Create(_L("CommitDecommit"), &CommitDecommit, KDefaultStackSize, NULL, (TAny*)&aChunk));
+	thread.Logon(s);
+	thread.SetPriority(EPriorityMore);
+	thread.Resume();
+
+	TUint8* firstpage=(TUint8*)_ALIGN_DOWN((TLinAddr)aChunk.Base(), PageSize);
+	for (TInt i=0; i < Repitions; i++)
+		{
+		TInt r = pagemove.TryMovingUserPage(firstpage, ETrue);
+		// Allow all valid return codes as we are only testing that this doesn't 
+		// crash the kernel and the page could be commited, paged out or decommited
+		// at any one time.
+		test_Value(r, r <= KErrNone);
+		}
+
+	thread.Kill(KErrNone);
+	User::WaitForRequest(s);
+	test_Equal(EExitKill,thread.ExitType());
+	test_KErrNone(thread.ExitReason());
+	thread.Close();
+	}
+
+
+void TestPageTableDiscard(RPageMove& pagemove, TUint8* array, TUint size)
+	{
+	_T_PRINTF(_L("Fill the array with some data\n"));
+	for (TUint i=0; i<size; i++) array[i] = i*i;
+
+	TUint8* firstpage = (TUint8*)_ALIGN_DOWN((TLinAddr)array, PageSize);
+	RThread thread;
+	thread.Open(RThread().Id());
+	SPinThreadArgs threadArgs;
+	threadArgs.iLinAddr = (TLinAddr)array;
+	threadArgs.iParentThread = thread;
+	threadArgs.iRealtimeState = User::ERealtimeStateOff;
+
+	TMovingPinStage endStage = EMovingPinStages;
+	if (!gPinningSupported)
+		endStage = EVirtualPinning;
+	
+	for (TUint pageTableInfo = 0; pageTableInfo < 2; pageTableInfo++)
+		{
+		for (TUint state = ENoPinning; state < (TUint)endStage; state++)
+			{
+			TThreadFunction threadFunc = NULL;
+			if (!pageTableInfo)
+			{
+			switch (state)
+				{
+				case ENoPinning:
+					test.Printf(_L("Attempt to move page tables whilst the pages they map are being modified\n"));
+					threadFunc = &ReadWriteByte;
+					break;
+				case EVirtualPinning:
+					test.Printf(_L("Attempt to move page tables whilst the pages they map are being virtually pinned\n"));
+					threadFunc = &VirtualPinPage;
+					break;
+				case EPhysicalPinning:
+					test.Printf(_L("Attempt to move page tables whilst the pages they map are being physically pinned\n"));
+					threadFunc = &PhysicalPinPage;
+					break;
+				}
+			}
+			else
+			{
+			switch (state)
+				{
+				case ENoPinning:
+					test.Printf(_L("Attempt to move page table infos whilst pages they refer to are being modified\n"));
+					threadFunc = &ReadWriteByte;
+					break;
+				case EVirtualPinning:
+					test.Printf(_L("Attempt to move page table infos whilst pages they refer to are being virtually pinned\n"));
+					threadFunc = &VirtualPinPage;
+					break;
+				case EPhysicalPinning:
+					test.Printf(_L("Attempt to move page table infos whilst pages they refer to are being physically pinned\n"));
+					threadFunc = &PhysicalPinPage;
+					break;
+				}
+			}
+			ThreadDie = EFalse;
+			TUint numThreads = (NumberOfCpus > 1) ? NumberOfCpus - 1 : 1;
+			RThread* threads = new RThread[numThreads];
+			TRequestStatus* s = new TRequestStatus[numThreads];
+			StartThreads(numThreads, threads, s, threadFunc, threadArgs);
+
+			_T_PRINTF(_L("Move first array page repeatedly\n"));
+			TUint inuse = 0;
+			for (TInt i=0; i < Repitions; i++)
+				{
+				TInt r;
+				if (!pageTableInfo)
+					r = pagemove.TryMovingPageTable(firstpage);
+				else
+					r = pagemove.TryMovingPageTableInfo(firstpage);					
+				if (i == 0)
+					{// If this is the first run allow the pinning threads to 
+					// unpin the memory now that we've definitely done at least 
+					// one page move with the page pinned.
+					_T_PRINTF(_L("signal to child\n"));
+					RThread::Rendezvous(KErrNone);
+					}
+				switch (r)
+					{
+					case KErrInUse:
+						inuse++;
+						break;
+					case KErrNotFound:
+						// The page table or page table info page was paged out.
+						break;
+					default:
+						test_KErrNone(r);
+						break;
+					}
+				}
+			test.Printf(_L("inuse %d\n"),inuse);
+			// A virtually pinned page should always return KErrInUse at least once.
+			test(state != EVirtualPinning || inuse);
+
+			ThreadDie = ETrue;
+			EndThreads(numThreads, threads, s);
+
+			_T_PRINTF(_L("Validate page data\n"));
+			for (TUint i=0; i<size; i++)
+				test_Equal((TUint8)(i*i), array[i]);
+			}
+		}
+	thread.Close();
+	}
+
+// Basic testing of moving rom pages.
+void TestMovingRom(RPageMove& aPageMove)
+	{
+	TUint8* pPage=(TUint8*)User::Alloc(PageSize);
+	test(pPage!=NULL);
+
+	TUint romHdr = UserSvr::RomHeaderAddress();
+
+	if (gPinningSupported)
+		{
+		// Pin an unpaged rom page to get the physical address of the rom page.
+		// Pinning unpaged rom actually does nothing except return the physical 
+		// address of the page.
+		RMemoryTestLdd ldd;
+		test_KErrNone(ldd.Open());
+		test_KErrNone(ldd.CreatePhysicalPinObject());
+
+		// Save contents of rom page.
+		Mem::Move(pPage,(TAny*)romHdr,PageSize);
+
+		test_KErrNone(ldd.PinPhysicalMemoryRO(romHdr, PageSize));
+		test_KErrNone(ldd.UnpinPhysicalMemory());
+
+		// Now move the page, d_memorytest saves the address of the pinned page
+		// depsite it being unpinned.
+		// Will get KErrArgument as rom pages don't have an SPageInfo so memory
+		// model doesn't treat rom as though they are in ram, which in most cases 
+		// they are.
+		test_Equal(KErrArgument, ldd.MovePinnedPhysicalMemory(0));
+
+		test_KErrNone(Mem::Compare((TUint8*)romHdr,PageSize,pPage,PageSize));
+		test_KErrNone(ldd.DestroyPhysicalPinObject());
+		ldd.Close();
+		}
+
+	if (gRomPagingSupported)
+		{
+		// Use paged part of rom for testing
+		TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
+		test(romHeader->iPageableRomStart);
+		TUint romAddr = (TUint)((TUint8*)romHeader + romHeader->iPageableRomStart + 64 * PageSize);
+
+		// We will use the 64th pagable rom page so check that it exists.
+		test(romHeader->iPageableRomSize >= 65 * PageSize);
+	
+		// Page in the rom page and save it contents.
+		Mem::Move(pPage,(TAny*)romAddr,PageSize);
+		// This will actually discard the page not move it.
+		test_KErrNone(aPageMove.TryMovingUserPage(pPage));
+
+		test_KErrNone(Mem::Compare((TUint8*)romAddr,PageSize,pPage,PageSize));
+		}
+	}
+
+
+void TestMovingCodeChunk(RPageMove& pagemove, RChunk aChunk, TBool aPagedData)
+	{
+	TUint8* p = aChunk.Base();
+
+	TUint8* firstpage = (TUint8*)_ALIGN_DOWN((TLinAddr)p, PageSize);
+	RThread thread;
+	thread.Open(RThread().Id());
+	SPinThreadArgs threadArgs;
+	threadArgs.iLinAddr = (TLinAddr)p;
+	threadArgs.iParentThread = thread;
+
+	test.Printf(_L("Attempt to move pages while they are being executed and modified\n"));
+	ThreadDie = EFalse;
+	RThread modCodeThread;
+	TRequestStatus s;
+	test_KErrNone(modCodeThread.Create(_L("User Data thread"), &ModifyCodeThread, KDefaultStackSize, NULL, &threadArgs));
+	modCodeThread.Logon(s);
+	TRequestStatus threadInitialised;
+	modCodeThread.Rendezvous(threadInitialised);
+	modCodeThread.Resume();
+
+	_T_PRINTF(_L("wait for child\n"));
+	User::WaitForRequest(threadInitialised);
+	test_KErrNone(threadInitialised.Int());
+
+	_T_PRINTF(_L("Move code chunk page repeatedly\n"));
+	TBool success=EFalse;
+	*(volatile TUint8*)p = *p; // Ensure the page of the first entry is paged in for the first move.
+	for (TInt i=0; i < Repitions; i++)
+		{
+		TInt r = pagemove.TryMovingUserPage(firstpage, ETrue);
+		if (i == 0)
+			{// If this is the first run allow the modifying thread to run now 
+			// we've done one move.
+			_T_PRINTF(_L("signal to child\n"));
+			RThread::Rendezvous(KErrNone);
+			}
+		switch (r)
+			{
+			case KErrInUse:
+				break;
+			case KErrArgument:
+				// The page was paged out, this should only happen for paged data.
+				test(aPagedData);
+				break;
+			default:
+				test_KErrNone(r);
+				success=ETrue;
+				break;
+			}
+		}
+	test(success);
+
+	ThreadDie = ETrue;
+	User::WaitForRequest(s);
+	test_Equal(EExitKill,modCodeThread.ExitType());
+	test_KErrNone(modCodeThread.ExitReason());
+	modCodeThread.Close();
+
+	thread.Close();
+	}
+
+GLDEF_C TInt E32Main()
+    {
+	test.Title();
+	if (!HaveMMU())
+		{
+		test.Printf(_L("This test requires an MMU\n"));
+		return KErrNone;
+		}
+
+	test.Start(_L("Load test LDD"));
+	TInt r=User::LoadLogicalDevice(KLddFileName);
+	test(r==KErrNone || r==KErrAlreadyExists);
+
+	test_KErrNone(UserHal::PageSizeInBytes(PageSize));
+
+	// Determine which types of paging are supported
+	TUint32 attrs = DPTest::Attributes();
+	gRomPagingSupported = (attrs & DPTest::ERomPaging) != 0;
+	gCodePagingSupported = (attrs & DPTest::ECodePaging) != 0;
+	gDataPagingSupported = (attrs & DPTest::EDataPaging) != 0;
+
+	// Does this memory model support pinning.
+	TInt mm = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, 0, 0) & EMemModelTypeMask;
+	gPinningSupported = mm >= EMemModelTypeFlexible;
+
+	RPageMove pagemove;
+	test.Next(_L("Open test LDD"));
+	test_KErrNone(pagemove.Open());
+
+	// Determine whether this is a smp device.
+	NumberOfCpus = pagemove.NumberOfCpus();
+	if (NumberOfCpus > 1)
+		Repitions = 1000;	// SMP system therefore likely to get KErrInUse in less repitions.
+
+	test.Next(_L("Attempting to move regular local data pages"));
+		{
+		const TInt size=16384;
+		TUint8* array = new TUint8[size];
+		test_NotNull(array);
+
+		TestUserData(pagemove, array, size);
+
+		_T_PRINTF(_L("Walk heap\n"));
+		User::Check();
+
+		delete [] array;
+		}
+
+	test.Next(_L("Attempting to move regular global coarse data pages"));
+		{
+		const TInt size=1<<20;	// Make this chunk multiple of 1MB so it is a coarse memory object on FMM
+		RChunk chunk;
+		test_KErrNone(chunk.CreateDisconnectedGlobal(_L("Dave"), 0, size, size));
+		TUint8* array = chunk.Base();
+
+		TestUserData(pagemove, array, size);
+		TestMovingRealtime(pagemove, array, size, NULL, EFalse);
+		TestCommitDecommit(pagemove, chunk);
+
+		chunk.Close();
+		}
+
+	if (gDataPagingSupported)
+		{
+		test.Next(_L("Attempting to move demand paged fine local user data pages"));
+		const TInt size=16384;
+		TChunkCreateInfo createInfo;
+		createInfo.SetDisconnected(0, size, size);
+		createInfo.SetPaging(TChunkCreateInfo::EPaged);
+		RChunk chunk;
+		test_KErrNone(chunk.Create(createInfo));
+		TUint8* array = chunk.Base();
+
+		TestUserData(pagemove, array, size, ETrue);
+		TestMovingRealtime(pagemove, array, size, NULL, EFalse, ETrue);
+		TestPageTableDiscard(pagemove, array, size);
+		TestCommitDecommit(pagemove, chunk);
+		chunk.Close();
+
+		test.Next(_L("Attempting to move demand paged coarse global user data pages"));
+		const TInt sizeCoarse = 1 << 20; // Make this chunk multiple of 1MB so it is a coarse memory object on FMM
+		TChunkCreateInfo createInfoCoarse;
+		createInfoCoarse.SetDisconnected(0, sizeCoarse, sizeCoarse);
+		createInfoCoarse.SetGlobal(_L("Dave"));
+		createInfoCoarse.SetPaging(TChunkCreateInfo::EPaged);
+		RChunk chunkCoarse;
+		test_KErrNone(chunkCoarse.Create(createInfoCoarse));
+		array = chunkCoarse.Base();
+
+		TestUserData(pagemove, array, sizeCoarse, ETrue);
+		TestMovingRealtime(pagemove, array, sizeCoarse, NULL, EFalse, ETrue);
+		TestPageTableDiscard(pagemove, array, sizeCoarse);
+		TestCommitDecommit(pagemove, chunkCoarse);
+		chunkCoarse.Close();
+		}
+
+	test.Next(_L("Attempting to move DLL writable static data pages"));
+		{
+		const TInt size=16384;
+		TUint8* array = DllWsd::Address();
+
+		TestUserData(pagemove, array, size);
+		}
+
+	test.Next(_L("Attempting to move user self-mod code chunk page when IMB'ing and executing"));
+	RChunk codeChunk;
+	test_KErrNone(codeChunk.CreateLocalCode(PageSize,PageSize));
+	TestMovingCodeChunk(pagemove, codeChunk, EFalse);
+	codeChunk.Close();
+
+	if (gDataPagingSupported)
+		{
+		test.Next(_L("Attempting to move paged user self-mod code chunk page when IMB'ing and executing"));
+		TChunkCreateInfo createInfo;
+		createInfo.SetCode(PageSize, PageSize);
+		createInfo.SetPaging(TChunkCreateInfo::EPaged);
+
+		RChunk pagedCodeChunk;
+		test_KErrNone(pagedCodeChunk.Create(createInfo));
+		TestMovingCodeChunk(pagemove, pagedCodeChunk, ETrue);
+		pagedCodeChunk.Close();
+		}
+
+	test.Next(_L("Attempting to move RAM drive"));
+	if ((MemModelAttributes()&EMemModelTypeMask) == EMemModelTypeMultiple)
+		{
+		for (TInt i=0; i<Repitions; i++)
+			test_KErrNone(pagemove.TryMovingUserPage((TAny*)0xA0000000));
+		}
+	else if ((MemModelAttributes()&EMemModelTypeMask) == EMemModelTypeMoving)
+		{
+		for (TInt i=0; i<Repitions; i++)
+			test_KErrNone(pagemove.TryMovingUserPage((TAny*)0x40000000));
+		}
+	else if ((MemModelAttributes()&EMemModelTypeMask) == EMemModelTypeFlexible)
+		{
+		// do nothing, RAM drive is not special
+		}
+	else
+		{
+		test.Printf(_L("Don't know where the RAM drive is!"));
+		test(0);
+		}
+	
+#if 0
+	test.Next(_L("Attempting to move kernel heap pages"));
+	for (TInt i=0; i<Repitions; i++)
+		test_KErrNone(pagemove.TryMovingKHeap());
+#endif
+
+	if ((MemModelAttributes()&EMemModelTypeMask) != EMemModelTypeFlexible)
+		{// Only the moving and multiple memory models move kernel stack pages.
+		test.Next(_L("Attempting to move kernel stack pages"));
+		for (TInt i=0; i<Repitions; i++)
+			test_KErrNone(pagemove.TryMovingKStack());
+		}
+
+	test.Next(_L("Attempting to move ROM pages"));
+	TestMovingRom(pagemove);
+
+	test.Next(_L("Attempting to move kernel code pages"));
+	for (TInt i=0; i<Repitions; i++)
+		test_KErrNone(pagemove.TryMovingKCode());
+
+	test.Next(_L("Attempting to move regular code pages"));
+	TestMovingCode(pagemove, RamLoadedFunction);
+	TestMovingRealtime(pagemove, NULL, 0, RamLoadedFunction, ETrue, EFalse);
+
+	if (gCodePagingSupported)
+		{
+		test.Next(_L("Attempting to move demand paged code pages"));
+		TestMovingCode(pagemove, DllTestFunction, ETrue);
+		TestMovingRealtime(pagemove, NULL, 0, DllTestFunction, ETrue, ETrue);
+		}
+
+	/* Setup CodeModifier Test Driver */
+	StartCodeModifierDriver();
+	test(KErrNone==Device.InitialiseCodeModifier(/* Max break points */ 5 ));
+
+	test.Next(_L("Attempting to move code page being modified\n"));
+	test_KErrNone(TestCodeModification(pagemove));
+
+	test.Next(_L("Attempting to move code (async) while page being modified"));
+	test_KErrNone(TestCodeModificationAsync(pagemove));
+
+	StopCodeModifierDriver();
+	
+	test.Next(_L("Attempting to move ROM Locale DLL Page"));
+	test_KErrNone(E32TestLocale(1));
+
+	test.Next(_L("Attempting to move RAM Locale DLL Page"));
+	test_KErrNone(E32TestLocale(0));
+
+	test.Next(_L("Close test LDD"));
+	pagemove.Close();
+	User::FreeLogicalDevice(KLddFileName);
+
+	test.End();
+	return(KErrNone);
+    }
+
+
+void testUS(const TLocale& aLocale)
+{
+	test.Printf(_L("Test US\n"));
+
+	test(aLocale.CountryCode()==1);
+	test(aLocale.DateFormat()==EDateAmerican);
+	test(aLocale.TimeFormat()==ETime12);
+	test(aLocale.CurrencySymbolPosition()==ELocaleBefore);
+	test(aLocale.CurrencySpaceBetween()==FALSE);
+	test(aLocale.CurrencyDecimalPlaces()==2);
+	test(aLocale.CurrencyNegativeInBrackets()==EFalse);
+	test(aLocale.CurrencyTriadsAllowed()==TRUE);
+	test(aLocale.ThousandsSeparator()==',');
+	test(aLocale.DecimalSeparator()=='.');
+	test(aLocale.DateSeparator(0)==0);
+	test(aLocale.DateSeparator(1)=='/');
+	test(aLocale.DateSeparator(2)=='/');
+	test(aLocale.DateSeparator(3)==0);
+	test(aLocale.TimeSeparator(0)==0);
+	test(aLocale.TimeSeparator(1)==':');
+	test(aLocale.TimeSeparator(2)==':');
+	test(aLocale.TimeSeparator(3)==0);
+	test(aLocale.AmPmSymbolPosition()==TRUE);
+	test(aLocale.AmPmSpaceBetween()==TRUE);
+	test(aLocale.HomeDaylightSavingZone()==EDstNorthern);
+	test(aLocale.WorkDays()==0x1f);
+	test(aLocale.StartOfWeek()==ESunday);
+	test(aLocale.ClockFormat()==EClockAnalog);
+	test(aLocale.UnitsGeneral()==EUnitsImperial);
+	test(aLocale.UnitsDistanceShort()==EUnitsImperial);
+	test(aLocale.UnitsDistanceLong()==EUnitsImperial);
+}
+
+
+void testUK(const TLocale& aLocale)
+{
+//#ifdef __WINS__
+	test(aLocale.CountryCode()==44);
+	test(aLocale.DateFormat()==EDateEuropean);
+	test(aLocale.TimeFormat()==ETime12);
+	test(aLocale.CurrencySymbolPosition()==ELocaleBefore);
+	test(aLocale.CurrencySpaceBetween()==FALSE);
+	test(aLocale.CurrencyDecimalPlaces()==2);
+	test(aLocale.CurrencyNegativeInBrackets()==EFalse);
+	test(aLocale.CurrencyTriadsAllowed()==TRUE);
+	test(aLocale.ThousandsSeparator()==',');
+	test(aLocale.DecimalSeparator()=='.');
+	test(aLocale.DateSeparator(0)==0);
+	test(aLocale.DateSeparator(1)=='/');
+	test(aLocale.DateSeparator(2)=='/');
+	test(aLocale.DateSeparator(3)==0);
+	test(aLocale.TimeSeparator(0)==0);
+	test(aLocale.TimeSeparator(1)==':');
+	test(aLocale.TimeSeparator(2)==':');
+	test(aLocale.TimeSeparator(3)==0);
+	test(aLocale.AmPmSymbolPosition()==TRUE);
+	test(aLocale.AmPmSpaceBetween()==TRUE);
+	test(aLocale.HomeDaylightSavingZone()==EDstEuropean);
+	test(aLocale.WorkDays()==0x1f);
+	test(aLocale.StartOfWeek()==EMonday);
+	test(aLocale.ClockFormat()==EClockAnalog);
+	test(aLocale.UnitsGeneral()==EUnitsImperial);
+	test(aLocale.UnitsDistanceShort()==EUnitsImperial);
+	test(aLocale.UnitsDistanceLong()==EUnitsImperial);
+//#endif
+}
+
+
+void testChangeLocale(TInt isrom)
+{
+	TLocale locale;
+	
+#ifdef __WINS__
+//We get a power-change notification 1 second after switch-on
+//So we wait for a second on WINS.
+//Should we fix this bug??
+	User::After(1000000);
+#endif
+	RChangeNotifier notifier;
+	TInt res=notifier.Create();
+	test(res==KErrNone);
+	TRequestStatus stat;
+	res=notifier.Logon(stat);
+	test(res==KErrNone);
+	//initial pattern of stat is already tested by t_chnot
+
+	res=notifier.Logon(stat);
+	test(res==KErrNone);
+	test(stat==KRequestPending);
+	if (isrom == 0) 
+		{
+		test.Printf(_L("Change to RAM US Locale\n")); 	
+		res=UserSvr::ChangeLocale(ELOCLUS);
+		}
+	else
+		{
+		test.Printf(_L("Change to ROM US Locale\n")); 	
+		res=UserSvr::ChangeLocale(ELOCLUS_ROM);
+		}
+	test.Printf(_L("res=%d\n"),res);
+	test(res==KErrNone);
+	test(stat.Int() & EChangesLocale);
+	res=notifier.Logon(stat);
+	test(res==KErrNone);
+	test(stat==KRequestPending);
+	
+	locale.Refresh();
+	testUS(locale);
+}
+
+
+LOCAL_C void LocaleLanguageGet(SLocaleLanguage& locale)
+{
+	TPckg<SLocaleLanguage> localeLanguageBuf(locale);
+	TInt r = RProperty::Get(KUidSystemCategory, KLocaleLanguageKey, localeLanguageBuf);
+	test(r == KErrNone || r == KErrNotFound);
+}
+
+LOCAL_C TInt E32TestLocale(TInt isrom)
+{
+	TInt r;
+	TAny *LocaleAddr;
+	TLocale locale;
+	
+	/* Setup the US Locale DLL and ensure the Locale got modified (testUS) */
+	testChangeLocale(isrom);
+ 
+	/* Now get a pointer to some data in the DLL. This will be used to move a
+	** page from the dll 
+	*/
+	SLocaleLanguage localeLanguage;
+	LocaleLanguageGet(localeLanguage);
+	LocaleAddr = (TAny *) localeLanguage.iDateSuffixTable;
+	test(LocaleAddr != NULL);
+
+	RPageMove pagemove;
+	r=pagemove.Open();
+	test_KErrNone(r);
+
+	r=pagemove.TryMovingLocaleDll(LocaleAddr);
+	
+	if (isrom == 0) 
+		{
+		test_KErrNone(r);
+		}
+	else
+		{
+		// When the locale is in rom it is in the unpaged part of rom and 
+		// Epoc::LinearToPhysical() won't be able to find the address.
+		test_Equal(KErrArgument, r)
+		}
+
+	test.Printf(_L("Locale Test: Page move done\n"));
+
+	/* Test US again. The kernel should have cached the locale informaton, so this will not
+	 * really be testing the pagmove.
+	 */
+	locale.Refresh();
+	testUS(locale);
+	
+	/* Reload the Default Locale */
+	test.Printf(_L("Locale Test: Change to UK Default\n"));
+	r=UserSvr::ChangeLocale(ELOCL_DEFAULT);	
+	test(r==KErrNone);
+	locale.Refresh();
+	testUK(locale);	
+
+	/* This will ACTUALLY test the page which was moved by making the kernel reload the Locale
+	 * information from the DLL. 
+	 */
+	if (isrom == 0) 
+		{
+		test.Printf(_L("RAM Locale Test: Change to US Again\n"));
+		r=UserSvr::ChangeLocale(ELOCLUS);	
+		}
+	else
+		{
+		test.Printf(_L("ROM Locale Test: Change to US Again\n"));
+		r=UserSvr::ChangeLocale(ELOCLUS_ROM);	
+		}
+
+
+	test(r==KErrNone);
+	locale.Refresh();
+	testUS(locale);
+
+	/* Reset the Locale to the default */
+	r=UserSvr::ChangeLocale(ELOCL_DEFAULT);	
+	test(r==KErrNone);
+	locale.Refresh();
+	testUK(locale);	
+	return(KErrNone);
+}
+
+LOCAL_C void StartCodeModifierDriver()
+	{
+	test.Printf(_L("Start CodeModifier Driver\n"));
+	TInt r = User::LoadLogicalDevice(KCodeModifierName);
+	test( r==KErrNone || r==KErrAlreadyExists);
+	if((r = Device.Open())!=KErrNone)	
+		{
+		User::FreeLogicalDevice(KCodeModifierName);
+		test.Printf(_L("Could not open LDD"));
+		test(0);
+		}
+	}
+
+
+LOCAL_C void StopCodeModifierDriver()
+	{
+
+	test.Printf(_L("Stop Code Modifier Driver\n"));
+	test(KErrNone==Device.CloseCodeModifier());
+	Device.Close();
+	User::FreeLogicalDevice(KCodeModifierName);
+	}
+
+
+LOCAL_C void TestCodeSetupDrive(RThread &thread)
+{
+	/* The CodeModifier driver (look in ../debug/d_codemodifier) takes two threads, we just use the
+	** first one */
+	test(KErrNone==Device.ThreadId(0, thread.Id()));
+}
+
+
+LOCAL_C TUint GetCodeData(TInt *CodePtr, TInt& Ignore, TInt& FirstJump, TInt& SecondJump)
+	{ 
+	TUint ModAddr;
+
+	Ignore     = *CodePtr++;
+	ModAddr    = (TUint)CodePtr;
+	FirstJump  = *CodePtr++;
+	SecondJump = *CodePtr++;
+	return ModAddr;
+	}
+
+LOCAL_C TInt TestCodeModification(RPageMove &pagemove)
+	{
+	TInt Ignore; 
+	TUint ModAddr;
+	TInt FirstJump;
+	TInt SecondJump;
+	RThread thread;
+	
+	ModAddr = GetCodeData((TInt *)TestCodeModFunc, Ignore, FirstJump, SecondJump); 
+	
+	test.Printf(_L("User Test code Returns = %d\n"), TestCodeModFunc());
+	test.Printf(_L("Ignore = %x First Jump = %x Second = %x \n"), Ignore, FirstJump, SecondJump);
+	
+	TestCodeSetupDrive(thread);
+
+	for (TInt i=0; i<Repitions * 10; i++)
+		{
+		
+		TInt r=Device.WriteCode(0, ModAddr,SecondJump,sizeof(TInt));
+		test_KErrNone(r);
+		r = TestCodeModFunc();
+		test (2 == r);
+
+		test_KErrNone(pagemove.TryMovingUserPage((TAny*)TestCodeModFunc));
+
+		r = Device.RestoreCode(0, ModAddr);
+		test_KErrNone(r);
+		r = TestCodeModFunc();
+		test (1 == r);
+		
+		test_KErrNone(pagemove.TryMovingUserPage((TAny*)TestCodeModFunc));
+
+		}
+
+	test.Printf(_L("User Test code = %d\n"), TestCodeModFunc());
+	return KErrNone;
+	}
+
+LOCAL_C int TestCodeAsync(TAny *NotUsed)
+	{
+	TInt Ignore; 
+	TUint ModAddr;
+	TInt FirstJump;
+	TInt SecondJump;
+
+	ModAddr = GetCodeData((TInt *)TestCodeModFunc, Ignore, FirstJump, SecondJump); 
+
+	FOREVER
+		{
+		TInt r = Device.WriteCode(0, ModAddr,SecondJump,sizeof(TInt));
+		test_KErrNone(r);
+		
+		r = TestCodeModFunc();
+		test (2 == r);
+
+		r = Device.RestoreCode(0, ModAddr);
+	
+		test_KErrNone(r);
+		r = TestCodeModFunc();
+		test (1 == r);
+		User::AfterHighRes(10);
+		}
+	}
+
+/* 
+ * Creates a Thread that modifies its code in a tight loop while the main
+ * thread moves the functions page around
+ */
+LOCAL_C TInt TestCodeModificationAsync(RPageMove& pagemove)
+	{
+	TInt ret;
+	RThread CodeThread;
+	TRequestStatus s;
+
+	
+	/* Create the Thread to modify the code segment */
+	test_KErrNone(CodeThread.Create(_L("TestCodeAsync"), TestCodeAsync, KDefaultStackSize, NULL, NULL));
+	CodeThread.Logon(s);
+	CodeThread.SetPriority(EPriorityMore);
+	CodeThread.Resume();
+
+	TestCodeSetupDrive(CodeThread);
+
+	/* Loop trying to move the code page while the thread (CodeThread) modifies it */
+	for (TInt i=0; i<Repitions; i++)
+		{
+		test_KErrNone(pagemove.TryMovingUserPage((TAny*)TestCodeModFunc));
+		}
+
+	CodeThread.Kill(KErrNone);
+	User::WaitForRequest(s);
+	test_Equal(EExitKill, CodeThread.ExitType());
+	test_KErrNone(CodeThread.ExitReason());
+	CodeThread.Close();
+
+	ret = TestCodeModFunc();
+	test(ret == 1 || ret == 2);
+
+	return KErrNone;
+	}