VirtualBox

source: vbox/trunk/src/VBox/Devices/Security/DevTpmPpi.cpp

Last change on this file was 105041, checked in by vboxsync, 3 months ago

Devices/Security: Implement separate device for the TPM PPI interface used by the firmware and ACPI tables for certain operations requiring pyhsical user presence (Windows requires those), bugref:10701

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.4 KB
Line 
1/* $Id: DevTpmPpi.cpp 105041 2024-06-27 08:52:03Z vboxsync $ */
2/** @file
3 * DevTpmPpi - Guest platform <-> VirtualBox TPM Physical Presence Interface Integration Framework.
4 *
5 * Based on: https://github.com/qemu/qemu/blob/master/docs/specs/tpm.rst (2024-06-26).
6 */
7
8/*
9 * Copyright (C) 2024 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30
31/*********************************************************************************************************************************
32* Header Files *
33*********************************************************************************************************************************/
34#define LOG_GROUP LOG_GROUP_DEV_TPM
35
36#include <VBox/vmm/pdmdev.h>
37#include <VBox/log.h>
38#include <VBox/err.h>
39#include <VBox/param.h>
40
41#include <iprt/asm.h>
42#include <iprt/assert.h>
43#include <iprt/ctype.h>
44
45#include "VBoxDD.h"
46
47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
51
52/** The TPM saved state version. */
53#define TPM_PPI_SAVED_STATE_VERSION 1
54
55/** The TPM PPI MMIO base default (compatible with qemu). */
56#define TPM_PPI_MMIO_BASE_DEFAULT UINT64_C(0xfed45000)
57/** The size of the TPM PPI MMIO area. */
58#define TPM_PPI_MMIO_SIZE _4K
59
60/** @name QEMU compatible PPI interface layout.
61 * @{ */
62/**
63 * The PPI structure layout (yes, it really is misaligned).
64 */
65#pragma pack(1)
66typedef union QEMUTPMPPI
67{
68 /** The byte view. */
69 uint8_t ab[0x400];
70 /** The structured view. */
71 struct
72 {
73 /** Supported operation flags set by the firmware for each operation. */
74 uint8_t abFunc[0x100];
75 /** SMI interrupt to use, set by firmware. Not supported. */
76 uint8_t bPpin;
77 /** ACPI function index to pass to SMM code, set by ACPI. Not supported. */
78 uint32_t u32Ppip;
79 /** Result of last executed operation, set by firmware. */
80 uint32_t u32Pprp;
81 /** Operation request number to execute, set by ACPI. */
82 uint32_t u32Pprq;
83 /** Operation request optional parameter, set by ACPI. */
84 uint32_t u32Pprm;
85 /** Last executed operation request number, copied from QEMUTPMPPI::u32PPrq field by firmware. */
86 uint32_t u32Lppr;
87 /** Result code from SMM function, not supported. */
88 uint32_t u32Fret;
89 /** Reserved for future use. */
90 uint8_t abRsvd[0x40];
91 /** Operation to execute after reboot by firmware, used by firmware. */
92 uint8_t bNextStep;
93 /** Memory overwrite variable. */
94 uint8_t bMovv;
95 } s;
96} QEMUTPMPPI;
97#pragma pack()
98AssertCompileSize(QEMUTPMPPI, 0x400);
99/** Pointer to the QEMU PPI structure layout. */
100typedef QEMUTPMPPI *PQEMUTPMPPI;
101/** Pointer to a const QEMU PPI structure layout. */
102typedef const QEMUTPMPPI *PCQEMUTPMPPI;
103/** @} */
104
105
106/*********************************************************************************************************************************
107* Structures and Typedefs *
108*********************************************************************************************************************************/
109
110/**
111 * Shared TPM PPI device state.
112 */
113typedef struct DEVTPMPPI
114{
115 /** Base MMIO address of the TPM PPI area. */
116 RTGCPHYS GCPhysMmio;
117 /** The handle of the MMIO region. */
118 IOMMMIOHANDLE hMmio;
119 /** The QEMU PPI state. */
120 QEMUTPMPPI Ppi;
121} DEVTPMPPI;
122/** Pointer to the shared TPM PPI device state. */
123typedef DEVTPMPPI *PDEVTPMPPI;
124
125/**
126 * TPM PPI device state for ring-3.
127 */
128typedef struct DEVTPMPPIR3
129{
130 /** Pointer to the device instance. */
131 PPDMDEVINS pDevIns;
132} DEVTPMPPIR3;
133/** Pointer to the TPM device state for ring-3. */
134typedef DEVTPMPPIR3 *PDEVTPMPPIR3;
135
136
137/**
138 * TPM PPI device state for ring-0.
139 */
140typedef struct DEVTPMPPIR0
141{
142 uint32_t u32Dummy;
143
144} DEVTPMPPIR0;
145/** Pointer to the TPM device state for ring-0. */
146typedef DEVTPMPPIR0 *PDEVTPMPPIR0;
147
148
149/**
150 * TPM PPI device state for raw-mode.
151 */
152typedef struct DEVTPMPPIRC
153{
154 uint32_t u32Dummy;
155} DEVTPMPPIRC;
156/** Pointer to the TPM device state for raw-mode. */
157typedef DEVTPMPPIRC *PDEVTPMPPIRC;
158
159/** The TPM PI device state for the current context. */
160typedef CTX_SUFF(DEVTPMPPI) DEVTPMPPICC;
161/** Pointer to the TPM PPI device state for the current context. */
162typedef CTX_SUFF(PDEVTPMPPI) PDEVTPMPPICC;
163
164
165/*********************************************************************************************************************************
166* Defined Constants And Macros *
167*********************************************************************************************************************************/
168
169
170/*********************************************************************************************************************************
171* Global Variables *
172*********************************************************************************************************************************/
173
174#ifdef IN_RING3
175/**
176 * SSM descriptor table for the TPM PPI structure.
177 */
178static SSMFIELD const g_aTpmPpiFields[] =
179{
180 SSMFIELD_ENTRY(QEMUTPMPPI, s.abFunc),
181 SSMFIELD_ENTRY(QEMUTPMPPI, s.bPpin),
182 SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Ppip),
183 SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Pprp),
184 SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Pprq),
185 SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Pprm),
186 SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Lppr),
187 SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Fret),
188 SSMFIELD_ENTRY(QEMUTPMPPI, s.bNextStep),
189 SSMFIELD_ENTRY(QEMUTPMPPI, s.bMovv),
190 SSMFIELD_ENTRY_TERM()
191};
192#endif
193
194
195/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
196
197/**
198 * @callback_method_impl{FNIOMMMIONEWREAD}
199 */
200static DECLCALLBACK(VBOXSTRICTRC) tpmPpiMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
201{
202 PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
203 RT_NOREF(pvUser);
204
205 LogFlowFunc(("off=%RGp cb=%u\n", off, cb));
206 if (off + cb > sizeof(pThis->Ppi))
207 return VINF_IOM_MMIO_UNUSED_FF;
208
209 memcpy(pv, &pThis->Ppi.ab[off], cb);
210 return VINF_SUCCESS;
211}
212
213
214/**
215 * @callback_method_impl{FNIOMMMIONEWWRITE}
216 */
217static DECLCALLBACK(VBOXSTRICTRC) tpmPpiMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
218{
219 PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
220 RT_NOREF(pvUser);
221
222 LogFlowFunc(("off=%RGp cb=%u\n", off, cb));
223 if (off + cb > sizeof(pThis->Ppi))
224 return VINF_SUCCESS;
225
226 memcpy(&pThis->Ppi.ab[off], pv, cb);
227 return VINF_SUCCESS;
228}
229
230
231#ifdef IN_RING3
232
233/* -=-=-=-=-=-=-=-=- Saved State -=-=-=-=-=-=-=-=- */
234
235/**
236 * @callback_method_impl{FNSSMDEVLIVEEXEC}
237 */
238static DECLCALLBACK(int) tpmPpiR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
239{
240 PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
241 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
242 RT_NOREF(uPass);
243
244 /* Save the part of the config used for verification purposes when restoring. */
245 pHlp->pfnSSMPutGCPhys(pSSM, pThis->GCPhysMmio);
246
247 return VINF_SSM_DONT_CALL_AGAIN;
248}
249
250
251/**
252 * @callback_method_impl{FNSSMDEVSAVEEXEC}
253 */
254static DECLCALLBACK(int) tpmPpiR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
255{
256 PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
257 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
258
259 tpmPpiR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
260
261 int rc = pHlp->pfnSSMPutStructEx(pSSM, &pThis->Ppi, sizeof(pThis->Ppi), 0 /*fFlags*/, &g_aTpmPpiFields[0], NULL);
262 AssertRCReturn(rc, rc);
263
264 return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX); /* sanity/terminator */
265}
266
267
268/**
269 * @callback_method_impl{FNSSMDEVLOADEXEC}
270 */
271static DECLCALLBACK(int) tpmPpiR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
272{
273 PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
274 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
275 RTGCPHYS GCPhysMmio;
276
277 Assert(uPass == SSM_PASS_FINAL); RT_NOREF(uPass);
278 AssertMsgReturn(uVersion == TPM_PPI_SAVED_STATE_VERSION, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
279
280 /* Verify the config first. */
281 int rc = pHlp->pfnSSMGetGCPhys(pSSM, &GCPhysMmio);
282 AssertRCReturn(rc, rc);
283 if (GCPhysMmio != pThis->GCPhysMmio)
284 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
285 N_("Config mismatch - saved GCPhysMmio=%#RGp; configured GCPhysMmio=%#RGp"),
286 GCPhysMmio, pThis->GCPhysMmio);
287
288 if (uPass == SSM_PASS_FINAL)
289 {
290 rc = pHlp->pfnSSMGetStructEx(pSSM, &pThis->Ppi, sizeof(pThis->Ppi), 0 /*fFlags*/, &g_aTpmPpiFields[0], NULL);
291 AssertRCReturn(rc, rc);
292
293 /* The marker. */
294 uint32_t u32;
295 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
296 AssertRCReturn(rc, rc);
297 AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
298 }
299
300 return VINF_SUCCESS;
301}
302
303
304/**
305 * @interface_method_impl{PDMDEVREG,pfnDestruct}
306 */
307static DECLCALLBACK(int) tpmPpiR3Destruct(PPDMDEVINS pDevIns)
308{
309 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
310 return VINF_SUCCESS;
311}
312
313
314/**
315 * @interface_method_impl{PDMDEVREG,pfnConstruct}
316 */
317static DECLCALLBACK(int) tpmPpiR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
318{
319 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
320 PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
321 PDEVTPMPPICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVTPMPPICC);
322 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
323 int rc;
324
325 RT_NOREF(iInstance);
326 Assert(iInstance == 0);
327
328 /*
329 * Initalize the basic variables so that the destructor always works.
330 */
331 pThisCC->pDevIns = pDevIns;
332
333 /*
334 * Validate and read the configuration.
335 */
336 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MmioBase", "");
337
338 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "MmioBase", &pThis->GCPhysMmio, TPM_PPI_MMIO_BASE_DEFAULT);
339 if (RT_FAILURE(rc))
340 return PDMDEV_SET_ERROR(pDevIns, rc,
341 N_("Configuration error: Failed to get the \"MmioBase\" value"));
342
343 /*
344 * Register the MMIO range, PDM API requests page aligned
345 * addresses and sizes.
346 */
347 rc = PDMDevHlpMmioCreateAndMap(pDevIns, pThis->GCPhysMmio, TPM_PPI_MMIO_SIZE, tpmPpiMmioWrite, tpmPpiMmioRead,
348 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
349 "TPM PPI MMIO", &pThis->hMmio);
350 AssertRCReturn(rc, rc);
351
352 /*
353 * Saved state.
354 */
355 rc = PDMDevHlpSSMRegister3(pDevIns, TPM_PPI_SAVED_STATE_VERSION, sizeof(*pThis),
356 tpmPpiR3LiveExec, tpmPpiR3SaveExec, tpmPpiR3LoadExec);
357 AssertRCReturn(rc, rc);
358
359 return VINF_SUCCESS;
360}
361
362
363#else /* !IN_RING3 */
364
365/**
366 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
367 */
368static DECLCALLBACK(int) tpmPpiRZConstruct(PPDMDEVINS pDevIns)
369{
370 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
371 PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
372
373 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, tpmPpiMmioWrite, tpmPpiMmioRead, NULL /*pvUser*/);
374 AssertRCReturn(rc, rc);
375
376 return VINF_SUCCESS;
377}
378
379#endif /* !IN_RING3 */
380
381
382/**
383 * The device registration structure.
384 */
385const PDMDEVREG g_DeviceTpmPpi =
386{
387 /* .u32Version = */ PDM_DEVREG_VERSION,
388 /* .uReserved0 = */ 0,
389 /* .szName = */ "tpm-ppi",
390 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
391 /* .fClass = */ PDM_DEVREG_CLASS_ARCH_BIOS,
392 /* .cMaxInstances = */ 1,
393 /* .uSharedVersion = */ 42,
394 /* .cbInstanceShared = */ sizeof(DEVTPMPPI),
395 /* .cbInstanceCC = */ sizeof(DEVTPMPPICC),
396 /* .cbInstanceRC = */ sizeof(DEVTPMPPIRC),
397 /* .cMaxPciDevices = */ 0,
398 /* .cMaxMsixVectors = */ 0,
399 /* .pszDescription = */ "Device implementing the TPM Physical Presence Interface (PPI)\n",
400#if defined(IN_RING3)
401 /* .pszRCMod = */ "",
402 /* .pszR0Mod = */ "",
403 /* .pfnConstruct = */ tpmPpiR3Construct,
404 /* .pfnDestruct = */ tpmPpiR3Destruct,
405 /* .pfnRelocate = */ NULL,
406 /* .pfnMemSetup = */ NULL,
407 /* .pfnPowerOn = */ NULL,
408 /* .pfnReset = */ NULL,
409 /* .pfnSuspend = */ NULL,
410 /* .pfnResume = */ NULL,
411 /* .pfnAttach = */ NULL,
412 /* .pfnDetach = */ NULL,
413 /* .pfnQueryInterface = */ NULL,
414 /* .pfnInitComplete = */ NULL,
415 /* .pfnPowerOff = */ NULL,
416 /* .pfnSoftReset = */ NULL,
417 /* .pfnReserved0 = */ NULL,
418 /* .pfnReserved1 = */ NULL,
419 /* .pfnReserved2 = */ NULL,
420 /* .pfnReserved3 = */ NULL,
421 /* .pfnReserved4 = */ NULL,
422 /* .pfnReserved5 = */ NULL,
423 /* .pfnReserved6 = */ NULL,
424 /* .pfnReserved7 = */ NULL,
425#elif defined(IN_RING0)
426 /* .pfnEarlyConstruct = */ NULL,
427 /* .pfnConstruct = */ tpmPpiRZConstruct,
428 /* .pfnDestruct = */ NULL,
429 /* .pfnFinalDestruct = */ NULL,
430 /* .pfnRequest = */ NULL,
431 /* .pfnReserved0 = */ NULL,
432 /* .pfnReserved1 = */ NULL,
433 /* .pfnReserved2 = */ NULL,
434 /* .pfnReserved3 = */ NULL,
435 /* .pfnReserved4 = */ NULL,
436 /* .pfnReserved5 = */ NULL,
437 /* .pfnReserved6 = */ NULL,
438 /* .pfnReserved7 = */ NULL,
439#elif defined(IN_RC)
440 /* .pfnConstruct = */ tpmPpiRZConstruct,
441 /* .pfnReserved0 = */ NULL,
442 /* .pfnReserved1 = */ NULL,
443 /* .pfnReserved2 = */ NULL,
444 /* .pfnReserved3 = */ NULL,
445 /* .pfnReserved4 = */ NULL,
446 /* .pfnReserved5 = */ NULL,
447 /* .pfnReserved6 = */ NULL,
448 /* .pfnReserved7 = */ NULL,
449#else
450# error "Not in IN_RING3, IN_RING0 or IN_RC!"
451#endif
452 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
453};
Note: See TracBrowser for help on using the repository browser.

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