kerneltest/e32test/mmu/t_demandpaging.cpp
changeset 245 647ab20fee2e
parent 176 af6ec97d9189
child 257 3e88ff8f41d5
child 293 0659d0e1a03c
--- a/kerneltest/e32test/mmu/t_demandpaging.cpp	Thu Aug 12 11:55:14 2010 +0100
+++ b/kerneltest/e32test/mmu/t_demandpaging.cpp	Thu Aug 12 12:51:24 2010 +0100
@@ -75,6 +75,7 @@
 #include <e32kpan.h>
 #include <e32property.h>
 #include <e32rom.h>
+#include <u32hal.h>
 #include "d_memorytest.h"
 #include "d_demandpaging.h"
 #include "d_gobble.h"
@@ -108,6 +109,14 @@
 // A descriptor whose header is in paged memory (actually just a pointer to a zero word)
 TDesC8* PagedHeaderDes = NULL;
 
+// An area of paged rom if rom paging is supported, or zero
+TUint8* RomPagedBuffer = NULL;
+TInt RomPagedBufferSize = 0;
+
+// An area of paged code if code paging is supported, or zero
+TUint8* CodePagedBuffer = NULL;
+TInt CodePagedBufferSize = 0;
+
 // A data paged chunk used as a buffer, if data paging is supported
 _LIT(KChunkName, "t_demandpaging chunk");
 RChunk DataPagedChunk;
@@ -119,7 +128,13 @@
 	return *aPtr;
 	}
 
+TUint8 WriteByte(volatile TUint8* aPtr)
+	{
+	return *aPtr = 1;
+	}
+
 #define READ(a) ReadByte((volatile TUint8*)(a))
+#define WRITE(a) WriteByte((volatile TUint8*)(a))
 
 void ThrashPaging(TUint aBytes)
 	{
@@ -435,12 +450,13 @@
 	EDesContentPaged
 	};
 
-enum TRealtimeOutcome
+enum TThreadOutcome
 	{
 	ENoError,
 	EBadDescriptor,
 	EServerTerminated,
-	ERealtimePanic
+	ERealtimePanic,
+	EAbortPanic
 	};
 
 class RTestSession : public RSessionBase
@@ -626,7 +642,7 @@
 	return r;
 	}
 
-void TestRealtimeOutcome(RThread aThread, TRealtimeOutcome aOutcome)
+void TestThreadOutcome(RThread aThread, TThreadOutcome aOutcome)
 	{
 	switch(aOutcome)
 		{
@@ -651,6 +667,12 @@
 			test_Equal(EIllegalFunctionForRealtimeThread, aThread.ExitReason());
 			break;
 
+		case EAbortPanic:
+			test_Equal(EExitPanic, aThread.ExitType());
+			// category for paging errors tested elsewhere
+			test_Equal(KErrAbort, aThread.ExitReason());
+			break;
+
 		default:
 			test(EFalse);
 		}
@@ -661,8 +683,9 @@
 				  TIpcObjectPaged aServerPaged,
 				  User::TRealtimeState aClientState,
 				  User::TRealtimeState aServerState,
-				  TRealtimeOutcome aClientOutcome,
-				  TRealtimeOutcome aServerOutcome)
+				  TThreadOutcome aClientOutcome,
+				  TThreadOutcome aServerOutcome,
+				  TPagingErrorContext aSimulatedError = EPagingErrorContextNone)
 	{
 	test.Printf(_L("TestPagedIpc %d %d %d %d %d %d %d\n"), aIpcDir, aClientPaged, aServerPaged,
 				aClientState, aServerState, aClientOutcome, aServerOutcome);
@@ -688,6 +711,11 @@
 	name = clientThread.Name();
 	test.Printf(_L("  client: %S\n"), &name);
 	clientThread.Logon(clientStatus);
+
+	// set up simulated failure if specifed
+	if (aSimulatedError != EPagingErrorContextNone)
+		test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalDebugSetFail, (TAny*)aSimulatedError, 0));
+	
 	clientThread.Resume();
 
 	User::WaitForRequest(serverStatus);
@@ -696,9 +724,13 @@
 
 	User::WaitForRequest(clientStatus);
 	test.Printf(_L("  client exit type is %d %d\n"), clientThread.ExitType(), clientThread.ExitReason());
+	
+	// cancel any simulated failure
+	if (aSimulatedError != EPagingErrorContextNone)
+		test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalDebugSetFail, (TAny*)EPagingErrorContextNone, 0));
 
-	TestRealtimeOutcome(serverThread, aServerOutcome);
-	TestRealtimeOutcome(clientThread, aClientOutcome);
+	TestThreadOutcome(serverThread, aServerOutcome);
+	TestThreadOutcome(clientThread, aClientOutcome);
 	
 	CLOSE_AND_WAIT(serverThread);
 	CLOSE_AND_WAIT(clientThread);
@@ -717,7 +749,7 @@
 	return KErrNone;
 	}
 
-TInt RunTestThread(User::TRealtimeState aType, TRealtimeOutcome aOutcome)
+TInt RunTestThread(User::TRealtimeState aType, TThreadOutcome aOutcome)
 	{
 	RThread thread;
 	TInt r=thread.Create(KNullDesC, &TestThreadFunction, 0x1000, NULL, (TAny*)aType);
@@ -729,7 +761,7 @@
 		return s.Int();
 	thread.Resume();
 	User::WaitForRequest(s);
-	TestRealtimeOutcome(thread, aOutcome);
+	TestThreadOutcome(thread, aOutcome);
 	CLOSE_AND_WAIT(thread);
 	return KErrNone;
 	}
@@ -811,6 +843,141 @@
 	test(KErrNone==DPTest::SetCacheSize(0,0));
 	}
 
+enum TPageFaultType
+	{
+	EPageFaultRomRead,
+	EPageFaultCodeRead,
+	EPageFaultDataRead,
+	EPageFaultDataWrite,
+	};
+
+
+TInt TestPagingErrorThreadFunction(TAny* aArg)
+	{
+	TUint8* ptr = (TUint8*)((TUint)aArg & ~1);
+	TBool write = ((TUint)aArg & 1) != 0;
+
+	if (write)
+		{
+		WRITE(ptr);
+		return DPTest::FlushCache();
+		}
+	else
+		{
+		READ(ptr);
+		return KErrNone;
+		}
+	}
+
+void TestPagingError(TPageFaultType aPageFaultType,
+					 TPagingErrorContext aSimulatedError,
+					 TExitType aExpectedExitType,
+					 const TDesC& aExpectedExitCategory,
+					 TInt aExpectedExitReason)
+	{
+	test.Printf(_L("TestPagingError %d %d %d \"%S\" %d\n"), aPageFaultType, aSimulatedError,
+				aExpectedExitType, &aExpectedExitCategory, aExpectedExitReason);
+	
+	TUint8* ptr;
+	TBool write;
+
+	switch(aPageFaultType)
+		{
+		case EPageFaultRomRead:   ptr = RomPagedBuffer;  write = EFalse; break;
+		case EPageFaultCodeRead:  ptr = CodePagedBuffer; write = EFalse; break;
+		case EPageFaultDataRead:  ptr = DataPagedBuffer; write = EFalse; break;
+		case EPageFaultDataWrite: ptr = DataPagedBuffer; write = ETrue;  break;
+		default: test(EFalse); return;
+		}
+
+	if (ptr == NULL) return;  // specified type of paging is not enabled
+
+	if (write)
+		READ(ptr);  // ensure data to be written is paged in
+	else
+		test_KErrNone(DPTest::FlushCache());  // ensure data to be read is paged out
+
+	// set up simulated failure
+	test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalDebugSetFail, (TAny*)aSimulatedError, 0));
+	
+	RThread thread;
+	TAny* arg = (TAny*)((TUint)ptr | (write ? 1 : 0));
+	test_KErrNone(thread.Create(KNullDesC, &TestPagingErrorThreadFunction, 0x1000, NULL, arg));
+	TRequestStatus s;
+	thread.Logon(s);
+	test_Equal(KRequestPending, s.Int());
+	thread.Resume();
+	User::WaitForRequest(s);
+
+	// cancel any simulated failure
+	test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalDebugSetFail, (TAny*)EPagingErrorContextNone, 0));
+
+	TExitCategoryName exitCategory = thread.ExitCategory();
+	test.Printf(_L("  thread exit type is %d \"%S\" %d\n"),
+				thread.ExitType(), &exitCategory, thread.ExitReason());
+	
+	test_Equal(aExpectedExitType, thread.ExitType());
+	if (aExpectedExitType == EExitPanic)
+		test_Equal(0, aExpectedExitCategory.Compare(exitCategory));	
+	test_Equal(aExpectedExitReason, thread.ExitReason());	
+	
+	CLOSE_AND_WAIT(thread);
+	}
+
+void CreateDataPagedChunk()
+	{
+	TChunkCreateInfo createInfo;
+	createInfo.SetNormal(KMinBufferSize, KMinBufferSize);
+	createInfo.SetPaging(TChunkCreateInfo::EPaged);
+	createInfo.SetOwner(EOwnerProcess);
+	createInfo.SetGlobal(KChunkName);
+	test_KErrNone(DataPagedChunk.Create(createInfo));
+	test(DataPagedChunk.IsPaged()); // this is only ever called if data paging is supported
+	DataPagedBuffer = (TUint8*)DataPagedChunk.Base();
+	}
+
+void TestPagingErrors()
+	{
+	// test what happens when the paging system encounters errors such as failure when accessing
+	// media or decompressing paged data
+
+	//              page fault type:     simulated error:                   exit type:  exit category:          exit reason:
+	TestPagingError(EPageFaultRomRead,   EPagingErrorContextNone,           EExitKill,  KNullDesC,              KErrNone);
+	TestPagingError(EPageFaultRomRead,   EPagingErrorContextRomRead,        EExitPanic, _L("PAGED-ROM-READ"),   KErrAbort);
+	TestPagingError(EPageFaultRomRead,   EPagingErrorContextRomDecompress,  EExitPanic, _L("PAGED-ROM-COMP"),   KErrAbort);
+	
+	TestPagingError(EPageFaultCodeRead,  EPagingErrorContextNone,           EExitKill,  KNullDesC,              KErrNone);
+	TestPagingError(EPageFaultCodeRead,  EPagingErrorContextCodeRead,       EExitPanic, _L("PAGED-CODE-READ"),  KErrAbort);
+	TestPagingError(EPageFaultCodeRead,  EPagingErrorContextCodeDecompress, EExitPanic, _L("PAGED-CODE-COMP"),  KErrAbort);
+
+	if (DataPagedBuffer)
+		{
+		// Note WDP write faults are only reported on the next read
+		WRITE(DataPagedBuffer);  // ensure page is not blank and will be read from swap
+		
+		//              page fault type:     simulated error:               exit type:  exit category:          exit reason:
+		TestPagingError(EPageFaultDataRead,  EPagingErrorContextNone,       EExitKill,  KNullDesC,              KErrNone);
+		TestPagingError(EPageFaultDataRead,  EPagingErrorContextDataRead,   EExitPanic, _L("PAGED-DATA-READ"),  KErrAbort);
+		TestPagingError(EPageFaultDataWrite, EPagingErrorContextDataWrite,  EExitKill,  KNullDesC,              KErrNone);
+		TestPagingError(EPageFaultDataRead,  EPagingErrorContextNone,       EExitPanic, _L("PAGED-DATA-WRITE"), KErrAbort);
+
+		// this will now always panic when we try to access the first page so destroy and re-create it
+		DataPagedChunk.Close();
+		CreateDataPagedChunk();
+		}
+
+	// test attribution of errors during IPC
+	TPagingErrorContext error;
+	if (RomPagedBuffer)
+		error = EPagingErrorContextRomRead;
+	else if (CodePagedBuffer)
+		error = EPagingErrorContextCodeRead;
+	else
+		error = EPagingErrorContextDataRead;
+	//           ipc dir:     client paged:     server paged:  client state:            server state:            client outcome:  server outcome:
+	TestPagedIpc(EServerRead, EDesContentPaged, ENothingPaged, User::ERealtimeStateOff, User::ERealtimeStateOff, EAbortPanic,     EBadDescriptor,  error);
+	}
+
 void TestLock()
 	{
 	// make sure live list is big enough
@@ -1287,50 +1454,45 @@
 	test.Start(_L("Initialisation"));
 	
 	if (DPTest::Attributes() & DPTest::ERomPaging)
+		{
 		test.Printf(_L("Rom paging supported\n"));
+		TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
+		test(romHeader->iPageableRomStart);
+		// todo: for some reason the first part of page of paged rom doesn't seem to get paged out
+		// when we flush the paging cache, hence RomPagedBuffer starts some way into this
+		RomPagedBuffer = (TUint8*)romHeader + romHeader->iPageableRomStart + 64 * PageSize; 
+		RomPagedBufferSize = romHeader->iPageableRomSize - 64 * PageSize;
+		test(RomPagedBufferSize > 0);
+		}
+	
 	if (DPTest::Attributes() & DPTest::ECodePaging)
+		{
 		test.Printf(_L("Code paging supported\n"));
+		test_KErrNone(PagedLibrary.Load(KTCodePagingDll4));		
+		TGetAddressOfDataFunction func = (TGetAddressOfDataFunction)PagedLibrary.Lookup(KGetAddressOfDataFunctionOrdinal);
+		CodePagedBuffer = (TUint8*)func(CodePagedBufferSize);
+		test_NotNull(CodePagedBuffer);
+		test(CodePagedBufferSize > KMinBufferSize);
+		}
+	
 	if (DPTest::Attributes() & DPTest::EDataPaging)
 		{
 		test.Printf(_L("Data paging supported\n"));
 		DataPagingSupported = ETrue;
-		TChunkCreateInfo createInfo;
-		createInfo.SetNormal(KMinBufferSize, KMinBufferSize);
-		createInfo.SetPaging(TChunkCreateInfo::EPaged);
-		createInfo.SetOwner(EOwnerProcess);
-		createInfo.SetGlobal(KChunkName);
-		test_KErrNone(DataPagedChunk.Create(createInfo));
-		test(DataPagedChunk.IsPaged()); // this is only ever called if data paging is supported
-		DataPagedBuffer = (TUint8*)DataPagedChunk.Base();
+		CreateDataPagedChunk();
 		}
 
 	if (DPTest::Attributes() & DPTest::ERomPaging)
 		{
 		// Use paged part of rom for testing
-		TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
-		test(romHeader->iPageableRomStart);
-		// todo: for some reason the first part of page of paged rom doesn't seem to get paged out
-		// when we flush the paging cache, hence LargeBuffer starts some way into this
-		LargeBuffer = (TUint8*)romHeader + romHeader->iPageableRomStart + 64 * PageSize; 
-		LargeBufferSize = romHeader->iPageableRomSize - 64 * PageSize;
-		test(LargeBufferSize > 0);
-		// Find a zero word in rom to set PagedHeaderDes to
-		TUint* ptr = (TUint*)LargeBuffer;
-		TUint* end = (TUint*)(LargeBuffer + LargeBufferSize);
-		while (*ptr && ptr < end)
-			++ptr;
-		test(*ptr == 0);
-		test.Printf(_L("Found zero word at %08x\n"), ptr);
-		PagedHeaderDes = (TDesC8*)ptr;
+		LargeBuffer = RomPagedBuffer;
+		LargeBufferSize = RomPagedBufferSize;
 		}
 	else if (DPTest::Attributes() & DPTest::ECodePaging)
 		{
 		// Use code paged DLL for testing
-		test_KErrNone(PagedLibrary.Load(KTCodePagingDll4));		
-		TGetAddressOfDataFunction func = (TGetAddressOfDataFunction)PagedLibrary.Lookup(KGetAddressOfDataFunctionOrdinal);
-		LargeBuffer = (TUint8*)func(LargeBufferSize);
-		test_NotNull(LargeBuffer);
-		PagedHeaderDes = (TDesC8*)LargeBuffer + 4;
+		LargeBuffer = CodePagedBuffer;
+		LargeBufferSize = CodePagedBufferSize;
 		}
 	else if (DPTest::Attributes() & DPTest::EDataPaging)
 		{
@@ -1344,7 +1506,16 @@
 		test.End();
 		return 0;
 		}
-		
+	
+	// Find a paged zero word to set PagedHeaderDes to
+	TUint* ptr = (TUint*)LargeBuffer;
+	TUint* end = (TUint*)(LargeBuffer + LargeBufferSize);
+	while (*ptr && ptr < end)
+		++ptr;
+	test(*ptr == 0);
+	test.Printf(_L("Found zero word at %08x\n"), ptr);
+	PagedHeaderDes = (TDesC8*)ptr;
+
 	test.Next(_L("Test HAL interface"));
 	TestHAL();
 	
@@ -1390,6 +1561,15 @@
 
 	test.Next(_L("Test no kernel faults when copying data from unpaged rom with mutex held"));
 	TestReadHoldingMutex();
+	
+#ifdef _DEBUG
+	// test hook in kernel not present in release mode
+	if ((MemModelAttributes() & EMemModelTypeMask) == EMemModelTypeFlexible)
+		{
+		test.Next(_L("Test unrecoverable errors while paging"));
+		TestPagingErrors();
+		}
+#endif
 
 	test.Next(_L("Close test driver"));
 	Ldd.DestroyPlatHwChunk();
@@ -1398,11 +1578,14 @@
 	test.Next(_L("Test setting publish and subscribe properties from paged area"));
 	TestPublishAndSubscribe();
 
+#ifndef _DEBUG
+	// no point benchmarking in debug mode
 	if (DPTest::Attributes() & DPTest::ERomPaging)
 		{
 		test.Next(_L("Rom Paging Benchmark"));
 		RomPagingBenchmark();
 		}
+#endif
 
 	PagedLibrary.Close();
 	gobbler.Close();