* [PATCH v3 1/5] tpm/tpm_tis_spi: Support TPM for SPI (Serial Peripheral Interface)
2024-11-01 18:57 [PATCH v3 0/5] TPM TIS SPI Support dan tan
@ 2024-11-01 18:57 ` dan tan
2024-11-01 18:57 ` [PATCH v3 2/5] tpm/tpm_tis_spi: activation for the PowerNV machines dan tan
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: dan tan @ 2024-11-01 18:57 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-ppc, stefanb, pbonzini, farosas, lvivier, clg
Implement support for TPM via SPI interface. The SPI bus master
is provided by PowerNV SPI device which is an SSI peripheral.
It can uses the tpm_emulator driver backend with the external
swtpm.
Although the implementation is endian neutral, the SPI
bus master provider, pnv_spi.c is only supported on the
PowerNV platform, thus, is big endian specific.
Signed-off-by: dan tan <dantan@linux.vnet.ibm.com>
---
docs/specs/tpm.rst | 15 ++
include/sysemu/tpm.h | 3 +
hw/tpm/tpm_tis_spi.c | 328 +++++++++++++++++++++++++++++++++++++++++++
hw/tpm/Kconfig | 6 +
hw/tpm/meson.build | 1 +
hw/tpm/trace-events | 7 +
6 files changed, 360 insertions(+)
create mode 100644 hw/tpm/tpm_tis_spi.c
diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst
index 1ad36ad709..63c3a43c7d 100644
--- a/docs/specs/tpm.rst
+++ b/docs/specs/tpm.rst
@@ -24,6 +24,7 @@ QEMU files related to TPM TIS interface:
- ``hw/tpm/tpm_tis_isa.c``
- ``hw/tpm/tpm_tis_sysbus.c``
- ``hw/tpm/tpm_tis_i2c.c``
+ - ``hw/tpm/tpm_tis_spi.c``
- ``hw/tpm/tpm_tis.h``
Both an ISA device and a sysbus device are available. The former is
@@ -33,6 +34,9 @@ Arm virt machine.
An I2C device support is also provided which can be instantiated in the Arm
based emulation machines. This device only supports the TPM 2 protocol.
+A Serial Peripheral Interface (SPI) device support has been added to the
+PowerNV emulation machines. This device only supports the TPM 2 protocol.
+
CRB interface
-------------
@@ -371,6 +375,17 @@ attached to I2C bus, use the following command line:
echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device
+In case a PowerNV machine is emulated and you want to use a TPM device
+attached to SPI bus, use the following command line (SPI bus master is
+provided by PowerNV SPI device):
+
+.. code-block:: console
+
+ qemu-system-ppc64 -m 2G -machine powernv10 -nographic \
+ -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
+ -tpmdev emulator,id=tpm0,chardev=chrtpm \
+ -device tpm-tis-spi,tpmdev=tpm0,bus=pnv-spi-bus.4
+
In case SeaBIOS is used as firmware, it should show the TPM menu item
after entering the menu with 'ESC'.
diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h
index 1ee568b3b6..22b05110e2 100644
--- a/include/sysemu/tpm.h
+++ b/include/sysemu/tpm.h
@@ -49,6 +49,7 @@ struct TPMIfClass {
#define TYPE_TPM_CRB "tpm-crb"
#define TYPE_TPM_SPAPR "tpm-spapr"
#define TYPE_TPM_TIS_I2C "tpm-tis-i2c"
+#define TYPE_TPM_TIS_SPI "tpm-tis-spi"
#define TPM_IS_TIS_ISA(chr) \
object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS_ISA)
@@ -60,6 +61,8 @@ struct TPMIfClass {
object_dynamic_cast(OBJECT(chr), TYPE_TPM_SPAPR)
#define TPM_IS_TIS_I2C(chr) \
object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS_I2C)
+#define TPM_IS_TIS_SPI(chr) \
+ object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS_SPI)
/* returns NULL unless there is exactly one TPM device */
static inline TPMIf *tpm_find(void)
diff --git a/hw/tpm/tpm_tis_spi.c b/hw/tpm/tpm_tis_spi.c
new file mode 100644
index 0000000000..5aacae8467
--- /dev/null
+++ b/hw/tpm/tpm_tis_spi.c
@@ -0,0 +1,328 @@
+/*
+ * QEMU PowerPC SPI TPM 2.0 model
+ *
+ * Copyright (c) 2024, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define IBM_PONQ
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/sysbus.h"
+#include "hw/acpi/tpm.h"
+#include "tpm_prop.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "tpm_tis.h"
+#include "hw/ssi/ssi.h"
+
+typedef struct TPMStateSPI {
+ /*< private >*/
+ SSIPeripheral parent_object;
+
+ uint8_t byte_offset; /* byte offset in transfer */
+ uint8_t wait_state_cnt; /* wait state counter */
+ uint8_t xfer_size; /* data size of transfer */
+ uint32_t reg_addr; /* register address of transfer */
+ uint32_t tis_addr; /* tis address including locty */
+
+ uint8_t spi_state; /* READ / WRITE / IDLE */
+#define SPI_STATE_IDLE 0
+#define SPI_STATE_WRITE 1
+#define SPI_STATE_READ 2
+
+ bool command;
+
+ /*< public >*/
+ TPMState tpm_state; /* not a QOM object */
+
+} TPMStateSPI;
+
+#define CMD_BYTE_WRITE (1 << 7)
+#define CMD_BYTE_XFER_SZ_MASK 0x1f
+#define TIS_SPI_HIGH_ADDR_BYTE 0xd4
+
+DECLARE_INSTANCE_CHECKER(TPMStateSPI, TPM_TIS_SPI, TYPE_TPM_TIS_SPI)
+
+static inline void tpm_tis_spi_clear_data(TPMStateSPI *spist)
+{
+ spist->spi_state = 0;
+ spist->byte_offset = 0;
+ spist->wait_state_cnt = 0;
+ spist->xfer_size = 0;
+ spist->reg_addr = 0;
+ spist->tis_addr = 0xffffffff;
+
+ return;
+}
+
+/* Callback from TPM to indicate that response is copied */
+static void tpm_tis_spi_request_completed(TPMIf *ti, int ret)
+{
+ TPMStateSPI *spist = TPM_TIS_SPI(ti);
+ TPMState *s = &spist->tpm_state;
+
+ /* Inform the common code. */
+ tpm_tis_request_completed(s, ret);
+}
+
+static enum TPMVersion tpm_tis_spi_get_tpm_version(TPMIf *ti)
+{
+ TPMStateSPI *spist = TPM_TIS_SPI(ti);
+ TPMState *s = &spist->tpm_state;
+
+ return tpm_tis_get_tpm_version(s);
+}
+
+/*
+ * TCG PC Client Platform TPM Profile Specification for TPM 2.0 ver 1.05 rev 14
+ *
+ * For system Software, the TPM has a 64-bit address of 0x0000_0000_FED4_xxxx.
+ * On SPI, the chipset passes the least significant 24 bits to the TPM.
+ * The upper bytes will be used by the chipset to select the TPM’s SPI CS#
+ * signal. Table 9 shows the locality based on the 16 least significant address
+ * bits and assume that either the LPC TPM sync or SPI TPM CS# is used.
+ *
+ */
+static void tpm_tis_spi_write(TPMStateSPI *spist, uint32_t addr, uint8_t val)
+{
+ TPMState *tpm_st = &spist->tpm_state;
+
+ trace_tpm_tis_spi_write(addr, val);
+ tpm_tis_write_data(tpm_st, addr, val, 1);
+}
+
+static uint8_t tpm_tis_spi_read(TPMStateSPI *spist, uint32_t addr)
+{
+ TPMState *tpm_st = &spist->tpm_state;
+ uint8_t data;
+
+ data = tpm_tis_read_data(tpm_st, addr, 1);
+ trace_tpm_tis_spi_read(addr, data);
+ return data;
+}
+
+static Property tpm_tis_spi_properties[] = {
+ DEFINE_PROP_TPMBE("tpmdev", TPMStateSPI, tpm_state.be_driver),
+ DEFINE_PROP_UINT32("irq", TPMStateSPI, tpm_state.irq_num, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tpm_tis_spi_reset(DeviceState *dev)
+{
+ TPMStateSPI *spist = TPM_TIS_SPI(dev);
+ TPMState *s = &spist->tpm_state;
+
+ tpm_tis_spi_clear_data(spist);
+ return tpm_tis_reset(s);
+}
+
+static uint32_t tpm_transfer(SSIPeripheral *ss, uint32_t tx)
+{
+ TPMStateSPI *spist = TPM_TIS_SPI(ss);
+ uint32_t rx = 0;
+ uint8_t byte; /* reversed byte value */
+ uint8_t offset = 0; /* offset of byte in payload */
+ uint8_t index; /* index of data byte in transfer */
+
+ /* new transfer or not */
+ if (spist->command) { /* new transfer start */
+ if (spist->spi_state != SPI_STATE_IDLE) {
+ qemu_log_mask(LOG_GUEST_ERROR, "unexpected new transfer\n");
+ }
+ spist->byte_offset = 0;
+ spist->wait_state_cnt = 0;
+ }
+ /*
+ * Explanation of wait_state:
+ * The original TPM model did not have wait state or "flow control" support
+ * built in. If you wanted to read a TPM register through SPI you sent
+ * the first byte with the read/write bit and size, then three address bytes
+ * and any additional bytes after that were don't care bytes for reads and
+ * the model would begin returning byte data to the SPI reader from the
+ * register address provided. In the real world this would mean that a
+ * TPM device had only the time between the 31st clock and the 32nd clock
+ * to fetch the register data that it had to provide to SPI MISO starting
+ * with the 32nd clock.
+ *
+ * In reality the TPM begins introducing a wait state at the 31st clock
+ * by holding MISO low. This is how it controls the "flow" of the
+ * operation. Once the data the TPM needs to return is ready it will
+ * select bit 31 + (8*N) to send back a 1 which indicates that it will
+ * now start returning data on MISO.
+ *
+ * The same wait states are applied to writes. In either the read or write
+ * case the wait state occurs between the command+address (4 bytes) and the
+ * data (1-n bytes) sections of the SPI frame. The code below introduces
+ * the support for a 32 bit wait state for P10. All reads and writes
+ * through the SPI interface MUST now be aware of the need to do flow
+ * control in order to use the TPM via SPI.
+ *
+ * In conjunction with these changes there were changes made to the SPIM
+ * engine that was introduced in P10 to support the 6x op code which is
+ * used to receive wait state 0s on the MISO line until it sees the b'1'
+ * come back before continuing to read real data from the SPI device(TPM).
+ */
+
+ trace_tpm_tis_spi_transfer_data("Payload byte_offset", spist->byte_offset);
+ /* process payload data */
+ while (offset < 4) {
+ spist->command = false;
+ byte = (tx >> (24 - 8 * offset)) & 0xFF;
+ trace_tpm_tis_spi_transfer_data("Extracted byte", byte);
+ trace_tpm_tis_spi_transfer_data("Payload offset", offset);
+ switch (spist->byte_offset) {
+ case 0: /* command byte */
+ if ((byte & CMD_BYTE_WRITE) == 0) { /* bit-7 */
+ spist->spi_state = SPI_STATE_WRITE;
+ trace_tpm_tis_spi_transfer_event("spi write");
+ } else {
+ spist->spi_state = SPI_STATE_READ;
+ trace_tpm_tis_spi_transfer_event("spi read");
+ }
+ spist->xfer_size = (byte & CMD_BYTE_XFER_SZ_MASK) + 1;
+ trace_tpm_tis_spi_transfer_data("xfer_size", spist->xfer_size);
+ break;
+ case 1: /* 1st address byte */
+ if (byte != TIS_SPI_HIGH_ADDR_BYTE) {
+ qemu_log_mask(LOG_GUEST_ERROR, "incorrect high address 0x%x\n",
+ byte);
+ }
+ spist->reg_addr = byte << 16;
+ trace_tpm_tis_spi_transfer_data("first addr byte", byte);
+ trace_tpm_tis_spi_transfer_addr("reg_addr", spist->reg_addr);
+ break;
+ case 2: /* 2nd address byte */
+ spist->reg_addr |= byte << 8;
+ trace_tpm_tis_spi_transfer_data("second addr byte", byte);
+ trace_tpm_tis_spi_transfer_addr("reg_addr", spist->reg_addr);
+ break;
+ case 3: /* 3rd address byte */
+ spist->reg_addr |= byte;
+ trace_tpm_tis_spi_transfer_data("third addr byte", byte);
+ trace_tpm_tis_spi_transfer_addr("reg_addr", spist->reg_addr);
+ break;
+ default: /* data bytes */
+ if (spist->wait_state_cnt < 4) {
+ spist->wait_state_cnt++;
+ if (spist->wait_state_cnt == 4) {
+ trace_tpm_tis_spi_transfer_data("wait complete, count",
+ spist->wait_state_cnt);
+ rx = rx | (0x01 << (24 - offset * 8));
+ return rx;
+ } else {
+ trace_tpm_tis_spi_transfer_data("in wait state, count",
+ spist->wait_state_cnt);
+ rx = 0;
+ }
+ } else {
+ index = spist->byte_offset - 4;
+ trace_tpm_tis_spi_transfer_data("index", index);
+ trace_tpm_tis_spi_transfer_data("data byte", byte);
+ trace_tpm_tis_spi_transfer_addr("reg_addr", spist->reg_addr);
+ if (index >= spist->xfer_size) {
+ /*
+ * SPI SSI framework limits both rx and tx
+ * to fixed 4-byte with each xfer
+ */
+ trace_tpm_tis_spi_transfer_event("index exceeds xfer_size");
+ return rx;
+ }
+ spist->tis_addr = spist->reg_addr + (index % 4);
+ if (spist->spi_state == SPI_STATE_WRITE) {
+ tpm_tis_spi_write(spist, spist->tis_addr, byte);
+ } else {
+ byte = tpm_tis_spi_read(spist, spist->tis_addr);
+ rx = rx | (byte << (24 - offset * 8));
+ trace_tpm_tis_spi_transfer_data("byte added to response",
+ byte);
+ trace_tpm_tis_spi_transfer_data("offset", offset);
+ }
+ }
+ break;
+ }
+ if ((spist->wait_state_cnt == 0) || (spist->wait_state_cnt == 4)) {
+ offset++;
+ spist->byte_offset++;
+ } else {
+ break;
+ }
+ }
+ return rx;
+}
+
+static int tpm_cs(SSIPeripheral *ss, bool select)
+{
+ TPMStateSPI *spist = TPM_TIS_SPI(ss);
+
+ if (select) {
+ spist->command = false;
+ spist->spi_state = SPI_STATE_IDLE;
+ } else {
+ spist->command = true;
+ }
+ return 0;
+}
+
+static void tpm_realize(SSIPeripheral *dev, Error **errp)
+{
+ TPMStateSPI *spist = TPM_TIS_SPI(dev);
+ TPMState *s = &spist->tpm_state;
+
+ spist->command = true;
+ spist->spi_state = SPI_STATE_IDLE;
+
+ if (!tpm_find()) {
+ error_setg(errp, "at most one TPM device is permitted");
+ return;
+ }
+
+ s->be_driver = qemu_find_tpm_be("tpm0");
+
+ if (!s->be_driver) {
+ error_setg(errp, "unable to find tpm backend device");
+ return;
+ }
+}
+
+static void tpm_tis_spi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ TPMIfClass *tc = TPM_IF_CLASS(klass);
+ SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
+
+ k->transfer = tpm_transfer;
+ k->realize = tpm_realize;
+ k->set_cs = tpm_cs;
+ k->cs_polarity = SSI_CS_LOW;
+
+ device_class_set_legacy_reset(dc, tpm_tis_spi_reset);
+ device_class_set_props(dc, tpm_tis_spi_properties);
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+
+ dc->desc = "PowerNV SPI TPM";
+
+ tc->model = TPM_MODEL_TPM_TIS;
+ tc->request_completed = tpm_tis_spi_request_completed;
+ tc->get_version = tpm_tis_spi_get_tpm_version;
+}
+
+static const TypeInfo tpm_tis_spi_info = {
+ .name = TYPE_TPM_TIS_SPI,
+ .parent = TYPE_SSI_PERIPHERAL,
+ .instance_size = sizeof(TPMStateSPI),
+ .class_init = tpm_tis_spi_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_TPM_IF },
+ { }
+ }
+};
+
+static void tpm_tis_spi_register_types(void)
+{
+ type_register_static(&tpm_tis_spi_info);
+}
+
+type_init(tpm_tis_spi_register_types)
diff --git a/hw/tpm/Kconfig b/hw/tpm/Kconfig
index a46663288c..5951c225cc 100644
--- a/hw/tpm/Kconfig
+++ b/hw/tpm/Kconfig
@@ -5,6 +5,12 @@ config TPM_TIS_I2C
select I2C
select TPM_TIS
+config TPM_TIS_SPI
+ bool
+ depends on TPM
+ select TPM_BACKEND
+ select TPM_TIS
+
config TPM_TIS_ISA
bool
depends on TPM && ISA_BUS
diff --git a/hw/tpm/meson.build b/hw/tpm/meson.build
index 6968e60b3f..e03cfb11b9 100644
--- a/hw/tpm/meson.build
+++ b/hw/tpm/meson.build
@@ -2,6 +2,7 @@ system_ss.add(when: 'CONFIG_TPM_TIS', if_true: files('tpm_tis_common.c'))
system_ss.add(when: 'CONFIG_TPM_TIS_ISA', if_true: files('tpm_tis_isa.c'))
system_ss.add(when: 'CONFIG_TPM_TIS_SYSBUS', if_true: files('tpm_tis_sysbus.c'))
system_ss.add(when: 'CONFIG_TPM_TIS_I2C', if_true: files('tpm_tis_i2c.c'))
+system_ss.add(when: 'CONFIG_TPM_TIS_SPI', if_true: files('tpm_tis_spi.c'))
system_ss.add(when: 'CONFIG_TPM_CRB', if_true: files('tpm_crb.c'))
system_ss.add(when: 'CONFIG_TPM_TIS', if_true: files('tpm_ppi.c'))
system_ss.add(when: 'CONFIG_TPM_CRB', if_true: files('tpm_ppi.c'))
diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events
index fa882dfefe..4b39be13e2 100644
--- a/hw/tpm/trace-events
+++ b/hw/tpm/trace-events
@@ -42,3 +42,10 @@ tpm_tis_i2c_recv(uint8_t data) "TPM I2C read: 0x%X"
tpm_tis_i2c_send(uint8_t data) "TPM I2C write: 0x%X"
tpm_tis_i2c_event(const char *event) "TPM I2C event: %s"
tpm_tis_i2c_send_reg(const char *name, int reg) "TPM I2C write register: %s(0x%X)"
+
+# tpm_tis_spi.c
+tpm_tis_spi_write(uint32_t addr, uint8_t val) "TPM SPI write - addr:0x%X 0x%x"
+tpm_tis_spi_read(uint32_t addr, uint8_t val) "TPM SPI read - addr:0x%X 0x%x"
+tpm_tis_spi_transfer_event(const char *event) "TPM SPI XFER event: %s"
+tpm_tis_spi_transfer_data(const char *name, uint8_t val) "TPM SPI XFER: %s = 0x%x"
+tpm_tis_spi_transfer_addr(const char *name, uint8_t val) "TPM SPI XFER: %s = 0x%08x"
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v3 2/5] tpm/tpm_tis_spi: activation for the PowerNV machines
2024-11-01 18:57 [PATCH v3 0/5] TPM TIS SPI Support dan tan
2024-11-01 18:57 ` [PATCH v3 1/5] tpm/tpm_tis_spi: Support TPM for SPI (Serial Peripheral Interface) dan tan
@ 2024-11-01 18:57 ` dan tan
2024-11-01 18:57 ` [PATCH v3 3/5] tests/qtest/tpm: add unit test to tis-spi dan tan
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: dan tan @ 2024-11-01 18:57 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-ppc, stefanb, pbonzini, farosas, lvivier, clg
The addition to ppc/Kconfig is for building this into the
qemu-system-ppc64 binary. The enablement requires the
following command line argument:
-device tpm-tis-spi,tpmdev=tpm0,bus=pnv-spi-bus.4
Signed-off-by: dan tan <dantan@linux.vnet.ibm.com>
---
hw/ppc/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index b44d91bebb..56dcf5902e 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -39,6 +39,7 @@ config POWERNV
select PCI_POWERNV
select PCA9552
select PCA9554
+ select TPM_TIS_SPI
select SERIAL_ISA
select SSI
select SSI_M25P80
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v3 3/5] tests/qtest/tpm: add unit test to tis-spi
2024-11-01 18:57 [PATCH v3 0/5] TPM TIS SPI Support dan tan
2024-11-01 18:57 ` [PATCH v3 1/5] tpm/tpm_tis_spi: Support TPM for SPI (Serial Peripheral Interface) dan tan
2024-11-01 18:57 ` [PATCH v3 2/5] tpm/tpm_tis_spi: activation for the PowerNV machines dan tan
@ 2024-11-01 18:57 ` dan tan
2024-11-01 18:57 ` [PATCH v3 4/5] tpm/tpm_tis_spi: Support TPM for SPI (rev 3) dan tan
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: dan tan @ 2024-11-01 18:57 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-ppc, stefanb, pbonzini, farosas, lvivier, clg
Add qtest cases to exercise main TPM locality functionality
The TPM device emulation is provided by swtpm, which is TCG
TPM 2.0, and TCG TPM TIS compliant. See
https://trustedcomputinggroup.org/wp-content/uploads/TCG_PC_Client_Platform_TPM_Profile_PTP_2.0_r1.03_v22.pdf
https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientTPMInterfaceSpecification_TIS__1-3_27_03212013.pdf
The SPI registers are specific to the PowerNV platform
architecture
Signed-off-by: dan tan <dantan@linux.vnet.ibm.com>
---
hw/tpm/tpm_tis_spi.c | 2 +-
tests/qtest/tpm-tis-spi-pnv-test.c | 700 +++++++++++++++++++++++++++++
tests/qtest/meson.build | 3 +-
3 files changed, 703 insertions(+), 2 deletions(-)
create mode 100644 tests/qtest/tpm-tis-spi-pnv-test.c
diff --git a/hw/tpm/tpm_tis_spi.c b/hw/tpm/tpm_tis_spi.c
index 5aacae8467..f8a78a5cfb 100644
--- a/hw/tpm/tpm_tis_spi.c
+++ b/hw/tpm/tpm_tis_spi.c
@@ -302,7 +302,7 @@ static void tpm_tis_spi_class_init(ObjectClass *klass, void *data)
device_class_set_props(dc, tpm_tis_spi_properties);
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- dc->desc = "PowerNV SPI TPM";
+ dc->desc = "SPI TPM";
tc->model = TPM_MODEL_TPM_TIS;
tc->request_completed = tpm_tis_spi_request_completed;
diff --git a/tests/qtest/tpm-tis-spi-pnv-test.c b/tests/qtest/tpm-tis-spi-pnv-test.c
new file mode 100644
index 0000000000..a367564dda
--- /dev/null
+++ b/tests/qtest/tpm-tis-spi-pnv-test.c
@@ -0,0 +1,700 @@
+/*
+ * QTest testcase for a Nuvoton NPCT75x TPM SPI device
+ * running on the PowerNV machine.
+ *
+ * Copyright (c) 2024, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include <glib/gstdio.h>
+#include "libqtest-single.h"
+#include "hw/acpi/tpm.h"
+#include "hw/pci/pci_ids.h"
+#include "qtest_aspeed.h"
+#include "tpm-emu.h"
+#include "hw/ssi/pnv_spi_regs.h"
+#include "pnv-xscom.h"
+
+#define SPI_TPM_BASE 0xc0080
+#define SPI_SHIFT_COUNTER_N1 0x30000000
+#define SPI_SHIFT_COUNTER_N2 0x40000000
+
+#define CFG_COUNT_COMPARE_1 0x0000000200000000
+#define MM_REG_RDR_MATCH 0x00000000ff01ff00
+#define SEQ_OP_REG_BASIC 0x1134416200100000
+
+#define TPM_TIS_8BITS_MASK 0xff
+#define SPI_TPM_TIS_ADDR 0xd40000
+#define TPM_WRITE_OP 0x0
+#define TPM_READ_OP 0x80
+
+#define SHORT_MAX_RETRIES 5
+#define LONG_MAX_RETRIES 10
+
+static const uint8_t TPM_CMD[12] =
+ "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00";
+
+#define DPRINTF(fmt, ...) do { \
+ if (DEBUG_TIS_TEST) { \
+ printf(fmt, ## __VA_ARGS__); \
+ } \
+} while (0)
+
+#define DEBUG_TIS_TEST 0
+
+#define DPRINTF_ACCESS \
+ DPRINTF("%s: %d: locty=%d l=%d access=0x%02x pending_request_flag=0x%x\n", \
+ __func__, __LINE__, locty, l, access, pending_request_flag)
+
+#define DPRINTF_STS \
+ DPRINTF("%s: %d: sts = 0x%08x\n", __func__, __LINE__, sts)
+
+static uint64_t pnv_spi_tpm_read(const PnvChip *chip, uint32_t reg);
+static void pnv_spi_tpm_write(const PnvChip *chip, uint32_t reg, uint64_t val);
+static uint64_t spi_read_reg(const PnvChip *chip);
+static void spi_write_reg(const PnvChip *chip, uint64_t val);
+static void spi_access_start(const PnvChip *chip, bool n2, uint8_t bytes,
+ uint8_t tpm_op, uint32_t tpm_reg);
+
+static inline void tpm_reg_writeb(const PnvChip *c,
+ int locl,
+ uint8_t reg,
+ uint8_t val)
+{
+ uint32_t tpm_reg_locl = SPI_TPM_TIS_ADDR | (locl << TPM_TIS_LOCALITY_SHIFT);
+
+ spi_access_start(c, false, 1, TPM_WRITE_OP, tpm_reg_locl | reg);
+ spi_write_reg(c, bswap64(val));
+}
+
+static inline uint8_t tpm_reg_readb(const PnvChip *c, int locl, uint8_t reg)
+{
+ uint32_t tpm_reg_locl = SPI_TPM_TIS_ADDR | (locl << TPM_TIS_LOCALITY_SHIFT);
+
+ spi_access_start(c, true, 1, TPM_READ_OP, tpm_reg_locl | reg);
+ return spi_read_reg(c);
+}
+
+static inline void tpm_reg_writew(const PnvChip *c,
+ int locl,
+ uint8_t reg,
+ uint32_t val)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ tpm_reg_writeb(c, locl, reg + i, ((val >> (8 * i)) & 0xff));
+ }
+}
+
+static inline uint32_t tpm_reg_readw(const PnvChip *c, int locl, uint8_t reg)
+{
+ uint32_t val = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ val |= tpm_reg_readb(c, locl, reg + i) << (8 * i);
+ }
+ return val;
+}
+
+static void pnv_spi_tpm_write(const PnvChip *chip,
+ uint32_t reg,
+ uint64_t val)
+{
+ uint32_t pcba = SPI_TPM_BASE + reg;
+ qtest_writeq(global_qtest, pnv_xscom_addr(chip, pcba), val);
+}
+
+static uint64_t pnv_spi_tpm_read(const PnvChip *chip, uint32_t reg)
+{
+ uint32_t pcba = SPI_TPM_BASE + reg;
+ return qtest_readq(global_qtest, pnv_xscom_addr(chip, pcba));
+}
+
+static void spi_access_start(const PnvChip *chip,
+ bool n2,
+ uint8_t bytes,
+ uint8_t tpm_op,
+ uint32_t tpm_reg)
+{
+ uint64_t cfg_reg;
+ uint64_t reg_op;
+ uint64_t seq_op = SEQ_OP_REG_BASIC;
+
+ cfg_reg = pnv_spi_tpm_read(chip, SPI_CLK_CFG_REG);
+ if (cfg_reg != CFG_COUNT_COMPARE_1) {
+ pnv_spi_tpm_write(chip, SPI_CLK_CFG_REG, CFG_COUNT_COMPARE_1);
+ }
+ /* bytes - sequencer operation register bits 24:31 */
+ if (n2) {
+ seq_op |= SPI_SHIFT_COUNTER_N2 | (bytes << 0x18);
+ } else {
+ seq_op |= SPI_SHIFT_COUNTER_N1 | (bytes << 0x18);
+ }
+ pnv_spi_tpm_write(chip, SPI_SEQ_OP_REG, seq_op);
+ pnv_spi_tpm_write(chip, SPI_MM_REG, MM_REG_RDR_MATCH);
+ pnv_spi_tpm_write(chip, SPI_CTR_CFG_REG, (uint64_t)0);
+ reg_op = bswap64(tpm_op) | ((uint64_t)tpm_reg << 0x20);
+ pnv_spi_tpm_write(chip, SPI_XMIT_DATA_REG, reg_op);
+}
+
+static void spi_op_complete(const PnvChip *chip)
+{
+ uint64_t cfg_reg;
+
+ cfg_reg = pnv_spi_tpm_read(chip, SPI_CLK_CFG_REG);
+ g_assert_cmpuint(CFG_COUNT_COMPARE_1, ==, cfg_reg);
+ pnv_spi_tpm_write(chip, SPI_CLK_CFG_REG, 0);
+}
+
+static void spi_write_reg(const PnvChip *chip, uint64_t val)
+{
+ int i;
+ uint64_t spi_sts;
+
+ for (i = 0; i < LONG_MAX_RETRIES; i++) {
+ spi_sts = pnv_spi_tpm_read(chip, SPI_STS_REG);
+ if (GETFIELD(SPI_STS_TDR_FULL, spi_sts) == 1) {
+ sleep(0.5);
+ } else {
+ break;
+ }
+ }
+ /* cannot write if SPI_STS_TDR_FULL bit is still set */
+ g_assert_cmpuint(0, ==, GETFIELD(SPI_STS_TDR_FULL, spi_sts));
+ pnv_spi_tpm_write(chip, SPI_XMIT_DATA_REG, val);
+
+ for (i = 0; i < SHORT_MAX_RETRIES; i++) {
+ spi_sts = pnv_spi_tpm_read(chip, SPI_STS_REG);
+ if (GETFIELD(SPI_STS_SHIFTER_FSM, spi_sts) & FSM_DONE) {
+ break;
+ } else {
+ sleep(0.1);
+ }
+ }
+ /* it should be done given the amount of time */
+ g_assert_cmpuint(0, ==, GETFIELD(SPI_STS_SHIFTER_FSM, spi_sts) & FSM_DONE);
+ spi_op_complete(chip);
+}
+
+static uint64_t spi_read_reg(const PnvChip *chip)
+{
+ int i;
+ uint64_t spi_sts, val = 0;
+
+ for (i = 0; i < LONG_MAX_RETRIES; i++) {
+ spi_sts = pnv_spi_tpm_read(chip, SPI_STS_REG);
+ if (GETFIELD(SPI_STS_RDR_FULL, spi_sts) == 1) {
+ val = pnv_spi_tpm_read(chip, SPI_RCV_DATA_REG);
+ break;
+ }
+ sleep(0.5);
+ }
+ for (i = 0; i < SHORT_MAX_RETRIES; i++) {
+ spi_sts = pnv_spi_tpm_read(chip, SPI_STS_REG);
+ if (GETFIELD(SPI_STS_RDR_FULL, spi_sts) == 1) {
+ sleep(0.1);
+ } else {
+ break;
+ }
+ }
+ /* SPI_STS_RDR_FULL bit should be reset after read */
+ g_assert_cmpuint(0, ==, GETFIELD(SPI_STS_RDR_FULL, spi_sts));
+ spi_op_complete(chip);
+ return val;
+}
+
+static void tpm_set_verify_loc(const PnvChip *chip, uint8_t loc)
+{
+ uint8_t access;
+ uint32_t tpm_sts;
+
+ g_test_message("TPM locality %d tests:", loc);
+ access = tpm_reg_readb(chip, loc, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ tpm_reg_writeb(chip, loc, TPM_TIS_REG_ACCESS, TPM_TIS_ACCESS_SEIZE);
+ tpm_reg_writeb(chip, loc, TPM_TIS_REG_ACCESS, TPM_TIS_ACCESS_REQUEST_USE);
+
+ access = tpm_reg_readb(chip, loc, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+ g_test_message("\tACCESS REG = 0x%x checked", access);
+
+ /* test tpm status register */
+ tpm_sts = tpm_reg_readw(chip, loc, TPM_TIS_REG_STS);
+ g_assert_cmpuint((tpm_sts & TPM_TIS_8BITS_MASK), ==, 0);
+ g_test_message("\tTPM STATUS: 0x%x, verified", tpm_sts);
+
+ /* release access */
+ tpm_reg_writeb(chip, loc, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+ access = tpm_reg_readb(chip, loc, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+ g_test_message("\tRELEASED ACCESS: 0x%x, checked", access);
+}
+
+static void test_spi_tpm_basic(const void *data)
+{
+ const PnvChip *chip = data;
+ uint32_t didvid, tpm_sts, en_int;
+ uint8_t access;
+
+ g_test_message("TPM TIS SPI interface basic tests:");
+ /* vendor ID and device ID ... check against the known value*/
+ spi_access_start(chip, true, 4, 0x83,
+ SPI_TPM_TIS_ADDR | TPM_TIS_REG_DID_VID);
+ didvid = spi_read_reg(chip);
+ g_assert_cmpint((didvid >> 16), ==, bswap16(TPM_TIS_TPM_VID));
+ g_assert_cmpint((didvid & 0xffff), ==, bswap16(TPM_TIS_TPM_DID));
+ g_test_message("\tDID_VID = 0x%x, verified", didvid);
+
+ /* access register, default see TCG TIS Spec (v1.3) table-14 */
+ access = tpm_reg_readb(chip, 0, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+ g_test_message("\tACCESS REG = 0x%x, checked", access);
+
+ /* interrupt enable register, default see TCG TIS Spec (v1.3) table-19 */
+ en_int = tpm_reg_readw(chip, 0, TPM_TIS_REG_INT_ENABLE);
+ g_assert_cmpuint(en_int, ==, TPM_TIS_INT_POLARITY_LOW_LEVEL);
+ g_test_message("\tINT ENABLE REG: 0x%x, verified", en_int);
+
+ /* status register, default see TCG TIS Spec (v1.3) table-15 */
+ tpm_sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ /* for no active locality */
+ g_assert_cmpuint(tpm_sts, ==, 0xffffffff);
+ g_test_message("\tTPM STATUS: 0x%x, verified", tpm_sts);
+}
+
+static void test_spi_tpm_locality(const void *data)
+{
+ const PnvChip *chip = data;
+ uint8_t locality;
+
+ /* Locality 4 has special security restrictions, testing 0-3 */
+ for (locality = 0; locality < TPM_TIS_NUM_LOCALITIES - 1; locality++) {
+ tpm_set_verify_loc(chip, locality);
+ }
+}
+
+/*
+ * Test case for seizing access by a higher number locality
+ */
+static void test_spi_tpm_access_seize_test(const void *data)
+{
+ const PnvChip *chip = data;
+ int locty, l;
+ uint8_t access;
+ uint8_t pending_request_flag;
+
+ g_test_message("TPM TIS SPI access seize tests:");
+ /* do not test locality 4 (hw only) */
+ for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) {
+ pending_request_flag = 0;
+
+ access = tpm_reg_readb(chip, locty, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* request use of locality */
+ tpm_reg_writeb(chip, locty, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_REQUEST_USE);
+
+ access = tpm_reg_readb(chip, locty, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* lower localities cannot seize access */
+ for (l = 0; l < locty; l++) {
+ /* lower locality is not active */
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* try to request use from 'l' */
+ tpm_reg_writeb(chip, l, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_REQUEST_USE);
+
+ /*
+ * requesting use from 'l' was not possible;
+ * we must see REQUEST_USE and possibly PENDING_REQUEST
+ */
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_REQUEST_USE |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /*
+ * locality 'locty' must be unchanged;
+ * we must see PENDING_REQUEST
+ */
+ access = tpm_reg_readb(chip, locty, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY |
+ TPM_TIS_ACCESS_PENDING_REQUEST |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* try to seize from 'l' */
+ tpm_reg_writeb(chip, l, TPM_TIS_REG_ACCESS, TPM_TIS_ACCESS_SEIZE);
+ /* seize from 'l' was not possible */
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_REQUEST_USE |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* locality 'locty' must be unchanged */
+ access = tpm_reg_readb(chip, locty, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY |
+ TPM_TIS_ACCESS_PENDING_REQUEST |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /*
+ * on the next loop we will have a PENDING_REQUEST flag
+ * set for locality 'l'
+ */
+ pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST;
+ }
+
+ /*
+ * higher localities can 'seize' access but not 'request use';
+ * note: this will activate first l+1, then l+2 etc.
+ */
+ for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES - 1; l++) {
+ /* try to 'request use' from 'l' */
+ tpm_reg_writeb(chip, l, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_REQUEST_USE);
+
+ /*
+ * requesting use from 'l' was not possible; we should see
+ * REQUEST_USE and may see PENDING_REQUEST
+ */
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_REQUEST_USE |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /*
+ * locality 'l-1' must be unchanged; we should always
+ * see PENDING_REQUEST from 'l' requesting access
+ */
+ access = tpm_reg_readb(chip, l - 1, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY |
+ TPM_TIS_ACCESS_PENDING_REQUEST |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* try to seize from 'l' */
+ tpm_reg_writeb(chip, l, TPM_TIS_REG_ACCESS, TPM_TIS_ACCESS_SEIZE);
+
+ /* seize from 'l' was possible */
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* l - 1 should show that it has BEEN_SEIZED */
+ access = tpm_reg_readb(chip, l - 1, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_BEEN_SEIZED |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* clear the BEEN_SEIZED flag and make sure it's gone */
+ tpm_reg_writeb(chip, l - 1, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_BEEN_SEIZED);
+
+ access = tpm_reg_readb(chip, l - 1, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+ }
+
+ /*
+ * PENDING_REQUEST will not be set if locty = 0 since all localities
+ * were active; in case of locty = 1, locality 0 will be active
+ * but no PENDING_REQUEST anywhere
+ */
+ if (locty <= 1) {
+ pending_request_flag = 0;
+ }
+
+ /* release access from l - 1; this activates locty - 1 */
+ l--;
+
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+
+ DPRINTF("%s: %d: relinquishing control on l = %d\n",
+ __func__, __LINE__, l);
+ tpm_reg_writeb(chip, l, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ for (l = locty - 1; l >= 0; l--) {
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* release this locality */
+ tpm_reg_writeb(chip, l, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+
+ if (l == 1) {
+ pending_request_flag = 0;
+ }
+ }
+
+ /* no locality may be active now */
+ for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) {
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+ }
+ g_test_message("\tTPM locality %d seize tests: passed", locty);
+ }
+}
+
+/*
+ * Test case for getting access when higher number locality relinquishes access
+ */
+static void test_spi_tpm_access_release_test(const void *data)
+{
+ const PnvChip *chip = data;
+ int locty, l;
+ uint8_t access;
+ uint8_t pending_request_flag;
+
+ g_test_message("TPM TIS SPI access release tests:");
+ /* do not test locality 4 (hw only) */
+ for (locty = TPM_TIS_NUM_LOCALITIES - 2; locty >= 0; locty--) {
+ pending_request_flag = 0;
+
+ access = tpm_reg_readb(chip, locty, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* request use of locality */
+ tpm_reg_writeb(chip, locty, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_REQUEST_USE);
+ access = tpm_reg_readb(chip, locty, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ /* request use of all other localities */
+ for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) {
+ if (l == locty) {
+ continue;
+ }
+ /*
+ * request use of locality 'l' -- we MUST see REQUEST USE and
+ * may see PENDING_REQUEST
+ */
+ tpm_reg_writeb(chip, l, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_REQUEST_USE);
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_REQUEST_USE |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+ pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST;
+ }
+ /* release locality 'locty' */
+ tpm_reg_writeb(chip, locty, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+ /*
+ * highest locality should now be active; release it and make sure the
+ * next highest locality is active afterwards
+ */
+ for (l = TPM_TIS_NUM_LOCALITIES - 2; l >= 0; l--) {
+ if (l == locty) {
+ continue;
+ }
+ /* 'l' should be active now */
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+ /* 'l' relinquishes access */
+ tpm_reg_writeb(chip, l, TPM_TIS_REG_ACCESS,
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+ access = tpm_reg_readb(chip, l, TPM_TIS_REG_ACCESS);
+ DPRINTF_ACCESS;
+ if (l == 1 || (locty <= 1 && l == 2)) {
+ pending_request_flag = 0;
+ }
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ pending_request_flag |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+ }
+ g_test_message("\tTPM locality %d seize tests: passed", locty);
+ }
+}
+
+/*
+ * Test case for transmitting packets
+ */
+static void test_spi_tpm_transmit_test(const void *data)
+{
+ const PnvChip *chip = data;
+ uint16_t bcount;
+ uint8_t access;
+ uint32_t sts;
+ int i;
+
+ g_test_message("TPM TIS SPI transmit tests:");
+ /* request use of locality 0 */
+ tpm_reg_writeb(chip, 0, TPM_TIS_REG_ACCESS, TPM_TIS_ACCESS_REQUEST_USE);
+ access = tpm_reg_readb(chip, 0, TPM_TIS_REG_ACCESS);
+ g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY |
+ TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
+
+ sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ DPRINTF_STS;
+
+ g_assert_cmpint(sts & 0xff, ==, 0);
+
+ bcount = (sts >> 8) & 0xffff;
+ g_test_message("\t\tbcount: %x, sts: %x", bcount, sts);
+ g_assert_cmpint(bcount, >=, 128);
+
+ tpm_reg_writew(chip, 0, TPM_TIS_REG_STS, TPM_TIS_STS_COMMAND_READY);
+ sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ DPRINTF_STS;
+ g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_COMMAND_READY);
+
+ /* transmit command */
+ for (i = 0; i < sizeof(TPM_CMD); i++) {
+ tpm_reg_writeb(chip, 0, TPM_TIS_REG_DATA_FIFO, TPM_CMD[i]);
+ sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ DPRINTF_STS;
+ if (i < sizeof(TPM_CMD) - 1) {
+ g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_EXPECT |
+ TPM_TIS_STS_VALID);
+ } else {
+ g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_VALID);
+ }
+ }
+ g_test_message("\ttransmit tests, check TPM_TIS_STS_EXPECT");
+
+ /* start processing */
+ tpm_reg_writew(chip, 0, TPM_TIS_REG_STS, TPM_TIS_STS_TPM_GO);
+
+ uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND;
+ do {
+ sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) {
+ break;
+ }
+ } while (g_get_monotonic_time() < end_time);
+
+ sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ DPRINTF_STS;
+ g_assert_cmpint(sts & 0xff, == , TPM_TIS_STS_VALID |
+ TPM_TIS_STS_DATA_AVAILABLE);
+ /* TCG TIS Spec (v1.3) table-15 */
+ g_test_message("\ttransmit tests, check tpmGo (w) & dataAvail (r)");
+ bcount = (sts >> 8) & 0xffff;
+
+ /* read response */
+ uint8_t tpm_msg[sizeof(struct tpm_hdr)];
+ g_assert_cmpint(sizeof(tpm_msg), ==, bcount);
+
+ for (i = 0; i < sizeof(tpm_msg); i++) {
+ tpm_msg[i] = tpm_reg_readb(chip, 0, TPM_TIS_REG_DATA_FIFO);
+ sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ DPRINTF_STS;
+ if (sts & TPM_TIS_STS_DATA_AVAILABLE) {
+ g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount);
+ }
+ }
+ g_test_message("\treceive tests, passed");
+ /* relinquish use of locality 0 */
+ tpm_reg_writeb(chip, 0, TPM_TIS_REG_ACCESS, TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+ access = tpm_reg_readb(chip, 0, TPM_TIS_REG_ACCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ char *args;
+ GThread *thread;
+ TPMTestState test;
+ g_autofree char *tmp_path = g_dir_make_tmp("qemu-tpm-tis-spi-test.XXXXXX",
+ NULL);
+
+ module_call_init(MODULE_INIT_QOM);
+ g_test_init(&argc, &argv, NULL);
+
+ test.addr = g_new0(SocketAddress, 1);
+ test.addr->type = SOCKET_ADDRESS_TYPE_UNIX;
+ test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL);
+ g_mutex_init(&test.data_mutex);
+ g_cond_init(&test.data_cond);
+ test.data_cond_signal = false;
+ test.tpm_version = TPM_VERSION_2_0;
+
+ thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test);
+ tpm_emu_test_wait_cond(&test);
+
+ args = g_strdup_printf("-m 2G -machine powernv10 -smp 2,cores=2,"
+ "threads=1 -accel tcg,thread=single -nographic "
+ "-chardev socket,id=chrtpm,path=%s "
+ "-tpmdev emulator,id=tpm0,chardev=chrtpm "
+ "-device tpm-tis-spi,tpmdev=tpm0,bus=pnv-spi-bus.4",
+ test.addr->u.q_unix.path);
+ qtest_start(args);
+ qtest_add_data_func("pnv-xscom/tpm-tis-spi/basic_test",
+ &pnv_chips[3], test_spi_tpm_basic);
+ qtest_add_data_func("pnv-xscom/tpm-tis-spi/locality_test",
+ &pnv_chips[3], test_spi_tpm_locality);
+ qtest_add_data_func("pnv-xscom/tpm-tis-spi/access_seize_test",
+ &pnv_chips[3], test_spi_tpm_access_seize_test);
+ qtest_add_data_func("pnv-xscom/tpm-tis-spi/access_release_test",
+ &pnv_chips[3], test_spi_tpm_access_release_test);
+ qtest_add_data_func("pnv-xscom/tpm-tis-spi/data_transmit_test",
+ &pnv_chips[3], test_spi_tpm_transmit_test);
+ ret = g_test_run();
+
+ qtest_end();
+ g_thread_join(thread);
+ g_unlink(test.addr->u.q_unix.path);
+ qapi_free_SocketAddress(test.addr);
+ g_rmdir(tmp_path);
+ g_free(args);
+ return ret;
+}
+
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index e8be8b3942..6b1aed929e 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -4,7 +4,6 @@ slow_qtests = {
'bios-tables-test' : 910,
'cdrom-test' : 610,
'device-introspect-test' : 720,
- 'ide-test' : 120,
'migration-test' : 480,
'npcm7xx_pwm-test': 300,
'npcm7xx_watchdog_timer-test': 120,
@@ -177,6 +176,7 @@ qtests_ppc64 = \
(config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \
(config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) + \
(config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-spi-seeprom-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_POWERNV') ? ['tpm-tis-spi-pnv-test'] : []) + \
(config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) + \
(config_all_devices.has_key('CONFIG_PSERIES') ? ['numa-test'] : []) + \
(config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) + \
@@ -348,6 +348,7 @@ qtests = {
'tpm-tis-i2c-test': [io, tpmemu_files, 'qtest_aspeed.c'],
'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'],
'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'],
+ 'tpm-tis-spi-pnv-test': [io, tpmemu_files],
'virtio-net-failover': files('migration-helpers.c'),
'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'),
'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'),
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v3 4/5] tpm/tpm_tis_spi: Support TPM for SPI (rev 3)
2024-11-01 18:57 [PATCH v3 0/5] TPM TIS SPI Support dan tan
` (2 preceding siblings ...)
2024-11-01 18:57 ` [PATCH v3 3/5] tests/qtest/tpm: add unit test to tis-spi dan tan
@ 2024-11-01 18:57 ` dan tan
2024-11-01 18:57 ` [PATCH v3 5/5] tests/qtest/tpm: add unit test to tis-spi " dan tan
2024-11-01 19:25 ` [PATCH v3 0/5] TPM TIS SPI Support Stefan Berger
5 siblings, 0 replies; 7+ messages in thread
From: dan tan @ 2024-11-01 18:57 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-ppc, stefanb, pbonzini, farosas, lvivier, clg
- moved variable tis_addr from TPMStateSPI struct to local
- added the VM suspend/resume support:
- added vmstate_tpm_tis_spi declaration
- added tpm_tis_spi_pre_save() function
- fixed trace formatting string
Signed-off-by: dan tan <dantan@linux.vnet.ibm.com>
---
hw/tpm/tpm_tis_spi.c | 50 ++++++++++++++++++++++++++++++++++++--------
hw/tpm/trace-events | 8 +++----
2 files changed, 45 insertions(+), 13 deletions(-)
diff --git a/hw/tpm/tpm_tis_spi.c b/hw/tpm/tpm_tis_spi.c
index f8a78a5cfb..4aee1c6d6c 100644
--- a/hw/tpm/tpm_tis_spi.c
+++ b/hw/tpm/tpm_tis_spi.c
@@ -17,6 +17,7 @@
#include "trace.h"
#include "tpm_tis.h"
#include "hw/ssi/ssi.h"
+#include "migration/vmstate.h"
typedef struct TPMStateSPI {
/*< private >*/
@@ -26,7 +27,6 @@ typedef struct TPMStateSPI {
uint8_t wait_state_cnt; /* wait state counter */
uint8_t xfer_size; /* data size of transfer */
uint32_t reg_addr; /* register address of transfer */
- uint32_t tis_addr; /* tis address including locty */
uint8_t spi_state; /* READ / WRITE / IDLE */
#define SPI_STATE_IDLE 0
@@ -46,14 +46,47 @@ typedef struct TPMStateSPI {
DECLARE_INSTANCE_CHECKER(TPMStateSPI, TPM_TIS_SPI, TYPE_TPM_TIS_SPI)
+static int tpm_tis_spi_pre_save(void *opaque)
+{
+ TPMStateSPI *spist = opaque;
+
+ return tpm_tis_pre_save(&spist->tpm_state);
+}
+
+static const VMStateDescription vmstate_tpm_tis_spi = {
+ .name = "tpm-tis-spi",
+ .version_id = 0,
+ .pre_save = tpm_tis_spi_pre_save,
+ .fields = (const VMStateField[]) {
+ VMSTATE_BUFFER(tpm_state.buffer, TPMStateSPI),
+ VMSTATE_UINT16(tpm_state.rw_offset, TPMStateSPI),
+ VMSTATE_UINT8(tpm_state.active_locty, TPMStateSPI),
+ VMSTATE_UINT8(tpm_state.aborting_locty, TPMStateSPI),
+ VMSTATE_UINT8(tpm_state.next_locty, TPMStateSPI),
+
+ VMSTATE_STRUCT_ARRAY(tpm_state.loc, TPMStateSPI,
+ TPM_TIS_NUM_LOCALITIES, 0,
+ vmstate_locty, TPMLocality),
+
+ /* spi specifics */
+ VMSTATE_UINT8(byte_offset, TPMStateSPI),
+ VMSTATE_UINT8(wait_state_cnt, TPMStateSPI),
+ VMSTATE_UINT8(xfer_size, TPMStateSPI),
+ VMSTATE_UINT32(reg_addr, TPMStateSPI),
+ VMSTATE_UINT8(spi_state, TPMStateSPI),
+ VMSTATE_BOOL(command, TPMStateSPI),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static inline void tpm_tis_spi_clear_data(TPMStateSPI *spist)
{
- spist->spi_state = 0;
+ spist->spi_state = SPI_STATE_IDLE;
spist->byte_offset = 0;
spist->wait_state_cnt = 0;
spist->xfer_size = 0;
spist->reg_addr = 0;
- spist->tis_addr = 0xffffffff;
return;
}
@@ -126,6 +159,7 @@ static uint32_t tpm_transfer(SSIPeripheral *ss, uint32_t tx)
uint8_t byte; /* reversed byte value */
uint8_t offset = 0; /* offset of byte in payload */
uint8_t index; /* index of data byte in transfer */
+ uint32_t tis_addr; /* tis address including locty */
/* new transfer or not */
if (spist->command) { /* new transfer start */
@@ -230,11 +264,11 @@ static uint32_t tpm_transfer(SSIPeripheral *ss, uint32_t tx)
trace_tpm_tis_spi_transfer_event("index exceeds xfer_size");
return rx;
}
- spist->tis_addr = spist->reg_addr + (index % 4);
+ tis_addr = spist->reg_addr + (index % 4);
if (spist->spi_state == SPI_STATE_WRITE) {
- tpm_tis_spi_write(spist, spist->tis_addr, byte);
+ tpm_tis_spi_write(spist, tis_addr, byte);
} else {
- byte = tpm_tis_spi_read(spist, spist->tis_addr);
+ byte = tpm_tis_spi_read(spist, tis_addr);
rx = rx | (byte << (24 - offset * 8));
trace_tpm_tis_spi_transfer_data("byte added to response",
byte);
@@ -271,9 +305,6 @@ static void tpm_realize(SSIPeripheral *dev, Error **errp)
TPMStateSPI *spist = TPM_TIS_SPI(dev);
TPMState *s = &spist->tpm_state;
- spist->command = true;
- spist->spi_state = SPI_STATE_IDLE;
-
if (!tpm_find()) {
error_setg(errp, "at most one TPM device is permitted");
return;
@@ -303,6 +334,7 @@ static void tpm_tis_spi_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->desc = "SPI TPM";
+ dc->vmsd = &vmstate_tpm_tis_spi;
tc->model = TPM_MODEL_TPM_TIS;
tc->request_completed = tpm_tis_spi_request_completed;
diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events
index 4b39be13e2..0324ceb6d0 100644
--- a/hw/tpm/trace-events
+++ b/hw/tpm/trace-events
@@ -44,8 +44,8 @@ tpm_tis_i2c_event(const char *event) "TPM I2C event: %s"
tpm_tis_i2c_send_reg(const char *name, int reg) "TPM I2C write register: %s(0x%X)"
# tpm_tis_spi.c
-tpm_tis_spi_write(uint32_t addr, uint8_t val) "TPM SPI write - addr:0x%X 0x%x"
-tpm_tis_spi_read(uint32_t addr, uint8_t val) "TPM SPI read - addr:0x%X 0x%x"
+tpm_tis_spi_write(uint32_t addr, uint8_t val) "TPM SPI write - addr:0x%08X 0x%02x"
+tpm_tis_spi_read(uint32_t addr, uint8_t val) "TPM SPI read - addr:0x%08X 0x%02x"
tpm_tis_spi_transfer_event(const char *event) "TPM SPI XFER event: %s"
-tpm_tis_spi_transfer_data(const char *name, uint8_t val) "TPM SPI XFER: %s = 0x%x"
-tpm_tis_spi_transfer_addr(const char *name, uint8_t val) "TPM SPI XFER: %s = 0x%08x"
+tpm_tis_spi_transfer_data(const char *name, uint8_t val) "TPM SPI XFER: %s = 0x%02x"
+tpm_tis_spi_transfer_addr(const char *name, uint32_t addr) "TPM SPI XFER: %s = 0x%08x"
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v3 5/5] tests/qtest/tpm: add unit test to tis-spi (rev 3)
2024-11-01 18:57 [PATCH v3 0/5] TPM TIS SPI Support dan tan
` (3 preceding siblings ...)
2024-11-01 18:57 ` [PATCH v3 4/5] tpm/tpm_tis_spi: Support TPM for SPI (rev 3) dan tan
@ 2024-11-01 18:57 ` dan tan
2024-11-01 19:25 ` [PATCH v3 0/5] TPM TIS SPI Support Stefan Berger
5 siblings, 0 replies; 7+ messages in thread
From: dan tan @ 2024-11-01 18:57 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-ppc, stefanb, pbonzini, farosas, lvivier, clg
- removed the function prototypes declaration
- fixed code format to comply with convention
- changed function names and variable names to be the same
as the tpm-tis-i2c test.
- change hard coded numbers to #define's with meaningful
names that are identifiable with spec documentation
Signed-off-by: dan tan <dantan@linux.vnet.ibm.com>
---
tests/qtest/tpm-tis-spi-pnv-test.c | 220 +++++++++++++++--------------
tests/qtest/meson.build | 1 +
2 files changed, 116 insertions(+), 105 deletions(-)
diff --git a/tests/qtest/tpm-tis-spi-pnv-test.c b/tests/qtest/tpm-tis-spi-pnv-test.c
index a367564dda..2d6e1186cf 100644
--- a/tests/qtest/tpm-tis-spi-pnv-test.c
+++ b/tests/qtest/tpm-tis-spi-pnv-test.c
@@ -19,6 +19,9 @@
#define SPI_TPM_BASE 0xc0080
#define SPI_SHIFT_COUNTER_N1 0x30000000
#define SPI_SHIFT_COUNTER_N2 0x40000000
+#define SPI_RWX_OPCODE_SHIFT 56
+#define SPI_RWX_ADDR_SHIFT 32
+#define SPI_CMD_DATA_SHIFT 56
#define CFG_COUNT_COMPARE_1 0x0000000200000000
#define MM_REG_RDR_MATCH 0x00000000ff01ff00
@@ -26,6 +29,7 @@
#define TPM_TIS_8BITS_MASK 0xff
#define SPI_TPM_TIS_ADDR 0xd40000
+#define SPI_EXTEND 0x03
#define TPM_WRITE_OP 0x0
#define TPM_READ_OP 0x80
@@ -50,53 +54,11 @@ static const uint8_t TPM_CMD[12] =
#define DPRINTF_STS \
DPRINTF("%s: %d: sts = 0x%08x\n", __func__, __LINE__, sts)
-static uint64_t pnv_spi_tpm_read(const PnvChip *chip, uint32_t reg);
-static void pnv_spi_tpm_write(const PnvChip *chip, uint32_t reg, uint64_t val);
-static uint64_t spi_read_reg(const PnvChip *chip);
-static void spi_write_reg(const PnvChip *chip, uint64_t val);
-static void spi_access_start(const PnvChip *chip, bool n2, uint8_t bytes,
- uint8_t tpm_op, uint32_t tpm_reg);
-
-static inline void tpm_reg_writeb(const PnvChip *c,
- int locl,
- uint8_t reg,
- uint8_t val)
-{
- uint32_t tpm_reg_locl = SPI_TPM_TIS_ADDR | (locl << TPM_TIS_LOCALITY_SHIFT);
-
- spi_access_start(c, false, 1, TPM_WRITE_OP, tpm_reg_locl | reg);
- spi_write_reg(c, bswap64(val));
-}
-
-static inline uint8_t tpm_reg_readb(const PnvChip *c, int locl, uint8_t reg)
-{
- uint32_t tpm_reg_locl = SPI_TPM_TIS_ADDR | (locl << TPM_TIS_LOCALITY_SHIFT);
-
- spi_access_start(c, true, 1, TPM_READ_OP, tpm_reg_locl | reg);
- return spi_read_reg(c);
-}
-
-static inline void tpm_reg_writew(const PnvChip *c,
- int locl,
- uint8_t reg,
- uint32_t val)
-{
- int i;
-
- for (i = 0; i < 4; i++) {
- tpm_reg_writeb(c, locl, reg + i, ((val >> (8 * i)) & 0xff));
- }
-}
-
-static inline uint32_t tpm_reg_readw(const PnvChip *c, int locl, uint8_t reg)
+static uint64_t pnv_spi_tpm_read(const PnvChip *chip, uint32_t reg)
{
- uint32_t val = 0;
- int i;
+ uint32_t pcba = SPI_TPM_BASE + reg;
- for (i = 0; i < 4; i++) {
- val |= tpm_reg_readb(c, locl, reg + i) << (8 * i);
- }
- return val;
+ return qtest_readq(global_qtest, pnv_xscom_addr(chip, pcba));
}
static void pnv_spi_tpm_write(const PnvChip *chip,
@@ -104,40 +66,8 @@ static void pnv_spi_tpm_write(const PnvChip *chip,
uint64_t val)
{
uint32_t pcba = SPI_TPM_BASE + reg;
- qtest_writeq(global_qtest, pnv_xscom_addr(chip, pcba), val);
-}
-static uint64_t pnv_spi_tpm_read(const PnvChip *chip, uint32_t reg)
-{
- uint32_t pcba = SPI_TPM_BASE + reg;
- return qtest_readq(global_qtest, pnv_xscom_addr(chip, pcba));
-}
-
-static void spi_access_start(const PnvChip *chip,
- bool n2,
- uint8_t bytes,
- uint8_t tpm_op,
- uint32_t tpm_reg)
-{
- uint64_t cfg_reg;
- uint64_t reg_op;
- uint64_t seq_op = SEQ_OP_REG_BASIC;
-
- cfg_reg = pnv_spi_tpm_read(chip, SPI_CLK_CFG_REG);
- if (cfg_reg != CFG_COUNT_COMPARE_1) {
- pnv_spi_tpm_write(chip, SPI_CLK_CFG_REG, CFG_COUNT_COMPARE_1);
- }
- /* bytes - sequencer operation register bits 24:31 */
- if (n2) {
- seq_op |= SPI_SHIFT_COUNTER_N2 | (bytes << 0x18);
- } else {
- seq_op |= SPI_SHIFT_COUNTER_N1 | (bytes << 0x18);
- }
- pnv_spi_tpm_write(chip, SPI_SEQ_OP_REG, seq_op);
- pnv_spi_tpm_write(chip, SPI_MM_REG, MM_REG_RDR_MATCH);
- pnv_spi_tpm_write(chip, SPI_CTR_CFG_REG, (uint64_t)0);
- reg_op = bswap64(tpm_op) | ((uint64_t)tpm_reg << 0x20);
- pnv_spi_tpm_write(chip, SPI_XMIT_DATA_REG, reg_op);
+ qtest_writeq(global_qtest, pnv_xscom_addr(chip, pcba), val);
}
static void spi_op_complete(const PnvChip *chip)
@@ -206,6 +136,89 @@ static uint64_t spi_read_reg(const PnvChip *chip)
return val;
}
+static void spi_access_start(const PnvChip *chip,
+ bool n2,
+ uint8_t bytes,
+ uint8_t tpm_op,
+ uint32_t tpm_reg)
+{
+ uint64_t cfg_reg;
+ uint64_t reg_op;
+ uint64_t seq_op = SEQ_OP_REG_BASIC;
+
+ cfg_reg = pnv_spi_tpm_read(chip, SPI_CLK_CFG_REG);
+ if (cfg_reg != CFG_COUNT_COMPARE_1) {
+ pnv_spi_tpm_write(chip, SPI_CLK_CFG_REG, CFG_COUNT_COMPARE_1);
+ }
+ /* bytes - sequencer operation register bits 24:31 */
+ if (n2) {
+ seq_op |= SPI_SHIFT_COUNTER_N2 | (bytes << 0x18);
+ } else {
+ seq_op |= SPI_SHIFT_COUNTER_N1 | (bytes << 0x18);
+ }
+ pnv_spi_tpm_write(chip, SPI_SEQ_OP_REG, seq_op);
+ pnv_spi_tpm_write(chip, SPI_MM_REG, MM_REG_RDR_MATCH);
+ pnv_spi_tpm_write(chip, SPI_CTR_CFG_REG, (uint64_t)0);
+ reg_op = ((uint64_t)tpm_op << SPI_RWX_OPCODE_SHIFT) |
+ ((uint64_t)tpm_reg << SPI_RWX_ADDR_SHIFT);
+ pnv_spi_tpm_write(chip, SPI_XMIT_DATA_REG, reg_op);
+}
+
+static inline void tpm_reg_writeb(const PnvChip *c,
+ uint8_t locty,
+ uint8_t reg,
+ uint8_t val)
+{
+ uint32_t tpm_reg_locty = SPI_TPM_TIS_ADDR |
+ (locty << TPM_TIS_LOCALITY_SHIFT);
+
+ spi_access_start(c, false, 1, TPM_WRITE_OP, tpm_reg_locty | reg);
+ spi_write_reg(c, (uint64_t) val << SPI_CMD_DATA_SHIFT);
+}
+
+static inline uint8_t tpm_reg_readb(const PnvChip *c,
+ uint8_t locty,
+ uint8_t reg)
+{
+ uint32_t tpm_reg_locty = SPI_TPM_TIS_ADDR |
+ (locty << TPM_TIS_LOCALITY_SHIFT);
+
+ spi_access_start(c, true, 1, TPM_READ_OP, tpm_reg_locty | reg);
+ return spi_read_reg(c);
+}
+
+static inline void tpm_reg_writel(const PnvChip *c,
+ uint8_t locty,
+ uint16_t reg,
+ uint32_t val)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ tpm_reg_writeb(c, locty, reg + i, ((val >> (8 * i)) & 0xff));
+ }
+}
+
+static inline uint32_t tpm_reg_readl(const PnvChip *c,
+ uint8_t locty,
+ uint16_t reg)
+{
+ uint32_t val = 0;
+ int i;
+
+ /* special case for SPI xmit data reg set RWX bits */
+ if (reg == TPM_TIS_REG_DID_VID) {
+ spi_access_start(c, true, 4, TPM_READ_OP | SPI_EXTEND,
+ locty | TPM_TIS_REG_DID_VID);
+ val = bswap32(spi_read_reg(c));
+ } else {
+ for (i = 0; i < 4; i++) {
+ val |= tpm_reg_readb(c, locty, reg + i) << (8 * i);
+ }
+ }
+ return val;
+}
+
static void tpm_set_verify_loc(const PnvChip *chip, uint8_t loc)
{
uint8_t access;
@@ -226,7 +239,7 @@ static void tpm_set_verify_loc(const PnvChip *chip, uint8_t loc)
g_test_message("\tACCESS REG = 0x%x checked", access);
/* test tpm status register */
- tpm_sts = tpm_reg_readw(chip, loc, TPM_TIS_REG_STS);
+ tpm_sts = tpm_reg_readl(chip, loc, TPM_TIS_REG_STS);
g_assert_cmpuint((tpm_sts & TPM_TIS_8BITS_MASK), ==, 0);
g_test_message("\tTPM STATUS: 0x%x, verified", tpm_sts);
@@ -239,6 +252,17 @@ static void tpm_set_verify_loc(const PnvChip *chip, uint8_t loc)
g_test_message("\tRELEASED ACCESS: 0x%x, checked", access);
}
+static void test_spi_tpm_locality(const void *data)
+{
+ const PnvChip *chip = data;
+ uint8_t locality;
+
+ /* Locality 4 has special security restrictions, testing 0-3 */
+ for (locality = 0; locality < TPM_TIS_NUM_LOCALITIES - 1; locality++) {
+ tpm_set_verify_loc(chip, locality);
+ }
+}
+
static void test_spi_tpm_basic(const void *data)
{
const PnvChip *chip = data;
@@ -247,11 +271,8 @@ static void test_spi_tpm_basic(const void *data)
g_test_message("TPM TIS SPI interface basic tests:");
/* vendor ID and device ID ... check against the known value*/
- spi_access_start(chip, true, 4, 0x83,
- SPI_TPM_TIS_ADDR | TPM_TIS_REG_DID_VID);
- didvid = spi_read_reg(chip);
- g_assert_cmpint((didvid >> 16), ==, bswap16(TPM_TIS_TPM_VID));
- g_assert_cmpint((didvid & 0xffff), ==, bswap16(TPM_TIS_TPM_DID));
+ didvid = tpm_reg_readl(chip, 0, TPM_TIS_REG_DID_VID);
+ g_assert_cmpint(didvid, ==, (1 << 16) | PCI_VENDOR_ID_IBM);
g_test_message("\tDID_VID = 0x%x, verified", didvid);
/* access register, default see TCG TIS Spec (v1.3) table-14 */
@@ -261,28 +282,17 @@ static void test_spi_tpm_basic(const void *data)
g_test_message("\tACCESS REG = 0x%x, checked", access);
/* interrupt enable register, default see TCG TIS Spec (v1.3) table-19 */
- en_int = tpm_reg_readw(chip, 0, TPM_TIS_REG_INT_ENABLE);
+ en_int = tpm_reg_readl(chip, 0, TPM_TIS_REG_INT_ENABLE);
g_assert_cmpuint(en_int, ==, TPM_TIS_INT_POLARITY_LOW_LEVEL);
g_test_message("\tINT ENABLE REG: 0x%x, verified", en_int);
/* status register, default see TCG TIS Spec (v1.3) table-15 */
- tpm_sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ tpm_sts = tpm_reg_readl(chip, 0, TPM_TIS_REG_STS);
/* for no active locality */
g_assert_cmpuint(tpm_sts, ==, 0xffffffff);
g_test_message("\tTPM STATUS: 0x%x, verified", tpm_sts);
}
-static void test_spi_tpm_locality(const void *data)
-{
- const PnvChip *chip = data;
- uint8_t locality;
-
- /* Locality 4 has special security restrictions, testing 0-3 */
- for (locality = 0; locality < TPM_TIS_NUM_LOCALITIES - 1; locality++) {
- tpm_set_verify_loc(chip, locality);
- }
-}
-
/*
* Test case for seizing access by a higher number locality
*/
@@ -582,7 +592,7 @@ static void test_spi_tpm_transmit_test(const void *data)
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
- sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ sts = tpm_reg_readl(chip, 0, TPM_TIS_REG_STS);
DPRINTF_STS;
g_assert_cmpint(sts & 0xff, ==, 0);
@@ -591,15 +601,15 @@ static void test_spi_tpm_transmit_test(const void *data)
g_test_message("\t\tbcount: %x, sts: %x", bcount, sts);
g_assert_cmpint(bcount, >=, 128);
- tpm_reg_writew(chip, 0, TPM_TIS_REG_STS, TPM_TIS_STS_COMMAND_READY);
- sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ tpm_reg_writel(chip, 0, TPM_TIS_REG_STS, TPM_TIS_STS_COMMAND_READY);
+ sts = tpm_reg_readl(chip, 0, TPM_TIS_REG_STS);
DPRINTF_STS;
g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_COMMAND_READY);
/* transmit command */
for (i = 0; i < sizeof(TPM_CMD); i++) {
tpm_reg_writeb(chip, 0, TPM_TIS_REG_DATA_FIFO, TPM_CMD[i]);
- sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ sts = tpm_reg_readl(chip, 0, TPM_TIS_REG_STS);
DPRINTF_STS;
if (i < sizeof(TPM_CMD) - 1) {
g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_EXPECT |
@@ -611,17 +621,17 @@ static void test_spi_tpm_transmit_test(const void *data)
g_test_message("\ttransmit tests, check TPM_TIS_STS_EXPECT");
/* start processing */
- tpm_reg_writew(chip, 0, TPM_TIS_REG_STS, TPM_TIS_STS_TPM_GO);
+ tpm_reg_writel(chip, 0, TPM_TIS_REG_STS, TPM_TIS_STS_TPM_GO);
uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND;
do {
- sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ sts = tpm_reg_readl(chip, 0, TPM_TIS_REG_STS);
if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) {
break;
}
} while (g_get_monotonic_time() < end_time);
- sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ sts = tpm_reg_readl(chip, 0, TPM_TIS_REG_STS);
DPRINTF_STS;
g_assert_cmpint(sts & 0xff, == , TPM_TIS_STS_VALID |
TPM_TIS_STS_DATA_AVAILABLE);
@@ -635,7 +645,7 @@ static void test_spi_tpm_transmit_test(const void *data)
for (i = 0; i < sizeof(tpm_msg); i++) {
tpm_msg[i] = tpm_reg_readb(chip, 0, TPM_TIS_REG_DATA_FIFO);
- sts = tpm_reg_readw(chip, 0, TPM_TIS_REG_STS);
+ sts = tpm_reg_readl(chip, 0, TPM_TIS_REG_STS);
DPRINTF_STS;
if (sts & TPM_TIS_STS_DATA_AVAILABLE) {
g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount);
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 6b1aed929e..74aa9f57e0 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -4,6 +4,7 @@ slow_qtests = {
'bios-tables-test' : 910,
'cdrom-test' : 610,
'device-introspect-test' : 720,
+ 'ide-test' : 120,
'migration-test' : 480,
'npcm7xx_pwm-test': 300,
'npcm7xx_watchdog_timer-test': 120,
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v3 0/5] TPM TIS SPI Support
2024-11-01 18:57 [PATCH v3 0/5] TPM TIS SPI Support dan tan
` (4 preceding siblings ...)
2024-11-01 18:57 ` [PATCH v3 5/5] tests/qtest/tpm: add unit test to tis-spi " dan tan
@ 2024-11-01 19:25 ` Stefan Berger
5 siblings, 0 replies; 7+ messages in thread
From: Stefan Berger @ 2024-11-01 19:25 UTC (permalink / raw)
To: dan tan, qemu-devel; +Cc: qemu-ppc, stefanb, pbonzini, farosas, lvivier, clg
On 11/1/24 2:57 PM, dan tan wrote:
> *** BLURB HERE ***
>
> Support TPM for SPI (Serial Peripheral Interface)
>
> Revision 3 summary:
> device support:
> - moved variable tis_addr from TPMStateSPI struct to local
> - added the VM suspend/resume support:
> - added vmstate_tpm_tis_spi declaration
> - added tpm_tis_spi_pre_save() function
> - fixed trace formatting string
Can you please update patch 1/3 with those changes relevant for this
patch and then patch 3/3 as well. I think it should be fairly simple to
modify each one of them by merging 4/5 into 1/5 and 5/5 into 3/5.
There's no reason that there are so many changes in 4/5 and 5/5
replacing new code.
Thanks
Stefan
^ permalink raw reply [flat|nested] 7+ messages in thread