diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/Devices/Security/DevTpmPpi.cpp | 453 |
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 +}; |