kernel/eka/drivers/rpmb/rpmbdevice.cpp
changeset 287 ddfd5aa0d58f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/rpmb/rpmbdevice.cpp	Mon Oct 11 19:11:06 2010 +0100
@@ -0,0 +1,434 @@
+// 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:
+// /os/kernelhwsrv/kernel/eka/drivers/rpmb/rpmbdevice.cpp
+// Kernel extension entry point for RPMB driver.
+// 
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+#include "OstTraceDefinitions.h"
+#ifdef OST_TRACE_COMPILER_IN_USE
+#include "../../include/drivers/locmedia_ost.h"
+#ifdef __VC32__
+#pragma warning(disable: 4127) // disabling warning "conditional expression is constant"
+#endif
+#include "rpmbdeviceTraces.h"
+#endif
+
+#include <kernel/kernel.h>
+#include <drivers/rpmbdevice.h>
+#include <drivers/sdcard.h>
+#include <drivers/sdio/sdio.h>
+#include <drivers/mmc.h>
+
+DRpmbDevice * DRpmbDevice::DRpmbDevicePtrs[KMaxPBusSockets*4] = {
+	NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL};
+
+EXPORT_C DRpmbDevice::DRpmbDevice():
+	iSessionEndCallBack(DRpmbDevice::SessionEndCallBack, this),
+	iDeviceIndex(KIndexNotAssigned)
+    {
+	}
+
+EXPORT_C DRpmbDevice::~DRpmbDevice()
+    {
+    Close();
+	}
+
+void DRpmbDevice::SessionEndCallBack(TAny* aSelf)
+    {
+    DRpmbDevice& self = *static_cast<DRpmbDevice*>(aSelf);
+    self.DoSessionEndCallBack();
+    }
+
+void DRpmbDevice::DoSessionEndCallBack()
+    {   
+    iSocket->EndInCritical();
+    Kern::SemaphoreSignal(*iRequestSemaphore);
+    }
+
+void DRpmbDevice::BusCallBack(TAny* aPtr, TInt aReason, TAny* a1, TAny* a2)
+{
+    DRpmbDevice* device = (DRpmbDevice*)aPtr;
+    TPBusState busState = (TPBusState) (TInt) a1;
+	TInt busError = (TInt) a2;
+
+    if(aReason == TPBusCallBack::EPBusStateChange 
+		&& busState == EPBusOn && busError == KErrNone)
+		{
+		Kern::SemaphoreSignal(*(device->iPowerUpSemaphore));
+        }
+}
+
+TInt DRpmbDevice::PowerUpStack()
+    {
+    //
+    // Power up the socket - This ensures that the socket is powered up 
+    // and the functions are re-enumerated.
+    //  
+    iBusCallBack.iFunction = BusCallBack;
+    iBusCallBack.iPtr=this;
+    iBusCallBack.SetSocket(iSocket->iSocketNumber);
+    iBusCallBack.Add();
+	NKern::ThreadEnterCS();
+    TInt r = Kern::SemaphoreCreate(iPowerUpSemaphore,_L("RPMBPowerUpSem"), 0);
+    if(r == KErrNone)
+        {
+		TInt r = iSocket->PowerUp();
+        if(r==KErrNone)
+            {
+            Kern::SemaphoreWait(*iPowerUpSemaphore);
+            }
+        }
+	NKern::ThreadLeaveCS();
+    return r;
+    }
+
+void DRpmbDevice::SetSynchronisationParms(TUint8 aDeviceIndex)
+	{
+	// Mark this instance as being associated with the requested index 
+	iDeviceIndex = aDeviceIndex;
+	// Mark the requested index as being associated with this instance
+	// Atomic operation ensures store is flushed from cache and committed 
+	// to global memory
+	__e32_atomic_store_ord_ptr(&(DRpmbDevicePtrs[iDeviceIndex]),this);
+	}
+
+
+void DRpmbDevice::ClearSynchronisationParms()
+	{
+	// Serialise access to global pointer array and it's local index
+	NKern::FMWait(&iSynchronisationParmsMutex);
+	if (iDeviceIndex < KMaxPBusSockets*4)
+		{
+		// Atomic operation for load from global memory and not from cache
+		DRpmbDevice * ptrTableEntry = 
+			(DRpmbDevice *)__e32_atomic_load_acq_ptr(&(DRpmbDevicePtrs[iDeviceIndex]));
+		// This instance of DRpmbDevice is associated with an index
+		// The associated index MUST be associated with this instance
+		__ASSERT_ALWAYS((ptrTableEntry == this), Kern::Fault(__FILE__, __LINE__));
+		// Disassociate index and instance
+		// Atomic operation ensures store is flushed from cache and committed 
+		// to global memory
+		__e32_atomic_store_ord_ptr(&(DRpmbDevicePtrs[iDeviceIndex]),NULL);
+		iDeviceIndex = KIndexNotAssigned;
+		}
+	// Serialise access to global pointer array and it's local index
+	NKern::FMSignal(&iSynchronisationParmsMutex);
+	}
+
+EXPORT_C TInt DRpmbDevice::Open(TUint aDeviceIndex)
+    {
+    //
+    //eMMC4.4+ devices have RPMB partitions and each MMC device may be configured as having an RPMB 
+	//partition in the baseport
+	//This function creates an MMC stack session for device aDeviceIndex
+	//This is used to access the RPMB partition on that device
+	//Extensions that use this interface during system startup should be located AFTER the RPMB and MMC
+	//extesnions in the system ROM and should not call this interface synchronously from a system
+	//startup initialisation routine
+	//aDeviceIndex the index of the device supporting the RPMB partition
+	//Returns KerrNone if successful
+	//Returns KErrNotReady if the baseport configuration hasn't been read yet
+    //Returns KErrNotSupported if the baseport configuration does not have a valid RPMB partition 
+	//or RPMB is not supported by the media device
+	//Otherwise retruns a systemwide error code
+    //
+    OstTrace0(TRACE_FLOW, DRPMBDEVICE_OPEN_1, "RPMB: >DrpmbDevice::Open");
+    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: >DrpmbDevice::Open"));
+	
+	TRpmbDeviceParms params;
+	params.iCardNumber = 0;
+	params.iSocketPtr = NULL;	
+    MRpmbInfo* rpmbInterface = NULL;
+	
+	TInt r = MMCGetExtInterface(KInterfaceRpmb, (MMCMExtInterface*&) rpmbInterface);
+	// MMCGetExtInterface currently returns KErrNotReady with rpmbInterface == NULL if the RPMB parameters 
+	// haven't yet been populated
+	// proveided any calling extension is located AFTER the RPMB and MMC extesnions in the system ROM and 
+	// does not call this interface synchronously from a system initialisation routine the following 
+	// shouldn't be asserted 
+	OstTrace1(TRACE_FLOW, DRPMBDEVICE_OPEN_2, "RPMB: DrpmbDevice Get Interface err = %d", r);
+	__KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: DrpmbDevice Get Interface err = %d", r));
+	__ASSERT_ALWAYS(r == KErrNone, Kern::Fault(__FILE__, __LINE__));
+		
+	if (rpmbInterface == NULL)
+		{
+		// unexpected error since MMCGetExtInterface didn't return an error
+	    OstTrace0(TRACE_FLOW, DRPMBDEVICE_OPEN_3, "RPMB: DrpmbDevice Null rpmbInterface");
+	    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: DrpmbDevice Null rpmbInterface"));
+		return KErrGeneral;
+		}
+		
+	// Interface currently supports a single device, device index = 0   
+	r = rpmbInterface->RpmbInfo(aDeviceIndex, params);
+    if(r != KErrNone)
+        {
+		// requested index non zero or baseport not configured with RPMB capable MMC device
+        OstTrace1(TRACE_FLOW, DRPMBDEVICE_OPEN_4, "RPMB: DrpmbDevice requested index non zero or baseport not configured, err = %d", r);
+        __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: DrpmbDevice requested index non zero or baseport not configured, err = %d", r));
+		return r;
+		}
+   
+    iSocket = params.iSocketPtr;
+	// iSocket cannot be NULL 
+	// TMMCardControllerInterface::RegisterMediaDevices ensures that the assigned value is not NULL
+	__ASSERT_ALWAYS((iSocket!=NULL), Kern::Fault(__FILE__, __LINE__));
+    
+	// Serialise access to global pointer array and it's local index
+	NKern::FMWait(&iSynchronisationParmsMutex);
+
+	if (iDeviceIndex != KIndexNotAssigned)
+		{
+		// This instance of DRpmbDevice is already open
+		if (iDeviceIndex == aDeviceIndex)
+			{
+			// Serialise access to global pointer array and it's local index
+			NKern::FMSignal(&iSynchronisationParmsMutex);
+			// Already open with requested index
+		    OstTrace0(TRACE_FLOW, DRPMBDEVICE_OPEN_5, "RPMB: DrpmbDevice already open with requested index");
+		    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: DrpmbDevice already open with requested index"));
+			return KErrNone;
+			}
+		else
+			{
+			// Serialise access to global pointer array and it's local index
+			NKern::FMSignal(&iSynchronisationParmsMutex);
+			// Already open with other index
+	        OstTrace0(TRACE_FLOW, DRPMBDEVICE_OPEN_6, "RPMB: DrpmbDevice already open with other index");
+	        __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: DrpmbDevice already open with other index"));
+			return KErrInUse;
+			}
+		}
+	else
+		{
+		// This instance of DRpmbDevice is not open
+
+		// Atomic operation for load from global memory and not from cache
+		DRpmbDevice * ptrTableEntry = 
+			(DRpmbDevice *)__e32_atomic_load_acq_ptr(&(DRpmbDevicePtrs[aDeviceIndex]));
+
+		if (ptrTableEntry == NULL)
+			{
+			SetSynchronisationParms((TUint8)(aDeviceIndex));
+			}
+		else
+			{
+			// Requested index cannot be associated with this instance of DRpmbdevice  
+			__ASSERT_ALWAYS(ptrTableEntry != this, Kern::Fault(__FILE__, __LINE__));
+			// Serialise access to global pointer array and it's local index
+			NKern::FMSignal(&iSynchronisationParmsMutex);
+			// Requested index already associated with a different instance of DRpmbDevice
+	        OstTrace0(TRACE_FLOW, DRPMBDEVICE_OPEN_7, "RPMB: DrpmbDevice requested index already associated with a different instance of DrpmbDevice");
+	        __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: DrpmbDevice requested index already associated with a different instance of DrpmbDevice"));
+			return KErrInUse;
+			}
+		}
+	NKern::FMSignal(&iSynchronisationParmsMutex);
+
+	r = PowerUpStack();
+    if (r != KErrNone && r != KErrCompletion)
+        {
+        // Stack wasn't already powered up and failed to power up
+		ClearSynchronisationParms();
+	    OstTrace1(TRACE_FLOW, DRPMBDEVICE_OPEN_8, "RPMB: DrpmbDevice wasn't already powered up and failed with err = %d", r);
+	    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: DrpmbDevice wasn't already powered up and failed with err = %d", r));
+		return r;
+        }
+    
+    DMMCStack* stack = iSocket->Stack(KBusNumber);
+    if(stack == NULL)
+        {
+		// KBusNumber = 0 so iSocket->Stack() returns iSocket->iStack 
+		// Expect this to have been pre-assigned
+		// Expect a socket to be bound to a stack
+        ClearSynchronisationParms();
+        OstTrace0(TRACE_FLOW, DRPMBDEVICE_OPEN_9, "RPMB: stack is NULL");
+        __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: stack is NULL"));
+        return KErrNoMemory;
+        }
+    
+    iCard = stack->CardP(params.iCardNumber);
+    if(iCard == NULL) 
+        {
+		// stack->CardP() returns stack->iCardArray->CardP(params.iCardNumber)
+		// Expect this to have been pre-assigned
+		// Expect card array to point to a card
+        ClearSynchronisationParms();
+        OstTrace0(TRACE_FLOW, DRPMBDEVICE_OPEN_10, "RPMB: card pointer is NULL");
+        __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: card pointer is NULL"));
+        return KErrNoMemory;
+        }
+    
+	NKern::ThreadEnterCS(); 
+    iSession = stack->AllocSession(iSessionEndCallBack);
+	NKern::ThreadLeaveCS(); 
+    if (iSession == NULL)
+        {
+		// DMMCStack::AllocSession() failed to create a new instance off DMMCSession
+        OstTrace0(TRACE_FLOW, DRPMBDEVICE_OPEN_11, "RPMB: failed to create a new instance of DMMCSession");
+        __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: failed to create a new instance of DMMCSession"));
+        return KErrNoMemory; 
+        }
+
+    iSession->SetStack(stack);
+    iSession->SetCard(iCard);
+    
+    TInt bufLen, minorBufLen;
+    stack->BufferInfo(iIntBuf, bufLen, minorBufLen);
+	// mmc media driver reserved the first KRpmbOneFramePacketLength bytes of the 
+	// PSL buffer to be used for RPMB requests / responses
+	iIntBuf += minorBufLen;
+    
+    if(iCard->ExtendedCSD().ExtendedCSDRev() < 5 || iCard->ExtendedCSD().RpmbSize() == 0)
+        {
+		// RPMB is not supported on selected hardware
+        ClearSynchronisationParms();
+        OstTrace0(TRACE_FLOW, DRPMBDEVICE_OPEN_12, "RPMB: feature is not supported on selected hardware");
+        __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: feature is not supported on selected hardware"));
+        return KErrNotSupported;
+        }
+    OstTrace0(TRACE_FLOW, DRPMBDEVICE_OPEN_13, "RPMB: <DrpmbDevice::Open");
+    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: <DrpmbDevice::Open"));
+    return KErrNone;
+    }
+
+EXPORT_C void DRpmbDevice::Close()
+    {
+	//
+    //Closes an interaction with an RPMB configured device 
+    //
+    OstTrace0(TRACE_FLOW, DRPMBDEVICE_CLOSE_1, "RPMB: >DrpmbDevice::Close");
+    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: >DrpmbDevice::Close"));
+ 	
+	ClearSynchronisationParms();
+    
+	iBusCallBack.Remove();
+
+	NKern::ThreadEnterCS();
+
+    if(iPowerUpSemaphore)
+		{
+        iPowerUpSemaphore->Close(NULL);
+		iPowerUpSemaphore = NULL;
+		}
+
+	if (iSession)
+		{
+		delete iSession;
+		iSession = NULL;
+		}
+
+	if(iRequestSemaphore)
+		{
+        iRequestSemaphore->Close(NULL);
+		iRequestSemaphore = NULL;
+		}
+   
+	NKern::ThreadLeaveCS();
+	OstTrace0(TRACE_FLOW, DRPMBDEVICE_CLOSE_2, "RPMB: <DrpmbDevice::Close");
+	__KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: <DrpmbDevice::Close"));
+    }
+
+EXPORT_C TInt DRpmbDevice::SendAccessRequest(TDes8& aRpmbRequest, TDes8& aRpmbResponse)
+    {
+    //
+    //sends a request to be handled by the RPMB partition
+    //aRpmbRequest - the physically contiguous region of memory containg the request to be handled
+    //aRpmbResponse - the physically contiguous region memory to where the response from the hardware will be written
+	//returns KErrNone if successful
+	//returns system wide epoc error code if set on call back from stack
+	//returns KErrArgument for invalid descriptor argument length
+	//returns KErrNotReady if not opened
+	//retruns KErrNotready if MMC stack is processing a posponed power down or a postponed media change event
+	//otherwise returns system wide error code 
+	//
+    
+    OstTrace0(TRACE_FLOW, DRPMBDEVICE_SENDACCESSREQUEST_1, "RPMB: >DrpmbDevice::SendAccessRequest");
+    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: >DrpmbDevice::SendAccessRequest"));
+
+	if ((aRpmbRequest.Length() != KRpmbOneFramePacketLength) || (aRpmbResponse.Length() != KRpmbOneFramePacketLength))
+		{
+		// insist on single frame access as mutiple read and multiple write (reliable write) notb yet supported
+		return KErrArgument;
+		}
+
+	if (iSession == NULL)
+        {
+		// DRpmbDevice::Open not called at all
+		// or not called after DRpmbDevice::Close
+	    OstTrace0(TRACE_FLOW, DRPMBDEVICE_SENDACCESSREQUEST_2, "RPMB: DMMCSession is NULL");
+	    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: DMMCSession is NULL"));
+        return KErrNotReady;
+        }
+
+	if (iRequestSemaphore == NULL)
+		{
+		// iRequestSemaphore zero filled prior to contruction
+		// create semaphore for waiting on stack
+		NKern::ThreadEnterCS();
+		TInt r = Kern::SemaphoreCreate(iRequestSemaphore,_L("RPMBRequestSemaphore"), 0);
+		NKern::ThreadLeaveCS();
+		if (r != KErrNone)
+			{
+			// failed to create semaphore 
+		    OstTrace1(TRACE_FLOW, DRPMBDEVICE_SENDACCESSREQUEST_3, "RPMB: failed to create semaphore err = %d", r);
+		    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: failed to create semaphore err = %d", r));
+			return r;
+			}
+		}
+
+    memcpy(iIntBuf, aRpmbRequest.Ptr(), KRpmbOneFramePacketLength);
+     
+    TInt r = iSocket->InCritical();
+	// may be KErrNotready if MMC stack is processing a posponed power down event or a postponed media change event
+	if (r == KErrNone)
+       {
+    iSession->SetPartition(TExtendedCSD::ESelectRPMB);            //Set RPMB Partition
+    iSession->ResetCommandStack();
+    iSession->FillCommandArgs(KDeviceAddress, KRpmbOneFramePacketLength, iIntBuf, KRpmbOneFramePacketLength);
+    iSession->iSessionID = ECIMRpmbAccess;
+
+        r = iSession->Engage();
+        if(r == KErrNone)
+            {
+			Kern::SemaphoreWait(*iRequestSemaphore);
+			memcpy((TUint8 *)aRpmbResponse.Ptr(), iIntBuf, KRpmbOneFramePacketLength);
+		    OstTrace1(TRACE_FLOW, DRPMBDEVICE_SENDACCESSREQUEST_4, "RPMB: request complete with %d", iSession->EpocErrorCode());
+		    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: request complete with %d", iSession->EpocErrorCode()));
+			return iSession->EpocErrorCode();  
+            }
+        }
+ 
+    iSocket->EndInCritical();
+    
+    OstTrace1(TRACE_FLOW, DRPMBDEVICE_SENDACCESSREQUEST_5, "RPMB: <DrpmbDevice::SendAccessRequest complete %d", r);
+    __KTRACE_OPT(KPBUS1, Kern::Printf("RPMB: <DrpmbDevice::SendAccessRequest complete %d", r));
+    
+    return r;
+    }
+
+DECLARE_STANDARD_EXTENSION()
+    {
+    __KTRACE_OPT(KBOOT,Kern::Printf("RPMBEXT.DLL DECLARE_STANDARD_EXTENSION entry point"));      
+	return KErrNone;
+    }
+