Home | History | Annotate | Download | only in Generic
      1 /** @addtogroup MCD_MCDIMPL_DAEMON_DEV
      2  * @{
      3  * @file
      4  *
      5  *
      6  * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. The name of the author may not be used to endorse or promote
     17  *    products derived from this software without specific prior
     18  *    written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     21  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     26  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include <cstdlib>
     34 #include <stdio.h>
     35 #include <inttypes.h>
     36 #include <list>
     37 
     38 #include "mc_linux.h"
     39 #include "McTypes.h"
     40 #include "Mci/mci.h"
     41 #include "mcVersionHelper.h"
     42 
     43 #include "CSemaphore.h"
     44 #include "CMcKMod.h"
     45 
     46 #include "MobiCoreDevice.h"
     47 #include "TrustZoneDevice.h"
     48 #include "NotificationQueue.h"
     49 
     50 #include "log.h"
     51 
     52 
     53 #define NQ_NUM_ELEMS      (16)
     54 #define NQ_BUFFER_SIZE    (2 * (sizeof(notificationQueueHeader_t)+  NQ_NUM_ELEMS * sizeof(notification_t)))
     55 #define MCP_BUFFER_SIZE   (sizeof(mcpBuffer_t))
     56 #define MCI_BUFFER_SIZE   (NQ_BUFFER_SIZE + MCP_BUFFER_SIZE)
     57 
     58 //------------------------------------------------------------------------------
     59 MC_CHECK_VERSION(MCI, 0, 2);
     60 
     61 //------------------------------------------------------------------------------
     62 __attribute__ ((weak)) MobiCoreDevice *getDeviceInstance(
     63     void
     64 )
     65 {
     66     return new TrustZoneDevice();
     67 }
     68 
     69 //------------------------------------------------------------------------------
     70 TrustZoneDevice::TrustZoneDevice(
     71     void
     72 )
     73 {
     74     // nothing to do
     75 }
     76 
     77 //------------------------------------------------------------------------------
     78 TrustZoneDevice::~TrustZoneDevice(
     79     void
     80 )
     81 {
     82     delete pMcKMod;
     83     delete pWsmMcp;
     84     delete nq;
     85 }
     86 
     87 
     88 //------------------------------------------------------------------------------
     89 /**
     90  * Set up MCI and wait till MC is initialized
     91  * @return true if mobicore is already initialized
     92  */
     93 bool TrustZoneDevice::initDevice(
     94     const char  *devFile,
     95     bool        loadMobiCore,
     96     const char  *mobicoreImage,
     97     bool        enableScheduler)
     98 {
     99     notificationQueue_t *nqStartOut;
    100     notificationQueue_t *nqStartIn;
    101     addr_t mciBuffer;
    102 
    103     pMcKMod = new CMcKMod();
    104     mcResult_t ret = pMcKMod->open(devFile);
    105     if (ret != MC_DRV_OK) {
    106         LOG_W(" Opening kernel module device failed");
    107         return false;
    108     }
    109     if (!pMcKMod->checkVersion()) {
    110         LOG_E("kernel module version mismatch");
    111         return false;
    112     }
    113 
    114     this->schedulerEnabled = enableScheduler;
    115 
    116     // Init MC with NQ and MCP buffer addresses
    117 
    118     // Set up MCI buffer
    119     if (!getMciInstance(MCI_BUFFER_SIZE, &pWsmMcp, &mciReused)) {
    120         return false;
    121     }
    122     mciBuffer = pWsmMcp->virtAddr;
    123 
    124     if (!checkMciVersion()) {
    125         return false;
    126     }
    127 
    128     // Only do a fastcall if MCI has not been reused (MC already initialized)
    129     if (!mciReused) {
    130         // Wipe memory before first usage
    131         bzero(mciBuffer, MCI_BUFFER_SIZE);
    132 
    133         // Init MC with NQ and MCP buffer addresses
    134         int ret = pMcKMod->fcInit(0, NQ_BUFFER_SIZE, NQ_BUFFER_SIZE, MCP_BUFFER_SIZE);
    135         if (ret != 0) {
    136             LOG_E("pMcKMod->fcInit() failed");
    137             return false;
    138         }
    139 
    140         // First empty N-SIQ which results in set up of the MCI structure
    141         if (!nsiq()) {
    142             return false;
    143         }
    144 
    145         // Wait until MobiCore state switches to MC_STATUS_INITIALIZED
    146         // It is assumed that MobiCore always switches state at a certain point in time.
    147         while (1) {
    148             uint32_t status = getMobicoreStatus();
    149 
    150             if (MC_STATUS_INITIALIZED == status) {
    151                 break;
    152             } else if (MC_STATUS_NOT_INITIALIZED == status) {
    153                 // Switch to MobiCore to give it more CPU time.
    154                 if (!yield())
    155                     return false;
    156 		::sleep(1);
    157             } else if (MC_STATUS_HALT == status) {
    158                 dumpMobicoreStatus();
    159                 LOG_E("MobiCore halted during init !!!, state is 0x%x", status);
    160                 return false;
    161             } else { // MC_STATUS_BAD_INIT or anything else
    162                 LOG_E("MCI buffer init failed, state is 0x%x", status);
    163                 return false;
    164             }
    165         }
    166     }
    167 
    168     nqStartOut = (notificationQueue_t *) mciBuffer;
    169     nqStartIn = (notificationQueue_t *) ((uint8_t *) nqStartOut
    170                                          + sizeof(notificationQueueHeader_t) + NQ_NUM_ELEMS
    171                                          * sizeof(notification_t));
    172 
    173     // Set up the NWd NQ
    174     nq = new NotificationQueue(nqStartIn, nqStartOut, NQ_NUM_ELEMS);
    175 
    176     mcpBuffer_t *mcpBuf = (mcpBuffer_t *) ((uint8_t *) mciBuffer + NQ_BUFFER_SIZE);
    177 
    178     // Set up the MC flags
    179     mcFlags = &(mcpBuf->mcFlags);
    180 
    181     // Set up the MCP message
    182     mcpMessage = &(mcpBuf->mcpMessage);
    183 
    184     // convert virtual address of mapping to physical address for the init.
    185     LOG_I("MCI established, at %p, phys=%p, reused=%s",
    186           pWsmMcp->virtAddr,
    187           pWsmMcp->physAddr,
    188           mciReused ? "true" : "false");
    189     return true;
    190 }
    191 
    192 
    193 //------------------------------------------------------------------------------
    194 void TrustZoneDevice::initDeviceStep2(
    195     void
    196 )
    197 {
    198     // not needed
    199 }
    200 
    201 
    202 //------------------------------------------------------------------------------
    203 bool TrustZoneDevice::yield(
    204     void
    205 )
    206 {
    207     int32_t ret = pMcKMod->fcYield();
    208     if (ret != 0) {
    209         LOG_E("pMcKMod->fcYield() failed: %d", ret);
    210     }
    211     return ret == 0;
    212 }
    213 
    214 
    215 //------------------------------------------------------------------------------
    216 bool TrustZoneDevice::nsiq(
    217     void
    218 )
    219 {
    220     // There is no need to set the NON-IDLE flag here. Sending an N-SIQ will
    221     // make the MobiCore run until it could set itself to a state where it
    222     // set the flag itself. IRQs and FIQs are disbaled for this period, so
    223     // there is no way the NWd can interrupt here.
    224 
    225     // not needed: mcFlags->schedule = MC_FLAG_SCHEDULE_NON_IDLE;
    226 
    227     int32_t ret = pMcKMod->fcNSIQ();
    228     if (ret != 0) {
    229         LOG_E("pMcKMod->fcNSIQ() failed : %d", ret);
    230         return false;
    231     }
    232     // now we have to wake the scheduler, so MobiCore gets CPU time.
    233     schedSync.signal();
    234     return true;
    235 }
    236 
    237 
    238 //------------------------------------------------------------------------------
    239 void TrustZoneDevice::notify(
    240     uint32_t sessionId
    241 )
    242 {
    243     // Check if it is MCP session - handle openSession() command
    244     if (sessionId != SID_MCP) {
    245         // Check if session ID exists to avoid flooding of nq by clients
    246         TrustletSession *ts = getTrustletSession(sessionId);
    247         if (ts == NULL) {
    248             LOG_E("no session with id=%d", sessionId);
    249             return;
    250         }
    251 
    252         LOG_I(" Sending notification for session %d to MobiCore", sessionId);
    253     } else {
    254         LOG_I(" Sending MCP notification to MobiCore");
    255     }
    256 
    257     // Notify MobiCore about new data
    258 
    259     notification_t notification = {
    260         .sessionId = sessionId,
    261         .payload = 0
    262     };
    263 
    264     nq->putNotification(&notification);
    265     //IMPROVEMENT-2012-03-07-maneaval What happens when/if nsiq fails?
    266     //In the old days an exception would be thrown but it was uncertain
    267     //where it was handled, some server(sock or Netlink). In that case
    268     //the server would just die but never actually signaled to the client
    269     //any error condition
    270     nsiq();
    271 }
    272 
    273 //------------------------------------------------------------------------------
    274 uint32_t TrustZoneDevice::getMobicoreStatus(void)
    275 {
    276     uint32_t status;
    277 
    278     pMcKMod->fcInfo(1, &status, NULL);
    279 
    280     return status;
    281 }
    282 
    283 //------------------------------------------------------------------------------
    284 bool TrustZoneDevice::checkMciVersion(void)
    285 {
    286     uint32_t version = 0;
    287     int ret;
    288     char *errmsg;
    289 
    290     ret = pMcKMod->fcInfo(MC_EXT_INFO_ID_MCI_VERSION, NULL, &version);
    291     if (ret != 0) {
    292         LOG_E("pMcKMod->fcInfo() failed with %d", ret);
    293         return false;
    294     }
    295 
    296     // Run-time check.
    297     if (!checkVersionOkMCI(version, &errmsg)) {
    298         LOG_E("%s", errmsg);
    299         return false;
    300     }
    301     LOG_I("%s", errmsg);
    302     return true;
    303 }
    304 
    305 //------------------------------------------------------------------------------
    306 void TrustZoneDevice::dumpMobicoreStatus(
    307     void
    308 )
    309 {
    310     int ret;
    311     uint32_t status, info;
    312     // read additional info about exception-point and print
    313     LOG_E("MobiCore halted !!!");
    314     ret = pMcKMod->fcInfo(1, &status, &info);
    315     LOG_W("MC_HALT: flags               : 0x%8x", info);
    316     ret = pMcKMod->fcInfo(2, &status, &info);
    317     LOG_W("MC_HALT: haltCode            : 0x%8x", info);
    318     ret = pMcKMod->fcInfo(3, &status, &info);
    319     LOG_W("MC_HALT: haltIp              : 0x%8x", info);
    320     ret = pMcKMod->fcInfo(4, &status, &info);
    321     LOG_W("MC_HALT: faultRec.cnt        : 0x%8x", info);
    322     ret = pMcKMod->fcInfo(5, &status, &info);
    323     LOG_W("MC_HALT: faultRec.cause      : 0x%8x", info);
    324     ret = pMcKMod->fcInfo(6, &status, &info);
    325     LOG_W("MC_HALT: faultRec.meta       : 0x%8x", info);
    326     ret = pMcKMod->fcInfo(7, &status, &info);
    327     LOG_W("MC_HALT: faultRec.thread     : 0x%8x", info);
    328     ret = pMcKMod->fcInfo(8, &status, &info);
    329     LOG_W("MC_HALT: faultRec.ip         : 0x%8x", info);
    330     ret = pMcKMod->fcInfo(9, &status, &info);
    331     LOG_W("MC_HALT: faultRec.sp         : 0x%8x", info);
    332     ret = pMcKMod->fcInfo(10, &status, &info);
    333     LOG_W("MC_HALT: faultRec.arch.dfsr  : 0x%8x", info);
    334     ret = pMcKMod->fcInfo(11, &status, &info);
    335     LOG_W("MC_HALT: faultRec.arch.adfsr : 0x%8x", info);
    336     ret = pMcKMod->fcInfo(12, &status, &info);
    337     LOG_W("MC_HALT: faultRec.arch.dfar  : 0x%8x", info);
    338     ret = pMcKMod->fcInfo(13, &status, &info);
    339     LOG_W("MC_HALT: faultRec.arch.ifsr  : 0x%8x", info);
    340     ret = pMcKMod->fcInfo(14, &status, &info);
    341     LOG_W("MC_HALT: faultRec.arch.aifsr : 0x%8x", info);
    342     ret = pMcKMod->fcInfo(15, &status, &info);
    343     LOG_W("MC_HALT: faultRec.arch.ifar  : 0x%8x", info);
    344     ret = pMcKMod->fcInfo(16, &status, &info);
    345     LOG_W("MC_HALT: mcData.flags        : 0x%8x", info);
    346     ret = pMcKMod->fcInfo(19, &status, &info);
    347     LOG_W("MC_HALT: mcExcep.partner     : 0x%8x", info);
    348     ret = pMcKMod->fcInfo(20, &status, &info);
    349     LOG_W("MC_HALT: mcExcep.peer        : 0x%8x", info);
    350     ret = pMcKMod->fcInfo(21, &status, &info);
    351     LOG_W("MC_HALT: mcExcep.message     : 0x%8x", info);
    352     ret = pMcKMod->fcInfo(22, &status, &info);
    353     LOG_W("MC_HALT: mcExcep.data        : 0x%8x", info);
    354 }
    355 
    356 //------------------------------------------------------------------------------
    357 bool TrustZoneDevice::waitSsiq(void)
    358 {
    359     uint32_t cnt;
    360     if (!pMcKMod->waitSSIQ(&cnt)) {
    361         LOG_E("pMcKMod->SSIQ() failed");
    362         return false;
    363     }
    364     LOG_I(" Received SSIQ interrupt from MobiCore, counter=%u", cnt);
    365     return true;
    366 }
    367 
    368 
    369 //------------------------------------------------------------------------------
    370 bool TrustZoneDevice::getMciInstance(uint32_t len, CWsm_ptr *mci, bool *reused)
    371 {
    372     addr_t virtAddr;
    373     uint32_t handle;
    374     addr_t physAddr;
    375     bool isReused = true;
    376     if (len == 0) {
    377         LOG_E("allocateWsm() length is 0");
    378         return false;
    379     }
    380 
    381     mcResult_t ret = pMcKMod->mapMCI(len, &handle, &virtAddr, &physAddr, &isReused);
    382     if (ret != MC_DRV_OK) {
    383         LOG_E("pMcKMod->mmap() failed: %x", ret);
    384         return false;
    385     }
    386 
    387     *mci = new CWsm(virtAddr, len, handle, physAddr);
    388     *reused = isReused;
    389     return true;
    390 }
    391 
    392 
    393 //------------------------------------------------------------------------------
    394 //bool TrustZoneDevice::freeWsm(CWsm_ptr pWsm)
    395 //{
    396 //  int ret = pMcKMod->free(pWsm->handle, pWsm->virtAddr, pWsm->len);
    397 //  if (ret != 0) {
    398 //      LOG_E("pMcKMod->free() failed: %d", ret);
    399 //      return false;
    400 //  }
    401 //  delete pWsm;
    402 //  return true;
    403 //}
    404 
    405 
    406 //------------------------------------------------------------------------------
    407 CWsm_ptr TrustZoneDevice::registerWsmL2(addr_t buffer, uint32_t len, uint32_t pid)
    408 {
    409     addr_t physAddr;
    410     uint32_t handle;
    411 
    412     int ret = pMcKMod->registerWsmL2(
    413                   buffer,
    414                   len,
    415                   pid,
    416                   &handle,
    417                   &physAddr);
    418     if (ret != 0) {
    419         LOG_E("ipMcKMod->registerWsmL2() failed: %d", ret);
    420         return NULL;
    421     }
    422 
    423     return new CWsm(buffer, len, handle, physAddr);
    424 }
    425 
    426 
    427 //------------------------------------------------------------------------------
    428 CWsm_ptr TrustZoneDevice::allocateContiguousPersistentWsm(uint32_t len)
    429 {
    430     CWsm_ptr pWsm = NULL;
    431     // Allocate shared memory
    432     addr_t virtAddr;
    433     uint32_t handle;
    434     addr_t physAddr;
    435 
    436     if (len == 0 )
    437         return NULL;
    438 
    439     if (pMcKMod->mapWsm(len, &handle, &virtAddr, &physAddr))
    440         return NULL;
    441 
    442     // Register (vaddr,paddr) with device
    443     pWsm = new CWsm(virtAddr, len, handle, physAddr);
    444 
    445     // Return pointer to the allocated memory
    446     return pWsm;
    447 }
    448 
    449 
    450 //------------------------------------------------------------------------------
    451 bool TrustZoneDevice::unregisterWsmL2(CWsm_ptr pWsm)
    452 {
    453     int ret = pMcKMod->unregisterWsmL2(pWsm->handle);
    454     if (ret != 0) {
    455         LOG_E("pMcKMod->unregisterWsmL2 failed: %d", ret);
    456         //IMPROVEMENT-2012-03-07 maneaval Make sure we don't leak objects
    457         return false;
    458     }
    459     delete pWsm;
    460     return true;
    461 }
    462 
    463 //------------------------------------------------------------------------------
    464 bool TrustZoneDevice::lockWsmL2(uint32_t handle)
    465 {
    466     int ret = pMcKMod->lockWsmL2(handle);
    467     if (ret != 0) {
    468         LOG_E("pMcKMod->unregisterWsmL2 failed: %d", ret);
    469         return false;
    470     }
    471     return true;
    472 }
    473 
    474 //------------------------------------------------------------------------------
    475 bool TrustZoneDevice::unlockWsmL2(uint32_t handle)
    476 {
    477     LOG_I("Unlocking buffer with handle %u", handle);
    478     int ret = pMcKMod->unlockWsmL2(handle);
    479     if (ret != 0) {
    480         // Failure here is not important
    481         LOG_I("pMcKMod->unregisterWsmL2 failed: %d", ret);
    482         return false;
    483     }
    484     return true;
    485 }
    486 
    487 //------------------------------------------------------------------------------
    488 bool TrustZoneDevice::cleanupWsmL2(void)
    489 {
    490     int ret = pMcKMod->cleanupWsmL2();
    491     if (ret != 0) {
    492         LOG_E("pMcKMod->cleanupWsmL2 failed: %d", ret);
    493         return false;
    494     }
    495     return true;
    496 }
    497 
    498 //------------------------------------------------------------------------------
    499 addr_t TrustZoneDevice::findWsmL2(uint32_t handle)
    500 {
    501     addr_t ret = pMcKMod->findWsmL2(handle);
    502     if (ret == NULL) {
    503         LOG_E("pMcKMod->findWsmL2 failed");
    504         return NULL;
    505     }
    506     LOG_I("Resolved buffer with handle %u to %p", handle, ret);
    507     return ret;
    508 }
    509 
    510 //------------------------------------------------------------------------------
    511 bool TrustZoneDevice::findContiguousWsm(uint32_t handle, addr_t *phys, uint32_t *len)
    512 {
    513     if (pMcKMod->findContiguousWsm(handle, phys, len)) {
    514         LOG_E("pMcKMod->findContiguousWsm failed");
    515         return false;
    516     }
    517     LOG_I("Resolved buffer with handle %u to %p", handle, phys);
    518     return true;
    519 }
    520 
    521 //------------------------------------------------------------------------------
    522 bool TrustZoneDevice::schedulerAvailable(void)
    523 {
    524     return schedulerEnabled;
    525 }
    526 
    527 //------------------------------------------------------------------------------
    528 //TODO Schedulerthread to be switched off if MC is idle. Will be woken up when
    529 //     driver is called again.
    530 void TrustZoneDevice::schedule(void)
    531 {
    532     uint32_t timeslice = SCHEDULING_FREQ;
    533     // loop forever
    534     for (;;) {
    535         // Scheduling decision
    536         if (MC_FLAG_SCHEDULE_IDLE == mcFlags->schedule) {
    537             // MobiCore is IDLE
    538 
    539             // Prevent unnecessary consumption of CPU cycles -> Wait until S-SIQ received
    540             schedSync.wait();
    541 
    542         } else {
    543             // MobiCore is not IDLE (anymore)
    544 
    545             // Check timeslice
    546             if (timeslice == 0) {
    547                 // Slice expired, so force MC internal scheduling decision
    548                 timeslice = SCHEDULING_FREQ;
    549                 if (!nsiq()) {
    550                     break;
    551                 }
    552             } else {
    553                 // Slice not used up, simply hand over control to the MC
    554                 timeslice--;
    555                 if (!yield()) {
    556                     break;
    557                 }
    558             }
    559         }
    560     } //for (;;)
    561 }
    562 //------------------------------------------------------------------------------
    563 void TrustZoneDevice::handleIrq(
    564     void
    565 )
    566 {
    567     LOG_I("Starting Notification Queue IRQ handler...");
    568     for (;;) {
    569         LOG_I(" No notifications pending");
    570         if (!waitSsiq()) {
    571             LOG_E("Waiting for SSIQ failed");
    572             break;
    573         }
    574         LOG_V("S-SIQ received");
    575 
    576         // Save all the
    577         for (;;) {
    578             notification_t *notification = nq->getNotification();
    579             if (NULL == notification) {
    580                 break;
    581             }
    582 
    583             // check if the notification belongs to the MCP session
    584             if (notification->sessionId == SID_MCP) {
    585                 LOG_I(" Found MCP notification, payload=%d",
    586                       notification->payload);
    587 
    588                 // Signal main thread of the driver to continue after MCP
    589                 // command has been processed by the MC
    590                 signalMcpNotification();
    591             } else {
    592                 LOG_I(" Found notification for session %d, payload=%d",
    593                       notification->sessionId, notification->payload);
    594 
    595                 // Get the NQ connection for the session ID
    596                 Connection *connection = getSessionConnection(notification->sessionId, notification);
    597                 if (connection == NULL) {
    598                     /* Couldn't find the session for this notifications
    599                      * In practice this only means one thing: there is
    600                      * a race condition between RTM and the Daemon and
    601                      * RTM won. But we shouldn't drop the notification
    602                      * right away we should just queue it in the device
    603                      */
    604                     LOG_W("Notification for unknown session ID");
    605                     queueUnknownNotification(*notification);
    606                 } else {
    607                     LOG_I(" Forward notification to McClient.");
    608                     // Forward session ID and additional payload of
    609                     // notification to the TLC/Application layer
    610                     connection->writeData((void *)notification,
    611                                           sizeof(notification_t));
    612                 }
    613             }
    614         }
    615 
    616         // Wake up scheduler
    617         schedSync.signal();
    618     }
    619     LOG_E("S-SIQ exception");
    620     // Tell main thread that "something happened"
    621     // MSH thread MUST not block!
    622     DeviceIrqHandler::setExiting();
    623     signalMcpNotification();
    624 }
    625 /** @} */
    626