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(¬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 //------------------------------------------------------------------------------
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