blob: 50f10d263b69093c1638aef969356bc41c73552a [file] [log] [blame]
/** @addtogroup MCD_MCDIMPL_DAEMON_CONHDLR
* @{
* @file
*
* Entry of the MobiCore Driver.
*
* <!-- 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 <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include "MobiCoreDriverApi.h"
#include "MobiCoreDriverCmd.h"
#include "mcVersion.h"
#include "mcVersionHelper.h"
#include "mc_linux.h"
#include "MobiCoreDriverDaemon.h"
#include "MobiCoreRegistry.h"
#include "MobiCoreDevice.h"
#include "NetlinkServer.h"
#include "log.h"
#define DRIVER_TCI_LEN 100
#include "Mci/mci.h"
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);
//------------------------------------------------------------------------------
MobiCoreDriverDaemon::MobiCoreDriverDaemon(
bool enableScheduler,
bool loadMobicore,
std::string mobicoreImage,
unsigned int donateRamSize,
bool loadDriver,
std::string driverPath
)
{
mobiCoreDevice = NULL;
this->enableScheduler = enableScheduler;
this->loadMobicore = loadMobicore;
this->mobicoreImage = mobicoreImage;
this->donateRamSize = donateRamSize;
this->loadDriver = loadDriver;
this->driverPath = driverPath;
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("Daemon starting up...");
LOG_I("Socket interface version is %u.%u", DAEMON_VERSION_MAJOR, DAEMON_VERSION_MINOR);
#ifdef MOBICORE_COMPONENT_BUILD_TAG
LOG_I("%s", MOBICORE_COMPONENT_BUILD_TAG);
#else
#warning "MOBICORE_COMPONENT_BUILD_TAG is not defined!"
#endif
LOG_I("Build timestamp is %s %s", __DATE__, __TIME__);
int i;
mobiCoreDevice = getDeviceInstance();
LOG_I("Daemon scheduler is %s", enableScheduler ? "enabled" : "disabled");
LOG_I("Initializing MobiCore Device");
if (!mobiCoreDevice->initDevice(
"/dev/" MC_ADMIN_DEVNODE,
loadMobicore,
mobicoreImage.c_str(),
enableScheduler)) {
LOG_E("Could not initialize MobiCore!");
return;
}
mobiCoreDevice->start();
LOG_I("Checking version of MobiCore");
checkMobiCoreVersion(mobiCoreDevice);
if (donateRamSize > 0) {
// Donate additional RAM to MC
LOG_I("Donating %u Kbytes to Mobicore", donateRamSize / 1024);
mobiCoreDevice->donateRam(donateRamSize);
}
if (mobiCoreDevice->mobicoreAlreadyRunning()) {
// MC is already initialized, remove all pending sessions
#define NUM_DRIVERS 2
#define NUM_TRUSTLETS 4
#define NUM_SESSIONS (1 + NUM_DRIVERS + NUM_TRUSTLETS)
for (i = 2; i < NUM_SESSIONS; i++) {
LOG_I("Closing session %i",i);
mobiCoreDevice->closeSession(i);
}
}
// Load device driver if requested
if (loadDriver) {
loadDeviceDriver(driverPath);
}
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();
}
// then wait for them to exit
for (i = 0; i < MAX_SERVERS; i++) {
servers[i]->join();
}
}
//------------------------------------------------------------------------------
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) {
LOG_I("dropConnection(): closing still open device.");
// A connection has been found and has to be closed
device->close(connection);
}
}
//------------------------------------------------------------------------------
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;
uint8_t *tci = NULL;
mcDrvRspOpenSession_t rspOpenSession;
do {
//mobiCoreDevice
FILE *fs = fopen (driverPath.c_str(), "rb");
if (!fs) {
LOG_E("%s: failed: cannot open %s", __FUNCTION__, driverPath.c_str());
break;
}
fclose(fs);
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;
MC_DRV_CMD_OPEN_SESSION_struct cmdOpenSession;
tci = (uint8_t *)malloc(DRIVER_TCI_LEN);
pTciWsm = mobiCoreDevice->allocateContiguousPersistentWsm(DRIVER_TCI_LEN);
if (pTciWsm == NULL) {
LOG_E("allocating WSM TCI for Trustlet failed");
break;
}
cmdOpenSession.deviceId = MC_DEVICE_ID_DEFAULT;
cmdOpenSession.tci = (uint32_t)pTciWsm->physAddr;
cmdOpenSession.len = DRIVER_TCI_LEN;
cmdOpenSession.handle = pTciWsm->handle;
conn = new Connection();
uint32_t mcRet = mobiCoreDevice->openSession(
conn,
&loadDataOpenSession,
&cmdOpenSession,
&(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, tci, 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); \
uint32_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 (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; \
}
//------------------------------------------------------------------------------
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
)
{
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));
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) {
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;
mcDrvRspOpenSession_t rspOpenSession;
mcResult_t ret = device->openSession(
connection,
&loadDataOpenSession,
&cmdOpenSession,
&(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.
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 (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;
}
// REV axh: we cannot trust the clientLib to give us a valid
// sessionId here. Thus we have to check that it belongs to
// the clientLib's process.
device->notify(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 = NULL;
uint32_t pAddrL2 = (uint32_t)device->findWsmL2(cmd.handle);
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(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(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 MobiCore 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));
}
//------------------------------------------------------------------------------
bool MobiCoreDriverDaemon::handleConnection(
Connection *connection
)
{
bool ret = false;
static CMutex mutex;
/* In case of RTM fault do not try to signal anything to MobiCore
* just answer NO to all incoming connections! */
if (mobiCoreDevice->getMcFault()) {
return false;
}
mutex.lock();
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;
}
if (rlen != sizeof(mcDrvCommandHeader)) {
LOG_E("Header length %i is not right.", (int)rlen);
break;
}
ret = true;
switch (mcDrvCommandHeader.commandId) {
//-----------------------------------------
case MC_DRV_CMD_OPEN_DEVICE:
processOpenDevice(connection);
break;
//-----------------------------------------
case MC_DRV_CMD_CLOSE_DEVICE:
processCloseDevice(connection);
break;
//-----------------------------------------
case MC_DRV_CMD_OPEN_SESSION:
processOpenSession(connection);
break;
//-----------------------------------------
case MC_DRV_CMD_CLOSE_SESSION:
processCloseSession(connection);
break;
//-----------------------------------------
case MC_DRV_CMD_NQ_CONNECT:
processNqConnect(connection);
break;
//-----------------------------------------
case MC_DRV_CMD_NOTIFY:
processNotify(connection);
break;
//-----------------------------------------
case MC_DRV_CMD_MAP_BULK_BUF:
processMapBulkBuf(connection);
break;
//-----------------------------------------
case MC_DRV_CMD_UNMAP_BULK_BUF:
processUnmapBulkBuf(connection);
break;
//-----------------------------------------
case MC_DRV_CMD_GET_VERSION:
processGetVersion(connection);
break;
//-----------------------------------------
case MC_DRV_CMD_GET_MOBICORE_VERSION:
processGetMobiCoreVersion(connection);
break;
//-----------------------------------------
default:
LOG_E("Unknown command: %d=0x%x",
mcDrvCommandHeader.commandId,
mcDrvCommandHeader.commandId);
ret = false;
break;
}
} while (0);
mutex.unlock();
LOG_I("handleConnection()<-------");
return ret;
}
//------------------------------------------------------------------------------
/**
* Print daemon command line options
*/
void printUsage(
int argc,
char *args[]
)
{
fprintf(stderr, "usage: %s [-mdsbh]\n", args[0]);
fprintf(stderr, "Start MobiCore Daemon\n\n");
fprintf(stderr, "-h\t\tshow this help\n");
fprintf(stderr, "-b\t\tfork to background\n");
fprintf(stderr, "-m IMAGE\tload mobicore from IMAGE to DDR\n");
fprintf(stderr, "-s\t\tdisable daemon scheduler(default enabled)\n");
fprintf(stderr, "-d SIZE\t\tdonate SIZE bytes to mobicore(disabled on most platforms)\n");
fprintf(stderr, "-r DRIVER\t\tMobiCore 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 MobiCore Driver Daemon.
*/
int main(
int argc,
char *args[]
)
{
// Create the MobiCore 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;
// Mobicore loading disable by default
int mobicoreFlag = 0;
// Autoload driver at start-up
int driverLoadFlag = 0;
std::string mobicoreImage, driverPath;
// Ram donation disabled by default
int donationSize = 0;
// By default don't fork
bool forkDaemon = false;
while ((c = getopt(argc, args, "m:d:r:sbh")) != -1) {
switch (c) {
case 'h': /* Help */
errFlag++;
break;
case 's': /* Disable Scheduler */
schedulerFlag = 0;
break;
case 'd': /* Ram Donation size */
donationSize = atoi(optarg);
break;
case 'm': /* Load mobicore image */
mobicoreFlag = 1;
mobicoreImage = optarg;
break;
case 'b': /* Fork to background */
forkDaemon = true;
break;
case 'r': /* Load mobicore driver at start-up */
driverLoadFlag = 1;
driverPath = optarg;
break;
case ':': /* -d or -m without 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) {
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 = sysconf(_SC_OPEN_MAX); 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 (SIGHUP, &action, NULL);
sigaction (SIGTERM, &action, NULL);
signal(SIGPIPE, SIG_IGN);
mobiCoreDriverDaemon = new MobiCoreDriverDaemon(
/* Scheduler status */
schedulerFlag,
/* Mobicore loading to DDR */
mobicoreFlag,
mobicoreImage,
/* Ram Donation */
donationSize,
/* Auto Driver loading */
driverLoadFlag,
driverPath);
// Start the driver
mobiCoreDriverDaemon->run();
delete mobiCoreDriverDaemon;
// This should not happen
LOG_E("Exiting MobiCoreDaemon");
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 MobiCore version info. MCP return code: %u", mcResult);
failed = true;
} else {
LOG_I("Product ID is %s", versionPayload.versionInfo.productId);
// Check MobiCore version info.
char *msg;
if (!checkVersionOkMCI(versionPayload.versionInfo.versionMci, &msg)) {
LOG_E("%s", msg);
failed = true;
}
LOG_I("%s", msg);
if (!checkVersionOkSO(versionPayload.versionInfo.versionSo, &msg)) {
LOG_E("%s", msg);
failed = true;
}
LOG_I("%s", msg);
if (!checkVersionOkMCLF(versionPayload.versionInfo.versionMclf, &msg)) {
LOG_E("%s", msg);
failed = true;
}
LOG_I("%s", msg);
if (!checkVersionOkCONTAINER(versionPayload.versionInfo.versionContainer, &msg)) {
LOG_E("%s", msg);
failed = true;
}
LOG_I("%s", msg);
}
if (failed) {
exit(1);
}
}
/** @} */