| /** @addtogroup MCD_MCDIMPL_DAEMON_CONHDLR |
| * @{ |
| * @file |
| * |
| * Entry of the MobiCore Driver. |
| */ |
| |
| /* |
| * Copyright (c) 2013 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. |
| */ |
| |
| #include <cstdlib> |
| #include <signal.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| |
| #include "mcVersion.h" |
| #include "mcVersionHelper.h" |
| #include "mc_linux.h" |
| #include "log.h" |
| #include "Mci/mci.h" |
| |
| #include "MobiCoreDriverApi.h" |
| #include "MobiCoreDriverCmd.h" |
| #include "MobiCoreDriverDaemon.h" |
| #include "PrivateRegistry.h" |
| #include "MobiCoreDevice.h" |
| #include "NetlinkServer.h" |
| #include "FSD.h" |
| |
| #define DRIVER_TCI_LEN 4096 |
| |
| MC_CHECK_VERSION(MCI, 0, 2); |
| MC_CHECK_VERSION(SO, 2, 0); |
| MC_CHECK_VERSION(MCLF, 2, 0); |
| MC_CHECK_VERSION(CONTAINER, 2, 0); |
| |
| static void checkMobiCoreVersion(MobiCoreDevice *mobiCoreDevice); |
| |
| #define LOG_I_RELEASE(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) |
| |
| pthread_mutex_t syncMutex = PTHREAD_MUTEX_INITIALIZER; |
| pthread_cond_t syncCondition = PTHREAD_COND_INITIALIZER; |
| bool Th_sync=false; |
| |
| //------------------------------------------------------------------------------ |
| MobiCoreDriverDaemon::MobiCoreDriverDaemon( |
| bool enableScheduler, |
| bool loadDriver, |
| std::vector<std::string> drivers) |
| { |
| mobiCoreDevice = NULL; |
| |
| this->enableScheduler = enableScheduler; |
| this->loadDriver = loadDriver; |
| this->drivers = drivers; |
| |
| for (int i = 0; i < MAX_SERVERS; i++) { |
| servers[i] = NULL; |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| MobiCoreDriverDaemon::~MobiCoreDriverDaemon( |
| void |
| ) |
| { |
| // Unload any device drivers might have been loaded |
| driverResourcesList_t::iterator it; |
| for (it = driverResources.begin(); it != driverResources.end(); it++) { |
| MobicoreDriverResources *res = *it; |
| mobiCoreDevice->closeSession(res->conn, res->sessionId); |
| mobiCoreDevice->unregisterWsmL2(res->pTciWsm); |
| } |
| delete mobiCoreDevice; |
| for (int i = 0; i < MAX_SERVERS; i++) { |
| delete servers[i]; |
| servers[i] = NULL; |
| } |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::run( |
| void |
| ) |
| { |
| LOG_I_RELEASE("Daemon starting up..."); |
| LOG_I_RELEASE("Socket interface version is %u.%u", DAEMON_VERSION_MAJOR, DAEMON_VERSION_MINOR); |
| |
| #ifdef MOBICORE_COMPONENT_BUILD_TAG |
| LOG_I_RELEASE("%s", MOBICORE_COMPONENT_BUILD_TAG); |
| #else |
| #warning "MOBICORE_COMPONENT_BUILD_TAG is not defined!" |
| #endif |
| |
| LOG_I_RELEASE("Build timestamp is %s %s", __DATE__, __TIME__); |
| |
| int i; |
| |
| mobiCoreDevice = getDeviceInstance(); |
| |
| LOG_I("Initializing Device, Daemon sheduler is %s", |
| enableScheduler ? "enabled" : "disabled"); |
| |
| // initialize device (setupo MCI) |
| if (!mobiCoreDevice->initDevice( |
| "/dev/" MC_ADMIN_DEVNODE, |
| enableScheduler)) { |
| LOG_E("Could not initialize <t-base!"); |
| return; |
| } |
| |
| // start device (scheduler) |
| mobiCoreDevice->start(); |
| |
| LOG_I_RELEASE("Checking version of <t-base"); |
| checkMobiCoreVersion(mobiCoreDevice); |
| |
| // Load device driver if requested |
| if (loadDriver) { |
| for (unsigned int i = 0; i < drivers.size(); i++) |
| loadDeviceDriver(drivers[i]); |
| } |
| |
| /* Look for tokens in the registry and pass them to <t-base for endorsement |
| * purposes. |
| */ |
| LOG_I("Looking for suitable tokens"); |
| |
| mcSoAuthTokenCont_t authtoken; |
| mcSoRootCont_t rootcont; |
| uint32_t sosize; |
| uint8_t *p = NULL; |
| |
| mcResult_t ret = mcRegistryReadAuthToken(&authtoken); |
| if (ret != MC_DRV_OK) { |
| LOG_I("Failed to read AuthToken (ret=%u). Trying Root Container", ret); |
| |
| sosize = sizeof(rootcont); |
| ret = mcRegistryReadRoot(&rootcont, &sosize); |
| if (ret != MC_DRV_OK) { |
| LOG_I("Failed to read Root Cont, (ret=%u)", ret); |
| LOG_W("Device endorsements not supported!"); |
| sosize = 0; |
| } |
| else { |
| LOG_I("Found Root Cont."); |
| p = (uint8_t *) &rootcont; |
| } |
| } else { |
| LOG_I("Found AuthToken."); |
| p = (uint8_t *) &authtoken; |
| sosize = sizeof(authtoken); |
| } |
| |
| if (sosize) { |
| LOG_I("Found token of size: %u", sosize); |
| if (!loadToken(p, sosize)) { |
| LOG_E("Failed to pass token to <t-base. " |
| "Device endorsements disabled"); |
| } |
| } |
| |
| LOG_I("Creating socket servers"); |
| // Start listening for incoming TLC connections |
| servers[0] = new NetlinkServer(this); |
| servers[1] = new Server(this, SOCK_PATH); |
| LOG_I("Successfully created servers"); |
| |
| // Start all the servers |
| for (i = 0; i < MAX_SERVERS; i++) { |
| servers[i]->start(i ? "McDaemon.Server" : "NetlinkServer"); |
| } |
| |
| // Create the <t-base File Storage Daemon |
| FSD *FileStorageDaemon = new FSD(); |
| // Start File Storage Daemon |
| FileStorageDaemon->start("McDaemon.FSD"); |
| |
| // then wait for them to exit |
| for (i = 0; i < MAX_SERVERS; i++) { |
| servers[i]->join(); |
| } |
| //Wait for File Storage Daemon to exit |
| FileStorageDaemon->join(); |
| delete FileStorageDaemon; |
| } |
| |
| //------------------------------------------------------------------------------ |
| bool MobiCoreDriverDaemon::checkPermission(Connection *connection) |
| { |
| #ifdef REGISTRY_CHECK_PERMISSIONS |
| struct ucred cred; |
| if (!connection) |
| return true; |
| |
| if (connection->getPeerCredentials(cred)) { |
| gid_t gid = getegid(); |
| uid_t uid = geteuid(); |
| LOG_I("Peer connection has pid = %u and uid = %u gid = %u", cred.pid, cred.uid, cred.gid); |
| LOG_I("Daemon has uid = %u gid = %u", cred.uid, cred.gid); |
| // If the daemon and the peer have the same uid or gid then we're good |
| if (gid == cred.gid || uid == cred.uid) { |
| return true; |
| } |
| return false; |
| |
| } |
| return false; |
| #else |
| return true; |
| #endif |
| } |
| |
| //------------------------------------------------------------------------------ |
| MobiCoreDevice *MobiCoreDriverDaemon::getDevice( |
| uint32_t deviceId |
| ) |
| { |
| // Always return the trustZoneDevice as it is currently the only one supported |
| if (MC_DEVICE_ID_DEFAULT != deviceId) |
| return NULL; |
| return mobiCoreDevice; |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::dropConnection( |
| Connection *connection |
| ) |
| { |
| // Check if a Device has already been registered with the connection |
| MobiCoreDevice *device = (MobiCoreDevice *) (connection->connectionData); |
| |
| if (device != NULL) { |
| // A connection has been found and has to be closed |
| LOG_I("dropConnection(): closing still open device."); |
| |
| // Make sure nobody else writes the MCP, e.g. netlink/socket server, cleanup of TAs |
| device->mutex_mcp.lock(); |
| device->close(connection); |
| device->mutex_mcp.unlock(); |
| } |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| size_t MobiCoreDriverDaemon::writeResult( |
| Connection *connection, |
| mcResult_t code |
| ) |
| { |
| if (0 != code) { |
| LOG_V(" sending error code %d", code); |
| } |
| return connection->writeData(&code, sizeof(mcResult_t)); |
| } |
| |
| //------------------------------------------------------------------------------ |
| bool MobiCoreDriverDaemon::loadDeviceDriver( |
| std::string driverPath |
| ) |
| { |
| bool ret = false; |
| CWsm_ptr pWsm = NULL, pTciWsm = NULL; |
| regObject_t *regObj = NULL; |
| Connection *conn = NULL; |
| mcDrvRspOpenSession_t rspOpenSession; |
| |
| do { |
| //mobiCoreDevice |
| LOG_I("%s: loading %s", __FUNCTION__, driverPath.c_str()); |
| |
| regObj = mcRegistryGetDriverBlob(driverPath.c_str()); |
| if (regObj == NULL) { |
| break;; |
| } |
| |
| LOG_I("registering L2 in kmod, p=%p, len=%i", |
| regObj->value, regObj->len); |
| |
| pWsm = mobiCoreDevice->registerWsmL2( |
| (addr_t)(regObj->value), regObj->len, 0); |
| if (pWsm == NULL) { |
| LOG_E("allocating WSM for Trustlet failed"); |
| break; |
| } |
| // Initialize information data of open session command |
| loadDataOpenSession_t loadDataOpenSession; |
| loadDataOpenSession.baseAddr = pWsm->physAddr; |
| loadDataOpenSession.offs = ((uint32_t) regObj->value) & 0xFFF; |
| loadDataOpenSession.len = regObj->len; |
| loadDataOpenSession.tlHeader = (mclfHeader_ptr) (regObj->value + regObj->tlStartOffset); |
| |
| pTciWsm = mobiCoreDevice->allocateContiguousPersistentWsm(DRIVER_TCI_LEN); |
| if (pTciWsm == NULL) { |
| LOG_E("allocating WSM TCI for Trustlet failed"); |
| break; |
| } |
| |
| conn = new Connection(); |
| uint32_t mcRet = mobiCoreDevice->openSession( |
| conn, |
| &loadDataOpenSession, |
| pTciWsm->handle, |
| pTciWsm->len, |
| 0, |
| &(rspOpenSession.payload)); |
| |
| // Unregister physical memory from kernel module. |
| // This will also destroy the WSM object. |
| mobiCoreDevice->unregisterWsmL2(pWsm); |
| pWsm = NULL; |
| |
| // Free memory occupied by Trustlet data |
| free(regObj); |
| regObj = NULL; |
| |
| if (mcRet != MC_MCP_RET_OK) { |
| LOG_E("open session error %d", mcRet); |
| break; |
| } |
| |
| ret = true; |
| } while (false); |
| // Free all allocated resources |
| if (ret == false) { |
| LOG_I("%s: Freeing previously allocated resources!", __FUNCTION__); |
| if (pWsm != NULL) { |
| if (!mobiCoreDevice->unregisterWsmL2(pWsm)) { |
| // At least make sure we don't leak the WSM object |
| delete pWsm; |
| } |
| } |
| // No matter if we free NULL objects |
| free(regObj); |
| |
| if (conn != NULL) { |
| delete conn; |
| } |
| } else if (conn != NULL) { |
| driverResources.push_back(new MobicoreDriverResources( |
| conn, pTciWsm, rspOpenSession.payload.sessionId)); |
| } |
| |
| return ret; |
| } |
| |
| #define RECV_PAYLOAD_FROM_CLIENT(CONNECTION, CMD_BUFFER) \ |
| { \ |
| void *payload = (void*)((uint32_t)CMD_BUFFER + sizeof(mcDrvCommandHeader_t)); \ |
| uint32_t payload_len = sizeof(*CMD_BUFFER) - sizeof(mcDrvCommandHeader_t); \ |
| int32_t rlen = CONNECTION->readData(payload, payload_len); \ |
| if (rlen < 0) { \ |
| LOG_E("reading from Client failed"); \ |
| /* it is questionable, if writing to broken socket has any effect here. */ \ |
| writeResult(CONNECTION, MC_DRV_ERR_DAEMON_SOCKET); \ |
| return; \ |
| } \ |
| if ((uint32_t)rlen != payload_len) {\ |
| LOG_E("wrong buffer length %i received from Client", rlen); \ |
| writeResult(CONNECTION, MC_DRV_ERR_DAEMON_SOCKET); \ |
| return; \ |
| } \ |
| } |
| |
| #define CHECK_DEVICE(DEVICE, CONNECTION) \ |
| if (DEVICE == NULL) \ |
| { \ |
| LOG_V("%s: no device associated with connection",__FUNCTION__); \ |
| writeResult(CONNECTION, MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN); \ |
| return; \ |
| } |
| |
| //------------------------------------------------------------------------------ |
| inline bool getData(Connection *con, void *buf, uint32_t len) |
| { |
| uint32_t rlen = con->readData(buf, len); |
| if (rlen < len || (int32_t)rlen < 0) { |
| LOG_E("reading from Client failed"); |
| return false; |
| } |
| return true; |
| } |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processOpenDevice(Connection *connection) |
| { |
| MC_DRV_CMD_OPEN_DEVICE_struct cmdOpenDevice; |
| RECV_PAYLOAD_FROM_CLIENT(connection, &cmdOpenDevice); |
| |
| // Check if device has been registered to the connection |
| MobiCoreDevice *device = (MobiCoreDevice *) (connection->connectionData); |
| if (NULL != device) { |
| LOG_E("processOpenDevice(): device already set"); |
| writeResult(connection, MC_DRV_ERR_DEVICE_ALREADY_OPEN); |
| return; |
| } |
| |
| LOG_I(" Opening deviceId %d ", cmdOpenDevice.deviceId); |
| |
| // Get device for device ID |
| device = getDevice(cmdOpenDevice.deviceId); |
| |
| // Check if a device for the given name has been found |
| if (device == NULL) { |
| LOG_E("invalid deviceId"); |
| writeResult(connection, MC_DRV_ERR_UNKNOWN_DEVICE); |
| return; |
| } |
| |
| // Register device object with connection |
| device->open(connection); |
| |
| // Return result code to client lib (no payload) |
| writeResult(connection, MC_DRV_OK); |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processCloseDevice( |
| Connection *connection |
| ) |
| { |
| // there is no payload to read |
| |
| // Device required |
| MobiCoreDevice *device = (MobiCoreDevice *) (connection->connectionData); |
| CHECK_DEVICE(device, connection); |
| |
| // No command data will be read |
| // Unregister device object with connection |
| device->close(connection); |
| |
| // there is no payload |
| writeResult(connection, MC_DRV_OK); |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processOpenSession(Connection *connection, bool isGpUuid) |
| { |
| MC_DRV_CMD_OPEN_SESSION_struct cmdOpenSession; |
| RECV_PAYLOAD_FROM_CLIENT(connection, &cmdOpenSession); |
| |
| // Device required |
| MobiCoreDevice *device = (MobiCoreDevice *) (connection->connectionData); |
| CHECK_DEVICE(device, connection); |
| |
| // Get service blob from registry |
| regObject_t *regObj = mcRegistryGetServiceBlob(&cmdOpenSession.uuid, isGpUuid); |
| if (NULL == regObj) { |
| writeResult(connection, MC_DRV_ERR_TRUSTLET_NOT_FOUND); |
| return; |
| } |
| if (regObj->len == 0) { |
| free(regObj); |
| writeResult(connection, MC_DRV_ERR_TRUSTLET_NOT_FOUND); |
| return; |
| } |
| LOG_I(" Sharing Service loaded at %p with Secure World", (addr_t)(regObj->value)); |
| |
| CWsm_ptr pWsm = device->registerWsmL2((addr_t)(regObj->value), regObj->len, 0); |
| if (pWsm == NULL) { |
| // Free memory occupied by Trustlet data |
| free(regObj); |
| LOG_E("allocating WSM for Trustlet failed"); |
| writeResult(connection, MC_DRV_ERR_DAEMON_KMOD_ERROR); |
| return; |
| } |
| // Initialize information data of open session command |
| loadDataOpenSession_t loadDataOpenSession; |
| loadDataOpenSession.baseAddr = pWsm->physAddr; |
| loadDataOpenSession.offs = ((uint32_t) regObj->value) & 0xFFF; |
| loadDataOpenSession.len = regObj->len; |
| loadDataOpenSession.tlHeader = (mclfHeader_ptr) (regObj->value + regObj->tlStartOffset); |
| |
| mcDrvRspOpenSession_t rspOpenSession; |
| mcResult_t ret = device->openSession( |
| connection, |
| &loadDataOpenSession, |
| cmdOpenSession.handle, |
| cmdOpenSession.len, |
| cmdOpenSession.tci, |
| &rspOpenSession.payload); |
| |
| // Unregister physical memory from kernel module. |
| LOG_I(" Service buffer was copied to Secure world and processed. Stop sharing of buffer."); |
| |
| // This will also destroy the WSM object. |
| if (!device->unregisterWsmL2(pWsm)) { |
| // TODO-2012-07-02-haenellu: Can this ever happen? And if so, we should assert(), also TL might still be running. |
| free(regObj); |
| writeResult(connection, MC_DRV_ERR_DAEMON_KMOD_ERROR); |
| return; |
| } |
| |
| // Free memory occupied by Trustlet data |
| free(regObj); |
| |
| if (ret != MC_DRV_OK) { |
| LOG_E("Service could not be loaded."); |
| writeResult(connection, ret); |
| } else { |
| rspOpenSession.header.responseId = ret; |
| connection->writeData( |
| &rspOpenSession, |
| sizeof(rspOpenSession)); |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| mcResult_t MobiCoreDriverDaemon::processLoadCheck(mcSpid_t spid, void *blob, uint32_t size) |
| { |
| |
| // Device required |
| MobiCoreDevice *device = getDevice(MC_DEVICE_ID_DEFAULT); |
| |
| if (device == NULL) { |
| LOG_E(" No device found"); |
| return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; |
| } |
| |
| // Get service blob from registry |
| regObject_t *regObj = mcRegistryMemGetServiceBlob(spid, blob, size); |
| if (NULL == regObj) { |
| LOG_E(" mcRegistryMemGetServiceBlob failed"); |
| return MC_DRV_ERR_TRUSTLET_NOT_FOUND; |
| } |
| if (regObj->len == 0) { |
| free(regObj); |
| LOG_E("mcRegistryMemGetServiceBlob returned registry object with length equal to zero"); |
| return MC_DRV_ERR_TRUSTLET_NOT_FOUND; |
| } |
| LOG_I(" Sharing Service loaded at %p with Secure World", (addr_t)(regObj->value)); |
| |
| CWsm_ptr pWsm = device->registerWsmL2((addr_t)(regObj->value), regObj->len, 0); |
| if (pWsm == NULL) { |
| // Free memory occupied by Trustlet data |
| free(regObj); |
| LOG_E("allocating WSM for Trustlet failed"); |
| return MC_DRV_ERR_DAEMON_KMOD_ERROR; |
| } |
| // Initialize information data of open session command |
| loadDataOpenSession_t loadDataOpenSession; |
| loadDataOpenSession.baseAddr = pWsm->physAddr; |
| loadDataOpenSession.offs = ((uint32_t) regObj->value) & 0xFFF; |
| loadDataOpenSession.len = regObj->len; |
| loadDataOpenSession.tlHeader = (mclfHeader_ptr) (regObj->value + regObj->tlStartOffset); |
| |
| mcDrvRspOpenSession_t rspOpenSession; |
| mcResult_t ret = device->checkLoad( |
| &loadDataOpenSession, |
| &rspOpenSession.payload); |
| |
| // Unregister physical memory from kernel module. |
| LOG_I(" Service buffer was copied to Secure world and processed. Stop sharing of buffer."); |
| |
| // This will also destroy the WSM object. |
| if (!device->unregisterWsmL2(pWsm)) { |
| // Free memory occupied by Trustlet data |
| free(regObj); |
| LOG_E("deallocating WSM for Trustlet failed"); |
| return MC_DRV_ERR_DAEMON_KMOD_ERROR; |
| } |
| |
| // Free memory occupied by Trustlet data |
| free(regObj); |
| |
| if (ret != MC_DRV_OK) { |
| LOG_E("TA could not be loaded."); |
| return ret; |
| } else { |
| return MC_DRV_OK; |
| } |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processOpenTrustlet(Connection *connection) |
| { |
| MC_DRV_CMD_OPEN_TRUSTLET_struct cmdOpenTrustlet; |
| RECV_PAYLOAD_FROM_CLIENT(connection, &cmdOpenTrustlet); |
| |
| // Device required |
| MobiCoreDevice *device = (MobiCoreDevice *) (connection->connectionData); |
| CHECK_DEVICE(device, connection); |
| |
| uint32_t total_len, rlen, len = cmdOpenTrustlet.trustlet_len; |
| uint8_t *payload = (uint8_t *)malloc(len); |
| uint8_t *p = payload; |
| if (payload == NULL) { |
| LOG_E("failed to allocate payload buffer"); |
| writeResult(connection, MC_DRV_ERR_DAEMON_SOCKET); |
| return; |
| } |
| total_len = 0; |
| while (total_len < len) { |
| rlen = connection->readData(p, len - total_len); |
| if ((int32_t)rlen < 0) { |
| LOG_E("reading from Client failed"); |
| /* it is questionable, if writing to broken socket has any effect here. */ |
| writeResult(connection, MC_DRV_ERR_DAEMON_SOCKET); |
| free(payload); |
| return; |
| } |
| total_len += rlen; |
| p += rlen; |
| } |
| |
| // Get service blob from registry |
| regObject_t *regObj = mcRegistryMemGetServiceBlob(cmdOpenTrustlet.spid, (uint8_t *)payload, len); |
| |
| // Free the payload object no matter what |
| free(payload); |
| if (regObj == NULL) { |
| writeResult(connection, MC_DRV_ERR_TRUSTLET_NOT_FOUND); |
| return; |
| } |
| |
| if (regObj->len == 0) { |
| free(regObj); |
| writeResult(connection, MC_DRV_ERR_TRUSTLET_NOT_FOUND); |
| return; |
| } |
| LOG_I(" Sharing Service loaded at %p with Secure World", (addr_t)(regObj->value)); |
| |
| CWsm_ptr pWsm = device->registerWsmL2((addr_t)(regObj->value), regObj->len, 0); |
| if (pWsm == NULL) { |
| free(regObj); |
| LOG_E("allocating WSM for Trustlet failed"); |
| writeResult(connection, MC_DRV_ERR_DAEMON_KMOD_ERROR); |
| return; |
| } |
| // Initialize information data of open session command |
| loadDataOpenSession_t loadDataOpenSession; |
| loadDataOpenSession.baseAddr = pWsm->physAddr; |
| loadDataOpenSession.offs = ((uint32_t) regObj->value) & 0xFFF; |
| loadDataOpenSession.len = regObj->len; |
| loadDataOpenSession.tlHeader = (mclfHeader_ptr) (regObj->value + regObj->tlStartOffset); |
| |
| mcDrvRspOpenSession_t rspOpenSession; |
| mcResult_t ret = device->openSession( |
| connection, |
| &loadDataOpenSession, |
| cmdOpenTrustlet.handle, |
| cmdOpenTrustlet.len, |
| cmdOpenTrustlet.tci, |
| &rspOpenSession.payload); |
| |
| // Unregister physical memory from kernel module. |
| LOG_I(" Service buffer was copied to Secure world and processed. Stop sharing of buffer."); |
| |
| // This will also destroy the WSM object. |
| if (!device->unregisterWsmL2(pWsm)) { |
| free(regObj); |
| // TODO-2012-07-02-haenellu: Can this ever happen? And if so, we should assert(), also TL might still be running. |
| writeResult(connection, MC_DRV_ERR_DAEMON_KMOD_ERROR); |
| return; |
| } |
| |
| // Free memory occupied by Trustlet data |
| free(regObj); |
| |
| if (ret != MC_DRV_OK) { |
| LOG_E("Service could not be loaded."); |
| writeResult(connection, ret); |
| } else { |
| rspOpenSession.header.responseId = ret; |
| connection->writeData( |
| &rspOpenSession, |
| sizeof(rspOpenSession)); |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processCloseSession(Connection *connection) |
| { |
| MC_DRV_CMD_CLOSE_SESSION_struct cmdCloseSession; |
| RECV_PAYLOAD_FROM_CLIENT(connection, &cmdCloseSession) |
| |
| // Device required |
| MobiCoreDevice *device = (MobiCoreDevice *) (connection->connectionData); |
| CHECK_DEVICE(device, connection); |
| |
| mcResult_t ret = device->closeSession(connection, cmdCloseSession.sessionId); |
| |
| // there is no payload |
| writeResult(connection, ret); |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processNqConnect(Connection *connection) |
| { |
| // Set up the channel for sending SWd notifications to the client |
| // MC_DRV_CMD_NQ_CONNECT is only allowed on new connections not |
| // associated with a device. If a device is registered to the |
| // connection NQ_CONNECT is not allowed. |
| |
| // Read entire command data |
| MC_DRV_CMD_NQ_CONNECT_struct cmd; |
| RECV_PAYLOAD_FROM_CLIENT(connection, &cmd); |
| |
| // device must be empty since this is a new connection |
| MobiCoreDevice *device = (MobiCoreDevice *)(connection->connectionData); |
| if (device != NULL) { |
| LOG_E("device already set\n"); |
| writeResult(connection, MC_DRV_ERR_NQ_FAILED); |
| return; |
| } |
| |
| // Remove the connection from the list of known client connections |
| for (int i = 0; i < MAX_SERVERS; i++) { |
| servers[i]->detachConnection(connection); |
| } |
| |
| device = getDevice(cmd.deviceId); |
| // Check if a device for the given name has been found |
| if (NULL == device) { |
| LOG_E("invalid deviceId"); |
| writeResult(connection, MC_DRV_ERR_UNKNOWN_DEVICE); |
| return; |
| } |
| |
| TrustletSession *ts = device->registerTrustletConnection( |
| connection, |
| &cmd); |
| if (!ts) { |
| LOG_E("registerTrustletConnection() failed!"); |
| writeResult(connection, MC_DRV_ERR_UNKNOWN); |
| return; |
| } |
| |
| writeResult(connection, MC_DRV_OK); |
| ts->processQueuedNotifications(); |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processNotify(Connection *connection) |
| { |
| // Read entire command data |
| MC_DRV_CMD_NOTIFY_struct cmd; |
| //RECV_PAYLOAD_FROM_CLIENT(connection, &cmd); |
| void *payload = (void *)((uint32_t)&cmd + sizeof(mcDrvCommandHeader_t)); |
| uint32_t payload_len = sizeof(cmd) - sizeof(mcDrvCommandHeader_t); |
| uint32_t rlen = connection->readData(payload, payload_len); |
| if ((int) rlen < 0) { |
| LOG_E("reading from Client failed"); |
| /* it is questionable, if writing to broken socket has any effect here. */ |
| // NOTE: notify fails silently |
| //writeResult(connection, MC_DRV_RSP_SOCKET_ERROR); |
| return; |
| } |
| if (rlen != payload_len) { |
| LOG_E("wrong buffer length %i received from Client", rlen); |
| // NOTE: notify fails silently |
| //writeResult(connection, MC_DRV_RSP_PAYLOAD_LENGTH_ERROR); |
| return; |
| } |
| |
| // Device required |
| MobiCoreDevice *device = (MobiCoreDevice *) (connection->connectionData); |
| if (NULL == device) { |
| LOG_V("%s: no device associated with connection", __FUNCTION__); |
| // NOTE: notify fails silently |
| // writeResult(connection,MC_DRV_RSP_DEVICE_NOT_OPENED); |
| return; |
| } |
| |
| device->notify(connection, cmd.sessionId); |
| // NOTE: for notifications there is no response at all |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processMapBulkBuf(Connection *connection) |
| { |
| MC_DRV_CMD_MAP_BULK_BUF_struct cmd; |
| |
| RECV_PAYLOAD_FROM_CLIENT(connection, &cmd); |
| |
| // Device required |
| MobiCoreDevice *device = (MobiCoreDevice *) (connection->connectionData); |
| CHECK_DEVICE(device, connection); |
| |
| if (!device->lockWsmL2(cmd.handle)) { |
| LOG_E("Couldn't lock the buffer!"); |
| writeResult(connection, MC_DRV_ERR_DAEMON_WSM_HANDLE_NOT_FOUND); |
| return; |
| } |
| |
| uint32_t secureVirtualAdr = (uint32_t)NULL; |
| uint64_t pAddrL2 = device->findWsmL2(cmd.handle, connection->socketDescriptor); |
| |
| if (pAddrL2 == 0) { |
| LOG_E("Failed to resolve WSM with handle %u", cmd.handle); |
| writeResult(connection, MC_DRV_ERR_DAEMON_WSM_HANDLE_NOT_FOUND); |
| return; |
| } |
| |
| // Map bulk memory to secure world |
| mcResult_t mcResult = device->mapBulk(connection, cmd.sessionId, cmd.handle, pAddrL2, |
| cmd.offsetPayload, cmd.lenBulkMem, &secureVirtualAdr); |
| |
| if (mcResult != MC_DRV_OK) { |
| writeResult(connection, mcResult); |
| return; |
| } |
| |
| mcDrvRspMapBulkMem_t rsp; |
| rsp.header.responseId = MC_DRV_OK; |
| rsp.payload.sessionId = cmd.sessionId; |
| rsp.payload.secureVirtualAdr = secureVirtualAdr; |
| connection->writeData(&rsp, sizeof(mcDrvRspMapBulkMem_t)); |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processUnmapBulkBuf(Connection *connection) |
| { |
| MC_DRV_CMD_UNMAP_BULK_BUF_struct cmd; |
| RECV_PAYLOAD_FROM_CLIENT(connection, &cmd) |
| |
| // Device required |
| MobiCoreDevice *device = (MobiCoreDevice *) (connection->connectionData); |
| CHECK_DEVICE(device, connection); |
| |
| // Unmap bulk memory from secure world |
| uint32_t mcResult = device->unmapBulk(connection, cmd.sessionId, cmd.handle, |
| cmd.secureVirtualAdr, cmd.lenBulkMem); |
| |
| if (mcResult != MC_DRV_OK) { |
| LOG_V("MCP UNMAP returned code %d", mcResult); |
| writeResult(connection, mcResult); |
| return; |
| } |
| |
| // TODO-2012-09-06-haenellu: Think about not ignoring the error case. |
| device->unlockWsmL2(cmd.handle); |
| |
| writeResult(connection, MC_DRV_OK); |
| } |
| |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processGetVersion( |
| Connection *connection |
| ) |
| { |
| // there is no payload to read |
| |
| mcDrvRspGetVersion_t rspGetVersion; |
| rspGetVersion.version = MC_MAKE_VERSION(DAEMON_VERSION_MAJOR, DAEMON_VERSION_MINOR); |
| rspGetVersion.responseId = MC_DRV_OK; |
| |
| connection->writeData(&rspGetVersion, sizeof(mcDrvRspGetVersion_t)); |
| } |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processGetMobiCoreVersion( |
| Connection *connection |
| ) |
| { |
| // there is no payload to read |
| |
| // Device required |
| MobiCoreDevice *device = (MobiCoreDevice *) (connection->connectionData); |
| CHECK_DEVICE(device, connection); |
| |
| // Get <t-base version info from secure world. |
| mcDrvRspGetMobiCoreVersion_t rspGetMobiCoreVersion; |
| |
| mcResult_t mcResult = device->getMobiCoreVersion(&rspGetMobiCoreVersion.payload); |
| |
| if (mcResult != MC_DRV_OK) { |
| LOG_V("MC GET_MOBICORE_VERSION returned code %d", mcResult); |
| writeResult(connection, mcResult); |
| return; |
| } |
| |
| rspGetMobiCoreVersion.header.responseId = MC_DRV_OK; |
| connection->writeData( |
| &rspGetMobiCoreVersion, |
| sizeof(rspGetMobiCoreVersion)); |
| } |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processRegistryReadData(uint32_t commandId, Connection *connection) |
| { |
| #define MAX_DATA_SIZE 512 |
| mcDrvResponseHeader_t rspRegistry = { responseId : |
| MC_DRV_ERR_INVALID_OPERATION |
| }; |
| void *buf = alloca(MAX_DATA_SIZE); |
| uint32_t len = MAX_DATA_SIZE; |
| mcSoAuthTokenCont_t auth; |
| mcSpid_t spid; |
| mcUuid_t uuid; |
| |
| if (!checkPermission(connection)) { |
| connection->writeData(&rspRegistry, sizeof(rspRegistry)); |
| return; |
| } |
| |
| switch (commandId) { |
| case MC_DRV_REG_READ_AUTH_TOKEN: |
| rspRegistry.responseId = mcRegistryReadAuthToken(&auth); |
| buf = &auth; |
| len = sizeof(mcSoAuthTokenCont_t); |
| break; |
| case MC_DRV_REG_READ_ROOT_CONT: |
| rspRegistry.responseId = mcRegistryReadRoot(buf, &len); |
| break; |
| case MC_DRV_REG_READ_SP_CONT: |
| if (!getData(connection, &spid, sizeof(spid))) |
| break; |
| rspRegistry.responseId = mcRegistryReadSp(spid, buf, &len); |
| break; |
| case MC_DRV_REG_READ_TL_CONT: |
| if (!getData(connection, &uuid, sizeof(uuid))) |
| break; |
| if (!getData(connection, &spid, sizeof(spid))) |
| break; |
| rspRegistry.responseId = mcRegistryReadTrustletCon(&uuid, spid, buf, &len); |
| break; |
| default: |
| break; |
| } |
| connection->writeData(&rspRegistry, sizeof(rspRegistry)); |
| if (rspRegistry.responseId != MC_DRV_ERR_INVALID_OPERATION) |
| connection->writeData(buf, len); |
| } |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processRegistryWriteData(uint32_t commandId, Connection *connection) |
| { |
| mcDrvResponseHeader_t rspRegistry = { responseId : |
| MC_DRV_ERR_INVALID_OPERATION |
| }; |
| uint32_t soSize; |
| void *so; |
| |
| if (!checkPermission(connection)) { |
| connection->writeData(&rspRegistry, sizeof(rspRegistry)); |
| return; |
| } |
| |
| // First read the SO data size |
| if (!getData(connection, &soSize, sizeof(soSize))) { |
| LOG_E("Failed to read SO data size"); |
| connection->writeData(&rspRegistry, sizeof(rspRegistry)); |
| return; |
| } |
| so = malloc(soSize); |
| if (so == NULL) { |
| LOG_E("Allocation failure"); |
| rspRegistry.responseId = MC_DRV_ERR_NO_FREE_MEMORY; |
| connection->writeData(&rspRegistry, sizeof(rspRegistry)); |
| return; |
| } |
| |
| switch (commandId) { |
| case MC_DRV_REG_STORE_AUTH_TOKEN: { |
| if (!getData(connection, so, soSize)) |
| break; |
| rspRegistry.responseId = mcRegistryStoreAuthToken(so, soSize); |
| break; |
| } |
| case MC_DRV_REG_WRITE_ROOT_CONT: { |
| if (!getData(connection, so, soSize)) |
| break; |
| rspRegistry.responseId = mcRegistryStoreRoot(so, soSize); |
| break; |
| } |
| case MC_DRV_REG_WRITE_SP_CONT: { |
| mcSpid_t spid; |
| if (!getData(connection, &spid, sizeof(spid))) |
| break; |
| if (!getData(connection, so, soSize)) |
| break; |
| rspRegistry.responseId = mcRegistryStoreSp(spid, so, soSize); |
| break; |
| } |
| case MC_DRV_REG_WRITE_TL_CONT: { |
| mcUuid_t uuid; |
| mcSpid_t spid; |
| if (!getData(connection, &uuid, sizeof(uuid))) |
| break; |
| if (!getData(connection, &spid, sizeof(spid))) |
| break; |
| if (!getData(connection, so, soSize)) |
| break; |
| rspRegistry.responseId = mcRegistryStoreTrustletCon(&uuid, spid, so, soSize); |
| break; |
| } |
| case MC_DRV_REG_WRITE_SO_DATA: { |
| if (!getData(connection, so, soSize)) |
| break; |
| rspRegistry.responseId = mcRegistryStoreData(so, soSize); |
| break; |
| } |
| case MC_DRV_REG_STORE_TA_BLOB: { |
| uint32_t blobSize = soSize; |
| mcSpid_t spid; |
| void *blob; |
| if (!getData(connection, &spid, sizeof(spid))) |
| break; |
| blob = malloc(blobSize); |
| if (blob == NULL) { |
| LOG_E("Allocation failure"); |
| rspRegistry.responseId = MC_DRV_ERR_NO_FREE_MEMORY; |
| break; |
| } |
| if (!getData(connection, blob, blobSize)) { |
| free(blob); |
| break; |
| } |
| //LOG_I("processLoadCheck"); |
| rspRegistry.responseId = processLoadCheck(spid, blob, blobSize); |
| if (rspRegistry.responseId != MC_DRV_OK){ |
| LOG_I("processLoadCheck failed"); |
| free(blob); |
| break; |
| } |
| //LOG_I("mcRegistryStoreTABlob"); |
| rspRegistry.responseId = mcRegistryStoreTABlob(spid, blob, blobSize); |
| free(blob); |
| break; |
| } |
| default: |
| break; |
| } |
| free(so); |
| connection->writeData(&rspRegistry, sizeof(rspRegistry)); |
| } |
| |
| //------------------------------------------------------------------------------ |
| void MobiCoreDriverDaemon::processRegistryDeleteData(uint32_t commandId, Connection *connection) |
| { |
| mcDrvResponseHeader_t rspRegistry = { responseId : |
| MC_DRV_ERR_INVALID_OPERATION |
| }; |
| mcSpid_t spid; |
| |
| if (!checkPermission(connection)) { |
| connection->writeData(&rspRegistry, sizeof(rspRegistry)); |
| return; |
| } |
| |
| switch (commandId) { |
| case MC_DRV_REG_DELETE_AUTH_TOKEN: |
| rspRegistry.responseId = mcRegistryDeleteAuthToken(); |
| break; |
| case MC_DRV_REG_DELETE_ROOT_CONT: |
| rspRegistry.responseId = mcRegistryCleanupRoot(); |
| break; |
| case MC_DRV_REG_DELETE_SP_CONT: |
| if (!getData(connection, &spid, sizeof(spid))) |
| break; |
| rspRegistry.responseId = mcRegistryCleanupSp(spid); |
| break; |
| case MC_DRV_REG_DELETE_TL_CONT: |
| mcUuid_t uuid; |
| if (!getData(connection, &uuid, sizeof(uuid))) |
| break; |
| if (!getData(connection, &spid, sizeof(spid))) |
| break; |
| rspRegistry.responseId = mcRegistryCleanupTrustlet(&uuid, spid); |
| break; |
| default: |
| break; |
| } |
| |
| connection->writeData(&rspRegistry, sizeof(rspRegistry)); |
| } |
| |
| //------------------------------------------------------------------------------ |
| bool MobiCoreDriverDaemon::handleConnection( |
| Connection *connection |
| ) |
| { |
| bool ret = false; |
| |
| // This is the big lock around everything the Daemon does, including socket and MCI access |
| static CMutex reg_mutex; |
| static CMutex siq_mutex; |
| |
| /* In case of RTM fault do not try to signal anything to MobiCore |
| * just answer NO to all incoming connections! */ |
| if (mobiCoreDevice->getMcFault()) { |
| LOG_I("Ignore request, <t-base has faulted before."); |
| return false; |
| } |
| |
| LOG_I("handleConnection()==== %p", connection); |
| do { |
| // Read header |
| mcDrvCommandHeader_t mcDrvCommandHeader; |
| ssize_t rlen = connection->readData( |
| &(mcDrvCommandHeader), |
| sizeof(mcDrvCommandHeader)); |
| |
| if (rlen == 0) { |
| LOG_V(" handleConnection(): Connection closed."); |
| break; |
| } |
| if (rlen == -1) { |
| LOG_E("Socket error."); |
| break; |
| } |
| if (rlen == -2) { |
| LOG_E("Timeout."); |
| break; |
| } |
| ret = true; |
| |
| switch (mcDrvCommandHeader.commandId) { |
| //----------------------------------------- |
| case MC_DRV_CMD_OPEN_DEVICE: |
| mobiCoreDevice->mutex_mcp.lock(); |
| processOpenDevice(connection); |
| mobiCoreDevice->mutex_mcp.unlock(); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_CLOSE_DEVICE: |
| mobiCoreDevice->mutex_mcp.lock(); |
| processCloseDevice(connection); |
| mobiCoreDevice->mutex_mcp.unlock(); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_OPEN_SESSION: |
| mobiCoreDevice->mutex_mcp.lock(); |
| processOpenSession(connection, false); |
| mobiCoreDevice->mutex_mcp.unlock(); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_OPEN_TRUSTLET: |
| mobiCoreDevice->mutex_mcp.lock(); |
| processOpenTrustlet(connection); |
| mobiCoreDevice->mutex_mcp.unlock(); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_OPEN_TRUSTED_APP: |
| mobiCoreDevice->mutex_mcp.lock(); |
| processOpenSession(connection, true); |
| mobiCoreDevice->mutex_mcp.unlock(); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_CLOSE_SESSION: |
| mobiCoreDevice->mutex_mcp.lock(); |
| processCloseSession(connection); |
| mobiCoreDevice->mutex_mcp.unlock(); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_NQ_CONNECT: |
| siq_mutex.lock(); |
| processNqConnect(connection); |
| siq_mutex.unlock(); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_NOTIFY: |
| siq_mutex.lock(); |
| processNotify(connection); |
| siq_mutex.unlock(); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_MAP_BULK_BUF: |
| mobiCoreDevice->mutex_mcp.lock(); |
| processMapBulkBuf(connection); |
| mobiCoreDevice->mutex_mcp.unlock(); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_UNMAP_BULK_BUF: |
| mobiCoreDevice->mutex_mcp.lock(); |
| processUnmapBulkBuf(connection); |
| mobiCoreDevice->mutex_mcp.unlock(); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_GET_VERSION: |
| processGetVersion(connection); |
| break; |
| //----------------------------------------- |
| case MC_DRV_CMD_GET_MOBICORE_VERSION: |
| mobiCoreDevice->mutex_mcp.lock(); |
| processGetMobiCoreVersion(connection); |
| mobiCoreDevice->mutex_mcp.unlock(); |
| break; |
| //----------------------------------------- |
| /* Registry functionality */ |
| // Write Registry Data |
| case MC_DRV_REG_STORE_AUTH_TOKEN: |
| case MC_DRV_REG_WRITE_ROOT_CONT: |
| case MC_DRV_REG_WRITE_SP_CONT: |
| case MC_DRV_REG_WRITE_TL_CONT: |
| case MC_DRV_REG_WRITE_SO_DATA: |
| case MC_DRV_REG_STORE_TA_BLOB: |
| reg_mutex.lock(); |
| processRegistryWriteData(mcDrvCommandHeader.commandId, connection); |
| reg_mutex.unlock(); |
| break; |
| //----------------------------------------- |
| // Read Registry Data |
| case MC_DRV_REG_READ_AUTH_TOKEN: |
| case MC_DRV_REG_READ_ROOT_CONT: |
| case MC_DRV_REG_READ_SP_CONT: |
| case MC_DRV_REG_READ_TL_CONT: |
| reg_mutex.lock(); |
| processRegistryReadData(mcDrvCommandHeader.commandId, connection); |
| reg_mutex.unlock(); |
| break; |
| //----------------------------------------- |
| // Delete registry data |
| case MC_DRV_REG_DELETE_AUTH_TOKEN: |
| case MC_DRV_REG_DELETE_ROOT_CONT: |
| case MC_DRV_REG_DELETE_SP_CONT: |
| case MC_DRV_REG_DELETE_TL_CONT: |
| reg_mutex.lock(); |
| processRegistryDeleteData(mcDrvCommandHeader.commandId, connection); |
| reg_mutex.unlock(); |
| break; |
| //----------------------------------------- |
| default: |
| LOG_E("Unknown command: %d=0x%x", |
| mcDrvCommandHeader.commandId, |
| mcDrvCommandHeader.commandId); |
| ret = false; |
| break; |
| } |
| } while (0); |
| |
| LOG_I("handleConnection()<-------"); |
| |
| return ret; |
| } |
| |
| //------------------------------------------------------------------------------ |
| /** |
| * Print daemon command line options |
| */ |
| |
| void printUsage( |
| int argc, |
| char *args[] |
| ) |
| { |
| #ifdef MOBICORE_COMPONENT_BUILD_TAG |
| fprintf(stderr, "<t-base Driver Daemon %u.%u. \"%s\" %s %s\n", DAEMON_VERSION_MAJOR, DAEMON_VERSION_MINOR, MOBICORE_COMPONENT_BUILD_TAG, __DATE__, __TIME__); |
| #else |
| #warning "MOBICORE_COMPONENT_BUILD_TAG is not defined!" |
| #endif |
| |
| fprintf(stderr, "usage: %s [-mdsbhp]\n", args[0]); |
| fprintf(stderr, "Start <t-base Daemon\n\n"); |
| fprintf(stderr, "-h\t\tshow this help\n"); |
| fprintf(stderr, "-b\t\tfork to background\n"); |
| fprintf(stderr, "-s\t\tdisable daemon scheduler(default enabled)\n"); |
| fprintf(stderr, "-r DRIVER\t<t-base driver to load at start-up\n"); |
| } |
| |
| //------------------------------------------------------------------------------ |
| /** |
| * Signal handler for daemon termination |
| * Using this handler instead of the standard libc one ensures the daemon |
| * can cleanup everything -> read() on a FD will now return EINTR |
| */ |
| void terminateDaemon( |
| int signum |
| ) |
| { |
| LOG_E("Signal %d received\n", signum); |
| } |
| |
| //------------------------------------------------------------------------------ |
| /** |
| * Main entry of the <t-base Driver Daemon. |
| */ |
| int main(int argc, char *args[]) |
| { |
| // Create the <t-base Driver Singleton |
| MobiCoreDriverDaemon *mobiCoreDriverDaemon = NULL; |
| // Process signal action |
| struct sigaction action; |
| |
| // Read the Command line options |
| extern char *optarg; |
| extern int optopt; |
| int c, errFlag = 0; |
| // Scheduler enabled by default |
| int schedulerFlag = 1; |
| // Autoload driver at start-up |
| int driverLoadFlag = 0; |
| std::vector<std::string> drivers; |
| // By default don't fork |
| bool forkDaemon = false; |
| |
| /* Initialize mutex and condition variable objects */ |
| pthread_mutex_init(&syncMutex, NULL); |
| pthread_cond_init (&syncCondition, NULL); |
| |
| while ((c = getopt(argc, args, "r:sbhp:")) != -1) { |
| switch (c) { |
| case 'h': /* Help */ |
| errFlag++; |
| break; |
| case 's': /* Disable Scheduler */ |
| schedulerFlag = 0; |
| break; |
| case 'b': /* Fork to background */ |
| forkDaemon = true; |
| break; |
| case 'r': /* Load <t-base driver at start-up */ |
| driverLoadFlag = 1; |
| drivers.push_back(optarg); |
| break; |
| case ':': /* -r operand */ |
| fprintf(stderr, "Option -%c requires an operand\n", optopt); |
| errFlag++; |
| break; |
| case '?': |
| fprintf(stderr, "Unrecognized option: -%c\n", optopt); |
| errFlag++; |
| } |
| } |
| if (errFlag) { |
| printUsage(argc, args); |
| exit(2); |
| } |
| |
| // We should fork the daemon to background |
| if (forkDaemon == true) { |
| |
| /* ignore terminal has been closed signal */ |
| signal(SIGHUP, SIG_IGN); |
| |
| int i = fork(); |
| if (i < 0) { |
| exit(1); |
| } |
| // Parent |
| else if (i > 0) { |
| exit(0); |
| } |
| |
| // obtain a new process group */ |
| setsid(); |
| /* close all descriptors */ |
| for (i = getdtablesize(); i >= 0; --i) { |
| close(i); |
| } |
| // STDIN, STDOUT and STDERR should all point to /dev/null */ |
| i = open("/dev/null", O_RDWR); |
| dup(i); |
| dup(i); |
| /* ignore tty signals */ |
| signal(SIGTSTP, SIG_IGN); |
| signal(SIGTTOU, SIG_IGN); |
| signal(SIGTTIN, SIG_IGN); |
| } |
| |
| // Set up the structure to specify the new action. |
| action.sa_handler = terminateDaemon; |
| sigemptyset (&action.sa_mask); |
| action.sa_flags = 0; |
| sigaction (SIGINT, &action, NULL); |
| sigaction (SIGTERM, &action, NULL); |
| signal(SIGPIPE, SIG_IGN); |
| |
| mobiCoreDriverDaemon = new MobiCoreDriverDaemon( |
| /* Scheduler status */ |
| schedulerFlag, |
| /* Auto Driver loading */ |
| driverLoadFlag, |
| drivers); |
| |
| // Start the driver |
| mobiCoreDriverDaemon->run(); |
| |
| delete mobiCoreDriverDaemon; |
| |
| pthread_mutex_destroy(&syncMutex); |
| pthread_cond_destroy(&syncCondition); |
| |
| // This should not happen |
| LOG_E("Exiting <t-base Daemon"); |
| |
| return EXIT_FAILURE; |
| } |
| |
| //------------------------------------------------------------------------------ |
| static void checkMobiCoreVersion( |
| MobiCoreDevice *mobiCoreDevice |
| ) |
| { |
| bool failed = false; |
| |
| // Get MobiCore version info. |
| mcDrvRspGetMobiCoreVersionPayload_t versionPayload; |
| mcResult_t mcResult = mobiCoreDevice->getMobiCoreVersion(&versionPayload); |
| |
| if (mcResult != MC_DRV_OK) { |
| LOG_E("Failed to obtain <t-base version info. MCP return code: %u", mcResult); |
| failed = true; |
| } else { |
| LOG_I_RELEASE("Product ID is %s", versionPayload.versionInfo.productId); |
| |
| // Check <t-base version info. |
| char *msg; |
| if (!checkVersionOkMCI(versionPayload.versionInfo.versionMci, &msg)) { |
| LOG_E("%s", msg); |
| failed = true; |
| } |
| LOG_I_RELEASE("%s", msg); |
| if (!checkVersionOkSO(versionPayload.versionInfo.versionSo, &msg)) { |
| LOG_E("%s", msg); |
| failed = true; |
| } |
| LOG_I_RELEASE("%s", msg); |
| if (!checkVersionOkMCLF(versionPayload.versionInfo.versionMclf, &msg)) { |
| LOG_E("%s", msg); |
| failed = true; |
| } |
| LOG_I_RELEASE("%s", msg); |
| if (!checkVersionOkCONTAINER(versionPayload.versionInfo.versionContainer, &msg)) { |
| LOG_E("%s", msg); |
| failed = true; |
| } |
| LOG_I_RELEASE("%s", msg); |
| } |
| |
| if (failed) { |
| exit(1); |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| bool MobiCoreDriverDaemon::loadToken(uint8_t *token, uint32_t sosize) |
| { |
| bool ret = false; |
| CWsm_ptr pWsm = NULL; |
| Connection *conn = NULL; |
| |
| do { |
| LOG_I("registering L2 in kmod, p=%p, len=%i", token, sosize); |
| |
| pWsm = mobiCoreDevice->registerWsmL2((addr_t) (token), sosize, 0); |
| if (pWsm == NULL) { |
| LOG_E("allocating WSM for Token failed"); |
| break; |
| } |
| |
| /* Initialize information data of LOAD_TOKEN command */ |
| loadTokenData_t loadTokenData; |
| loadTokenData.addr = pWsm->physAddr; |
| loadTokenData.offs = ((uint32_t) token) & 0xFFF; |
| loadTokenData.len = sosize; |
| |
| conn = new Connection(); |
| uint32_t mcRet = mobiCoreDevice->loadToken(conn, &loadTokenData); |
| |
| /* Unregister physical memory from kernel module. This will also destroy |
| * the WSM object. |
| */ |
| mobiCoreDevice->unregisterWsmL2(pWsm); |
| pWsm = NULL; |
| |
| if (mcRet != MC_MCP_RET_OK) { |
| LOG_E("LOAD_TOKEN error 0x%x", mcRet); |
| break; |
| } |
| ret = true; |
| |
| } while (false); |
| |
| delete pWsm; |
| delete conn; |
| |
| return ret; |
| } |
| |
| /** @} */ |