kerneltest/e32test/defrag/t_pagemove.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 4 56f325a607ea
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// 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;
	}