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 = { sessionId :
    260                                     sessionId, payload : 0
    261                                   };
    262 
    263     nq->putNotification(&notification);
    264     //IMPROVEMENT-2012-03-07-maneaval What happens when/if nsiq fails?
    265     //In the old days an exception would be thrown but it was uncertain
    266     //where it was handled, some server(sock or Netlink). In that case
    267     //the server would just die but never actually signaled to the client
    268     //any error condition
    269     nsiq();
    270 }
    271 
    272 //------------------------------------------------------------------------------
    273 uint32_t TrustZoneDevice::getMobicoreStatus(void)
    274 {
    275     uint32_t status;
    276 
    277     pMcKMod->fcInfo(1, &status, NULL);
    278 
    279     return status;
    280 }
    281 
    282 //------------------------------------------------------------------------------
    283 bool TrustZoneDevice::checkMciVersion(void)
    284 {
    285     uint32_t version = 0;
    286     int ret;
    287     char *errmsg;
    288 
    289     ret = pMcKMod->fcInfo(MC_EXT_INFO_ID_MCI_VERSION, NULL, &version);
    290     if (ret != 0) {
    291         LOG_E("pMcKMod->fcInfo() failed with %d", ret);
    292         return false;
    293     }
    294 
    295     // Run-time check.
    296     if (!checkVersionOkMCI(version, &errmsg)) {
    297         LOG_E("%s", errmsg);
    298         return false;
    299     }
    300     LOG_I("%s", errmsg);
    301     return true;
    302 }
    303 
    304 //------------------------------------------------------------------------------
    305 void TrustZoneDevice::dumpMobicoreStatus(
    306     void
    307 )
    308 {
    309     int ret;
    310     uint32_t status, info;
    311     // read additional info about exception-point and print
    312     LOG_E("MobiCore halted !!!");
    313     ret = pMcKMod->fcInfo(1, &status, &info);
    314     LOG_W("MC_HALT: flags               : 0x%8x", info);
    315     ret = pMcKMod->fcInfo(2, &status, &info);
    316     LOG_W("MC_HALT: haltCode            : 0x%8x", info);
    317     ret = pMcKMod->fcInfo(3, &status, &info);
    318     LOG_W("MC_HALT: haltIp              : 0x%8x", info);
    319     ret = pMcKMod->fcInfo(4, &status, &info);
    320     LOG_W("MC_HALT: faultRec.cnt        : 0x%8x", info);
    321     ret = pMcKMod->fcInfo(5, &status, &info);
    322     LOG_W("MC_HALT: faultRec.cause      : 0x%8x", info);
    323     ret = pMcKMod->fcInfo(6, &status, &info);
    324     LOG_W("MC_HALT: faultRec.meta       : 0x%8x", info);
    325     ret = pMcKMod->fcInfo(7, &status, &info);
    326     LOG_W("MC_HALT: faultRec.thread     : 0x%8x", info);
    327     ret = pMcKMod->fcInfo(8, &status, &info);
    328     LOG_W("MC_HALT: faultRec.ip         : 0x%8x", info);
    329     ret = pMcKMod->fcInfo(9, &status, &info);
    330     LOG_W("MC_HALT: faultRec.sp         : 0x%8x", info);
    331     ret = pMcKMod->fcInfo(10, &status, &info);
    332     LOG_W("MC_HALT: faultRec.arch.dfsr  : 0x%8x", info);
    333     ret = pMcKMod->fcInfo(11, &status, &info);
    334     LOG_W("MC_HALT: faultRec.arch.adfsr : 0x%8x", info);
    335     ret = pMcKMod->fcInfo(12, &status, &info);
    336     LOG_W("MC_HALT: faultRec.arch.dfar  : 0x%8x", info);
    337     ret = pMcKMod->fcInfo(13, &status, &info);
    338     LOG_W("MC_HALT: faultRec.arch.ifsr  : 0x%8x", info);
    339     ret = pMcKMod->fcInfo(14, &status, &info);
    340     LOG_W("MC_HALT: faultRec.arch.aifsr : 0x%8x", info);
    341     ret = pMcKMod->fcInfo(15, &status, &info);
    342     LOG_W("MC_HALT: faultRec.arch.ifar  : 0x%8x", info);
    343     ret = pMcKMod->fcInfo(16, &status, &info);
    344     LOG_W("MC_HALT: mcData.flags        : 0x%8x", info);
    345     ret = pMcKMod->fcInfo(19, &status, &info);
    346     LOG_W("MC_HALT: mcExcep.partner     : 0x%8x", info);
    347     ret = pMcKMod->fcInfo(20, &status, &info);
    348     LOG_W("MC_HALT: mcExcep.peer        : 0x%8x", info);
    349     ret = pMcKMod->fcInfo(21, &status, &info);
    350     LOG_W("MC_HALT: mcExcep.message     : 0x%8x", info);
    351     ret = pMcKMod->fcInfo(22, &status, &info);
    352     LOG_W("MC_HALT: mcExcep.data        : 0x%8x", info);
    353 }
    354 
    355 //------------------------------------------------------------------------------
    356 bool TrustZoneDevice::waitSsiq(void)
    357 {
    358     uint32_t cnt;
    359     if (!pMcKMod->waitSSIQ(&cnt)) {
    360         LOG_E("pMcKMod->SSIQ() failed");
    361         return false;
    362     }
    363     LOG_I(" Received SSIQ interrupt from MobiCore, counter=%u", cnt);
    364     return true;
    365 }
    366 
    367 
    368 //------------------------------------------------------------------------------
    369 bool TrustZoneDevice::getMciInstance(uint32_t len, CWsm_ptr *mci, bool *reused)
    370 {
    371     addr_t virtAddr;
    372     uint32_t handle;
    373     addr_t physAddr;
    374     bool isReused = true;
    375     if (len == 0) {
    376         LOG_E("allocateWsm() length is 0");
    377         return false;
    378     }
    379 
    380     mcResult_t ret = pMcKMod->mapMCI(len, &handle, &virtAddr, &physAddr, &isReused);
    381     if (ret != MC_DRV_OK) {
    382         LOG_E("pMcKMod->mmap() failed: %x", ret);
    383         return false;
    384     }
    385 
    386     *mci = new CWsm(virtAddr, len, handle, physAddr);
    387     *reused = isReused;
    388     return true;
    389 }
    390 
    391 
    392 //------------------------------------------------------------------------------
    393 //bool TrustZoneDevice::freeWsm(CWsm_ptr pWsm)
    394 //{
    395 //  int ret = pMcKMod->free(pWsm->handle, pWsm->virtAddr, pWsm->len);
    396 //  if (ret != 0) {
    397 //      LOG_E("pMcKMod->free() failed: %d", ret);
    398 //      return false;
    399 //  }
    400 //  delete pWsm;
    401 //  return true;
    402 //}
    403 
    404 
    405 //------------------------------------------------------------------------------
    406 CWsm_ptr TrustZoneDevice::registerWsmL2(addr_t buffer, uint32_t len, uint32_t pid)
    407 {
    408     addr_t physAddr;
    409     uint32_t handle;
    410 
    411     int ret = pMcKMod->registerWsmL2(
    412                   buffer,
    413                   len,
    414                   pid,
    415                   &handle,
    416                   &physAddr);
    417     if (ret != 0) {
    418         LOG_E("ipMcKMod->registerWsmL2() failed: %d", ret);
    419         return NULL;
    420     }
    421 
    422     return new CWsm(buffer, len, handle, physAddr);
    423 }
    424 
    425 
    426 //------------------------------------------------------------------------------
    427 CWsm_ptr TrustZoneDevice::allocateContiguousPersistentWsm(uint32_t len)
    428 {
    429     CWsm_ptr pWsm = NULL;
    430     // Allocate shared memory
    431     addr_t virtAddr;
    432     uint32_t handle;
    433     addr_t physAddr;
    434 
    435     if (len == 0 )
    436         return NULL;
    437 
    438     if (pMcKMod->mapWsm(len, &handle, &virtAddr, &physAddr))
    439         return NULL;
    440 
    441     // Register (vaddr,paddr) with device
    442     pWsm = new CWsm(virtAddr, len, handle, physAddr);
    443 
    444     // Return pointer to the allocated memory
    445     return pWsm;
    446 }
    447 
    448 
    449 //------------------------------------------------------------------------------
    450 bool TrustZoneDevice::unregisterWsmL2(CWsm_ptr pWsm)
    451 {
    452     int ret = pMcKMod->unregisterWsmL2(pWsm->handle);
    453     if (ret != 0) {
    454         LOG_E("pMcKMod->unregisterWsmL2 failed: %d", ret);
    455         //IMPROVEMENT-2012-03-07 maneaval Make sure we don't leak objects
    456         return false;
    457     }
    458     delete pWsm;
    459     return true;
    460 }
    461 
    462 //------------------------------------------------------------------------------
    463 bool TrustZoneDevice::lockWsmL2(uint32_t handle)
    464 {
    465     int ret = pMcKMod->lockWsmL2(handle);
    466     if (ret != 0) {
    467         LOG_E("pMcKMod->unregisterWsmL2 failed: %d", ret);
    468         return false;
    469     }
    470     return true;
    471 }
    472 
    473 //------------------------------------------------------------------------------
    474 bool TrustZoneDevice::unlockWsmL2(uint32_t handle)
    475 {
    476     LOG_I("Unlocking buffer with handle %u", handle);
    477     int ret = pMcKMod->unlockWsmL2(handle);
    478     if (ret != 0) {
    479         // Failure here is not important
    480         LOG_I("pMcKMod->unregisterWsmL2 failed: %d", ret);
    481         return false;
    482     }
    483     return true;
    484 }
    485 
    486 //------------------------------------------------------------------------------
    487 bool TrustZoneDevice::cleanupWsmL2(void)
    488 {
    489     int ret = pMcKMod->cleanupWsmL2();
    490     if (ret != 0) {
    491         LOG_E("pMcKMod->cleanupWsmL2 failed: %d", ret);
    492         return false;
    493     }
    494     return true;
    495 }
    496 
    497 //------------------------------------------------------------------------------
    498 addr_t TrustZoneDevice::findWsmL2(uint32_t handle)
    499 {
    500     addr_t ret = pMcKMod->findWsmL2(handle);
    501     if (ret == NULL) {
    502         LOG_E("pMcKMod->findWsmL2 failed");
    503         return NULL;
    504     }
    505     LOG_I("Resolved buffer with handle %u to %p", handle, ret);
    506     return ret;
    507 }
    508 
    509 //------------------------------------------------------------------------------
    510 bool TrustZoneDevice::findContiguousWsm(uint32_t handle, addr_t *phys, uint32_t *len)
    511 {
    512     if (pMcKMod->findContiguousWsm(handle, phys, len)) {
    513         LOG_E("pMcKMod->findContiguousWsm failed");
    514         return false;
    515     }
    516     LOG_I("Resolved buffer with handle %u to %p", handle, phys);
    517     return true;
    518 }
    519 
    520 //------------------------------------------------------------------------------
    521 bool TrustZoneDevice::schedulerAvailable(void)
    522 {
    523     return schedulerEnabled;
    524 }
    525 
    526 //------------------------------------------------------------------------------
    527 //TODO Schedulerthread to be switched off if MC is idle. Will be woken up when
    528 //     driver is called again.
    529 void TrustZoneDevice::schedule(void)
    530 {
    531     uint32_t timeslice = SCHEDULING_FREQ;
    532     // loop forever
    533     for (;;) {
    534         // Scheduling decision
    535         if (MC_FLAG_SCHEDULE_IDLE == mcFlags->schedule) {
    536             // MobiCore is IDLE
    537 
    538             // Prevent unnecessary consumption of CPU cycles -> Wait until S-SIQ received
    539             schedSync.wait();
    540 
    541         } else {
    542             // MobiCore is not IDLE (anymore)
    543 
    544             // Check timeslice
    545             if (timeslice == 0) {
    546                 // Slice expired, so force MC internal scheduling decision
    547                 timeslice = SCHEDULING_FREQ;
    548                 if (!nsiq()) {
    549                     break;
    550                 }
    551             } else {
    552                 // Slice not used up, simply hand over control to the MC
    553                 timeslice--;
    554                 if (!yield()) {
    555                     break;
    556                 }
    557             }
    558         }
    559     } //for (;;)
    560 }
    561 //------------------------------------------------------------------------------
    562 void TrustZoneDevice::handleIrq(
    563     void
    564 )
    565 {
    566     LOG_I("Starting Notification Queue IRQ handler...");
    567     for (;;) {
    568         LOG_I(" No notifications pending");
    569         if (!waitSsiq()) {
    570             LOG_E("Waiting for SSIQ failed");
    571             break;
    572         }
    573         LOG_V("S-SIQ received");
    574 
    575         // Save all the
    576         for (;;) {
    577             notification_t *notification = nq->getNotification();
    578             if (NULL == notification) {
    579                 break;
    580             }
    581 
    582             // check if the notification belongs to the MCP session
    583             if (notification->sessionId == SID_MCP) {
    584                 LOG_I(" Found MCP notification, payload=%d",
    585                       notification->payload);
    586 
    587                 // Signal main thread of the driver to continue after MCP
    588                 // command has been processed by the MC
    589                 signalMcpNotification();
    590             } else {
    591                 LOG_I(" Found notification for session %d, payload=%d",
    592                       notification->sessionId, notification->payload);
    593 
    594                 // Get the NQ connection for the session ID
    595                 Connection *connection = getSessionConnection(notification->sessionId, notification);
    596                 if (connection == NULL) {
    597                     /* Couldn't find the session for this notifications
    598                      * In practice this only means one thing: there is
    599                      * a race condition between RTM and the Daemon and
    600                      * RTM won. But we shouldn't drop the notification
    601                      * right away we should just queue it in the device
    602                      */
    603                     LOG_W("Notification for unknown session ID");
    604                     queueUnknownNotification(*notification);
    605                 } else {
    606                     LOG_I(" Forward notification to McClient.");
    607                     // Forward session ID and additional payload of
    608                     // notification to the TLC/Application layer
    609                     connection->writeData((void *)notification,
    610                                           sizeof(notification_t));
    611                 }
    612             }
    613         }
    614 
    615         // Wake up scheduler
    616         schedSync.signal();
    617     }
    618     LOG_E("S-SIQ exception");
    619     // Tell main thread that "something happened"
    620     // MSH thread MUST not block!
    621     DeviceIrqHandler::setExiting();
    622     signalMcpNotification();
    623 }
    624 /** @} */
    625