qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v1 2/2] tpm: Add TPM I2C Atmel frontend


From: Fabio Urquiza
Subject: [Qemu-devel] [PATCH v1 2/2] tpm: Add TPM I2C Atmel frontend
Date: Tue, 29 Nov 2016 16:30:52 -0300

Add a new frontend to the TPM backend that emulate the Atmel I2C TPM
AT97SC3204T device and make it available to use on ARM machines that have
I2C Bus configured.

Signed-off-by: Fabio Urquiza <address@hidden>
---
 default-configs/aarch64-softmmu.mak |   1 +
 default-configs/arm-softmmu.mak     |   1 +
 hw/tpm/Makefile.objs                |   1 +
 hw/tpm/tpm_i2c_atmel.c              | 362 ++++++++++++++++++++++++++++++++++++
 4 files changed, 365 insertions(+)
 create mode 100644 hw/tpm/tpm_i2c_atmel.c

diff --git a/default-configs/aarch64-softmmu.mak 
b/default-configs/aarch64-softmmu.mak
index 2449483..232957e 100644
--- a/default-configs/aarch64-softmmu.mak
+++ b/default-configs/aarch64-softmmu.mak
@@ -7,3 +7,4 @@ CONFIG_AUX=y
 CONFIG_DDC=y
 CONFIG_DPCD=y
 CONFIG_XLNX_ZYNQMP=y
+CONFIG_TPM_I2C_ATMEL=$(CONFIG_TPM)
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 6de3e16..ef3c8ac 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -115,3 +115,4 @@ CONFIG_ACPI=y
 CONFIG_SMBIOS=y
 CONFIG_ASPEED_SOC=y
 CONFIG_GPIO_KEY=y
+CONFIG_TPM_I2C_ATMEL=$(CONFIG_TPM)
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
index 64cecc3..0d0f0b1 100644
--- a/hw/tpm/Makefile.objs
+++ b/hw/tpm/Makefile.objs
@@ -1,2 +1,3 @@
 common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
+common-obj-$(CONFIG_TPM_I2C_ATMEL) += tpm_i2c_atmel.o
 common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
diff --git a/hw/tpm/tpm_i2c_atmel.c b/hw/tpm/tpm_i2c_atmel.c
new file mode 100644
index 0000000..774b3c3
--- /dev/null
+++ b/hw/tpm/tpm_i2c_atmel.c
@@ -0,0 +1,362 @@
+/*
+ * tpm_i2c_atmel.c - QEMU's TPM I2C interface emulator
+ *
+ * Copyright (C) 2012, HPE Corporation
+ *
+ * Authors:
+ *  Fabio Urquiza <address@hidden>
+ *
+ * Based on tpm_tis.c:
+ *  Stefan Berger <address@hidden>
+ *  David Safford <address@hidden>
+ *
+ * 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 I2C interface according to specs found at
+ * http://www.trustedcomputinggroup.org. This implementation currently
+ * supports version 1.2 Atmel AT97SC3204T CI, 10 December 2016
+ *
+ * TPM I2C for TPM 2 implementation following TCG TPM I2C Interface
+ * Specification TPM Profile (PTP) Specification, Familiy 2.0, Revision 1.0
+ */
+
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/main-loop.h"
+#include "hw/i2c/i2c.h"
+#include "qemu/bcd.h"
+#include "sysemu/tpm_backend.h"
+#include "tpm_int.h"
+#include "qapi/error.h"
+
+#define DEBUG_TIS 0
+
+#define DPRINTF(fmt, ...) do { \
+    if (DEBUG_TIS) { \
+        printf(fmt, ## __VA_ARGS__); \
+    } \
+} while (0);
+
+/* vendor-specific registers */
+#define TPM_TIS_STS_TPM_FAMILY_MASK         (0x3 << 26)/* TPM 2.0 */
+#define TPM_TIS_STS_TPM_FAMILY1_2           (0 << 26)  /* TPM 2.0 */
+#define TPM_TIS_STS_TPM_FAMILY2_0           (1 << 26)  /* TPM 2.0 */
+
+#define TPM_TIS_STS_VALID                 (1 << 7)
+#define TPM_TIS_STS_DATA_AVAILABLE        (1 << 4)
+#define TPM_TIS_STS_SELFTEST_DONE         (1 << 2)
+
+#define TPM_TIS_ACCESS_TPM_REG_VALID_STS  (1 << 7)
+
+#define TPM_TIS_IFACE_ID_INTERFACE_TIS1_3   (0xf)     /* TPM 2.0 */
+#define TPM_TIS_IFACE_ID_INTERFACE_FIFO     (0x0)     /* TPM 2.0 */
+#define TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO (0 << 4)  /* TPM 2.0 */
+#define TPM_TIS_IFACE_ID_CAP_5_LOCALITIES   (1 << 8)  /* TPM 2.0 */
+#define TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED  (1 << 13) /* TPM 2.0 */
+#define TPM_TIS_IFACE_ID_INT_SEL_LOCK       (1 << 19) /* TPM 2.0 */
+
+#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \
+    (TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \
+     (~0u << 4)/* all of it is don't care */)
+
+/* if backend was a TPM 2.0: */
+#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \
+    (TPM_TIS_IFACE_ID_INTERFACE_FIFO | \
+     TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO | \
+     TPM_TIS_IFACE_ID_CAP_5_LOCALITIES | \
+     TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED)
+
+#define TPM_TIS_NO_DATA_BYTE  0xff
+
+static const VMStateDescription vmstate_tpm_i2c_atmel = {
+    .name = "tpm",
+    .unmigratable = 1,
+};
+
+static uint32_t tpm_i2c_atmel_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+    return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
+}
+
+static void tpm_i2c_atmel_show_buffer(const TPMSizedBuffer *sb,
+    const char *string)
+{
+#ifdef DEBUG_TIS
+    uint32_t len, i;
+
+    len = tpm_i2c_atmel_get_size_from_buffer(sb);
+    DPRINTF("tpm_tis: %s length = %d\n", string, len);
+    for (i = 0; i < len; i++) {
+        if (i && !(i % 16)) {
+            DPRINTF("\n");
+        }
+        DPRINTF("%.2X ", sb->buffer[i]);
+    }
+    DPRINTF("\n");
+#endif
+}
+
+/*
+ * Set the given flags in the STS register by clearing the register but
+ * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting
+ * the new flags.
+ *
+ * The SELFTEST_DONE flag is acquired from the backend that determines it by
+ * peeking into TPM commands.
+ *
+ * A VM suspend/resume will preserve the flag by storing it into the VM
+ * device state, but the backend will not remember it when QEMU is started
+ * again. Therefore, we cache the flag here. Once set, it will not be unset
+ * except by a reset.
+ */
+static inline void tpm_i2c_atmel_sts_set(TPMLocality *l, uint32_t flags)
+{
+    l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK;
+    l->sts |= flags;
+}
+
+static inline uint32_t tpm_i2c_atmel_tpm_start_recv(TPMState *s)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+    tis->loc[0].r_offset = 0;
+
+    return !(tis->loc[0].sts & TPM_TIS_STS_DATA_AVAILABLE);
+}
+
+static inline void tpm_i2c_atmel_tpm_start_send(TPMState *s)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+    tis->loc[0].r_offset = 0;
+    tis->loc[0].w_offset = 0;
+}
+
+/*
+ * Send a request to the TPM.
+ */
+static inline void tpm_i2c_atmel_tpm_send(TPMState *s)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+
+    if (tis->loc[0].w_offset &&
+        tis->loc[0].state != TPM_TIS_STATE_EXECUTION) {
+        tpm_i2c_atmel_show_buffer(&tis->loc[0].w_buffer, "tpm_tis: To TPM");
+
+        s->locty_number = 0;
+        s->locty_data = &tis->loc[0];
+
+        /*
+         * w_offset serves as length indicator for length of data;
+         * it's reset when the response comes back
+         */
+        tis->loc[0].state = TPM_TIS_STATE_EXECUTION;
+
+        tpm_backend_deliver_request(s->be_driver);
+    }
+}
+
+
+static void tpm_i2c_atmel_receive_bh(void *opaque)
+{
+    TPMState *s = opaque;
+    TPMTISEmuState *tis = &s->s.tis;
+
+    tpm_i2c_atmel_sts_set(&tis->loc[0],
+                    TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
+    tis->loc[0].state = TPM_TIS_STATE_COMPLETION;
+    tis->loc[0].r_offset = 0;
+    tis->loc[0].w_offset = 0;
+    DPRINTF("tpm_i2c_atmel: tpm_i2c_atmel_receive_bh");
+
+}
+
+/*
+ * Read a byte of response data
+ */
+static inline uint32_t tpm_i2c_atmel_data_read(TPMState *s)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+    uint32_t ret = TPM_TIS_NO_DATA_BYTE;
+    uint16_t len;
+
+    if ((tis->loc[0].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
+        len = tpm_i2c_atmel_get_size_from_buffer(&tis->loc[0].r_buffer);
+
+        ret = tis->loc[0].r_buffer.buffer[tis->loc[0].r_offset++];
+        if (tis->loc[0].r_offset >= len) {
+            /* got last byte */
+            tpm_i2c_atmel_sts_set(&tis->loc[0], TPM_TIS_STS_VALID);
+        }
+        DPRINTF("tpm_i2c_atmel: tpm_i2c_atmel_data_read byte 0x%02x   [%d]\n",
+                ret, tis->loc[0].r_offset - 1);
+    } else {
+        DPRINTF("tpm_i2c_atmel: !TPM_TIS_STS_DATA_AVAILABLE [%d]\n",
+                tis->loc[0].sts);
+    }
+
+    return ret;
+}
+
+static void tpm_i2c_atmel_event(I2CSlave *i2c, enum i2c_event event)
+{
+    TPMState *s = TPM(&(i2c->qdev));
+    i2c->busy = 0;
+
+    switch (event) {
+    case I2C_START_RECV:
+        i2c->busy = tpm_i2c_atmel_tpm_start_recv(s);
+        break;
+    case I2C_START_SEND:
+        tpm_i2c_atmel_tpm_start_send(s);
+        break;
+    case I2C_FINISH:
+        tpm_i2c_atmel_tpm_send(s);
+        break;
+    default:
+        break;
+    }
+}
+
+static int tpm_i2c_atmel_recv(I2CSlave *i2c)
+{
+    TPMState *s = TPM(&(i2c->qdev));
+    return tpm_i2c_atmel_data_read(s);
+}
+
+static int tpm_i2c_atmel_send(I2CSlave *i2c, uint8_t data)
+{
+    TPMState *s = TPM(&(i2c->qdev));
+    TPMTISEmuState *tis = &s->s.tis;
+    tis->loc[0].w_buffer.buffer[tis->loc[0].w_offset++] = data;
+    return 0;
+}
+
+static void tpm_i2c_atmel_receive_cb(TPMState *s, uint8_t locty,
+                               bool is_selftest_done)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+
+    assert(locty == 0);
+
+    if (is_selftest_done) {
+        tis->loc[0].sts |= TPM_TIS_STS_SELFTEST_DONE;
+    }
+
+    qemu_bh_schedule(tis->bh);
+}
+
+
+static void tpm_i2c_atmel_realizefn(DeviceState *dev, Error **errp)
+{
+    TPMState *s = TPM(dev);
+    TPMTISEmuState *tis = &s->s.tis;
+
+    DPRINTF("backend %s\n", s->backend);
+    s->be_driver = qemu_find_tpm(s->backend);
+    if (!s->be_driver) {
+        error_setg(errp, "tpm_i2c_atmel: backend driver with id %s could not "
+                   "be found", s->backend);
+        return;
+    }
+
+    s->be_driver->fe_model = TPM_MODEL_TPM_TIS;
+
+    if (tpm_backend_init(s->be_driver, s, tpm_i2c_atmel_receive_cb)) {
+        error_setg(errp, "tpm_i2c_atmel: backend driver with id %s could not "
+                   "be initialized", s->backend);
+        return;
+    }
+
+    if (tis->irq_num > 15) {
+        error_setg(errp, "tpm_i2c_atmel: IRQ %d for TPM TIS is outside valid "
+                   "range of 0 to 15", tis->irq_num);
+        return;
+    }
+
+    tis->bh = qemu_bh_new(tpm_i2c_atmel_receive_bh, s);
+}
+
+static int tpm_i2c_atmel_do_startup_tpm(TPMState *s)
+{
+    return tpm_backend_startup_tpm(s->be_driver);
+}
+
+
+static int tpm_i2c_atmel_init(I2CSlave *i2c)
+{
+    return 0;
+}
+
+static void tpm_i2c_atmel_reset(DeviceState *dev)
+{
+    TPMState *s = TPM(dev);
+    TPMTISEmuState *tis = &s->s.tis;
+
+    s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
+
+    tpm_backend_reset(s->be_driver);
+
+    tis->active_locty = TPM_TIS_NO_LOCALITY;
+    tis->next_locty = TPM_TIS_NO_LOCALITY;
+    tis->aborting_locty = TPM_TIS_NO_LOCALITY;
+
+    /* ATMEL AT97SC3204T only uses locality 0 */
+    memset(tis->loc, 0, sizeof(tis->loc));
+    tis->loc[0].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
+    switch (s->be_tpm_version) {
+    case TPM_VERSION_UNSPEC:
+        break;
+    case TPM_VERSION_1_2:
+        tis->loc[0].sts = TPM_TIS_STS_TPM_FAMILY1_2;
+        tis->loc[0].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3;
+        break;
+    case TPM_VERSION_2_0:
+        tis->loc[0].sts = TPM_TIS_STS_TPM_FAMILY2_0;
+        tis->loc[0].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0;
+        break;
+    }
+        tis->loc[0].state = TPM_TIS_STATE_IDLE;
+
+    tpm_backend_realloc_buffer(s->be_driver, &tis->loc[0].w_buffer);
+    tpm_backend_realloc_buffer(s->be_driver, &tis->loc[0].r_buffer);
+
+    tpm_i2c_atmel_do_startup_tpm(s);
+}
+
+static Property tpm_tis_properties[] = {
+    DEFINE_PROP_UINT32("irq", TPMState,
+                       s.tis.irq_num, TPM_TIS_IRQ),
+    DEFINE_PROP_STRING("tpmdev", TPMState, backend),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tpm_i2c_atmel_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->init = tpm_i2c_atmel_init;
+    k->event = tpm_i2c_atmel_event;
+    k->recv = tpm_i2c_atmel_recv;
+    k->send = tpm_i2c_atmel_send;
+    dc->realize = tpm_i2c_atmel_realizefn;
+    dc->props = tpm_tis_properties;
+    dc->reset = tpm_i2c_atmel_reset;
+    dc->vmsd = &vmstate_tpm_i2c_atmel;
+}
+
+static const TypeInfo tpm_i2c_atmel_info = {
+    .name          = TYPE_TPM_TIS,
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(TPMState),
+    .class_init    = tpm_i2c_atmel_class_init,
+};
+
+static void tpm_i2c_atmel_register_types(void)
+{
+    type_register_static(&tpm_i2c_atmel_info);
+    tpm_register_model(TPM_MODEL_TPM_TIS);
+}
+
+type_init(tpm_i2c_atmel_register_types)
-- 
2.8.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]