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 <pthread.h>
35 #include "McTypes.h"
36 
37 #include "DeviceScheduler.h"
38 #include "DeviceIrqHandler.h"
39 #include "ExcDevice.h"
40 #include "Connection.h"
41 #include "TrustletSession.h"
42 
43 #include "MobiCoreDevice.h"
44 #include "Mci/mci.h"
45 #include "mcLoadFormat.h"
46 
47 
48 #include "log.h"
49 #include "public/MobiCoreDevice.h"
50 
51 
52 //------------------------------------------------------------------------------
MobiCoreDevice()53 MobiCoreDevice::MobiCoreDevice()
54 {
55     mcFault = false;
56 }
57 
58 //------------------------------------------------------------------------------
~MobiCoreDevice()59 MobiCoreDevice::~MobiCoreDevice()
60 {
61     delete mcVersionInfo;
62     mcVersionInfo = NULL;
63 }
64 
65 //------------------------------------------------------------------------------
getTrustletSession(uint32_t sessionId)66 TrustletSession *MobiCoreDevice::getTrustletSession(uint32_t sessionId)
67 {
68     for (trustletSessionIterator_t session = trustletSessions.begin();
69             session != trustletSessions.end();
70             ++session) {
71         TrustletSession *tsTmp = *session;
72         if (tsTmp->sessionId == sessionId) {
73             return tsTmp;
74         }
75     }
76     return NULL;
77 }
78 
79 
cleanSessionBuffers(TrustletSession * session)80 void MobiCoreDevice::cleanSessionBuffers(TrustletSession *session)
81 {
82     CWsm_ptr pWsm = session->popBulkBuff();
83 
84     while (pWsm) {
85         unlockWsmL2(pWsm->handle);
86         pWsm = session->popBulkBuff();
87     }
88 }
89 //------------------------------------------------------------------------------
removeTrustletSession(uint32_t sessionId)90 void MobiCoreDevice::removeTrustletSession(uint32_t sessionId)
91 {
92     for (trustletSessionIterator_t session = trustletSessions.begin();
93             session != trustletSessions.end();
94             ++session) {
95         if ((*session)->sessionId == sessionId) {
96             cleanSessionBuffers(*session);
97             trustletSessions.erase(session);
98             return;
99         }
100     }
101 }
102 //------------------------------------------------------------------------------
getSessionConnection(uint32_t sessionId,notification_t * notification)103 Connection *MobiCoreDevice::getSessionConnection(uint32_t sessionId, notification_t *notification)
104 {
105     Connection *con = NULL;
106     TrustletSession *ts = NULL;
107 
108     ts = getTrustletSession(sessionId);
109     if (ts == NULL) {
110         return NULL;
111     }
112 
113     con = ts->notificationConnection;
114     if (con == NULL) {
115         ts->queueNotification(notification);
116         return NULL;
117     }
118 
119     return con;
120 }
121 
122 
123 //------------------------------------------------------------------------------
open(Connection * connection)124 bool MobiCoreDevice::open(Connection *connection)
125 {
126     // Link this device to the connection
127     connection->connectionData = this;
128     return true;
129 }
130 
131 
132 //------------------------------------------------------------------------------
133 /**
134  * Close device.
135  *
136  * Removes all sessions to a connection. Though, clientLib rejects the closeDevice()
137  * command if still sessions connected to the device, this is needed to clean up all
138  * sessions if client dies.
139  */
close(Connection * connection)140 void MobiCoreDevice::close(Connection *connection)
141 {
142     trustletSessionList_t::reverse_iterator interator;
143     static CMutex mutex;
144     // 1. Iterate through device session to find connection
145     // 2. Decide what to do with open Trustlet sessions
146     // 3. Remove & delete deviceSession from vector
147 
148     // Enter critical section
149     mutex.lock();
150     for (interator = trustletSessions.rbegin();
151             interator != trustletSessions.rend();
152             interator++) {
153         TrustletSession *ts = *interator;
154 
155         if (ts->deviceConnection == connection) {
156             closeSession(connection, ts->sessionId);
157         }
158     }
159     // Leave critical section
160     mutex.unlock();
161 
162     // After the trustlet is done make sure to tell the driver to cleanup
163     // all the orphaned drivers
164     cleanupWsmL2();
165 
166     connection->connectionData = NULL;
167 }
168 
169 
170 //------------------------------------------------------------------------------
start(void)171 void MobiCoreDevice::start(void)
172 {
173     // Call the device specific initialization
174     //  initDevice();
175 
176     LOG_I("Starting DeviceIrqHandler...");
177     // Start the irq handling thread
178     DeviceIrqHandler::start();
179 
180     if (schedulerAvailable()) {
181         LOG_I("Starting DeviceScheduler...");
182         // Start the scheduling handling thread
183         DeviceScheduler::start();
184     } else {
185         LOG_I("No DeviceScheduler available.");
186     }
187 }
188 
189 
190 //------------------------------------------------------------------------------
signalMcpNotification(void)191 void MobiCoreDevice::signalMcpNotification(void)
192 {
193     mcpSessionNotification.signal();
194 }
195 
196 
197 //------------------------------------------------------------------------------
waitMcpNotification(void)198 bool MobiCoreDevice::waitMcpNotification(void)
199 {
200     int counter = 5;
201     while (1) {
202         // In case of fault just return, nothing to do here
203         if (mcFault) {
204             return false;
205         }
206         // Wait 10 seconds for notification
207         if (mcpSessionNotification.wait(10) == false) {
208             // No MCP answer received and mobicore halted, dump mobicore status
209             // then throw exception
210             LOG_I("No MCP answer received in 2 seconds.");
211             if (getMobicoreStatus() == MC_STATUS_HALT) {
212                 dumpMobicoreStatus();
213                 mcFault = true;
214                 return false;
215             } else {
216                 counter--;
217                 if (counter < 1) {
218                     mcFault = true;
219                     return false;
220                 }
221             }
222         } else {
223             break;
224         }
225     }
226 
227     // Check healthiness state of the device
228     if (DeviceIrqHandler::isExiting()) {
229         LOG_I("waitMcpNotification(): IrqHandler thread died! Joining");
230         DeviceIrqHandler::join();
231         LOG_I("waitMcpNotification(): Joined");
232         LOG_E("IrqHandler thread died!");
233         return false;
234     }
235 
236     if (DeviceScheduler::isExiting()) {
237         LOG_I("waitMcpNotification(): Scheduler thread died! Joining");
238         DeviceScheduler::join();
239         LOG_I("waitMcpNotification(): Joined");
240         LOG_E("Scheduler thread died!");
241         return false;
242     }
243     return true;
244 }
245 
246 
247 //------------------------------------------------------------------------------
openSession(Connection * deviceConnection,loadDataOpenSession_ptr pLoadDataOpenSession,MC_DRV_CMD_OPEN_SESSION_struct * cmdOpenSession,mcDrvRspOpenSessionPayload_ptr pRspOpenSessionPayload)248 mcResult_t MobiCoreDevice::openSession(
249     Connection                      *deviceConnection,
250     loadDataOpenSession_ptr         pLoadDataOpenSession,
251     MC_DRV_CMD_OPEN_SESSION_struct  *cmdOpenSession,
252     mcDrvRspOpenSessionPayload_ptr  pRspOpenSessionPayload
253 )
254 {
255     do {
256         addr_t tci;
257         uint32_t len;
258         uint32_t handle = cmdOpenSession->handle;
259 
260         if (!findContiguousWsm(handle, &tci, &len)) {
261             LOG_E("Failed to find contiguous WSM %u", handle);
262             return MC_DRV_ERR_DAEMON_WSM_HANDLE_NOT_FOUND;
263         }
264 
265         if (!lockWsmL2(handle)) {
266             LOG_E("Failed to lock contiguous WSM %u", handle);
267             return MC_DRV_ERR_DAEMON_WSM_HANDLE_NOT_FOUND;
268         }
269 
270         // Write MCP open message to buffer
271         mcpMessage->cmdOpen.cmdHeader.cmdId = MC_MCP_CMD_OPEN_SESSION;
272         mcpMessage->cmdOpen.uuid = cmdOpenSession->uuid;
273         mcpMessage->cmdOpen.wsmTypeTci = WSM_CONTIGUOUS;
274         mcpMessage->cmdOpen.adrTciBuffer = (uint32_t)(tci);
275         mcpMessage->cmdOpen.ofsTciBuffer = 0;
276         mcpMessage->cmdOpen.lenTciBuffer = len;
277 
278         LOG_I(" Using phys=%p, len=%d as TCI buffer",
279               (addr_t)(cmdOpenSession->tci),
280               cmdOpenSession->len);
281 
282         // check if load data is provided
283         mcpMessage->cmdOpen.wsmTypeLoadData = WSM_L2;
284         mcpMessage->cmdOpen.adrLoadData = (uint32_t)pLoadDataOpenSession->baseAddr;
285         mcpMessage->cmdOpen.ofsLoadData = pLoadDataOpenSession->offs;
286         mcpMessage->cmdOpen.lenLoadData = pLoadDataOpenSession->len;
287         memcpy(&mcpMessage->cmdOpen.tlHeader, pLoadDataOpenSession->tlHeader, sizeof(*pLoadDataOpenSession->tlHeader));
288 
289         // Clear the notifications queue. We asume the race condition we have
290         // seen in openSession never happens elsewhere
291         notifications = std::queue<notification_t>();
292         // Notify MC about a new command inside the MCP buffer
293         notify(SID_MCP);
294 
295         // Wait till response from MC is available
296         if (!waitMcpNotification()) {
297             // Here Mobicore can be considered dead.
298             unlockWsmL2(handle);
299             return MC_DRV_ERR_DAEMON_MCI_ERROR;
300         }
301 
302         // Check if the command response ID is correct
303         if ((MC_MCP_CMD_OPEN_SESSION | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) {
304             LOG_E("CMD_OPEN_SESSION got invalid MCP command response(0x%X)", mcpMessage->rspHeader.rspId);
305             // Something is messing with our MCI memory, we cannot know if the Trustlet was loaded.
306             // Had in been loaded, we are loosing track of it here.
307             unlockWsmL2(handle);
308             return MC_DRV_ERR_DAEMON_MCI_ERROR;
309         }
310 
311         uint32_t mcRet = mcpMessage->rspOpen.rspHeader.result;
312 
313         if (mcRet != MC_MCP_RET_OK) {
314             LOG_E("MCP OPEN returned code %d.", mcRet);
315             unlockWsmL2(handle);
316             return MAKE_MC_DRV_MCP_ERROR(mcRet);
317         }
318 
319         LOG_I(" After MCP OPEN, we have %d queued notifications",
320               notifications.size());
321         // Read MC answer from MCP buffer
322         TrustletSession *trustletSession = new TrustletSession(
323             deviceConnection,
324             mcpMessage->rspOpen.sessionId);
325 
326         pRspOpenSessionPayload->sessionId = trustletSession->sessionId;
327         pRspOpenSessionPayload->deviceSessionId = (uint32_t)trustletSession;
328         pRspOpenSessionPayload->sessionMagic = trustletSession->sessionMagic;
329 
330         trustletSessions.push_back(trustletSession);
331 
332         trustletSession->addBulkBuff(new CWsm((void *)pLoadDataOpenSession->offs, pLoadDataOpenSession->len, handle, 0));
333 
334         // We have some queued notifications and we need to send them to them
335         // trustlet session
336         while (!notifications.empty()) {
337             trustletSession->queueNotification(&notifications.front());
338             notifications.pop();
339         }
340 
341     } while (0);
342     return MC_DRV_OK;
343 }
344 
345 
346 //------------------------------------------------------------------------------
registerTrustletConnection(Connection * connection,MC_DRV_CMD_NQ_CONNECT_struct * cmdNqConnect)347 TrustletSession *MobiCoreDevice::registerTrustletConnection(
348     Connection                    *connection,
349     MC_DRV_CMD_NQ_CONNECT_struct *cmdNqConnect
350 )
351 {
352     LOG_I(" Registering notification socket with Service session %d.",
353           cmdNqConnect->sessionId);
354     LOG_V("  Searching sessionId %d with sessionMagic %d",
355           cmdNqConnect->sessionId,
356           cmdNqConnect->sessionMagic);
357 
358     for (trustletSessionIterator_t iterator = trustletSessions.begin();
359             iterator != trustletSessions.end();
360             ++iterator) {
361         TrustletSession *ts = *iterator;
362 
363         if (ts != (TrustletSession *) (cmdNqConnect->deviceSessionId)) {
364             continue;
365         }
366 
367         if ( (ts->sessionMagic != cmdNqConnect->sessionMagic)
368                 || (ts->sessionId != cmdNqConnect->sessionId)) {
369             continue;
370         }
371 
372         ts->notificationConnection = connection;
373 
374         LOG_I(" Found Service session, registered connection.");
375 
376         return ts;
377     }
378 
379     LOG_I("registerTrustletConnection(): search failed");
380     return NULL;
381 }
382 
383 
384 //------------------------------------------------------------------------------
closeSession(uint32_t sessionId)385 mcResult_t MobiCoreDevice::closeSession(uint32_t sessionId)
386 {
387     LOG_I(" Write MCP CLOSE message to MCI, notify and wait");
388 
389     // Write MCP close message to buffer
390     mcpMessage->cmdClose.cmdHeader.cmdId = MC_MCP_CMD_CLOSE_SESSION;
391     mcpMessage->cmdClose.sessionId = sessionId;
392 
393     // Notify MC about the availability of a new command inside the MCP buffer
394     notify(SID_MCP);
395 
396     // Wait till response from MSH is available
397     if (!waitMcpNotification()) {
398         return MC_DRV_ERR_DAEMON_MCI_ERROR;
399     }
400 
401     // Check if the command response ID is correct
402     if ((MC_MCP_CMD_CLOSE_SESSION | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) {
403         LOG_E("CMD_CLOSE_SESSION got invalid MCP response");
404         return MC_DRV_ERR_DAEMON_MCI_ERROR;
405     }
406 
407     // Read MC answer from MCP buffer
408     uint32_t mcRet = mcpMessage->rspOpen.rspHeader.result;
409 
410     if (mcRet != MC_MCP_RET_OK) {
411         LOG_E("CMD_CLOSE_SESSION error %d", mcRet);
412         return MAKE_MC_DRV_MCP_ERROR(mcRet);
413     }
414 
415     return MC_DRV_OK;
416 }
417 
418 //------------------------------------------------------------------------------
419 /**
420  * TODO-2012-09-19-haenellu: Do some more checks here, otherwise rogue clientLib
421  * can close sessions from different TLCs. That is, deviceConnection is ignored below.
422  *
423  * Need connection as well as according session ID, so that a client can not
424  * close sessions not belonging to him.
425  */
closeSession(Connection * deviceConnection,uint32_t sessionId)426 mcResult_t MobiCoreDevice::closeSession(Connection *deviceConnection, uint32_t sessionId)
427 {
428     TrustletSession *ts = getTrustletSession(sessionId);
429     if (ts == NULL) {
430         LOG_E("no session found with id=%d", sessionId);
431         return MC_DRV_ERR_DAEMON_UNKNOWN_SESSION;
432     }
433 
434     uint32_t mcRet = closeSession(sessionId);
435     if (mcRet != MC_DRV_OK) {
436         return mcRet;
437     }
438 
439     // remove objects
440     removeTrustletSession(sessionId);
441     delete ts;
442 
443     return MC_DRV_OK;
444 }
445 
446 
447 //------------------------------------------------------------------------------
mapBulk(uint32_t sessionId,uint32_t handle,uint32_t pAddrL2,uint32_t offsetPayload,uint32_t lenBulkMem,uint32_t * secureVirtualAdr)448 mcResult_t MobiCoreDevice::mapBulk(uint32_t sessionId, uint32_t handle, uint32_t pAddrL2,
449                                    uint32_t offsetPayload, uint32_t lenBulkMem, uint32_t *secureVirtualAdr)
450 {
451     TrustletSession *ts = getTrustletSession(sessionId);
452     if (ts == NULL) {
453         LOG_E("no session found with id=%d", sessionId);
454         return MC_DRV_ERR_DAEMON_UNKNOWN_SESSION;
455     }
456 
457     // TODO-2012-09-06-haenellu: Think about not ignoring the error case, ClientLib does not allow this.
458     ts->addBulkBuff(new CWsm((void *)offsetPayload, lenBulkMem, handle, (void *)pAddrL2));
459     // Write MCP map message to buffer
460     mcpMessage->cmdMap.cmdHeader.cmdId = MC_MCP_CMD_MAP;
461     mcpMessage->cmdMap.sessionId = sessionId;
462     mcpMessage->cmdMap.wsmType = WSM_L2;
463     mcpMessage->cmdMap.adrBuffer = (uint32_t)(pAddrL2);
464     mcpMessage->cmdMap.ofsBuffer = offsetPayload;
465     mcpMessage->cmdMap.lenBuffer = lenBulkMem;
466 
467     // Notify MC about the availability of a new command inside the MCP buffer
468     notify(SID_MCP);
469 
470     // Wait till response from MC is available
471     if (!waitMcpNotification()) {
472         return MC_DRV_ERR_DAEMON_MCI_ERROR;
473     }
474 
475     // Check if the command response ID is correct
476     if (mcpMessage->rspHeader.rspId != (MC_MCP_CMD_MAP | FLAG_RESPONSE)) {
477         LOG_E("CMD_MAP got invalid MCP response");
478         return MC_DRV_ERR_DAEMON_MCI_ERROR;
479     }
480 
481     uint32_t mcRet = mcpMessage->rspMap.rspHeader.result;
482 
483     if (mcRet != MC_MCP_RET_OK) {
484         LOG_E("MCP MAP returned code %d.", mcRet);
485         return MAKE_MC_DRV_MCP_ERROR(mcRet);
486     }
487 
488     *secureVirtualAdr = mcpMessage->rspMap.secureVirtualAdr;
489     return MC_DRV_OK;
490 }
491 
492 
493 //------------------------------------------------------------------------------
unmapBulk(uint32_t sessionId,uint32_t handle,uint32_t secureVirtualAdr,uint32_t lenBulkMem)494 mcResult_t MobiCoreDevice::unmapBulk(uint32_t sessionId, uint32_t handle,
495                                      uint32_t secureVirtualAdr, uint32_t lenBulkMem)
496 {
497     TrustletSession *ts = getTrustletSession(sessionId);
498     if (ts == NULL) {
499         LOG_E("no session found with id=%d", sessionId);
500         return MC_DRV_ERR_DAEMON_UNKNOWN_SESSION;
501     }
502 
503     // Write MCP unmap command to buffer
504     mcpMessage->cmdUnmap.cmdHeader.cmdId = MC_MCP_CMD_UNMAP;
505     mcpMessage->cmdUnmap.sessionId = sessionId;
506     mcpMessage->cmdUnmap.wsmType = WSM_L2;
507     mcpMessage->cmdUnmap.secureVirtualAdr = secureVirtualAdr;
508     mcpMessage->cmdUnmap.lenVirtualBuffer = lenBulkMem;
509 
510     // Notify MC about the availability of a new command inside the MCP buffer
511     notify(SID_MCP);
512 
513     // Wait till response from MC is available
514     if (!waitMcpNotification()) {
515         return MC_DRV_ERR_DAEMON_MCI_ERROR;
516     }
517 
518     // Check if the command response ID is correct
519     if (mcpMessage->rspHeader.rspId != (MC_MCP_CMD_UNMAP | FLAG_RESPONSE)) {
520         LOG_E("CMD_OPEN_SESSION got invalid MCP response");
521         return MC_DRV_ERR_DAEMON_MCI_ERROR;
522     }
523 
524     uint32_t mcRet = mcpMessage->rspUnmap.rspHeader.result;
525 
526     if (mcRet != MC_MCP_RET_OK) {
527         LOG_E("MCP UNMAP returned code %d.", mcRet);
528         return MAKE_MC_DRV_MCP_ERROR(mcRet);
529     } else {
530         // Just remove the buffer
531         // TODO-2012-09-06-haenellu: Haven't we removed it already?
532         if (!ts->removeBulkBuff(handle))
533             LOG_I("unmapBulk(): no buffer found found with handle=%u", handle);
534     }
535 
536     return MC_DRV_OK;
537 }
538 
539 
540 //------------------------------------------------------------------------------
donateRam(const uint32_t donationSize)541 void MobiCoreDevice::donateRam(const uint32_t donationSize)
542 {
543     // Donate additional RAM to the MobiCore
544     CWsm_ptr ram = allocateContiguousPersistentWsm(donationSize);
545     if (NULL == ram) {
546         LOG_E("Allocation of additional RAM failed");
547         return;
548     }
549     ramType_t ramType = RAM_GENERIC;
550     addr_t adrBuffer = ram->physAddr;
551     const uint32_t numPages = donationSize / (4 * 1024);
552 
553 
554     LOG_I("donateRam(): adrBuffer=%p, numPages=%d, ramType=%d",
555           adrBuffer,
556           numPages,
557           ramType);
558 
559     do {
560         // Write MCP open message to buffer
561         mcpMessage->cmdDonateRam.cmdHeader.cmdId = MC_MCP_CMD_DONATE_RAM;
562         mcpMessage->cmdDonateRam.adrBuffer = (uint32_t) adrBuffer;
563         mcpMessage->cmdDonateRam.numPages = numPages;
564         mcpMessage->cmdDonateRam.ramType = ramType;
565 
566         // Notify MC about a new command inside the MCP buffer
567         notify(SID_MCP);
568 
569         // Wait till response from MC is available
570         if (!waitMcpNotification()) {
571             break;
572         }
573 
574         // Check if the command response ID is correct
575         if ((MC_MCP_CMD_DONATE_RAM | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) {
576             LOG_E("donateRam(): CMD_DONATE_RAM got invalid MCP response - rspId is: %d",
577                   mcpMessage->rspHeader.rspId);
578             break;
579         }
580 
581         uint32_t mcRet = mcpMessage->rspDonateRam.rspHeader.result;
582         if (MC_MCP_RET_OK != mcRet) {
583             LOG_E("donateRam(): CMD_DONATE_RAM error %d", mcRet);
584             break;
585         }
586 
587         LOG_I("donateRam() succeeded.");
588 
589     } while (0);
590 }
591 
592 //------------------------------------------------------------------------------
getMobiCoreVersion(mcDrvRspGetMobiCoreVersionPayload_ptr pRspGetMobiCoreVersionPayload)593 mcResult_t MobiCoreDevice::getMobiCoreVersion(
594     mcDrvRspGetMobiCoreVersionPayload_ptr pRspGetMobiCoreVersionPayload
595 )
596 {
597     // If MobiCore version info already fetched.
598     if (mcVersionInfo != NULL) {
599         pRspGetMobiCoreVersionPayload->versionInfo = *mcVersionInfo;
600         return MC_DRV_OK;
601         // Otherwise, fetch it via MCP.
602     } else {
603         // Write MCP unmap command to buffer
604         mcpMessage->cmdGetMobiCoreVersion.cmdHeader.cmdId = MC_MCP_CMD_GET_MOBICORE_VERSION;
605 
606         // Notify MC about the availability of a new command inside the MCP buffer
607         notify(SID_MCP);
608 
609         // Wait till response from MC is available
610         if (!waitMcpNotification()) {
611             return MC_DRV_ERR_DAEMON_MCI_ERROR;
612         }
613 
614         // Check if the command response ID is correct
615         if ((MC_MCP_CMD_GET_MOBICORE_VERSION | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) {
616             LOG_E("MC_MCP_CMD_GET_MOBICORE_VERSION got invalid MCP response");
617             return MC_DRV_ERR_DAEMON_MCI_ERROR;
618         }
619 
620         uint32_t  mcRet = mcpMessage->rspGetMobiCoreVersion.rspHeader.result;
621 
622         if (mcRet != MC_MCP_RET_OK) {
623             LOG_E("MC_MCP_CMD_GET_MOBICORE_VERSION error %d", mcRet);
624             return MAKE_MC_DRV_MCP_ERROR(mcRet);
625         }
626 
627         pRspGetMobiCoreVersionPayload->versionInfo = mcpMessage->rspGetMobiCoreVersion.versionInfo;
628 
629         // Store MobiCore info for future reference.
630         mcVersionInfo = new mcVersionInfo_t();
631         *mcVersionInfo = pRspGetMobiCoreVersionPayload->versionInfo;
632         return MC_DRV_OK;
633     }
634 }
635 
636 //------------------------------------------------------------------------------
queueUnknownNotification(notification_t notification)637 void MobiCoreDevice::queueUnknownNotification(
638     notification_t notification
639 )
640 {
641     notifications.push(notification);
642 }
643 
644 /** @} */
645