|
From: | Ninad Palsule |
Subject: | Re: [PATCH v7 3/3] tpm: Add support for TPM device over I2C bus |
Date: | Mon, 27 Mar 2023 10:54:57 -0500 |
User-agent: | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Thunderbird/102.8.0 |
Hi Stefan, On 3/27/23 8:40 AM, Stefan Berger wrote:
On 3/26/23 18:44, Ninad Palsule wrote:Qemu already supports devices attached to ISA and sysbus. This drop adds support for the I2C bus attached TPM devices. I2C model only supports TPM2 protocol.--- /dev/null +++ b/hw/tpm/tpm_tis_i2c.c @@ -0,0 +1,540 @@ +/* + * tpm_tis_i2c.c - QEMU's TPM TIS I2C Device + *+ * This work is licensed under the terms of the GNU GPL, version 2 or later.+ * See the COPYING file in the top-level directory. + * + * Implementation of the TIS interface according to specs found at + * http://www.trustedcomputinggroup.org. This implementation currently + * supports version 1.3, 21 March 2013+ * In the developers menu choose the PC Client section then find the TIS+ * specification. + * + * TPM TIS for TPM 2 implementation following TCG PC Client Platform + * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 + * + * TPM I2C implementation follows TCG TPM I2c Interface specification, + * Family 2.0, Level 00, Revision 1.00 + */ + +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "hw/sysbus.h" +#include "hw/acpi/tpm.h" +#include "migration/vmstate.h" +#include "tpm_prop.h" +#include "qemu/log.h" +#include "trace.h" +#include "tpm_tis.h" + +/* TPM_STS mask for read bits 31:26 must be zero */ +#define TPM_I2C_STS_READ_MASK 0x03ffffff + +/* TPM_I2C_INT_ENABLE mask */ +#define TPM_I2C_INT_ENABLE_MASK (TPM_TIS_INT_ENABLED | \ + TPM_TIS_INT_DATA_AVAILABLE | \ + TPM_TIS_INT_STS_VALID | \ + TPM_TIS_INT_LOCALITY_CHANGED | \ + TPM_TIS_INT_COMMAND_READY)Since we cannot test interrupts with Linux since the driver doesn't support it, can you set this mask to 0 and move it into the public header file. Please also apply the mask to vthe alue returned from reading to the TPM_INT_CAPABILITY register.
OK, done.
+ +/* Operations */ +#define OP_SEND 1 +#define OP_RECV 2 + +typedef struct TPMStateI2C { + /*< private >*/ + I2CSlave parent_obj; + + uint8_t offset; /* offset into data[] */ + uint8_t operation; /* OP_SEND & OP_RECV */ + uint8_t data[5]; /* Data */ + + /* i2c registers */ + uint8_t loc_sel; /* Current locality */ + uint8_t csum_enable; /* Is checksum enabled */ + + /* Derived from the above */ + const char *reg_name; /* Register name */+ uint32_t tis_addr; /* Converted tis address including locty */+ + /*< public >*/ + TPMState state; /* not a QOM object */ + +} TPMStateI2C; + +DECLARE_INSTANCE_CHECKER(TPMStateI2C, TPM_TIS_I2C, + TYPE_TPM_TIS_I2C) + +/* Prototype */+static inline void tpm_tis_i2c_to_tis_reg(TPMStateI2C *i2cst, uint8_t i2c_reg);+ +/* Register map */ +typedef struct regMap { + uint8_t i2c_reg; /* I2C register */ + uint16_t tis_reg; /* TIS register */ + const char *reg_name; /* Register name */ +} I2CRegMap; + +/* + * The register values in the common code is different than the latest + * register numbers as per the spec hence add the conversion map + */ +static const I2CRegMap tpm_tis_reg_map[] = { + /* + * These registers are sent to TIS layer. The register with UNKNOWN + * mapping are not sent to TIS layer and handled in I2c layer. + * NOTE: Adding frequently used registers at the start + */+ { TPM_I2C_REG_DATA_FIFO, TPM_TIS_REG_DATA_FIFO, "FIFO", },+ { TPM_I2C_REG_STS, TPM_TIS_REG_STS, "STS", },+ { TPM_I2C_REG_DATA_CSUM_GET, TPM_I2C_REG_UNKNOWN, "CSUM_GET", },+ { TPM_I2C_REG_LOC_SEL, TPM_I2C_REG_UNKNOWN, "LOC_SEL", }, + { TPM_I2C_REG_ACCESS, TPM_TIS_REG_ACCESS, "ACCESS", },+ { TPM_I2C_REG_INT_ENABLE, TPM_TIS_REG_INT_ENABLE, "INTR_ENABLE",}, + { TPM_I2C_REG_INT_CAPABILITY, TPM_TIS_REG_INT_ENABLE, "INTR_CAP", },This mapping here is wrong. It should map to TPM_TIS_REG_INT_CAPABILITY.
OK, Now I handle this register in the I2C so changed to mapping to UNKNOWN
+ { TPM_I2C_REG_INTF_CAPABILITY, TPM_TIS_REG_INTF_CAPABILITY, "INTF_CAP", },+ { TPM_I2C_REG_DID_VID, TPM_TIS_REG_DID_VID, "DID_VID", }, + { TPM_I2C_REG_RID, TPM_TIS_REG_RID, "RID", },+ { TPM_I2C_REG_I2C_DEV_ADDRESS, TPM_I2C_REG_UNKNOWN, "DEV_ADDRESS",}, + { TPM_I2C_REG_DATA_CSUM_ENABLE, TPM_I2C_REG_UNKNOWN, "CSUM_ENABLE",},+}; ++/* + * Send function only remembers data in the buffer and then calls + * TPM TIS common code during FINISH event. + */ +static int tpm_tis_i2c_send(I2CSlave *i2c, uint8_t data) +{ + TPMStateI2C *i2cst = TPM_TIS_I2C(i2c); + + /* Reject non-supported registers. */ + if (i2cst->offset == 0) { + /* Convert I2C register to TIS register */ + tpm_tis_i2c_to_tis_reg(i2cst, data); + if (i2cst->tis_addr == 0xffffffff) { + return 0xffffffff; + } + + trace_tpm_tis_i2c_send_reg(i2cst->reg_name, data); + + /* We do not support device address change */ + if (data == TPM_I2C_REG_I2C_DEV_ADDRESS) { + qemu_log_mask(LOG_UNIMP, "%s: Device address change " + "is not supported.\n", __func__); + return 0xffffffff; + } + } else { + trace_tpm_tis_i2c_send(data); + } + + if (i2cst->offset < sizeof(i2cst->data)) { + i2cst->operation = OP_SEND; + + /* Remember data locally for non-FIFO registers */ + if ((i2cst->offset == 0) || + (i2cst->data[0] != TPM_I2C_REG_DATA_FIFO)) { + i2cst->data[i2cst->offset++] = data; + } else {+ tpm_tis_write_data(&i2cst->state, i2cst->tis_addr, data, 1);+ } + + return 0; + + } + + /* Return non-zero to indicate NAK */ + return 1; +} + +static Property tpm_tis_i2c_properties[] = { + DEFINE_PROP_UINT32("irq", TPMStateI2C, state.irq_num, TPM_TIS_IRQ),You should remove this here. Maybe one day we will add it back when we can test it.
Done
+ DEFINE_PROP_TPMBE("tpmdev", TPMStateI2C, state.be_driver), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tpm_tis_i2c_realizefn(DeviceState *dev, Error **errp) +{ + TPMStateI2C *i2cst = TPM_TIS_I2C(dev); + TPMState *s = &i2cst->state; + + if (!tpm_find()) { + error_setg(errp, "at most one TPM device is permitted"); + return; + } + + /* + * Get the backend pointer. It is not initialized propery during + * device_class_set_props + */ + s->be_driver = qemu_find_tpm_be("tpm0"); + + if (!s->be_driver) { + error_setg(errp, "'tpmdev' property is required"); + return; + } + if (s->irq_num > 15) { + error_setg(errp, "IRQ %d is outside valid range of 0 to 15", + s->irq_num); + return; + }This block can go.
Done
+} + +static void tpm_tis_i2c_reset(DeviceState *dev) +{ + TPMStateI2C *i2cst = TPM_TIS_I2C(dev); + TPMState *s = &i2cst->state; + + tpm_tis_i2c_clear_data(i2cst); + + i2cst->csum_enable = 0; + i2cst->loc_sel = 0x00; + + return tpm_tis_reset(s); +} + +static void tpm_tis_i2c_initfn(Object *obj) +{ + TPMStateI2C *i2cst = TPM_TIS_I2C(obj); + TPMState *s = &i2cst->state; + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); +}This function also has to go.
Done Thanks for the review. Ninad
[Prev in Thread] | Current Thread | [Next in Thread] |