[PATCH v2 3/3] tests/qtest/tpm: add unit test to tis-spi

dan tan posted 3 patches 4 weeks ago
[PATCH v2 3/3] tests/qtest/tpm: add unit test to tis-spi
Posted by dan tan 4 weeks ago
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>
---
 tests/qtest/tpm-tis-spi-pnv-test.c | 700 +++++++++++++++++++++++++++++
 tests/qtest/meson.build            |   3 +-
 2 files changed, 702 insertions(+), 1 deletion(-)
 create mode 100644 tests/qtest/tpm-tis-spi-pnv-test.c

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
Re: [PATCH v2 3/3] tests/qtest/tpm: add unit test to tis-spi
Posted by Stefan Berger 3 weeks, 6 days ago

On 10/25/24 4:12 PM, dan tan wrote:
> 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>
> ---
;
> +
> +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));

spi_write_reg(c, (uint64_t)val << 56);

A #define for the 56 would be good.


> +
> +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);


Same #define to use here, maybe called SPI_XMIT_DATA_OP_SHIFT. And one 
for the 0x20, maybe called SPI_XMIT_DATA_ADDR_SHIFT. Any reference to a 
spec?

(uint64_t)tmp_op << SPI_XMIT_DATA_OP_SHIFT | (uint64_t)(tpm_reg & 
0xffffff) << SPI_XMIT_DATA_ADDR_SHIFT;


> +    pnv_spi_tpm_write(chip, SPI_XMIT_DATA_REG, reg_op);
> +}
> +
Re: [PATCH v2 3/3] tests/qtest/tpm: add unit test to tis-spi
Posted by Stefan Berger 3 weeks, 6 days ago

On 10/25/24 4:12 PM, dan tan wrote:
> 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>
> ---
>   tests/qtest/tpm-tis-spi-pnv-test.c | 700 +++++++++++++++++++++++++++++
>   tests/qtest/meson.build            |   3 +-
>   2 files changed, 702 insertions(+), 1 deletion(-)
>   create mode 100644 tests/qtest/tpm-tis-spi-pnv-test.c
> 
> 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);

All of these prototypes can go. I put the function in order starting 
with lowest level right here:

pnv_spi_tpm_read
pnv_spi_tpm_write
spi_op_complete
spi_write_reg
spi_read_reg

See last patch here: 
https://github.com/stefanberger/qemu-tpm/commits/tpm_tis_spi.v2/

> +
> +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,

writew -> writel  since we have 'l' for 32bit in all other tests as well

> +                                  int locl,

uint8_t locty   since we call it like this everywhere

> +                                  uint8_t reg,

uint16_t reg  since DID_VID reg is 0xf00 (really only needed for readl 
but change here as well)

> +                                  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)

readw->readl
uint8_t locty
uint16_t reg   (DID_VID reg is 0xf00)

> +{
> +    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;

empty line here

> +    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;

empty line here

> +    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)
> +{

Move this function to before `test_spi_tpm_locality(const void *data)`
since they belong together

> +    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));

replace with so it's the same as in i2c test:

     /* vendor ID and device ID ... check against the known value*/
     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);


> +    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,


Remove this

>     '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'),