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(¬ification); 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