blob: b7412687dc012ecc01b4d56f8f93ff4cf8fd3402 [file] [log] [blame]
/** @addtogroup MCD_MCDIMPL_DAEMON_DEV
* @{
* @file
*/
/*
* <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cstdlib>
#include <pthread.h>
#include "McTypes.h"
#include "DeviceScheduler.h"
#include "DeviceIrqHandler.h"
#include "ExcDevice.h"
#include "Connection.h"
#include "TrustletSession.h"
#include "MobiCoreDevice.h"
#include "Mci/mci.h"
#include "mcLoadFormat.h"
#include "log.h"
#include "public/MobiCoreDevice.h"
//------------------------------------------------------------------------------
MobiCoreDevice::MobiCoreDevice()
{
mcFault = false;
}
//------------------------------------------------------------------------------
MobiCoreDevice::~MobiCoreDevice()
{
delete mcVersionInfo;
mcVersionInfo = NULL;
}
//------------------------------------------------------------------------------
TrustletSession *MobiCoreDevice::getTrustletSession(uint32_t sessionId)
{
for (trustletSessionIterator_t session = trustletSessions.begin();
session != trustletSessions.end();
++session) {
TrustletSession *tsTmp = *session;
if (tsTmp->sessionId == sessionId) {
return tsTmp;
}
}
return NULL;
}
void MobiCoreDevice::cleanSessionBuffers(TrustletSession *session)
{
CWsm_ptr pWsm = session->popBulkBuff();
while (pWsm) {
unlockWsmL2(pWsm->handle);
pWsm = session->popBulkBuff();
}
}
//------------------------------------------------------------------------------
void MobiCoreDevice::removeTrustletSession(uint32_t sessionId)
{
for (trustletSessionIterator_t session = trustletSessions.begin();
session != trustletSessions.end();
++session) {
if ((*session)->sessionId == sessionId) {
cleanSessionBuffers(*session);
trustletSessions.erase(session);
return;
}
}
}
//------------------------------------------------------------------------------
Connection *MobiCoreDevice::getSessionConnection(uint32_t sessionId, notification_t *notification)
{
Connection *con = NULL;
TrustletSession *ts = NULL;
ts = getTrustletSession(sessionId);
if (ts == NULL) {
return NULL;
}
con = ts->notificationConnection;
if (con == NULL) {
ts->queueNotification(notification);
return NULL;
}
return con;
}
//------------------------------------------------------------------------------
bool MobiCoreDevice::open(Connection *connection)
{
// Link this device to the connection
connection->connectionData = this;
return true;
}
//------------------------------------------------------------------------------
/**
* Close device.
*
* Removes all sessions to a connection. Though, clientLib rejects the closeDevice()
* command if still sessions connected to the device, this is needed to clean up all
* sessions if client dies.
*/
void MobiCoreDevice::close(Connection *connection)
{
trustletSessionList_t::reverse_iterator interator;
static CMutex mutex;
// 1. Iterate through device session to find connection
// 2. Decide what to do with open Trustlet sessions
// 3. Remove & delete deviceSession from vector
// Enter critical section
mutex.lock();
for (interator = trustletSessions.rbegin();
interator != trustletSessions.rend();
interator++) {
TrustletSession *ts = *interator;
if (ts->deviceConnection == connection) {
closeSession(connection, ts->sessionId);
}
}
// Leave critical section
mutex.unlock();
// After the trustlet is done make sure to tell the driver to cleanup
// all the orphaned drivers
cleanupWsmL2();
connection->connectionData = NULL;
}
//------------------------------------------------------------------------------
void MobiCoreDevice::start(void)
{
// Call the device specific initialization
// initDevice();
LOG_I("Starting DeviceIrqHandler...");
// Start the irq handling thread
DeviceIrqHandler::start();
if (schedulerAvailable()) {
LOG_I("Starting DeviceScheduler...");
// Start the scheduling handling thread
DeviceScheduler::start();
} else {
LOG_I("No DeviceScheduler available.");
}
}
//------------------------------------------------------------------------------
void MobiCoreDevice::signalMcpNotification(void)
{
mcpSessionNotification.signal();
}
//------------------------------------------------------------------------------
bool MobiCoreDevice::waitMcpNotification(void)
{
int counter = 5;
while (1) {
// In case of fault just return, nothing to do here
if (mcFault) {
return false;
}
// Wait 10 seconds for notification
if (mcpSessionNotification.wait(10) == false) {
// No MCP answer received and mobicore halted, dump mobicore status
// then throw exception
LOG_I("No MCP answer received in 2 seconds.");
if (getMobicoreStatus() == MC_STATUS_HALT) {
dumpMobicoreStatus();
mcFault = true;
return false;
} else {
counter--;
if (counter < 1) {
mcFault = true;
return false;
}
}
} else {
break;
}
}
// Check healthiness state of the device
if (DeviceIrqHandler::isExiting()) {
LOG_I("waitMcpNotification(): IrqHandler thread died! Joining");
DeviceIrqHandler::join();
LOG_I("waitMcpNotification(): Joined");
LOG_E("IrqHandler thread died!");
return false;
}
if (DeviceScheduler::isExiting()) {
LOG_I("waitMcpNotification(): Scheduler thread died! Joining");
DeviceScheduler::join();
LOG_I("waitMcpNotification(): Joined");
LOG_E("Scheduler thread died!");
return false;
}
return true;
}
//------------------------------------------------------------------------------
mcResult_t MobiCoreDevice::openSession(
Connection *deviceConnection,
loadDataOpenSession_ptr pLoadDataOpenSession,
uint32_t tciHandle,
uint32_t tciLen,
mcDrvRspOpenSessionPayload_ptr pRspOpenSessionPayload)
{
do {
addr_t tci;
uint32_t len;
if (!findContiguousWsm(tciHandle, &tci, &len)) {
LOG_E("Failed to find contiguous WSM %u", tciHandle);
return MC_DRV_ERR_DAEMON_WSM_HANDLE_NOT_FOUND;
}
if (!lockWsmL2(tciHandle)) {
LOG_E("Failed to lock contiguous WSM %u", tciHandle);
return MC_DRV_ERR_DAEMON_WSM_HANDLE_NOT_FOUND;
}
if (tciLen == 0 || tciLen > len) {
LOG_E("Invalid TCI len from client %u, driver = %u",
pLoadDataOpenSession->len, len);
return MC_DRV_ERR_TCI_GREATER_THAN_WSM;
}
// Write MCP open message to buffer
mcpMessage->cmdOpen.cmdHeader.cmdId = MC_MCP_CMD_OPEN_SESSION;
mcpMessage->cmdOpen.uuid = pLoadDataOpenSession->tlHeader->mclfHeaderV2.uuid;
mcpMessage->cmdOpen.wsmTypeTci = WSM_CONTIGUOUS;
mcpMessage->cmdOpen.adrTciBuffer = (uint32_t)(tci);
mcpMessage->cmdOpen.ofsTciBuffer = 0;
mcpMessage->cmdOpen.lenTciBuffer = tciLen;
LOG_I(" Using phys=%p, len=%d as TCI buffer", (addr_t)tci, tciLen);
// check if load data is provided
mcpMessage->cmdOpen.wsmTypeLoadData = WSM_L2;
mcpMessage->cmdOpen.adrLoadData = (uint32_t)pLoadDataOpenSession->baseAddr;
mcpMessage->cmdOpen.ofsLoadData = pLoadDataOpenSession->offs;
mcpMessage->cmdOpen.lenLoadData = pLoadDataOpenSession->len;
memcpy(&mcpMessage->cmdOpen.tlHeader, pLoadDataOpenSession->tlHeader, sizeof(*pLoadDataOpenSession->tlHeader));
// Clear the notifications queue. We asume the race condition we have
// seen in openSession never happens elsewhere
notifications = std::queue<notification_t>();
// Notify MC about a new command inside the MCP buffer
notify(SID_MCP);
// Wait till response from MC is available
if (!waitMcpNotification()) {
// Here Mobicore can be considered dead.
unlockWsmL2(tciHandle);
return MC_DRV_ERR_DAEMON_MCI_ERROR;
}
// Check if the command response ID is correct
if ((MC_MCP_CMD_OPEN_SESSION | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) {
LOG_E("CMD_OPEN_SESSION got invalid MCP command response(0x%X)", mcpMessage->rspHeader.rspId);
// Something is messing with our MCI memory, we cannot know if the Trustlet was loaded.
// Had in been loaded, we are loosing track of it here.
unlockWsmL2(tciHandle);
return MC_DRV_ERR_DAEMON_MCI_ERROR;
}
uint32_t mcRet = mcpMessage->rspOpen.rspHeader.result;
if (mcRet != MC_MCP_RET_OK) {
LOG_E("MCP OPEN returned code %d.", mcRet);
unlockWsmL2(tciHandle);
return MAKE_MC_DRV_MCP_ERROR(mcRet);
}
LOG_I(" After MCP OPEN, we have %d queued notifications",
notifications.size());
// Read MC answer from MCP buffer
TrustletSession *trustletSession = new TrustletSession(
deviceConnection,
mcpMessage->rspOpen.sessionId);
pRspOpenSessionPayload->sessionId = trustletSession->sessionId;
pRspOpenSessionPayload->deviceSessionId = (uint32_t)trustletSession;
pRspOpenSessionPayload->sessionMagic = trustletSession->sessionMagic;
trustletSessions.push_back(trustletSession);
trustletSession->addBulkBuff(new CWsm((void *)pLoadDataOpenSession->offs, pLoadDataOpenSession->len, tciHandle, 0));
// We have some queued notifications and we need to send them to them
// trustlet session
while (!notifications.empty()) {
trustletSession->queueNotification(&notifications.front());
notifications.pop();
}
} while (0);
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
TrustletSession *MobiCoreDevice::registerTrustletConnection(
Connection *connection,
MC_DRV_CMD_NQ_CONNECT_struct *cmdNqConnect
)
{
LOG_I(" Registering notification socket with Service session %d.",
cmdNqConnect->sessionId);
LOG_V(" Searching sessionId %d with sessionMagic %d",
cmdNqConnect->sessionId,
cmdNqConnect->sessionMagic);
for (trustletSessionIterator_t iterator = trustletSessions.begin();
iterator != trustletSessions.end();
++iterator) {
TrustletSession *ts = *iterator;
if (ts != (TrustletSession *) (cmdNqConnect->deviceSessionId)) {
continue;
}
if ( (ts->sessionMagic != cmdNqConnect->sessionMagic)
|| (ts->sessionId != cmdNqConnect->sessionId)) {
continue;
}
ts->notificationConnection = connection;
LOG_I(" Found Service session, registered connection.");
return ts;
}
LOG_I("registerTrustletConnection(): search failed");
return NULL;
}
//------------------------------------------------------------------------------
mcResult_t MobiCoreDevice::closeSession(uint32_t sessionId)
{
LOG_I(" Write MCP CLOSE message to MCI, notify and wait");
// Write MCP close message to buffer
mcpMessage->cmdClose.cmdHeader.cmdId = MC_MCP_CMD_CLOSE_SESSION;
mcpMessage->cmdClose.sessionId = sessionId;
// Notify MC about the availability of a new command inside the MCP buffer
notify(SID_MCP);
// Wait till response from MSH is available
if (!waitMcpNotification()) {
return MC_DRV_ERR_DAEMON_MCI_ERROR;
}
// Check if the command response ID is correct
if ((MC_MCP_CMD_CLOSE_SESSION | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) {
LOG_E("CMD_CLOSE_SESSION got invalid MCP response");
return MC_DRV_ERR_DAEMON_MCI_ERROR;
}
// Read MC answer from MCP buffer
uint32_t mcRet = mcpMessage->rspOpen.rspHeader.result;
if (mcRet != MC_MCP_RET_OK) {
LOG_E("CMD_CLOSE_SESSION error %d", mcRet);
return MAKE_MC_DRV_MCP_ERROR(mcRet);
}
return MC_DRV_OK;
}
/**
* TODO-2012-09-19-haenellu: Do some more checks here, otherwise rogue clientLib
* can close sessions from different TLCs. That is, deviceConnection is ignored below.
*
* Need connection as well as according session ID, so that a client can not
* close sessions not belonging to him.
*/
mcResult_t MobiCoreDevice::closeSession(Connection *deviceConnection, uint32_t sessionId)
{
TrustletSession *ts = getTrustletSession(sessionId);
if (ts == NULL) {
LOG_E("no session found with id=%d", sessionId);
return MC_DRV_ERR_DAEMON_UNKNOWN_SESSION;
}
uint32_t mcRet = closeSession(sessionId);
if (mcRet != MC_DRV_OK) {
return mcRet;
}
// remove objects
removeTrustletSession(sessionId);
delete ts;
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t MobiCoreDevice::mapBulk(uint32_t sessionId, uint32_t handle, uint32_t pAddrL2,
uint32_t offsetPayload, uint32_t lenBulkMem, uint32_t *secureVirtualAdr)
{
TrustletSession *ts = getTrustletSession(sessionId);
if (ts == NULL) {
LOG_E("no session found with id=%d", sessionId);
return MC_DRV_ERR_DAEMON_UNKNOWN_SESSION;
}
// TODO-2012-09-06-haenellu: Think about not ignoring the error case, ClientLib does not allow this.
ts->addBulkBuff(new CWsm((void *)offsetPayload, lenBulkMem, handle, (void *)pAddrL2));
// Write MCP map message to buffer
mcpMessage->cmdMap.cmdHeader.cmdId = MC_MCP_CMD_MAP;
mcpMessage->cmdMap.sessionId = sessionId;
mcpMessage->cmdMap.wsmType = WSM_L2;
mcpMessage->cmdMap.adrBuffer = (uint32_t)(pAddrL2);
mcpMessage->cmdMap.ofsBuffer = offsetPayload;
mcpMessage->cmdMap.lenBuffer = lenBulkMem;
// Notify MC about the availability of a new command inside the MCP buffer
notify(SID_MCP);
// Wait till response from MC is available
if (!waitMcpNotification()) {
return MC_DRV_ERR_DAEMON_MCI_ERROR;
}
// Check if the command response ID is correct
if (mcpMessage->rspHeader.rspId != (MC_MCP_CMD_MAP | FLAG_RESPONSE)) {
LOG_E("CMD_MAP got invalid MCP response");
return MC_DRV_ERR_DAEMON_MCI_ERROR;
}
uint32_t mcRet = mcpMessage->rspMap.rspHeader.result;
if (mcRet != MC_MCP_RET_OK) {
LOG_E("MCP MAP returned code %d.", mcRet);
return MAKE_MC_DRV_MCP_ERROR(mcRet);
}
*secureVirtualAdr = mcpMessage->rspMap.secureVirtualAdr;
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t MobiCoreDevice::unmapBulk(uint32_t sessionId, uint32_t handle,
uint32_t secureVirtualAdr, uint32_t lenBulkMem)
{
TrustletSession *ts = getTrustletSession(sessionId);
if (ts == NULL) {
LOG_E("no session found with id=%d", sessionId);
return MC_DRV_ERR_DAEMON_UNKNOWN_SESSION;
}
// Write MCP unmap command to buffer
mcpMessage->cmdUnmap.cmdHeader.cmdId = MC_MCP_CMD_UNMAP;
mcpMessage->cmdUnmap.sessionId = sessionId;
mcpMessage->cmdUnmap.wsmType = WSM_L2;
mcpMessage->cmdUnmap.secureVirtualAdr = secureVirtualAdr;
mcpMessage->cmdUnmap.lenVirtualBuffer = lenBulkMem;
// Notify MC about the availability of a new command inside the MCP buffer
notify(SID_MCP);
// Wait till response from MC is available
if (!waitMcpNotification()) {
return MC_DRV_ERR_DAEMON_MCI_ERROR;
}
// Check if the command response ID is correct
if (mcpMessage->rspHeader.rspId != (MC_MCP_CMD_UNMAP | FLAG_RESPONSE)) {
LOG_E("CMD_OPEN_SESSION got invalid MCP response");
return MC_DRV_ERR_DAEMON_MCI_ERROR;
}
uint32_t mcRet = mcpMessage->rspUnmap.rspHeader.result;
if (mcRet != MC_MCP_RET_OK) {
LOG_E("MCP UNMAP returned code %d.", mcRet);
return MAKE_MC_DRV_MCP_ERROR(mcRet);
} else {
// Just remove the buffer
// TODO-2012-09-06-haenellu: Haven't we removed it already?
if (!ts->removeBulkBuff(handle))
LOG_I("unmapBulk(): no buffer found found with handle=%u", handle);
}
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
void MobiCoreDevice::donateRam(const uint32_t donationSize)
{
// Donate additional RAM to the MobiCore
CWsm_ptr ram = allocateContiguousPersistentWsm(donationSize);
if (NULL == ram) {
LOG_E("Allocation of additional RAM failed");
return;
}
ramType_t ramType = RAM_GENERIC;
addr_t adrBuffer = ram->physAddr;
const uint32_t numPages = donationSize / (4 * 1024);
LOG_I("donateRam(): adrBuffer=%p, numPages=%d, ramType=%d",
adrBuffer,
numPages,
ramType);
do {
// Write MCP open message to buffer
mcpMessage->cmdDonateRam.cmdHeader.cmdId = MC_MCP_CMD_DONATE_RAM;
mcpMessage->cmdDonateRam.adrBuffer = (uint32_t) adrBuffer;
mcpMessage->cmdDonateRam.numPages = numPages;
mcpMessage->cmdDonateRam.ramType = ramType;
// Notify MC about a new command inside the MCP buffer
notify(SID_MCP);
// Wait till response from MC is available
if (!waitMcpNotification()) {
break;
}
// Check if the command response ID is correct
if ((MC_MCP_CMD_DONATE_RAM | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) {
LOG_E("donateRam(): CMD_DONATE_RAM got invalid MCP response - rspId is: %d",
mcpMessage->rspHeader.rspId);
break;
}
uint32_t mcRet = mcpMessage->rspDonateRam.rspHeader.result;
if (MC_MCP_RET_OK != mcRet) {
LOG_E("donateRam(): CMD_DONATE_RAM error %d", mcRet);
break;
}
LOG_I("donateRam() succeeded.");
} while (0);
}
//------------------------------------------------------------------------------
mcResult_t MobiCoreDevice::getMobiCoreVersion(
mcDrvRspGetMobiCoreVersionPayload_ptr pRspGetMobiCoreVersionPayload
)
{
// If MobiCore version info already fetched.
if (mcVersionInfo != NULL) {
pRspGetMobiCoreVersionPayload->versionInfo = *mcVersionInfo;
return MC_DRV_OK;
// Otherwise, fetch it via MCP.
} else {
// Write MCP unmap command to buffer
mcpMessage->cmdGetMobiCoreVersion.cmdHeader.cmdId = MC_MCP_CMD_GET_MOBICORE_VERSION;
// Notify MC about the availability of a new command inside the MCP buffer
notify(SID_MCP);
// Wait till response from MC is available
if (!waitMcpNotification()) {
return MC_DRV_ERR_DAEMON_MCI_ERROR;
}
// Check if the command response ID is correct
if ((MC_MCP_CMD_GET_MOBICORE_VERSION | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) {
LOG_E("MC_MCP_CMD_GET_MOBICORE_VERSION got invalid MCP response");
return MC_DRV_ERR_DAEMON_MCI_ERROR;
}
uint32_t mcRet = mcpMessage->rspGetMobiCoreVersion.rspHeader.result;
if (mcRet != MC_MCP_RET_OK) {
LOG_E("MC_MCP_CMD_GET_MOBICORE_VERSION error %d", mcRet);
return MAKE_MC_DRV_MCP_ERROR(mcRet);
}
pRspGetMobiCoreVersionPayload->versionInfo = mcpMessage->rspGetMobiCoreVersion.versionInfo;
// Store MobiCore info for future reference.
mcVersionInfo = new mcVersionInfo_t();
*mcVersionInfo = pRspGetMobiCoreVersionPayload->versionInfo;
return MC_DRV_OK;
}
}
//------------------------------------------------------------------------------
void MobiCoreDevice::queueUnknownNotification(
notification_t notification
)
{
notifications.push(notification);
}
/** @} */