diff options
Diffstat (limited to 'mobicore/MobiCoreDriverLib/ClientLib/ClientLib.cpp')
-rw-r--r-- | mobicore/MobiCoreDriverLib/ClientLib/ClientLib.cpp | 1494 |
1 files changed, 1494 insertions, 0 deletions
diff --git a/mobicore/MobiCoreDriverLib/ClientLib/ClientLib.cpp b/mobicore/MobiCoreDriverLib/ClientLib/ClientLib.cpp new file mode 100644 index 0000000..8776486 --- /dev/null +++ b/mobicore/MobiCoreDriverLib/ClientLib/ClientLib.cpp @@ -0,0 +1,1494 @@ +/* + * Copyright (c) 2013-2014 TRUSTONIC LIMITED + * All rights reserved. + * + * 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. Neither the name of the TRUSTONIC LIMITED nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT HOLDER OR + * CONTRIBUTORS 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. + */ +/** + * <t-base Driver API. + * + * Functions for accessing <t-base functionality from the normal world. + * Handles sessions and notifications via MCI buffer. + */ +#include <stdint.h> +#ifndef WIN32 +#include <stdbool.h> +#include <list> +#include <errno.h> +#include "assert.h" +#endif + +#include "public/MobiCoreDriverApi.h" + +#ifndef WIN32 +#include "mc_linux.h" +#include "Connection.h" +#include "CMutex.h" +#include "Device.h" +#include "mcVersionHelper.h" + +#include "Daemon/public/MobiCoreDriverCmd.h" +#include "Daemon/public/mcVersion.h" + +#include "log.h" + +#include "Mci/mcimcp.h" + +MC_CHECK_VERSION(DAEMON, 0, 2); + +/** Notification data structure. */ +typedef struct { + uint32_t sessionId; /**< Session ID. */ + int32_t payload; /**< Additional notification information. */ +} notification_t; + +using namespace std; + +static list<Device *> devices; + +// Forward declarations. +uint32_t getDaemonVersion(Connection *devCon, uint32_t *version); + +static CMutex devMutex; +//------------------------------------------------------------------------------ +Device *resolveDeviceId(uint32_t deviceId) +{ + for (list<Device *>::iterator iterator = devices.begin(); + iterator != devices.end(); + ++iterator) { + Device *device = (*iterator); + + if (device->deviceId == deviceId) { + return device; + } + } + return NULL; +} + + +//------------------------------------------------------------------------------ +void addDevice(Device *device) +{ + devices.push_back(device); +} + + +//------------------------------------------------------------------------------ +bool removeDevice(uint32_t deviceId) +{ + for (list<Device *>::iterator iterator = devices.begin(); + iterator != devices.end(); + ++iterator) { + Device *device = (*iterator); + + if (device->deviceId == deviceId) { + devices.erase(iterator); + delete device; + return true; + } + } + return false; +} + +//------------------------------------------------------------------------------ +// Parameter checking functions +// Note that android-ndk renames __func__ to __PRETTY_FUNCTION__ +// see also /prebuilt/ndk/android-ndk-r4/platforms/android-8/arch-arm/usr/include/sys/cdefs.h + +#define CHECK_DEVICE(device) \ + if (NULL == device) \ + { \ + LOG_E("Device has not been found"); \ + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; \ + break; \ + } + +#define CHECK_DEVICE_CLOSED(device, deviceId) \ + if (NULL == device && MC_DEVICE_ID_DEFAULT == deviceId) \ + { \ + LOG_E("Device not open"); \ + mcResult = MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; \ + break; \ + } else \ + CHECK_DEVICE(device); + + + +#define CHECK_NOT_NULL(X) \ + if (NULL == X) \ + { \ + LOG_E("Parameter \""#X "\" is NULL"); \ + mcResult = MC_DRV_ERR_NULL_POINTER; \ + break; \ + } + +#define CHECK_SESSION(S,SID) \ + if (NULL == S) \ + { \ + LOG_E("Session %03x not found", SID); \ + mcResult = MC_DRV_ERR_UNKNOWN_SESSION; \ + break; \ + } + +//------------------------------------------------------------------------------ +// Socket marshaling and checking functions +#define SEND_TO_DAEMON(CONNECTION, COMMAND, ...) \ +{ \ + COMMAND ##_struct x = { \ + COMMAND, \ + __VA_ARGS__ \ + }; \ + int ret = CONNECTION->writeData(&x, sizeof x); \ + if(ret < 0) { \ + LOG_E("sending to Daemon failed."); \ + mcResult = MC_DRV_ERR_SOCKET_WRITE; \ + break; \ + } \ +} + +#define RECV_FROM_DAEMON(CONNECTION, RSP_STRUCT) \ +{ \ + int rlen = CONNECTION->readData( \ + RSP_STRUCT, \ + sizeof(*RSP_STRUCT)); \ + if (rlen <= 0) { \ + LOG_E("reading from Daemon failed"); \ + mcResult = MC_DRV_ERR_SOCKET_READ; \ + break; \ + } \ + if (rlen != sizeof(*RSP_STRUCT) && rlen != sizeof(mcDrvResponseHeader_t)) {\ + LOG_E("wrong buffer length %i received from Daemon", rlen); \ + mcResult = MC_DRV_ERR_SOCKET_LENGTH; \ + break; \ + } \ +} +#endif /* WIN32 */ + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcOpenDevice(uint32_t deviceId) +{ + mcResult_t mcResult = MC_DRV_OK; +#ifndef WIN32 + + Connection *devCon = NULL; + + devMutex.lock(); + LOG_I("===%s(%i)===", __FUNCTION__, deviceId); + + do { + Device *device = resolveDeviceId(deviceId); + if (device != NULL) { + LOG_E("Device %d already opened", deviceId); + mcResult = MC_DRV_OK; + device->openCount++; + break; + } + + // Handle SIGPIPE inside write() + // If Daemon crashes and ClientLib writes to named socket, + // a sigpipe is send to ClientLib/TLC and kills it. + signal(SIGPIPE, SIG_IGN); + + // Open new connection to device + devCon = new Connection(); + if (!devCon->connect(SOCK_PATH)) { + LOG_W(" Could not connect to %s socket", SOCK_PATH); + mcResult = MC_DRV_ERR_SOCKET_CONNECT; + break; + } + + // Runtime check of Daemon version. + char *errmsg; + uint32_t version = 0; + mcResult = getDaemonVersion(devCon, &version); + if (mcResult != MC_DRV_OK) { + break; + } + if (!checkVersionOkDAEMON(version, &errmsg)) { + LOG_E("%s", errmsg); + mcResult = MC_DRV_ERR_DAEMON_VERSION; + break; + } + LOG_I(" %s", errmsg); + + // Forward device open to the daemon and read result + SEND_TO_DAEMON(devCon, MC_DRV_CMD_OPEN_DEVICE, deviceId); + + RECV_FROM_DAEMON(devCon, &mcResult); + + if (mcResult != MC_DRV_OK) { + LOG_W(" %s(): Request at Daemon failed, respId=%x ", __FUNCTION__, mcResult); + break; + } + + // there is no payload to read + + device = new Device(deviceId, devCon); + mcResult = device->open("/dev/" MC_USER_DEVNODE); + if (mcResult != MC_DRV_OK) { + delete device; + // devCon is freed in the Device destructor + devCon = NULL; + LOG_E("Could not open device file: /dev/%s", MC_USER_DEVNODE); + break; + } + + addDevice(device); + device->openCount++; + + } while (false); + + devMutex.unlock(); + if (mcResult != MC_DRV_OK) { + if (devCon != NULL) + delete devCon; + LOG_I(" Device not opened."); + } else { + LOG_I(" Successfully opened the device."); + } + +#endif /* WIN32 */ + return mcResult; +} + + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcCloseDevice( + uint32_t deviceId +) +{ + mcResult_t mcResult = MC_DRV_OK; +#ifndef WIN32 + + devMutex.lock(); + LOG_I("===%s(%i)===", __FUNCTION__, deviceId); + do { + Device *device = resolveDeviceId(deviceId); + CHECK_DEVICE_CLOSED(device, deviceId); + + Connection *devCon = device->connection; + + if (device->openCount != 1) { + device->openCount--; + break; + } + + // Check if daemon is still alive + if (!devCon->isConnectionAlive()) { + removeDevice(deviceId); + LOG_E("Daemon is dead removing device"); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + // Return if not all sessions have been closed + // TODO-2012-08-31-haenellu: improve check, if device connection is dead, this makes no more sense. + if (device->hasSessions()) { + LOG_E("Trying to close device while sessions are still pending."); + mcResult = MC_DRV_ERR_SESSION_PENDING; + break; + } + + SEND_TO_DAEMON(devCon, MC_DRV_CMD_CLOSE_DEVICE); + + RECV_FROM_DAEMON(devCon, &mcResult); + + if (mcResult != MC_DRV_OK) { + LOG_W(" %s(): Request at Daemon failed, respId=%d ", __FUNCTION__, mcResult); + break; + } + + removeDevice(deviceId); + + } while (false); + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcOpenSession( + mcSessionHandle_t *session, + const mcUuid_t *uuid, + uint8_t *tci, + uint32_t len +) +{ + mcResult_t mcResult = MC_DRV_OK; +#ifndef WIN32 + + devMutex.lock(); + LOG_I("===%s()===", __FUNCTION__); + + BulkBufferDescriptor *bulkBuf = NULL; + + do { + uint32_t handle = 0; + CHECK_NOT_NULL(session); + CHECK_NOT_NULL(uuid); + + if (len > MC_MAX_TCI_LEN) { + LOG_E("TCI length is longer than %d", MC_MAX_TCI_LEN); + mcResult = MC_DRV_ERR_TCI_TOO_BIG; + break; + } + + // Get the device associated with the given session + Device *device = resolveDeviceId(session->deviceId); + CHECK_DEVICE(device); + + Connection *devCon = device->connection; + + // First assume the TCI is a contiguous buffer + // Get the physical address of the given TCI + CWsm_ptr pWsm = device->findContiguousWsm(tci); + if (pWsm == NULL) { + if (tci != NULL && len != 0) { + // Then assume it's a normal buffer that needs to be mapped + mcResult = device->mapBulkBuf(tci, len, &bulkBuf); + if (mcResult != MC_DRV_OK) { + LOG_E("Registering buffer failed. ret=%x", mcResult); + mcResult = MC_DRV_ERR_WSM_NOT_FOUND; + break; + } + handle = bulkBuf->handle; + } else if ( len != 0 ) { + LOG_E("mcOpenSession(): length is more than allocated TCI"); + mcResult = MC_DRV_ERR_TCI_GREATER_THAN_WSM; + break; + } + } else { + if (pWsm->len < len) { + LOG_E("mcOpenSession(): length is more than allocated TCI"); + mcResult = MC_DRV_ERR_TCI_GREATER_THAN_WSM; + break; + } + handle = pWsm->handle; + } + + SEND_TO_DAEMON(devCon, MC_DRV_CMD_OPEN_SESSION, + session->deviceId, + *uuid, + (uint32_t)((uintptr_t)tci & 0xFFF), + handle, + len); + + // Read command response + RECV_FROM_DAEMON(devCon, &mcResult); + + if (mcResult != MC_DRV_OK) { + // TODO-2012-09-06-haenellu: Remove this code once tests can handle it + + if (MC_DRV_ERROR_MAJOR(mcResult) != MC_DRV_ERR_MCP_ERROR) { + LOG_E("Daemon could not open session, responseId %d.", mcResult); + } else { + uint32_t mcpResult = MC_DRV_ERROR_MCP(mcResult); + LOG_E("<t-base reported failing of MC_MCP_CMD_OPEN_SESSION command, mcpResult %d.", mcpResult); + + // IMPROVEMENT-2012-09-03-haenellu: Remove this switch case and use MCP code in tests. + switch (mcpResult) { + case MC_MCP_RET_ERR_WRONG_PUBLIC_KEY: + mcResult = MC_DRV_ERR_WRONG_PUBLIC_KEY; + break; + case MC_MCP_RET_ERR_CONTAINER_TYPE_MISMATCH: + mcResult = MC_DRV_ERR_CONTAINER_TYPE_MISMATCH; + break; + case MC_MCP_RET_ERR_CONTAINER_LOCKED: + mcResult = MC_DRV_ERR_CONTAINER_LOCKED; + break; + case MC_MCP_RET_ERR_SP_NO_CHILD: + mcResult = MC_DRV_ERR_SP_NO_CHILD; + break; + case MC_MCP_RET_ERR_TL_NO_CHILD: + mcResult = MC_DRV_ERR_TL_NO_CHILD; + break; + case MC_MCP_RET_ERR_UNWRAP_ROOT_FAILED: + mcResult = MC_DRV_ERR_UNWRAP_ROOT_FAILED; + break; + case MC_MCP_RET_ERR_UNWRAP_SP_FAILED: + mcResult = MC_DRV_ERR_UNWRAP_SP_FAILED; + break; + case MC_MCP_RET_ERR_UNWRAP_TRUSTLET_FAILED: + mcResult = MC_DRV_ERR_UNWRAP_TRUSTLET_FAILED; + break; + default: + // TODO-2012-09-06-haenellu: Remove line and adapt codes in tests. + mcResult = MC_DRV_ERR_MCP_ERROR; + break; + } + } + break; // loading of Trustlet failed, unlock mutex and return + } + + // read payload + mcDrvRspOpenSessionPayload_t rspOpenSessionPayload; + RECV_FROM_DAEMON(devCon, &rspOpenSessionPayload); + + // Register session with handle + session->sessionId = rspOpenSessionPayload.sessionId; + + LOG_I(" Service is started. Setting up channel for notifications."); + + // Set up second channel for notifications + Connection *sessionConnection = new Connection(); + if (!sessionConnection->connect(SOCK_PATH)) { + LOG_E("Could not connect to %s", SOCK_PATH); + delete sessionConnection; + // Here we know we couldn't connect to the Daemon. + // Maybe we should use existing connection to close Trustlet. + mcResult = MC_DRV_ERR_SOCKET_CONNECT; + break; + } + + do { + SEND_TO_DAEMON(sessionConnection, MC_DRV_CMD_NQ_CONNECT, + session->deviceId, + session->sessionId, + rspOpenSessionPayload.deviceSessionId, + rspOpenSessionPayload.sessionMagic); + + RECV_FROM_DAEMON(sessionConnection, &mcResult); + + if (mcResult != MC_DRV_OK) { + LOG_E("CMD_NQ_CONNECT failed, respId=%d", mcResult); + break; + } + + } while (0); + if (mcResult != MC_DRV_OK) { + delete sessionConnection; + // Here we know we couldn't communicate well with the Daemon. + // Maybe we should use existing connection to close Trustlet. + break; // unlock mutex and return + } + + // there is no payload. + + // Session has been established, new session object must be created + Session *sessionObj = device->createNewSession(session->sessionId, sessionConnection); + // If the session tci was a mapped buffer then register it + if (bulkBuf) + sessionObj->addBulkBuf(bulkBuf); + + LOG_I(" Successfully opened session %03x.", session->sessionId); + + } while (false); + + if (mcResult != MC_DRV_OK && bulkBuf) { + delete bulkBuf; + } + +// TODO: enable as soon as there are more error codes +// if (mcResult == MC_DRV_ERR_SOCKET_WRITE || mcResult == MC_DRV_ERR_SOCKET_READ) { +// LOG_E("Connection is dead, removing device."); +// removeDevice(session->deviceId); +// } + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcOpenTrustlet( + mcSessionHandle_t *session, + mcSpid_t spid, + uint8_t *trustlet, + uint32_t tlen, + uint8_t *tci, + uint32_t len +) +{ + mcResult_t mcResult = MC_DRV_OK; +#ifndef WIN32 + + devMutex.lock(); + LOG_I("===%s()===", __FUNCTION__); + + BulkBufferDescriptor *bulkBuf = NULL; + + do { + uint32_t handle = 0; + CHECK_NOT_NULL(session); + CHECK_NOT_NULL(trustlet); + CHECK_NOT_NULL(tci); + + if (len > MC_MAX_TCI_LEN) { + LOG_E("TCI length is longer than %d", MC_MAX_TCI_LEN); + mcResult = MC_DRV_ERR_TCI_TOO_BIG; + break; + } + + // Get the device associated with the given session + Device *device = resolveDeviceId(session->deviceId); + CHECK_DEVICE(device); + + Connection *devCon = device->connection; + + // First assume the TCI is a contiguous buffer + // Get the physical address of the given TCI + CWsm_ptr pWsm = device->findContiguousWsm(tci); + if (pWsm == NULL) { + // Then assume it's a normal buffer that needs to be mapped + mcResult = device->mapBulkBuf(tci, len, &bulkBuf); + if (mcResult != MC_DRV_OK) { + LOG_E("Registering buffer failed. ret=%x", mcResult); + mcResult = MC_DRV_ERR_WSM_NOT_FOUND;; + break; + } + handle = bulkBuf->handle; + } else { + if (pWsm->len < len) { + LOG_E("mcOpenSession(): length is more than allocated TCI"); + mcResult = MC_DRV_ERR_TCI_GREATER_THAN_WSM; + break; + } + handle = pWsm->handle; + } + + SEND_TO_DAEMON(devCon, MC_DRV_CMD_OPEN_TRUSTLET, + session->deviceId, + spid, + (uint32_t)tlen, + (uint32_t)((uintptr_t)tci & 0xFFF), + handle, + len); + + // Send the full trustlet data + int ret = devCon->writeData(trustlet, tlen); + if (ret < 0) { + LOG_E("sending to Daemon failed."); + \ + mcResult = MC_DRV_ERR_SOCKET_WRITE; + \ + break; + } + + // Read command response + RECV_FROM_DAEMON(devCon, &mcResult); + + if (mcResult != MC_DRV_OK) { + // TODO-2012-09-06-haenellu: Remove this code once tests can handle it + + if (MC_DRV_ERROR_MAJOR(mcResult) != MC_DRV_ERR_MCP_ERROR) { + LOG_E("Daemon could not open session, responseId %d.", mcResult); + } else { + uint32_t mcpResult = MC_DRV_ERROR_MCP(mcResult); + LOG_E("<t-base reported failing of MC_MCP_CMD_OPEN_SESSION command, mcpResult %d.", mcpResult); + + // IMPROVEMENT-2012-09-03-haenellu: Remove this switch case and use MCP code in tests. + switch (mcpResult) { + case MC_MCP_RET_ERR_WRONG_PUBLIC_KEY: + mcResult = MC_DRV_ERR_WRONG_PUBLIC_KEY; + break; + case MC_MCP_RET_ERR_CONTAINER_TYPE_MISMATCH: + mcResult = MC_DRV_ERR_CONTAINER_TYPE_MISMATCH; + break; + case MC_MCP_RET_ERR_CONTAINER_LOCKED: + mcResult = MC_DRV_ERR_CONTAINER_LOCKED; + break; + case MC_MCP_RET_ERR_SP_NO_CHILD: + mcResult = MC_DRV_ERR_SP_NO_CHILD; + break; + case MC_MCP_RET_ERR_TL_NO_CHILD: + mcResult = MC_DRV_ERR_TL_NO_CHILD; + break; + case MC_MCP_RET_ERR_UNWRAP_ROOT_FAILED: + mcResult = MC_DRV_ERR_UNWRAP_ROOT_FAILED; + break; + case MC_MCP_RET_ERR_UNWRAP_SP_FAILED: + mcResult = MC_DRV_ERR_UNWRAP_SP_FAILED; + break; + case MC_MCP_RET_ERR_UNWRAP_TRUSTLET_FAILED: + mcResult = MC_DRV_ERR_UNWRAP_TRUSTLET_FAILED; + break; + default: + // TODO-2012-09-06-haenellu: Remove line and adapt codes in tests. + mcResult = MC_DRV_ERR_MCP_ERROR; + break; + } + } + break; // loading of Trustlet failed, unlock mutex and return + } + + // read payload + mcDrvRspOpenSessionPayload_t rspOpenSessionPayload; + RECV_FROM_DAEMON(devCon, &rspOpenSessionPayload); + + // Register session with handle + session->sessionId = rspOpenSessionPayload.sessionId; + + LOG_I(" Service is started. Setting up channel for notifications."); + + // Set up second channel for notifications + Connection *sessionConnection = new Connection(); + if (!sessionConnection->connect(SOCK_PATH)) { + LOG_E("Could not connect to %s", SOCK_PATH); + delete sessionConnection; + // Here we know we couldn't connect to the Daemon. + // Maybe we should use existing connection to close Trustlet. + mcResult = MC_DRV_ERR_SOCKET_CONNECT; + break; + } + + do { + SEND_TO_DAEMON(sessionConnection, MC_DRV_CMD_NQ_CONNECT, + session->deviceId, + session->sessionId, + rspOpenSessionPayload.deviceSessionId, + rspOpenSessionPayload.sessionMagic); + + RECV_FROM_DAEMON(sessionConnection, &mcResult); + + if (mcResult != MC_DRV_OK) { + LOG_E("CMD_NQ_CONNECT failed, respId=%d", mcResult); + break; + } + + } while (0); + + if (mcResult != MC_DRV_OK) { + delete sessionConnection; + // Here we know we couldn't communicate well with the Daemon. + // Maybe we should use existing connection to close Trustlet. + break; // unlock mutex and return + } + + // there is no payload. + + // Session has been established, new session object must be created + Session *sessionObj = device->createNewSession(session->sessionId, sessionConnection); + // If the session tci was a mapped buffer then register it + if (bulkBuf) + sessionObj->addBulkBuf(bulkBuf); + + LOG_I(" Successfully opened session %03x.", session->sessionId); + + } while (false); + + if (mcResult != MC_DRV_OK && bulkBuf) { + delete bulkBuf; + } + +// TODO: enable as soon as there are more error codes +// if (mcResult == MC_DRV_ERR_SOCKET_WRITE || mcResult == MC_DRV_ERR_SOCKET_READ) { +// LOG_E("Connection is dead, removing device."); +// removeDevice(session->deviceId); +// } + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcOpenGPTA( + mcSessionHandle_t *session, + const mcUuid_t *uuid, + uint8_t *tci, + uint32_t len +) +{ + mcResult_t mcResult = MC_DRV_OK; + +#ifndef WIN32 + devMutex.lock(); + LOG_I("===%s()===", __FUNCTION__); + + BulkBufferDescriptor *bulkBuf = NULL; + + do { + uint32_t handle = 0; + CHECK_NOT_NULL(session); + CHECK_NOT_NULL(uuid); + + if (len > MC_MAX_TCI_LEN) { + LOG_E("TCI length is longer than %d", MC_MAX_TCI_LEN); + mcResult = MC_DRV_ERR_TCI_TOO_BIG; + break; + } + + // Get the device associated with the given session + Device *device = resolveDeviceId(session->deviceId); + CHECK_DEVICE(device); + + Connection *devCon = device->connection; + + // First assume the TCI is a contiguous buffer + // Get the physical address of the given TCI + CWsm_ptr pWsm = device->findContiguousWsm(tci); + if (pWsm == NULL) { + if (tci != NULL && len != 0) { + // Then assume it's a normal buffer that needs to be mapped + mcResult = device->mapBulkBuf(tci, len, &bulkBuf); + if (mcResult != MC_DRV_OK) { + LOG_E("Registering buffer failed. ret=%x", mcResult); + mcResult = MC_DRV_ERR_WSM_NOT_FOUND; + break; + } + handle = bulkBuf->handle; + } else if ( len != 0 ) { + LOG_E("mcOpenSession(): length is more than allocated TCI"); + mcResult = MC_DRV_ERR_TCI_GREATER_THAN_WSM; + break; + } + } else { + if (pWsm->len < len) { + LOG_E("mcOpenSession(): length is more than allocated TCI"); + mcResult = MC_DRV_ERR_TCI_GREATER_THAN_WSM; + break; + } + handle = pWsm->handle; + } + + SEND_TO_DAEMON(devCon, MC_DRV_CMD_OPEN_TRUSTED_APP, + session->deviceId, + *uuid, + (uint32_t)((uintptr_t)tci & 0xFFF), + handle, + len); + + // Read command response + RECV_FROM_DAEMON(devCon, &mcResult); + + if (mcResult != MC_DRV_OK) { + // TODO-2012-09-06-haenellu: Remove this code once tests can handle it + + if (MC_DRV_ERROR_MAJOR(mcResult) != MC_DRV_ERR_MCP_ERROR) { + LOG_E("Daemon could not open session, responseId %d.", mcResult); + } else { + uint32_t mcpResult = MC_DRV_ERROR_MCP(mcResult); + LOG_E("<t-base reported failing of MC_MCP_CMD_OPEN_SESSION command, mcpResult %d.", mcpResult); + + // IMPROVEMENT-2012-09-03-haenellu: Remove this switch case and use MCP code in tests. + switch (mcpResult) { + case MC_MCP_RET_ERR_WRONG_PUBLIC_KEY: + mcResult = MC_DRV_ERR_WRONG_PUBLIC_KEY; + break; + case MC_MCP_RET_ERR_CONTAINER_TYPE_MISMATCH: + mcResult = MC_DRV_ERR_CONTAINER_TYPE_MISMATCH; + break; + case MC_MCP_RET_ERR_CONTAINER_LOCKED: + mcResult = MC_DRV_ERR_CONTAINER_LOCKED; + break; + case MC_MCP_RET_ERR_SP_NO_CHILD: + mcResult = MC_DRV_ERR_SP_NO_CHILD; + break; + case MC_MCP_RET_ERR_TL_NO_CHILD: + mcResult = MC_DRV_ERR_TL_NO_CHILD; + break; + case MC_MCP_RET_ERR_UNWRAP_ROOT_FAILED: + mcResult = MC_DRV_ERR_UNWRAP_ROOT_FAILED; + break; + case MC_MCP_RET_ERR_UNWRAP_SP_FAILED: + mcResult = MC_DRV_ERR_UNWRAP_SP_FAILED; + break; + case MC_MCP_RET_ERR_UNWRAP_TRUSTLET_FAILED: + mcResult = MC_DRV_ERR_UNWRAP_TRUSTLET_FAILED; + break; + default: + // TODO-2012-09-06-haenellu: Remove line and adapt codes in tests. + mcResult = MC_DRV_ERR_MCP_ERROR; + break; + } + } + break; // loading of Trustlet failed, unlock mutex and return + } + + // read payload + mcDrvRspOpenSessionPayload_t rspOpenSessionPayload; + RECV_FROM_DAEMON(devCon, &rspOpenSessionPayload); + + // Register session with handle + session->sessionId = rspOpenSessionPayload.sessionId; + + LOG_I(" Service is started. Setting up channel for notifications."); + + // Set up second channel for notifications + Connection *sessionConnection = new Connection(); + if (!sessionConnection->connect(SOCK_PATH)) { + LOG_E("Could not connect to %s", SOCK_PATH); + delete sessionConnection; + // Here we know we couldn't connect to the Daemon. + // Maybe we should use existing connection to close Trustlet. + mcResult = MC_DRV_ERR_SOCKET_CONNECT; + break; + } + + do { + SEND_TO_DAEMON(sessionConnection, MC_DRV_CMD_NQ_CONNECT, + session->deviceId, + session->sessionId, + rspOpenSessionPayload.deviceSessionId, + rspOpenSessionPayload.sessionMagic); + + RECV_FROM_DAEMON(sessionConnection, &mcResult); + + if (mcResult != MC_DRV_OK) { + LOG_E("CMD_NQ_CONNECT failed, respId=%d", mcResult); + break; + } + + } while (0); + if (mcResult != MC_DRV_OK) { + delete sessionConnection; + // Here we know we couldn't communicate well with the Daemon. + // Maybe we should use existing connection to close Trustlet. + break; // unlock mutex and return + } + + // there is no payload. + + // Session has been established, new session object must be created + Session *sessionObj = device->createNewSession(session->sessionId, sessionConnection); + // If the session tci was a mapped buffer then register it + if (bulkBuf) + sessionObj->addBulkBuf(bulkBuf); + + LOG_I(" Successfully opened session %03x.", session->sessionId); + + } while (false); + + if (mcResult != MC_DRV_OK && bulkBuf) { + delete bulkBuf; + } + +// TODO: enable as soon as there are more error codes +// if (mcResult == MC_DRV_ERR_SOCKET_WRITE || mcResult == MC_DRV_ERR_SOCKET_READ) { +// LOG_E("Connection is dead, removing device."); +// removeDevice(session->deviceId); +// } + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcCloseSession(mcSessionHandle_t *session) +{ + mcResult_t mcResult = MC_DRV_OK; +#ifndef WIN32 + + LOG_I("===%s()===", __FUNCTION__); + devMutex.lock(); + do { + CHECK_NOT_NULL(session); + LOG_I(" Closing session %03x.", session->sessionId); + + Device *device = resolveDeviceId(session->deviceId); + CHECK_DEVICE(device); + + Connection *devCon = device->connection; + + Session *nqSession = device->resolveSessionId(session->sessionId); + + CHECK_SESSION(nqSession, session->sessionId); + + SEND_TO_DAEMON(devCon, MC_DRV_CMD_CLOSE_SESSION, session->sessionId); + + RECV_FROM_DAEMON(devCon, &mcResult); + + if (mcResult != MC_DRV_OK) { + LOG_E("CMD_CLOSE_SESSION failed, respId=%d", mcResult); + // TODO-2012-08-03-haenellu: Remove once tests can handle it. + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + + bool r = device->removeSession(session->sessionId); + if (!r) + { + LOG_E("removeSession failed"); + assert(0); + } + + + + } while (false); + + if (mcResult == MC_DRV_ERR_SOCKET_WRITE || mcResult == MC_DRV_ERR_SOCKET_READ) { + LOG_E("Connection is dead, removing device."); + removeDevice(session->deviceId); + } + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcNotify( + mcSessionHandle_t *session +) +{ + mcResult_t mcResult = MC_DRV_OK; +#ifndef WIN32 + + devMutex.lock(); + LOG_I("===%s()===", __FUNCTION__); + + do { + CHECK_NOT_NULL(session); + LOG_I(" Notifying session %03x.", session->sessionId); + + Device *device = resolveDeviceId(session->deviceId); + CHECK_DEVICE(device); + + Connection *devCon = device->connection; + + Session *nqsession = device->resolveSessionId(session->sessionId); + CHECK_SESSION(nqsession, session->sessionId); + + SEND_TO_DAEMON(devCon, MC_DRV_CMD_NOTIFY, session->sessionId); + // Daemon will not return a response + } while (false); + + if (mcResult == MC_DRV_ERR_SOCKET_WRITE) { + LOG_E("Connection is dead, removing device."); + removeDevice(session->deviceId); + } + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcWaitNotification( + mcSessionHandle_t *session, + int32_t timeout +) +{ + mcResult_t mcResult = MC_DRV_OK; +#ifndef WIN32 + + // TODO-2012-11-02-gurel: devMutex locking and unlocking had to be commented out + // below. Otherwise, when there are multiple threads in Nwd TLC side, we endup a + // deadlock situation, e.g. one thread waits for notification and another one sends + // notification. + + //devMutex.lock(); + LOG_I("===%s()===", __FUNCTION__); + + do { + CHECK_NOT_NULL(session); + LOG_I(" Waiting for notification of session %03x.", session->sessionId); + + Device *device = resolveDeviceId(session->deviceId); + CHECK_DEVICE(device); + + Session *nqSession = device->resolveSessionId(session->sessionId); + CHECK_SESSION(nqSession, session->sessionId); + + Connection *nqconnection = nqSession->notificationConnection; + uint32_t count = 0; + + // Read notification queue till it's empty + for (;;) { + notification_t notification; + ssize_t numRead = nqconnection->readData( + ¬ification, + sizeof(notification_t), + timeout); + // Check for interrupted system call and loop, but only if timeout is infinite + if ((numRead == -1) && (errno == EINTR)) { + if (timeout == MC_INFINITE_TIMEOUT) { + continue; + } else if (timeout == MC_INFINITE_TIMEOUT_INTERRUPTIBLE) { + mcResult = MC_DRV_ERR_INTERRUPTED_BY_SIGNAL; + break; + } + } + //Exit on timeout in first run + //Later runs have timeout set to 0. -2 means, there is no more data. + if (count == 0 && numRead == -2 ) { + LOG_W("Timeout hit at %s", __FUNCTION__); + mcResult = MC_DRV_ERR_TIMEOUT; + break; + } + if (count == 0 && numRead == 0 ) { + LOG_E("Connection is dead, removing device."); + removeDevice(session->deviceId); + mcResult = MC_DRV_ERR_NOTIFICATION; + break; + } + // After first notification the queue will be drained, Thus we set + // no timeout for the following reads + timeout = 0; + + if (numRead != sizeof(notification_t)) { + if (count == 0) { + //failure in first read, notify it + mcResult = MC_DRV_ERR_NOTIFICATION; + LOG_E("read notification failed, %i bytes received", (int)numRead); + break; + } else { + // Read of the n-th notification failed/timeout. We don't tell the + // caller, as we got valid notifications before. + mcResult = MC_DRV_OK; + break; + } + } + + count++; + LOG_I(" Received notification %d for session %03x, payload=%d", + count, notification.sessionId, notification.payload); + + if (notification.payload != 0) { + // Session end point died -> store exit code + nqSession->setErrorInfo(notification.payload); + + mcResult = MC_DRV_INFO_NOTIFICATION; + break; + } + } // for(;;) + + } while (false); + + //devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcMallocWsm( + uint32_t deviceId, + uint32_t align, + uint32_t len, + uint8_t **wsm, + uint32_t wsmFlags) +{ + mcResult_t mcResult = MC_DRV_ERR_UNKNOWN; +#ifndef WIN32 + + LOG_I("===%s(len=%i)===", __FUNCTION__, len); + + devMutex.lock(); + + do { + Device *device = resolveDeviceId(deviceId); + + // Is the device known + // CHECK_DEVICE(device); + + // Is the device opened. + CHECK_DEVICE_CLOSED(device, deviceId) + + CHECK_NOT_NULL(wsm); + + CWsm_ptr pWsm; + mcResult = device->allocateContiguousWsm(len, &pWsm); + if (mcResult != MC_DRV_OK) { + LOG_W(" Allocation of WSM failed"); + break; + } + + *wsm = (uint8_t *)pWsm->virtAddr; + mcResult = MC_DRV_OK; + + } while (false); + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcFreeWsm( + uint32_t deviceId, + uint8_t *wsm +) +{ + mcResult_t mcResult = MC_DRV_ERR_UNKNOWN; +#ifndef WIN32 + + Device *device; + + devMutex.lock(); + + LOG_I("===%s(%p)===", __FUNCTION__, wsm); + + do { + + // Get the device associated wit the given session + device = resolveDeviceId(deviceId); + + // Is the device known + CHECK_DEVICE(device); + + // Is the device opened. + CHECK_DEVICE_CLOSED(device, deviceId) + + // find WSM object + CWsm_ptr pWsm = device->findContiguousWsm(wsm); + if (pWsm == NULL) { + LOG_E("address is unknown to mcFreeWsm"); + mcResult = MC_DRV_ERR_WSM_NOT_FOUND; + break; + } + + // Free the given virtual address + mcResult = device->freeContiguousWsm(pWsm); + if (mcResult != MC_DRV_OK) { + LOG_E("Free of virtual address failed"); + break; + } + mcResult = MC_DRV_OK; + + } while (false); + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcMap( + mcSessionHandle_t *sessionHandle, + void *buf, + uint32_t bufLen, + mcBulkMap_t *mapInfo +) +{ + mcResult_t mcResult = MC_DRV_ERR_UNKNOWN; +#ifndef WIN32 + + static CMutex mutex; + + LOG_I("===%s()===", __FUNCTION__); + + devMutex.lock(); + + do { + CHECK_NOT_NULL(sessionHandle); + CHECK_NOT_NULL(mapInfo); + CHECK_NOT_NULL(buf); + + // Determine device the session belongs to + Device *device = resolveDeviceId(sessionHandle->deviceId); + // Is the device known + CHECK_DEVICE(device); + + // Is the device opened. + CHECK_DEVICE_CLOSED(device, sessionHandle->deviceId) + + Connection *devCon = device->connection; + + // Get session + Session *session = device->resolveSessionId(sessionHandle->sessionId); + CHECK_SESSION(session, sessionHandle->sessionId); + + LOG_I(" Mapping %p to session %03x.", buf, sessionHandle->sessionId); + + // Register mapped bulk buffer to Kernel Module and keep mapped bulk buffer in mind + BulkBufferDescriptor *bulkBuf; + mcResult = session->addBulkBuf(buf, bufLen, &bulkBuf); + if (mcResult != MC_DRV_OK) { + LOG_E("Registering buffer failed. ret=%x", mcResult); + break; + } + + SEND_TO_DAEMON(devCon, MC_DRV_CMD_MAP_BULK_BUF, + session->sessionId, + bulkBuf->handle, + 0, + (uint32_t)((uintptr_t)bulkBuf->virtAddr & 0xFFF), + bulkBuf->len); + + // Read command response + RECV_FROM_DAEMON(devCon, &mcResult); + + if (mcResult != MC_DRV_OK) { + LOG_E("CMD_MAP_BULK_BUF failed, respId=%d", mcResult); + // TODO-2012-09-06-haenellu: Remove once tests can handle it. + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + + // Unregister mapped bulk buffer from Kernel Module and remove mapped + // bulk buffer from session maintenance + if (session->removeBulkBuf(buf) != MC_DRV_OK) { + // Removing of bulk buffer not possible + LOG_E("Unregistering of bulk memory from Kernel Module failed"); + } + break; + } + + mcDrvRspMapBulkMemPayload_t rspMapBulkMemPayload; + RECV_FROM_DAEMON(devCon, &rspMapBulkMemPayload); + + // Set mapping info for internal structures + bulkBuf->sVirtualAddr = (uint32_t)rspMapBulkMemPayload.secureVirtualAdr; + // Set mapping info for Trustlet + // NOTE: The "nice" piece of code is needed because sVirtualAddr has 2 different + // types to accomodate backward compatibility but also keep the length to 32bits + // so on 32 bit systems it's void* but on 64 bit systems it's uint32_t + *(uint32_t*)&mapInfo->sVirtualAddr = bulkBuf->sVirtualAddr; + mapInfo->sVirtualLen = bufLen; + mcResult = MC_DRV_OK; + + } while (false); + +// // TODO: enable as soon as there are more error codes +// if (mcResult == MC_DRV_ERR_SOCKET_WRITE || mcResult == MC_DRV_ERR_SOCKET_READ) { +// LOG_E("Connection is dead, removing device."); +// removeDevice(sessionHandle->deviceId); +// } + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcUnmap( + mcSessionHandle_t *sessionHandle, + void *buf, + mcBulkMap_t *mapInfo +) +{ + mcResult_t mcResult = MC_DRV_ERR_UNKNOWN; +#ifndef WIN32 + + static CMutex mutex; + + LOG_I("===%s()===", __FUNCTION__); + + devMutex.lock(); + + do { + CHECK_NOT_NULL(sessionHandle); + CHECK_NOT_NULL(mapInfo); + CHECK_NOT_NULL(buf); + if (mapInfo->sVirtualAddr == 0) { + LOG_E("Invalid secure virtual address %u.", (uintptr_t)mapInfo->sVirtualAddr); + mcResult = MC_DRV_ERR_NULL_POINTER; + break; + } + + // Determine device the session belongs to + Device *device = resolveDeviceId(sessionHandle->deviceId); + // Is the device known + CHECK_DEVICE(device); + + // Is the device opened. + CHECK_DEVICE_CLOSED(device, sessionHandle->deviceId) + + Connection *devCon = device->connection; + + // Get session + Session *session = device->resolveSessionId(sessionHandle->sessionId); + CHECK_SESSION(session, sessionHandle->sessionId); + + uint32_t handle = session->getBufHandle((uint32_t)mapInfo->sVirtualAddr, mapInfo->sVirtualLen); + if (handle == 0) { + LOG_E("Unable to find internal handle for buffer %u.", (uintptr_t)mapInfo->sVirtualAddr); + mcResult = MC_DRV_ERR_BLK_BUFF_NOT_FOUND; + break; + } + + LOG_I(" Unmapping %p(handle=%u) from session %03x.", buf, handle, sessionHandle->sessionId); + + SEND_TO_DAEMON(devCon, MC_DRV_CMD_UNMAP_BULK_BUF, + session->sessionId, + handle, + (uint32_t)mapInfo->sVirtualAddr, + mapInfo->sVirtualLen); + + RECV_FROM_DAEMON(devCon, &mcResult); + + if (mcResult != MC_DRV_OK) { + LOG_E("Daemon reported failing of UNMAP BULK BUF command, responseId %d.", mcResult); + // TODO-2012-09-06-haenellu: Remove once tests can handle it. + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + // Unregister mapped bulk buffer from Kernel Module and remove mapped + // bulk buffer from session maintenance + mcResult = session->removeBulkBuf(buf); + if (mcResult != MC_DRV_OK) { + LOG_E("Unregistering of bulk memory from Kernel Module failed."); + break; + } + + mcResult = MC_DRV_OK; + + } while (false); + + if (mcResult == MC_DRV_ERR_SOCKET_WRITE || mcResult == MC_DRV_ERR_SOCKET_READ) { + LOG_E("Connection is dead, removing device."); + removeDevice(sessionHandle->deviceId); + } + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcGetSessionErrorCode( + mcSessionHandle_t *session, + int32_t *lastErr +) +{ + mcResult_t mcResult = MC_DRV_OK; +#ifndef WIN32 + + devMutex.lock(); + LOG_I("===%s()===", __FUNCTION__); + + do { + CHECK_NOT_NULL(session); + CHECK_NOT_NULL(lastErr); + + // Get device + Device *device = resolveDeviceId(session->deviceId); + // Is the device known + CHECK_DEVICE(device); + + // Is the device opened. + CHECK_DEVICE_CLOSED(device, session->deviceId) + + // Get session + Session *nqsession = device->resolveSessionId(session->sessionId); + CHECK_SESSION(nqsession, session->sessionId); + + // get session error code from session + *lastErr = nqsession->getLastErr(); + + } while (false); + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + +//------------------------------------------------------------------------------ +__MC_CLIENT_LIB_API mcResult_t mcGetMobiCoreVersion( + uint32_t deviceId, + mcVersionInfo_t *versionInfo +) +{ + mcResult_t mcResult = MC_DRV_OK; +#ifndef WIN32 + + devMutex.lock(); + LOG_I("===%s()===", __FUNCTION__); + + do { + Device *device = resolveDeviceId(deviceId); + + // Is the device known + CHECK_DEVICE(device); + + // Is the device opened. + CHECK_DEVICE_CLOSED(device, deviceId) + + CHECK_NOT_NULL(versionInfo); + + Connection *devCon = device->connection; + + SEND_TO_DAEMON(devCon, MC_DRV_CMD_GET_MOBICORE_VERSION); + + // Read GET MOBICORE VERSION response. + + RECV_FROM_DAEMON(devCon, &mcResult); + + if (mcResult != MC_DRV_OK) { + LOG_E("MC_DRV_CMD_GET_MOBICORE_VERSION bad response, respId=%d", mcResult); + // TODO-2012-09-06-haenellu: Remove once tests can handle it. + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + // Read payload. + mcVersionInfo_t versionInfo_socket; + RECV_FROM_DAEMON(devCon, &versionInfo_socket); + + *versionInfo = versionInfo_socket; + + } while (0); + + devMutex.unlock(); + +#endif /* WIN32 */ + return mcResult; +} + +#ifndef WIN32 +//------------------------------------------------------------------------------ +// Only called by mcOpenDevice() +// Must be taken with devMutex locked. +uint32_t getDaemonVersion(Connection *devCon, uint32_t *version) +{ + mcResult_t mcResult = MC_DRV_OK; + uint32_t v = 0; + + LOG_I("===%s()===", __FUNCTION__); + + do { + SEND_TO_DAEMON(devCon, MC_DRV_CMD_GET_VERSION); + + RECV_FROM_DAEMON(devCon, &mcResult); + + if (mcResult != MC_DRV_OK) { + LOG_E("MC_DRV_CMD_GET_VERSION bad response, respId=%d", mcResult); + // version is still 0, we don't further analyze response here. + break; + } + + RECV_FROM_DAEMON(devCon, &v); + + } while (0); + + if (mcResult == MC_DRV_OK) { + *version = v; + } + + return mcResult; +} +#endif /* WIN32 */ + |