os/kernelhwsrv/kerneltest/e32test/mmu/t_codepaging.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\mmu\t_codepaging.cpp
    15 // This test relies on four dlls which it loads dynamically:
    16 // - t_codepaging_dll		Very simple dll, contains a single function.  Used for testing state
    17 // changes	of pages
    18 // - t_codepaging_dll2	 	Contains 8 pages of data, used for testing the correct data is paged
    19 // - t_codepaging_dll3		Statically links to t_codepaging_sll, used for testing ReadExportDir
    20 // - t_codepaging_dll4		Large dll, used for testing code segment that span more than one page
    21 // table
    22 // - t_codepaging_dll5		Contains relocatable const data.
    23 // - t_codepaging_dll6		Contains relocatable writable data.
    24 // - t_codepaging_dll7		Statically linked to t_codepaging_dll5 to check dependent DLLs
    25 // are initialised correctly.
    26 // Suite of tests specifically to test the code paging portion of demand 
    27 // paging.
    28 // 002 Exercise ReadExportDir with one code seg mapped already into current process
    29 // 003 Exercise ReadExportDir with one code seg mapped into different process
    30 // 004 Check locking of code which then gets unloaded
    31 // 004.01 Load test driver...
    32 // 004.02 Load/unload dll
    33 // 004.03 Load dll again
    34 // 004.04 Get data from DLL
    35 // 004.05 Lock DLL data
    36 // 004.06 Check DLL data
    37 // 004.07 Close DLL
    38 // 004.08 Check DLL loaded at different address
    39 // 004.09 Unlock DLL data
    40 // 004.10 Check DLL loaded at original address
    41 // 004.11 Cleanup
    42 // 005 Test writing to paged code
    43 // 005.01 Load DLL
    44 // 005.02 Get data from DLL
    45 // 005.03 Write to pages in DLL
    46 // 006 Running tests on drive I:
    47 // 007 Test accessing pages by executing code
    48 // 008 Test accessing pages by reading code
    49 // 009 Test accessing pages by reading code from another process via an alias
    50 // 010 Test unmapping paged code
    51 // 011 Test interactions between two processes
    52 // 012 Test that the contents of a paged DLL are as expected
    53 // 013 Test relocated const data in DLL
    54 // 014 Test relocated writable data in DLL
    55 // 015 Test relocated writable data in dependent DLL
    56 // 016 Test relocated writable data in preloaded dependent DLL
    57 // 017 Test relocated writable data in preloaded dependent DLL opened in other process
    58 // 018 Test killing a thread while it is taking paging faults
    59 // 019 Test unloading a library while another thread is executing it
    60 // 020 Test random access to a large dll
    61 // 021 Test accessing paged code from 2 processes at 1 priority level(s) for 5 seconds
    62 // 022 Test accessing paged code from 5 processes at 1 priority level(s) for 10 seconds
    63 // 023 Test accessing paged code from 10 processes at 1 priority level(s) for 20 seconds
    64 // 024 Test accessing paged code from 5 processes at 2 priority level(s) for 10 seconds
    65 // 025 Test accessing paged code from 50 processes at 1 priority level(s) for 2 seconds
    66 // 026 Running tests on drive Z:
    67 // 027 Test accessing pages by executing code
    68 // 028 Test accessing pages by reading code
    69 // 029 Test accessing pages by reading code from another process via an alias
    70 // 030 Test unmapping paged code
    71 // 031 Test interactions between two processes
    72 // 032 Test that the contents of a paged DLL are as expected
    73 // 033 Test relocated const data in DLL
    74 // 034 Test relocated writable data in DLL
    75 // 035 Test relocated writable data in dependent DLL
    76 // 036 Test relocated writable data in preloaded dependent DLL
    77 // 037 Test relocated writable data in preloaded dependent DLL opened in other process
    78 // 038 Test killing a thread while it is taking paging faults
    79 // 039 Test unloading a library while another thread is executing it
    80 // 040 Test random access to a large dll
    81 // 041 Test accessing paged code from 2 processes at 1 priority level(s) for 5 seconds
    82 // 042 Test accessing paged code from 5 processes at 1 priority level(s) for 10 seconds
    83 // 043 Test accessing paged code from 10 processes at 1 priority level(s) for 20 seconds
    84 // 044 Test accessing paged code from 5 processes at 2 priority level(s) for 10 seconds
    85 // 045 Test accessing paged code from 50 processes at 1 priority level(s) for 2 seconds
    86 // 
    87 //
    88 
    89 //! @SYMTestCaseID			KBASE-T_CODEPAGING-0335
    90 //! @SYMTestType			UT
    91 //! @SYMPREQ				PREQ1110
    92 //! @SYMTestCaseDesc		Demand Paging Code Paging tests.
    93 //! @SYMTestActions			001 Code paging tests
    94 //! @SYMTestExpectedResults All tests should pass.
    95 //! @SYMTestPriority        High
    96 //! @SYMTestStatus          Implemented
    97 
    98 
    99 #define __E32TEST_EXTENSION__
   100 #include <e32test.h>
   101 #include <f32file.h>
   102 #include <e32math.h>
   103 #include <dptest.h>
   104 
   105 #include "mmudetect.h"
   106 #include "d_memorytest.h"
   107 #include "d_demandpaging.h"
   108 #include "t_codepaging_dll.h"
   109 #include "paging_info.h"
   110 
   111 class TPagingDriveInfo
   112 	{
   113 public:
   114 	TChar iDriveLetter;
   115 	TDriveInfo iDriveInfo;
   116 	};
   117 
   118 RArray<TPagingDriveInfo> SupportedDrives;
   119 
   120 /// Page attributes, cut-n-paste'd from mmubase.h
   121 enum TType
   122 	{
   123 //	EInvalid=0,			// No physical RAM exists for this page
   124 //	EFixed=1,			// RAM fixed at boot time
   125 //	EUnused=2,			// Page is unused
   126 //	EChunk=3,
   127 //	ECodeSeg=4,
   128 //	EHwChunk=5,
   129 //	EPageTable=6,
   130 //	EPageDir=7,
   131 //	EPtInfo=8,
   132 //	EShadow=9,
   133 
   134 	EPagedROM=10,
   135 	EPagedCode=11,
   136 	EPagedData=12,
   137 	EPagedCache=13,
   138 	EPagedFree=14,
   139 	};
   140 
   141 enum TState
   142 	{
   143 	EStateNormal 			= 0,	// no special state
   144 	EStatePagedYoung 		= 1,
   145 	EStatePagedOld 			= 2,
   146 	EStatePagedDead 		= 3,	// Not possible on the flexible memory model.
   147 	EStatePagedLocked 		= 4,
   148 	EStatePagedOldestClean 	= 5,	// Flexible memory model only.
   149 	EStatePagedOldestDirty 	= 6,	// Flexible memory model only.
   150 	};
   151 
   152 
   153 
   154 /// The possible states for a logical page of RAM loaded code
   155 enum TPageState
   156 	{
   157 	EStateUnmapped,
   158 	EStatePagedOut,
   159 	EStateYoung,
   160 	EStateOld,
   161 	EStateOldestClean,
   162 	EStateOldestDirty,
   163 
   164 	ENumPageStates
   165 	};
   166 
   167 const TUint KPagedStateShift = 8;
   168 const TUint KPagedStateMask = 0xff00;
   169 
   170 
   171 /// The possible states for a physical page of RAM loaded code
   172 enum TPhysState
   173 	{
   174 	EPhysNotPresent,
   175 	EPhysYoung,
   176 	EPhysOld,
   177 	EPhysOldestClean,
   178 	EPhysOldestDirty,
   179 
   180 	ENumPhysStates
   181 	};
   182 
   183 /// Names of the logical page states
   184 const char* StateNames[ENumPageStates] =
   185 	{
   186 	"Unmapped",
   187 	"PagedOut",
   188 	"Young",
   189 	"Old",
   190 	"OldestClean",
   191 	"OldestDirty"
   192 	};
   193 
   194 /// Names of the physical page states
   195 const char* PhysStateNames[ENumPhysStates] =
   196 	{
   197 	"NotPresent",
   198 	"Young",
   199 	"Old",
   200 	"OldestClean",
   201 	"OldestDirty"
   202 	};
   203 
   204 /// Array of physical page states indexed by logical page state
   205 TPhysState PhysStateFromPageState[ENumPageStates] =
   206 	{
   207 	EPhysNotPresent,
   208 	EPhysNotPresent,
   209 	EPhysYoung,
   210 	EPhysOld,
   211 	EPhysOldestClean,
   212 	EPhysOldestDirty,
   213 	};
   214 
   215 /// The expected logical page state bitmask for each state
   216 TInt ExpectedPageState[ENumPageStates] =
   217 	{
   218 	0,
   219 	EPageStatePageTablePresent | EPageStateInRamCode | EPageStatePaged,
   220 	EPageStatePageTablePresent | EPageStateInRamCode | EPageStatePaged | EPageStatePtePresent | EPageStatePteValid,
   221 	EPageStatePageTablePresent | EPageStateInRamCode | EPageStatePaged | EPageStatePtePresent,
   222 	EPageStatePageTablePresent | EPageStateInRamCode | EPageStatePaged | EPageStatePtePresent,
   223 	EPageStatePageTablePresent | EPageStateInRamCode | EPageStatePaged | EPageStatePtePresent
   224 	};
   225 
   226 /// Extra bits we expect to be set on the multiple memory model
   227 TInt ExpectedPageStateMultipleExtra[ENumPageStates] =
   228 	{
   229 	EPageStateCodeChunkPresent,
   230 	EPageStateCodeChunkPresent,
   231 	EPageStateCodeChunkPresent | EPageStatePhysAddrPresent,
   232 	EPageStateCodeChunkPresent | EPageStatePhysAddrPresent
   233 	};
   234 
   235 /// Mask for the bits of the page state related to the physicsal page that we check
   236 TInt PhysStateMask = 0xffff;
   237 
   238 /// The expected physical page state bitmask for each state
   239 TInt ExpectedPhysState[ENumPhysStates] =
   240 	{
   241 	0,
   242 	EPagedCode | (EStatePagedYoung<<8),
   243 	EPagedCode | (EStatePagedOld<<8),
   244 	EPagedCode | (EStatePagedOldestClean<<8),
   245 	EPagedCode | (EStatePagedOldestDirty<<8)
   246 	};
   247 
   248 typedef void (*TFunc)(void);
   249 typedef void (*TFunc1)(TInt aArg1);
   250 typedef TFunc TTransitionTable[ENumPageStates][ENumPageStates];
   251 
   252 void LoadLibrary();
   253 void UnloadLibrary();
   254 void AccessPage();
   255 void MakeOld();
   256 void MakeOldest();
   257 void MakePagedOut();
   258 
   259 TTransitionTable StateTransitions =
   260 	{
   261 // Current:			Next:	EStateUnmapped	EStatePagedOut	EStateYoung		EStateOld	EStateOldestClean	EStateOldestDirty	
   262 /* EStateUnmapped 	*/	{	0,				LoadLibrary,	0,				0,			0,					0			},
   263 /* EStatePagedOut	*/	{	UnloadLibrary,	0,				AccessPage,		0,			0,					0			},
   264 /* EStateYoung		*/	{	UnloadLibrary,	MakePagedOut,	AccessPage,		MakeOld,	0,					0			},
   265 /* EStateOld		*/	{	UnloadLibrary,	MakePagedOut,	AccessPage,		0,			MakeOldest,			MakeOldest	},
   266 /* EStateOldestClean*/	{	UnloadLibrary,	MakePagedOut,	AccessPage,		0,			0,					0			},
   267 /* EStateOldestDirty*/	{	UnloadLibrary,	MakePagedOut,	AccessPage,		0,			0,					0			},
   268 	};
   269 
   270 const TInt KMaxPathLen = 16;
   271 typedef TPageState TStatePath[KMaxPathLen];
   272 
   273 // Test paths through the possible states that excercises all transitions except those back to unmapped
   274 // Doesn't consider dirty pages.
   275 TStatePath TestPathNoOldest =
   276 	{
   277 	EStateUnmapped,
   278 	EStatePagedOut,
   279 	EStateYoung,
   280 	EStateOld,
   281 	EStateYoung,
   282 	EStateOld,
   283 	EStatePagedOut,
   284 	EStateUnmapped,
   285 	};
   286 
   287 TStatePath TestPathOldest =
   288 	{
   289 	EStateUnmapped,
   290 	EStatePagedOut,
   291 	EStateYoung,
   292 	EStateOld,
   293 	EStateOldestClean,
   294 	EStateYoung,
   295 	EStateOld,
   296 	EStateYoung,
   297 	EStateOld,
   298 	EStatePagedOut,
   299 	EStateYoung,
   300 	EStateOld,
   301 	EStateOldestClean,
   302 	EStatePagedOut,
   303 	EStateUnmapped,
   304 	};
   305 
   306 TStatePath* TestPath = NULL;
   307 
   308 /// The different ways of accessing paged code
   309 enum TAccessMethod
   310 	{
   311 	EAccessExec,
   312 	EAccessRead,
   313 	EAccessAliasRead
   314 	};
   315 
   316 _LIT(KLibraryName, "t_codepaging_dll");
   317 _LIT(KSearchPathTemplate, "?:\\sys\\bin");
   318 
   319 // RTest stuff /////////////////////////////////////////////////////////////////
   320 
   321 RTest test(_L("T_CODEPAGING"));
   322 
   323 #define test_noError(x) { TInt _r = (x); if (_r < 0) HandleError(_r, __LINE__); }
   324 #define test_notNull(x) { TAny* _a = (TAny*)(x); if (_a == NULL) HandleNull(__LINE__); }
   325 #define test_equal(e, a) { TInt _e = TInt(e); TInt _a = TInt(a); if (_e != _a) HandleNotEqual(_e, _a, __LINE__); }
   326 
   327 void HandleError(TInt aError, TInt aLine)
   328 	{
   329 	test.Printf(_L("Error %d\n"), aError);
   330 	test.operator()(EFalse, aLine);
   331 	}
   332 
   333 void HandleNull(TInt aLine)
   334 	{
   335 	test.Printf(_L("Null value\n"));
   336 	test.operator()(EFalse, aLine);
   337 	}
   338 
   339 void HandleNotEqual(TInt aExpected, TInt aActual, TInt aLine)
   340 	{
   341 	test.Printf(_L("Expected 0x%x but got 0x%x\n"), aExpected, aActual);
   342 	test.operator()(EFalse, aLine);
   343 	}
   344 
   345 //  Server session /////////////////////////////////////////////////////////////
   346 
   347 _LIT(KServerName, "t_codepaging_server");
   348 
   349 class RTestSession : public RSessionBase
   350 	{
   351 public:
   352 	enum TMessage
   353 		{
   354 		EKill,
   355 		EExec,
   356 		ESetCurrentDrive,
   357 		EDesRead,
   358 		ETestPageState,
   359 		ETestStateTransition,
   360 		EStartRandomAccessThread
   361 		};
   362 public:
   363 	TInt Connect(TInt aProcessNum);
   364 	inline void Kill()
   365 		{ test_noError(RSessionBase::SendReceive(EKill,TIpcArgs())); }
   366 	inline void Exec(TFunc aFunc)
   367 		{ test_noError(RSessionBase::SendReceive(EExec,TIpcArgs((TInt)aFunc))); }
   368 	inline void SetCurrentDrive(TUint16 aDrive)
   369 		{ test_noError(RSessionBase::SendReceive(ESetCurrentDrive,TIpcArgs(aDrive))); }
   370 	inline void DesRead(const TDesC8& aData)
   371 		{ test_noError(RSessionBase::SendReceive(EDesRead,TIpcArgs(&aData))); }
   372 	inline void TestPageState(TPageState aState, TPhysState aPhysState)
   373 		{ test_noError(RSessionBase::SendReceive(ETestPageState,TIpcArgs(aState, aPhysState))); }
   374 	inline void TestStateTransition(TPageState aState)
   375 		{ test_noError(RSessionBase::SendReceive(ETestStateTransition,TIpcArgs(aState))); }
   376 	inline void StartRandomAccessThread(TThreadPriority aPriority)
   377 		{ test_noError(RSessionBase::SendReceive(EStartRandomAccessThread,TIpcArgs(aPriority))); }
   378 	};
   379 
   380 TInt RTestSession::Connect(TInt aProcessNum)
   381 	{
   382 	TBuf<32> name;
   383 	name.AppendFormat(_L("%S-%d"), &KServerName, aProcessNum);
   384 	return CreateSession(name,TVersion());
   385 	}
   386 
   387 
   388 // Global data /////////////////////////////////////////////////////////////////
   389 
   390 TBool MovingMemoryModel;
   391 TBool MultipleMemoryModel;
   392 TBool FlexibleMemoryModel;
   393 TBool HaveOldestLists;
   394 TInt ProcessNum;
   395 
   396 RTestSession OtherProcess;
   397 
   398 RLibrary PagedLibrary;
   399 TBool LibraryLoaded = EFalse;
   400 
   401 TTestFunction Library_TestFunction = NULL;
   402 
   403 TAccessMethod AccessMethod;
   404 
   405 RLibrary LargeLibrary;
   406 TBool LargeLibraryLoaded = EFalse;
   407 const TUint8* LargeDataStart;
   408 const TUint8* LargeDataEnd;
   409 const TUint8* LargeDataPtr;
   410 TInt PagesReadSinceLastAccess = 0;
   411 
   412 TInt LiveListSize;
   413 TInt PageSize;
   414 
   415 TPageState State;
   416 TPhysState PhysState;
   417 
   418 TUint16 CurrentDrive;
   419 TInt LocalDriveNumber;
   420 
   421 RThread RandomAccessThread;
   422 volatile TBool RandomAccessKill = EFalse;
   423 
   424 TBool CanForcePageOut = ETrue;
   425 
   426 // Utility functions ///////////////////////////////////////////////////////////
   427 
   428 TPtrC16 GetMediaType(TInt aMediaType)
   429 	{
   430 	_LIT(KMediaNotPresent, "MediaNotPresent");
   431 	_LIT(KMediaUnknown, "MediaUnknown");
   432 	_LIT(KMediaFloppy, "MediaFloppy");
   433 	_LIT(KMediaHardDisk, "MediaHardDisk");
   434 	_LIT(KMediaCdRom, "MediaCdRom");
   435 	_LIT(KMediaRam, "MediaRam");
   436 	_LIT(KMediaFlash, "MediaFlash");
   437 	_LIT(KMediaRom, "MediaRom");
   438 	_LIT(KMediaRemote, "MediaRemote");
   439 	_LIT(KMediaNANDFlash, "MediaNANDFlash");
   440 	_LIT(KMediaUnKnown, "MediaUnKnown");
   441 
   442 	switch(aMediaType)
   443 		{
   444 		case EMediaNotPresent:
   445 			return KMediaNotPresent();
   446 		case EMediaUnknown:
   447 			return KMediaUnknown();
   448 		case EMediaFloppy:
   449 			return KMediaFloppy();
   450 		case EMediaHardDisk:
   451 			return KMediaHardDisk();
   452 		case EMediaCdRom:
   453 			return KMediaCdRom();
   454 		case EMediaRam:
   455 			return KMediaRam();
   456 		case EMediaFlash:
   457 			return KMediaFlash();
   458 		case EMediaRom:
   459 			return KMediaRom();
   460 		case EMediaRemote:
   461 			return KMediaRemote();
   462 		case EMediaNANDFlash:
   463 			return KMediaNANDFlash();
   464 		default:
   465 			return KMediaUnKnown();
   466 		}
   467 	}
   468 
   469 // Get the list of pageable drives
   470 void GetSupportedDrives(TBool aVerbose = EFalse)
   471 	{
   472 	if (aVerbose)
   473 		{
   474 		test.Printf(_L("Supported drives:\n"));
   475 		test.Printf(_L("     Type             Attr     MedAttr  Filesystem\n"));
   476 		}
   477 		
   478 	RFs fs;
   479 	test_noError(fs.Connect());
   480 
   481 	TDriveList driveList;
   482 	TDriveInfo driveInfo;
   483 
   484 	TInt r = fs.DriveList(driveList);
   485     test_noError(r);
   486 
   487 	TBool NandPageableMediaFound = EFalse;
   488 
   489 	for (TInt drvNum=0; drvNum<KMaxDrives; ++drvNum)
   490 		{
   491 	    if(!driveList[drvNum])
   492 	        continue;   //-- skip unexisting drive
   493 	
   494 	    r = fs.Drive(driveInfo, drvNum);
   495 	    test_noError(r);
   496 
   497 
   498 		TChar ch;
   499 		r = fs.DriveToChar(drvNum, ch);
   500 		test_noError(r);
   501 
   502 		TBuf<256> fileSystemName;
   503 		r = fs.FileSystemName(fileSystemName, drvNum);
   504 		test_noError(r);
   505 	
   506 		if ((driveInfo.iDriveAtt & KDriveAttPageable) && (driveInfo.iType == EMediaNANDFlash))
   507 			NandPageableMediaFound = ETrue;
   508 
   509 		TBool pageable = EFalse;
   510 		if (driveInfo.iDriveAtt & KDriveAttPageable)
   511 			pageable = ETrue;
   512 
   513 		// If we've already found a pageable NAND drive, 
   514 		// then assume the Z: drive is pageable too if it's got a composite file system
   515 		_LIT(KCompositeName,"Composite");
   516 		if ((fileSystemName == KCompositeName()) && NandPageableMediaFound)
   517 			pageable = ETrue;
   518 			
   519 		if (pageable)
   520 			{
   521 			TChar ch;
   522 			r = fs.DriveToChar(drvNum, ch);
   523 			test_noError(r);
   524 
   525 			TPagingDriveInfo pagingDriveInfo;
   526 			pagingDriveInfo.iDriveLetter = ch;
   527 			pagingDriveInfo.iDriveInfo = driveInfo;
   528 
   529 			r = SupportedDrives.Append(pagingDriveInfo);
   530 			test_noError(r);
   531 			}
   532 		
   533 		if (aVerbose)
   534 			{
   535 			TPtrC16 mediaType = GetMediaType(driveInfo.iType);
   536 			_LIT(KPageable, "pageable");
   537 			test.Printf(_L("  %c: %16S %08x %08x %10S %S\n"), 
   538 						(TInt) ch, &mediaType, driveInfo.iDriveAtt, driveInfo.iMediaAtt,
   539 						&fileSystemName, (pageable ? &KPageable : &KNullDesC));
   540 			}
   541 		
   542 		}
   543 
   544 	fs.Close();
   545 	}
   546 
   547 TInt GetPageState(TAny* aPage)
   548 	{
   549 	TInt r = UserSvr::HalFunction(EHalGroupVM, EVMPageState, aPage, 0);
   550 	test_noError(r);
   551 	return r;
   552 	}
   553 
   554 // Force a page to be paged in or rejuvenated, to simulate aging of pages in the live list
   555 void ForcePageIn()
   556 	{
   557 	// Find a page that's old or paged out
   558 	do
   559 		{
   560 		LargeDataPtr += PageSize;
   561 		if (LargeDataPtr >= LargeDataEnd)
   562 			LargeDataPtr = LargeDataStart;
   563 		}
   564 	while (GetPageState((TAny*)LargeDataPtr) & EPageStatePteValid);
   565 
   566 	// and read from it to make it young
   567 	TUint32 value = *(volatile TUint8*)LargeDataPtr;
   568 	(void)value;
   569 	++PagesReadSinceLastAccess;
   570 	}
   571 
   572 void FlushAllPages()
   573 	{
   574 	test_noError(UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0));
   575 	}
   576 
   577 void TestCurrentState()
   578 	{
   579 	test_Value(State, State >= 0 && State < ENumPageStates);
   580 	test_Value(PhysState, PhysState >= 0 && PhysState < ENumPhysStates);
   581 	
   582 	TInt stateBits = GetPageState((TAny*)Library_TestFunction);
   583 	TInt expected = ExpectedPageState[State];
   584 	if (MultipleMemoryModel)
   585 		expected |= ExpectedPageStateMultipleExtra[State];
   586 	TUint physStateIgnore = 0;
   587 	if (FlexibleMemoryModel)
   588 		{
   589 		expected &= ~EPageStatePageTablePresent; // flexible memory model allocates page tables on demand
   590 		physStateIgnore = 0xff; // flexible memory model doesn't have separate page types for code/data/ROM
   591 		}
   592 
   593 	test_equal(expected, stateBits & (~PhysStateMask))
   594 	test_equal(ExpectedPhysState[PhysState] & ~physStateIgnore, stateBits & PhysStateMask & ~physStateIgnore)
   595 	}
   596 
   597 void TestPageState(TPageState aExpected, TPhysState aExpectedPhys)
   598 	{
   599 	RDebug::Printf("%d:  %-12s %-12s", ProcessNum, StateNames[aExpected], PhysStateNames[aExpectedPhys]);
   600 	test_equal(State, aExpected);
   601 	test_equal(PhysState, aExpectedPhys);
   602 	TestCurrentState();
   603 	}
   604 
   605 TInt PathLength(const TStatePath& aPath)
   606 	{
   607 	TInt i = 1;
   608 	while (aPath[i] != EStateUnmapped && i < KMaxPathLen)
   609 		++i;
   610 	return i + 1;
   611 	}
   612 
   613 TInt FindState(const TStatePath& aPath, TPageState aTarget)
   614 	{
   615 	TInt len = PathLength(aPath);
   616 	TInt j;
   617 	for (j = 1 ; j < len ; ++j)
   618 		{
   619 		if (aPath[j] == aTarget)
   620 			return j;
   621 		}
   622 	return -1;
   623 	}
   624 
   625 TInt WriteByte(TAny* aArg)
   626 	{
   627 	TUint8* ptr = (TUint8*)aArg;
   628 	*ptr = 23;
   629 	return KErrNone;
   630 	}
   631 
   632 void StartOtherProcess(TInt aProcessNum, RTestSession& aSession)
   633 	{
   634 	RProcess me, other;
   635 	TBuf<16> arg;
   636 	arg.AppendNum(aProcessNum);
   637 	test_noError(other.Create(me.FileName(), arg));
   638 	TRequestStatus status;
   639 	other.Rendezvous(status);
   640 	other.Resume();
   641 	User::WaitForRequest(status);
   642 	test_noError(status.Int());
   643 	test_equal(EExitPending, other.ExitType());
   644 	test_noError(aSession.Connect(aProcessNum));
   645 	other.Close();
   646 	}
   647 
   648 const TDesC& LibrarySearchPath(TUint16 aDrive)
   649 	{
   650 	static TBuf<32> path;
   651 	path = KSearchPathTemplate;
   652 	path[0] = aDrive;
   653 	return path;
   654 	}
   655 
   656 const TDesC& LibraryName(TInt aLibraryNum, TUint16 aDrive)
   657 	{
   658 	// this gives dlls a different name on each drive so we can be sure we're loading the right one
   659 	static TBuf<32> name;
   660 	name = KLibraryName;
   661 	if (aLibraryNum > 1)
   662 		name.AppendNum(aLibraryNum);
   663 	if (aDrive != 'Z')
   664 		name.AppendFormat(_L("_%c"), aDrive);
   665 	return name;
   666 	}
   667 
   668 const TDesC& LibraryFilename(TInt aLibraryNum, TUint16 aDrive)
   669 	{
   670 	static TBuf<40> filename;
   671 	filename = LibrarySearchPath(aDrive);
   672 	filename.AppendFormat(_L("\\%S.dll"), &LibraryName(aLibraryNum, aDrive));
   673 	return filename;
   674 	}
   675 
   676 TInt LoadSpecificLibrary(RLibrary& aLibrary, TInt aLibraryNum, TUint16 aDrive)
   677 	{
   678 	const TDesC& name = LibraryName(aLibraryNum, aDrive);
   679 	const TDesC& path = LibrarySearchPath(aDrive);
   680 	return aLibrary.Load(name, path);
   681 	}
   682 
   683 TInt GetLocDrvNumber(TUint16 aDrive)
   684 	{
   685 	RFs fs;
   686 	RFile file;
   687 
   688 	TBuf<40> libname = LibraryFilename(1, aDrive);
   689 	
   690 	fs.Connect();
   691 	TInt r=file.Open(fs,libname,EFileRead);
   692 	if(r!=KErrNone)
   693 		test.Printf(_L("%d: Error %d: could not open file %S\n"),ProcessNum, r, &libname);
   694 	test(r==KErrNone);
   695 
   696 	SBlockMapInfo info;
   697 	TInt64 start=0;
   698 	r=file.BlockMap(info,start, -1,ETestDebug);
   699 
   700 	if (r!=KErrNone && r!=KErrCompletion)
   701 		test.Printf(_L("Error %d: could not obtain block map\n"),r);
   702 	test(r==KErrNone || r==KErrCompletion);
   703 	TInt locDriveNumber=info.iLocalDriveNumber;
   704 
   705 	file.Close();
   706 	fs.Close();
   707 	return locDriveNumber;
   708 	}
   709 
   710 void LoadLargeLibrary()
   711 	{
   712 	test(!LargeLibraryLoaded);
   713 	test_noError(LoadSpecificLibrary(LargeLibrary, 4, CurrentDrive));
   714 	TGetAddressOfDataFunction func = (TGetAddressOfDataFunction)LargeLibrary.Lookup(KGetAddressOfDataFunctionOrdinal);
   715 	TInt size;
   716 	LargeDataStart = (TUint8*)func(size);
   717 	test_notNull(LargeDataStart);
   718 	if (size < LiveListSize*PageSize)
   719 		{
   720 		// We need an area of paged data large enough to ensure we can cause a page of our choice to
   721 		// be paged out.  If the size of the live list for testing is too small, we'll skip some tests
   722 		CanForcePageOut = EFalse;
   723 		}
   724 	LargeDataEnd = LargeDataStart + size;
   725 	LargeDataPtr = LargeDataStart;
   726  	LargeLibraryLoaded = ETrue;
   727 	}
   728 
   729 void UnloadLargeLibrary()
   730 	{
   731 	test(LargeLibraryLoaded);
   732 	LargeLibrary.Close();
   733 	LargeDataStart = NULL;
   734 	LargeDataEnd = NULL;
   735 	LargeDataPtr = NULL;
   736 	LargeLibraryLoaded = EFalse;
   737 	}
   738 
   739 // Page in a page and keep aging it to see if it ever reaches an oldest list. 
   740 TBool SetHaveOldestLists()
   741 	{
   742 	AccessMethod = EAccessExec;
   743 	AccessPage();
   744 	TInt pagedState = (GetPageState((TAny*)Library_TestFunction) & KPagedStateMask) >> KPagedStateShift;
   745 	do	
   746 		{
   747 		ForcePageIn();
   748 		pagedState = (GetPageState((TAny*)Library_TestFunction) & KPagedStateMask) >> KPagedStateShift;
   749 		if (EStatePagedOldestClean == pagedState || EStatePagedOldestDirty == pagedState)
   750 			break;
   751 		}
   752 	while (	PagesReadSinceLastAccess <= LiveListSize);
   753 
   754 	HaveOldestLists = EStatePagedOldestClean == pagedState || EStatePagedOldestDirty == pagedState;
   755 	return HaveOldestLists;
   756 	}
   757 
   758 void SetCurrentDrive(TUint16 aDrive)
   759 	{
   760 	if (LargeLibraryLoaded)
   761 		UnloadLargeLibrary();
   762 	CurrentDrive = aDrive;
   763 	LocalDriveNumber = GetLocDrvNumber(aDrive);
   764 	LoadLargeLibrary();
   765 	if (!Library_TestFunction)
   766 		{
   767 		LoadLibrary();
   768 		Library_TestFunction = (TTestFunction)PagedLibrary.Lookup(KTestFunctionOrdinal);
   769 		test_notNull(Library_TestFunction);
   770 		if (SetHaveOldestLists())
   771 			TestPath = &TestPathOldest;
   772 		else
   773 			TestPath = &TestPathNoOldest;
   774 		UnloadLibrary();
   775 		FlushAllPages();
   776 		}
   777 	}
   778 
   779 // State transition functions //////////////////////////////////////////////////
   780 
   781 void LoadLibrary()
   782 	{
   783 	test_noError(LoadSpecificLibrary(PagedLibrary, 1, CurrentDrive));
   784 	if (MovingMemoryModel)
   785 		FlushAllPages(); // to make sure pages aren't already mapped
   786 	LibraryLoaded = ETrue;
   787 	}
   788 
   789 void UnloadLibrary()
   790 	{
   791 	PagedLibrary.Close();
   792 	LibraryLoaded = EFalse;
   793 	}
   794 
   795 void AccessPage()
   796 	{
   797 	switch (AccessMethod)
   798 		{
   799 		case EAccessExec:
   800 			Library_TestFunction();
   801 			break;
   802 
   803 		case EAccessRead:
   804 			{
   805 			TUint8 x = *(volatile TUint8*)Library_TestFunction;
   806 			(void)x;
   807 			}
   808 			break;
   809 
   810 		case EAccessAliasRead:
   811 			{
   812 			TPtrC8 des((TUint8*)Library_TestFunction, 4);  // descriptor header must be in different page to data
   813 			OtherProcess.DesRead(des);
   814 			}
   815 			break;
   816 
   817 		}
   818 	PagesReadSinceLastAccess = 0;
   819 	}
   820 
   821 void MakeOld()
   822 	{
   823 	TInt initialState = GetPageState((TAny*)Library_TestFunction);
   824 	do	
   825 		ForcePageIn();
   826 	while (PagesReadSinceLastAccess <= LiveListSize &&
   827 		   initialState == GetPageState((TAny*)Library_TestFunction));
   828 	TUint pagedState = (GetPageState((TAny*)Library_TestFunction) & KPagedStateMask) >> KPagedStateShift;
   829 	test_Equal(EStatePagedOld, pagedState);
   830 	}
   831 
   832 void MakeOldest()
   833 	{
   834 	TInt pagedState = (GetPageState((TAny*)Library_TestFunction) & KPagedStateMask) >> KPagedStateShift;
   835 	do	
   836 		{
   837 		ForcePageIn();
   838 		pagedState = (GetPageState((TAny*)Library_TestFunction) & KPagedStateMask) >> KPagedStateShift;
   839 		if (EStatePagedOldestClean == pagedState ||	EStatePagedOldestDirty == pagedState)
   840 			break;
   841 		}
   842 	while (PagesReadSinceLastAccess <= LiveListSize);
   843 	test_Value(pagedState, EStatePagedOldestClean == pagedState || EStatePagedOldestDirty == pagedState);
   844 	}
   845 
   846 void MakePagedOut()
   847 	{
   848 	TInt finalListState1 = EStatePagedOld;
   849 	TInt finalListState2 = EStatePagedOld;
   850 	if (HaveOldestLists)
   851 		{
   852 		finalListState1 = EStatePagedOldestClean;
   853 		finalListState2 = EStatePagedOldestDirty;
   854 		}
   855 
   856 	TInt pagedState = (GetPageState((TAny*)Library_TestFunction) & KPagedStateMask) >> KPagedStateShift;
   857 	// Get the page onto the final list(s) so it can be detected when it is paged out.
   858 	while (	pagedState != finalListState1 && pagedState != finalListState2 &&
   859 			PagesReadSinceLastAccess <= LiveListSize)
   860 		{
   861 		ForcePageIn();
   862 		pagedState = (GetPageState((TAny*)Library_TestFunction) & KPagedStateMask) >> KPagedStateShift;
   863 		}
   864 	// Now force the page off the paging lists.
   865 	pagedState = GetPageState((TAny*)Library_TestFunction);
   866 	do
   867 		{
   868 		ForcePageIn();
   869 		}
   870 	while (	PagesReadSinceLastAccess <= LiveListSize &&
   871 			pagedState == GetPageState((TAny*)Library_TestFunction));
   872 	}
   873 
   874 // Test functions //////////////////////////////////////////////////////////////
   875 
   876 void Initialise()
   877 	{
   878 	CurrentDrive = 'Z';
   879 	
   880 	TUint32 memModelAttrs = MemModelAttributes();
   881 	MovingMemoryModel = ((memModelAttrs & EMemModelTypeMask) == EMemModelTypeMoving);
   882 	MultipleMemoryModel = ((memModelAttrs & EMemModelTypeMask) == EMemModelTypeMultiple);
   883 	FlexibleMemoryModel = ((memModelAttrs & EMemModelTypeMask) == EMemModelTypeFlexible);
   884 
   885 	test_noError(UserSvr::HalFunction(EHalGroupKernel, EKernelHalPageSizeInBytes, &PageSize, 0));
   886 	
   887 	SVMCacheInfo info;
   888 	test_noError(UserSvr::HalFunction(EHalGroupVM, EVMHalGetCacheSize, &info, 0));
   889 	LiveListSize = info.iMaxSize / PageSize;
   890 	}
   891 
   892 void CopyDllFragmented(RFs& aFs, const TDesC& aSourceName, const TDesC& aDestName)
   893 	{
   894 	test.Printf(_L("  %S\n"), &aDestName);
   895 
   896 	TInt r = aFs.MkDirAll(aDestName);
   897 	test(r == KErrNone || r == KErrAlreadyExists);
   898 
   899 	TBuf<40> tempName(aDestName);
   900 	tempName.Append(_L(".tmp"));
   901 
   902 	RFile in, out, temp;
   903 	test_noError(in.Open(aFs, aSourceName, EFileRead));
   904 	test_noError(out.Replace(aFs, aDestName, EFileWrite));
   905 	test_noError(temp.Replace(aFs, tempName, EFileWrite));
   906 
   907 	const TInt KBufferSize = 3333;
   908 	TBuf8<KBufferSize> buffer;
   909 	
   910 	test_noError(temp.Write(buffer));
   911 	test_noError(temp.Flush());
   912 
   913 	TInt size;
   914 	test_noError(in.Size(size));
   915 	TInt pos = 0;
   916 	while (pos < size)
   917 		{
   918 		test_noError(in.Read(buffer));
   919 		test_noError(out.Write(buffer));
   920 		test_noError(out.Flush());
   921 		test_noError(temp.Write(buffer));
   922 		test_noError(temp.Flush());
   923 		pos += buffer.Length();
   924 		}
   925 	
   926 	in.Close();
   927 	out.Close();
   928 	temp.Close();
   929 	}
   930 
   931 void CopyDllToSupportedDrives(RFs& aFs, CFileMan* aFileMan, TInt aLibraryNum)
   932 	{
   933 	TBuf<40> source = LibraryFilename(aLibraryNum, 'Z');
   934 
   935 	test.Printf(_L("Copying %S to:\n"), &source);
   936 	
   937 	for (TInt i = 0 ; i < SupportedDrives.Count() ; ++i)
   938 		{
   939 		TUint8 drive = SupportedDrives[i].iDriveLetter;
   940 		if (!(SupportedDrives[i].iDriveInfo.iMediaAtt & KMediaAttWriteProtected))
   941 			{
   942 			TBuf<40> dest = LibraryFilename(aLibraryNum, drive);
   943 			CopyDllFragmented(aFs, source, dest);
   944 			}
   945 		}
   946 	}
   947 
   948 void CopyDllsToSupportedDrives()
   949 	{
   950 	RFs fs;
   951 	test_noError(fs.Connect());
   952 
   953 	CTrapCleanup* cleanup = CTrapCleanup::New();
   954 	test_notNull(cleanup);
   955 	
   956 	CFileMan* fileMan = NULL;
   957 	TRAPD(r, fileMan = CFileMan::NewL(fs));
   958 	test_noError(r);
   959 
   960 	for (TInt i = 1 ; i <= 7 ; ++i)
   961 		CopyDllToSupportedDrives(fs, fileMan, i);
   962 	
   963 	delete fileMan;
   964 	delete cleanup;	
   965 	fs.Close();
   966 	}
   967 
   968 void TestStateTransition(TPageState aNext)
   969 	{
   970 	TPhysState nextPhys = PhysStateFromPageState[aNext];
   971 	RDebug::Printf("%d:  %-12s            -> %-12s", ProcessNum, StateNames[State], StateNames[aNext]);
   972 	TFunc func = StateTransitions[State][aNext];
   973 	test_notNull(func);
   974 	func();
   975 	State = aNext;
   976 	PhysState = nextPhys;
   977 	TestCurrentState();
   978 	}
   979 
   980 void RunPathTest(const TStatePath& aPath, TInt aStart = 0, TInt aEnd = -1)
   981 	{
   982 	if (aEnd == -1)
   983 		aEnd = PathLength(aPath) - 1;
   984 
   985 	// Check we're already in the starting state
   986 	TestPageState(aPath[aStart], PhysStateFromPageState[aPath[aStart]]);
   987 
   988 	for (TInt i = aStart + 1 ; i <= aEnd ; ++i)
   989 		TestStateTransition(aPath[i]);
   990 	}
   991 
   992 void RunUnmapTest(const TStatePath& aPath)
   993 	{
   994 	TInt len = PathLength(aPath);
   995 	
   996 	// Test an unmodified code paged page can be unmapped from all the possible 
   997 	// states it can be in.
   998 	TInt endState = EStateOld;
   999 	if (HaveOldestLists)
  1000 		endState = EStateOldestClean;
  1001 		
  1002 	for (TInt i = EStateUnmapped + 1; i <= endState; ++i)
  1003 		{
  1004 		TPageState target = (TPageState)i;
  1005 		RDebug::Printf("\nUnmap from %s:\n", StateNames[target]);
  1006 
  1007 		TStatePath path;
  1008 		memcpy(path, aPath, sizeof(path));
  1009 
  1010 		TInt j = FindState(path, target) + 1;
  1011 		test_Value(j, j > 0 && j < len + 1);
  1012 		path[j] = EStateUnmapped;
  1013 		
  1014 		RunPathTest(path, 0, j);
  1015 		}
  1016 	}
  1017 
  1018 void GoToState(TPageState aState)
  1019 	{
  1020 	if (LibraryLoaded)
  1021 		{
  1022 		UnloadLibrary();
  1023 		State = EStateUnmapped;
  1024 		PhysState = PhysStateFromPageState[State];
  1025 		}
  1026 		
  1027 	TInt i = FindState(*TestPath, aState);
  1028 	test(i != -1);
  1029 	RunPathTest(*TestPath, 0, i);
  1030 	}
  1031 
  1032 void RunMultiProcessTest()
  1033 	{
  1034 	TStatePath& testPath = *TestPath;
  1035 	TInt len = PathLength(testPath);
  1036 	
  1037 	TInt endState = EStateOld;
  1038 	if (HaveOldestLists)
  1039 		endState = EStateOldestClean;
  1040 	for (TInt i = EStateUnmapped; i <= endState; ++i)
  1041 		{
  1042 		TPageState target = (TPageState)i;
  1043 		RDebug::Printf("\nTesting interaction with second process in state %s:\n", StateNames[target]);
  1044 		
  1045 		GoToState(target);
  1046 		TPageState state2 = testPath[0];  // current state in other process
  1047 		OtherProcess.TestPageState(state2, PhysStateFromPageState[state2]);
  1048 		for (TInt i = 1 ; i < len ; ++i)
  1049 			{
  1050 			TPageState next2 = testPath[i];
  1051 			OtherProcess.TestStateTransition(next2);
  1052 			
  1053 			// Update physical state if affected by transition in other process
  1054 			if ((State == EStateYoung || State == EStateOld || State == EStateOldestClean) &&
  1055 				(state2 != EStateUnmapped && next2 != EStateUnmapped))
  1056 				PhysState = PhysStateFromPageState[next2];
  1057 
  1058 			// Update logical state in this process if affected by transition in other process
  1059 			if (State == EStateYoung && next2 == EStateOld)
  1060 				State = EStateOld;
  1061 			else if (State == EStateOld && next2 == EStateOldestClean)
  1062 				State = EStateOldestClean;
  1063 			else if ((State == EStateYoung || State == EStateOld || State == EStateOldestClean) &&
  1064 					 (state2 == EStateOld  || state2 == EStateOldestClean) && next2 == EStatePagedOut)
  1065 				State = EStatePagedOut;
  1066 
  1067 			RDebug::Printf("%d:  %-12s %-12s", ProcessNum, StateNames[State], PhysStateNames[PhysState]);
  1068 			TestCurrentState();
  1069 			state2 = next2;
  1070 			}
  1071 		}
  1072 
  1073 	if (LibraryLoaded)
  1074 		{
  1075 		UnloadLibrary();
  1076 		State = EStateUnmapped;
  1077 		PhysState = PhysStateFromPageState[State];
  1078 		}	
  1079 	}
  1080 
  1081 void TestReadExportDir()
  1082 	{
  1083 	RLibrary library;
  1084 	test_noError(LoadSpecificLibrary(library, 3, CurrentDrive));
  1085 	TTestFunction func = (TTestFunction)library.Lookup(KTestFunctionOrdinal);
  1086 	test_notNull(func);
  1087 	test_noError(func());
  1088 	library.Close();
  1089 	}
  1090 
  1091 void RunReadExportDirTest()
  1092 	{
  1093 	test.Next(_L("Exercise ReadExportDir with one code seg mapped already into current process"));
  1094 	LoadLibrary();
  1095 	TestReadExportDir();
  1096 	UnloadLibrary();
  1097 
  1098 	test.Next(_L("Exercise ReadExportDir with one code seg mapped into different process"));
  1099 	OtherProcess.Exec(LoadLibrary);
  1100 	TestReadExportDir();
  1101 	OtherProcess.Exec(UnloadLibrary);
  1102 	}
  1103 
  1104 void RunWriteToPagedCodeTest()
  1105 	{
  1106 	test.Next(_L("Test writing to paged code"));
  1107 
  1108 	RMemoryTestLdd memoryTest;
  1109 	test(KErrNone==memoryTest.Open());
  1110 
  1111 	FlushAllPages();
  1112 	TUint8* ptr = (TUint8*)LargeDataStart;
  1113 	while(ptr<LargeDataEnd)
  1114 		{
  1115 		TInt stateBits = GetPageState(ptr);
  1116 		// write to paged out memory should cause exception...
  1117 		test(KErrBadDescriptor==memoryTest.WriteMemory(ptr,0));
  1118 		// page state should be unchanged...
  1119 		test_equal(stateBits,GetPageState(ptr))
  1120 		// page-in in memory...
  1121 		TUint32 value = *(TUint32*)ptr;
  1122 		// page state should be changed...
  1123 		test(stateBits!=GetPageState(ptr));
  1124 		// write to paged out memory should still cause exception...
  1125 		test(KErrBadDescriptor==memoryTest.WriteMemory(ptr,~value));
  1126 		// memory should be unchanged...
  1127 		test(value==*(TUint32*)ptr);
  1128 		ptr += PageSize;
  1129 		}
  1130 
  1131 	memoryTest.Close();
  1132 	}
  1133 
  1134 void RunPageLockingTest()
  1135 	{
  1136 	test.Next(_L("Check locking of code which then gets unloaded"));
  1137 
  1138 	// load test driver...
  1139 	test.Start(_L("Load test driver..."));
  1140 	RDemandPagingTestLdd ldd;
  1141 	TInt r = User::LoadLogicalDevice(KDemandPagingTestLddName);
  1142 	test(r==KErrNone || r==KErrAlreadyExists);
  1143 	test(ldd.Open()==KErrNone);
  1144 
  1145 	// load once to get address that code will be loaded at...
  1146 	test.Next(_L("Load/unload dll"));
  1147 	RLibrary library;
  1148 	test_noError(LoadSpecificLibrary(library, 5, CurrentDrive));
  1149 	TGetAddressOfRelocatedDataFunction func = (TGetAddressOfRelocatedDataFunction)library.Lookup(KGetAddressOfDataFunctionOrdinal);
  1150 	test_notNull(func);
  1151 	library.Close();
  1152 
  1153 	// load again and check it's at the same place...
  1154 	test.Next(_L("Load dll again"));
  1155 	test_noError(LoadSpecificLibrary(library, 5, CurrentDrive));
  1156 	TGetAddressOfRelocatedDataFunction func2 = (TGetAddressOfRelocatedDataFunction)library.Lookup(KGetAddressOfDataFunctionOrdinal);
  1157 	test_equal(func,func2);
  1158 
  1159 	// get address of data in the DLL...
  1160 	test.Next(_L("Get data from DLL"));
  1161 	void* d;
  1162 	void* c;
  1163 	TInt size;
  1164 	void** data = func(size,d,c);
  1165 
  1166 	// lock pages...
  1167 	test.Next(_L("Lock DLL data"));
  1168 	r = ldd.Lock(data,size);
  1169 	test_equal(r,1);
  1170 
  1171 	// check data...
  1172 	test.Next(_L("Check DLL data"));
  1173 	for (TInt i = 0 ; i < size / 4 ; i+=2)
  1174 		{
  1175 		test_equal(c, data[i]);
  1176 		test_equal(d, data[i+1]);
  1177 		}
  1178 
  1179 	// close library...
  1180 	test.Next(_L("Close DLL"));
  1181 	library.Close();
  1182 	User::After(1000000);
  1183 
  1184 	if(!FlexibleMemoryModel) // flexible memory model doesn't actually hog virtual address when locked (pinned)
  1185 		{
  1186 		// load again and check it's at a different place
  1187 		// (because the locked memory is hogging the old place)...
  1188 		test.Next(_L("Check DLL loaded at different address"));
  1189 		test_noError(LoadSpecificLibrary(library, 5, CurrentDrive));
  1190 		func2 = (TGetAddressOfRelocatedDataFunction)library.Lookup(KGetAddressOfDataFunctionOrdinal);
  1191 		test(func!=func2);
  1192 		library.Close();
  1193 		User::After(1000000);
  1194 
  1195 		// unlock pages...
  1196 		test.Next(_L("Unlock DLL data"));
  1197 		r = ldd.Unlock();
  1198 		User::After(1000000);
  1199 
  1200 		// load again and check it's back at the original place
  1201 		// (because the locked memory now gone)...
  1202 		test.Next(_L("Check DLL loaded at original address"));
  1203 		test_noError(LoadSpecificLibrary(library, 5, CurrentDrive));
  1204 		func2 = (TGetAddressOfRelocatedDataFunction)library.Lookup(KGetAddressOfDataFunctionOrdinal);
  1205 		test(func==func2);
  1206 		library.Close();
  1207 		}
  1208 
  1209 	// cleanup...
  1210 	test.Next(_L("Cleanup"));
  1211 	ldd.Close();
  1212 
  1213 	test.End();
  1214 	}
  1215 
  1216 void TestContentsOfPagedDll()
  1217 	{
  1218 	test.Next(_L("Test that the contents of a paged DLL are as expected"));
  1219 
  1220 	RLibrary library2;
  1221 	test_noError(LoadSpecificLibrary(library2, 2, CurrentDrive));
  1222 
  1223 	TGetAddressOfDataFunction func = (TGetAddressOfDataFunction)library2.Lookup(KGetAddressOfDataFunctionOrdinal);
  1224 	test_notNull(func);
  1225 
  1226 	TInt size;
  1227 	TUint* data;
  1228 	data = func(size);
  1229 	test_notNull(data);
  1230 
  1231 	// Data contents are psuedorandom numbers generated according to the following scheme
  1232 	const TInt A = 1664525;
  1233 	const TInt B = 1013904223;
  1234 	TUint v = 23;
  1235 	for (TInt i = 0 ; i < size / 4 ; ++i)
  1236 		{
  1237 		v = A * v + B;
  1238 		test_equal(v, data[i]);
  1239 		}
  1240 
  1241 	library2.Close();
  1242 	}
  1243 
  1244 
  1245 void CheckRelocatableData(RLibrary& library)
  1246 	{
  1247 	TGetAddressOfRelocatedDataFunction func = (TGetAddressOfRelocatedDataFunction)library.Lookup(KGetAddressOfDataFunctionOrdinal);
  1248 	test_notNull(func);
  1249 	void* d;
  1250 	void* c;
  1251 	TInt size;
  1252 	void** data = func(size,d,c);
  1253 	test_equal(d, data);
  1254 	for (TInt i = 0 ; i < size / 4 ; i+=2)
  1255 		{
  1256 		test_equal(c, data[i]);
  1257 		test_equal(d, data[i+1]);
  1258 		}
  1259 	}
  1260 
  1261 
  1262 void OtherProcessCheckRelocatableData()
  1263 	{
  1264 	RLibrary library;
  1265 	test_noError(LoadSpecificLibrary(library, 7, CurrentDrive));
  1266 	CheckRelocatableData(library);
  1267 	library.Close();
  1268 	}
  1269 
  1270 
  1271 void TestContentsOfPagedDllWithRelocatedData()
  1272 	{
  1273 	test.Next(_L("Test relocated const data in DLL"));
  1274 	PagingInfo::ResetBenchmarks();
  1275 	RLibrary library;
  1276 	test_noError(LoadSpecificLibrary(library, 5, CurrentDrive));
  1277 	CheckRelocatableData(library);
  1278 	library.Close();
  1279 	PagingInfo::PrintBenchmarks();	// worst case fixups
  1280 
  1281 	test.Next(_L("Test relocated writable data in DLL"));
  1282 	test_noError(LoadSpecificLibrary(library, 6, CurrentDrive));
  1283 	CheckRelocatableData(library);
  1284 	library.Close();
  1285 
  1286 	test.Next(_L("Test relocated writable data in dependent DLL"));
  1287 	test_noError(LoadSpecificLibrary(library, 7, CurrentDrive));
  1288 	CheckRelocatableData(library);
  1289 	library.Close();
  1290 
  1291 	test.Next(_L("Test relocated writable data in preloaded dependent DLL"));
  1292 	RLibrary library2;
  1293 	test_noError(LoadSpecificLibrary(library2, 6, CurrentDrive));
  1294 	test_noError(LoadSpecificLibrary(library, 7, CurrentDrive));
  1295 	CheckRelocatableData(library);
  1296 	library.Close();
  1297 	library2.Close();
  1298 
  1299 	test.Next(_L("Test relocated writable data in preloaded dependent DLL opened in other process"));
  1300 	test_noError(LoadSpecificLibrary(library2, 6, CurrentDrive));
  1301 	OtherProcess.Exec(OtherProcessCheckRelocatableData);
  1302 	library2.Close();
  1303 	}
  1304 
  1305 
  1306 TInt RandomAccessFunc(TAny* aArg)
  1307 	{
  1308 	const TUint8* dataStart = LargeDataStart;
  1309 	const TUint8* dataEnd = LargeDataEnd;	
  1310 	TInt size = dataEnd - dataStart;
  1311 	TUint32 random = (User::FastCounter() << 8) | ProcessNum;
  1312 	TInt i = 0;
  1313 	while (!RandomAccessKill)
  1314 		{
  1315 		random = random*69069+1;
  1316 		TInt offset = random % size;
  1317 		TInt value = dataStart[offset];
  1318 		if (offset != 0 && value != 0)
  1319 			return KErrGeneral;
  1320 		++i;
  1321 		}
  1322 
  1323 	RDebug::Printf("%d: Performed %d accesses", ProcessNum, i);
  1324 	return KErrNone;
  1325 	}
  1326 
  1327 void StartRandomAccessThread(TThreadPriority aPriority)
  1328 	{
  1329 	RandomAccessKill = EFalse;
  1330 	test_noError(RandomAccessThread.Create(_L("RandomAccessThread"), RandomAccessFunc, 4096, NULL, 0));
  1331 	RDebug::Printf("%d: starting thread with priority %d", ProcessNum, aPriority);
  1332 	RandomAccessThread.SetPriority(aPriority);
  1333 	RandomAccessThread.Resume();
  1334 	}
  1335 
  1336 void KillRandomAccessThread()
  1337 	{
  1338 	test_equal(EExitPending, RandomAccessThread.ExitType());
  1339 	TRequestStatus status;
  1340 	RandomAccessThread.Logon(status);
  1341 	RandomAccessKill = ETrue;
  1342 	User::WaitForRequest(status);
  1343 	test_equal(EExitKill, RandomAccessThread.ExitType());
  1344 	test_equal(0, RandomAccessThread.ExitReason());
  1345 	RandomAccessThread.Close();
  1346 	PagedLibrary.Close();
  1347 	}
  1348 
  1349 void TestLargeDll(TInt aDelay)
  1350 	{
  1351 	test.Next(_L("Test random access to a large dll"));
  1352 	StartRandomAccessThread(EPriorityLess);
  1353 	User::After(aDelay * 1000000);
  1354 	KillRandomAccessThread();
  1355 	}
  1356 
  1357 void TestKillThreadWhilePaging()
  1358 	{
  1359  	test.Next(_L("Test killing a thread while it is taking paging faults"));
  1360 	for (TInt i = 0 ; i < 50 ; ++i)
  1361 		{
  1362 		RDebug::Printf("  iteration %d", i);
  1363 		StartRandomAccessThread(EPriorityLess);
  1364 		User::After(10000);  // time for ~ 10 paging requests
  1365 		test_equal(EExitPending, RandomAccessThread.ExitType());
  1366 		TRequestStatus status;
  1367 		RandomAccessThread.Logon(status);
  1368 		RandomAccessThread.Terminate(666);
  1369 		User::WaitForRequest(status);
  1370 		test_equal(EExitTerminate, RandomAccessThread.ExitType());
  1371 		test_equal(666, RandomAccessThread.ExitReason());
  1372 		RandomAccessThread.Close();
  1373 		PagedLibrary.Close();
  1374 		}
  1375 	}
  1376 
  1377 void TestUnloadDllWhilePaging()
  1378 	{
  1379  	test.Next(_L("Test unloading a library while another thread is accessing it"));
  1380 	OtherProcess.Exec(UnloadLargeLibrary);
  1381 	for (TInt i = 0 ; i < 50 ; ++i)
  1382 		{
  1383 		RDebug::Printf("  iteration %d", i);
  1384 		StartRandomAccessThread(EPriorityLess);
  1385 		User::After(10000);  // time for ~ 10 paging requests
  1386 		test_equal(EExitPending, RandomAccessThread.ExitType());
  1387 		TRequestStatus status;
  1388 		RandomAccessThread.Logon(status);
  1389 		UnloadLargeLibrary();
  1390 		PagedLibrary.Close();
  1391 		User::WaitForRequest(status);
  1392 		test_equal(EExitPanic, RandomAccessThread.ExitType());
  1393 		test_equal(3, RandomAccessThread.ExitReason());  // KERN-EXEC 3
  1394 		RandomAccessThread.Close();
  1395 		LoadLargeLibrary();
  1396 		}
  1397 	OtherProcess.Exec(LoadLargeLibrary);
  1398 	}
  1399 
  1400 void PrintElapsedTime(TTime& aStartTime)
  1401 	{		
  1402 	TTime timeNow;
  1403 	timeNow.UniversalTime();
  1404 	TTimeIntervalSeconds elapsed;
  1405 	test_noError(timeNow.SecondsFrom(aStartTime, elapsed));
  1406 	test.Printf(_L("%d seconds elapsed\n"), elapsed.Int());
  1407 	}
  1408 
  1409 void TestManyProcesses(TInt aCount, TInt aDelay, TInt aPriorities = 1)
  1410 	{
  1411 	TBuf<128> name;
  1412 	name.AppendFormat(_L("Test accessing paged code from %d processes at %d priority level(s) for %d seconds"),
  1413 					  aCount, aPriorities, aDelay);
  1414 	test.Next(name);
  1415 
  1416 	TTime startTime;
  1417 	startTime.UniversalTime();
  1418 
  1419 	// start subprocesses and let them initialise
  1420 	RArray<RTestSession> processes;
  1421 	TInt threadsAtEachPriority = aCount / aPriorities;
  1422 	for (TInt i = 0 ; i < aCount ; ++i)
  1423 		{
  1424 		RTestSession sess;
  1425 		StartOtherProcess(i + 3, sess);
  1426 		test_noError(processes.Append(sess));
  1427 		sess.SetCurrentDrive(CurrentDrive);
  1428 		}
  1429 	test.Printf(_L("Started subprocesses: "));
  1430 	PrintElapsedTime(startTime);
  1431 	
  1432 	// then start random accesses to paged memory
  1433 	for (TInt i = 0 ; i < aCount ; ++i)
  1434 		{
  1435 		TThreadPriority pri;
  1436 		switch (i / threadsAtEachPriority)
  1437 			{
  1438 			case 0:  pri = EPriorityLess; break;
  1439 			default: pri = EPriorityMuchLess; break;
  1440 			}
  1441 		processes[i].StartRandomAccessThread(pri);
  1442 		}
  1443 	test.Printf(_L("Started threads: "));
  1444 	PrintElapsedTime(startTime);
  1445 
  1446 	test_noError(PagingInfo::ResetAll(LocalDriveNumber,EMediaPagingStatsCode));
  1447 	User::After(aDelay * 1000000);
  1448 	test_noError(PagingInfo::PrintAll(LocalDriveNumber,EMediaPagingStatsCode));
  1449 
  1450 	test.Printf(_L("Killing subprocesses: "));
  1451 	PrintElapsedTime(startTime);
  1452 		
  1453 	for (TInt i = 0 ; i < aCount ; ++i)
  1454 		{
  1455 		processes[i].Exec(KillRandomAccessThread);
  1456 		processes[i].Kill();
  1457 		processes[i].Close();
  1458 		}
  1459 
  1460 	test.Printf(_L("Test finished: "));
  1461 	PrintElapsedTime(startTime);
  1462 
  1463 	processes.Close();
  1464 	}
  1465 
  1466 void TestCacheSize()
  1467 	{
  1468 	test.Next(_L("Test cache size within bounds"));
  1469 	TUint sizeMin = 0;
  1470 	TUint sizeMax = 0;
  1471 	TUint currentSize = 0;
  1472 	DPTest::CacheSize(sizeMin,sizeMax,currentSize);
  1473 	test.Printf(_L("  minimum size == %d pages\n"), sizeMin >> 12);
  1474 	test.Printf(_L("  maximum size == %d pages\n"), sizeMax >> 12);
  1475 	test.Printf(_L("  current size == %d pages\n"), currentSize >> 12);
  1476 	test(currentSize >= sizeMin);
  1477 	test(currentSize <= sizeMax);
  1478 	}
  1479 
  1480 void RunUnalignedAliasAccessTest()
  1481 	{
  1482 	test.Next(_L("Test accesses to aliased non-word-aligned data"));
  1483 	
  1484 	for (TInt size = 0 ; size <= 28 ; ++ size)
  1485 		{
  1486 		test.Printf(_L("  size = %d:"), size);
  1487 		for (TInt align = 0 ; align <= 3 ; ++align)
  1488 			{
  1489 			test.Printf(_L(" %d"), align);
  1490 			TPtrC8 des(LargeDataStart + align, size);
  1491 			FlushAllPages();
  1492 			OtherProcess.DesRead(des);			
  1493 			}
  1494 		test.Printf(_L("\n"));
  1495 		}
  1496 	}
  1497 
  1498 void TestCodeChunkCreated()
  1499 	{
  1500 	LoadLibrary();
  1501 	TAny* func = (TAny*)PagedLibrary.Lookup(KTestFunctionOrdinal);
  1502 	test_notNull(func);
  1503 	FlushAllPages();
  1504 	test(GetPageState(func) & EPageStateCodeChunkPresent);
  1505 	UnloadLibrary();
  1506 	FlushAllPages();
  1507 	test(!(GetPageState(func) & EPageStateCodeChunkPresent));
  1508 	}
  1509 
  1510 void TestRepeatedLoading()
  1511 	{
  1512 	test.Next(_L("Test loading/unloading a DLL doesn't leak address space"));
  1513 
  1514 	for (TInt dll = 1 ; dll <= 7 ; ++dll)
  1515 		{
  1516 		test.Printf(_L("  trying dll %d...\n"), dll);
  1517 		
  1518 		RLibrary library;
  1519 		test_noError(LoadSpecificLibrary(library, dll, CurrentDrive));
  1520 		TLibraryFunction func1 = library.Lookup(1);
  1521 		library.Close();
  1522 		
  1523 		test_noError(LoadSpecificLibrary(library, dll, CurrentDrive));
  1524 		TLibraryFunction func2 = library.Lookup(1);
  1525 		library.Close();
  1526 
  1527 		test_equal(func1, func2);
  1528 		}
  1529 	}
  1530 
  1531 void RunDriveIndependantTests()
  1532 	{
  1533 	if (MultipleMemoryModel)
  1534 		{
  1535 		test.Next(_L("Test code chunk created and destroyed correctly"));
  1536 		TestCodeChunkCreated();
  1537 		}
  1538 	
  1539 	SetCurrentDrive('Z');
  1540 
  1541 	if (CanForcePageOut)
  1542 		{
  1543 		test.Next(_L("Test accessing pages by executing code"));
  1544 		AccessMethod = EAccessExec;
  1545 		RunPathTest(*TestPath);
  1546 
  1547 		test.Next(_L("Test accessing pages by reading code"));
  1548 		AccessMethod = EAccessRead;
  1549 		RunPathTest(*TestPath);
  1550 
  1551 		if (!MovingMemoryModel)
  1552 			{
  1553 			test.Next(_L("Test accessing pages by reading code from another process via an alias"));
  1554 			AccessMethod = EAccessAliasRead;
  1555 			RunPathTest(*TestPath);
  1556 			}
  1557 
  1558 		test.Next(_L("Test unmapping paged code"));
  1559 		AccessMethod = EAccessExec;
  1560 		RunUnmapTest(*TestPath);
  1561 
  1562 		if (!MovingMemoryModel)
  1563 			{
  1564 			test.Next(_L("Test interactions between two processes"));
  1565 			RunMultiProcessTest();
  1566 			}
  1567 		}
  1568 
  1569 	RunReadExportDirTest();
  1570 	RunPageLockingTest();
  1571 	RunWriteToPagedCodeTest();
  1572 	RunUnalignedAliasAccessTest();
  1573 	TestRepeatedLoading();
  1574 	}
  1575 
  1576 void RunPerDriveTests()
  1577 	{
  1578 	TestContentsOfPagedDll();
  1579 	TestContentsOfPagedDllWithRelocatedData();
  1580 	TestKillThreadWhilePaging();
  1581 	TestUnloadDllWhilePaging();
  1582 	
  1583 	TestLargeDll(5);
  1584 
  1585 	TestManyProcesses(2, 5, 1);
  1586 	TestManyProcesses(5, 10, 1);
  1587 	TestManyProcesses(10, 20, 1);
  1588 	TestManyProcesses(5, 10, 2);
  1589 	TestManyProcesses(50, 2, 1);
  1590 	}
  1591 
  1592 void RunAllTests()
  1593 	{
  1594 
  1595 	RunDriveIndependantTests();
  1596 	
  1597 	for (TInt i = 0 ; i < SupportedDrives.Count() ; ++i)
  1598 		{
  1599 		SetCurrentDrive(SupportedDrives[i].iDriveLetter);
  1600 		OtherProcess.SetCurrentDrive(CurrentDrive);
  1601 
  1602 		TBuf<32> message;
  1603 		message.AppendFormat(_L("Running tests on drive %c:"), (TUint) SupportedDrives[i].iDriveLetter);
  1604 		test.Next(message);		
  1605 		RunPerDriveTests();
  1606 		}
  1607 	TestCacheSize();
  1608 	}
  1609 
  1610 // Server implementation ///////////////////////////////////////////////////////
  1611 
  1612 class CTestSession : public CSession2
  1613 	{
  1614 public:
  1615 	virtual void ServiceL(const RMessage2& aMessage);
  1616 	};
  1617 
  1618 void CTestSession::ServiceL(const RMessage2& aMessage)
  1619 	{
  1620 	TInt r = KErrNone;
  1621 	switch (aMessage.Function())
  1622 		{
  1623 		case RTestSession::EKill:
  1624 			CActiveScheduler::Stop();
  1625 			break;
  1626 
  1627 		case RTestSession::EExec:
  1628 			((TFunc)aMessage.Int0())();		   
  1629 			break;
  1630 
  1631 		case RTestSession::ESetCurrentDrive:
  1632 			SetCurrentDrive(aMessage.Int0());
  1633 			break;
  1634 
  1635 		case RTestSession::EDesRead:
  1636 			{
  1637 			TBuf8<32> buf;
  1638 			if (buf.MaxSize() < aMessage.GetDesLength(0))
  1639 				r = KErrArgument;
  1640 			else
  1641 				r = aMessage.Read(0, buf);
  1642 			}
  1643 			break;
  1644 
  1645 		case RTestSession::ETestPageState:
  1646 			TestPageState((TPageState)aMessage.Int0(), (TPhysState)aMessage.Int1());
  1647 			break;
  1648 			
  1649 		case RTestSession::ETestStateTransition:
  1650 			TestStateTransition((TPageState)aMessage.Int0());
  1651 			break;
  1652 
  1653 		case RTestSession::EStartRandomAccessThread:
  1654 			StartRandomAccessThread((TThreadPriority)aMessage.Int0());
  1655 			break;
  1656 
  1657 		default:
  1658 			r = KErrNotSupported;
  1659 			break;
  1660 		}
  1661 	
  1662 	aMessage.Complete(r);
  1663 	}
  1664 
  1665 class CTestServer : public CServer2
  1666 	{
  1667 public:
  1668 	CTestServer() : CServer2(0) { }
  1669 	virtual CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const;
  1670  };
  1671 
  1672 CSession2* CTestServer::NewSessionL(const TVersion& /*aVersion*/,const RMessage2& /*aMessage*/) const
  1673 	{
  1674 	return new (ELeave) CTestSession();
  1675 	}
  1676 
  1677 void DoStartServerL()
  1678 	{
  1679 	CActiveScheduler* activeScheduler = new CActiveScheduler;
  1680 	test_notNull(activeScheduler);
  1681 	CActiveScheduler::Install(activeScheduler);
  1682 	CTestServer* server = new CTestServer();
  1683 	test_notNull(server);
  1684 	TBuf<32> name;
  1685 	name.AppendFormat(_L("%S-%d"), &KServerName, ProcessNum);
  1686 	test_noError(server->Start(name));
  1687 	RProcess().Rendezvous(KErrNone);
  1688 	CActiveScheduler::Start();
  1689 	delete server;
  1690 	delete activeScheduler;
  1691 	}
  1692 
  1693 void StartServer()
  1694 	{
  1695 	CTrapCleanup* cleanupStack = CTrapCleanup::New();
  1696 	test_notNull(cleanupStack);
  1697 	TRAPD(leaveError,DoStartServerL());	
  1698 	test_noError(leaveError);
  1699 	delete cleanupStack;
  1700 	}
  1701 
  1702 void SecondaryProcess()
  1703 	{
  1704 	TBuf<16> cmd;
  1705 	User::CommandLine(cmd);
  1706 	TLex lex(cmd);
  1707 	lex.Val(ProcessNum);
  1708 
  1709 	TBuf<32> name;
  1710 	name.AppendFormat(_L("t_codepaging-%d"), ProcessNum);
  1711 	RProcess me;
  1712 	test_noError(me.RenameMe(name));
  1713 	
  1714 	GetSupportedDrives(EFalse);
  1715 	Initialise();
  1716 	SetCurrentDrive('Z');
  1717 	StartServer();
  1718 	}
  1719 
  1720 void MainProcess()
  1721 	{
  1722 	ProcessNum = 1;
  1723 		
  1724 	test.Title();
  1725 	test.Start(_L("Code paging tests"));
  1726 	
  1727 	TUint32 memModelAttributes=UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL);
  1728 	TUint32 pagingPolicy = E32Loader::PagingPolicy();
  1729 	TBool codePagingSupported = (memModelAttributes & EMemModelAttrCodePaging) != 0;
  1730 	TBool pagingPolicyAllowsPaging = pagingPolicy != EKernelConfigCodePagingPolicyNoPaging;
  1731 	test_Equal(codePagingSupported, pagingPolicyAllowsPaging);
  1732 	if(!codePagingSupported)
  1733 		{
  1734 		test.Printf(_L("TESTS NOT RUN - Code paging not enabled on system.\n"));
  1735 		test.End();
  1736 		return;
  1737 		}
  1738 	
  1739 	GetSupportedDrives(ETrue);
  1740 	test(SupportedDrives.Count() > 0);
  1741 
  1742 	// Turn off evil lazy dll unloading
  1743 	RLoader l;
  1744 	test(l.Connect()==KErrNone);
  1745 	test(l.CancelLazyDllUnload()==KErrNone);
  1746 	l.Close();
  1747 
  1748 	CopyDllsToSupportedDrives();
  1749 
  1750 	Initialise();
  1751 
  1752 	StartOtherProcess(2, OtherProcess);
  1753 
  1754 	RunAllTests();
  1755 
  1756 	OtherProcess.Kill();
  1757 	OtherProcess.Close();
  1758 	test.End();
  1759 	}
  1760 
  1761 
  1762 TInt E32Main()
  1763 	{
  1764 	if (User::CommandLineLength() == 0)
  1765 		MainProcess();
  1766 	else
  1767 		SecondaryProcess();
  1768 	
  1769 	return 0;
  1770 	}