summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Security
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Security')
-rw-r--r--src/VBox/Devices/Security/DevTpmPpi.cpp453
1 files changed, 453 insertions, 0 deletions
diff --git a/src/VBox/Devices/Security/DevTpmPpi.cpp b/src/VBox/Devices/Security/DevTpmPpi.cpp
new file mode 100644
index 00000000..03159d88
--- /dev/null
+++ b/src/VBox/Devices/Security/DevTpmPpi.cpp
@@ -0,0 +1,453 @@
+/* $Id: DevTpmPpi.cpp $ */
+/** @file
+ * DevTpmPpi - Guest platform <-> VirtualBox TPM Physical Presence Interface Integration Framework.
+ *
+ * Based on: https://github.com/qemu/qemu/blob/master/docs/specs/tpm.rst (2024-06-26).
+ */
+
+/*
+ * Copyright (C) 2024 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_TPM
+
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/param.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** The TPM saved state version. */
+#define TPM_PPI_SAVED_STATE_VERSION 1
+
+/** The TPM PPI MMIO base default (compatible with qemu). */
+#define TPM_PPI_MMIO_BASE_DEFAULT UINT64_C(0xfed45000)
+/** The size of the TPM PPI MMIO area. */
+#define TPM_PPI_MMIO_SIZE _4K
+
+/** @name QEMU compatible PPI interface layout.
+ * @{ */
+/**
+ * The PPI structure layout (yes, it really is misaligned).
+ */
+#pragma pack(1)
+typedef union QEMUTPMPPI
+{
+ /** The byte view. */
+ uint8_t ab[0x400];
+ /** The structured view. */
+ struct
+ {
+ /** Supported operation flags set by the firmware for each operation. */
+ uint8_t abFunc[0x100];
+ /** SMI interrupt to use, set by firmware. Not supported. */
+ uint8_t bPpin;
+ /** ACPI function index to pass to SMM code, set by ACPI. Not supported. */
+ uint32_t u32Ppip;
+ /** Result of last executed operation, set by firmware. */
+ uint32_t u32Pprp;
+ /** Operation request number to execute, set by ACPI. */
+ uint32_t u32Pprq;
+ /** Operation request optional parameter, set by ACPI. */
+ uint32_t u32Pprm;
+ /** Last executed operation request number, copied from QEMUTPMPPI::u32PPrq field by firmware. */
+ uint32_t u32Lppr;
+ /** Result code from SMM function, not supported. */
+ uint32_t u32Fret;
+ /** Reserved for future use. */
+ uint8_t abRsvd[0x40];
+ /** Operation to execute after reboot by firmware, used by firmware. */
+ uint8_t bNextStep;
+ /** Memory overwrite variable. */
+ uint8_t bMovv;
+ } s;
+} QEMUTPMPPI;
+#pragma pack()
+AssertCompileSize(QEMUTPMPPI, 0x400);
+/** Pointer to the QEMU PPI structure layout. */
+typedef QEMUTPMPPI *PQEMUTPMPPI;
+/** Pointer to a const QEMU PPI structure layout. */
+typedef const QEMUTPMPPI *PCQEMUTPMPPI;
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Shared TPM PPI device state.
+ */
+typedef struct DEVTPMPPI
+{
+ /** Base MMIO address of the TPM PPI area. */
+ RTGCPHYS GCPhysMmio;
+ /** The handle of the MMIO region. */
+ IOMMMIOHANDLE hMmio;
+ /** The QEMU PPI state. */
+ QEMUTPMPPI Ppi;
+} DEVTPMPPI;
+/** Pointer to the shared TPM PPI device state. */
+typedef DEVTPMPPI *PDEVTPMPPI;
+
+/**
+ * TPM PPI device state for ring-3.
+ */
+typedef struct DEVTPMPPIR3
+{
+ /** Pointer to the device instance. */
+ PPDMDEVINS pDevIns;
+} DEVTPMPPIR3;
+/** Pointer to the TPM device state for ring-3. */
+typedef DEVTPMPPIR3 *PDEVTPMPPIR3;
+
+
+/**
+ * TPM PPI device state for ring-0.
+ */
+typedef struct DEVTPMPPIR0
+{
+ uint32_t u32Dummy;
+
+} DEVTPMPPIR0;
+/** Pointer to the TPM device state for ring-0. */
+typedef DEVTPMPPIR0 *PDEVTPMPPIR0;
+
+
+/**
+ * TPM PPI device state for raw-mode.
+ */
+typedef struct DEVTPMPPIRC
+{
+ uint32_t u32Dummy;
+} DEVTPMPPIRC;
+/** Pointer to the TPM device state for raw-mode. */
+typedef DEVTPMPPIRC *PDEVTPMPPIRC;
+
+/** The TPM PI device state for the current context. */
+typedef CTX_SUFF(DEVTPMPPI) DEVTPMPPICC;
+/** Pointer to the TPM PPI device state for the current context. */
+typedef CTX_SUFF(PDEVTPMPPI) PDEVTPMPPICC;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+#ifdef IN_RING3
+/**
+ * SSM descriptor table for the TPM PPI structure.
+ */
+static SSMFIELD const g_aTpmPpiFields[] =
+{
+ SSMFIELD_ENTRY(QEMUTPMPPI, s.abFunc),
+ SSMFIELD_ENTRY(QEMUTPMPPI, s.bPpin),
+ SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Ppip),
+ SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Pprp),
+ SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Pprq),
+ SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Pprm),
+ SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Lppr),
+ SSMFIELD_ENTRY(QEMUTPMPPI, s.u32Fret),
+ SSMFIELD_ENTRY(QEMUTPMPPI, s.bNextStep),
+ SSMFIELD_ENTRY(QEMUTPMPPI, s.bMovv),
+ SSMFIELD_ENTRY_TERM()
+};
+#endif
+
+
+/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWREAD}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) tpmPpiMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
+{
+ PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
+ RT_NOREF(pvUser);
+
+ LogFlowFunc(("off=%RGp cb=%u\n", off, cb));
+ if (off + cb > sizeof(pThis->Ppi))
+ return VINF_IOM_MMIO_UNUSED_FF;
+
+ memcpy(pv, &pThis->Ppi.ab[off], cb);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWWRITE}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) tpmPpiMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
+{
+ PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
+ RT_NOREF(pvUser);
+
+ LogFlowFunc(("off=%RGp cb=%u\n", off, cb));
+ if (off + cb > sizeof(pThis->Ppi))
+ return VINF_SUCCESS;
+
+ memcpy(&pThis->Ppi.ab[off], pv, cb);
+ return VINF_SUCCESS;
+}
+
+
+#ifdef IN_RING3
+
+/* -=-=-=-=-=-=-=-=- Saved State -=-=-=-=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNSSMDEVLIVEEXEC}
+ */
+static DECLCALLBACK(int) tpmPpiR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+{
+ PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ RT_NOREF(uPass);
+
+ /* Save the part of the config used for verification purposes when restoring. */
+ pHlp->pfnSSMPutGCPhys(pSSM, pThis->GCPhysMmio);
+
+ return VINF_SSM_DONT_CALL_AGAIN;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) tpmPpiR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ tpmPpiR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
+
+ int rc = pHlp->pfnSSMPutStructEx(pSSM, &pThis->Ppi, sizeof(pThis->Ppi), 0 /*fFlags*/, &g_aTpmPpiFields[0], NULL);
+ AssertRCReturn(rc, rc);
+
+ return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX); /* sanity/terminator */
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) tpmPpiR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ RTGCPHYS GCPhysMmio;
+
+ Assert(uPass == SSM_PASS_FINAL); RT_NOREF(uPass);
+ AssertMsgReturn(uVersion == TPM_PPI_SAVED_STATE_VERSION, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
+
+ /* Verify the config first. */
+ int rc = pHlp->pfnSSMGetGCPhys(pSSM, &GCPhysMmio);
+ AssertRCReturn(rc, rc);
+ if (GCPhysMmio != pThis->GCPhysMmio)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
+ N_("Config mismatch - saved GCPhysMmio=%#RGp; configured GCPhysMmio=%#RGp"),
+ GCPhysMmio, pThis->GCPhysMmio);
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ rc = pHlp->pfnSSMGetStructEx(pSSM, &pThis->Ppi, sizeof(pThis->Ppi), 0 /*fFlags*/, &g_aTpmPpiFields[0], NULL);
+ AssertRCReturn(rc, rc);
+
+ /* The marker. */
+ uint32_t u32;
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+ AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) tpmPpiR3Destruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) tpmPpiR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
+ PDEVTPMPPICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVTPMPPICC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ int rc;
+
+ RT_NOREF(iInstance);
+ Assert(iInstance == 0);
+
+ /*
+ * Initalize the basic variables so that the destructor always works.
+ */
+ pThisCC->pDevIns = pDevIns;
+
+ /*
+ * Validate and read the configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MmioBase", "");
+
+ rc = pHlp->pfnCFGMQueryU64Def(pCfg, "MmioBase", &pThis->GCPhysMmio, TPM_PPI_MMIO_BASE_DEFAULT);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"MmioBase\" value"));
+
+ /*
+ * Register the MMIO range, PDM API requests page aligned
+ * addresses and sizes.
+ */
+ rc = PDMDevHlpMmioCreateAndMap(pDevIns, pThis->GCPhysMmio, TPM_PPI_MMIO_SIZE, tpmPpiMmioWrite, tpmPpiMmioRead,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ "TPM PPI MMIO", &pThis->hMmio);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Saved state.
+ */
+ rc = PDMDevHlpSSMRegister3(pDevIns, TPM_PPI_SAVED_STATE_VERSION, sizeof(*pThis),
+ tpmPpiR3LiveExec, tpmPpiR3SaveExec, tpmPpiR3LoadExec);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+
+#else /* !IN_RING3 */
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) tpmPpiRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVTPMPPI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPMPPI);
+
+ int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, tpmPpiMmioWrite, tpmPpiMmioRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_RING3 */
+
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DeviceTpmPpi =
+{
+ /* .u32Version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "tpm-ppi",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_ARCH_BIOS,
+ /* .cMaxInstances = */ 1,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(DEVTPMPPI),
+ /* .cbInstanceCC = */ sizeof(DEVTPMPPICC),
+ /* .cbInstanceRC = */ sizeof(DEVTPMPPIRC),
+ /* .cMaxPciDevices = */ 0,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "Device implementing the TPM Physical Presence Interface (PPI)\n",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "",
+ /* .pszR0Mod = */ "",
+ /* .pfnConstruct = */ tpmPpiR3Construct,
+ /* .pfnDestruct = */ tpmPpiR3Destruct,
+ /* .pfnRelocate = */ NULL,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ NULL,
+ /* .pfnSuspend = */ NULL,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ NULL,
+ /* .pfnDetach = */ NULL,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ NULL,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ tpmPpiRZConstruct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+ /* .pfnRequest = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ tpmPpiRZConstruct,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION
+};