diff -r 000000000000 -r bde4ae8d615e os/kernelhwsrv/kerneltest/e32test/defrag/t_pagemove.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/kernelhwsrv/kerneltest/e32test/defrag/t_pagemove.cpp Fri Jun 15 03:10:57 2012 +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 +#include +#include +#include +#include +#include +#include +#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 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 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; iBase(); + 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 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; iiPageableRomStart); + 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 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