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 //------------------------------------------------------------------------------
getDeviceInstance(void)62 __attribute__ ((weak)) MobiCoreDevice *getDeviceInstance(
63     void
64 )
65 {
66     return new TrustZoneDevice();
67 }
68 
69 //------------------------------------------------------------------------------
TrustZoneDevice(void)70 TrustZoneDevice::TrustZoneDevice(
71     void
72 )
73 {
74     // nothing to do
75 }
76 
77 //------------------------------------------------------------------------------
~TrustZoneDevice(void)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  */
initDevice(const char * devFile,bool loadMobiCore,const char * mobicoreImage,bool enableScheduler)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 //------------------------------------------------------------------------------
initDeviceStep2(void)194 void TrustZoneDevice::initDeviceStep2(
195     void
196 )
197 {
198     // not needed
199 }
200 
201 
202 //------------------------------------------------------------------------------
yield(void)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 //------------------------------------------------------------------------------
nsiq(void)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 //------------------------------------------------------------------------------
notify(uint32_t sessionId)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 //------------------------------------------------------------------------------
getMobicoreStatus(void)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 //------------------------------------------------------------------------------
checkMciVersion(void)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 //------------------------------------------------------------------------------
dumpMobicoreStatus(void)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 //------------------------------------------------------------------------------
waitSsiq(void)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 //------------------------------------------------------------------------------
getMciInstance(uint32_t len,CWsm_ptr * mci,bool * reused)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 //------------------------------------------------------------------------------
registerWsmL2(addr_t buffer,uint32_t len,uint32_t pid)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 //------------------------------------------------------------------------------
allocateContiguousPersistentWsm(uint32_t len)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 //------------------------------------------------------------------------------
unregisterWsmL2(CWsm_ptr pWsm)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 //------------------------------------------------------------------------------
lockWsmL2(uint32_t handle)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 //------------------------------------------------------------------------------
unlockWsmL2(uint32_t handle)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 //------------------------------------------------------------------------------
cleanupWsmL2(void)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 //------------------------------------------------------------------------------
findWsmL2(uint32_t handle)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 //------------------------------------------------------------------------------
findContiguousWsm(uint32_t handle,addr_t * phys,uint32_t * len)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 //------------------------------------------------------------------------------
schedulerAvailable(void)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.
schedule(void)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 //------------------------------------------------------------------------------
handleIrq(void)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