--- /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;
+ }
+