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