VirtualBox

source: vbox/trunk/src/VBox/Devices/Security/DrvTpmEmuTpms.cpp@ 91327

Last change on this file since 91327 was 91327, checked in by vboxsync, 3 years ago

Devices/Security/DrvTpmEmuTpms: Store the TPM state in the power off callback because the NVRAM store driver below is already destroyed when the destruction callback is called, bugref:10075

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.9 KB
Line 
1/* $Id: DrvTpmEmuTpms.cpp 91327 2021-09-22 15:15:08Z vboxsync $ */
2/** @file
3 * TPM emulation driver based on libtpms.
4 */
5
6/*
7 * Copyright (C) 2021 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_TPM_EMU
23#include <VBox/vmm/pdmdrv.h>
24#include <VBox/vmm/pdmtpmifs.h>
25#include <iprt/assert.h>
26#include <iprt/file.h>
27#include <iprt/mem.h>
28#include <iprt/string.h>
29#include <iprt/semaphore.h>
30#include <iprt/uuid.h>
31#include <iprt/vfs.h>
32#include <iprt/zip.h>
33
34#include <iprt/formats/tpm.h>
35
36#include <libtpms/tpm_library.h>
37#include <libtpms/tpm_error.h>
38#include <libtpms/tpm_tis.h>
39#include <libtpms/tpm_nvfilename.h>
40
41#include <unistd.h>
42#include <stdlib.h>
43
44#include "VBoxDD.h"
45
46
47/*********************************************************************************************************************************
48* Defined Constants And Macros *
49*********************************************************************************************************************************/
50
51/** The TPMS saved state version. */
52#define TPMS_SAVED_STATE_VERSION 1
53
54
55/*********************************************************************************************************************************
56* Structures and Typedefs *
57*********************************************************************************************************************************/
58
59/**
60 * TPM emulation driver instance data.
61 *
62 * @implements PDMITPMCONNECTOR
63 */
64typedef struct DRVTPMEMU
65{
66 /** The stream interface. */
67 PDMITPMCONNECTOR ITpmConnector;
68 /** Pointer to the driver instance. */
69 PPDMDRVINS pDrvIns;
70 /** The VFS interface of the driver below for NVRAM/TPM state loading and storing. */
71 PPDMIVFSCONNECTOR pDrvVfs;
72
73 /** The TPM version we are emulating. */
74 TPMVERSION enmVersion;
75 /** The buffer size the TPM advertises. */
76 uint32_t cbBuffer;
77 /** Currently set locality. */
78 uint8_t bLoc;
79 /** Flag whether the TPM state was saved in save state operation (skips writing the state to the NVRAM file). */
80 bool fSsmCalled;
81
82 /** NVRAM file path. */
83 char *pszNvramPath;
84
85 void *pvNvPermall;
86 size_t cbNvPermall;
87
88 void *pvNvVolatile;
89 size_t cbNvVolatile;
90
91} DRVTPMEMU;
92/** Pointer to the TPM emulator instance data. */
93typedef DRVTPMEMU *PDRVTPMEMU;
94
95/** The special no current locality selected value. */
96#define TPM_NO_LOCALITY_SELECTED 0xff
97
98
99/*********************************************************************************************************************************
100* Global Variables *
101*********************************************************************************************************************************/
102/** Pointer to the (only) instance data in this driver. */
103static PDRVTPMEMU g_pDrvTpmEmuTpms = NULL;
104
105
106/*********************************************************************************************************************************
107* Internal Functions *
108*********************************************************************************************************************************/
109
110/**
111 * Tries to load the NVRAM.
112 *
113 * @returns VBox status code.
114 * @param pThis The emulator driver instance data.
115 */
116static int drvTpmEmuTpmsNvramLoad(PDRVTPMEMU pThis)
117{
118 RTVFSIOSTREAM hVfsIos;
119 uint32_t offError = 0;
120 RTERRINFOSTATIC ErrInfo;
121 int rc = RTVfsChainOpenIoStream(pThis->pszNvramPath, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
122 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
123 if (RT_FAILURE(rc))
124 {
125 if (rc == VERR_FILE_NOT_FOUND) /* First run. */
126 rc = VINF_SUCCESS;
127
128 return rc;
129 }
130
131 RTVFSFSSTREAM hVfsFss = NIL_RTVFSFSSTREAM;
132 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, &hVfsFss);
133 RTVfsIoStrmRelease(hVfsIos);
134 if (RT_SUCCESS(rc))
135 {
136 /*
137 * Process the stream.
138 */
139 for (;;)
140 {
141 /*
142 * Retrieve the next object.
143 */
144 char *pszName;
145 RTVFSOBJ hVfsObj;
146 rc = RTVfsFsStrmNext(hVfsFss, &pszName, NULL, &hVfsObj);
147 if (RT_FAILURE(rc))
148 {
149 if (rc == VERR_EOF)
150 rc = VINF_SUCCESS;
151 break;
152 }
153
154 RTFSOBJINFO UnixInfo;
155 rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
156 if (RT_SUCCESS(rc))
157 {
158 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
159 {
160 case RTFS_TYPE_FILE:
161 {
162 void **ppvDataPtr = NULL;
163 size_t *pcbData = NULL;
164 if (!RTStrCmp(pszName, TPM_PERMANENT_ALL_NAME))
165 {
166 ppvDataPtr = &pThis->pvNvPermall;
167 pcbData = &pThis->cbNvPermall;
168 }
169 else if (!RTStrCmp(pszName, TPM_VOLATILESTATE_NAME))
170 {
171 ppvDataPtr = &pThis->pvNvVolatile;
172 pcbData = &pThis->cbNvVolatile;
173 }
174 else
175 rc = VERR_NOT_FOUND;
176
177 if (RT_SUCCESS(rc))
178 {
179 *ppvDataPtr = RTMemAllocZ(UnixInfo.cbObject);
180 if (*ppvDataPtr)
181 {
182 RTVFSIOSTREAM hVfsIosData = RTVfsObjToIoStream(hVfsObj);
183
184 rc = RTVfsIoStrmRead(hVfsIosData, *ppvDataPtr, UnixInfo.cbObject, true /*fBlocking*/, NULL);
185 *pcbData = UnixInfo.cbObject;
186 RTVfsIoStrmRelease(hVfsIosData);
187 }
188 else
189 rc = VERR_NO_MEMORY;
190 }
191 break;
192 }
193 default:
194 rc = VERR_NOT_SUPPORTED;
195 break;
196 }
197 }
198
199 /*
200 * Release the current object and string.
201 */
202 RTVfsObjRelease(hVfsObj);
203 RTStrFree(pszName);
204
205 if (RT_FAILURE(rc))
206 break;
207 }
208 }
209
210 return rc;
211}
212
213
214static int drvTpmEmuTpmsNvramStoreEntity(RTVFSFSSTREAM hVfsFss, const char *pszName, const void *pvData, size_t cbData)
215{
216 RTVFSIOSTREAM hVfsIosData;
217
218 int rc = RTVfsIoStrmFromBuffer(RTFILE_O_READ, pvData, cbData, &hVfsIosData);
219 if (RT_SUCCESS(rc))
220 {
221 RTVFSOBJ hVfsObj = RTVfsObjFromIoStream(hVfsIosData);
222 rc = RTVfsFsStrmAdd(hVfsFss, pszName, hVfsObj, 0 /*fFlags*/);
223 RTVfsObjRelease(hVfsObj);
224
225 RTVfsIoStrmRelease(hVfsIosData);
226 }
227
228 return rc;
229}
230
231
232/**
233 * Stores the NVRAM content.
234 *
235 * @returns VBox status code.
236 * @param pThis The emulator driver instance data.
237 */
238static int drvTpmEmuTpmsNvramStore(PDRVTPMEMU pThis)
239{
240 uint32_t offError = 0;
241 RTERRINFOSTATIC ErrInfo;
242 RTVFSIOSTREAM hVfsIos;
243
244 int rc = RTVfsChainOpenIoStream(pThis->pszNvramPath, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
245 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
246 if (RT_SUCCESS(rc))
247 {
248 RTVFSFSSTREAM hVfsFss;
249 rc = RTZipTarFsStreamToIoStream(hVfsIos, RTZIPTARFORMAT_GNU, 0 /*fFlags*/, &hVfsFss);
250 if (RT_SUCCESS(rc))
251 {
252 rc = drvTpmEmuTpmsNvramStoreEntity(hVfsFss, TPM_PERMANENT_ALL_NAME, pThis->pvNvPermall, pThis->cbNvPermall);
253 if (RT_SUCCESS(rc) && pThis->pvNvVolatile)
254 rc = drvTpmEmuTpmsNvramStoreEntity(hVfsFss, TPM_VOLATILESTATE_NAME, pThis->pvNvVolatile, pThis->cbNvVolatile);
255
256 RTVfsFsStrmRelease(hVfsFss);
257 }
258
259 RTVfsIoStrmRelease(hVfsIos);
260 }
261
262 return rc;
263}
264
265
266/**
267 * Tries to load the NVRAM from the VFS driver below.
268 *
269 * @returns VBox status code.
270 * @param pThis The emulator driver instance data.
271 */
272static int drvTpmEmuTpmsNvramLoadFromVfs(PDRVTPMEMU pThis)
273{
274 uint64_t cbState = 0;
275 int rc = pThis->pDrvVfs->pfnQuerySize(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, TPM_PERMANENT_ALL_NAME, &cbState);
276 if (RT_SUCCESS(rc))
277 {
278 pThis->pvNvPermall = RTMemAllocZ(cbState);
279 if (pThis->pvNvPermall)
280 {
281 pThis->cbNvPermall = (size_t)cbState;
282 rc = pThis->pDrvVfs->pfnReadAll(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, TPM_PERMANENT_ALL_NAME,
283 pThis->pvNvPermall, pThis->cbNvPermall);
284 if (RT_SUCCESS(rc))
285 {
286 /* Load the volatile state if existing. */
287 rc = pThis->pDrvVfs->pfnQuerySize(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, TPM_VOLATILESTATE_NAME, &cbState);
288 if (RT_SUCCESS(rc))
289 {
290 pThis->pvNvVolatile = RTMemAllocZ(cbState);
291 if (pThis->pvNvVolatile)
292 {
293 pThis->cbNvVolatile = (size_t)cbState;
294 rc = pThis->pDrvVfs->pfnReadAll(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, TPM_VOLATILESTATE_NAME,
295 pThis->pvNvVolatile, pThis->cbNvVolatile);
296 }
297 }
298 else if (rc == VERR_NOT_FOUND)
299 rc = VINF_SUCCESS; /* This is fine if there is no volatile state. */
300 }
301 }
302 else
303 rc = VERR_NO_MEMORY;
304 }
305 else if (rc == VERR_NOT_FOUND)
306 rc = VINF_SUCCESS; /* This is fine for the first start of a new VM. */
307
308 return rc;
309}
310
311
312/**
313 * Stores the NVRAM content using the VFS driver below.
314 *
315 * @returns VBox status code.
316 * @param pThis The emulator driver instance data.
317 */
318static int drvTpmEmuTpmsNvramStoreToVfs(PDRVTPMEMU pThis)
319{
320 AssertPtr(pThis->pvNvPermall);
321 int rc = pThis->pDrvVfs->pfnWriteAll(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, TPM_PERMANENT_ALL_NAME,
322 pThis->pvNvPermall, pThis->cbNvPermall);
323 if ( RT_SUCCESS(rc)
324 && pThis->pvNvVolatile)
325 rc = pThis->pDrvVfs->pfnWriteAll(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, TPM_VOLATILESTATE_NAME,
326 pThis->pvNvVolatile, pThis->cbNvVolatile);
327
328 return rc;
329}
330
331
332/* -=-=-=-=- PDMITPMCONNECTOR interface callabcks. -=-=-=-=- */
333
334
335/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetVersion} */
336static DECLCALLBACK(TPMVERSION) drvTpmEmuTpmsGetVersion(PPDMITPMCONNECTOR pInterface)
337{
338 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
339 return pThis->enmVersion;
340}
341
342
343/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetLocalityMax} */
344static DECLCALLBACK(uint32_t) drvTpmEmuGetLocalityMax(PPDMITPMCONNECTOR pInterface)
345{
346 RT_NOREF(pInterface);
347 return 4;
348}
349
350
351/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetBufferSize} */
352static DECLCALLBACK(uint32_t) drvTpmEmuGetBufferSize(PPDMITPMCONNECTOR pInterface)
353{
354 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
355 return pThis->cbBuffer;
356}
357
358
359/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetEstablishedFlag} */
360static DECLCALLBACK(bool) drvTpmEmuTpmsGetEstablishedFlag(PPDMITPMCONNECTOR pInterface)
361{
362 RT_NOREF(pInterface);
363
364 TPM_BOOL fTpmEst = FALSE;
365 TPM_RESULT rcTpm = TPM_IO_TpmEstablished_Get(&fTpmEst);
366 if (RT_LIKELY(rcTpm == TPM_SUCCESS))
367 return RT_BOOL(fTpmEst);
368
369 return false;
370}
371
372
373/** @interface_method_impl{PDMITPMCONNECTOR,pfnResetEstablishedFlag} */
374static DECLCALLBACK(int) drvTpmEmuTpmsResetEstablishedFlag(PPDMITPMCONNECTOR pInterface, uint8_t bLoc)
375{
376 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
377 uint8_t bLocOld = pThis->bLoc;
378
379 pThis->bLoc = bLoc;
380 TPM_RESULT rcTpm = TPM_IO_TpmEstablished_Reset();
381 pThis->bLoc = bLocOld;
382
383 if (RT_LIKELY(rcTpm == TPM_SUCCESS))
384 return VINF_SUCCESS;
385
386 LogRelMax(10, ("DrvTpmEmuTpms#%u: Failed to reset the established flag with %#x\n",
387 pThis->pDrvIns->iInstance, rcTpm));
388 return VERR_DEV_IO_ERROR;
389}
390
391
392/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdExec} */
393static DECLCALLBACK(int) drvTpmEmuTpmsCmdExec(PPDMITPMCONNECTOR pInterface, uint8_t bLoc, const void *pvCmd, size_t cbCmd, void *pvResp, size_t cbResp)
394{
395 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
396
397 pThis->bLoc = bLoc;
398
399 uint8_t *pbRespBuf = NULL;
400 uint32_t cbRespBuf = 0;
401 uint32_t cbRespActual = 0;
402 TPM_RESULT rcTpm = TPMLIB_Process(&pbRespBuf, &cbRespActual, &cbRespBuf, (uint8_t *)pvCmd, cbCmd);
403 if (RT_LIKELY(rcTpm == TPM_SUCCESS))
404 {
405 memcpy(pvResp, pbRespBuf, RT_MIN(cbResp, cbRespActual));
406 free(pbRespBuf);
407 return VINF_SUCCESS;
408 }
409
410 LogRelMax(10, ("DrvTpmEmuTpms#%u: Failed to execute command with %#x\n",
411 pThis->pDrvIns->iInstance, rcTpm));
412 return VERR_DEV_IO_ERROR;
413}
414
415
416/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdCancel} */
417static DECLCALLBACK(int) drvTpmEmuTpmsCmdCancel(PPDMITPMCONNECTOR pInterface)
418{
419 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
420
421 TPM_RESULT rcTpm = TPMLIB_CancelCommand();
422 if (RT_LIKELY(rcTpm == TPM_SUCCESS))
423 return VINF_SUCCESS;
424
425 LogRelMax(10, ("DrvTpmEmuTpms#%u: Failed to cancel outstanding command with %#x\n",
426 pThis->pDrvIns->iInstance, rcTpm));
427 return VERR_DEV_IO_ERROR;
428}
429
430
431/** @interface_method_impl{PDMIBASE,pfnQueryInterface} */
432static DECLCALLBACK(void *) drvTpmEmuTpmsQueryInterface(PPDMIBASE pInterface, const char *pszIID)
433{
434 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
435 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
436 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
437 PDMIBASE_RETURN_INTERFACE(pszIID, PDMITPMCONNECTOR, &pThis->ITpmConnector);
438 return NULL;
439}
440
441
442/* -=-=-=-=- libtpms_callbacks -=-=-=-=- */
443
444
445static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamInit(void)
446{
447 PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
448 RT_NOREF(pThis);
449
450 return TPM_SUCCESS;
451}
452
453
454static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamLoadData(uint8_t **ppvData, uint32_t *pcbLength,
455 uint32_t idTpm, const char *pszName)
456{
457 PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
458
459 AssertReturn(idTpm == 0, TPM_FAIL);
460
461 void *pvDataPtr = NULL;
462 size_t cbData = 0;
463 if (!RTStrCmp(pszName, TPM_PERMANENT_ALL_NAME))
464 {
465 pvDataPtr = pThis->pvNvPermall;
466 cbData = pThis->cbNvPermall;
467 }
468 else if (!RTStrCmp(pszName, TPM_VOLATILESTATE_NAME))
469 {
470 pvDataPtr = pThis->pvNvVolatile;
471 cbData = pThis->cbNvVolatile;
472 }
473 else
474 return TPM_FAIL;
475
476 if (pvDataPtr)
477 {
478 *ppvData = (uint8_t *)malloc(cbData);
479 if (*ppvData)
480 {
481 memcpy(*ppvData, pvDataPtr, cbData);
482 *pcbLength = (uint32_t)cbData;
483 return TPM_SUCCESS;
484 }
485
486 return TPM_FAIL;
487 }
488
489 return TPM_RETRY;
490}
491
492
493static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamStoreData(const uint8_t *pvData, uint32_t cbLength,
494 uint32_t idTpm, const char *pszName)
495{
496 PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
497
498 AssertReturn(idTpm == 0, TPM_FAIL);
499
500 void **ppvDataPtr = NULL;
501 size_t *pcbData = NULL;
502 if (!RTStrCmp(pszName, TPM_PERMANENT_ALL_NAME))
503 {
504 ppvDataPtr = &pThis->pvNvPermall;
505 pcbData = &pThis->cbNvPermall;
506 }
507 else if (!RTStrCmp(pszName, TPM_VOLATILESTATE_NAME))
508 {
509 ppvDataPtr = &pThis->pvNvVolatile;
510 pcbData = &pThis->cbNvVolatile;
511 }
512 else
513 return TPM_FAIL;
514
515 if ( *ppvDataPtr
516 && *pcbData == cbLength)
517 {
518 memcpy(*ppvDataPtr, pvData, cbLength);
519 return TPM_SUCCESS;
520 }
521 else
522 {
523 if (*ppvDataPtr)
524 RTMemFree(*ppvDataPtr);
525
526 *ppvDataPtr = RTMemDup(pvData, cbLength);
527 if (*ppvDataPtr)
528 {
529 *pcbData = cbLength;
530 return TPM_SUCCESS;
531 }
532 }
533
534 return TPM_FAIL;
535}
536
537
538static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamDeleteName(uint32_t idTpm, const char *pszName, TPM_BOOL fMustExist)
539{
540 PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
541
542 AssertReturn(idTpm == 0, TPM_FAIL);
543
544 void **ppvDataPtr = NULL;
545 size_t *pcbData = NULL;
546 if (!RTStrCmp(pszName, TPM_PERMANENT_ALL_NAME))
547 {
548 ppvDataPtr = &pThis->pvNvPermall;
549 pcbData = &pThis->cbNvPermall;
550 }
551 else if (!RTStrCmp(pszName, TPM_VOLATILESTATE_NAME))
552 {
553 ppvDataPtr = &pThis->pvNvVolatile;
554 pcbData = &pThis->cbNvVolatile;
555 }
556 else
557 return TPM_SUCCESS;
558
559 if (*ppvDataPtr)
560 {
561 RTMemFree(*ppvDataPtr);
562 *ppvDataPtr = NULL;
563 *pcbData = 0;
564 }
565 else if (fMustExist)
566 return TPM_FAIL;
567
568 return TPM_SUCCESS;
569}
570
571
572static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkIoInit(void)
573{
574 return TPM_SUCCESS;
575}
576
577
578static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkIoGetLocality(TPM_MODIFIER_INDICATOR *pLocalityModifier, uint32_t idTpm)
579{
580 PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
581
582 AssertReturn(idTpm == 0, TPM_FAIL);
583
584 *pLocalityModifier = pThis->bLoc;
585 return TPM_SUCCESS;
586}
587
588
589static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkIoGetPhysicalPresence(TPM_BOOL *pfPhysicalPresence, uint32_t idTpm)
590{
591 AssertReturn(idTpm == 0, TPM_FAIL);
592
593 *pfPhysicalPresence = TRUE;
594 return TPM_SUCCESS;
595}
596
597
598/* -=-=-=-=-=-=-=-=- Saved State -=-=-=-=-=-=-=-=- */
599
600/**
601 * @callback_method_impl{FNSSMDEVSAVEEXEC}
602 */
603static DECLCALLBACK(int) drvTpmEmuTpmsSaveExec(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
604{
605 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
606 uint8_t *pbTpmStatePerm = NULL;
607 uint32_t cbTpmStatePerm = 0;
608 uint8_t *pbTpmStateVol = NULL;
609 uint32_t cbTpmStateVol = 0;
610
611 TPM_RESULT rcTpm = TPMLIB_GetState(TPMLIB_STATE_PERMANENT, &pbTpmStatePerm, &cbTpmStatePerm);
612 if (rcTpm == TPM_SUCCESS)
613 rcTpm = TPMLIB_GetState(TPMLIB_STATE_VOLATILE, &pbTpmStateVol, &cbTpmStateVol);
614 if (rcTpm == TPM_SUCCESS)
615 {
616 SSMR3PutU32(pSSM, cbTpmStatePerm);
617 int rc = SSMR3PutU32(pSSM, cbTpmStateVol);
618 AssertRCReturn(rc, rc);
619
620 SSMR3PutMem(pSSM, pbTpmStatePerm, cbTpmStatePerm);
621 SSMR3PutMem(pSSM, pbTpmStateVol, cbTpmStateVol);
622
623 free(pbTpmStatePerm);
624 free(pbTpmStateVol);
625 rc = SSMR3PutU32(pSSM, UINT32_MAX); /* sanity/terminator */
626 if (RT_SUCCESS(rc))
627 pThis->fSsmCalled = true;
628 return rc;
629 }
630
631 if (pbTpmStatePerm)
632 free(pbTpmStatePerm);
633
634 return VERR_NO_MEMORY;
635}
636
637
638/**
639 * @callback_method_impl{FNSSMDEVLOADEXEC}
640 */
641static DECLCALLBACK(int) drvTpmEmuTpmsLoadExec(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
642{
643 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
644
645 Assert(uPass == SSM_PASS_FINAL); RT_NOREF(uPass);
646 AssertMsgReturn(uVersion == TPMS_SAVED_STATE_VERSION, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
647
648 uint8_t *pbTpmStatePerm = NULL;
649 uint32_t cbTpmStatePerm = 0;
650 uint8_t *pbTpmStateVol = NULL;
651 uint32_t cbTpmStateVol = 0;
652
653 int rc = SSMR3GetU32(pSSM, &cbTpmStatePerm);
654 AssertRCReturn(rc, rc);
655
656 rc = SSMR3GetU32(pSSM, &cbTpmStateVol);
657 AssertRCReturn(rc, rc);
658
659 pbTpmStatePerm = (uint8_t *)RTMemAllocZ(cbTpmStatePerm);
660 if (pbTpmStatePerm)
661 {
662 pbTpmStateVol = (uint8_t *)RTMemAllocZ(cbTpmStateVol);
663 if (pbTpmStateVol)
664 {
665 rc = SSMR3GetMem(pSSM, pbTpmStatePerm, cbTpmStatePerm);
666 if (RT_SUCCESS(rc))
667 rc = SSMR3GetMem(pSSM, pbTpmStateVol, cbTpmStateVol);
668
669 if (RT_SUCCESS(rc))
670 {
671 TPM_RESULT rcTpm = TPMLIB_SetState(TPMLIB_STATE_PERMANENT, pbTpmStatePerm, cbTpmStatePerm);
672 if (rcTpm == TPM_SUCCESS)
673 {
674 rcTpm = TPMLIB_SetState(TPMLIB_STATE_VOLATILE, pbTpmStateVol, cbTpmStateVol);
675 if (rcTpm == TPM_SUCCESS)
676 {
677 uint32_t u32 = 0;
678
679 /* The marker. */
680 rc = SSMR3GetU32(pSSM, &u32);
681 AssertRCReturn(rc, rc);
682 AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
683
684 pThis->fSsmCalled = true;
685 }
686 else
687 rc = VERR_INVALID_PARAMETER;
688 }
689 else
690 rc = VERR_INVALID_PARAMETER;
691 }
692
693 RTMemFree(pbTpmStateVol);
694 }
695
696 RTMemFree(pbTpmStatePerm);
697 }
698 else
699 rc = VERR_NO_MEMORY;
700
701 return rc;
702}
703
704
705/**
706 * @callback_method_impl{FNSSMDEVLOADDONE}
707 */
708static DECLCALLBACK(int) drvTpmEmuTpmsLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
709{
710 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
711 RT_NOREF(pSSM);
712
713 if (!pThis->fSsmCalled)
714 {
715 /* Issue a warning as restoring a saved state without loading the TPM state will most likely cause issues in the guest. */
716 }
717
718 pThis->fSsmCalled = false;
719 return VINF_SUCCESS;
720}
721
722
723/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
724
725/**
726 * @interface_method_impl{PDMDRVREG,pfnPowerOn}
727 */
728static DECLCALLBACK(void) drvTpmEmuTpmsPowerOn(PPDMDRVINS pDrvIns)
729{
730 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
731
732 TPM_RESULT rcTpm = TPMLIB_MainInit();
733 if (RT_UNLIKELY(rcTpm != TPM_SUCCESS))
734 {
735 LogRel(("DrvTpmEmuTpms#%u: Failed to initialize TPM emulation with %#x\n",
736 pDrvIns->iInstance, rcTpm));
737 PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS, "Failed to startup the TPM with %u", rcTpm);
738 }
739}
740
741
742/**
743 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
744 */
745static DECLCALLBACK(void) drvTpmEmuTpmsPowerOff(PPDMDRVINS pDrvIns)
746{
747 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
748
749 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
750
751 TPMLIB_Terminate();
752
753 int rc;
754 if (pThis->pDrvVfs)
755 rc = drvTpmEmuTpmsNvramStoreToVfs(pThis);
756 else
757 rc = drvTpmEmuTpmsNvramStore(pThis);
758 AssertRC(rc);
759}
760
761
762/** @copydoc FNPDMDRVDESTRUCT */
763static DECLCALLBACK(void) drvTpmEmuTpmsDestruct(PPDMDRVINS pDrvIns)
764{
765 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
766
767 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
768 LogFlow(("%s\n", __FUNCTION__));
769
770 if (pThis->pvNvPermall)
771 {
772 RTMemFree(pThis->pvNvPermall);
773 pThis->pvNvPermall = NULL;
774 }
775
776 if (pThis->pvNvVolatile)
777 {
778 RTMemFree(pThis->pvNvVolatile);
779 pThis->pvNvVolatile = NULL;
780 }
781
782#if 0
783 if (pThis->pszNvramPath)
784 {
785 PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszNvramPath);
786 pThisCC->pszNvramPath = NULL;
787 }
788#endif
789}
790
791
792/** @copydoc FNPDMDRVCONSTRUCT */
793static DECLCALLBACK(int) drvTpmEmuTpmsConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
794{
795 RT_NOREF(fFlags);
796 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
797 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
798
799 /*
800 * Init the static parts.
801 */
802 pThis->pDrvIns = pDrvIns;
803 pThis->enmVersion = TPMVERSION_UNKNOWN;
804 pThis->bLoc = TPM_NO_LOCALITY_SELECTED;
805 pThis->fSsmCalled = false;
806 pThis->pvNvPermall = NULL;
807 pThis->cbNvPermall = 0;
808 pThis->pvNvVolatile = NULL;
809 pThis->cbNvVolatile = 0;
810
811 /* IBase */
812 pDrvIns->IBase.pfnQueryInterface = drvTpmEmuTpmsQueryInterface;
813 /* ITpmConnector */
814 pThis->ITpmConnector.pfnGetVersion = drvTpmEmuTpmsGetVersion;
815 pThis->ITpmConnector.pfnGetLocalityMax = drvTpmEmuGetLocalityMax;
816 pThis->ITpmConnector.pfnGetBufferSize = drvTpmEmuGetBufferSize;
817 pThis->ITpmConnector.pfnGetEstablishedFlag = drvTpmEmuTpmsGetEstablishedFlag;
818 pThis->ITpmConnector.pfnResetEstablishedFlag = drvTpmEmuTpmsResetEstablishedFlag;
819 pThis->ITpmConnector.pfnCmdExec = drvTpmEmuTpmsCmdExec;
820 pThis->ITpmConnector.pfnCmdCancel = drvTpmEmuTpmsCmdCancel;
821
822 /*
823 * Validate and read the configuration.
824 */
825 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "TpmVersion|BufferSize|NvramPath", "");
826
827 TPMLIB_SetDebugFD(STDERR_FILENO);
828 TPMLIB_SetDebugLevel(~0);
829
830 /*
831 * Try attach the VFS driver below and query it's VFS interface.
832 */
833 PPDMIBASE pBase = NULL;
834 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
835 if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
836 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
837 N_("Failed to attach driver below us! %Rrc"), rc);
838 if (pBase)
839 {
840 pThis->pDrvVfs = PDMIBASE_QUERY_INTERFACE(pBase, PDMIVFSCONNECTOR);
841 if (!pThis->pDrvVfs)
842 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
843 N_("No VFS interface below"));
844
845 rc = drvTpmEmuTpmsNvramLoadFromVfs(pThis);
846 if (RT_FAILURE(rc))
847 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
848 N_("Failed to load TPM NVRAM data with %Rrc"), rc);
849 }
850 else
851 {
852 rc = CFGMR3QueryStringAlloc(pCfg, "NvramPath", &pThis->pszNvramPath);
853 if (RT_FAILURE(rc))
854 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
855 N_("Configuration error: querying \"NvramPath\" resulted in %Rrc"), rc);
856
857 rc = drvTpmEmuTpmsNvramLoad(pThis);
858 if (RT_FAILURE(rc))
859 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
860 N_("Failed to load TPM NVRAM data with %Rrc"), rc);
861 }
862
863 TPMLIB_TPMVersion enmVersion = TPMLIB_TPM_VERSION_2;
864 uint32_t uTpmVersion = 0;
865 rc = CFGMR3QueryU32Def(pCfg, "TpmVersion", &uTpmVersion, 2);
866 if (RT_FAILURE(rc))
867 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
868 N_("Configuration error: querying \"TpmVersion\" resulted in %Rrc"), rc);
869
870 switch (uTpmVersion)
871 {
872 case 1:
873 enmVersion = TPMLIB_TPM_VERSION_1_2;
874 pThis->enmVersion = TPMVERSION_1_2;
875 break;
876 case 2:
877 enmVersion = TPMLIB_TPM_VERSION_2;
878 pThis->enmVersion = TPMVERSION_2_0;
879 break;
880 default:
881 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
882 N_("Configuration error: \"TpmVersion\" %u is not supported"), uTpmVersion);
883 }
884
885 TPM_RESULT rcTpm = TPMLIB_ChooseTPMVersion(enmVersion);
886 if (rcTpm != TPM_SUCCESS)
887 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
888 N_("Failed to set the TPM version for the emulated TPM with %d"), rcTpm);
889
890 int cbBufferMax = 0;
891 rcTpm = TPMLIB_GetTPMProperty(TPMPROP_TPM_BUFFER_MAX, &cbBufferMax);
892 if (rcTpm != TPM_SUCCESS)
893 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
894 N_("Querying the maximum supported buffer size failed with %u"), rcTpm);
895
896 rc = CFGMR3QueryU32Def(pCfg, "BufferSize", &pThis->cbBuffer, (uint32_t)cbBufferMax);
897 if (RT_FAILURE(rc))
898 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
899 N_("Configuration error: querying \"BufferSize\" resulted in %Rrc"), rc);
900
901 uint32_t cbBufferMin = 0;
902 uint32_t cbBuffer = TPMLIB_SetBufferSize(pThis->cbBuffer, &cbBufferMin, NULL /*max_size*/);
903 if (pThis->cbBuffer != cbBuffer)
904 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
905 N_("Failed to set buffer size (%u) of the emulated TPM with %u (min %u, max %d)"),
906 pThis->cbBuffer, cbBuffer, cbBufferMin, cbBufferMax);
907
908 struct libtpms_callbacks Callbacks;
909 Callbacks.sizeOfStruct = sizeof(Callbacks);
910 Callbacks.tpm_nvram_init = drvTpmEmuTpmsCbkNvRamInit;
911 Callbacks.tpm_nvram_loaddata = drvTpmEmuTpmsCbkNvRamLoadData;
912 Callbacks.tpm_nvram_storedata = drvTpmEmuTpmsCbkNvRamStoreData;
913 Callbacks.tpm_nvram_deletename = drvTpmEmuTpmsCbkNvRamDeleteName;
914 Callbacks.tpm_io_init = drvTpmEmuTpmsCbkIoInit;
915 Callbacks.tpm_io_getlocality = drvTpmEmuTpmsCbkIoGetLocality;
916 Callbacks.tpm_io_getphysicalpresence = drvTpmEmuTpmsCbkIoGetPhysicalPresence;
917 rcTpm = TPMLIB_RegisterCallbacks(&Callbacks);
918 if (rcTpm != TPM_SUCCESS)
919 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
920 N_("Failed to register callbacks with the TPM emulation: %u"),
921 rcTpm);
922
923 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, TPMS_SAVED_STATE_VERSION, 0 /*cbGuess*/,
924 NULL /*pfnLivePrep*/, NULL /*pfnLiveExec*/, NULL /*pfnLiveVote*/,
925 NULL /*pfnSavePrep*/, drvTpmEmuTpmsSaveExec, NULL /*pfnSaveDone*/,
926 NULL /*pfnLoadPrep*/, drvTpmEmuTpmsLoadExec, drvTpmEmuTpmsLoadDone);
927 if (RT_FAILURE(rc))
928 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
929 N_("Failed to register saved state handlers"));
930
931 /* We can only have one instance of the TPM emulation and require the global variable for the callbacks unfortunately. */
932 g_pDrvTpmEmuTpms = pThis;
933 return VINF_SUCCESS;
934}
935
936
937/**
938 * TPM libtpms emulator driver registration record.
939 */
940const PDMDRVREG g_DrvTpmEmuTpms =
941{
942 /* u32Version */
943 PDM_DRVREG_VERSION,
944 /* szName */
945 "TpmEmuTpms",
946 /* szRCMod */
947 "",
948 /* szR0Mod */
949 "",
950 /* pszDescription */
951 "TPM emulation driver based on libtpms.",
952 /* fFlags */
953 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
954 /* fClass. */
955 PDM_DRVREG_CLASS_STREAM,
956 /* cMaxInstances */
957 1,
958 /* cbInstance */
959 sizeof(DRVTPMEMU),
960 /* pfnConstruct */
961 drvTpmEmuTpmsConstruct,
962 /* pfnDestruct */
963 drvTpmEmuTpmsDestruct,
964 /* pfnRelocate */
965 NULL,
966 /* pfnIOCtl */
967 NULL,
968 /* pfnPowerOn */
969 drvTpmEmuTpmsPowerOn,
970 /* pfnReset */
971 NULL,
972 /* pfnSuspend */
973 NULL,
974 /* pfnResume */
975 NULL,
976 /* pfnAttach */
977 NULL,
978 /* pfnDetach */
979 NULL,
980 /* pfnPowerOff */
981 drvTpmEmuTpmsPowerOff,
982 /* pfnSoftReset */
983 NULL,
984 /* u32EndVersion */
985 PDM_DRVREG_VERSION
986};
987
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette