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