os/kernelhwsrv/kerneltest/e32test/defrag/t_pagemove.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of the License "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // e32test\defrag\t_pagemove.cpp
    15 
    16 //
    17 //--------------------------------------------------------------------------------------------------
    18 //! @SYMTestCaseID			KBASE-T_PAGEMOVE-0572
    19 //! @SYMTestType			UT
    20 //! @SYMPREQ				PREQ308
    21 //! @SYMTestCaseDesc		Test physical page moving
    22 //!							t_pagemove loads and opens the logical device driver ("D_PAGEMOVE.LDD"). 
    23 //!							Following this, it requests that the driver attempt to move  
    24 //!							various kinds of pages directly. 
    25 //!
    26 //!							API Information:
    27 //!								RBusLogicalChannel
    28 //!
    29 //!							Platforms/Drives/Compatibility:
    30 //!								Hardware only. No defrag support on emulator. 
    31 //!
    32 //! @SYMTestActions			1  -  Move regular local data pages
    33 //! 						2  -  Move regular global data pages
    34 //! 						3  -  Move DLL writable static data pages
    35 //! 						4  -  Move user self-modifying code chunk pages
    36 //! 						5  -  Move RAM drive pages
    37 //!							6  -  Move kernel heap pages (*********DISABLED************)
    38 //! 						7  -  Move kernel stack pages
    39 //! 						8  -  Move kernel code pages
    40 //! 						9  -  Move regular code pages
    41 //! 						10 -  Move code whilst the page is being modified
    42 //! 						11 -  Move code (async) whilst the page is being modified
    43 //! 						12 -  Move ROM locale DLL pages
    44 //! 						13 -  Move RAM locale DLL pages
    45 //! 						14 -  Moving pages whilst they are being virtually pinned and unpinned.
    46 //! 						15 -  Moving pages whilst they are being physically pinned and unpinned.
    47 //! @SYMTestExpectedResults All tests should pass.
    48 //! @SYMTestPriority        High
    49 //! @SYMTestStatus          Implemented
    50 //--------------------------------------------------------------------------------------------------
    51 //
    52 #define __E32TEST_EXTENSION__
    53 #include <e32test.h>
    54 #include <e32math.h>
    55 #include <e32uid.h>
    56 #include <e32hal.h>
    57 #include <e32std.h>
    58 #include <e32std_private.h>
    59 #include <dptest.h>
    60 #include "d_pagemove.h"
    61 #include "t_pagemove_dll.h"
    62 #include "t_pmwsd.h"
    63 #include "..\mmu\mmudetect.h"
    64 #include "..\debug\d_codemodifier.h"
    65 #include "..\mmu\d_memorytest.h"
    66 
    67 //#define _DEBUG_MSG
    68 #ifdef _DEBUG_MSG
    69 #define _R_PRINTF(x) 	RDebug::Printf(x)
    70 #define _T_PRINTF(x)	test.Printf(x)
    71 #else
    72 #define _R_PRINTF(x)
    73 #define _T_PRINTF(x)
    74 #endif
    75 
    76 LOCAL_D RTest test(_L("T_PAGEMOVE"));
    77 
    78 _LIT(ELOCL_DEFAULT, "");
    79 _LIT(ELOCLUS, "T_LOCLUS_RAM");
    80 _LIT(ELOCLUS_ROM, "T_LOCLUS");
    81 LOCAL_C TInt E32TestLocale(TInt);
    82 
    83 RCodeModifierDevice Device;
    84 extern TInt TestCodeModFunc();
    85 
    86 extern TInt Increment(TInt);
    87 extern TUint Increment_Length();
    88 extern TInt Decrement(TInt);
    89 extern TUint Decrement_Length();
    90 typedef TInt (*PFI)(TInt);
    91 
    92 LOCAL_C void StartCodeModifierDriver();
    93 LOCAL_C void StopCodeModifierDriver();
    94 LOCAL_C TInt TestCodeModification(RPageMove &);
    95 LOCAL_C TInt TestCodeModificationAsync(RPageMove& pagemove);
    96 
    97 
    98 const TPtrC KLddFileName=_L("D_PAGEMOVE.LDD");
    99 TInt Repitions=4000;
   100 
   101 TInt PageSize;
   102 TUint NumberOfCpus;
   103 
   104 volatile TBool ThreadDie;
   105 
   106 TBool gDataPagingSupported;
   107 TBool gRomPagingSupported;
   108 TBool gCodePagingSupported;
   109 TBool gPinningSupported;
   110 
   111 // This executable is ram loaded (see mmp file) so this function will do fine
   112 // as a test of RAM-loaded code.
   113 TInt RamLoadedFunction()
   114 	{
   115 	return KArbitraryNumber;
   116 	}
   117 
   118 struct SPinThreadArgs
   119 	{
   120 	TLinAddr iLinAddr;
   121 	TTestFunction iTestFunc;
   122 	RThread iParentThread;
   123 	User::TRealtimeState iRealtimeState;
   124 	};
   125 
   126 
   127 void StartThreads(	TUint aNumThreads, RThread* aThreads, TRequestStatus* aStatus, 
   128 					TThreadFunction aThreadFunc, SPinThreadArgs& aThreadArgs)
   129 	{
   130 	for (TUint i = 0; i < aNumThreads; i++)
   131 		{
   132 		test_KErrNone(aThreads[i].Create(KNullDesC, aThreadFunc, KDefaultStackSize, NULL, &aThreadArgs));
   133 		aThreads[i].Logon(aStatus[i]);
   134 		TRequestStatus threadInitialised;
   135 		aThreads[i].Rendezvous(threadInitialised);
   136 		aThreads[i].Resume();
   137 		_T_PRINTF(_L("wait for child\n"));
   138 		User::WaitForRequest(threadInitialised);
   139 		test_KErrNone(threadInitialised.Int());
   140 		}
   141 	}
   142 
   143 void EndThreads(TUint aNumThreads, RThread* aThreads, TRequestStatus* aStatus)
   144 	{
   145 	for (TUint i = 0; i < aNumThreads; i++)
   146 		{
   147 		User::WaitForRequest(aStatus[i]);
   148 		test_Equal(EExitKill, aThreads[i].ExitType());
   149 		test_KErrNone(aThreads[i].ExitReason());
   150 		aThreads[i].Close();
   151 		}
   152 	}
   153 
   154 
   155 void Reschedule(TInt64& aSeed)
   156 	{
   157 	if (NumberOfCpus == 1)
   158 		{
   159 		TInt rand = Math::Rand(aSeed);
   160 		if ((rand & 0x5) == 5)
   161 			User::AfterHighRes(rand & 0x7);
   162 		}
   163 	}
   164 
   165 TInt ReadWriteByte(TAny* aParam)
   166 	{
   167 	SPinThreadArgs* args = (SPinThreadArgs*)aParam;
   168 	volatile TUint8* byte = (volatile TUint8*)args->iLinAddr;
   169 	TInt64 seed = Math::Random()*Math::Random();
   170 
   171 	test_KErrNone(User::SetRealtimeState(args->iRealtimeState));
   172 
   173 	// Ensure the the parentThread has moved the page at least once
   174 	// before we start accessing it.
   175 	TRequestStatus status;
   176 	args->iParentThread.Rendezvous(status);
   177 	RThread::Rendezvous(KErrNone);
   178 	_R_PRINTF("wait for parent");
   179 	User::WaitForRequest(status);
   180 	_R_PRINTF("acesssing page");
   181 
   182 	FOREVER
   183 		{
   184 		*byte = *byte;
   185 		Reschedule(seed);
   186 		if (ThreadDie)
   187 			break;
   188 		}
   189 	return KErrNone;
   190 	}
   191 
   192 
   193 TInt RunCodeThread(TAny* aParam)
   194 	{
   195 	TInt64 seed = Math::Random()*Math::Random();
   196 	SPinThreadArgs* args = (SPinThreadArgs*)aParam;
   197 
   198 	test_KErrNone(User::SetRealtimeState(args->iRealtimeState));
   199 
   200 	// Ensure the the parentThread has moved the page at least once
   201 	// before we start accessing it.
   202 	TRequestStatus status;
   203 	args->iParentThread.Rendezvous(status);
   204 	RThread::Rendezvous(KErrNone);
   205 	_R_PRINTF("wait for parent");
   206 	User::WaitForRequest(status);
   207 	_R_PRINTF("acesssing page");
   208 
   209 	FOREVER
   210 		{
   211 		TInt r = args->iTestFunc();
   212 		if (r != KArbitraryNumber)
   213 			return KErrGeneral;
   214 		Reschedule(seed);
   215 		if (ThreadDie)
   216 			break;
   217 		}
   218 	return KErrNone;
   219 	}
   220 
   221 
   222 TInt VirtualPinPage(TAny* aParam)
   223 	{
   224 	TInt64 seed = Math::Random()*Math::Random();
   225 	SPinThreadArgs* args = (SPinThreadArgs*)aParam;
   226 	RMemoryTestLdd ldd;
   227 	test_KErrNone(ldd.Open());
   228 
   229 	test_KErrNone(ldd.CreateVirtualPinObject());
   230 
   231 	TBool firstRun = ETrue;
   232 	FOREVER
   233 		{
   234 		// Pin the page of aParam.
   235 		test_KErrNone(ldd.PinVirtualMemory(args->iLinAddr, PageSize));
   236 		if (firstRun)
   237 			{// On the first run ensure that the page is definitely pinned when
   238 			// the parent thread first attempts to move it.
   239 			TRequestStatus status;
   240 			args->iParentThread.Rendezvous(status);
   241 			RThread::Rendezvous(KErrNone);
   242 			User::WaitForRequest(status);
   243 			test_KErrNone(status.Int());
   244 			firstRun = EFalse;
   245 			}
   246 		Reschedule(seed);
   247 		test_KErrNone(ldd.UnpinVirtualMemory());
   248 		if (ThreadDie)
   249 			break;
   250 		}
   251 	test_KErrNone(ldd.DestroyVirtualPinObject());
   252 	ldd.Close();
   253 	return KErrNone;
   254 	}
   255 
   256 
   257 TInt PhysicalPinPage(TAny* aParam)
   258 	{
   259 	TInt64 seed = Math::Random()*Math::Random();
   260 	SPinThreadArgs* args = (SPinThreadArgs*)aParam;
   261 
   262 	RMemoryTestLdd ldd;
   263 	test_KErrNone(ldd.Open());
   264 
   265 	test_KErrNone(ldd.CreatePhysicalPinObject());
   266 
   267 	TBool firstRun = ETrue;
   268 	FOREVER
   269 		{
   270 		// Pin the page of aParam, use a read only pinning so that pinning code 
   271 		// doesn't return KErrAccessDenied as writable mappings not allowed on code.
   272 		test_KErrNone(ldd.PinPhysicalMemoryRO(args->iLinAddr, PageSize));
   273 		if (firstRun)
   274 			{// On the first run ensure that the page is definitely pinned when
   275 			// the parent thread first attempts to move it.
   276 			TRequestStatus status;
   277 			args->iParentThread.Rendezvous(status);
   278 			RThread::Rendezvous(KErrNone);
   279 			User::WaitForRequest(status);
   280 			test_KErrNone(status.Int());
   281 			firstRun = EFalse;
   282 			}
   283 		Reschedule(seed);
   284 		test_KErrNone(ldd.UnpinPhysicalMemory());
   285 		if (ThreadDie)
   286 			break;
   287 		}
   288 	test_KErrNone(ldd.DestroyPhysicalPinObject());
   289 	ldd.Close();
   290 	return KErrNone;
   291 	}
   292 
   293 TInt ModifyCodeThread(TAny* aParam)
   294 	{
   295 	SPinThreadArgs* args = (SPinThreadArgs*)aParam;
   296 	TUint8* p = (TUint8*)args->iLinAddr;
   297 	PFI func = (PFI)p;
   298 
   299 	// Ensure the the parentThread has moved the page at least once
   300 	// before we start accessing it.
   301 	TRequestStatus status;
   302 	args->iParentThread.Rendezvous(status);
   303 	RThread::Rendezvous(KErrNone);
   304 	_R_PRINTF("wait for parent");
   305 	User::WaitForRequest(status);
   306 	_R_PRINTF("modifiying page");
   307 
   308 	while (!ThreadDie)
   309 		{
   310 		Mem::Copy(p, (TAny*)&Increment, Increment_Length());
   311 		User::IMB_Range(p, p+Increment_Length());
   312 		test_Equal(8, func(7));
   313 
   314 		Mem::Copy(p, (TAny*)&Decrement, Decrement_Length());
   315 		User::IMB_Range(p, p+Decrement_Length());
   316 		test_Equal(6, func(7));
   317 		}
   318 	return KErrNone;
   319 	}
   320 
   321 
   322 enum TMovingPinStage
   323 	{
   324 	ENoPinning,
   325 	EVirtualPinning,
   326 	EPhysicalPinning,
   327 	EMovingPinStages,
   328 	};
   329 
   330 void TestUserData(RPageMove& pagemove, TUint8* array, TInt size, TBool aPagedData=EFalse)
   331 	{
   332 	_T_PRINTF(_L("Fill the array with some data\n"));
   333 	for (TInt i=0; i<size; i++) array[i] = i*i;
   334 
   335 	TUint8* firstpage = (TUint8*)_ALIGN_DOWN((TLinAddr)array, PageSize);
   336 	RThread thread;
   337 	thread.Open(RThread().Id());
   338 	SPinThreadArgs threadArgs;
   339 	threadArgs.iLinAddr = (TLinAddr)array;
   340 	threadArgs.iParentThread = thread;
   341 	threadArgs.iRealtimeState = User::ERealtimeStateOff;
   342 
   343 	TMovingPinStage endStage = EMovingPinStages;
   344 	if (!gPinningSupported)
   345 		endStage = EVirtualPinning;
   346 
   347 	for (TUint state = ENoPinning; state < (TUint)endStage; state++)
   348 		{
   349 		TThreadFunction threadFunc = NULL;
   350 		switch (state)
   351 			{
   352 			case ENoPinning:
   353 				test.Printf(_L("Attempt to move pages while they are being modified\n"));
   354 				threadFunc = &ReadWriteByte;
   355 				break;
   356 			case EVirtualPinning:
   357 				test.Printf(_L("Attempt to move pages while they are being virtually pinned\n"));
   358 				threadFunc = &VirtualPinPage;
   359 				break;
   360 			case EPhysicalPinning:
   361 				test.Printf(_L("Attempt to move pages while they are being physically pinned\n"));
   362 				threadFunc = &PhysicalPinPage;
   363 				break;
   364 			}
   365 		ThreadDie = EFalse;
   366 		TUint numThreads = (NumberOfCpus > 1) ? NumberOfCpus - 1 : 1;
   367 		RThread* userDataThread = new RThread[numThreads];
   368 		TRequestStatus* s = new TRequestStatus[numThreads];
   369 		StartThreads(numThreads, userDataThread, s, threadFunc, threadArgs);
   370 
   371 		_T_PRINTF(_L("Move first array page repeatedly\n"));
   372 		TBool success=EFalse;
   373 		TUint inuse = 0;
   374 		*(volatile TUint8*)array = *array;	// Ensure the page of the first entry is paged in for the first move.
   375 		for (TInt i=0; i < Repitions*2; i++)
   376 			{
   377 			TInt r = pagemove.TryMovingUserPage(firstpage, ETrue);
   378 			if (i == 0)
   379 				{// If this is the first run allow the pinning threads to 
   380 				// unpin the memory now that we've definitely done at least 
   381 				// one page move with the page pinned.
   382 				_T_PRINTF(_L("signal to child\n"));
   383 				RThread::Rendezvous(KErrNone);
   384 				}
   385 			switch (r)
   386 				{
   387 				case KErrInUse:
   388 					inuse++;
   389 					break;
   390 				case KErrArgument:
   391 					// The page was paged out, this should only happen for paged data.
   392 					test(aPagedData);
   393 					break;
   394 				default:
   395 					test_KErrNone(r);
   396 					success=ETrue;
   397 					break;
   398 				}
   399 			}
   400 		// Can't guarantee that for paged data the page and its page tables will
   401 		// be paged in, in most cases it will be at least once.
   402 		// Pinning the page should always return KErrInUse except for virtually 
   403 		// pinned non-paged memory as virtual pinning is a nop for unpaged memory.
   404 		test.Printf(_L("inuse test removed; inuse %d\n"),inuse);
   405 		//test(inuse || aPagedData || state == EVirtualPinning);
   406 		test(success || state == EPhysicalPinning);
   407 
   408 		ThreadDie = ETrue;
   409 		EndThreads(numThreads, userDataThread, s);
   410 
   411 		_T_PRINTF(_L("Validate page data\n"));
   412 		for (TInt i=0; i<size; i++)
   413 			test_Equal((TUint8)(i*i), array[i]);
   414 		}
   415 	thread.Close();
   416 	}
   417 
   418 
   419 void TestMovingCode(RPageMove& aPagemove, TTestFunction aFunc, TBool aPaged=EFalse)
   420 	{
   421 	TUint8* firstpage = (TUint8*)_ALIGN_DOWN((TLinAddr)aFunc, PageSize);
   422 	RThread thread;
   423 	thread.Open(RThread().Id());
   424 	SPinThreadArgs threadArgs;
   425 	threadArgs.iLinAddr = (TLinAddr)firstpage;
   426 	threadArgs.iTestFunc = aFunc;
   427 	threadArgs.iParentThread = thread;
   428 	threadArgs.iRealtimeState = User::ERealtimeStateOff;
   429 
   430 	TMovingPinStage endStage = EMovingPinStages;
   431 	if (!gPinningSupported)
   432 		endStage = EVirtualPinning;
   433 
   434 	for (TUint state = ENoPinning; state < (TUint)endStage; state++)
   435 		{
   436 		TThreadFunction threadFunc = NULL;
   437 		switch (state)
   438 			{
   439 			case ENoPinning:
   440 				test.Printf(_L("Attempt to move pages while they are being executed\n"));
   441 				threadFunc = &RunCodeThread;
   442 				test_Equal(KArbitraryNumber, aFunc()); // Ensure the page is paged in.
   443 				break;
   444 			case EVirtualPinning:
   445 				test.Printf(_L("Attempt to move pages while they are being virtually pinned\n"));
   446 				threadFunc = &VirtualPinPage;
   447 				break;
   448 			case EPhysicalPinning:
   449 				test.Printf(_L("Attempt to move pages while they are being physically pinned\n"));
   450 				threadFunc = &PhysicalPinPage;
   451 				break;
   452 			}
   453 		ThreadDie = EFalse;
   454 		TUint numThreads = (NumberOfCpus > 1) ? NumberOfCpus - 1 : 1;
   455 		RThread* codeRunThread = new RThread[numThreads];
   456 		TRequestStatus* s = new TRequestStatus[numThreads];
   457 		StartThreads(numThreads, codeRunThread, s, threadFunc, threadArgs);
   458 
   459 		_T_PRINTF(_L("Move first code page repeatedly\n"));
   460 		test_Equal(KArbitraryNumber, aFunc());	
   461 		TBool inuse=EFalse, success=EFalse;
   462 		for (TInt i=0; i < Repitions; i++)
   463 			{
   464 			TInt r = aPagemove.TryMovingUserPage(firstpage, ETrue);
   465 			if (i == 0)
   466 				{// If this is the first run allow the pinning threads to 
   467 				// unpin the memory now that we've definitely done at least 
   468 				// one page move with the page pinned.
   469 				_T_PRINTF(_L("signal to child\n"));
   470 				RThread::Rendezvous(KErrNone);
   471 				}
   472 			switch (r)
   473 				{
   474 				case KErrInUse:
   475 					inuse=ETrue;
   476 					break;
   477 				case KErrArgument:
   478 					// The page was paged out, this should only happen for paged code.
   479 					test(aPaged);
   480 					break;
   481 				default:
   482 					test_KErrNone(r);
   483 					success=ETrue;
   484 					break;
   485 				}
   486 			}
   487 		// Physical pinning or adding a new pinning while a page is being moved
   488 		// should prevent code pages being moved.
   489 		switch (state)
   490 		{
   491 			case ENoPinning :			
   492 				test(!inuse || aPaged);	// Stealing may get KErrInUse but this should only happen for paged code.
   493 			case EVirtualPinning :
   494 				test(success);
   495 				break;
   496 			case EPhysicalPinning :
   497 				break;
   498 		}
   499 
   500 		ThreadDie = ETrue;
   501 		EndThreads(numThreads, codeRunThread, s);
   502 
   503 		_T_PRINTF(_L("Validate page data\n"));
   504 		test_Equal(KArbitraryNumber, aFunc());		
   505 		}
   506 	thread.Close();
   507 	}
   508 
   509 
   510 void TestMovingRealtime(RPageMove& aPagemove, TUint8* aArray, TInt aSize, TTestFunction aFunc, TBool aCode, TBool aPaged=EFalse)
   511 	{
   512 	TThreadFunction threadFunc;
   513 	TLinAddr pageAddr;
   514 	RThread thread;
   515 	TUint8* firstpage;
   516 	thread.Open(RThread().Id());
   517 	SPinThreadArgs threadArgs;
   518 	threadArgs.iParentThread = thread;
   519 	if (aCode)
   520 		{
   521 		pageAddr = (TLinAddr)aFunc;
   522 		firstpage = (TUint8*)_ALIGN_DOWN(pageAddr, PageSize);
   523 		threadArgs.iLinAddr = (TLinAddr)firstpage;
   524 		threadFunc = RunCodeThread;
   525 		threadArgs.iTestFunc = aFunc;
   526 		test_Equal(KArbitraryNumber, aFunc());
   527 		}
   528 	else
   529 		{
   530 		pageAddr = (TLinAddr)aArray;
   531 		firstpage = (TUint8*)_ALIGN_DOWN(pageAddr, PageSize);
   532 		threadArgs.iLinAddr = (TLinAddr)aArray;
   533 		threadFunc = ReadWriteByte;
   534 		_T_PRINTF(_L("Fill the array with some data\n"));
   535 		for (TInt i=0; i<aSize; i++) aArray[i] = i*i;
   536 		}
   537 
   538 	RMemoryTestLdd ldd;
   539 
   540 	TMovingPinStage endStage = EMovingPinStages;
   541 	if (gPinningSupported)
   542 		{
   543 		test_KErrNone(ldd.Open());
   544 		test_KErrNone(ldd.CreateVirtualPinObject());
   545 		test_KErrNone(ldd.CreatePhysicalPinObject());
   546 		}
   547 	else
   548 		endStage = EVirtualPinning;
   549 
   550 	for (TUint state = ENoPinning; state < (TUint)endStage; state++)
   551 		{
   552 		switch (state)
   553 			{
   554 			case ENoPinning:
   555 				test.Printf(_L("Attempt to move pages while they are being accessed\n"));
   556 				break;
   557 			case EVirtualPinning:
   558 				test.Printf(_L("Attempt to move pages while they are virtually pinned\n"));
   559 				test_KErrNone(ldd.PinVirtualMemory((TLinAddr)firstpage, PageSize));
   560 
   561 				break;
   562 			case EPhysicalPinning:
   563 				test.Printf(_L("Attempt to move pages while they are physically pinned\n"));
   564 				test_KErrNone(ldd.PinPhysicalMemoryRO((TLinAddr)firstpage, PageSize));
   565 				break;
   566 			}
   567 		for (	TUint realtimeState = User::ERealtimeStateOff; 
   568 				realtimeState <= User::ERealtimeStateWarn; 
   569 				realtimeState++)
   570 			{
   571 			ThreadDie = EFalse;
   572 			RThread accessThread;
   573 			TRequestStatus s;
   574 			threadArgs.iRealtimeState = (User::TRealtimeState)realtimeState;
   575 			test_KErrNone(accessThread.Create(_L("Realtime Thread"), threadFunc, KDefaultStackSize, NULL, &threadArgs));
   576 			accessThread.Logon(s);
   577 			TRequestStatus threadInitialised;
   578 			accessThread.Rendezvous(threadInitialised);
   579 			accessThread.Resume();
   580 
   581 			_T_PRINTF(_L("wait for child\n"));
   582 			User::WaitForRequest(threadInitialised);
   583 			test_KErrNone(threadInitialised.Int());
   584 
   585 			_T_PRINTF(_L("Move page repeatedly\n"));
   586 			TBool success=EFalse, pagedOut=EFalse;
   587 			TUint inuse=0;
   588 			if (aCode)
   589 				{
   590 				test_Equal(KArbitraryNumber, aFunc());
   591 				}
   592 			else
   593 				{
   594 				*(volatile TUint8*)aArray = *aArray;
   595 				}
   596 
   597 			for (TInt i=0; i < Repitions; i++)
   598 				{
   599 				TInt r = aPagemove.TryMovingUserPage(firstpage, ETrue);
   600 				if (i == 0)
   601 					{
   602 					_T_PRINTF(_L("signal to child\n"));
   603 					RThread::Rendezvous(KErrNone);
   604 					}
   605 				switch (r)
   606 					{
   607 					case KErrInUse:
   608 						inuse++;
   609 						break;
   610 					case KErrArgument:
   611 						// The page was paged out, this should only happen for paged code.
   612 						test(aPaged);
   613 						pagedOut = ETrue;
   614 						break;
   615 					default:
   616 						test_KErrNone(r);
   617 						success=ETrue;
   618 						break;
   619 					}
   620 				}
   621 			ThreadDie = ETrue;
   622 			User::WaitForRequest(s);
   623 			test.Printf(_L("inuse %d\n"),inuse);
   624 			switch (state)
   625 				{
   626 				case ENoPinning :
   627 					test(success);
   628 					if (EExitPanic == accessThread.ExitType())
   629 						{
   630 						test(accessThread.ExitCategory()==_L("KERN-EXEC"));
   631 						test_Equal(EIllegalFunctionForRealtimeThread, accessThread.ExitReason());
   632 						test(aPaged && realtimeState == User::ERealtimeStateOn);
   633 						}
   634 					else
   635 						{
   636 						test_Equal(EExitKill,accessThread.ExitType());
   637 						test_KErrNone(accessThread.ExitReason());
   638 						}
   639 					// Ensure the page is paged in before we attempt to move it again with a different realtime state.
   640 					if (aCode)
   641 						{
   642 						test_Equal(KArbitraryNumber, aFunc());
   643 						}
   644 					else
   645 						{
   646 						*(volatile TUint8*)aArray = *aArray;
   647 						}
   648 					break;				
   649 				case EVirtualPinning :
   650 					test(!aCode || !inuse);
   651 					test(success);
   652 					test(!pagedOut);
   653 					test_Equal(EExitKill,accessThread.ExitType());
   654 					test_KErrNone(accessThread.ExitReason());
   655 					break;
   656 				case EPhysicalPinning :
   657 					test(!success);
   658 					break;
   659 				}
   660 			accessThread.Close();
   661 			}
   662 		if (gPinningSupported)
   663 			{
   664 			// Unpin any pinned memory.
   665 			test_KErrNone(ldd.UnpinVirtualMemory());
   666 			test_KErrNone(ldd.UnpinPhysicalMemory());
   667 			}
   668 
   669 		_T_PRINTF(_L("Validate page data\n"));
   670 		if (aCode)
   671 			{
   672 			test_Equal(KArbitraryNumber, aFunc());
   673 			}
   674 		else
   675 			{
   676 			for (TInt i=0; i<aSize; i++)
   677 				test_Equal((TUint8)(i*i), aArray[i]);
   678 			}
   679 			
   680 		}
   681 	if (gPinningSupported)
   682 		{
   683 		test_KErrNone(ldd.DestroyVirtualPinObject());
   684 		test_KErrNone(ldd.DestroyPhysicalPinObject());
   685 		ldd.Close();
   686 		}
   687 	thread.Close();
   688 	}
   689 
   690 // Only commits and decommits the first page as that is the only page that is being moved.
   691 // Plus this ensures the page table and page directories of the chunk are always allocated
   692 // and therefore prevents Epoc::LinearToPhysical() from crashing the system.
   693 TInt CommitDecommit(TAny* aParam)
   694 	{
   695 	RChunk* chunk = (RChunk*) aParam;
   696 	volatile TUint8* byte = chunk->Base();
   697 	FOREVER
   698 		{
   699 		*byte = *byte;
   700 		User::AfterHighRes(0);
   701 		TInt r = chunk->Decommit(0, PageSize);
   702 		if (r != KErrNone)
   703 			return r;
   704 		User::AfterHighRes(0);
   705 		r = chunk->Commit(0, PageSize);
   706 		if (r != KErrNone)
   707 			return r;
   708 		}
   709 	}
   710 
   711 void TestCommitDecommit(RPageMove& pagemove, RChunk& aChunk)
   712 	{
   713 	test.Printf(_L("Attempt to move a page while it is being committed and decommited\n"));
   714 	RThread thread;
   715 	TRequestStatus s;
   716 	test_KErrNone(thread.Create(_L("CommitDecommit"), &CommitDecommit, KDefaultStackSize, NULL, (TAny*)&aChunk));
   717 	thread.Logon(s);
   718 	thread.SetPriority(EPriorityMore);
   719 	thread.Resume();
   720 
   721 	TUint8* firstpage=(TUint8*)_ALIGN_DOWN((TLinAddr)aChunk.Base(), PageSize);
   722 	for (TInt i=0; i < Repitions; i++)
   723 		{
   724 		TInt r = pagemove.TryMovingUserPage(firstpage, ETrue);
   725 		// Allow all valid return codes as we are only testing that this doesn't 
   726 		// crash the kernel and the page could be commited, paged out or decommited
   727 		// at any one time.
   728 		test_Value(r, r <= KErrNone);
   729 		}
   730 
   731 	thread.Kill(KErrNone);
   732 	User::WaitForRequest(s);
   733 	test_Equal(EExitKill,thread.ExitType());
   734 	test_KErrNone(thread.ExitReason());
   735 	thread.Close();
   736 	}
   737 
   738 
   739 void TestPageTableDiscard(RPageMove& pagemove, TUint8* array, TUint size)
   740 	{
   741 	_T_PRINTF(_L("Fill the array with some data\n"));
   742 	for (TUint i=0; i<size; i++) array[i] = i*i;
   743 
   744 	TUint8* firstpage = (TUint8*)_ALIGN_DOWN((TLinAddr)array, PageSize);
   745 	RThread thread;
   746 	thread.Open(RThread().Id());
   747 	SPinThreadArgs threadArgs;
   748 	threadArgs.iLinAddr = (TLinAddr)array;
   749 	threadArgs.iParentThread = thread;
   750 	threadArgs.iRealtimeState = User::ERealtimeStateOff;
   751 
   752 	TMovingPinStage endStage = EMovingPinStages;
   753 	if (!gPinningSupported)
   754 		endStage = EVirtualPinning;
   755 	
   756 	for (TUint pageTableInfo = 0; pageTableInfo < 2; pageTableInfo++)
   757 		{
   758 		for (TUint state = ENoPinning; state < (TUint)endStage; state++)
   759 			{
   760 			TThreadFunction threadFunc = NULL;
   761 			if (!pageTableInfo)
   762 			{
   763 			switch (state)
   764 				{
   765 				case ENoPinning:
   766 					test.Printf(_L("Attempt to move page tables whilst the pages they map are being modified\n"));
   767 					threadFunc = &ReadWriteByte;
   768 					break;
   769 				case EVirtualPinning:
   770 					test.Printf(_L("Attempt to move page tables whilst the pages they map are being virtually pinned\n"));
   771 					threadFunc = &VirtualPinPage;
   772 					break;
   773 				case EPhysicalPinning:
   774 					test.Printf(_L("Attempt to move page tables whilst the pages they map are being physically pinned\n"));
   775 					threadFunc = &PhysicalPinPage;
   776 					break;
   777 				}
   778 			}
   779 			else
   780 			{
   781 			switch (state)
   782 				{
   783 				case ENoPinning:
   784 					test.Printf(_L("Attempt to move page table infos whilst pages they refer to are being modified\n"));
   785 					threadFunc = &ReadWriteByte;
   786 					break;
   787 				case EVirtualPinning:
   788 					test.Printf(_L("Attempt to move page table infos whilst pages they refer to are being virtually pinned\n"));
   789 					threadFunc = &VirtualPinPage;
   790 					break;
   791 				case EPhysicalPinning:
   792 					test.Printf(_L("Attempt to move page table infos whilst pages they refer to are being physically pinned\n"));
   793 					threadFunc = &PhysicalPinPage;
   794 					break;
   795 				}
   796 			}
   797 			ThreadDie = EFalse;
   798 			TUint numThreads = (NumberOfCpus > 1) ? NumberOfCpus - 1 : 1;
   799 			RThread* threads = new RThread[numThreads];
   800 			TRequestStatus* s = new TRequestStatus[numThreads];
   801 			StartThreads(numThreads, threads, s, threadFunc, threadArgs);
   802 
   803 			_T_PRINTF(_L("Move first array page repeatedly\n"));
   804 			TUint inuse = 0;
   805 			for (TInt i=0; i < Repitions; i++)
   806 				{
   807 				TInt r;
   808 				if (!pageTableInfo)
   809 					r = pagemove.TryMovingPageTable(firstpage);
   810 				else
   811 					r = pagemove.TryMovingPageTableInfo(firstpage);					
   812 				if (i == 0)
   813 					{// If this is the first run allow the pinning threads to 
   814 					// unpin the memory now that we've definitely done at least 
   815 					// one page move with the page pinned.
   816 					_T_PRINTF(_L("signal to child\n"));
   817 					RThread::Rendezvous(KErrNone);
   818 					}
   819 				switch (r)
   820 					{
   821 					case KErrInUse:
   822 						inuse++;
   823 						break;
   824 					case KErrNotFound:
   825 						// The page table or page table info page was paged out.
   826 						break;
   827 					default:
   828 						test_KErrNone(r);
   829 						break;
   830 					}
   831 				}
   832 			test.Printf(_L("inuse %d\n"),inuse);
   833 			// A virtually pinned page should always return KErrInUse at least once.
   834 			test(state != EVirtualPinning || inuse);
   835 
   836 			ThreadDie = ETrue;
   837 			EndThreads(numThreads, threads, s);
   838 
   839 			_T_PRINTF(_L("Validate page data\n"));
   840 			for (TUint i=0; i<size; i++)
   841 				test_Equal((TUint8)(i*i), array[i]);
   842 			}
   843 		}
   844 	thread.Close();
   845 	}
   846 
   847 // Basic testing of moving rom pages.
   848 void TestMovingRom(RPageMove& aPageMove)
   849 	{
   850 	TUint8* pPage=(TUint8*)User::Alloc(PageSize);
   851 	test(pPage!=NULL);
   852 
   853 	TUint romHdr = UserSvr::RomHeaderAddress();
   854 
   855 	if (gPinningSupported)
   856 		{
   857 		// Pin an unpaged rom page to get the physical address of the rom page.
   858 		// Pinning unpaged rom actually does nothing except return the physical 
   859 		// address of the page.
   860 		RMemoryTestLdd ldd;
   861 		test_KErrNone(ldd.Open());
   862 		test_KErrNone(ldd.CreatePhysicalPinObject());
   863 
   864 		// Save contents of rom page.
   865 		Mem::Move(pPage,(TAny*)romHdr,PageSize);
   866 
   867 		test_KErrNone(ldd.PinPhysicalMemoryRO(romHdr, PageSize));
   868 		test_KErrNone(ldd.UnpinPhysicalMemory());
   869 
   870 		// Now move the page, d_memorytest saves the address of the pinned page
   871 		// depsite it being unpinned.
   872 		// Will get KErrArgument as rom pages don't have an SPageInfo so memory
   873 		// model doesn't treat rom as though they are in ram, which in most cases 
   874 		// they are.
   875 		test_Equal(KErrArgument, ldd.MovePinnedPhysicalMemory(0));
   876 
   877 		test_KErrNone(Mem::Compare((TUint8*)romHdr,PageSize,pPage,PageSize));
   878 		test_KErrNone(ldd.DestroyPhysicalPinObject());
   879 		ldd.Close();
   880 		}
   881 
   882 	if (gRomPagingSupported)
   883 		{
   884 		// Use paged part of rom for testing
   885 		TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
   886 		test(romHeader->iPageableRomStart);
   887 		TUint romAddr = (TUint)((TUint8*)romHeader + romHeader->iPageableRomStart + 64 * PageSize);
   888 
   889 		// We will use the 64th pagable rom page so check that it exists.
   890 		test(romHeader->iPageableRomSize >= 65 * PageSize);
   891 	
   892 		// Page in the rom page and save it contents.
   893 		Mem::Move(pPage,(TAny*)romAddr,PageSize);
   894 		// This will actually discard the page not move it.
   895 		test_KErrNone(aPageMove.TryMovingUserPage(pPage));
   896 
   897 		test_KErrNone(Mem::Compare((TUint8*)romAddr,PageSize,pPage,PageSize));
   898 		}
   899 	}
   900 
   901 
   902 void TestMovingCodeChunk(RPageMove& pagemove, RChunk aChunk, TBool aPagedData)
   903 	{
   904 	TUint8* p = aChunk.Base();
   905 
   906 	TUint8* firstpage = (TUint8*)_ALIGN_DOWN((TLinAddr)p, PageSize);
   907 	RThread thread;
   908 	thread.Open(RThread().Id());
   909 	SPinThreadArgs threadArgs;
   910 	threadArgs.iLinAddr = (TLinAddr)p;
   911 	threadArgs.iParentThread = thread;
   912 
   913 	test.Printf(_L("Attempt to move pages while they are being executed and modified\n"));
   914 	ThreadDie = EFalse;
   915 	RThread modCodeThread;
   916 	TRequestStatus s;
   917 	test_KErrNone(modCodeThread.Create(_L("User Data thread"), &ModifyCodeThread, KDefaultStackSize, NULL, &threadArgs));
   918 	modCodeThread.Logon(s);
   919 	TRequestStatus threadInitialised;
   920 	modCodeThread.Rendezvous(threadInitialised);
   921 	modCodeThread.Resume();
   922 
   923 	_T_PRINTF(_L("wait for child\n"));
   924 	User::WaitForRequest(threadInitialised);
   925 	test_KErrNone(threadInitialised.Int());
   926 
   927 	_T_PRINTF(_L("Move code chunk page repeatedly\n"));
   928 	TBool success=EFalse;
   929 	*(volatile TUint8*)p = *p; // Ensure the page of the first entry is paged in for the first move.
   930 	for (TInt i=0; i < Repitions; i++)
   931 		{
   932 		TInt r = pagemove.TryMovingUserPage(firstpage, ETrue);
   933 		if (i == 0)
   934 			{// If this is the first run allow the modifying thread to run now 
   935 			// we've done one move.
   936 			_T_PRINTF(_L("signal to child\n"));
   937 			RThread::Rendezvous(KErrNone);
   938 			}
   939 		switch (r)
   940 			{
   941 			case KErrInUse:
   942 				break;
   943 			case KErrArgument:
   944 				// The page was paged out, this should only happen for paged data.
   945 				test(aPagedData);
   946 				break;
   947 			default:
   948 				test_KErrNone(r);
   949 				success=ETrue;
   950 				break;
   951 			}
   952 		}
   953 	test(success);
   954 
   955 	ThreadDie = ETrue;
   956 	User::WaitForRequest(s);
   957 	test_Equal(EExitKill,modCodeThread.ExitType());
   958 	test_KErrNone(modCodeThread.ExitReason());
   959 	modCodeThread.Close();
   960 
   961 	thread.Close();
   962 	}
   963 
   964 GLDEF_C TInt E32Main()
   965     {
   966 	test.Title();
   967 	if (!HaveMMU())
   968 		{
   969 		test.Printf(_L("This test requires an MMU\n"));
   970 		return KErrNone;
   971 		}
   972 
   973 	test.Start(_L("Load test LDD"));
   974 	TInt r=User::LoadLogicalDevice(KLddFileName);
   975 	test(r==KErrNone || r==KErrAlreadyExists);
   976 
   977 	test_KErrNone(UserHal::PageSizeInBytes(PageSize));
   978 
   979 	// Determine which types of paging are supported
   980 	TUint32 attrs = DPTest::Attributes();
   981 	gRomPagingSupported = (attrs & DPTest::ERomPaging) != 0;
   982 	gCodePagingSupported = (attrs & DPTest::ECodePaging) != 0;
   983 	gDataPagingSupported = (attrs & DPTest::EDataPaging) != 0;
   984 
   985 	// Does this memory model support pinning.
   986 	TInt mm = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, 0, 0) & EMemModelTypeMask;
   987 	gPinningSupported = mm >= EMemModelTypeFlexible;
   988 
   989 	RPageMove pagemove;
   990 	test.Next(_L("Open test LDD"));
   991 	test_KErrNone(pagemove.Open());
   992 
   993 	// Determine whether this is a smp device.
   994 	NumberOfCpus = pagemove.NumberOfCpus();
   995 	if (NumberOfCpus > 1)
   996 		Repitions = 1000;	// SMP system therefore likely to get KErrInUse in less repitions.
   997 
   998 	test.Next(_L("Attempting to move regular local data pages"));
   999 		{
  1000 		const TInt size=16384;
  1001 		TUint8* array = new TUint8[size];
  1002 		test_NotNull(array);
  1003 
  1004 		TestUserData(pagemove, array, size);
  1005 
  1006 		_T_PRINTF(_L("Walk heap\n"));
  1007 		User::Check();
  1008 
  1009 		delete [] array;
  1010 		}
  1011 
  1012 	test.Next(_L("Attempting to move regular global coarse data pages"));
  1013 		{
  1014 		const TInt size=1<<20;	// Make this chunk multiple of 1MB so it is a coarse memory object on FMM
  1015 		RChunk chunk;
  1016 		test_KErrNone(chunk.CreateDisconnectedGlobal(_L("Dave"), 0, size, size));
  1017 		TUint8* array = chunk.Base();
  1018 
  1019 		TestUserData(pagemove, array, size);
  1020 		TestMovingRealtime(pagemove, array, size, NULL, EFalse);
  1021 		TestCommitDecommit(pagemove, chunk);
  1022 
  1023 		chunk.Close();
  1024 		}
  1025 
  1026 	if (gDataPagingSupported)
  1027 		{
  1028 		test.Next(_L("Attempting to move demand paged fine local user data pages"));
  1029 		const TInt size=16384;
  1030 		TChunkCreateInfo createInfo;
  1031 		createInfo.SetDisconnected(0, size, size);
  1032 		createInfo.SetPaging(TChunkCreateInfo::EPaged);
  1033 		RChunk chunk;
  1034 		test_KErrNone(chunk.Create(createInfo));
  1035 		TUint8* array = chunk.Base();
  1036 
  1037 		TestUserData(pagemove, array, size, ETrue);
  1038 		TestMovingRealtime(pagemove, array, size, NULL, EFalse, ETrue);
  1039 		TestPageTableDiscard(pagemove, array, size);
  1040 		TestCommitDecommit(pagemove, chunk);
  1041 		chunk.Close();
  1042 
  1043 		test.Next(_L("Attempting to move demand paged coarse global user data pages"));
  1044 		const TInt sizeCoarse = 1 << 20; // Make this chunk multiple of 1MB so it is a coarse memory object on FMM
  1045 		TChunkCreateInfo createInfoCoarse;
  1046 		createInfoCoarse.SetDisconnected(0, sizeCoarse, sizeCoarse);
  1047 		createInfoCoarse.SetGlobal(_L("Dave"));
  1048 		createInfoCoarse.SetPaging(TChunkCreateInfo::EPaged);
  1049 		RChunk chunkCoarse;
  1050 		test_KErrNone(chunkCoarse.Create(createInfoCoarse));
  1051 		array = chunkCoarse.Base();
  1052 
  1053 		TestUserData(pagemove, array, sizeCoarse, ETrue);
  1054 		TestMovingRealtime(pagemove, array, sizeCoarse, NULL, EFalse, ETrue);
  1055 		TestPageTableDiscard(pagemove, array, sizeCoarse);
  1056 		TestCommitDecommit(pagemove, chunkCoarse);
  1057 		chunkCoarse.Close();
  1058 		}
  1059 
  1060 	test.Next(_L("Attempting to move DLL writable static data pages"));
  1061 		{
  1062 		const TInt size=16384;
  1063 		TUint8* array = DllWsd::Address();
  1064 
  1065 		TestUserData(pagemove, array, size);
  1066 		}
  1067 
  1068 	test.Next(_L("Attempting to move user self-mod code chunk page when IMB'ing and executing"));
  1069 	RChunk codeChunk;
  1070 	test_KErrNone(codeChunk.CreateLocalCode(PageSize,PageSize));
  1071 	TestMovingCodeChunk(pagemove, codeChunk, EFalse);
  1072 	codeChunk.Close();
  1073 
  1074 	if (gDataPagingSupported)
  1075 		{
  1076 		test.Next(_L("Attempting to move paged user self-mod code chunk page when IMB'ing and executing"));
  1077 		TChunkCreateInfo createInfo;
  1078 		createInfo.SetCode(PageSize, PageSize);
  1079 		createInfo.SetPaging(TChunkCreateInfo::EPaged);
  1080 
  1081 		RChunk pagedCodeChunk;
  1082 		test_KErrNone(pagedCodeChunk.Create(createInfo));
  1083 		TestMovingCodeChunk(pagemove, pagedCodeChunk, ETrue);
  1084 		pagedCodeChunk.Close();
  1085 		}
  1086 
  1087 	test.Next(_L("Attempting to move RAM drive"));
  1088 	if ((MemModelAttributes()&EMemModelTypeMask) == EMemModelTypeMultiple)
  1089 		{
  1090 		for (TInt i=0; i<Repitions; i++)
  1091 			test_KErrNone(pagemove.TryMovingUserPage((TAny*)0xA0000000));
  1092 		}
  1093 	else if ((MemModelAttributes()&EMemModelTypeMask) == EMemModelTypeMoving)
  1094 		{
  1095 		for (TInt i=0; i<Repitions; i++)
  1096 			test_KErrNone(pagemove.TryMovingUserPage((TAny*)0x40000000));
  1097 		}
  1098 	else if ((MemModelAttributes()&EMemModelTypeMask) == EMemModelTypeFlexible)
  1099 		{
  1100 		// do nothing, RAM drive is not special
  1101 		}
  1102 	else
  1103 		{
  1104 		test.Printf(_L("Don't know where the RAM drive is!"));
  1105 		test(0);
  1106 		}
  1107 	
  1108 #if 0
  1109 	test.Next(_L("Attempting to move kernel heap pages"));
  1110 	for (TInt i=0; i<Repitions; i++)
  1111 		test_KErrNone(pagemove.TryMovingKHeap());
  1112 #endif
  1113 
  1114 	if ((MemModelAttributes()&EMemModelTypeMask) != EMemModelTypeFlexible)
  1115 		{// Only the moving and multiple memory models move kernel stack pages.
  1116 		test.Next(_L("Attempting to move kernel stack pages"));
  1117 		for (TInt i=0; i<Repitions; i++)
  1118 			test_KErrNone(pagemove.TryMovingKStack());
  1119 		}
  1120 
  1121 	test.Next(_L("Attempting to move ROM pages"));
  1122 	TestMovingRom(pagemove);
  1123 
  1124 	test.Next(_L("Attempting to move kernel code pages"));
  1125 	for (TInt i=0; i<Repitions; i++)
  1126 		test_KErrNone(pagemove.TryMovingKCode());
  1127 
  1128 	test.Next(_L("Attempting to move regular code pages"));
  1129 	TestMovingCode(pagemove, RamLoadedFunction);
  1130 	TestMovingRealtime(pagemove, NULL, 0, RamLoadedFunction, ETrue, EFalse);
  1131 
  1132 	if (gCodePagingSupported)
  1133 		{
  1134 		test.Next(_L("Attempting to move demand paged code pages"));
  1135 		TestMovingCode(pagemove, DllTestFunction, ETrue);
  1136 		TestMovingRealtime(pagemove, NULL, 0, DllTestFunction, ETrue, ETrue);
  1137 		}
  1138 
  1139 	/* Setup CodeModifier Test Driver */
  1140 	StartCodeModifierDriver();
  1141 	test(KErrNone==Device.InitialiseCodeModifier(/* Max break points */ 5 ));
  1142 
  1143 	test.Next(_L("Attempting to move code page being modified\n"));
  1144 	test_KErrNone(TestCodeModification(pagemove));
  1145 
  1146 	test.Next(_L("Attempting to move code (async) while page being modified"));
  1147 	test_KErrNone(TestCodeModificationAsync(pagemove));
  1148 
  1149 	StopCodeModifierDriver();
  1150 	
  1151 	test.Next(_L("Attempting to move ROM Locale DLL Page"));
  1152 	test_KErrNone(E32TestLocale(1));
  1153 
  1154 	test.Next(_L("Attempting to move RAM Locale DLL Page"));
  1155 	test_KErrNone(E32TestLocale(0));
  1156 
  1157 	test.Next(_L("Close test LDD"));
  1158 	pagemove.Close();
  1159 	User::FreeLogicalDevice(KLddFileName);
  1160 
  1161 	test.End();
  1162 	return(KErrNone);
  1163     }
  1164 
  1165 
  1166 void testUS(const TLocale& aLocale)
  1167 {
  1168 	test.Printf(_L("Test US\n"));
  1169 
  1170 	test(aLocale.CountryCode()==1);
  1171 	test(aLocale.DateFormat()==EDateAmerican);
  1172 	test(aLocale.TimeFormat()==ETime12);
  1173 	test(aLocale.CurrencySymbolPosition()==ELocaleBefore);
  1174 	test(aLocale.CurrencySpaceBetween()==FALSE);
  1175 	test(aLocale.CurrencyDecimalPlaces()==2);
  1176 	test(aLocale.CurrencyNegativeInBrackets()==EFalse);
  1177 	test(aLocale.CurrencyTriadsAllowed()==TRUE);
  1178 	test(aLocale.ThousandsSeparator()==',');
  1179 	test(aLocale.DecimalSeparator()=='.');
  1180 	test(aLocale.DateSeparator(0)==0);
  1181 	test(aLocale.DateSeparator(1)=='/');
  1182 	test(aLocale.DateSeparator(2)=='/');
  1183 	test(aLocale.DateSeparator(3)==0);
  1184 	test(aLocale.TimeSeparator(0)==0);
  1185 	test(aLocale.TimeSeparator(1)==':');
  1186 	test(aLocale.TimeSeparator(2)==':');
  1187 	test(aLocale.TimeSeparator(3)==0);
  1188 	test(aLocale.AmPmSymbolPosition()==TRUE);
  1189 	test(aLocale.AmPmSpaceBetween()==TRUE);
  1190 	test(aLocale.HomeDaylightSavingZone()==EDstNorthern);
  1191 	test(aLocale.WorkDays()==0x1f);
  1192 	test(aLocale.StartOfWeek()==ESunday);
  1193 	test(aLocale.ClockFormat()==EClockAnalog);
  1194 	test(aLocale.UnitsGeneral()==EUnitsImperial);
  1195 	test(aLocale.UnitsDistanceShort()==EUnitsImperial);
  1196 	test(aLocale.UnitsDistanceLong()==EUnitsImperial);
  1197 }
  1198 
  1199 
  1200 void testUK(const TLocale& aLocale)
  1201 {
  1202 //#ifdef __WINS__
  1203 	test(aLocale.CountryCode()==44);
  1204 	test(aLocale.DateFormat()==EDateEuropean);
  1205 	test(aLocale.TimeFormat()==ETime12);
  1206 	test(aLocale.CurrencySymbolPosition()==ELocaleBefore);
  1207 	test(aLocale.CurrencySpaceBetween()==FALSE);
  1208 	test(aLocale.CurrencyDecimalPlaces()==2);
  1209 	test(aLocale.CurrencyNegativeInBrackets()==EFalse);
  1210 	test(aLocale.CurrencyTriadsAllowed()==TRUE);
  1211 	test(aLocale.ThousandsSeparator()==',');
  1212 	test(aLocale.DecimalSeparator()=='.');
  1213 	test(aLocale.DateSeparator(0)==0);
  1214 	test(aLocale.DateSeparator(1)=='/');
  1215 	test(aLocale.DateSeparator(2)=='/');
  1216 	test(aLocale.DateSeparator(3)==0);
  1217 	test(aLocale.TimeSeparator(0)==0);
  1218 	test(aLocale.TimeSeparator(1)==':');
  1219 	test(aLocale.TimeSeparator(2)==':');
  1220 	test(aLocale.TimeSeparator(3)==0);
  1221 	test(aLocale.AmPmSymbolPosition()==TRUE);
  1222 	test(aLocale.AmPmSpaceBetween()==TRUE);
  1223 	test(aLocale.HomeDaylightSavingZone()==EDstEuropean);
  1224 	test(aLocale.WorkDays()==0x1f);
  1225 	test(aLocale.StartOfWeek()==EMonday);
  1226 	test(aLocale.ClockFormat()==EClockAnalog);
  1227 	test(aLocale.UnitsGeneral()==EUnitsImperial);
  1228 	test(aLocale.UnitsDistanceShort()==EUnitsImperial);
  1229 	test(aLocale.UnitsDistanceLong()==EUnitsImperial);
  1230 //#endif
  1231 }
  1232 
  1233 
  1234 void testChangeLocale(TInt isrom)
  1235 {
  1236 	TLocale locale;
  1237 	
  1238 #ifdef __WINS__
  1239 //We get a power-change notification 1 second after switch-on
  1240 //So we wait for a second on WINS.
  1241 //Should we fix this bug??
  1242 	User::After(1000000);
  1243 #endif
  1244 	RChangeNotifier notifier;
  1245 	TInt res=notifier.Create();
  1246 	test(res==KErrNone);
  1247 	TRequestStatus stat;
  1248 	res=notifier.Logon(stat);
  1249 	test(res==KErrNone);
  1250 	//initial pattern of stat is already tested by t_chnot
  1251 
  1252 	res=notifier.Logon(stat);
  1253 	test(res==KErrNone);
  1254 	test(stat==KRequestPending);
  1255 	if (isrom == 0) 
  1256 		{
  1257 		test.Printf(_L("Change to RAM US Locale\n")); 	
  1258 		res=UserSvr::ChangeLocale(ELOCLUS);
  1259 		}
  1260 	else
  1261 		{
  1262 		test.Printf(_L("Change to ROM US Locale\n")); 	
  1263 		res=UserSvr::ChangeLocale(ELOCLUS_ROM);
  1264 		}
  1265 	test.Printf(_L("res=%d\n"),res);
  1266 	test(res==KErrNone);
  1267 	test(stat.Int() & EChangesLocale);
  1268 	res=notifier.Logon(stat);
  1269 	test(res==KErrNone);
  1270 	test(stat==KRequestPending);
  1271 	
  1272 	locale.Refresh();
  1273 	testUS(locale);
  1274 }
  1275 
  1276 
  1277 LOCAL_C void LocaleLanguageGet(SLocaleLanguage& locale)
  1278 {
  1279 	TPckg<SLocaleLanguage> localeLanguageBuf(locale);
  1280 	TInt r = RProperty::Get(KUidSystemCategory, KLocaleLanguageKey, localeLanguageBuf);
  1281 	test(r == KErrNone || r == KErrNotFound);
  1282 }
  1283 
  1284 LOCAL_C TInt E32TestLocale(TInt isrom)
  1285 {
  1286 	TInt r;
  1287 	TAny *LocaleAddr;
  1288 	TLocale locale;
  1289 	
  1290 	/* Setup the US Locale DLL and ensure the Locale got modified (testUS) */
  1291 	testChangeLocale(isrom);
  1292  
  1293 	/* Now get a pointer to some data in the DLL. This will be used to move a
  1294 	** page from the dll 
  1295 	*/
  1296 	SLocaleLanguage localeLanguage;
  1297 	LocaleLanguageGet(localeLanguage);
  1298 	LocaleAddr = (TAny *) localeLanguage.iDateSuffixTable;
  1299 	test(LocaleAddr != NULL);
  1300 
  1301 	RPageMove pagemove;
  1302 	r=pagemove.Open();
  1303 	test_KErrNone(r);
  1304 
  1305 	r=pagemove.TryMovingLocaleDll(LocaleAddr);
  1306 	
  1307 	if (isrom == 0) 
  1308 		{
  1309 		test_KErrNone(r);
  1310 		}
  1311 	else
  1312 		{
  1313 		// When the locale is in rom it is in the unpaged part of rom and 
  1314 		// Epoc::LinearToPhysical() won't be able to find the address.
  1315 		test_Equal(KErrArgument, r)
  1316 		}
  1317 
  1318 	test.Printf(_L("Locale Test: Page move done\n"));
  1319 
  1320 	/* Test US again. The kernel should have cached the locale informaton, so this will not
  1321 	 * really be testing the pagmove.
  1322 	 */
  1323 	locale.Refresh();
  1324 	testUS(locale);
  1325 	
  1326 	/* Reload the Default Locale */
  1327 	test.Printf(_L("Locale Test: Change to UK Default\n"));
  1328 	r=UserSvr::ChangeLocale(ELOCL_DEFAULT);	
  1329 	test(r==KErrNone);
  1330 	locale.Refresh();
  1331 	testUK(locale);	
  1332 
  1333 	/* This will ACTUALLY test the page which was moved by making the kernel reload the Locale
  1334 	 * information from the DLL. 
  1335 	 */
  1336 	if (isrom == 0) 
  1337 		{
  1338 		test.Printf(_L("RAM Locale Test: Change to US Again\n"));
  1339 		r=UserSvr::ChangeLocale(ELOCLUS);	
  1340 		}
  1341 	else
  1342 		{
  1343 		test.Printf(_L("ROM Locale Test: Change to US Again\n"));
  1344 		r=UserSvr::ChangeLocale(ELOCLUS_ROM);	
  1345 		}
  1346 
  1347 
  1348 	test(r==KErrNone);
  1349 	locale.Refresh();
  1350 	testUS(locale);
  1351 
  1352 	/* Reset the Locale to the default */
  1353 	r=UserSvr::ChangeLocale(ELOCL_DEFAULT);	
  1354 	test(r==KErrNone);
  1355 	locale.Refresh();
  1356 	testUK(locale);	
  1357 	return(KErrNone);
  1358 }
  1359 
  1360 LOCAL_C void StartCodeModifierDriver()
  1361 	{
  1362 	test.Printf(_L("Start CodeModifier Driver\n"));
  1363 	TInt r = User::LoadLogicalDevice(KCodeModifierName);
  1364 	test( r==KErrNone || r==KErrAlreadyExists);
  1365 	if((r = Device.Open())!=KErrNone)	
  1366 		{
  1367 		User::FreeLogicalDevice(KCodeModifierName);
  1368 		test.Printf(_L("Could not open LDD"));
  1369 		test(0);
  1370 		}
  1371 	}
  1372 
  1373 
  1374 LOCAL_C void StopCodeModifierDriver()
  1375 	{
  1376 
  1377 	test.Printf(_L("Stop Code Modifier Driver\n"));
  1378 	test(KErrNone==Device.CloseCodeModifier());
  1379 	Device.Close();
  1380 	User::FreeLogicalDevice(KCodeModifierName);
  1381 	}
  1382 
  1383 
  1384 LOCAL_C void TestCodeSetupDrive(RThread &thread)
  1385 {
  1386 	/* The CodeModifier driver (look in ../debug/d_codemodifier) takes two threads, we just use the
  1387 	** first one */
  1388 	test(KErrNone==Device.ThreadId(0, thread.Id()));
  1389 }
  1390 
  1391 
  1392 LOCAL_C TUint GetCodeData(TInt *CodePtr, TInt& Ignore, TInt& FirstJump, TInt& SecondJump)
  1393 	{ 
  1394 	TUint ModAddr;
  1395 
  1396 	Ignore     = *CodePtr++;
  1397 	ModAddr    = (TUint)CodePtr;
  1398 	FirstJump  = *CodePtr++;
  1399 	SecondJump = *CodePtr++;
  1400 	return ModAddr;
  1401 	}
  1402 
  1403 LOCAL_C TInt TestCodeModification(RPageMove &pagemove)
  1404 	{
  1405 	TInt Ignore; 
  1406 	TUint ModAddr;
  1407 	TInt FirstJump;
  1408 	TInt SecondJump;
  1409 	RThread thread;
  1410 	
  1411 	ModAddr = GetCodeData((TInt *)TestCodeModFunc, Ignore, FirstJump, SecondJump); 
  1412 	
  1413 	test.Printf(_L("User Test code Returns = %d\n"), TestCodeModFunc());
  1414 	test.Printf(_L("Ignore = %x First Jump = %x Second = %x \n"), Ignore, FirstJump, SecondJump);
  1415 	
  1416 	TestCodeSetupDrive(thread);
  1417 
  1418 	for (TInt i=0; i<Repitions * 10; i++)
  1419 		{
  1420 		
  1421 		TInt r=Device.WriteCode(0, ModAddr,SecondJump,sizeof(TInt));
  1422 		test_KErrNone(r);
  1423 		r = TestCodeModFunc();
  1424 		test (2 == r);
  1425 
  1426 		test_KErrNone(pagemove.TryMovingUserPage((TAny*)TestCodeModFunc));
  1427 
  1428 		r = Device.RestoreCode(0, ModAddr);
  1429 		test_KErrNone(r);
  1430 		r = TestCodeModFunc();
  1431 		test (1 == r);
  1432 		
  1433 		test_KErrNone(pagemove.TryMovingUserPage((TAny*)TestCodeModFunc));
  1434 
  1435 		}
  1436 
  1437 	test.Printf(_L("User Test code = %d\n"), TestCodeModFunc());
  1438 	return KErrNone;
  1439 	}
  1440 
  1441 LOCAL_C int TestCodeAsync(TAny *NotUsed)
  1442 	{
  1443 	TInt Ignore; 
  1444 	TUint ModAddr;
  1445 	TInt FirstJump;
  1446 	TInt SecondJump;
  1447 
  1448 	ModAddr = GetCodeData((TInt *)TestCodeModFunc, Ignore, FirstJump, SecondJump); 
  1449 
  1450 	FOREVER
  1451 		{
  1452 		TInt r = Device.WriteCode(0, ModAddr,SecondJump,sizeof(TInt));
  1453 		test_KErrNone(r);
  1454 		
  1455 		r = TestCodeModFunc();
  1456 		test (2 == r);
  1457 
  1458 		r = Device.RestoreCode(0, ModAddr);
  1459 	
  1460 		test_KErrNone(r);
  1461 		r = TestCodeModFunc();
  1462 		test (1 == r);
  1463 		User::AfterHighRes(10);
  1464 		}
  1465 	}
  1466 
  1467 /* 
  1468  * Creates a Thread that modifies its code in a tight loop while the main
  1469  * thread moves the functions page around
  1470  */
  1471 LOCAL_C TInt TestCodeModificationAsync(RPageMove& pagemove)
  1472 	{
  1473 	TInt ret;
  1474 	RThread CodeThread;
  1475 	TRequestStatus s;
  1476 
  1477 	
  1478 	/* Create the Thread to modify the code segment */
  1479 	test_KErrNone(CodeThread.Create(_L("TestCodeAsync"), TestCodeAsync, KDefaultStackSize, NULL, NULL));
  1480 	CodeThread.Logon(s);
  1481 	CodeThread.SetPriority(EPriorityMore);
  1482 	CodeThread.Resume();
  1483 
  1484 	TestCodeSetupDrive(CodeThread);
  1485 
  1486 	/* Loop trying to move the code page while the thread (CodeThread) modifies it */
  1487 	for (TInt i=0; i<Repitions; i++)
  1488 		{
  1489 		test_KErrNone(pagemove.TryMovingUserPage((TAny*)TestCodeModFunc));
  1490 		}
  1491 
  1492 	CodeThread.Kill(KErrNone);
  1493 	User::WaitForRequest(s);
  1494 	test_Equal(EExitKill, CodeThread.ExitType());
  1495 	test_KErrNone(CodeThread.ExitReason());
  1496 	CodeThread.Close();
  1497 
  1498 	ret = TestCodeModFunc();
  1499 	test(ret == 1 || ret == 2);
  1500 
  1501 	return KErrNone;
  1502 	}