From: Stefan Berger <stefanb@linux.ibm.com>
To: dan tan <dantan@linux.vnet.ibm.com>,
qemu-devel@nongnu.org, stefanb@linux.vnet.ibm.com,
pbonzini@redhat.com, farosas@suse.de, lvivier@redhat.com,
clg@kaod.org
Cc: qemu-ppc@nongnu.org
Subject: Re: [PATCH v2 3/3] tests/qtest/tpm: add unit test to tis-spi
Date: Sat, 26 Oct 2024 21:56:54 -0400 [thread overview]
Message-ID: <ba579d34-15b1-4a7d-84b1-2a97f6249a9e@linux.ibm.com> (raw)
In-Reply-To: <20241025201247.29574-4-dantan@linux.vnet.ibm.com>
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'),
next prev parent reply other threads:[~2024-10-27 1:57 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-25 20:12 [PATCH v2 0/3] *** TPM TIS SPI pull request *** dan tan
2024-10-25 20:12 ` [PATCH v2 1/3] tpm/tpm_tis_spi: Support TPM for SPI (Serial Peripheral Interface) dan tan
2024-10-27 1:47 ` Stefan Berger
2024-10-25 20:12 ` [PATCH v2 2/3] tpm/tpm_tis_spi: activation for the PowerNV machines dan tan
2024-10-25 20:12 ` [PATCH v2 3/3] tests/qtest/tpm: add unit test to tis-spi dan tan
2024-10-27 1:56 ` Stefan Berger [this message]
2024-10-27 17:42 ` Stefan Berger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ba579d34-15b1-4a7d-84b1-2a97f6249a9e@linux.ibm.com \
--to=stefanb@linux.ibm.com \
--cc=clg@kaod.org \
--cc=dantan@linux.vnet.ibm.com \
--cc=farosas@suse.de \
--cc=lvivier@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-ppc@nongnu.org \
--cc=stefanb@linux.vnet.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).