summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Security
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/Security
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/Security')
-rw-r--r--src/VBox/Devices/Security/DevTpm.cpp1874
-rw-r--r--src/VBox/Devices/Security/DrvTpmEmu.cpp1021
-rw-r--r--src/VBox/Devices/Security/DrvTpmEmuTpms.cpp534
-rw-r--r--src/VBox/Devices/Security/DrvTpmHost.cpp403
4 files changed, 3832 insertions, 0 deletions
diff --git a/src/VBox/Devices/Security/DevTpm.cpp b/src/VBox/Devices/Security/DevTpm.cpp
new file mode 100644
index 00000000..936a62aa
--- /dev/null
+++ b/src/VBox/Devices/Security/DevTpm.cpp
@@ -0,0 +1,1874 @@
+/* $Id: DevTpm.cpp $ */
+/** @file
+ * DevTpm - Trusted Platform Module emulation.
+ *
+ * This emulation is based on the spec available under (as of 2021-08-02):
+ * https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p05p_r14_pub.pdf
+ */
+
+/*
+ * Copyright (C) 2021-2023 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/vmm/pdmtpmifs.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+
+#include <iprt/formats/tpm.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** The TPM saved state version. */
+#define TPM_SAVED_STATE_VERSION 1
+
+/** Default vendor ID. */
+#define TPM_VID_DEFAULT 0x1014
+/** Default device ID. */
+#define TPM_DID_DEFAULT 0x0001
+/** Default revision ID. */
+#define TPM_RID_DEFAULT 0x01
+/** Maximum size of the data buffer in bytes. */
+#define TPM_DATA_BUFFER_SIZE_MAX 3968
+
+/** The TPM MMIO base default as defined in chapter 5.2. */
+#define TPM_MMIO_BASE_DEFAULT 0xfed40000
+/** The size of the TPM MMIO area. */
+#define TPM_MMIO_SIZE 0x5000
+
+/** Number of localities as mandated by the TPM spec. */
+#define TPM_LOCALITY_COUNT 5
+/** Size of each locality in the TPM MMIO area (chapter 6.5.2).*/
+#define TPM_LOCALITY_MMIO_SIZE 0x1000
+
+/** @name TPM locality register related defines for the FIFO interface.
+ * @{ */
+/** Ownership management for a particular locality. */
+#define TPM_FIFO_LOCALITY_REG_ACCESS 0x00
+/** Indicates whether a dynamic OS has been established on this platform before.. */
+# define TPM_FIFO_LOCALITY_REG_ACCESS_ESTABLISHMENT RT_BIT(0)
+/** On reads indicates whether the locality requests use of the TPM (1) or not or is already active locality (0),
+ * writing a 1 requests the locality to be granted getting the active locality.. */
+# define TPM_FIFO_LOCALITY_REG_ACCESS_REQUEST_USE RT_BIT(1)
+/** Indicates whether another locality is requesting usage of the TPM. */
+# define TPM_FIFO_LOCALITY_REG_ACCESS_PENDING_REQUEST RT_BIT(2)
+/** Writing a 1 forces the TPM to give control to the locality if it has a higher priority. */
+# define TPM_FIFO_LOCALITY_REG_ACCESS_SEIZE RT_BIT(3)
+/** On reads indicates whether this locality has been seized by a higher locality (1) or not (0), writing a 1 clears this bit. */
+# define TPM_FIFO_LOCALITY_REG_ACCESS_BEEN_SEIZED RT_BIT(4)
+/** On reads indicates whether this locality is active (1) or not (0), writing a 1 relinquishes control for this locality. */
+# define TPM_FIFO_LOCALITY_REG_ACCESS_ACTIVE RT_BIT(5)
+/** Set bit indicates whether all other bits in this register have valid data. */
+# define TPM_FIFO_LOCALITY_REG_ACCESS_VALID RT_BIT(7)
+/** Writable mask. */
+# define TPM_FIFO_LOCALITY_REG_ACCESS_WR_MASK 0x3a
+
+/** Interrupt enable register. */
+#define TPM_FIFO_LOCALITY_REG_INT_ENABLE 0x08
+/** Data available interrupt enable bit. */
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_DATA_AVAIL RT_BIT_32(0)
+/** Status valid interrupt enable bit. */
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_STS_VALID RT_BIT_32(1)
+/** Locality change interrupt enable bit. */
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_LOCALITY_CHANGE RT_BIT_32(2)
+/** Interrupt polarity configuration. */
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_POLARITY_MASK 0x18
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_POLARITY_SHIFT 3
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_POLARITY_SET(a) ((a) << TPM_FIFO_LOCALITY_REG_INT_POLARITY_SHIFT)
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_POLARITY_GET(a) (((a) & TPM_FIFO_LOCALITY_REG_INT_POLARITY_MASK) >> TPM_FIFO_LOCALITY_REG_INT_POLARITY_SHIFT)
+/** High level interrupt trigger. */
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_POLARITY_HIGH 0
+/** Low level interrupt trigger. */
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_POLARITY_LOW 1
+/** Rising edge interrupt trigger. */
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_POLARITY_RISING 2
+/** Falling edge interrupt trigger. */
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_POLARITY_FALLING 3
+/** Command ready enable bit. */
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_CMD_RDY RT_BIT_32(7)
+/** Global interrupt enable/disable bit. */
+# define TPM_FIFO_LOCALITY_REG_INT_ENABLE_GLOBAL RT_BIT_32(31)
+
+/** Configured interrupt vector register. */
+#define TPM_FIFO_LOCALITY_REG_INT_VEC 0x0c
+
+/** Interrupt status register. */
+#define TPM_FIFO_LOCALITY_REG_INT_STS 0x10
+/** Data available interrupt occured bit, writing a 1 clears the bit. */
+# define TPM_FIFO_LOCALITY_REG_INT_STS_DATA_AVAIL RT_BIT_32(0)
+/** Status valid interrupt occured bit, writing a 1 clears the bit. */
+# define TPM_FIFO_LOCALITY_REG_INT_STS_STS_VALID RT_BIT_32(1)
+/** Locality change interrupt occured bit, writing a 1 clears the bit. */
+# define TPM_FIFO_LOCALITY_REG_INT_STS_LOCALITY_CHANGE RT_BIT_32(2)
+/** Command ready occured bit, writing a 1 clears the bit. */
+# define TPM_FIFO_LOCALITY_REG_INT_STS_CMD_RDY RT_BIT_32(7)
+/** Writable mask. */
+# define TPM_FIFO_LOCALITY_REG_INT_STS_WR_MASK UINT32_C(0x87)
+
+/** Interfacce capabilities register. */
+#define TPM_FIFO_LOCALITY_REG_IF_CAP 0x14
+/** Flag whether the TPM supports the data avilable interrupt. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_INT_DATA_AVAIL RT_BIT(0)
+/** Flag whether the TPM supports the status valid interrupt. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_INT_STS_VALID RT_BIT(1)
+/** Flag whether the TPM supports the data avilable interrupt. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_INT_LOCALITY_CHANGE RT_BIT(2)
+/** Flag whether the TPM supports high level interrupts. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_INT_LVL_HIGH RT_BIT(3)
+/** Flag whether the TPM supports low level interrupts. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_INT_LVL_LOW RT_BIT(4)
+/** Flag whether the TPM supports rising edge interrupts. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_INT_RISING_EDGE RT_BIT(5)
+/** Flag whether the TPM supports falling edge interrupts. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_INT_FALLING_EDGE RT_BIT(6)
+/** Flag whether the TPM supports the command ready interrupt. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_INT_CMD_RDY RT_BIT(7)
+/** Flag whether the busrt count field is static or dynamic. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_BURST_CNT_STATIC RT_BIT(8)
+/** Maximum transfer size support. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_DATA_XFER_SZ_MASK 0x600
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_DATA_XFER_SZ_SHIFT 9
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_DATA_XFER_SZ_SET(a) ((a) << TPM_FIFO_LOCALITY_REG_IF_CAP_DATA_XFER_SZ_SHIFT)
+/** Only legacy transfers supported. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_DATA_XFER_SZ_LEGACY 0x0
+/** 8B maximum transfer size. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_DATA_XFER_SZ_8B 0x1
+/** 32B maximum transfer size. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_DATA_XFER_SZ_32B 0x2
+/** 64B maximum transfer size. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_DATA_XFER_SZ_64B 0x3
+/** Interface version. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_IF_VERSION_MASK UINT32_C(0x70000000)
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_IF_VERSION_SHIFT 28
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_IF_VERSION_SET(a) ((a) << TPM_FIFO_LOCALITY_REG_IF_CAP_IF_VERSION_SHIFT)
+/** Interface 1.21 or ealier. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_IF_VERSION_IF_1_21 0
+/** Interface 1.3. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_IF_VERSION_IF_1_3 2
+/** Interface 1.3 for TPM 2.0. */
+# define TPM_FIFO_LOCALITY_REG_IF_CAP_IF_VERSION_IF_1_3_TPM2 3
+
+/** TPM status register. */
+#define TPM_FIFO_LOCALITY_REG_STS 0x18
+/** Writing a 1 forces the TPM to re-send the response. */
+# define TPM_FIFO_LOCALITY_REG_STS_RESPONSE_RETRY RT_BIT_32(1)
+/** Indicating whether the TPM has finished a self test. */
+# define TPM_FIFO_LOCALITY_REG_STS_SELF_TEST_DONE RT_BIT_32(2)
+/** Flag indicating whether the TPM expects more data for the command. */
+# define TPM_FIFO_LOCALITY_REG_STS_EXPECT RT_BIT_32(3)
+/** Flag indicating whether the TPM has more response data available. */
+# define TPM_FIFO_LOCALITY_REG_STS_DATA_AVAIL RT_BIT_32(4)
+/** Written by software to cause the TPM to execute a previously transfered command. */
+# define TPM_FIFO_LOCALITY_REG_STS_TPM_GO RT_BIT_32(5)
+/** On reads indicates whether the TPM is ready to receive a new command (1) or not (0),
+ * a write of 1 causes the TPM to transition to this state. */
+# define TPM_FIFO_LOCALITY_REG_STS_CMD_RDY RT_BIT_32(6)
+/** Indicates whether the Expect and data available bits are valid. */
+# define TPM_FIFO_LOCALITY_REG_STS_VALID RT_BIT_32(7)
+/** Sets the burst count. */
+# define TPM_FIFO_LOCALITY_REG_STS_BURST_CNT_MASK UINT32_C(0xffff00)
+# define TPM_FIFO_LOCALITY_REG_STS_BURST_CNT_SHIFT UINT32_C(8)
+# define TPM_FIFO_LOCALITY_REG_STS_BURST_CNT_SET(a) ((a) << TPM_FIFO_LOCALITY_REG_STS_BURST_CNT_SHIFT)
+/** Cancels the active command. */
+# define TPM_FIFO_LOCALITY_REG_STS_CMD_CANCEL RT_BIT_32(24)
+/** Reset establishment bit. */
+# define TPM_FIFO_LOCALITY_REG_STS_RST_ESTABLISHMENT RT_BIT_32(25)
+/** Sets the TPM family. */
+# define TPM_FIFO_LOCALITY_REG_STS_TPM_FAMILY_MASK UINT32_C(0x0c000000)
+# define TPM_FIFO_LOCALITY_REG_STS_TPM_FAMILY_SHIFT UINT32_C(26)
+# define TPM_FIFO_LOCALITY_REG_STS_TPM_FAMILY_SET(a) ((a) << TPM_FIFO_LOCALITY_REG_STS_TPM_FAMILY_SHIFT)
+# define TPM_FIFO_LOCALITY_REG_STS_TPM_FAMILY_1_2 UINT32_C(0)
+# define TPM_FIFO_LOCALITY_REG_STS_TPM_FAMILY_2_0 UINT32_C(1)
+
+
+/** TPM end of HASH operation signal register for locality 4. */
+#define TPM_FIFO_LOCALITY_REG_HASH_END 0x20
+/** Data FIFO read/write register. */
+#define TPM_FIFO_LOCALITY_REG_DATA_FIFO 0x24
+/** TPM start of HASH operation signal register for locality 4. */
+#define TPM_FIFO_LOCALITY_REG_HASH_START 0x28
+
+/** Locality interface ID register. */
+#define TPM_FIFO_LOCALITY_REG_INTF_ID 0x30
+/** Interface type field. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_MASK UINT32_C(0xf)
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_SHIFT 0
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_SET(a) ((a) << TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_SHIFT)
+/** FIFO interface as defined in PTP for TPM 2.0 is active. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_FIFO_TPM20 0x0
+/** CRB interface is active. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_CRB 0x1
+/** FIFO interface as defined in TIS 1.3 is active. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_TIS1_3 0xf
+/** Interface type field. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_VERS_MASK UINT32_C(0xf)
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_VERS_SHIFT 4
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_VERS_SET(a) ((a) << TPM_FIFO_LOCALITY_REG_INTF_ID_IF_VERS_SHIFT)
+/** FIFO interface for TPM 2.0 */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_VERS_FIFO 0
+/** CRB interface version 0. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_VERS_CRB 1
+/** Only locality 0 is supported when clear, set if 5 localities are supported. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_LOCALITY RT_BIT(8)
+/** Maximum transfer size support. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_MASK 0x1800
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_SHIFT 11
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_SET(a) ((a) << TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_SHIFT)
+/** Only legacy transfers supported. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_LEGACY 0x0
+/** 8B maximum transfer size. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_8B 0x1
+/** 32B maximum transfer size. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_32B 0x2
+/** 64B maximum transfer size. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_64B 0x3
+/** FIFO interface is supported and may be selected. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_FIFO RT_BIT(13)
+/** CRB interface is supported and may be selected. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_CRB RT_BIT(14)
+/** Interrupt polarity configuration. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_MASK 0x60000
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_SHIFT 17
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_SET(a) ((a) << TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_SHIFT)
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_GET(a) (((a) & TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_MASK) >> TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_SHIFT)
+/** Selects the FIFO interface, takes effect on next _TPM_INIT. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_FIFO 0
+/** Selects the CRB interface, takes effect on next _TPM_INIT. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_CRB 1
+/** Locks the interface selector field and prevents further changes. */
+# define TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_LOCK RT_BIT(19)
+
+
+/** Extended data FIFO read/write register. */
+#define TPM_FIFO_LOCALITY_REG_XDATA_FIFO 0x80
+/** TPM device and vendor ID. */
+#define TPM_FIFO_LOCALITY_REG_DID_VID 0xf00
+/** TPM revision ID. */
+#define TPM_FIFO_LOCALITY_REG_RID 0xf04
+/** @} */
+
+
+/** @name TPM locality register related defines for the CRB interface.
+ * @{ */
+/** Locality state register. */
+#define TPM_CRB_LOCALITY_REG_STATE 0x00
+/** Indicates whether a dynamic OS has been established on this platform before.. */
+# define TPM_CRB_LOCALITY_REG_ESTABLISHMENT RT_BIT(0)
+/** Flag whether the host has a locality assigned (1) or not (0). */
+# define TPM_CRB_LOCALITY_REG_STATE_LOC_ASSIGNED RT_BIT(1)
+/** Indicates the currently active locality. */
+# define TPM_CRB_LOCALITY_REG_STATE_ACTIVE_LOC_MASK UINT32_C(0x1c)
+# define TPM_CRB_LOCALITY_REG_STATE_ACTIVE_LOC_SHIFT 2
+# define TPM_CRB_LOCALITY_REG_STATE_ACTIVE_LOC_SET(a) ((a) << TPM_CRB_LOCALITY_REG_STATE_ACTIVE_LOC_SHIFT)
+/** Flag whether the register contains valid values. */
+# define TPM_CRB_LOCALITY_REG_STATE_VALID RT_BIT(7)
+
+/** Locality control register. */
+#define TPM_CRB_LOCALITY_REG_CTRL 0x08
+/** Request TPM access from this locality. */
+# define TPM_CRB_LOCALITY_REG_CTRL_REQ_ACCESS RT_BIT(0)
+/** Release TPM access from this locality. */
+# define TPM_CRB_LOCALITY_REG_CTRL_RELINQUISH RT_BIT(1)
+/** Seize TPM access in favor of this locality if it has a higher priority. */
+# define TPM_CRB_LOCALITY_REG_CTRL_SEIZE RT_BIT(2)
+/** Resets the established bit if written from locality 3 or 4. */
+# define TPM_CRB_LOCALITY_REG_CTRL_RST_ESTABLISHMENT RT_BIT(3)
+
+/** Locality status register. */
+#define TPM_CRB_LOCALITY_REG_STS 0x0c
+/** Locality has been granted access to the TPM. */
+# define TPM_CRB_LOCALITY_REG_STS_GRANTED RT_BIT(0)
+/** A higher locality has seized the TPM from this locality. */
+# define TPM_CRB_LOCALITY_REG_STS_SEIZED RT_BIT(1)
+
+/** Locality interface ID register. */
+#define TPM_CRB_LOCALITY_REG_INTF_ID 0x30
+/** Interface type field. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_TYPE_MASK UINT32_C(0xf)
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_TYPE_SHIFT 0
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_TYPE_SET(a) ((a) << TPM_CRB_LOCALITY_REG_INTF_ID_IF_TYPE_SHIFT)
+/** FIFO interface as defined in PTP for TPM 2.0 is active. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_TYPE_FIFO_TPM20 0x0
+/** CRB interface is active. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_TYPE_CRB 0x1
+/** FIFO interface as defined in TIS 1.3 is active. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_TYPE_TIS1_3 0xf
+/** Interface type field. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_VERS_MASK UINT32_C(0xf)
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_VERS_SHIFT 4
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_VERS_SET(a) ((a) << TPM_CRB_LOCALITY_REG_INTF_ID_IF_VERS_SHIFT)
+/** FIFO interface for TPM 2.0 */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_VERS_FIFO 0
+/** CRB interface version 0. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_VERS_CRB 1
+/** Only locality 0 is supported when clear, set if 5 localities are supported. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_LOCALITY RT_BIT(8)
+/** @todo TPM supports ... */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_CRB_IDLE_BYPASS RT_BIT(9)
+/** Maximum transfer size support. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_MASK 0x1800
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_SHIFT 11
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_SET(a) ((a) << TPM_CRB_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_SHIFT)
+/** Only legacy transfers supported. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_LEGACY 0x0
+/** 8B maximum transfer size. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_8B 0x1
+/** 32B maximum transfer size. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_32B 0x2
+/** 64B maximum transfer size. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_64B 0x3
+/** FIFO interface is supported and may be selected. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_FIFO RT_BIT(13)
+/** CRB interface is supported and may be selected. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_CAP_CRB RT_BIT(14)
+/** Interrupt polarity configuration. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_MASK 0x60000
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_SHIFT 17
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_SET(a) ((a) << TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_SHIFT)
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_GET(a) (((a) & TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_MASK) >> TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_SHIFT)
+/** Selects the FIFO interface, takes effect on next _TPM_INIT. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_FIFO 0
+/** Selects the CRB interface, takes effect on next _TPM_INIT. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_CRB 1
+/** Locks the interface selector field and prevents further changes. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_LOCK RT_BIT(19)
+/** Revision ID field. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_RID_SHIFT 17
+# define TPM_CRB_LOCALITY_REG_INTF_ID_RID_SET(a) ((uint64_t)(a) << TPM_CRB_LOCALITY_REG_INTF_ID_RID_SHIFT)
+/** Vendor ID field. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_VID_SHIFT 32
+# define TPM_CRB_LOCALITY_REG_INTF_ID_VID_SET(a) ((uint64_t)(a) << TPM_CRB_LOCALITY_REG_INTF_ID_VID_SHIFT)
+/** Device ID field. */
+# define TPM_CRB_LOCALITY_REG_INTF_ID_DID_SHIFT 48
+# define TPM_CRB_LOCALITY_REG_INTF_ID_DID_SET(a) ((uint64_t)(a) << TPM_CRB_LOCALITY_REG_INTF_ID_DID_SHIFT)
+
+/** Locality CRB extension register (optional and locality 0 only). */
+#define TPM_CRB_LOCALITY_REG_CTRL_EXT 0x38
+
+/** Locality CRB request register. */
+#define TPM_CRB_LOCALITY_REG_CTRL_REQ 0x40
+/** The TPM should transition to the ready state to receive a new command. */
+# define TPM_CRB_LOCALITY_REG_CTRL_REQ_CMD_RDY RT_BIT(0)
+/** The TPM should transition to the idle state. */
+# define TPM_CRB_LOCALITY_REG_CTRL_REQ_IDLE RT_BIT(1)
+
+/** Locality CRB status register. */
+#define TPM_CRB_LOCALITY_REG_CTRL_STS 0x44
+/** This bit indicates that the TPM ran into a fatal error if set. */
+# define TPM_CRB_LOCALITY_REG_CTRL_STS_TPM_FATAL_ERR RT_BIT(0)
+/** This bit indicates that the TPM is in the idle state. */
+# define TPM_CRB_LOCALITY_REG_CTRL_STS_TPM_IDLE RT_BIT(1)
+
+/** Locality CRB cancel register. */
+#define TPM_CRB_LOCALITY_REG_CTRL_CANCEL 0x48
+/** Locality CRB start register. */
+#define TPM_CRB_LOCALITY_REG_CTRL_START 0x4c
+
+/** Locality interrupt enable register. */
+#define TPM_CRB_LOCALITY_REG_INT_ENABLE 0x50
+/** Enable the "TPM has executed a reqeust and response is available" interrupt. */
+# define TPM_CRB_LOCALITY_REG_INT_ENABLE_START RT_BIT(0)
+/** Enable the "TPM has transitioned to the command ready state" interrupt. */
+# define TPM_CRB_LOCALITY_REG_INT_CMD_RDY RT_BIT(1)
+/** Enable the "TPM has cleared the establishment flag" interrupt. */
+# define TPM_CRB_LOCALITY_REG_INT_ESTABLISHMENT_CLR RT_BIT(2)
+/** Enable the "active locality has changed" interrupt. */
+# define TPM_CRB_LOCALITY_REG_INT_LOC_CHANGED RT_BIT(3)
+/** Enables interrupts globally as defined by the individual bits in this register. */
+# define TPM_CRB_LOCALITY_REG_INT_GLOBAL_ENABLE RT_BIT(31)
+
+/** Locality interrupt status register. */
+#define TPM_CRB_LOCALITY_REG_INT_STS 0x54
+/** Indicates that the TPM as executed a command and the response is available for reading, writing a 1 clears the bit. */
+# define TPM_CRB_LOCALITY_REG_INT_STS_START RT_BIT(0)
+/** Indicates that the TPM has finished the transition to the ready state, writing a 1 clears this bit. */
+# define TPM_CRB_LOCALITY_REG_INT_STS_CMD_RDY RT_BIT(1)
+/** Indicates that the TPM has cleared the establishment flag, writing a 1 clears this bit. */
+# define TPM_CRB_LOCALITY_REG_INT_STS_ESTABLISHMENT_CLR RT_BIT(2)
+/** Indicates that a locality change has occurrec, writing a 1 clears this bit. */
+# define TPM_CRB_LOCALITY_REG_INT_STS_LOC_CHANGED RT_BIT(3)
+
+/** Locality command buffer size register. */
+#define TPM_CRB_LOCALITY_REG_CTRL_CMD_SZ 0x58
+/** Locality command buffer low address register. */
+#define TPM_CRB_LOCALITY_REG_CTRL_CMD_LADDR 0x5c
+/** Locality command buffer low address register. */
+#define TPM_CRB_LOCALITY_REG_CTRL_CMD_HADDR 0x60
+/** Locality response buffer size register. */
+#define TPM_CRB_LOCALITY_REG_CTRL_RSP_SZ 0x64
+/** Locality response buffer address register. */
+#define TPM_CRB_LOCALITY_REG_CTRL_RSP_ADDR 0x68
+/** Locality data buffer. */
+#define TPM_CRB_LOCALITY_REG_DATA_BUFFER 0x80
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Possible TPM states
+ * (see chapter 5.6.12.1 Figure 3 State Transition Diagram).
+ */
+typedef enum DEVTPMSTATE
+{
+ /** Invalid state, do not use. */
+ DEVTPMSTATE_INVALID = 0,
+ /** Idle state. */
+ DEVTPMSTATE_IDLE,
+ /** Ready to accept command data. */
+ DEVTPMSTATE_READY,
+ /** Command data being transfered. */
+ DEVTPMSTATE_CMD_RECEPTION,
+ /** Command is being executed by the TPM. */
+ DEVTPMSTATE_CMD_EXEC,
+ /** Command has completed and data can be read. */
+ DEVTPMSTATE_CMD_COMPLETION,
+ /** Command is being canceled. */
+ DEVTPMSTATE_CMD_CANCEL,
+ /** TPM ran into a fatal error and is not operational. */
+ DEVTPMSTATE_FATAL_ERROR,
+ /** Last valid state (used for saved state sanity check). */
+ DEVTPMSTATE_LAST_VALID = DEVTPMSTATE_FATAL_ERROR,
+ /** 32bit hack. */
+ DEVTPMSTATE_32BIT_HACK = 0x7fffffff
+} DEVTPMSTATE;
+
+
+/**
+ * Locality state.
+ */
+typedef struct DEVTPMLOCALITY
+{
+ /** The interrupt enable register. */
+ uint32_t uRegIntEn;
+ /** The interrupt status register. */
+ uint32_t uRegIntSts;
+} DEVTPMLOCALITY;
+/** Pointer to a locality state. */
+typedef DEVTPMLOCALITY *PDEVTPMLOCALITY;
+/** Pointer to a const locality state. */
+typedef const DEVTPMLOCALITY *PCDEVTPMLOCALITY;
+
+
+/**
+ * Shared TPM device state.
+ */
+typedef struct DEVTPM
+{
+ /** Base MMIO address of the TPM device. */
+ RTGCPHYS GCPhysMmio;
+ /** The handle of the MMIO region. */
+ IOMMMIOHANDLE hMmio;
+ /** The handle for the ring-3 task. */
+ PDMTASKHANDLE hTpmCmdTask;
+ /** The vendor ID configured. */
+ uint16_t uVenId;
+ /** The device ID configured. */
+ uint16_t uDevId;
+ /** The revision ID configured. */
+ uint8_t bRevId;
+ /** The IRQ value. */
+ uint8_t uIrq;
+ /** Flag whether CRB access mode is used. */
+ bool fCrb;
+ /** Flag whether the TPM driver below supportes other localities than 0. */
+ bool fLocChangeSup;
+ /** Flag whether the establishment bit is set. */
+ bool fEstablishmentSet;
+
+ /** Currently selected locality. */
+ uint8_t bLoc;
+ /** States of the implemented localities. */
+ DEVTPMLOCALITY aLoc[TPM_LOCALITY_COUNT];
+ /** Bitmask of localities having requested access to the TPM. */
+ uint32_t bmLocReqAcc;
+ /** Bitmask of localities having been seized access from the TPM. */
+ uint32_t bmLocSeizedAcc;
+ /** The current state of the TPM. */
+ DEVTPMSTATE enmState;
+ /** The TPM version being emulated. */
+ TPMVERSION enmTpmVers;
+
+ /** Size of the command/response buffer. */
+ uint32_t cbCmdResp;
+ /** Offset into the Command/Response buffer. */
+ uint32_t offCmdResp;
+ /** Command/Response buffer. */
+ uint8_t abCmdResp[TPM_DATA_BUFFER_SIZE_MAX];
+} DEVTPM;
+/** Pointer to the shared TPM device state. */
+typedef DEVTPM *PDEVTPM;
+
+/** The special no current locality selected value. */
+#define TPM_NO_LOCALITY_SELECTED 0xff
+
+
+/**
+ * TPM device state for ring-3.
+ */
+typedef struct DEVTPMR3
+{
+ /** Pointer to the device instance. */
+ PPDMDEVINS pDevIns;
+ /** The base interface for LUN\#0. */
+ PDMIBASE IBase;
+ /** The base interface below. */
+ R3PTRTYPE(PPDMIBASE) pDrvBase;
+ /** The TPM connector interface below. */
+ R3PTRTYPE(PPDMITPMCONNECTOR) pDrvTpm;
+} DEVTPMR3;
+/** Pointer to the TPM device state for ring-3. */
+typedef DEVTPMR3 *PDEVTPMR3;
+
+
+/**
+ * TPM device state for ring-0.
+ */
+typedef struct DEVTPMR0
+{
+ uint32_t u32Dummy;
+} DEVTPMR0;
+/** Pointer to the TPM device state for ring-0. */
+typedef DEVTPMR0 *PDEVTPMR0;
+
+
+/**
+ * TPM device state for raw-mode.
+ */
+typedef struct DEVTPMRC
+{
+ uint32_t u32Dummy;
+} DEVTPMRC;
+/** Pointer to the TPM device state for raw-mode. */
+typedef DEVTPMRC *PDEVTPMRC;
+
+/** The TPM device state for the current context. */
+typedef CTX_SUFF(DEVTPM) DEVTPMCC;
+/** Pointer to the TPM device state for the current context. */
+typedef CTX_SUFF(PDEVTPM) PDEVTPMCC;
+
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef IN_RING3
+/**
+ * SSM descriptor table for the TPM structure.
+ */
+static SSMFIELD const g_aTpmFields[] =
+{
+ SSMFIELD_ENTRY(DEVTPM, fEstablishmentSet),
+ SSMFIELD_ENTRY(DEVTPM, bLoc),
+ SSMFIELD_ENTRY(DEVTPM, aLoc[0].uRegIntEn),
+ SSMFIELD_ENTRY(DEVTPM, aLoc[0].uRegIntSts),
+ SSMFIELD_ENTRY(DEVTPM, aLoc[1].uRegIntEn),
+ SSMFIELD_ENTRY(DEVTPM, aLoc[1].uRegIntSts),
+ SSMFIELD_ENTRY(DEVTPM, aLoc[2].uRegIntEn),
+ SSMFIELD_ENTRY(DEVTPM, aLoc[2].uRegIntSts),
+ SSMFIELD_ENTRY(DEVTPM, aLoc[3].uRegIntEn),
+ SSMFIELD_ENTRY(DEVTPM, aLoc[3].uRegIntSts),
+ SSMFIELD_ENTRY(DEVTPM, aLoc[4].uRegIntEn),
+ SSMFIELD_ENTRY(DEVTPM, aLoc[4].uRegIntSts),
+ SSMFIELD_ENTRY(DEVTPM, bmLocReqAcc),
+ SSMFIELD_ENTRY(DEVTPM, bmLocSeizedAcc),
+ SSMFIELD_ENTRY(DEVTPM, enmState),
+ SSMFIELD_ENTRY(DEVTPM, offCmdResp),
+ SSMFIELD_ENTRY(DEVTPM, abCmdResp),
+ SSMFIELD_ENTRY_TERM()
+};
+#endif
+
+
+/**
+ * Sets the IRQ line of the given device to the given state.
+ *
+ * @param pDevIns Pointer to the PDM device instance data.
+ * @param pThis Pointer to the shared TPM device.
+ * @param iLvl The interrupt level to set.
+ */
+DECLINLINE(void) tpmIrqReq(PPDMDEVINS pDevIns, PDEVTPM pThis, int iLvl)
+{
+ PDMDevHlpISASetIrqNoWait(pDevIns, pThis->uIrq, iLvl);
+}
+
+
+/**
+ * Updates the IRQ status of the given locality.
+ *
+ * @param pDevIns Pointer to the PDM device instance data.
+ * @param pThis Pointer to the shared TPM device.
+ * @param pLoc The locality state.
+ */
+static void tpmLocIrqUpdate(PPDMDEVINS pDevIns, PDEVTPM pThis, PDEVTPMLOCALITY pLoc)
+{
+ if ( (pLoc->uRegIntEn & TPM_CRB_LOCALITY_REG_INT_GLOBAL_ENABLE) /* Aliases with TPM_FIFO_LOCALITY_REG_INT_ENABLE_GLOBAL */
+ && (pLoc->uRegIntEn & pLoc->uRegIntSts))
+ tpmIrqReq(pDevIns, pThis, 1);
+ else
+ tpmIrqReq(pDevIns, pThis, 0);
+}
+
+
+/**
+ * Sets the interrupt status for the given locality, firing an interrupt if necessary.
+ *
+ * @param pDevIns Pointer to the PDM device instance data.
+ * @param pThis Pointer to the shared TPM device.
+ * @param pLoc The locality state.
+ * @param uSts The interrupt status bit to set.
+ */
+static void tpmLocSetIntSts(PPDMDEVINS pDevIns, PDEVTPM pThis, PDEVTPMLOCALITY pLoc, uint32_t uSts)
+{
+ pLoc->uRegIntSts |= uSts;
+ tpmLocIrqUpdate(pDevIns, pThis, pLoc);
+}
+
+
+/**
+ * Selects the next locality which has requested access.
+ *
+ * @param pDevIns Pointer to the PDM device instance data.
+ * @param pThis Pointer to the shared TPM device.
+ */
+static void tpmLocSelectNext(PPDMDEVINS pDevIns, PDEVTPM pThis)
+{
+ Assert(pThis->bmLocReqAcc);
+ Assert(pThis->bLoc == TPM_NO_LOCALITY_SELECTED);
+ pThis->bLoc = (uint8_t)ASMBitLastSetU32(pThis->bmLocReqAcc) - 1; /* Select one with highest priority. */
+
+ tpmLocSetIntSts(pDevIns, pThis, &pThis->aLoc[pThis->bLoc], TPM_CRB_LOCALITY_REG_INT_STS_LOC_CHANGED);
+}
+
+
+/**
+ * Returns the given locality being accessed from the given TPM MMIO offset.
+ *
+ * @returns Locality number.
+ * @param off The offset into the TPM MMIO region.
+ */
+DECLINLINE(uint8_t) tpmGetLocalityFromOffset(RTGCPHYS off)
+{
+ return off / TPM_LOCALITY_MMIO_SIZE;
+}
+
+
+/**
+ * Returns the given register of a particular locality being accessed from the given TPM MMIO offset.
+ *
+ * @returns Register index being accessed.
+ * @param off The offset into the TPM MMIO region.
+ */
+DECLINLINE(uint32_t) tpmGetRegisterFromOffset(RTGCPHYS off)
+{
+ return off % TPM_LOCALITY_MMIO_SIZE;
+}
+
+
+/**
+ * Read from a FIFO interface register.
+ *
+ * @returns VBox strict status code.
+ * @param pDevIns Pointer to the PDM device instance data.
+ * @param pThis Pointer to the shared TPM device.
+ * @param pLoc The locality state being read from.
+ * @param bLoc The locality index.
+ * @param uReg The register offset being accessed.
+ * @param pu64 Where to store the read data.
+ * @param cb Number of bytes to read.
+ */
+static VBOXSTRICTRC tpmMmioFifoRead(PPDMDEVINS pDevIns, PDEVTPM pThis, PDEVTPMLOCALITY pLoc,
+ uint8_t bLoc, uint32_t uReg, uint64_t *pu64, size_t cb)
+{
+ RT_NOREF(pDevIns);
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+
+ /* Special path for the data buffer. */
+ if ( ( ( uReg >= TPM_FIFO_LOCALITY_REG_DATA_FIFO
+ && uReg < TPM_FIFO_LOCALITY_REG_DATA_FIFO + sizeof(uint32_t))
+ || ( uReg >= TPM_FIFO_LOCALITY_REG_XDATA_FIFO
+ && uReg < TPM_FIFO_LOCALITY_REG_XDATA_FIFO + sizeof(uint32_t)))
+ && bLoc == pThis->bLoc
+ && pThis->enmState == DEVTPMSTATE_CMD_COMPLETION)
+ {
+ if (pThis->offCmdResp <= pThis->cbCmdResp - cb)
+ {
+ memcpy(pu64, &pThis->abCmdResp[pThis->offCmdResp], cb);
+ pThis->offCmdResp += (uint32_t)cb;
+ }
+ else
+ memset(pu64, 0xff, cb);
+ return VINF_SUCCESS;
+ }
+
+ uint64_t u64;
+ switch (uReg)
+ {
+ case TPM_FIFO_LOCALITY_REG_ACCESS:
+ u64 = TPM_FIFO_LOCALITY_REG_ACCESS_VALID;
+ if (pThis->bLoc == bLoc)
+ u64 |= TPM_FIFO_LOCALITY_REG_ACCESS_ACTIVE;
+ if (pThis->bmLocSeizedAcc & RT_BIT_32(bLoc))
+ u64 |= TPM_FIFO_LOCALITY_REG_ACCESS_BEEN_SEIZED;
+ if (pThis->bmLocReqAcc & ~RT_BIT_32(bLoc))
+ u64 |= TPM_FIFO_LOCALITY_REG_ACCESS_PENDING_REQUEST;
+ if ( pThis->bLoc != bLoc
+ && pThis->bmLocReqAcc & RT_BIT_32(bLoc))
+ u64 |= TPM_FIFO_LOCALITY_REG_ACCESS_REQUEST_USE;
+ if (pThis->fEstablishmentSet)
+ u64 |= TPM_FIFO_LOCALITY_REG_ACCESS_ESTABLISHMENT;
+ break;
+ case TPM_FIFO_LOCALITY_REG_INT_ENABLE:
+ u64 = pLoc->uRegIntEn;
+ break;
+ case TPM_FIFO_LOCALITY_REG_INT_VEC:
+ u64 = pThis->uIrq;
+ break;
+ case TPM_FIFO_LOCALITY_REG_INT_STS:
+ u64 = pLoc->uRegIntSts;
+ break;
+ case TPM_FIFO_LOCALITY_REG_IF_CAP:
+ u64 = TPM_FIFO_LOCALITY_REG_IF_CAP_INT_DATA_AVAIL
+ | TPM_FIFO_LOCALITY_REG_IF_CAP_INT_STS_VALID
+ | TPM_FIFO_LOCALITY_REG_IF_CAP_INT_LOCALITY_CHANGE
+ | TPM_FIFO_LOCALITY_REG_IF_CAP_INT_LVL_LOW
+ | TPM_FIFO_LOCALITY_REG_IF_CAP_INT_CMD_RDY
+ | TPM_FIFO_LOCALITY_REG_IF_CAP_DATA_XFER_SZ_SET(TPM_FIFO_LOCALITY_REG_IF_CAP_DATA_XFER_SZ_64B)
+ | TPM_FIFO_LOCALITY_REG_IF_CAP_IF_VERSION_SET(TPM_FIFO_LOCALITY_REG_IF_CAP_IF_VERSION_IF_1_3); /** @todo Make some of them configurable? */
+ break;
+ case TPM_FIFO_LOCALITY_REG_STS:
+ if (bLoc != pThis->bLoc)
+ {
+ u64 = UINT64_MAX;
+ break;
+ }
+
+ u64 = TPM_FIFO_LOCALITY_REG_STS_TPM_FAMILY_SET( pThis->enmTpmVers == TPMVERSION_1_2
+ ? TPM_FIFO_LOCALITY_REG_STS_TPM_FAMILY_1_2
+ : TPM_FIFO_LOCALITY_REG_STS_TPM_FAMILY_2_0)
+ | TPM_FIFO_LOCALITY_REG_STS_BURST_CNT_SET(_1K)
+ | TPM_FIFO_LOCALITY_REG_STS_VALID;
+ if (pThis->enmState == DEVTPMSTATE_READY)
+ u64 |= TPM_FIFO_LOCALITY_REG_STS_CMD_RDY;
+ else if (pThis->enmState == DEVTPMSTATE_CMD_RECEPTION) /* When in the command reception state check whether all of the command data has been received. */
+ {
+ if ( pThis->offCmdResp < sizeof(TPMREQHDR)
+ || pThis->offCmdResp < RTTpmReqGetSz((PCTPMREQHDR)&pThis->abCmdResp[0]))
+ u64 |= TPM_FIFO_LOCALITY_REG_STS_EXPECT;
+ }
+ else if (pThis->enmState == DEVTPMSTATE_CMD_COMPLETION) /* Check whether there is more response data available. */
+ {
+ if (pThis->offCmdResp < RTTpmRespGetSz((PCTPMRESPHDR)&pThis->abCmdResp[0]))
+ u64 |= TPM_FIFO_LOCALITY_REG_STS_DATA_AVAIL;
+ }
+ break;
+ case TPM_FIFO_LOCALITY_REG_INTF_ID:
+ u64 = TPM_FIFO_LOCALITY_REG_INTF_ID_IF_VERS_SET(TPM_FIFO_LOCALITY_REG_INTF_ID_IF_VERS_FIFO)
+ | TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_SET(TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_64B)
+ | TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_GET(TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_FIFO)
+ | TPM_FIFO_LOCALITY_REG_INTF_ID_IF_SEL_LOCK;
+ if (pThis->enmTpmVers == TPMVERSION_1_2)
+ u64 |= TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_SET(TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_TIS1_3);
+ else
+ u64 |= TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_SET(TPM_FIFO_LOCALITY_REG_INTF_ID_IF_TYPE_FIFO_TPM20);
+
+ if (pThis->fLocChangeSup) /* Only advertise the locality capability if the driver below supports it. */
+ u64 |= TPM_FIFO_LOCALITY_REG_INTF_ID_CAP_LOCALITY;
+ break;
+ case TPM_FIFO_LOCALITY_REG_DID_VID:
+ u64 = RT_H2BE_U32(RT_MAKE_U32(pThis->uVenId, pThis->uDevId));
+ break;
+ case TPM_FIFO_LOCALITY_REG_RID:
+ u64 = pThis->bRevId;
+ break;
+ default: /* Return ~0. */
+ u64 = UINT64_MAX;
+ break;
+ }
+
+ *pu64 = u64;
+
+ return rc;
+}
+
+
+/**
+ * Read to a FIFO interface register.
+ *
+ * @returns VBox strict status code.
+ * @param pDevIns Pointer to the PDM device instance data.
+ * @param pThis Pointer to the shared TPM device.
+ * @param pLoc The locality state being written to.
+ * @param bLoc The locality index.
+ * @param uReg The register offset being accessed.
+ * @param u64 The value to write.
+ * @param cb Number of bytes to write.
+ */
+static VBOXSTRICTRC tpmMmioFifoWrite(PPDMDEVINS pDevIns, PDEVTPM pThis, PDEVTPMLOCALITY pLoc,
+ uint8_t bLoc, uint32_t uReg, uint64_t u64, size_t cb)
+{
+#ifdef IN_RING3
+ PDEVTPMR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVTPMR3);
+#endif
+
+ /* Special path for the data buffer. */
+ if ( ( ( uReg >= TPM_FIFO_LOCALITY_REG_DATA_FIFO
+ && uReg < TPM_FIFO_LOCALITY_REG_DATA_FIFO + sizeof(uint32_t))
+ || ( uReg >= TPM_FIFO_LOCALITY_REG_XDATA_FIFO
+ && uReg < TPM_FIFO_LOCALITY_REG_XDATA_FIFO + sizeof(uint32_t)))
+ && bLoc == pThis->bLoc
+ && ( pThis->enmState == DEVTPMSTATE_READY
+ || pThis->enmState == DEVTPMSTATE_CMD_RECEPTION))
+ {
+ pThis->enmState = DEVTPMSTATE_CMD_RECEPTION;
+ if (pThis->offCmdResp <= pThis->cbCmdResp - cb)
+ {
+ memcpy(&pThis->abCmdResp[pThis->offCmdResp], &u64, cb);
+ pThis->offCmdResp += (uint32_t)cb;
+ }
+ return VINF_SUCCESS;
+ }
+
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ uint32_t u32 = (uint32_t)u64;
+
+ switch (uReg)
+ {
+ case TPM_FIFO_LOCALITY_REG_ACCESS:
+ u32 &= TPM_FIFO_LOCALITY_REG_ACCESS_WR_MASK;
+ /*
+ * Chapter 5.6.11, 2 states that writing to this register with more than one
+ * bit set to '1' is vendor specific, we decide to ignore such writes to make the logic
+ * below simpler.
+ */
+ if (!RT_IS_POWER_OF_TWO(u32))
+ break;
+
+ /* Seize access only if this locality has a higher priority than the currently selected one. */
+ if ( (u32 & TPM_FIFO_LOCALITY_REG_ACCESS_SEIZE)
+ && pThis->bLoc != TPM_NO_LOCALITY_SELECTED
+ && bLoc > pThis->bLoc)
+ {
+ pThis->bmLocSeizedAcc |= RT_BIT_32(pThis->bLoc);
+ /** @todo Abort command. */
+ pThis->bLoc = bLoc;
+ }
+
+ if ( (u64 & TPM_FIFO_LOCALITY_REG_ACCESS_REQUEST_USE)
+ && !(pThis->bmLocReqAcc & RT_BIT_32(bLoc)))
+ {
+ pThis->bmLocReqAcc |= RT_BIT_32(bLoc);
+ if (pThis->bLoc == TPM_NO_LOCALITY_SELECTED)
+ {
+ pThis->bLoc = bLoc; /* Doesn't fire an interrupt. */
+ pThis->bmLocSeizedAcc &= ~RT_BIT_32(bLoc);
+ }
+ }
+
+ if ( (u64 & TPM_FIFO_LOCALITY_REG_ACCESS_ACTIVE)
+ && (pThis->bmLocReqAcc & RT_BIT_32(bLoc)))
+ {
+ pThis->bmLocReqAcc &= ~RT_BIT_32(bLoc);
+ if (pThis->bLoc == bLoc)
+ {
+ pThis->bLoc = TPM_NO_LOCALITY_SELECTED;
+ if (pThis->bmLocReqAcc)
+ tpmLocSelectNext(pDevIns, pThis); /* Select the next locality. */
+ }
+ }
+ break;
+ case TPM_FIFO_LOCALITY_REG_INT_ENABLE:
+ if (bLoc != pThis->bLoc)
+ break;
+ pLoc->uRegIntEn = u32;
+ tpmLocIrqUpdate(pDevIns, pThis, pLoc);
+ break;
+ case TPM_FIFO_LOCALITY_REG_INT_STS:
+ if (bLoc != pThis->bLoc)
+ break;
+ pLoc->uRegIntSts &= ~(u32 & TPM_FIFO_LOCALITY_REG_INT_STS_WR_MASK);
+ tpmLocIrqUpdate(pDevIns, pThis, pLoc);
+ break;
+ case TPM_FIFO_LOCALITY_REG_STS:
+ /*
+ * Writes are ignored completely if the locality being accessed is not the
+ * current active one or if the value has multiple bits set (not a power of two),
+ * see chapter 5.6.12.1.
+ */
+ if ( bLoc != pThis->bLoc
+ || !RT_IS_POWER_OF_TWO(u64))
+ break;
+
+ if ( (u64 & TPM_FIFO_LOCALITY_REG_STS_CMD_RDY)
+ && ( pThis->enmState == DEVTPMSTATE_IDLE
+ || pThis->enmState == DEVTPMSTATE_CMD_COMPLETION))
+ {
+ pThis->enmState = DEVTPMSTATE_READY;
+ pThis->offCmdResp = 0;
+ tpmLocSetIntSts(pDevIns, pThis, pLoc, TPM_FIFO_LOCALITY_REG_INT_STS_CMD_RDY);
+ }
+
+ if ( (u64 & TPM_FIFO_LOCALITY_REG_STS_TPM_GO)
+ && pThis->enmState == DEVTPMSTATE_CMD_RECEPTION)
+ {
+ pThis->enmState = DEVTPMSTATE_CMD_EXEC;
+ rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hTpmCmdTask);
+ }
+
+ if ( (u64 & TPM_FIFO_LOCALITY_REG_STS_RST_ESTABLISHMENT)
+ && pThis->bLoc >= 3
+ && ( pThis->enmState == DEVTPMSTATE_IDLE
+ || pThis->enmState == DEVTPMSTATE_CMD_COMPLETION))
+ {
+#ifndef IN_RING3
+ rc = VINF_IOM_R3_MMIO_WRITE;
+ break;
+#else
+ if (pThisCC->pDrvTpm)
+ {
+ int rc2 = pThisCC->pDrvTpm->pfnResetEstablishedFlag(pThisCC->pDrvTpm, pThis->bLoc);
+ if (RT_SUCCESS(rc2))
+ pThis->fEstablishmentSet = false;
+ else
+ pThis->enmState = DEVTPMSTATE_FATAL_ERROR;
+ }
+ else
+ pThis->fEstablishmentSet = false;
+#endif
+ }
+
+ if ( (u64 & TPM_FIFO_LOCALITY_REG_STS_CMD_CANCEL)
+ && pThis->enmState == DEVTPMSTATE_CMD_EXEC)
+ {
+#ifndef IN_RING3
+ rc = VINF_IOM_R3_MMIO_WRITE;
+ break;
+#else
+ if (pThisCC->pDrvTpm)
+ {
+ pThis->enmState = DEVTPMSTATE_CMD_CANCEL;
+ int rc2 = pThisCC->pDrvTpm->pfnCmdCancel(pThisCC->pDrvTpm);
+ if (RT_FAILURE(rc2))
+ pThis->enmState = DEVTPMSTATE_FATAL_ERROR;
+ }
+#endif
+ }
+
+ break;
+ case TPM_FIFO_LOCALITY_REG_INT_VEC:
+ case TPM_FIFO_LOCALITY_REG_IF_CAP:
+ case TPM_FIFO_LOCALITY_REG_DID_VID:
+ case TPM_FIFO_LOCALITY_REG_RID:
+ default: /* Ignore. */
+ break;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Read from a CRB interface register.
+ *
+ * @returns VBox strict status code.
+ * @param pDevIns Pointer to the PDM device instance data.
+ * @param pThis Pointer to the shared TPM device.
+ * @param pLoc The locality state being read from.
+ * @param bLoc The locality index.
+ * @param uReg The register offset being accessed.
+ * @param pu64 Where to store the read data.
+ * @param cb Size of the read in bytes.
+ */
+static VBOXSTRICTRC tpmMmioCrbRead(PPDMDEVINS pDevIns, PDEVTPM pThis, PDEVTPMLOCALITY pLoc,
+ uint8_t bLoc, uint32_t uReg, uint64_t *pu64, size_t cb)
+{
+ RT_NOREF(pDevIns);
+
+ /* Special path for the data buffer. */
+ if ( uReg >= TPM_CRB_LOCALITY_REG_DATA_BUFFER
+ && uReg < TPM_CRB_LOCALITY_REG_DATA_BUFFER + pThis->cbCmdResp
+ && bLoc == pThis->bLoc
+ && pThis->enmState == DEVTPMSTATE_CMD_COMPLETION)
+ {
+ memcpy(pu64, &pThis->abCmdResp[uReg - TPM_CRB_LOCALITY_REG_DATA_BUFFER], cb);
+ return VINF_SUCCESS;
+ }
+
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ uint64_t u64 = UINT64_MAX;
+ switch (uReg)
+ {
+ case TPM_CRB_LOCALITY_REG_STATE:
+ u64 = TPM_CRB_LOCALITY_REG_STATE_VALID
+ | ( pThis->bLoc != TPM_NO_LOCALITY_SELECTED
+ ? TPM_CRB_LOCALITY_REG_STATE_ACTIVE_LOC_SET(pThis->bLoc) | TPM_CRB_LOCALITY_REG_STATE_LOC_ASSIGNED
+ : TPM_CRB_LOCALITY_REG_STATE_ACTIVE_LOC_SET(0));
+ if (pThis->fEstablishmentSet)
+ u64 |= TPM_CRB_LOCALITY_REG_ESTABLISHMENT;
+ break;
+ case TPM_CRB_LOCALITY_REG_STS:
+ u64 = pThis->bLoc == bLoc
+ ? TPM_CRB_LOCALITY_REG_STS_GRANTED
+ : 0;
+ u64 |= pThis->bmLocSeizedAcc & RT_BIT_32(bLoc)
+ ? TPM_CRB_LOCALITY_REG_STS_SEIZED
+ : 0;
+ break;
+ case TPM_CRB_LOCALITY_REG_INTF_ID:
+ u64 = TPM_CRB_LOCALITY_REG_INTF_ID_IF_TYPE_SET(TPM_CRB_LOCALITY_REG_INTF_ID_IF_TYPE_CRB)
+ | TPM_CRB_LOCALITY_REG_INTF_ID_IF_VERS_SET(TPM_CRB_LOCALITY_REG_INTF_ID_IF_VERS_CRB)
+ | TPM_CRB_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_SET(TPM_CRB_LOCALITY_REG_INTF_ID_CAP_DATA_XFER_SZ_64B)
+ | TPM_CRB_LOCALITY_REG_INTF_ID_CAP_CRB
+ | TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_GET(TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_CRB)
+ | TPM_CRB_LOCALITY_REG_INTF_ID_IF_SEL_LOCK
+ | TPM_CRB_LOCALITY_REG_INTF_ID_RID_SET(pThis->bRevId)
+ | TPM_CRB_LOCALITY_REG_INTF_ID_VID_SET(pThis->uVenId)
+ | TPM_CRB_LOCALITY_REG_INTF_ID_DID_SET(pThis->uDevId);
+
+ if (pThis->fLocChangeSup) /* Only advertise the locality capability if the driver below supports it. */
+ u64 |= TPM_CRB_LOCALITY_REG_INTF_ID_CAP_LOCALITY;
+
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_REQ:
+ if (bLoc != pThis->bLoc)
+ break;
+ /*
+ * Command ready and go idle are always 0 upon read
+ * as we don't need time to transition to this state
+ * when written by the guest.
+ */
+ u64 = 0;
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_STS:
+ if (bLoc != pThis->bLoc)
+ break;
+ if (pThis->enmState == DEVTPMSTATE_FATAL_ERROR)
+ u64 = TPM_CRB_LOCALITY_REG_CTRL_STS_TPM_FATAL_ERR;
+ else if (pThis->enmState == DEVTPMSTATE_IDLE)
+ u64 = TPM_CRB_LOCALITY_REG_CTRL_STS_TPM_IDLE;
+ else
+ u64 = 0;
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_CANCEL:
+ if (bLoc != pThis->bLoc)
+ break;
+ if (pThis->enmState == DEVTPMSTATE_CMD_CANCEL)
+ u64 = 0x1;
+ else
+ u64 = 0;
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_START:
+ if (bLoc != pThis->bLoc)
+ break;
+ if (pThis->enmState == DEVTPMSTATE_CMD_EXEC)
+ u64 = 0x1;
+ else
+ u64 = 0;
+ break;
+ case TPM_CRB_LOCALITY_REG_INT_ENABLE:
+ u64 = pLoc->uRegIntEn;
+ break;
+ case TPM_CRB_LOCALITY_REG_INT_STS:
+ u64 = pLoc->uRegIntSts;
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_CMD_LADDR:
+ u64 = pThis->GCPhysMmio + (bLoc * TPM_LOCALITY_MMIO_SIZE) + TPM_CRB_LOCALITY_REG_DATA_BUFFER;
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_CMD_HADDR:
+ u64 = (pThis->GCPhysMmio + (bLoc * TPM_LOCALITY_MMIO_SIZE) + TPM_CRB_LOCALITY_REG_DATA_BUFFER) >> 32;
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_CMD_SZ:
+ case TPM_CRB_LOCALITY_REG_CTRL_RSP_SZ:
+ u64 = pThis->cbCmdResp;
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_RSP_ADDR:
+ u64 = pThis->GCPhysMmio + (bLoc * TPM_LOCALITY_MMIO_SIZE) + TPM_CRB_LOCALITY_REG_DATA_BUFFER;
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL: /* Writeonly */
+ u64 = 0;
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_EXT:
+ default:
+ break; /* Return ~0 */
+ }
+
+ *pu64 = u64;
+ return rc;
+}
+
+
+/**
+ * Read to a CRB interface register.
+ *
+ * @returns VBox strict status code.
+ * @param pDevIns Pointer to the PDM device instance data.
+ * @param pThis Pointer to the shared TPM device.
+ * @param pLoc The locality state being written to.
+ * @param bLoc The locality index.
+ * @param uReg The register offset being accessed.
+ * @param u64 The value to write.
+ * @param cb Size of the write in bytes.
+ */
+static VBOXSTRICTRC tpmMmioCrbWrite(PPDMDEVINS pDevIns, PDEVTPM pThis, PDEVTPMLOCALITY pLoc,
+ uint8_t bLoc, uint32_t uReg, uint64_t u64, size_t cb)
+{
+#ifdef IN_RING3
+ PDEVTPMR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVTPMR3);
+#endif
+
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ uint32_t u32 = (uint32_t)u64;
+
+ /* Special path for the data buffer. */
+ if ( uReg >= TPM_CRB_LOCALITY_REG_DATA_BUFFER
+ && uReg < TPM_CRB_LOCALITY_REG_DATA_BUFFER + pThis->cbCmdResp
+ && bLoc == pThis->bLoc
+ && ( pThis->enmState == DEVTPMSTATE_READY
+ || pThis->enmState == DEVTPMSTATE_CMD_RECEPTION))
+ {
+ pThis->enmState = DEVTPMSTATE_CMD_RECEPTION;
+ memcpy(&pThis->abCmdResp[uReg - TPM_CRB_LOCALITY_REG_DATA_BUFFER], &u64, cb);
+ return VINF_SUCCESS;
+ }
+
+ switch (uReg)
+ {
+ case TPM_CRB_LOCALITY_REG_CTRL:
+ {
+ /* See chapter 6.5.3.2.2.1. */
+ if ( (u64 & TPM_CRB_LOCALITY_REG_CTRL_RST_ESTABLISHMENT)
+ && pThis->bLoc >= 3
+ && ( pThis->enmState == DEVTPMSTATE_IDLE
+ || pThis->enmState == DEVTPMSTATE_CMD_COMPLETION))
+ {
+#ifndef IN_RING3
+ rc = VINF_IOM_R3_MMIO_WRITE;
+ break;
+#else
+ if (pThisCC->pDrvTpm)
+ {
+ int rc2 = pThisCC->pDrvTpm->pfnResetEstablishedFlag(pThisCC->pDrvTpm, pThis->bLoc);
+ if (RT_SUCCESS(rc2))
+ pThis->fEstablishmentSet = false;
+ else
+ pThis->enmState = DEVTPMSTATE_FATAL_ERROR;
+ }
+ else
+ pThis->fEstablishmentSet = false;
+#endif
+ }
+
+ /*
+ * The following three checks should be mutually exclusive as the writer shouldn't
+ * request, relinquish and seize access in the same write.
+ */
+ /* Seize access only if this locality has a higher priority than the currently selected one. */
+ if ( (u64 & TPM_CRB_LOCALITY_REG_CTRL_SEIZE)
+ && pThis->bLoc != TPM_NO_LOCALITY_SELECTED
+ && bLoc > pThis->bLoc)
+ {
+ if (pThis->enmState == DEVTPMSTATE_CMD_EXEC)
+ {
+#ifndef IN_RING3
+ rc = VINF_IOM_R3_MMIO_WRITE;
+ break;
+#else
+ pThis->enmState = DEVTPMSTATE_CMD_CANCEL;
+ if (pThisCC->pDrvTpm)
+ {
+ int rc2 = pThisCC->pDrvTpm->pfnCmdCancel(pThisCC->pDrvTpm);
+ if (RT_FAILURE(rc2))
+ pThis->enmState = DEVTPMSTATE_FATAL_ERROR;
+ else
+ {
+ pThis->enmState = DEVTPMSTATE_CMD_COMPLETION;
+ tpmLocSetIntSts(pDevIns, pThis, pLoc, TPM_CRB_LOCALITY_REG_INT_STS_START);
+ }
+ }
+#endif
+ }
+
+ pThis->bmLocSeizedAcc |= RT_BIT_32(pThis->bLoc);
+ pThis->bLoc = bLoc;
+ }
+
+ if ( (u64 & TPM_CRB_LOCALITY_REG_CTRL_REQ_ACCESS)
+ && !(pThis->bmLocReqAcc & RT_BIT_32(bLoc)))
+ {
+ pThis->bmLocReqAcc |= RT_BIT_32(bLoc);
+ if (pThis->bLoc == TPM_NO_LOCALITY_SELECTED)
+ {
+ pThis->bLoc = bLoc; /* Doesn't fire an interrupt. */
+ pThis->bmLocSeizedAcc &= ~RT_BIT_32(bLoc);
+ }
+ }
+
+ if ( (u64 & TPM_CRB_LOCALITY_REG_CTRL_RELINQUISH)
+ && (pThis->bmLocReqAcc & RT_BIT_32(bLoc)))
+ {
+ pThis->bmLocReqAcc &= ~RT_BIT_32(bLoc);
+ if (pThis->bLoc == bLoc)
+ {
+ pThis->bLoc = TPM_NO_LOCALITY_SELECTED;
+ if (pThis->bmLocReqAcc)
+ tpmLocSelectNext(pDevIns, pThis); /* Select the next locality. */
+ }
+ }
+ break;
+ }
+ case TPM_CRB_LOCALITY_REG_CTRL_REQ:
+ if ( bLoc != pThis->bLoc
+ || !RT_IS_POWER_OF_TWO(u32)) /* Ignore if multiple bits are set. */
+ break;
+ if ( (u32 & TPM_CRB_LOCALITY_REG_CTRL_REQ_CMD_RDY)
+ && ( pThis->enmState == DEVTPMSTATE_IDLE
+ || pThis->enmState == DEVTPMSTATE_CMD_COMPLETION))
+ {
+ pThis->enmState = DEVTPMSTATE_READY;
+ tpmLocSetIntSts(pDevIns, pThis, pLoc, TPM_CRB_LOCALITY_REG_INT_STS_CMD_RDY);
+ }
+ else if ( (u32 & TPM_CRB_LOCALITY_REG_CTRL_REQ_IDLE)
+ && pThis->enmState != DEVTPMSTATE_CMD_EXEC)
+ {
+ /* Invalidate the command/response buffer. */
+ RT_ZERO(pThis->abCmdResp);
+ pThis->offCmdResp = 0;
+ pThis->enmState = DEVTPMSTATE_IDLE;
+ }
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_CANCEL:
+ if (bLoc != pThis->bLoc)
+ break;
+ if ( pThis->enmState == DEVTPMSTATE_CMD_EXEC
+ && u32 == 0x1)
+ {
+#ifndef IN_RING3
+ rc = VINF_IOM_R3_MMIO_WRITE;
+ break;
+#else
+ pThis->enmState = DEVTPMSTATE_CMD_CANCEL;
+ if (pThisCC->pDrvTpm)
+ {
+ int rc2 = pThisCC->pDrvTpm->pfnCmdCancel(pThisCC->pDrvTpm);
+ if (RT_FAILURE(rc2))
+ pThis->enmState = DEVTPMSTATE_FATAL_ERROR;
+ else
+ {
+ pThis->enmState = DEVTPMSTATE_CMD_COMPLETION;
+ tpmLocSetIntSts(pDevIns, pThis, pLoc, TPM_CRB_LOCALITY_REG_INT_STS_START);
+ }
+ }
+#endif
+ }
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_START:
+ if (bLoc != pThis->bLoc)
+ break;
+ if ( pThis->enmState == DEVTPMSTATE_CMD_RECEPTION
+ && u32 == 0x1)
+ {
+ pThis->enmState = DEVTPMSTATE_CMD_EXEC;
+ rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hTpmCmdTask);
+ }
+ break;
+ case TPM_CRB_LOCALITY_REG_INT_ENABLE:
+ pLoc->uRegIntEn = u32;
+ tpmLocIrqUpdate(pDevIns, pThis, pLoc);
+ break;
+ case TPM_CRB_LOCALITY_REG_INT_STS:
+ pLoc->uRegIntSts &= ~u32;
+ tpmLocIrqUpdate(pDevIns, pThis, pLoc);
+ break;
+ case TPM_CRB_LOCALITY_REG_CTRL_EXT: /* Not implemented. */
+ case TPM_CRB_LOCALITY_REG_STATE: /* Readonly */
+ case TPM_CRB_LOCALITY_REG_INTF_ID:
+ case TPM_CRB_LOCALITY_REG_CTRL_STS:
+ case TPM_CRB_LOCALITY_REG_CTRL_CMD_LADDR:
+ case TPM_CRB_LOCALITY_REG_CTRL_CMD_HADDR:
+ case TPM_CRB_LOCALITY_REG_CTRL_CMD_SZ:
+ case TPM_CRB_LOCALITY_REG_CTRL_RSP_SZ:
+ case TPM_CRB_LOCALITY_REG_CTRL_RSP_ADDR:
+ default: /* Ignore. */
+ break;
+ }
+
+ return rc;
+}
+
+
+/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWREAD}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) tpmMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
+{
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+ RT_NOREF(pvUser);
+
+ AssertReturn(cb <= sizeof(uint64_t), VERR_INTERNAL_ERROR);
+
+ RTGCPHYS offAligned = off & ~UINT64_C(0x3);
+ uint8_t cBitsShift = (off & 0x3) * 8;
+
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ uint32_t uReg = tpmGetRegisterFromOffset(offAligned);
+ uint8_t bLoc = tpmGetLocalityFromOffset(offAligned);
+ PDEVTPMLOCALITY pLoc = &pThis->aLoc[bLoc];
+
+ uint64_t u64;
+ if (pThis->fCrb)
+ rc = tpmMmioCrbRead(pDevIns, pThis, pLoc, bLoc, uReg, &u64, cb);
+ else
+ rc = tpmMmioFifoRead(pDevIns, pThis, pLoc, bLoc, uReg, &u64, cb);
+
+ LogFlowFunc((": %RGp %#x %#llx\n", off, cb, u64));
+
+ if (rc == VINF_SUCCESS)
+ {
+ switch (cb)
+ {
+ case 1: *(uint8_t *)pv = (uint8_t)(u64 >> cBitsShift); break;
+ case 2: *(uint16_t *)pv = (uint16_t)(u64 >> cBitsShift); break;
+ case 4: *(uint32_t *)pv = (uint32_t)(u64 >> cBitsShift); break;
+ case 8: *(uint64_t *)pv = u64; break;
+ default: AssertFailedBreakStmt(rc = VERR_INTERNAL_ERROR);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWWRITE}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) tpmMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
+{
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+ RT_NOREF(pvUser);
+
+ Assert(!(off & (cb - 1)));
+
+ uint64_t u64;
+ switch (cb)
+ {
+ case 1: u64 = *(const uint8_t *)pv; break;
+ case 2: u64 = *(const uint16_t *)pv; break;
+ case 4: u64 = *(const uint32_t *)pv; break;
+ case 8: u64 = *(const uint64_t *)pv; break;
+ default: AssertFailedReturn(VERR_INTERNAL_ERROR);
+ }
+
+ LogFlowFunc((": %RGp %#llx\n", off, u64));
+
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ uint32_t uReg = tpmGetRegisterFromOffset(off);
+ uint8_t bLoc = tpmGetLocalityFromOffset(off);
+ PDEVTPMLOCALITY pLoc = &pThis->aLoc[bLoc];
+
+ if (pThis->fCrb)
+ rc = tpmMmioCrbWrite(pDevIns, pThis, pLoc, bLoc, uReg, u64, cb);
+ else
+ rc = tpmMmioFifoWrite(pDevIns, pThis, pLoc, bLoc, uReg, u64, cb);
+
+ return rc;
+}
+
+
+#ifdef IN_RING3
+
+/**
+ * @callback_method_impl{FNPDMTASKDEV, Execute a command in ring-3}
+ */
+static DECLCALLBACK(void) tpmR3CmdExecWorker(PPDMDEVINS pDevIns, void *pvUser)
+{
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+ PDEVTPMR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVTPMR3);
+ RT_NOREF(pvUser);
+ LogFlowFunc(("\n"));
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
+
+ if (pThisCC->pDrvTpm)
+ {
+ size_t cbCmd = RTTpmReqGetSz((PCTPMREQHDR)&pThis->abCmdResp[0]);
+ int rc = pThisCC->pDrvTpm->pfnCmdExec(pThisCC->pDrvTpm, pThis->bLoc, &pThis->abCmdResp[0], cbCmd,
+ &pThis->abCmdResp[0], sizeof(pThis->abCmdResp));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->enmState = DEVTPMSTATE_CMD_COMPLETION;
+ pThis->offCmdResp = 0;
+ if (pThis->fCrb)
+ tpmLocSetIntSts(pThisCC->pDevIns, pThis, &pThis->aLoc[pThis->bLoc], TPM_CRB_LOCALITY_REG_INT_STS_START);
+ else
+ tpmLocSetIntSts(pThisCC->pDevIns, pThis, &pThis->aLoc[pThis->bLoc], TPM_FIFO_LOCALITY_REG_INT_STS_DATA_AVAIL | TPM_FIFO_LOCALITY_REG_INT_STS_STS_VALID);
+ }
+ else
+ {
+ /* Set fatal error. */
+ pThis->enmState = DEVTPMSTATE_FATAL_ERROR;
+ }
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
+}
+
+
+/**
+ * Resets the shared hardware TPM state.
+ *
+ * @param pThis Pointer to the shared TPM device.
+ */
+static void tpmR3HwReset(PDEVTPM pThis)
+{
+ pThis->enmState = DEVTPMSTATE_IDLE;
+ pThis->bLoc = TPM_NO_LOCALITY_SELECTED;
+ pThis->bmLocReqAcc = 0;
+ pThis->bmLocSeizedAcc = 0;
+ pThis->offCmdResp = 0;
+ RT_ZERO(pThis->abCmdResp);
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aLoc); i++)
+ {
+ PDEVTPMLOCALITY pLoc = &pThis->aLoc[i];
+ pLoc->uRegIntEn = 0;
+ pLoc->uRegIntSts = 0;
+ }
+}
+
+
+/* -=-=-=-=-=-=-=-=- Saved State -=-=-=-=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNSSMDEVLIVEEXEC}
+ */
+static DECLCALLBACK(int) tpmR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+{
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ RT_NOREF(uPass);
+
+ /* Save the part of the config used for verification purposes when restoring. */
+ pHlp->pfnSSMPutGCPhys(pSSM, pThis->GCPhysMmio);
+ pHlp->pfnSSMPutU16( pSSM, pThis->uVenId);
+ pHlp->pfnSSMPutU16( pSSM, pThis->uDevId);
+ pHlp->pfnSSMPutU8( pSSM, pThis->bRevId);
+ pHlp->pfnSSMPutU8( pSSM, pThis->uIrq);
+ pHlp->pfnSSMPutBool( pSSM, pThis->fLocChangeSup);
+ pHlp->pfnSSMPutU32( pSSM, (uint32_t)pThis->enmTpmVers);
+ pHlp->pfnSSMPutU32( pSSM, pThis->cbCmdResp);
+
+ return VINF_SSM_DONT_CALL_AGAIN;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) tpmR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ tpmR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
+
+ int rc = pHlp->pfnSSMPutStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aTpmFields[0], NULL);
+ AssertRCReturn(rc, rc);
+
+ return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX); /* sanity/terminator */
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) tpmR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ bool f;
+ RTGCPHYS GCPhysMmio;
+ TPMVERSION enmTpmVers;
+
+ Assert(uPass == SSM_PASS_FINAL); RT_NOREF(uPass);
+ AssertMsgReturn(uVersion == TPM_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);
+
+ rc = pHlp->pfnSSMGetU16(pSSM, &u16);
+ AssertRCReturn(rc, rc);
+ if (u16 != pThis->uVenId)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
+ N_("Config mismatch - saved uVenId=%#RX16; configured uVenId=%#RX16"),
+ u16, pThis->uVenId);
+
+ rc = pHlp->pfnSSMGetU16(pSSM, &u16);
+ AssertRCReturn(rc, rc);
+ if (u16 != pThis->uDevId)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
+ N_("Config mismatch - saved uDevId=%#RX16; configured uDevId=%#RX16"),
+ u16, pThis->uDevId);
+
+ rc = pHlp->pfnSSMGetU8(pSSM, &u8);
+ AssertRCReturn(rc, rc);
+ if (u8 != pThis->bRevId)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
+ N_("Config mismatch - saved bRevId=%#RX8; configured bDevId=%#RX8"),
+ u8, pThis->bRevId);
+
+ rc = pHlp->pfnSSMGetU8(pSSM, &u8);
+ AssertRCReturn(rc, rc);
+ if (u8 != pThis->uIrq)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
+ N_("Config mismatch - saved uIrq=%#RX8; configured uIrq=%#RX8"),
+ u8, pThis->uIrq);
+
+ rc = pHlp->pfnSSMGetBool(pSSM, &f);
+ AssertRCReturn(rc, rc);
+ if (f != pThis->fLocChangeSup)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
+ N_("Config mismatch - saved fLocChangeSup=%RTbool; configured fLocChangeSup=%RTbool"),
+ f, pThis->fLocChangeSup);
+
+ rc = pHlp->pfnSSMGetU32(pSSM, (uint32_t *)&enmTpmVers);
+ AssertRCReturn(rc, rc);
+ if (enmTpmVers != pThis->enmTpmVers)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
+ N_("Config mismatch - saved enmTpmVers=%RU32; configured enmTpmVers=%RU32"),
+ enmTpmVers, pThis->enmTpmVers);
+
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+ if (u32 != pThis->cbCmdResp)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
+ N_("Config mismatch - saved cbCmdResp=%RU32; configured cbCmdResp=%RU32"),
+ u32, pThis->cbCmdResp);
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ rc = pHlp->pfnSSMGetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aTpmFields[0], NULL);
+
+ /* The marker. */
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+ AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+
+ /* Verify device state sanity. */
+ AssertLogRelMsgReturn( pThis->enmState > DEVTPMSTATE_INVALID
+ && pThis->enmState <= DEVTPMSTATE_LAST_VALID,
+ ("Invalid TPM state loaded from saved state: %#x\n", pThis->enmState),
+ VERR_SSM_UNEXPECTED_DATA);
+
+ AssertLogRelMsgReturn(pThis->offCmdResp <= pThis->cbCmdResp,
+ ("Invalid TPM command/response buffer offset loaded from saved state: %#x\n", pThis->offCmdResp),
+ VERR_SSM_UNEXPECTED_DATA);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=-=-=-=-=- PDMIBASE -=-=-=-=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) tpmR3QueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PDEVTPMCC pThisCC = RT_FROM_MEMBER(pInterface, DEVTPMCC, IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
+ //PDMIBASE_RETURN_INTERFACE(pszIID, PDMITPMPORT, &pThisCC->ITpmPort);
+ return NULL;
+}
+
+
+/* -=-=-=-=-=-=-=-=- PDMDEVREG -=-=-=-=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnPowerOn}
+ */
+static DECLCALLBACK(void) tpmR3PowerOn(PPDMDEVINS pDevIns)
+{
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+ PDEVTPMCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVTPMCC);
+
+ if (pThisCC->pDrvTpm)
+ pThis->fEstablishmentSet = pThisCC->pDrvTpm->pfnGetEstablishedFlag(pThisCC->pDrvTpm);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) tpmR3Reset(PPDMDEVINS pDevIns)
+{
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+ PDEVTPMCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVTPMCC);
+
+ tpmR3HwReset(pThis);
+ if (pThisCC->pDrvTpm)
+ pThis->fEstablishmentSet = pThisCC->pDrvTpm->pfnGetEstablishedFlag(pThisCC->pDrvTpm);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) tpmR3Destruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+
+ /** @todo */
+ RT_NOREF(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) tpmR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+ PDEVTPMCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVTPMCC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ int rc;
+
+ RT_NOREF(iInstance);
+
+ pThis->hTpmCmdTask = NIL_PDMTASKHANDLE;
+
+ pThisCC->pDevIns = pDevIns;
+
+ /* IBase */
+ pThisCC->IBase.pfnQueryInterface = tpmR3QueryInterface;
+
+ /*
+ * Validate and read the configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Irq"
+ "|MmioBase"
+ "|VendorId"
+ "|DeviceId"
+ "|RevisionId"
+ "|Crb",
+ "");
+
+ rc = pHlp->pfnCFGMQueryU8Def(pCfg, "Irq", &pThis->uIrq, 10);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Irq\" value"));
+
+ rc = pHlp->pfnCFGMQueryU64Def(pCfg, "MmioBase", &pThis->GCPhysMmio, TPM_MMIO_BASE_DEFAULT);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"MmioBase\" value"));
+
+ rc = pHlp->pfnCFGMQueryU16Def(pCfg, "VendorId", &pThis->uDevId, TPM_VID_DEFAULT);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"VendorId\" value"));
+
+ rc = pHlp->pfnCFGMQueryU16Def(pCfg, "DeviceId", &pThis->uDevId, TPM_DID_DEFAULT);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"DeviceId\" value"));
+
+ rc = pHlp->pfnCFGMQueryU8Def(pCfg, "RevisionId", &pThis->bRevId, TPM_RID_DEFAULT);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"RevisionId\" value"));
+
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Crb", &pThis->fCrb, false);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Crb\" value"));
+
+ /*
+ * Register the MMIO range, PDM API requests page aligned
+ * addresses and sizes.
+ */
+ rc = PDMDevHlpMmioCreateAndMap(pDevIns, pThis->GCPhysMmio, TPM_MMIO_SIZE, tpmMmioWrite, tpmMmioRead,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ "TPM MMIO", &pThis->hMmio);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Attach any TPM driver below.
+ */
+ rc = PDMDevHlpDriverAttach(pDevIns, 0 /*iLUN*/, &pThisCC->IBase, &pThisCC->pDrvBase, "TPM");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrvTpm = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMITPMCONNECTOR);
+ AssertLogRelMsgReturn(pThisCC->pDrvTpm, ("TPM#%d: Driver is missing the TPM interface.\n", iInstance), VERR_PDM_MISSING_INTERFACE);
+
+ pThis->cbCmdResp = RT_MIN(pThisCC->pDrvTpm->pfnGetBufferSize(pThisCC->pDrvTpm), TPM_DATA_BUFFER_SIZE_MAX);
+ pThis->fLocChangeSup = pThisCC->pDrvTpm->pfnGetLocalityMax(pThisCC->pDrvTpm) > 0;
+
+ pThis->enmTpmVers = pThisCC->pDrvTpm->pfnGetVersion(pThisCC->pDrvTpm);
+ if (pThis->enmTpmVers == TPMVERSION_UNKNOWN)
+ return PDMDEV_SET_ERROR(pDevIns, VERR_NOT_SUPPORTED, N_("The emulated TPM version is not supported"));
+ }
+ else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+ {
+ pThis->fLocChangeSup = false;
+ pThis->fEstablishmentSet = false;
+ pThis->cbCmdResp = TPM_DATA_BUFFER_SIZE_MAX;
+
+ pThisCC->pDrvBase = NULL;
+ pThisCC->pDrvTpm = NULL;
+ LogRel(("TPM#%d: no unit\n", iInstance));
+ }
+ else
+ AssertLogRelMsgRCReturn(rc, ("TPM#%d: Failed to attach to TPM driver. rc=%Rrc\n", iInstance, rc), rc);
+
+ /* Create task for executing requests in ring-3. */
+ rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "TPMCmdWrk",
+ tpmR3CmdExecWorker, NULL /*pvUser*/, &pThis->hTpmCmdTask);
+ AssertRCReturn(rc,rc);
+
+ /*
+ * Saved state.
+ */
+ rc = PDMDevHlpSSMRegister3(pDevIns, TPM_SAVED_STATE_VERSION, sizeof(*pThis),
+ tpmR3LiveExec, tpmR3SaveExec, tpmR3LoadExec);
+ AssertRCReturn(rc, rc);
+
+ tpmR3HwReset(pThis);
+ return VINF_SUCCESS;
+}
+
+#else /* !IN_RING3 */
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) tpmRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVTPM pThis = PDMDEVINS_2_DATA(pDevIns, PDEVTPM);
+
+ int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, tpmMmioWrite, tpmMmioRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_RING3 */
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DeviceTpm =
+{
+ /* .u32Version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "tpm",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_SERIAL,
+ /* .cMaxInstances = */ 1,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(DEVTPM),
+ /* .cbInstanceCC = */ sizeof(DEVTPMCC),
+ /* .cbInstanceRC = */ sizeof(DEVTPMRC),
+ /* .cMaxPciDevices = */ 0,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "Trusted Platform Module",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ tpmR3Construct,
+ /* .pfnDestruct = */ tpmR3Destruct,
+ /* .pfnRelocate = */ NULL,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ tpmR3PowerOn,
+ /* .pfnReset = */ tpmR3Reset,
+ /* .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 = */ tpmRZConstruct,
+ /* .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 = */ tpmRZConstruct,
+ /* .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
+};
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
+
diff --git a/src/VBox/Devices/Security/DrvTpmEmu.cpp b/src/VBox/Devices/Security/DrvTpmEmu.cpp
new file mode 100644
index 00000000..8cadd338
--- /dev/null
+++ b/src/VBox/Devices/Security/DrvTpmEmu.cpp
@@ -0,0 +1,1021 @@
+/* $Id: DrvTpmEmu.cpp $ */
+/** @file
+ * TPM emulator using a TCP/socket interface to talk to swtpm (https://github.com/stefanberger/swtpm).
+ */
+
+/*
+ * Copyright (C) 2021-2023 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_DRV_TPM_EMU
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmtpmifs.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/stream.h>
+#include <iprt/alloc.h>
+#include <iprt/pipe.h>
+#include <iprt/poll.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/socket.h>
+#include <iprt/tcp.h>
+#include <iprt/uuid.h>
+#include <iprt/json.h>
+
+#include <iprt/formats/tpm.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/** @name Protocol definitions to communicate with swtpm, taken from https://github.com/stefanberger/swtpm/blob/master/include/swtpm/tpm_ioctl.h
+ * @{ */
+/**
+ * Commands going over the control channel (big endian).
+ */
+typedef enum SWTPMCMD
+{
+ /** Not used. */
+ SWTPMCMD_INVALID = 0,
+ SWTPMCMD_GET_CAPABILITY,
+ SWTPMCMD_INIT,
+ SWTPMCMD_SHUTDOWN,
+ SWTPMCMD_GET_TPMESTABLISHED,
+ SWTPMCMD_SET_LOCALITY,
+ SWTPMCMD_HASH_START,
+ SWTPMCMD_HASH_DATA,
+ SWTPMCMD_HASH_END,
+ SWTPMCMD_CANCEL_TPM_CMD,
+ SWTPMCMD_STORE_VOLATILE,
+ SWTPMCMD_RESET_TPMESTABLISHED,
+ SWTPMCMD_GET_STATEBLOB,
+ SWTPMCMD_SET_STATEBLOB,
+ SWTPMCMD_STOP,
+ SWTPMCMD_GET_CONFIG,
+ SWTPMCMD_SET_DATAFD,
+ SWTPMCMD_SET_BUFFERSIZE,
+ SWTPMCMD_GET_INFO,
+ /** Blow the enum up to a 32bit size. */
+ SWTPMCMD_32BIT_HACK = 0x7fffffff
+} SWTPMCMD;
+
+
+/**
+ * Command/Response header.
+ */
+typedef union SWTPMHDR
+{
+ /** The command opcode. */
+ SWTPMCMD enmCmd;
+ /** The response result. */
+ uint32_t u32Resp;
+} SWTPMHDR;
+AssertCompileSize(SWTPMHDR, sizeof(uint32_t));
+/** Pointer to a command/response header. */
+typedef SWTPMHDR *PSWTPMHDR;
+/** Pointer to a const command/response header. */
+typedef const SWTPMHDR *PCSWTPMHDR;
+
+
+/**
+ * Additional command data for SWTPMCMD_INIT.
+ */
+typedef struct SWTPMCMDTPMINIT
+{
+ /** Additional flags */
+ uint32_t u32Flags;
+} SWTPMCMDTPMINIT;
+/** Pointer to a command data struct for SWTPMCMD_INIT. */
+typedef SWTPMCMDTPMINIT *PSWTPMCMDTPMINIT;
+/** Pointer to a const command data struct for SWTPMCMD_INIT. */
+typedef const SWTPMCMDTPMINIT *PCSWTPMCMDTPMINIT;
+
+
+/** @name Capabilities as returned by SWTPMCMD_INIT.
+ * @{ */
+#define SWTPMCMD_INIT_F_DELETE_VOLATILE RT_BIT_32(0);
+/** @} */
+
+
+/**
+ * Response data for a SWTPMCMD_GET_CAPABILITY command.
+ */
+typedef struct SWTPMRESPGETCAPABILITY
+{
+ /** The capabilities supported. */
+ uint32_t u32Caps;
+} SWTPMRESPGETCAPABILITY;
+/** Pointer to a response data struct for SWTPMCMD_GET_CAPABILITY. */
+typedef SWTPMRESPGETCAPABILITY *PSWTPMRESPGETCAPABILITY;
+/** Pointer to a const response data struct for SWTPMCMD_GET_CAPABILITY. */
+typedef const SWTPMRESPGETCAPABILITY *PCSWTPMRESPGETCAPABILITY;
+
+
+/** @name Capabilities as returned by SWTPMCMD_GET_CAPABILITY.
+ * @{ */
+#define SWTPM_CAP_INIT RT_BIT_32(0)
+#define SWTPM_CAP_SHUTDOWN RT_BIT_32(1)
+#define SWTPM_CAP_GET_TPMESTABLISHED RT_BIT_32(2)
+#define SWTPM_CAP_SET_LOCALITY RT_BIT_32(3)
+#define SWTPM_CAP_HASHING RT_BIT_32(4)
+#define SWTPM_CAP_CANCEL_TPM_CMD RT_BIT_32(5)
+#define SWTPM_CAP_STORE_VOLATILE RT_BIT_32(6)
+#define SWTPM_CAP_RESET_TPMESTABLISHED RT_BIT_32(7)
+#define SWTPM_CAP_GET_STATEBLOB RT_BIT_32(8)
+#define SWTPM_CAP_SET_STATEBLOB RT_BIT_32(9)
+#define SWTPM_CAP_STOP RT_BIT_32(10)
+#define SWTPM_CAP_GET_CONFIG RT_BIT_32(11)
+#define SWTPM_CAP_SET_DATAFD RT_BIT_32(12)
+#define SWTPM_CAP_SET_BUFFERSIZE RT_BIT_32(13)
+#define SWTPM_CAP_GET_INFO RT_BIT_32(14)
+#define SWTPM_CAP_SEND_COMMAND_HEADER RT_BIT_32(15)
+/** @} */
+
+
+/**
+ * Additional command data for SWTPMCMD_SET_LOCALITY.
+ */
+typedef struct SWTPMCMDSETLOCALITY
+{
+ /** The locality to set */
+ uint8_t bLoc;
+} SWTPMCMDSETLOCALITY;
+/** Pointer to a command data struct for SWTPMCMD_SET_LOCALITY. */
+typedef SWTPMCMDSETLOCALITY *PSWTPMCMDSETLOCALITY;
+/** Pointer to a const command data struct for SWTPMCMD_SET_LOCALITY. */
+typedef const SWTPMCMDSETLOCALITY *PCSWTPMCMDSETLOCALITY;
+
+
+/**
+ * Additional command data for SWTPMCMD_GET_CONFIG.
+ */
+typedef struct SWTPMCMDGETCONFIG
+{
+ /** Combination of SWTPM_GET_CONFIG_F_XXX. */
+ uint64_t u64Flags;
+ /** The offset where to start reading from. */
+ uint32_t u32Offset;
+ /** Some padding to a 8 byte alignment. */
+ uint32_t u32Padding;
+} SWTPMCMDGETCONFIG;
+/** Pointer to a response data struct for SWTPMCMD_GET_CONFIG. */
+typedef SWTPMCMDGETCONFIG *PSWTPMCMDGETCONFIG;
+/** Pointer to a const response data struct for SWTPMCMD_GET_CONFIG. */
+typedef const SWTPMCMDGETCONFIG *PCSWTPMCMDGETCONFIG;
+
+
+/** @name Flags for SWTPMCMDGETCONFIG::u64Flags.
+ * @{ */
+/** Return the TPM specification JSON object. */
+#define SWTPM_GET_CONFIG_F_TPM_SPECIFICATION RT_BIT_64(0)
+/** Return the TPM attributes JSON object. */
+#define SWTPM_GET_CONFIG_F_TPM_ATTRIBUTES RT_BIT_64(1)
+/** @} */
+
+
+/**
+ * Response data for a SWTPMCMD_GET_CONFIG command.
+ */
+typedef struct SWTPMRESPGETCONFIG
+{
+ /** Total size of the object in bytes. */
+ uint32_t cbTotal;
+ /** Size of the chunk returned in this response. */
+ uint32_t cbThis;
+} SWTPMRESPGETCONFIG;
+/** Pointer to a response data struct for SWTPMCMD_GET_CONFIG. */
+typedef SWTPMRESPGETCONFIG *PSWTPMRESPGETCONFIG;
+/** Pointer to a const response data struct for SWTPMCMD_GET_CONFIG. */
+typedef const SWTPMRESPGETCONFIG *PCSWTPMRESPGETCONFIG;
+
+
+/**
+ * Response data for a SWTPMCMD_GET_TPMESTABLISHED command.
+ */
+typedef struct SWTPMRESPGETTPMEST
+{
+ /** Flag whether the TPM established bit is set for the TPM. */
+ uint8_t fEst;
+} SWTPMRESPGETTPMEST;
+/** Pointer to a response data struct for SWTPMCMD_GET_TPMESTABLISHED. */
+typedef SWTPMRESPGETTPMEST *PSWTPMRESPGETTPMEST;
+/** Pointer to a const response data struct for SWTPMCMD_GET_TPMESTABLISHED. */
+typedef const SWTPMRESPGETTPMEST *PCSWTPMRESPGETTPMEST;
+
+
+/**
+ * Additional command data for SWTPMCMD_RESET_TPMESTABLISHED.
+ */
+typedef struct SWTPMCMDRSTEST
+{
+ /** The locality resetting trying to reset the established bit. */
+ uint8_t bLoc;
+} SWTPMCMDRSTEST;
+/** Pointer to a response data struct for SWTPMCMD_GET_TPMESTABLISHED. */
+typedef SWTPMCMDRSTEST *PSWTPMCMDRSTEST;
+/** Pointer to a const response data struct for SWTPMCMD_GET_TPMESTABLISHED. */
+typedef const SWTPMCMDRSTEST *PCSWTPMCMDRSTEST;
+
+
+/**
+ * Additional command data for SWTPMCMD_SET_BUFFERSIZE.
+ */
+typedef struct SWTPMCMDSETBUFSZ
+{
+ /** The buffer size to set, 0 to query for the currently used buffer size. */
+ uint32_t cbBuffer;
+} SWTPMCMDSETBUFSZ;
+/** Pointer to a command data struct for SWTPMCMD_SET_BUFFERSIZE. */
+typedef SWTPMCMDSETBUFSZ *PSWTPMCMDSETBUFSZ;
+/** Pointer to a const command data struct for SWTPMCMD_SET_BUFFERSIZE. */
+typedef const SWTPMCMDSETBUFSZ *PCSWTPMCMDSETBUFSZ;
+
+
+/**
+ * Response data for a SWTPMCMD_SET_BUFFERSIZE command.
+ */
+typedef struct SWTPMRESPSETBUFSZ
+{
+ /** Buffer size in use. */
+ uint32_t cbBuffer;
+ /** Minimum supported buffer size. */
+ uint32_t cbBufferMin;
+ /** Maximum supported buffer size. */
+ uint32_t cbBufferMax;
+} SWTPMRESPSETBUFSZ;
+/** Pointer to a response data struct for SWTPMCMD_SET_BUFFERSIZE. */
+typedef SWTPMRESPSETBUFSZ *PSWTPMRESPSETBUFSZ;
+/** Pointer to a const response data struct for SWTPMCMD_SET_BUFFERSIZE. */
+typedef const SWTPMRESPSETBUFSZ *PCSWTPMRESPSETBUFSZ;
+
+
+/**
+ * TPM emulator driver instance data.
+ *
+ * @implements PDMITPMCONNECTOR
+ */
+typedef struct DRVTPMEMU
+{
+ /** The stream interface. */
+ PDMITPMCONNECTOR ITpmConnector;
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvIns;
+
+ /** Socket handle for the control connection. */
+ RTSOCKET hSockCtrl;
+ /** Socket handle for the data connection. */
+ RTSOCKET hSockData;
+
+ /** Currently set locality. */
+ uint8_t bLoc;
+
+ /** TPM version offered by the emulator. */
+ TPMVERSION enmTpmVers;
+ /** Capabilities offered by the TPM emulator. */
+ uint32_t fCaps;
+ /** Buffer size for the emulated TPM. */
+ uint32_t cbBuffer;
+} DRVTPMEMU;
+/** Pointer to the TPM emulator instance data. */
+typedef DRVTPMEMU *PDRVTPMEMU;
+
+/** The special no current locality selected value. */
+#define TPM_NO_LOCALITY_SELECTED 0xff
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Executes the given command over the control connection to the TPM emulator.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the TPM emulator driver instance data.
+ * @param enmCmd The command to execute.
+ * @param pvCmd Additional command data to send.
+ * @param cbCmd Size of the additional command data in bytes.
+ * @param pu32Resp Where to store the response code from the reply.
+ * @param pvResp Where to store additional resposne data.
+ * @param cbResp Size of the Response data in bytes (excluding the response status code which is implicit).
+ * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
+ *
+ * @note This method can return success even though the request at such failed, check the content of pu32Resp!
+ */
+static int drvTpmEmuExecCtrlCmdEx(PDRVTPMEMU pThis, SWTPMCMD enmCmd, const void *pvCmd, size_t cbCmd, uint32_t *pu32Resp,
+ void *pvResp, size_t cbResp, RTMSINTERVAL cMillies)
+{
+ SWTPMHDR Hdr;
+ RTSGBUF SgBuf;
+ RTSGSEG aSegs[2];
+ uint32_t cSegs = 1;
+
+ Hdr.enmCmd = (SWTPMCMD)RT_H2BE_U32(enmCmd);
+ aSegs[0].pvSeg = &Hdr;
+ aSegs[0].cbSeg = sizeof(Hdr);
+ if (cbCmd)
+ {
+ cSegs++;
+ aSegs[1].pvSeg = (void *)pvCmd;
+ aSegs[1].cbSeg = cbCmd;
+ }
+
+ RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
+ int rc = RTSocketSgWrite(pThis->hSockCtrl, &SgBuf);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSocketSelectOne(pThis->hSockCtrl, cMillies);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t u32Resp = 0;
+ rc = RTSocketRead(pThis->hSockCtrl, &u32Resp, sizeof(u32Resp), NULL /*pcbRead*/);
+ if (RT_SUCCESS(rc))
+ {
+ *pu32Resp = RT_BE2H_U32(u32Resp);
+ if (*pu32Resp == 0)
+ {
+ if (cbResp)
+ rc = RTSocketRead(pThis->hSockCtrl, pvResp, cbResp, NULL /*pcbRead*/);
+ }
+ else
+ rc = VERR_NET_IO_ERROR;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Continue receiving a response from a previous call of drvTpmEmuExecCtrlCmdEx() or
+ * drvTpmEmuExecCtrlCmdNoPayload().
+ *
+ * @param pThis Pointer to the TPM emulator driver instance data.
+ * @param enmCmd The command to execute.
+ * @param pvResp Where to store additional resposne data.
+ * @param cbResp Size of the additional response data in bytes.
+ * @param cMillies Number of milliseconds to wait before aborting the receive with a timeout error.
+ */
+static int drvTpmEmuExecCtrlCmdRespCont(PDRVTPMEMU pThis, void *pvResp, size_t cbResp, RTMSINTERVAL cMillies)
+{
+ int rc = RTSocketSelectOne(pThis->hSockCtrl, cMillies);
+ if (RT_SUCCESS(rc))
+ rc = RTSocketRead(pThis->hSockCtrl, pvResp, cbResp, NULL /*pcbRead*/);
+
+ return rc;
+}
+
+
+/**
+ * Executes the given command over the control connection to the TPM emulator - variant with no command payload.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NET_IO_ERROR if the executed command returned an error in the response status field.
+ * @param pThis Pointer to the TPM emulator driver instance data.
+ * @param enmCmd The command to execute.
+ * @param pvResp Where to store additional resposne data.
+ * @param cbResp Size of the Response data in bytes (excluding the response status code which is implicit).
+ * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
+ */
+static int drvTpmEmuExecCtrlCmdNoPayload(PDRVTPMEMU pThis, SWTPMCMD enmCmd, void *pvResp, size_t cbResp, RTMSINTERVAL cMillies)
+{
+ uint32_t u32Resp = 0;
+ int rc = drvTpmEmuExecCtrlCmdEx(pThis, enmCmd, NULL /*pvCmd*/, 0 /*cbCmd*/, &u32Resp,
+ pvResp, cbResp, cMillies);
+ if (RT_SUCCESS(rc))
+ {
+ if (u32Resp != 0)
+ rc = VERR_NET_IO_ERROR;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Executes the given command over the control connection to the TPM emulator - variant with no response payload other than the result.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NET_IO_ERROR if the executed command returned an error in the response status field.
+ * @param pThis Pointer to the TPM emulator driver instance data.
+ * @param enmCmd The command to execute.
+ * @param pvCmd Additional command data to send.
+ * @param cbCmd Size of the additional command data in bytes.
+ * @param pu32Resp Where to store the response code from the reply.
+ * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
+ */
+static int drvTpmEmuExecCtrlCmdNoResp(PDRVTPMEMU pThis, SWTPMCMD enmCmd, const void *pvCmd, size_t cbCmd, uint32_t *pu32Resp,
+ RTMSINTERVAL cMillies)
+{
+ return drvTpmEmuExecCtrlCmdEx(pThis, enmCmd, pvCmd, cbCmd, pu32Resp,
+ NULL /*pvResp*/, 0 /*cbResp*/, cMillies);
+}
+
+
+/**
+ * Executes the given command over the control connection to the TPM emulator - variant with no command and response payload.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NET_IO_ERROR if the executed command returned an error in the response status field.
+ * @param pThis Pointer to the TPM emulator driver instance data.
+ * @param enmCmd The command to execute.
+ * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
+ */
+static int drvTpmEmuExecCtrlCmdNoPayloadAndResp(PDRVTPMEMU pThis, SWTPMCMD enmCmd, RTMSINTERVAL cMillies)
+{
+ uint32_t u32Resp = 0;
+ int rc = drvTpmEmuExecCtrlCmdEx(pThis, enmCmd, NULL /*pvCmd*/, 0 /*cbCmd*/, &u32Resp,
+ NULL /*pvResp*/, 0 /*cbResp*/, cMillies);
+ if (RT_SUCCESS(rc))
+ {
+ if (u32Resp != 0)
+ rc = VERR_NET_IO_ERROR;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Queries the version of the TPM offered by the remote emulator.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the TPM emulator driver instance data.
+ */
+static int drvTpmEmuQueryTpmVersion(PDRVTPMEMU pThis)
+{
+ SWTPMCMDGETCONFIG Cmd;
+ SWTPMRESPGETCONFIG Resp;
+ uint8_t abData[_4K];
+ uint32_t u32Resp = 0;
+
+ RT_ZERO(Cmd); RT_ZERO(Resp);
+ Cmd.u64Flags = RT_H2BE_U64(SWTPM_GET_CONFIG_F_TPM_SPECIFICATION);
+ Cmd.u32Offset = 0;
+ int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_GET_INFO, &Cmd, sizeof(Cmd), &u32Resp,
+ &Resp, sizeof(Resp), RT_MS_10SEC);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Currently it is not necessary to get the information in chunks, a single
+ * transaction is enough. To fend off future versions of swtpm requiring this
+ * we return an error here if the total length is not equal to the length of the chunk.
+ */
+ if (RT_BE2H_U32(Resp.cbTotal) == RT_BE2H_U32(Resp.cbThis))
+ {
+ /* Fetch the response body. */
+ rc = drvTpmEmuExecCtrlCmdRespCont(pThis, &abData[0], RT_BE2H_U32(Resp.cbThis), RT_MS_10SEC);
+ if (RT_SUCCESS(rc))
+ {
+ RTJSONVAL hJsonVal = NIL_RTJSONVAL;
+ rc = RTJsonParseFromBuf(&hJsonVal, &abData[0], RT_BE2H_U32(Resp.cbThis), NULL /*pErrInfo*/);
+ if (RT_SUCCESS(rc))
+ {
+ RTJSONVAL hJsonTpmSpec = NIL_RTJSONVAL;
+ rc = RTJsonValueQueryByName(hJsonVal, "TPMSpecification", &hJsonTpmSpec);
+ if (RT_SUCCESS(rc))
+ {
+ RTJSONVAL hJsonTpmFam = NIL_RTJSONVAL;
+ rc = RTJsonValueQueryByName(hJsonTpmSpec, "family", &hJsonTpmFam);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszFam = NULL;
+ rc = RTJsonValueQueryString(hJsonTpmFam, &pszFam);
+ if (RT_SUCCESS(rc))
+ {
+ if (!RTStrCmp(pszFam, "1.2"))
+ pThis->enmTpmVers = TPMVERSION_1_2;
+ else if (!RTStrCmp(pszFam, "2.0"))
+ pThis->enmTpmVers = TPMVERSION_2_0;
+ else
+ pThis->enmTpmVers = TPMVERSION_UNKNOWN;
+ }
+
+ RTJsonValueRelease(hJsonTpmFam);
+ }
+
+ RTJsonValueRelease(hJsonTpmSpec);
+ }
+
+ RTJsonValueRelease(hJsonVal);
+ }
+ }
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Queries the capabilities of the remote TPM emulator and verifies that
+ * it offers everything we require for operation.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the TPM emulator driver instance data.
+ */
+static int drvTpmEmuQueryCaps(PDRVTPMEMU pThis)
+{
+ SWTPMRESPGETCAPABILITY Resp;
+ int rc = drvTpmEmuExecCtrlCmdNoPayload(pThis, SWTPMCMD_GET_CAPABILITY, &Resp, sizeof(Resp), RT_MS_10SEC);
+ if (RT_SUCCESS(rc))
+ pThis->fCaps = RT_BE2H_U32(Resp.u32Caps);
+
+ return rc;
+}
+
+
+/**
+ * Queries the maximum supported buffer size by the emulation.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the TPM emulator driver instance data.
+ * @param pcbBufferMax Where to store the maximum supported buffer size on success.
+ */
+static int drvTpmEmuQueryBufferSzMax(PDRVTPMEMU pThis, uint32_t *pcbBufferMax)
+{
+ SWTPMCMDSETBUFSZ Cmd;
+ SWTPMRESPSETBUFSZ Resp;
+ uint32_t u32Resp = 0;
+
+ RT_ZERO(Cmd); RT_ZERO(Resp);
+ Cmd.cbBuffer = RT_H2BE_U32(0);
+ int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_SET_BUFFERSIZE, &Cmd, sizeof(Cmd), &u32Resp,
+ &Resp, sizeof(Resp), RT_MS_10SEC);
+ if (RT_SUCCESS(rc))
+ {
+ if (u32Resp == 0)
+ *pcbBufferMax = RT_BE2H_U32(Resp.cbBufferMax);
+ else
+ rc = VERR_NET_IO_ERROR;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Queries the maximum supported buffer size by the emulation.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the TPM emulator driver instance data.
+ * @param cbBuffer The buffer size to set.
+ */
+static int drvTpmEmuSetBufferSz(PDRVTPMEMU pThis, uint32_t cbBuffer)
+{
+ SWTPMCMDSETBUFSZ Cmd;
+ SWTPMRESPSETBUFSZ Resp;
+ uint32_t u32Resp = 0;
+
+ RT_ZERO(Cmd); RT_ZERO(Resp);
+ Cmd.cbBuffer = RT_H2BE_U32(cbBuffer);
+ int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_SET_BUFFERSIZE, &Cmd, sizeof(Cmd), &u32Resp,
+ &Resp, sizeof(Resp), RT_MS_10SEC);
+ if ( RT_SUCCESS(rc)
+ && u32Resp != 0)
+ rc = VERR_NET_IO_ERROR;
+
+ return rc;
+}
+
+
+/**
+ * Sets the given locality for the emulated TPM.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the TPM emulator driver instance data.
+ * @param bLoc The locality to set.
+ */
+static int drvTpmEmuSetLocality(PDRVTPMEMU pThis, uint8_t bLoc)
+{
+ SWTPMCMDSETLOCALITY Cmd;
+ uint32_t u32Resp = 0;
+
+ Cmd.bLoc = bLoc;
+ int rc = drvTpmEmuExecCtrlCmdNoResp(pThis, SWTPMCMD_SET_LOCALITY, &Cmd, sizeof(Cmd), &u32Resp, RT_MS_10SEC);
+ if ( RT_SUCCESS(rc)
+ && u32Resp != 0)
+ rc = VERR_NET_IO_ERROR;
+
+ if (RT_SUCCESS(rc))
+ pThis->bLoc = bLoc;
+
+ return rc;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetVersion} */
+static DECLCALLBACK(TPMVERSION) drvTpmEmuGetVersion(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+ return pThis->enmTpmVers;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetLocalityMax} */
+static DECLCALLBACK(uint32_t) drvTpmEmuGetLocalityMax(PPDMITPMCONNECTOR pInterface)
+{
+ RT_NOREF(pInterface);
+ return 4;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetBufferSize} */
+static DECLCALLBACK(uint32_t) drvTpmEmuGetBufferSize(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+ return pThis->cbBuffer;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetEstablishedFlag} */
+static DECLCALLBACK(bool) drvTpmEmuGetEstablishedFlag(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+
+ SWTPMRESPGETTPMEST Resp;
+ int rc = drvTpmEmuExecCtrlCmdNoPayload(pThis, SWTPMCMD_GET_TPMESTABLISHED, &Resp, sizeof(Resp), RT_MS_10SEC);
+ if (RT_SUCCESS(rc)
+ && Resp.fEst != 0)
+ return true;
+
+ return false;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnResetEstablishedFlag} */
+static DECLCALLBACK(int) drvTpmEmuResetEstablishedFlag(PPDMITPMCONNECTOR pInterface, uint8_t bLoc)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+
+ SWTPMCMDRSTEST Cmd;
+ uint32_t u32Resp = 0;
+
+ Cmd.bLoc = bLoc;
+ int rc = drvTpmEmuExecCtrlCmdNoResp(pThis, SWTPMCMD_RESET_TPMESTABLISHED, &Cmd, sizeof(Cmd), &u32Resp, RT_MS_10SEC);
+ if ( RT_SUCCESS(rc)
+ && u32Resp != 0)
+ rc = VERR_NET_IO_ERROR;
+
+ return rc;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdExec} */
+static DECLCALLBACK(int) drvTpmEmuCmdExec(PPDMITPMCONNECTOR pInterface, uint8_t bLoc, const void *pvCmd, size_t cbCmd, void *pvResp, size_t cbResp)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+
+ int rc = VINF_SUCCESS;
+ if (pThis->bLoc != bLoc)
+ rc = drvTpmEmuSetLocality(pThis, bLoc);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSocketWrite(pThis->hSockData, pvCmd, cbCmd);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSocketSelectOne(pThis->hSockData, RT_MS_10SEC);
+ if (RT_SUCCESS(rc))
+ {
+ /* Read the response header in first. */
+ TPMRESPHDR RespHdr;
+ rc = RTSocketRead(pThis->hSockData, &RespHdr, sizeof(RespHdr), NULL /*pcbRead*/);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbHdrResp = RTTpmRespGetSz(&RespHdr);
+ if (cbHdrResp <= cbResp - sizeof(RespHdr))
+ {
+ memcpy(pvResp, &RespHdr, sizeof(RespHdr));
+
+ if (cbHdrResp > sizeof(RespHdr))
+ rc = RTSocketRead(pThis->hSockData, (uint8_t *)pvResp + sizeof(RespHdr), cbHdrResp - sizeof(RespHdr),
+ NULL /*pcbRead*/);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdCancel} */
+static DECLCALLBACK(int) drvTpmEmuCmdCancel(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+
+ return drvTpmEmuExecCtrlCmdNoPayloadAndResp(pThis, SWTPMCMD_CANCEL_TPM_CMD, RT_MS_10SEC);
+}
+
+
+/** @interface_method_impl{PDMIBASE,pfnQueryInterface} */
+static DECLCALLBACK(void *) drvTpmEmuQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMITPMCONNECTOR, &pThis->ITpmConnector);
+ return NULL;
+}
+
+
+/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnPowerOn}
+ */
+static DECLCALLBACK(void) drvTpmEmuPowerOn(PPDMDRVINS pDrvIns)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
+
+ SWTPMCMDTPMINIT Cmd;
+ uint32_t u32Resp = 0;
+
+ RT_ZERO(Cmd);
+ Cmd.u32Flags = 0;
+ int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_INIT, &Cmd, sizeof(Cmd), &u32Resp,
+ NULL, 0, RT_MS_10SEC);
+ if (RT_FAILURE(rc))
+ PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, "Failed to startup the TPM with %Rrc", rc);
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnPowerOff}
+ */
+static DECLCALLBACK(void) drvTpmEmuPowerOff(PPDMDRVINS pDrvIns)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
+
+ int rc = drvTpmEmuExecCtrlCmdNoPayload(pThis, SWTPMCMD_SHUTDOWN, NULL, 0, RT_MS_10SEC);
+ if (RT_FAILURE(rc))
+ PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, "Failed to shutdown the TPM with %Rrc", rc);
+}
+
+
+/** @copydoc FNPDMDRVDESTRUCT */
+static DECLCALLBACK(void) drvTpmEmuDestruct(PPDMDRVINS pDrvIns)
+{
+ PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
+ LogFlow(("%s\n", __FUNCTION__));
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ if (pThis->hSockCtrl != NIL_RTSOCKET)
+ {
+ int rc = RTSocketShutdown(pThis->hSockCtrl, true /* fRead */, true /* fWrite */);
+ AssertRC(rc);
+
+ rc = RTSocketClose(pThis->hSockCtrl);
+ AssertRC(rc); RT_NOREF(rc);
+
+ pThis->hSockCtrl = NIL_RTSOCKET;
+ }
+
+ if (pThis->hSockData != NIL_RTSOCKET)
+ {
+ int rc = RTSocketShutdown(pThis->hSockData, true /* fRead */, true /* fWrite */);
+ AssertRC(rc);
+
+ rc = RTSocketClose(pThis->hSockData);
+ AssertRC(rc); RT_NOREF(rc);
+
+ pThis->hSockCtrl = NIL_RTSOCKET;
+ }
+}
+
+
+/** @copydoc FNPDMDRVCONSTRUCT */
+static DECLCALLBACK(int) drvTpmEmuConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ pThis->hSockCtrl = NIL_RTSOCKET;
+ pThis->hSockData = NIL_RTSOCKET;
+ pThis->enmTpmVers = TPMVERSION_UNKNOWN;
+ pThis->bLoc = TPM_NO_LOCALITY_SELECTED;
+
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvTpmEmuQueryInterface;
+ /* ITpmConnector */
+ pThis->ITpmConnector.pfnGetVersion = drvTpmEmuGetVersion;
+ pThis->ITpmConnector.pfnGetLocalityMax = drvTpmEmuGetLocalityMax;
+ pThis->ITpmConnector.pfnGetBufferSize = drvTpmEmuGetBufferSize;
+ pThis->ITpmConnector.pfnGetEstablishedFlag = drvTpmEmuGetEstablishedFlag;
+ pThis->ITpmConnector.pfnResetEstablishedFlag = drvTpmEmuResetEstablishedFlag;
+ pThis->ITpmConnector.pfnCmdExec = drvTpmEmuCmdExec;
+ pThis->ITpmConnector.pfnCmdCancel = drvTpmEmuCmdCancel;
+
+ /*
+ * Validate and read the configuration.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|BufferSize", "");
+
+ char szLocation[_1K];
+ int rc = pHlp->pfnCFGMQueryString(pCfg, "Location", &szLocation[0], sizeof(szLocation));
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
+
+ /*
+ * Create/Open the socket.
+ */
+ char *pszPort = strchr(szLocation, ':');
+ if (!pszPort)
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS,
+ N_("DrvTpmEmu#%d: The location misses the port to connect to"),
+ pDrvIns->iInstance);
+
+ *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
+ uint32_t uPort = 0;
+ rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("DrvTpmEmu#%d: The port part of the location is not a numerical value"),
+ pDrvIns->iInstance);
+
+ rc = RTTcpClientConnect(szLocation, uPort, &pThis->hSockCtrl);
+ *pszPort = ':'; /* Restore delimiter before checking the status. */
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("DrvTpmEmu#%d failed to connect to control socket %s"),
+ pDrvIns->iInstance, szLocation);
+
+ rc = drvTpmEmuQueryCaps(pThis);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("DrvTpmEmu#%d failed to query capabilities offered by %s"),
+ pDrvIns->iInstance, szLocation);
+
+ if (!(pThis->fCaps & SWTPM_CAP_GET_CONFIG))
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
+ N_("DrvTpmEmu#%d Emulated TPM at '%s' misses the GET_CONFIG capability"),
+ pDrvIns->iInstance, szLocation);
+
+ rc = drvTpmEmuQueryTpmVersion(pThis);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("DrvTpmEmu#%d failed to query TPM version from %s"),
+ pDrvIns->iInstance, szLocation);
+
+ if (pThis->enmTpmVers == TPMVERSION_UNKNOWN)
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
+ N_("DrvTpmEmu#%d Emulated TPM version of %s is not supported"),
+ pDrvIns->iInstance, szLocation);
+
+ const char *pszTpmVers = NULL;
+ uint32_t fCapsReq = SWTPM_CAP_INIT | SWTPM_CAP_SHUTDOWN | SWTPM_CAP_GET_TPMESTABLISHED
+ | SWTPM_CAP_SET_LOCALITY | SWTPM_CAP_CANCEL_TPM_CMD | SWTPM_CAP_GET_STATEBLOB
+ | SWTPM_CAP_SET_STATEBLOB | SWTPM_CAP_STOP | SWTPM_CAP_SET_BUFFERSIZE;
+ switch (pThis->enmTpmVers)
+ {
+ case TPMVERSION_1_2:
+ /* No additional capabilities needed. */
+ pszTpmVers = "1.2";
+ break;
+ case TPMVERSION_2_0:
+ fCapsReq |= SWTPM_CAP_RESET_TPMESTABLISHED;
+ pszTpmVers = "2.0";
+ break;
+ default:
+ AssertMsgFailedReturn(("DrvTpmEmu#%d Emulated TPM version %d is not correctly handled", pDrvIns->iInstance, pThis->enmTpmVers),
+ VERR_INVALID_STATE);
+ }
+
+ if ((pThis->fCaps & fCapsReq) != fCapsReq)
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
+ N_("DrvTpmEmu#%d Emulated TPM version of %s does not offer required set of capabilities (%#x requested vs. %#x offered)"),
+ pDrvIns->iInstance, szLocation, fCapsReq, pThis->fCaps);
+
+ uint32_t cbBufferMax = 0;
+ rc = drvTpmEmuQueryBufferSzMax(pThis, &cbBufferMax);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("DrvTpmEmu#%d failed to query maximum buffer size from %s"),
+ pDrvIns->iInstance, szLocation);
+
+ /* Configure the buffer size. */
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "BufferSize", &pThis->cbBuffer, cbBufferMax);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Configuration error: querying \"BufferSize\" resulted in %Rrc"), rc);
+
+ /* Set the buffer size. */
+ rc = drvTpmEmuSetBufferSz(pThis, pThis->cbBuffer);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("DrvTpmEmu#%d failed to set buffer size to %u for %s"),
+ pDrvIns->iInstance, pThis->cbBuffer, szLocation);
+
+ /* Connect the data channel now. */
+ /** @todo Allow configuring a different port. */
+ *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
+ rc = RTTcpClientConnect(szLocation, uPort + 1, &pThis->hSockData);
+ *pszPort = ':'; /* Restore delimiter before checking the status. */
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("DrvTpmEmu#%d failed to connect to data socket %s"),
+ pDrvIns->iInstance, szLocation);
+
+ LogRel(("DrvTpmEmu#%d: Connected to %s, emulating TPM version %s\n", pDrvIns->iInstance, szLocation, pszTpmVers));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * TPM emulator driver registration record.
+ */
+const PDMDRVREG g_DrvTpmEmu =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "TpmEmu",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "TPM emulator driver.",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_STREAM,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVTPMEMU),
+ /* pfnConstruct */
+ drvTpmEmuConstruct,
+ /* pfnDestruct */
+ drvTpmEmuDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ drvTpmEmuPowerOn,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ drvTpmEmuPowerOff,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Security/DrvTpmEmuTpms.cpp b/src/VBox/Devices/Security/DrvTpmEmuTpms.cpp
new file mode 100644
index 00000000..2059c780
--- /dev/null
+++ b/src/VBox/Devices/Security/DrvTpmEmuTpms.cpp
@@ -0,0 +1,534 @@
+/* $Id: DrvTpmEmuTpms.cpp $ */
+/** @file
+ * TPM emulation driver based on libtpms.
+ */
+
+/*
+ * Copyright (C) 2021-2023 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_DRV_TPM_EMU
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmtpmifs.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/uuid.h>
+#include <iprt/vfs.h>
+#include <iprt/zip.h>
+
+#include <libtpms/tpm_library.h>
+#include <libtpms/tpm_error.h>
+#include <libtpms/tpm_tis.h>
+#include <libtpms/tpm_nvfilename.h>
+
+#include <iprt/formats/tpm.h>
+
+#if 0
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * TPM emulation driver instance data.
+ *
+ * @implements PDMITPMCONNECTOR
+ */
+typedef struct DRVTPMEMU
+{
+ /** The stream interface. */
+ PDMITPMCONNECTOR ITpmConnector;
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvIns;
+ /** The VFS interface of the driver below for NVRAM/TPM state loading and storing. */
+ PPDMIVFSCONNECTOR pDrvVfs;
+
+ /** The TPM version we are emulating. */
+ TPMVERSION enmVersion;
+ /** The buffer size the TPM advertises. */
+ uint32_t cbBuffer;
+ /** Currently set locality. */
+ uint8_t bLoc;
+} DRVTPMEMU;
+/** Pointer to the TPM emulator instance data. */
+typedef DRVTPMEMU *PDRVTPMEMU;
+
+/** The special no current locality selected value. */
+#define TPM_NO_LOCALITY_SELECTED 0xff
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to the (only) instance data in this driver. */
+static PDRVTPMEMU g_pDrvTpmEmuTpms = NULL;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/* -=-=-=-=- PDMITPMCONNECTOR interface callabcks. -=-=-=-=- */
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetVersion} */
+static DECLCALLBACK(TPMVERSION) drvTpmEmuTpmsGetVersion(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+ return pThis->enmVersion;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetLocalityMax} */
+static DECLCALLBACK(uint32_t) drvTpmEmuGetLocalityMax(PPDMITPMCONNECTOR pInterface)
+{
+ RT_NOREF(pInterface);
+ return 4;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetBufferSize} */
+static DECLCALLBACK(uint32_t) drvTpmEmuGetBufferSize(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+ return pThis->cbBuffer;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetEstablishedFlag} */
+static DECLCALLBACK(bool) drvTpmEmuTpmsGetEstablishedFlag(PPDMITPMCONNECTOR pInterface)
+{
+ RT_NOREF(pInterface);
+
+ TPM_BOOL fTpmEst = FALSE;
+ TPM_RESULT rcTpm = TPM_IO_TpmEstablished_Get(&fTpmEst);
+ if (RT_LIKELY(rcTpm == TPM_SUCCESS))
+ return RT_BOOL(fTpmEst);
+
+ return false;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnResetEstablishedFlag} */
+static DECLCALLBACK(int) drvTpmEmuTpmsResetEstablishedFlag(PPDMITPMCONNECTOR pInterface, uint8_t bLoc)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+ uint8_t bLocOld = pThis->bLoc;
+
+ pThis->bLoc = bLoc;
+ TPM_RESULT rcTpm = TPM_IO_TpmEstablished_Reset();
+ pThis->bLoc = bLocOld;
+
+ if (RT_LIKELY(rcTpm == TPM_SUCCESS))
+ return VINF_SUCCESS;
+
+ LogRelMax(10, ("DrvTpmEmuTpms#%u: Failed to reset the established flag with %#x\n",
+ pThis->pDrvIns->iInstance, rcTpm));
+ return VERR_DEV_IO_ERROR;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdExec} */
+static DECLCALLBACK(int) drvTpmEmuTpmsCmdExec(PPDMITPMCONNECTOR pInterface, uint8_t bLoc, const void *pvCmd, size_t cbCmd, void *pvResp, size_t cbResp)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+
+ pThis->bLoc = bLoc;
+
+ uint8_t *pbRespBuf = NULL;
+ uint32_t cbRespBuf = 0;
+ uint32_t cbRespActual = 0;
+ TPM_RESULT rcTpm = TPMLIB_Process(&pbRespBuf, &cbRespActual, &cbRespBuf, (uint8_t *)pvCmd, (uint32_t)cbCmd);
+ if (RT_LIKELY(rcTpm == TPM_SUCCESS))
+ {
+ memcpy(pvResp, pbRespBuf, RT_MIN(cbResp, cbRespActual));
+ free(pbRespBuf);
+ return VINF_SUCCESS;
+ }
+
+ LogRelMax(10, ("DrvTpmEmuTpms#%u: Failed to execute command with %#x\n",
+ pThis->pDrvIns->iInstance, rcTpm));
+ return VERR_DEV_IO_ERROR;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdCancel} */
+static DECLCALLBACK(int) drvTpmEmuTpmsCmdCancel(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
+
+ TPM_RESULT rcTpm = TPMLIB_CancelCommand();
+ if (RT_LIKELY(rcTpm == TPM_SUCCESS))
+ return VINF_SUCCESS;
+
+ LogRelMax(10, ("DrvTpmEmuTpms#%u: Failed to cancel outstanding command with %#x\n",
+ pThis->pDrvIns->iInstance, rcTpm));
+ return VERR_DEV_IO_ERROR;
+}
+
+
+/** @interface_method_impl{PDMIBASE,pfnQueryInterface} */
+static DECLCALLBACK(void *) drvTpmEmuTpmsQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMITPMCONNECTOR, &pThis->ITpmConnector);
+ return NULL;
+}
+
+
+/* -=-=-=-=- libtpms_callbacks -=-=-=-=- */
+
+
+static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamInit(void)
+{
+ PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
+ RT_NOREF(pThis);
+
+ return TPM_SUCCESS;
+}
+
+
+static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamLoadData(uint8_t **ppvData, uint32_t *pcbLength,
+ uint32_t idTpm, const char *pszName)
+{
+ PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
+
+ AssertReturn(idTpm == 0, TPM_FAIL);
+
+ uint64_t cbState = 0;
+ int rc = pThis->pDrvVfs->pfnQuerySize(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, pszName, &cbState);
+ if ( RT_SUCCESS(rc)
+ && cbState == (uint32_t)cbState)
+ {
+ void *pvData = malloc(cbState);
+ if (RT_LIKELY(pvData))
+ {
+ rc = pThis->pDrvVfs->pfnReadAll(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, pszName,
+ pvData, cbState);
+ if (RT_SUCCESS(rc))
+ {
+ *ppvData = (uint8_t *)pvData;
+ *pcbLength = (uint32_t)cbState;
+ return VINF_SUCCESS;
+ }
+
+ free(pvData);
+ }
+ }
+ else if (rc == VERR_NOT_FOUND)
+ return TPM_RETRY; /* This is fine for the first start of a new VM. */
+
+ return TPM_FAIL;
+}
+
+
+static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamStoreData(const uint8_t *pvData, uint32_t cbLength,
+ uint32_t idTpm, const char *pszName)
+{
+ PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
+
+ AssertReturn(idTpm == 0, TPM_FAIL);
+
+ int rc = pThis->pDrvVfs->pfnWriteAll(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, pszName,
+ pvData, cbLength);
+ if (RT_SUCCESS(rc))
+ return TPM_SUCCESS;
+
+ return TPM_FAIL;
+}
+
+
+static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamDeleteName(uint32_t idTpm, const char *pszName, TPM_BOOL fMustExist)
+{
+ PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
+
+ AssertReturn(idTpm == 0, TPM_FAIL);
+
+ int rc = pThis->pDrvVfs->pfnDelete(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, pszName);
+ if ( RT_SUCCESS(rc)
+ || ( rc == VERR_NOT_FOUND
+ && !fMustExist))
+ return TPM_SUCCESS;
+
+ return TPM_FAIL;
+}
+
+
+static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkIoInit(void)
+{
+ return TPM_SUCCESS;
+}
+
+
+static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkIoGetLocality(TPM_MODIFIER_INDICATOR *pLocalityModifier, uint32_t idTpm)
+{
+ PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
+
+ AssertReturn(idTpm == 0, TPM_FAIL);
+
+ *pLocalityModifier = pThis->bLoc;
+ return TPM_SUCCESS;
+}
+
+
+static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkIoGetPhysicalPresence(TPM_BOOL *pfPhysicalPresence, uint32_t idTpm)
+{
+ AssertReturn(idTpm == 0, TPM_FAIL);
+
+ *pfPhysicalPresence = TRUE;
+ return TPM_SUCCESS;
+}
+
+
+/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnPowerOn}
+ */
+static DECLCALLBACK(void) drvTpmEmuTpmsPowerOn(PPDMDRVINS pDrvIns)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ TPM_RESULT rcTpm = TPMLIB_MainInit();
+ if (RT_UNLIKELY(rcTpm != TPM_SUCCESS))
+ {
+ LogRel(("DrvTpmEmuTpms#%u: Failed to initialize TPM emulation with %#x\n",
+ pDrvIns->iInstance, rcTpm));
+ PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS, "Failed to startup the TPM with %u", rcTpm);
+ }
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnReset}
+ */
+static DECLCALLBACK(void) drvTpmEmuTpmsReset(PPDMDRVINS pDrvIns)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ TPMLIB_Terminate();
+ TPM_RESULT rcTpm = TPMLIB_MainInit();
+ if (RT_UNLIKELY(rcTpm != TPM_SUCCESS))
+ {
+ LogRel(("DrvTpmEmuTpms#%u: Failed to reset TPM emulation with %#x\n",
+ pDrvIns->iInstance, rcTpm));
+ PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS, "Failed to startup the TPM with %u", rcTpm);
+ }
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnPowerOff}
+ */
+static DECLCALLBACK(void) drvTpmEmuTpmsPowerOff(PPDMDRVINS pDrvIns)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ TPMLIB_Terminate();
+}
+
+
+/** @copydoc FNPDMDRVCONSTRUCT */
+static DECLCALLBACK(int) drvTpmEmuTpmsConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ pThis->enmVersion = TPMVERSION_UNKNOWN;
+ pThis->bLoc = TPM_NO_LOCALITY_SELECTED;
+
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvTpmEmuTpmsQueryInterface;
+ /* ITpmConnector */
+ pThis->ITpmConnector.pfnGetVersion = drvTpmEmuTpmsGetVersion;
+ pThis->ITpmConnector.pfnGetLocalityMax = drvTpmEmuGetLocalityMax;
+ pThis->ITpmConnector.pfnGetBufferSize = drvTpmEmuGetBufferSize;
+ pThis->ITpmConnector.pfnGetEstablishedFlag = drvTpmEmuTpmsGetEstablishedFlag;
+ pThis->ITpmConnector.pfnResetEstablishedFlag = drvTpmEmuTpmsResetEstablishedFlag;
+ pThis->ITpmConnector.pfnCmdExec = drvTpmEmuTpmsCmdExec;
+ pThis->ITpmConnector.pfnCmdCancel = drvTpmEmuTpmsCmdCancel;
+
+ /*
+ * Validate and read the configuration.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "TpmVersion|BufferSize", "");
+
+#if 0
+ TPMLIB_SetDebugFD(STDERR_FILENO);
+ TPMLIB_SetDebugLevel(~0);
+#endif
+
+ /*
+ * Try attach the VFS driver below and query it's VFS interface.
+ */
+ PPDMIBASE pBase = NULL;
+ int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Failed to attach driver below us! %Rrc"), rc);
+ pThis->pDrvVfs = PDMIBASE_QUERY_INTERFACE(pBase, PDMIVFSCONNECTOR);
+ if (!pThis->pDrvVfs)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
+ N_("No VFS interface below"));
+
+ TPMLIB_TPMVersion enmVersion = TPMLIB_TPM_VERSION_2;
+ uint32_t uTpmVersion = 0;
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "TpmVersion", &uTpmVersion, 2);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Configuration error: querying \"TpmVersion\" resulted in %Rrc"), rc);
+
+ switch (uTpmVersion)
+ {
+ case 1:
+ enmVersion = TPMLIB_TPM_VERSION_1_2;
+ pThis->enmVersion = TPMVERSION_1_2;
+ break;
+ case 2:
+ enmVersion = TPMLIB_TPM_VERSION_2;
+ pThis->enmVersion = TPMVERSION_2_0;
+ break;
+ default:
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
+ N_("Configuration error: \"TpmVersion\" %u is not supported"), uTpmVersion);
+ }
+
+ TPM_RESULT rcTpm = TPMLIB_ChooseTPMVersion(enmVersion);
+ if (rcTpm != TPM_SUCCESS)
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Failed to set the TPM version for the emulated TPM with %d"), rcTpm);
+
+ int cbBufferMax = 0;
+ rcTpm = TPMLIB_GetTPMProperty(TPMPROP_TPM_BUFFER_MAX, &cbBufferMax);
+ if (rcTpm != TPM_SUCCESS)
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Querying the maximum supported buffer size failed with %u"), rcTpm);
+
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "BufferSize", &pThis->cbBuffer, (uint32_t)cbBufferMax);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Configuration error: querying \"BufferSize\" resulted in %Rrc"), rc);
+
+ uint32_t cbBufferMin = 0;
+ uint32_t cbBuffer = TPMLIB_SetBufferSize(pThis->cbBuffer, &cbBufferMin, NULL /*max_size*/);
+ if (pThis->cbBuffer != cbBuffer)
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Failed to set buffer size (%u) of the emulated TPM with %u (min %u, max %d)"),
+ pThis->cbBuffer, cbBuffer, cbBufferMin, cbBufferMax);
+
+ struct libtpms_callbacks Callbacks;
+ Callbacks.sizeOfStruct = sizeof(Callbacks);
+ Callbacks.tpm_nvram_init = drvTpmEmuTpmsCbkNvRamInit;
+ Callbacks.tpm_nvram_loaddata = drvTpmEmuTpmsCbkNvRamLoadData;
+ Callbacks.tpm_nvram_storedata = drvTpmEmuTpmsCbkNvRamStoreData;
+ Callbacks.tpm_nvram_deletename = drvTpmEmuTpmsCbkNvRamDeleteName;
+ Callbacks.tpm_io_init = drvTpmEmuTpmsCbkIoInit;
+ Callbacks.tpm_io_getlocality = drvTpmEmuTpmsCbkIoGetLocality;
+ Callbacks.tpm_io_getphysicalpresence = drvTpmEmuTpmsCbkIoGetPhysicalPresence;
+ rcTpm = TPMLIB_RegisterCallbacks(&Callbacks);
+ if (rcTpm != TPM_SUCCESS)
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Failed to register callbacks with the TPM emulation: %u"),
+ rcTpm);
+
+ /* We can only have one instance of the TPM emulation and require the global variable for the callbacks unfortunately. */
+ g_pDrvTpmEmuTpms = pThis;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * TPM libtpms emulator driver registration record.
+ */
+const PDMDRVREG g_DrvTpmEmuTpms =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "TpmEmuTpms",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "TPM emulation driver based on libtpms.",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_STREAM,
+ /* cMaxInstances */
+ 1,
+ /* cbInstance */
+ sizeof(DRVTPMEMU),
+ /* pfnConstruct */
+ drvTpmEmuTpmsConstruct,
+ /* pfnDestruct */
+ NULL,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ drvTpmEmuTpmsPowerOn,
+ /* pfnReset */
+ drvTpmEmuTpmsReset,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ drvTpmEmuTpmsPowerOff,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Security/DrvTpmHost.cpp b/src/VBox/Devices/Security/DrvTpmHost.cpp
new file mode 100644
index 00000000..e9625f57
--- /dev/null
+++ b/src/VBox/Devices/Security/DrvTpmHost.cpp
@@ -0,0 +1,403 @@
+/* $Id: DrvTpmHost.cpp $ */
+/** @file
+ * TPM host access driver.
+ */
+
+/*
+ * Copyright (C) 2021-2023 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_DRV_TPM_HOST
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmtpmifs.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/uuid.h>
+#include <iprt/tpm.h>
+
+#include <iprt/formats/tpm.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * TPM 1.2 buffer size capability response.
+ */
+#pragma pack(1)
+typedef struct TPMRESPGETBUFSZ
+{
+ TPMRESPHDR Hdr;
+ uint32_t u32Length;
+ uint32_t cbBuf;
+} TPMRESPGETBUFSZ;
+#pragma pack()
+typedef TPMRESPGETBUFSZ *PTPMRESPGETBUFSZ;
+typedef const TPMRESPGETBUFSZ *PCTPMRESPGETBUFSZ;
+
+
+/**
+ * TPM 2.0 buffer size capability response.
+ */
+#pragma pack(1)
+typedef struct TPM2RESPGETBUFSZ
+{
+ TPMRESPHDR Hdr;
+ uint8_t fMore;
+ uint32_t u32Cap;
+ uint32_t u32Count;
+ uint32_t u32Prop;
+ uint32_t u32Value;
+} TPM2RESPGETBUFSZ;
+#pragma pack()
+typedef TPM2RESPGETBUFSZ *PTPM2RESPGETBUFSZ;
+typedef const TPM2RESPGETBUFSZ *PCTPM2RESPGETBUFSZ;
+
+
+/**
+ * TPM Host driver instance data.
+ *
+ * @implements PDMITPMCONNECTOR
+ */
+typedef struct DRVTPMHOST
+{
+ /** The stream interface. */
+ PDMITPMCONNECTOR ITpmConnector;
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvIns;
+
+ /** Handle to the host TPM. */
+ RTTPM hTpm;
+ /** Cached TPM version. */
+ TPMVERSION enmTpmVersion;
+ /** Cached buffer size of the host TPM. */
+ uint32_t cbBuffer;
+} DRVTPMHOST;
+/** Pointer to the TPM emulator instance data. */
+typedef DRVTPMHOST *PDRVTPMHOST;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Queries the buffer size of the host TPM.
+ *
+ * @returns VBox status code.
+ * @param pThis The host TPM driver instance data.
+ */
+static int drvTpmHostQueryBufferSize(PDRVTPMHOST pThis)
+{
+ uint8_t abResp[_1K];
+ int rc = VINF_SUCCESS;
+
+ switch (pThis->enmTpmVersion)
+ {
+ case TPMVERSION_1_2:
+ {
+ TPMREQGETCAPABILITY Req;
+
+ Req.Hdr.u16Tag = RT_H2BE_U16(TPM_TAG_RQU_COMMAND);
+ Req.Hdr.cbReq = RT_H2BE_U32(sizeof(Req));
+ Req.Hdr.u32Ordinal = RT_H2BE_U32(TPM_ORD_GETCAPABILITY);
+ Req.u32Cap = RT_H2BE_U32(TPM_CAP_PROPERTY);
+ Req.u32Length = RT_H2BE_U32(sizeof(uint32_t));
+ Req.u32SubCap = RT_H2BE_U32(TPM_CAP_PROP_INPUT_BUFFER);
+ rc = RTTpmReqExec(pThis->hTpm, 0 /*bLoc*/, &Req, sizeof(Req), &abResp[0], sizeof(abResp), NULL /*pcbResp*/);
+ break;
+ }
+ case TPMVERSION_2_0:
+ {
+ TPM2REQGETCAPABILITY Req;
+
+ Req.Hdr.u16Tag = RT_H2BE_U16(TPM2_ST_NO_SESSIONS);
+ Req.Hdr.cbReq = RT_H2BE_U32(sizeof(Req));
+ Req.Hdr.u32Ordinal = RT_H2BE_U32(TPM2_CC_GET_CAPABILITY);
+ Req.u32Cap = RT_H2BE_U32(TPM2_CAP_TPM_PROPERTIES);
+ Req.u32Property = RT_H2BE_U32(TPM2_PT_INPUT_BUFFER);
+ Req.u32Count = RT_H2BE_U32(1);
+ rc = RTTpmReqExec(pThis->hTpm, 0 /*bLoc*/, &Req, sizeof(Req), &abResp[0], sizeof(abResp), NULL /*pcbResp*/);
+ break;
+ }
+ default:
+ AssertFailed();
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ switch (pThis->enmTpmVersion)
+ {
+ case TPMVERSION_1_2:
+ {
+ PCTPMRESPGETBUFSZ pResp = (PCTPMRESPGETBUFSZ)&abResp[0];
+
+ if ( RTTpmRespGetSz(&pResp->Hdr) == sizeof(*pResp)
+ && RT_BE2H_U32(pResp->u32Length) == sizeof(uint32_t))
+ pThis->cbBuffer = RT_BE2H_U32(pResp->cbBuf);
+ else
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ case TPMVERSION_2_0:
+ {
+ PCTPM2RESPGETBUFSZ pResp = (PCTPM2RESPGETBUFSZ)&abResp[0];
+
+ if ( RTTpmRespGetSz(&pResp->Hdr) == sizeof(*pResp)
+ && RT_BE2H_U32(pResp->u32Count) == 1)
+ pThis->cbBuffer = RT_BE2H_U32(pResp->u32Value);
+ else
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ default:
+ AssertFailed();
+ }
+ }
+
+ return rc;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetVersion} */
+static DECLCALLBACK(TPMVERSION) drvTpmHostGetVersion(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMHOST pThis = RT_FROM_MEMBER(pInterface, DRVTPMHOST, ITpmConnector);
+ return pThis->enmTpmVersion;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetLocalityMax} */
+static DECLCALLBACK(uint32_t) drvTpmHostGetLocalityMax(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMHOST pThis = RT_FROM_MEMBER(pInterface, DRVTPMHOST, ITpmConnector);
+ return RTTpmGetLocalityMax(pThis->hTpm);
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetBufferSize} */
+static DECLCALLBACK(uint32_t) drvTpmHostGetBufferSize(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMHOST pThis = RT_FROM_MEMBER(pInterface, DRVTPMHOST, ITpmConnector);
+ return pThis->cbBuffer;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetEstablishedFlag} */
+static DECLCALLBACK(bool) drvTpmHostGetEstablishedFlag(PPDMITPMCONNECTOR pInterface)
+{
+ RT_NOREF(pInterface);
+ return false;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnResetEstablishedFlag} */
+static DECLCALLBACK(int) drvTpmHostResetEstablishedFlag(PPDMITPMCONNECTOR pInterface, uint8_t bLoc)
+{
+ RT_NOREF(pInterface, bLoc);
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdExec} */
+static DECLCALLBACK(int) drvTpmHostCmdExec(PPDMITPMCONNECTOR pInterface, uint8_t bLoc, const void *pvCmd, size_t cbCmd, void *pvResp, size_t cbResp)
+{
+ RT_NOREF(bLoc);
+ PDRVTPMHOST pThis = RT_FROM_MEMBER(pInterface, DRVTPMHOST, ITpmConnector);
+
+ return RTTpmReqExec(pThis->hTpm, 0 /*bLoc*/, pvCmd, cbCmd, pvResp, cbResp, NULL /*pcbResp*/);
+}
+
+
+/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdCancel} */
+static DECLCALLBACK(int) drvTpmHostCmdCancel(PPDMITPMCONNECTOR pInterface)
+{
+ PDRVTPMHOST pThis = RT_FROM_MEMBER(pInterface, DRVTPMHOST, ITpmConnector);
+
+ return RTTpmReqCancel(pThis->hTpm);
+}
+
+
+/** @interface_method_impl{PDMIBASE,pfnQueryInterface} */
+static DECLCALLBACK(void *) drvTpmHostQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVTPMHOST pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMHOST);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMITPMCONNECTOR, &pThis->ITpmConnector);
+ return NULL;
+}
+
+
+/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
+
+/** @copydoc FNPDMDRVDESTRUCT */
+static DECLCALLBACK(void) drvTpmHostDestruct(PPDMDRVINS pDrvIns)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ PDRVTPMHOST pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMHOST);
+ LogFlow(("%s\n", __FUNCTION__));
+
+ if (pThis->hTpm != NIL_RTTPM)
+ {
+ int rc = RTTpmClose(pThis->hTpm);
+ AssertRC(rc);
+
+ pThis->hTpm = NIL_RTTPM;
+ }
+}
+
+
+/** @copydoc FNPDMDRVCONSTRUCT */
+static DECLCALLBACK(int) drvTpmHostConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVTPMHOST pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMHOST);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ pThis->hTpm = NIL_RTTPM;
+
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvTpmHostQueryInterface;
+ /* ITpmConnector */
+ pThis->ITpmConnector.pfnGetVersion = drvTpmHostGetVersion;
+ pThis->ITpmConnector.pfnGetLocalityMax = drvTpmHostGetLocalityMax;
+ pThis->ITpmConnector.pfnGetBufferSize = drvTpmHostGetBufferSize;
+ pThis->ITpmConnector.pfnGetEstablishedFlag = drvTpmHostGetEstablishedFlag;
+ pThis->ITpmConnector.pfnResetEstablishedFlag = drvTpmHostResetEstablishedFlag;
+ pThis->ITpmConnector.pfnCmdExec = drvTpmHostCmdExec;
+ pThis->ITpmConnector.pfnCmdCancel = drvTpmHostCmdCancel;
+
+ /*
+ * Validate and read the configuration.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "TpmId", "");
+
+ uint32_t idTpm = RTTPM_ID_DEFAULT;
+ int rc = pHlp->pfnCFGMQueryU32Def(pCfg, "TpmId", &idTpm, RTTPM_ID_DEFAULT);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Configuration error: querying \"TpmId\" resulted in %Rrc"), rc);
+
+ rc = RTTpmOpen(&pThis->hTpm, idTpm);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("DrvTpmHost%d: Opening TPM with id %u failed with %Rrc"), pDrvIns->iInstance, idTpm, rc);
+
+ RTTPMVERSION enmVersion = RTTpmGetVersion(pThis->hTpm);
+
+ switch (enmVersion)
+ {
+ case RTTPMVERSION_1_2:
+ pThis->enmTpmVersion = TPMVERSION_1_2;
+ break;
+ case RTTPMVERSION_2_0:
+ pThis->enmTpmVersion = TPMVERSION_2_0;
+ break;
+ case RTTPMVERSION_UNKNOWN:
+ default:
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
+ N_("DrvTpmHost%d: TPM version %u of TPM id %u is not supported"),
+ pDrvIns->iInstance, enmVersion, idTpm);
+ }
+
+ rc = drvTpmHostQueryBufferSize(pThis);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("DrvTpmHost%d: Querying input buffer size of TPM with id %u failed with %Rrc"),
+ pDrvIns->iInstance, idTpm, rc);
+
+ LogRel(("DrvTpmHost#%d: Connected to TPM %u.\n", pDrvIns->iInstance, idTpm));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * TPM host driver registration record.
+ */
+const PDMDRVREG g_DrvTpmHost =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "TpmHost",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "TPM host driver.",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_STREAM,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVTPMHOST),
+ /* pfnConstruct */
+ drvTpmHostConstruct,
+ /* pfnDestruct */
+ drvTpmHostDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+