kerneltest/e32test/rpmb/driver/d_rpmb.cpp
changeset 287 ddfd5aa0d58f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/rpmb/driver/d_rpmb.cpp	Mon Oct 11 19:11:06 2010 +0100
@@ -0,0 +1,1225 @@
+// Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32test/rpmb/d_rpmb.cpp
+// LDD for testing RPMB kernel extension
+// 
+//
+
+/**
+ @file
+ @internalComponent
+ @prototype
+*/
+
+#include "d_rpmb.h"
+#include "../t_rpmb.h"
+
+_LIT(KRpmbTestThreadName,"RpmbTestThread");
+const TInt KRpmbTestPriority = 24;
+
+TDfcQue RpmbTestDfcQ;
+TDfc* DfcPtr;
+
+// please read this before uncommenting out #define RPMBTESTS_TRY_TO_WRITE_KEY:
+// Don't want to write key 'pre production' on real device as this will lock 
+// the system out of the RPMB partition (the key should be preprogrammed as 
+// part of the production process so that the system has access to the key - 
+// if the key has already been written it can't be written again).
+// #define RPMBTESTS_TRY_TO_WRITE_KEY
+
+
+/****************************************
+ Implementation of DRpmbTestFactory class
+*****************************************/
+
+DRpmbTestFactory::DRpmbTestFactory()
+	{
+	}
+
+DRpmbTestFactory::~DRpmbTestFactory()
+	{
+	}
+
+TInt DRpmbTestFactory::Create(DLogicalChannelBase*& aChannel)
+	{
+	aChannel = new DRpmbTest;
+	return KErrNone;
+	}
+
+TInt DRpmbTestFactory::Install()
+	{
+	return SetName(&KRpmbTestLddName);
+	}
+
+void DRpmbTestFactory::GetCaps(TDes8& /*aDes*/) const
+// implementation required since DLogicalDevice::GetCaps is pure virtual
+	{
+	// do nothing 
+	}
+
+
+static void RpmbTestDfcFunction(TAny* /*aPtr*/)
+	{
+	DRpmbDevice * rpmb;
+	rpmb = new DRpmbDevice;
+	TInt r = rpmb->Open(0);
+	Kern::Printf("%S Early on DRpmbDevice::Open test returned %d",&KDRpmbTestBanner, r);
+	// ASSERT to indicate test failure if DRpmbDevice::Open() returned error
+	__ASSERT_ALWAYS((r==KErrNone), Kern::Fault(__FILE__, __LINE__));
+	rpmb->Close();
+	delete rpmb;
+	// run more extensive tests early on 
+	// note the unconventional use of a class derived from DLogicalChannelBase which is justified
+	// in the current context to provide more startup test coverage
+	DRpmbTest * test;
+	test = new DRpmbTest;
+	// while a call to DRpmbTest::DRpmbDeviceTests() works in this context
+	// a call to DRpmbTest::RpmbStackTests() would fail as DRpmbTest::DoCreate() wouldn't be
+	// called so that DRpmbTest::iStackSemPtr wouldn't get set up properly so that when it 
+	// was uaed an exception would be generated
+	r = test->DRpmbDeviceTests();
+	Kern::Printf("%S Early on DRpmbTest::DRpmbDeviceTests() returned %d",&KDRpmbTestBanner, r);
+	// ASSERT to indicate test failure
+	__ASSERT_ALWAYS((r==KErrNone), Kern::Fault(__FILE__, __LINE__));
+	// not safe to delete test as  not setup conventionally DRpmbTest::DoCreate() wasn't called 
+	// at setup and DLogicalChannelBase::~DLogicalChannelBase__sub_object() would generate an 
+	// exception
+	}
+
+
+/********************************
+ Entry point for RPMB test driver
+*********************************/
+
+DECLARE_STANDARD_EXTENSION()
+    {     
+	TInt r = Kern::DfcQInit(&RpmbTestDfcQ,KRpmbTestPriority,&KRpmbTestThreadName);;      
+	if (r != KErrNone)
+		return r;
+	DfcPtr = new TDfc(RpmbTestDfcFunction,NULL, &RpmbTestDfcQ, KMaxDfcPriority-7);
+	DfcPtr->Enque();     
+	return KErrNone;
+    }
+
+DECLARE_EXTENSION_LDD()
+	{
+	return new DRpmbTestFactory;
+	}
+
+/*********************************
+ Implementation of DRpmbTest class
+**********************************/
+
+DRpmbTest::DRpmbTest():
+// initialise parameters
+	iStackSemPtr(NULL),
+	iPowerSemPtr(NULL),
+	iSessionEndCallBack(DRpmbTest::StackCallBack, this)
+	{
+	}
+
+DRpmbTest::~DRpmbTest()
+	{
+	
+	iBusCallBack.Remove();
+		
+	if (iPowerSemPtr)
+		{
+		iPowerSemPtr->Close(NULL);
+		}
+	if (iStackSemPtr)
+		{
+		iStackSemPtr->Close(NULL);
+		}
+	if (iSession)
+		{
+		delete iSession;
+		}
+	}
+
+TInt DRpmbTest::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/)
+	{
+	TInt r = KErrNone;
+	r = Kern::SemaphoreCreate(iStackSemPtr, _L("DRpmbStackSem"), 0);
+	return r;
+	}
+
+TInt DRpmbTest::Request(TInt aFunction, TAny* /*a1*/, TAny* /*a2*/)
+	{
+	TInt r = KErrNotSupported;
+	switch (aFunction)
+		{
+		case RTestRpmb::ERunTests:
+			{
+			// respond to user side request 
+			// execute tests for RPMB kernel extension
+			// run standatd tests that use the DRpmbDevice interface
+            Kern::Printf("	");
+            Kern::Printf("%S << START DRpmbDevice TESTS >>", &KDRpmbTestBanner);
+			r = DRpmbDeviceTests();
+            if (r != KErrNone)
+                {
+                Kern::Printf("%S DPrmbDevice test FAILED", &KDRpmbTestBanner);
+                break;
+                }
+            Kern::Printf("%S >> ALL DRpmbDevice TESTS PASSED", &KDRpmbTestBanner);
+			// run tests that use the MMC stack bypassing the RPMB kernel extension
+            Kern::Printf("	");
+	        Kern::Printf("%S << START STACK TESTS >>", &KDRpmbTestBanner);
+			r = RpmbStackTests();
+			if (r != KErrNone)
+			    {
+                Kern::Printf("%S stack test FAILED", &KDRpmbTestBanner);
+                break;
+			    }
+            Kern::Printf("%S >> ALL STACK TESTS PASSED", &KDRpmbTestBanner);
+			break;
+			}
+		default:
+			break;
+		}
+	return r;
+	}
+
+
+// test RPMB using the DRpmbDevice interface
+TInt DRpmbTest::DRpmbDeviceTests()
+// open interface and make data write, ket write, counter read and data write accesses
+	{				
+	TInt size = (TInt)(2*KRpmbOneFramePacketLength);
+
+	NKern::ThreadEnterCS();
+	iRequest = (TUint8 *)Kern::Alloc(size);
+	NKern::ThreadLeaveCS();
+
+	iResponse = iRequest + KRpmbOneFramePacketLength;
+
+	TInt r = SendAccessRequestNotOpen();
+	
+	if (r==KErrNone)
+		{
+		r = OpenWithBadIndex();
+		}
+
+	if (r==KErrNone)
+		{
+		r = MultipleOpen();
+		}
+	
+	if (r==KErrNone)
+		{
+		r = iRpmb.Open(0);
+		}
+	
+	if (r!=KErrNone)
+		{
+		Kern::Printf("%S DRpmbTest::DRpmbDeviceTest DRpmbDevice open error = %d", 
+		        &KDRpmbTestBanner, r);
+		}
+	else
+		{
+		r  = SendAccessRequestBadParms();
+		}
+
+	if (r==KErrNone)
+		{
+		r = InvalidRequestId();
+		}   
+	
+	if (r==KErrNone)
+		{
+		r = IsKeyProgrammed();
+		}   
+	
+// Don't want to write key 'pre production' on real device as this will lock 
+// the system out of the RPMB partition (the key should be preprogrammed as 
+// part of the production process so that the system has access to the key - 
+// if the key has already been written it can't be written again)
+#ifdef RPMBTESTS_TRY_TO_WRITE_KEY
+	if (r==KErrNone)
+		{
+		r = WriteKey();
+		}
+#endif
+
+	if (r==KErrNone)
+		{  
+		r = ReadWriteCounter();
+		}
+
+	if (r==KErrNone)
+		{
+		r = ReadData();
+		}
+
+	iRpmb.Close();
+	
+	NKern::ThreadEnterCS();	
+
+	Kern::Free(iRequest);
+	
+	NKern::ThreadLeaveCS();
+	
+	return r;
+}
+
+TInt DRpmbTest::SendAccessRequestNotOpen()
+// test cases where DRpmbDevice::SendAccessRequest() is called 
+// 1.) before DRpmbDevice::Open() is ever called
+// 2,) after DRpmbDevice::Close() has been called but before DRpmbDevice::Open() is called
+	{	
+	Kern::Printf("%S DRpmbDevice::SendAccessRequest not open test", &KDRpmbTestBanner);     
+	// wrap up IO buffers in descriptors to pass across interface
+
+	TPtr8 request(iRequest,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+	TPtr8 response(iResponse,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+
+
+	// test call DRpmbDevice::SendAccessRequest before EVER calling open
+
+	TInt r = iRpmb.SendAccessRequest(request, response);
+	if (r != KErrNotReady)
+		{
+        Kern::Printf("%S SendAccessRequest not open test, before EVER opening, unexpected error = %d", 
+                &KDRpmbTestBanner, r);     
+		return KErrGeneral;
+		}
+
+	// test open and close and call again
+	r = iRpmb.Open(0);
+	if (r != KErrNone)
+		{
+        Kern::Printf("%S SendAccessRequest not open test, opening, unexpected error = %d", 
+                &KDRpmbTestBanner, r);     
+		return KErrGeneral;
+		}
+	iRpmb.Close();
+
+	r = iRpmb.SendAccessRequest(request, response);
+	if (r != KErrNotReady)
+		{
+        Kern::Printf("%S SendAccessRequest not open test, before opening AFTER closing, unexpected error = %d", 
+                &KDRpmbTestBanner, r);     
+		return KErrGeneral;
+		}
+	
+	Kern::Printf("%S DRpmbDevice::SendAccessRequest not open test PASS", &KDRpmbTestBanner);     
+	return KErrNone;
+}
+
+
+TInt DRpmbTest::OpenWithBadIndex()
+// test case where DRpmbDevice::Open() is called with a device index of more than zero
+	{
+	Kern::Printf("%S DRpmbDevice::Open bad index test", &KDRpmbTestBanner);     
+	
+	// test index > 0
+	TInt r = iRpmb.Open(1);
+	if (r != KErrGeneral)
+		{
+        Kern::Printf("%S RpmbDevice::Open bad index test, unexpected error = %d", 
+                &KDRpmbTestBanner, r);     
+		return KErrGeneral;
+		}
+	
+	Kern::Printf("%S DRpmbDevice::Open bad index test PASS", &KDRpmbTestBanner);     
+	return KErrNone;
+}
+
+
+TInt DRpmbTest::MultipleOpen()
+// test case where DRpmbDevice::Open() is called twice with the same index parameter and
+// the same instance of DRpmbDevice
+// test case where DRpmbDevice::Open() is called twice with the same index parameter and
+// a different instance of DRpmbDevice
+	{
+	Kern::Printf("%S DRpmbDevice::Open use more than once test", &KDRpmbTestBanner);     
+	
+	TInt r = iRpmb.Open(0);
+	if (r != KErrNone)
+		{
+        Kern::Printf("%S RpmbDevice::Open use more than once test first open, unexpected error = %d", 
+                &KDRpmbTestBanner, r);     
+		return KErrGeneral;
+		}
+
+	r = iRpmb.Open(0);
+	if (r != KErrNone)
+		{
+        Kern::Printf("%S RpmbDevice::Open use more than once test second open, unexpected error = %d", 
+                &KDRpmbTestBanner, r);     
+		return KErrGeneral;	
+		}
+
+	r = iRpmbSecondInstance.Open(0);
+	if (r != KErrInUse)
+		{
+        Kern::Printf("%S RpmbDevice::Open use more than once test third open, unexpected error = %d", 
+                &KDRpmbTestBanner, r);     
+		return KErrGeneral;	
+		}
+
+	iRpmb.Close();
+	iRpmbSecondInstance.Close();
+
+	Kern::Printf("%S DRpmbDevice::Open use more than once test PASS", &KDRpmbTestBanner);     
+
+	return KErrNone;
+	}
+
+
+TInt DRpmbTest::SendAccessRequestBadParms()
+// test cases where DRpmbDevice::SendAccessRequest is called with secriptor arguments with invalid lengths
+	{
+	Kern::Printf("%S DRpmbDevice::SendAccessRequest bad parms test", &KDRpmbTestBanner);     
+	
+	// test both lengths !=	KRpmbOneFramePacketLength
+	TBuf<KRpmbOneFramePacketLength-1> request;
+	TBuf<KRpmbOneFramePacketLength+1> response;
+	request.SetLength(KRpmbOneFramePacketLength-1);
+	response.SetLength(KRpmbOneFramePacketLength+1);
+
+	TInt r = iRpmb.SendAccessRequest(request, response);
+	if (r != KErrArgument)
+		{
+        Kern::Printf("%S SendAccessRequest bad parms test, both params bad, unexpected error = %d", 
+                &KDRpmbTestBanner, r);     
+		return KErrGeneral;
+		}
+
+	// test request length != KRpmbOneFramePacketLength
+	TBuf<KRpmbOneFramePacketLength+1> request1;
+	TBuf<KRpmbOneFramePacketLength> response1;
+	request1.SetLength(KRpmbOneFramePacketLength+1);
+	response1.SetLength(KRpmbOneFramePacketLength);
+
+	r = iRpmb.SendAccessRequest(request1, response1);
+	if (r != KErrArgument)
+		{
+        Kern::Printf("%S SendAccessRequest bad parms test, request param bad, unexpected error = %d", 
+                &KDRpmbTestBanner, r);     
+		return KErrGeneral;
+		}
+
+	// test response length !=KRpmbOneFramePacketLength
+	TBuf<KRpmbOneFramePacketLength> request2;
+	TBuf<KRpmbOneFramePacketLength-1> response2;
+	request2.SetLength(KRpmbOneFramePacketLength);
+	response2.SetLength(KRpmbOneFramePacketLength-1);
+
+	r = iRpmb.SendAccessRequest(request1, response1);
+	if (r != KErrArgument)
+		{
+        Kern::Printf("%S SendAccessRequest bad parms test, response param bad, unexpected error = %d", 
+                &KDRpmbTestBanner, r);     
+		return KErrGeneral;
+		}
+	
+	
+	Kern::Printf("%S DRpmbDevice::SendAccessRequest bad parms test PASS", &KDRpmbTestBanner);     
+	return KErrNone;
+}
+
+TInt DRpmbTest::InvalidRequestId()
+// send a request with an invalid request ID to the RPMB partition
+// when SendAccessRequest returns iResponse should not have reponse or result fields filled in
+	{
+    Kern::Printf("%S invalid request ID test", &KDRpmbTestBanner);     
+    
+	// set up write request packet with blank MAC
+	memset(iRequest, 0, KRpmbOneFramePacketLength);
+	* (iRequest + KRpmbRequestLsbOffset) = KRpmbRequestReadResultRegister + 1;
+   
+	// wrap up IO buffers in descriptors to pass across interface
+	TPtr8 request(iRequest,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+	TPtr8 response(iResponse,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+
+	TInt r = iRpmb.SendAccessRequest(request, response);
+	if (r != KErrNone)
+		{
+        Kern::Printf("%S invalid request ID test, send request error = %d", 
+                &KDRpmbTestBanner, r);     
+		return r;
+		}
+
+	// check that response or result fields have not been set in response
+	TUint resp = DecodeResponse(iResponse);
+	TUint result = DecodeResult(iResponse);
+
+	if (resp != 0x0006)
+		{
+        Kern::Printf("%S invalid request ID test, unexpected response = %d error", 
+                &KDRpmbTestBanner, resp);
+		return KErrGeneral;
+		}
+
+	if (result != 0x0000)
+		{
+		Kern::Printf("%S invalid request ID test, unexpected result = %d error", 
+                &KDRpmbTestBanner, result);
+		return KErrGeneral;
+		}
+
+	Kern::Printf("%S invalid requeset ID test PASS", &KDRpmbTestBanner);     
+	return KErrNone;
+	}
+
+
+TInt DRpmbTest::IsKeyProgrammed()
+// send a data write request to the RPMB partition
+// because the request has a blank MAC (not authenticated) 
+// the partition returns operation result authentication failure if the key is programmed
+// else the partition returns authentication key not programmed
+	{
+    Kern::Printf("%S data write test", &KDRpmbTestBanner);     
+    
+	// set up write request packet with blank MAC
+	memset(iRequest, 0, KRpmbOneFramePacketLength);
+	* (iRequest + KRpmbRequestLsbOffset) = KRpmbRequestWriteData;
+   
+	// wrap up IO buffers in descriptors to pass across interface
+	TPtr8 request(iRequest,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+	TPtr8 response(iResponse,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+
+	TInt r = iRpmb.SendAccessRequest(request, response);
+	if (r != KErrNone)
+		{
+        Kern::Printf("%S data write test, send request error = %d", 
+                &KDRpmbTestBanner, r);     
+		return r;
+		}
+		
+	TUint resp = DecodeResponse(iResponse);
+	TUint result = DecodeResult(iResponse);
+
+	if (resp != KRpmbResponseWriteData)
+		{
+        Kern::Printf("%S data write test, unexpected response = %d error", 
+                &KDRpmbTestBanner, resp);
+		return KErrGeneral;
+		}
+
+	if (result == KRpmbResultKeyNotProgrammed)
+		{
+		iKeySet = EFalse;
+	    Kern::Printf("%S data write test, key NOT programmed", 
+	            &KDRpmbTestBanner);     
+		}
+	else if (result == KRpmbResultAuthenticationFailure)
+		{
+		iKeySet = ETrue;
+	    Kern::Printf("%S data write test, key ALREADY programmed", 
+	            &KDRpmbTestBanner);     
+		}
+	else
+		{
+        Kern::Printf("%S data write test, unexpected result = %d error", 
+                &KDRpmbTestBanner, result);
+		return KErrGeneral;
+		}
+	Kern::Printf("%S data write test PASS", &KDRpmbTestBanner);     
+	return KErrNone;
+	}
+
+TInt DRpmbTest::WriteKey()
+	{
+    Kern::Printf("%S write key test", &KDRpmbTestBanner);     
+
+    // set up write key request
+	memset(iRequest, 0, KRpmbOneFramePacketLength);
+	* (iRequest + KRpmbRequestLsbOffset) = KRpmbRequestWriteKey;
+    // key = 0x0101010101010101010101010101010101010101010101010101010101010101
+	memset(iRequest+KRpmbKeyOffset,1, KRpmbKeyLength); 
+
+	// wrap up IO buffers in descriptors to pass across interface
+	TPtr8 request(iRequest,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+	TPtr8 response(iResponse,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+
+	TInt r = iRpmb.SendAccessRequest(request, response);
+	if (r != KErrNone)
+		{
+        Kern::Printf("%S write key test, send request error = %d", 
+                &KDRpmbTestBanner, r);     
+		return r;
+		}
+
+	TUint resp = DecodeResponse(iResponse);
+	TUint result = DecodeResult(iResponse);
+
+	if (resp != KRpmbResponseWriteKey)
+		{
+        Kern::Printf("%S write key test, unexpected repsponse = %d error", 
+             &KDRpmbTestBanner, resp);
+		return KErrGeneral;
+		}
+
+	if (iKeySet)
+		{
+		if (result == KRpmbResultGeneralFailure)
+			{
+            Kern::Printf("%S write key test, key ALREADY written", 
+                    &KDRpmbTestBanner);     
+            Kern::Printf("%S write key test PASS", 
+                    &KDRpmbTestBanner);     
+			return KErrNone;
+			}
+		else
+			{
+            Kern::Printf("%S write key test, unexpected result = %d error", 
+                    &KDRpmbTestBanner, result);
+			return KErrGeneral; 
+			}
+		}
+	else
+		{
+		if(result == KRpmbResultOk)
+			{
+            Kern::Printf("%S write key test, key JUST written", 
+		            &KDRpmbTestBanner);     
+            Kern::Printf("%S write key test PASS", &KDRpmbTestBanner);     
+			return KErrNone;
+			}
+		else
+			{
+	       Kern::Printf("%S write key test unexpected result = %d error", 
+	                &KDRpmbTestBanner, result);
+			return KErrGeneral;
+			}
+		}
+	}
+
+TInt DRpmbTest::ReadWriteCounter()
+	{
+    Kern::Printf("%S read write counter test", &KDRpmbTestBanner);     
+
+	// set up read write counter request packet
+	memset(iRequest, 0, KRpmbOneFramePacketLength);
+	* (iRequest + KRpmbRequestLsbOffset) = KRpmbRequestReadWriteCounter;
+   
+	// wrap up IO buffers in descriptors to pass across interface
+	TPtr8 request(iRequest,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+	TPtr8 response(iResponse,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+
+	TInt r = iRpmb.SendAccessRequest(request, response);
+	if (r!=KErrNone)
+		{
+        Kern::Printf("%S read write counter test, send request error = %d", 
+                &KDRpmbTestBanner, r);     
+		return r;
+		}
+
+	TUint resp = DecodeResponse(iResponse);
+	TUint result = DecodeResult(iResponse);
+	TUint32 counter = DecodeCounter(iResponse);
+
+	if (resp != KRpmbResponseReadWriteCounter)
+		{
+        Kern::Printf("%S read write counter test, unexpected repsponse = %d error", 
+                &KDRpmbTestBanner, resp);
+		return KErrGeneral;
+		}
+
+#ifdef  RPMBTESTS_TRY_TO_WRITE_KEY
+	if (result == KRpmbResultOk)
+#else
+	if (result == KRpmbResultOk || result == KRpmbResultKeyNotProgrammed)
+#endif
+		{
+		Kern::Printf("%S read write counter test write counter = %d", 
+			&KDRpmbTestBanner, counter);     
+        Kern::Printf("%S read write counter test PASS", &KDRpmbTestBanner);     
+		return KErrNone;
+		}
+	else
+		{
+        Kern::Printf("%S read write counter test, unexpected result = %d error", 
+                &KDRpmbTestBanner, result);
+		return KErrGeneral;
+		}	
+	}
+
+TInt DRpmbTest::ReadData()
+	{
+    Kern::Printf("%S read data test", &KDRpmbTestBanner);     
+    
+	// set up read request packet
+	memset(iRequest, 0, KRpmbOneFramePacketLength);
+	* (iRequest + KRpmbRequestLsbOffset) = KRpmbRequestReadData;
+   
+	// wrap up IO buffers in descriptors to pass across interface
+	TPtr8 request(iRequest,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+	TPtr8 response(iResponse,KRpmbOneFramePacketLength,KRpmbOneFramePacketLength);
+
+	TInt r=iRpmb.SendAccessRequest(request, response);
+	if (r!=KErrNone)
+		{
+        Kern::Printf("%S read data test, send request error = %d", 
+                &KDRpmbTestBanner, r);     
+		return r;
+		}
+
+	TUint resp = DecodeResponse(iResponse);
+	TUint result = DecodeResult(iResponse);
+
+	if (resp != KRpmbResponseReadData)
+		{
+        Kern::Printf("%S read data test, unexpected repsponse = %d error", 
+                &KDRpmbTestBanner, resp);
+		return KErrGeneral;
+		}
+
+#ifdef  RPMBTESTS_TRY_TO_WRITE_KEY
+	if (result == KRpmbResultOk)
+#else
+	if (result == KRpmbResultOk || result == KRpmbResultKeyNotProgrammed)
+#endif
+		{
+ 		DisplayReadData(iResponse);
+        Kern::Printf("%S read data test PASS", &KDRpmbTestBanner);     
+		return KErrNone;
+		}
+	else
+		{
+        Kern::Printf("%S read data test, unexpected result = %d error", 
+             &KDRpmbTestBanner, result);
+        return KErrGeneral;
+		}		
+	}
+
+TUint DRpmbTest::DecodeResponse(TUint8 * aResp)
+	{
+	return (* (aResp + KRpmbResponseLsbOffset) + ((* (aResp + KRpmbResponseMsbOffset)) << 8));
+	}
+
+TUint DRpmbTest::DecodeResult(TUint8 * aResp)
+	{
+	return ((* (aResp + KRpmbResultLsbOffset) + ((* (aResp + KRpmbResultMsbOffset)) << 8)) 
+	        & KRpmbResultCounterExpiredMask);
+	}
+
+TUint32 DRpmbTest::DecodeCounter(TUint8 * aResp)
+	{
+
+	return ((* (aResp + KRpmbCounterByteOneOffset)) + ((* (aResp + KRpmbCounterByteTwoOffset)) << 8)
+		+ ((* (aResp + KRpmbCounterByteThreeOffset)) << 16) + ((* (aResp + KRpmbCounterByteFourOffset)) << 24));
+	}
+
+void DRpmbTest::DisplayReadData(TUint8 * aResp)
+	{
+	TUint8 * displayPtr = aResp + KRpmbDataOffset;
+    Kern::Printf("%S data field:", &KDRpmbTestBanner);
+	for (TInt i=0; i<8; i++)
+		{
+		Kern::Printf("%x%x%x%x %x%x%x%x %x%x%x%x %x%x%x%x %x%x%x%x %x%x%x%x %x%x%x%x %x%x%x%x",
+			* displayPtr       , * (displayPtr +  1), * (displayPtr +  2), * (displayPtr +  3),
+			* (displayPtr +  4), * (displayPtr +  5), * (displayPtr +  6), * (displayPtr +  7),
+			* (displayPtr +  8), * (displayPtr +  9), * (displayPtr + 10), * (displayPtr + 11),
+			* (displayPtr + 12), * (displayPtr + 13), * (displayPtr + 14), * (displayPtr + 15),
+			* (displayPtr + 16), * (displayPtr + 17), * (displayPtr + 18), * (displayPtr + 19),
+			* (displayPtr + 20), * (displayPtr + 21), * (displayPtr + 22), * (displayPtr + 23),
+			* (displayPtr + 24), * (displayPtr + 25), * (displayPtr + 26), * (displayPtr + 27),
+			* (displayPtr + 28), * (displayPtr + 29), * (displayPtr + 30), * (displayPtr + 31));
+		displayPtr +=32;
+		}
+	}
+
+void DRpmbTest::StackCallBack(TAny * aSelf)
+// call back from MMC stack session 
+    {
+	// dereference any pointer
+	DRpmbTest& self = *static_cast<DRpmbTest*>(aSelf);
+	// signal semaphore so that calling code progresses
+	Kern::SemaphoreSignal(*(self.iStackSemPtr));
+	}
+
+TInt DRpmbTest::RpmbStackTests()
+// Test code verfies all four access types through a direct connection to the MMC protocol stack 
+	{
+	TInt r = SetupForStackTests();
+	if (r == KErrNone)
+		{
+		r = StackBadIndex();
+		}
+	if (r == KErrNone)
+		{
+		r = StackIsKeyProgrammed();
+		}
+// Don't want to write key 'pre production' on a real device as this will lock 
+// the system out of the RPMB partition (the key should be preprogrammed as 
+// part of the production process so that the system has access to the key - 
+// if the key has already been written it can't be written again)
+#ifdef RPMBTESTS_TRY_TO_WRITE_KEY
+	if (r == KErrNone)
+		{
+		r = StackWriteKey();
+		}
+#endif
+	if (r == KErrNone)
+		{
+		r = StackReadWriteCounter();
+		}
+	if (r == KErrNone)
+		{
+		r = StackReadData();
+		}
+	return r;
+	}
+
+
+TInt DRpmbTest::StackBadIndex()
+// Call MMCGetExtInterface with invalid device id
+//   - don't expect params to be returned
+//   - expect KErrGeneral return code
+	{
+	Kern::Printf("%S stack device index test", &KDRpmbTestBanner);     
+
+	MRpmbInfo* rpmbInterface = NULL;
+	TRpmbDeviceParms parms;
+
+	TInt r = MMCGetExtInterface(KInterfaceRpmb, (MMCMExtInterface*&) rpmbInterface, this); //this pointer not used in the case of Rpmb
+    if (r!=KErrNone)
+        {
+        Kern::Printf("%S stack device index, MMCGetInterface returned error = %d",
+             &KDRpmbTestBanner, r);
+        return KErrGeneral;
+        }
+	if (rpmbInterface == NULL)
+		{
+		// unexpected error since MMCGetExtInterface didn't return an error
+        Kern::Printf("%S stack device index, MMCGetInterface returned NULL interface pointer",
+             &KDRpmbTestBanner, r);
+		return KErrGeneral;
+		}
+
+    else
+        {
+		r = rpmbInterface->RpmbInfo(1, parms);
+		if (r == KErrNone)
+			{
+			Kern::Printf("%S stack device index test, parmas returned for index 1 error",
+				&KDRpmbTestBanner, r);
+			return KErrGeneral;
+			}
+		else if (r != KErrGeneral)
+			{
+			Kern::Printf("%S stack device index test, unexpected rc for index 1 error",
+				&KDRpmbTestBanner, r);
+			return KErrGeneral;
+			}
+		}
+    Kern::Printf("%S stack device index test PASS", &KDRpmbTestBanner);     
+	return KErrNone;
+}
+
+
+void DRpmbTest::BusCallBack(TAny* aPtr, TInt aReason, TAny* a1, TAny* a2)
+{
+    DRpmbTest* lddPtr = (DRpmbTest*)aPtr;
+    TPBusState busState = (TPBusState) (TInt) a1;
+	TInt busError = (TInt) a2;
+
+    if(aReason == TPBusCallBack::EPBusStateChange 
+		&& busState == EPBusOn && busError == KErrNone)
+		{
+		Kern::SemaphoreSignal(*(lddPtr->iPowerSemPtr));
+        }
+}
+
+TInt DRpmbTest::SetupForStackTests()
+	{
+	Kern::Printf("%S setup for stack tests", &KDRpmbTestBanner);     
+
+	TRpmbDeviceParms parms;
+	
+	parms.iCardNumber = 0;
+	parms.iSocketPtr = NULL;
+	
+	DMMCStack* stack = NULL;
+	TUint cardNumber = 0;
+	MRpmbInfo* rpmbInterface = NULL;
+
+    TInt r = MMCGetExtInterface(KInterfaceRpmb, (MMCMExtInterface*&) rpmbInterface, this); //this pointer not used in the case of Rpmb
+    if (r!=KErrNone)
+        {
+        Kern::Printf("%S setup for stack tests, MMCGetInterface returned error = %d",
+             &KDRpmbTestBanner, r);
+        return KErrGeneral;
+        }
+	if (rpmbInterface == NULL)
+		{
+		// unexpected error since MMCGetExtInterface didn't return an error
+		return KErrGeneral;
+		}
+
+    else
+        {
+		r = rpmbInterface->RpmbInfo(0, parms);
+		if (r != KErrNone)
+			{
+			// requested index non zero or bseport not configured with RPMB capable MMC device
+			return r;
+			}
+        
+        cardNumber = parms.iCardNumber;        
+        iSocket = parms.iSocketPtr;
+        if(iSocket == NULL)
+            {
+            Kern::Printf("%S setup for stack tests, socket pointer is NULL",
+                 &KDRpmbTestBanner);
+              return KErrNoMemory;
+            }
+        
+		// set up to be informed of changes on bus
+		iBusCallBack.iFunction = BusCallBack;
+		iBusCallBack.iPtr=this;
+		iBusCallBack.SetSocket(iSocket->iSocketNumber);
+		iBusCallBack.Add();
+
+        // power up the stack
+        // media drivers don't have to do this at this stage because already done
+ 		NKern::ThreadEnterCS();	
+
+		r = Kern::SemaphoreCreate(iPowerSemPtr,_L("DpmbPowerSem"), 0);
+
+		if (r!=KErrNone)
+			{		
+			NKern::ThreadLeaveCS();
+            Kern::Printf("%S sese you below thentup for stack tests, SemaphoreCreate returned %d",
+                    &KDRpmbTestBanner, r);
+            return KErrGeneral;
+			}
+		
+		r = iSocket->PowerUp();
+				
+        if (r!=KErrNone && r!= KErrCompletion)
+            {
+			NKern::ThreadLeaveCS();
+            Kern::Printf("%S setup for stack tests, PowerUp returned %d",
+                    &KDRpmbTestBanner, r);
+            return KErrGeneral;
+            }
+		
+        if (r == KErrNone)
+			{
+            // wait for socket to power up
+			Kern::SemaphoreWait(*iPowerSemPtr);
+			}
+
+		NKern::ThreadLeaveCS();
+  
+        stack = iSocket->Stack(0);
+        if (stack == NULL)
+            {
+            // baseport poorly configured
+            Kern::Printf("%S setup for stack tests, stack pointer is NULL",
+                &KDRpmbTestBanner);
+            return KErrGeneral;
+            }
+        }
+
+    TMMCard* card = stack->CardP(cardNumber);
+
+	if (card == NULL)
+		{
+		// baseport poorly configured
+        Kern::Printf("%S setup for stack tests, card pointer is NULL",
+            &KDRpmbTestBanner);
+		return KErrGeneral;
+		}
+
+	NKern::ThreadEnterCS();	
+
+ 	iSession = stack->AllocSession(iSessionEndCallBack);
+	
+	NKern::ThreadLeaveCS();
+	
+	if (iSession == NULL)
+		{
+		Kern::Printf("%S setup for stack tests, session pointer is NULL",
+		        &KDRpmbTestBanner);
+		return(KErrNoMemory);
+		}
+ 
+	iSession->SetStack(stack);
+
+	iSession->SetCard(card);
+		
+	TUint32 revision = 0;
+
+	// loop until extended csd becomes available in case earlier pause wasn't long enough
+	// this shouldn't need to be done
+	// a reliable method not depending on delays and or polling is required
+	do {
+		stack = iSocket->Stack(0);
+		if (stack)
+		{
+			card = stack->CardP(cardNumber);	
+			if (card)
+			{
+				revision = card->ExtendedCSD().ExtendedCSDRev();
+			}
+		}
+	} while (revision==0);
+		
+	TUint size = card->ExtendedCSD().RpmbSize();
+		
+	if(revision < 5 || size == 0)
+		{
+		Kern::Printf("%S setup for stack test, rpmb partition NOT detected", &KDRpmbTestBanner); 
+		return KErrNotSupported;
+		}
+	
+	// use memory from stack
+    TInt bufLen, minorBufLen;
+    stack->BufferInfo(iBufPtr, bufLen, minorBufLen);
+	// mmc media driver reserved the first KRpmbOneFramePacketLength bytes of the 
+	// PSL buffer to be used for RPMB requests / responses
+	iBufPtr += minorBufLen;
+
+	return KErrNone;
+	}
+
+
+TInt DRpmbTest::StackIsKeyProgrammed()
+// Write Data to block 0 without MACing 
+//   - expect not MACed error if key set 
+//   - expect key not prgrammed error if key not programmed
+    {    
+
+	Kern::Printf("%S write data stack test", &KDRpmbTestBanner);     
+        
+    // set up write request packet in stack memory
+    memset(iBufPtr,0,KRpmbOneFramePacketLength);
+    * (iBufPtr + KRpmbRequestLsbOffset) = KRpmbRequestWriteData;
+        
+    TInt r = SendToStackAndWait();
+	if (r != KErrNone)
+		{
+		return r;
+		}
+
+	TUint response = DecodeResponse(iBufPtr); 
+	TUint result = DecodeResult(iBufPtr);
+
+	if (response == KRpmbResponseWriteData)
+		{
+		if (result == KRpmbResultAuthenticationFailure)
+			{
+            Kern::Printf("%S write data stack test, key IS written", 
+                    &KDRpmbTestBanner);     
+            Kern::Printf("%S write data stack test PASS", &KDRpmbTestBanner);     
+			iKeySet = ETrue;
+			return KErrNone;
+			}
+		else if (result == KRpmbResultKeyNotProgrammed)
+			{
+            Kern::Printf("%S write data stack test, key NOT written", 
+                    &KDRpmbTestBanner);     
+            Kern::Printf("%S write data stack test PASS",&KDRpmbTestBanner);
+            iKeySet = EFalse;
+            return KErrNone;
+			}
+		}
+    Kern::Printf("%S write data stack test FAILED, response = %d, result = %d", 
+            &KDRpmbTestBanner, response, result);     
+    return KErrGeneral;
+	}
+
+TInt DRpmbTest::StackWriteKey()
+// Write key
+//   - expect no error if key not programmed
+//   - expect general error if key already programmed
+    {    
+    Kern::Printf("%S write key stack test", &KDRpmbTestBanner);     
+
+    // set up write key request packet in stack memory
+    memset(iBufPtr,0,KRpmbOneFramePacketLength);
+    * (iBufPtr + KRpmbRequestLsbOffset) = KRpmbRequestWriteKey;
+        
+    TInt r = SendToStackAndWait();
+	if (r != KErrNone)
+		{
+		return r;
+		}
+
+	TUint response = DecodeResponse(iBufPtr); 
+	TUint result = DecodeResult(iBufPtr);
+
+	if (response == KRpmbResponseWriteKey)
+		{
+		if (iKeySet && result == KRpmbResultGeneralFailure)
+			{
+            Kern::Printf("%S write key stack test, key ALREADY written", 
+                    &KDRpmbTestBanner);     
+            Kern::Printf("%S write key stack test PASS", &KDRpmbTestBanner);     
+			return KErrNone;
+			}
+		else if (!iKeySet && result == KRpmbResultOk)
+			{
+            Kern::Printf("%S write key stack test, key JUST written", 
+                    &KDRpmbTestBanner);     
+            Kern::Printf("%S write key stack test PASS", &KDRpmbTestBanner);     
+			return KErrNone;
+			}
+		}
+    Kern::Printf("%S write key stack test FAILED, response = %d, result = %d", 
+            &KDRpmbTestBanner, response, result);     
+    return KErrGeneral;	
+	}
+
+TInt DRpmbTest::StackReadWriteCounter()
+// Read Data Counter
+// - expect no error
+    {    
+    Kern::Printf("%S read write counter stack test", &KDRpmbTestBanner);     
+
+    // set up write key request packet in stack memory
+    memset(iBufPtr,0,KRpmbOneFramePacketLength);
+    * (iBufPtr + KRpmbRequestLsbOffset) = KRpmbRequestReadWriteCounter;
+        
+    TInt r = SendToStackAndWait();
+	if (r != KErrNone)
+		{
+		return r;
+		}
+	
+	TUint response = DecodeResponse(iBufPtr); 
+	TUint result = DecodeResult(iBufPtr);
+	TUint32 counter = DecodeCounter(iBufPtr);
+
+#ifdef  RPMBTESTS_TRY_TO_WRITE_KEY
+	if ((response == KRpmbResponseReadWriteCounter) && (result == KRpmbResultOk))
+#else
+	if ((response == KRpmbResponseReadWriteCounter) 
+		&& (result == KRpmbResultOk || result == KRpmbResultKeyNotProgrammed))
+#endif
+		{
+        Kern::Printf("%S read write counter stack test, counter = %d", 
+			&KDRpmbTestBanner, counter);     
+        Kern::Printf("%S read write counter stack test PASS", 
+                &KDRpmbTestBanner);     
+		return KErrNone;
+		}
+    Kern::Printf("%S read write counrter stack test FAILED, response = %d, result = %d", 
+            &KDRpmbTestBanner, response, result);     
+    return KErrGeneral;	
+	}
+
+
+TInt DRpmbTest::StackReadData()
+// Read Data from block 0
+// - expect no error
+    {    
+    Kern::Printf("%S read data stack test", &KDRpmbTestBanner);     
+
+    // set up write key request packet in stack memory
+    memset(iBufPtr,0,KRpmbOneFramePacketLength);
+    * (iBufPtr + KRpmbRequestLsbOffset) = KRpmbRequestReadData;
+        
+    TInt r = SendToStackAndWait();
+	if (r != KErrNone)
+		{
+		return r;
+		}
+
+	TUint response = DecodeResponse(iBufPtr); 
+	TUint result = DecodeResult(iBufPtr);
+
+#ifdef  RPMBTESTS_TRY_TO_WRITE_KEY
+	if ((response == KRpmbResponseReadData) && (result == KRpmbResultOk))
+#else
+	if ((response == KRpmbResponseReadData) 
+		&& (result == KRpmbResultOk || result == KRpmbResultKeyNotProgrammed))
+#endif
+		{
+		DisplayReadData(iBufPtr);
+        Kern::Printf("%S read data stack test PASS", &KDRpmbTestBanner);     
+		return KErrNone;
+		}
+	Kern::Printf("%S read data stack test FAILED, response = %d, result = %d", 
+            &KDRpmbTestBanner, response, result);     
+    return KErrGeneral;
+	}
+
+TInt DRpmbTest::SendToStackAndWait()
+	{
+// comment out to get timings with RPMB -> RPMB switches in place of User 
+// Area -> RPMB switces
+#define RPMBSTACKTEST_FIRST_SWITCH_TO_USER_AREA
+#ifdef RPMBSTACKTEST_FIRST_SWITCH_TO_USER_AREA	
+	// in normal operation it may be necessary to switch to RPMB partition
+	// prior to access
+	// so force the switch to occur by pre switching to the user area
+    iSession->SetPartition(TExtendedCSD::ESelectUserArea);
+#endif
+
+	TInt start, postswitch, end;
+
+	start = NKern::TickCount();
+
+	// lock stack
+	TInt r = iSocket->InCritical();
+	if (r != KErrNone)
+		{
+		Kern::Printf("%S DRpmbTest::SendToStackAndWait, error=%d",
+		        &KDRpmbTestBanner, r);
+		iSocket->EndInCritical();
+		return KErrGeneral;
+		}
+
+	 // switch to RPMB partition
+    iSession->SetPartition(TExtendedCSD::ESelectRPMB);
+
+	postswitch = NKern::TickCount();
+	
+    // set up for write exchange 
+    iSession->ResetCommandStack();
+    iSession->FillCommandArgs(0, KRpmbOneFramePacketLength, iBufPtr, KRpmbOneFramePacketLength);
+    iSession->iSessionID = ECIMRpmbAccess;
+    iSession->Engage();
+    
+    // wait for stack call to complete
+	r = Kern::SemaphoreWait(*iStackSemPtr);
+
+	end = NKern::TickCount();
+
+	Kern::Printf("%S switch to RPMB took %d %d uS timer ticks",
+		&KDRpmbTestBanner, start - postswitch, NKern::TickPeriod());
+	Kern::Printf("%S RPMB ECIMRpmbAccess took %d %d uS timer ticks",
+		&KDRpmbTestBanner, end - postswitch, NKern::TickPeriod());
+
+	if (r != KErrNone)
+		{
+		Kern::Printf("%S DRpmbTest::SendToStackAndWait, SemaphoreWait return code = %d",
+		        &KDRpmbTestBanner, r);
+		iSocket->EndInCritical();
+		return KErrGeneral;
+		}
+
+	// check stack epoc return code
+	r = iSession->EpocErrorCode();
+	if (r != KErrNone)
+		{		
+		Kern::Printf("%S DRpmbTest::stack epoc return code = %d",
+		        &KDRpmbTestBanner, r);
+		iSocket->EndInCritical();
+		return KErrGeneral;
+		}
+		
+	// unlock stack
+	iSocket->EndInCritical();
+
+	return KErrNone;
+	}
+