qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 42/42] WIP: add TPM CRB device


From: Marc-André Lureau
Subject: [Qemu-devel] [PATCH 42/42] WIP: add TPM CRB device
Date: Tue, 10 Oct 2017 00:56:23 +0200

tpm_crb is a device for TPM 2.0 Command Response Buffer (CRB)
Interface as defined in TCG PC Client Platform TPM Profile (PTP)
Specification Family “2.0” Level 00 Revision 01.03 v22.

The PTP allows device implementation to switch between TIS and CRB
model at run time, but given that CRB is a simpler device to
implement, I chose to implement it as a different device.

The device doesn't implement other locality than 0 for now (my laptop
TPM doesn't either, so I assume this isn't so bad)

The command/reply memory region is statically allocated after the CRB
registers address TPM_CRB_ADDR_BASE + sizeof(struct crb_regs). I
wonder if the BIOS could or should allocate it instead, or what size
to use.

The PTP doesn't specify a particular bus to put the device. I chose to
add it on the system bus directly, so it could hopefully be used
easily on a different platform. However, I am not sure this is easily
done or a smart approach. It fails to init on piix, because
error_on_sysbus_device() check. Removing this check, the device is
still functional... We may want to put it on ISA bus instead for now.

Tested with some success with Linux upstream and Windows 10. The
device is recognized and correctly transmit command/response with
passthrough/emu, but the swtpm emulator has some issues I need to
investigate further.

Signed-off-by: Marc-André Lureau <address@hidden>
---
 qapi/tpm.json                      |   7 +-
 include/hw/acpi/tpm.h              |  65 ++++++++
 include/sysemu/tpm.h               |   3 +
 hw/i386/acpi-build.c               |  19 +++
 hw/tpm/tpm_crb.c                   | 320 +++++++++++++++++++++++++++++++++++++
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/tpm/Makefile.objs               |   1 +
 8 files changed, 414 insertions(+), 3 deletions(-)
 create mode 100644 hw/tpm/tpm_crb.c

diff --git a/qapi/tpm.json b/qapi/tpm.json
index 7093f268fb..12a4509ad6 100644
--- a/qapi/tpm.json
+++ b/qapi/tpm.json
@@ -10,11 +10,12 @@
 #
 # An enumeration of TPM models
 #
-# @tpm-tis: TPM TIS model
+# @tpm-tis: TPM TIS model (since 1.5)
+# @tpm-crb: TPM CRB model (since 2.11)
 #
 # Since: 1.5
 ##
-{ 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] }
+{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb' ] }
 
 ##
 # @query-tpm-models:
@@ -28,7 +29,7 @@
 # Example:
 #
 # -> { "execute": "query-tpm-models" }
-# <- { "return": [ "tpm-tis" ] }
+# <- { "return": [ "tpm-tis", "tpm-crb" ] }
 #
 ##
 { 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h
index 6d516c6a7f..c0b9a0ca6e 100644
--- a/include/hw/acpi/tpm.h
+++ b/include/hw/acpi/tpm.h
@@ -16,11 +16,75 @@
 #ifndef HW_ACPI_TPM_H
 #define HW_ACPI_TPM_H
 
+#include "qemu/osdep.h"
+
 #define TPM_TIS_ADDR_BASE           0xFED40000
 #define TPM_TIS_ADDR_SIZE           0x5000
 
 #define TPM_TIS_IRQ                 5
 
+struct crb_regs {
+    union {
+        uint32_t loc_state;
+        struct {
+            unsigned tpm_established:1;
+            unsigned loc_assigned:1;
+            unsigned active_locality:3;
+            unsigned reserved:2;
+            unsigned tpm_reg_valid_sts:1;
+        } loc_state_bits;
+    };
+    uint32_t reserved1;
+    uint32_t loc_ctrl;
+    uint32_t loc_sts;
+    uint8_t reserved2[32];
+    union {
+        uint64_t intf_id;
+        struct {
+            unsigned type:4;
+            unsigned version:4;
+            unsigned cap_locality:1;
+            unsigned cap_crb_idle_bypass:1;
+            unsigned reserved1:1;
+            unsigned cap_data_xfer_size_support:2;
+            unsigned cap_fifo:1;
+            unsigned cap_crb:1;
+            unsigned cap_if_res:2;
+            unsigned if_selector:2;
+            unsigned if_selector_lock:1;
+            unsigned reserved2:4;
+            unsigned rid:8;
+            unsigned vid:16;
+            unsigned did:16;
+        } intf_id_bits;
+    };
+    uint64_t ctrl_ext;
+
+    uint32_t ctrl_req;
+    union {
+        uint32_t ctrl_sts;
+        struct {
+            unsigned tpm_sts:1;
+            unsigned tpm_idle:1;
+            unsigned reserved:30;
+        } ctrl_sts_bits;
+    };
+    uint32_t ctrl_cancel;
+    uint32_t ctrl_start;
+    uint32_t ctrl_int_enable;
+    uint32_t ctrl_int_sts;
+    uint32_t ctrl_cmd_size;
+    uint32_t ctrl_cmd_pa_low;
+    uint32_t ctrl_cmd_pa_high;
+    uint32_t ctrl_rsp_size;
+    uint64_t ctrl_rsp_pa;
+} QEMU_PACKED;
+
+#define TPM_CRB_ADDR_BASE           0xFED40000
+#define TPM_CRB_ADDR_SIZE           0x1000
+#define TPM_CRB_ADDR_CTRL \
+    (TPM_CRB_ADDR_BASE + offsetof(struct crb_regs, ctrl_req))
+
 #define TPM_LOG_AREA_MINIMUM_SIZE   (64 * 1024)
 
 #define TPM_TCPA_ACPI_CLASS_CLIENT  0
@@ -30,5 +94,6 @@
 #define TPM2_ACPI_CLASS_SERVER      1
 
 #define TPM2_START_METHOD_MMIO      6
+#define TPM2_START_METHOD_CRB       7
 
 #endif /* HW_ACPI_TPM_H */
diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h
index 8223ec621c..bdc6bde109 100644
--- a/include/sysemu/tpm.h
+++ b/include/sysemu/tpm.h
@@ -46,9 +46,12 @@ int tpm_init(void);
 void tpm_cleanup(void);
 
 #define TYPE_TPM_TIS                "tpm-tis"
+#define TYPE_TPM_CRB                "tpm-crb"
 
 #define TPM_IS_TIS(chr)                             \
     object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS)
+#define TPM_IS_CRB(chr)                             \
+    object_dynamic_cast(OBJECT(chr), TYPE_TPM_CRB)
 
 static inline TPMIf *tpm_find(void)
 {
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index ee38b00e31..f9345c75e6 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -2224,6 +2224,22 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
             aml_append(sb_scope, scope);
         }
     }
+
+    if (TPM_IS_CRB(tpm_find())) {
+        dev = aml_device("TPM");
+        aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
+        crs = aml_resource_template();
+        aml_append(crs, aml_memory32_fixed(TPM_CRB_ADDR_BASE,
+                                           TPM_CRB_ADDR_SIZE, AML_READ_WRITE));
+        aml_append(dev, aml_name_decl("_CRS", crs));
+
+        method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+        aml_append(method, aml_return(aml_int(0x0f)));
+        aml_append(dev, method);
+
+        aml_append(sb_scope, dev);
+    }
+
     aml_append(dsdt, sb_scope);
 
     /* copy AML table into ACPI tables blob and patch header there */
@@ -2284,6 +2300,9 @@ build_tpm2(GArray *table_data, BIOSLinker *linker)
     if (TPM_IS_TIS(tpm_find())) {
         tpm2_ptr->control_area_address = cpu_to_le64(0);
         tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
+    } else if (TPM_IS_CRB(tpm_find())) {
+        tpm2_ptr->control_area_address = cpu_to_le32(TPM_CRB_ADDR_CTRL);
+        tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_CRB);
     } else {
         g_warn_if_reached();
     }
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
new file mode 100644
index 0000000000..64039ebc8e
--- /dev/null
+++ b/hw/tpm/tpm_crb.c
@@ -0,0 +1,320 @@
+/*
+ * tpm_crb.c - QEMU's TPM CRB interface emulator
+ *
+ * Copyright (c) 2017 Red Hat, Inc.
+ *
+ * Authors:
+ *   Marc-André Lureau <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.
+ *
+ * tpm_crb is a device for TPM 2.0 Command Response Buffer (CRB) Interface
+ * as defined in TCG PC Client Platform TPM Profile (PTP) Specification
+ * Family “2.0” Level 00 Revision 01.03 v22
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+
+#include "hw/pci/pci_ids.h"
+#include "hw/acpi/tpm.h"
+#include "sysemu/tpm_backend.h"
+#include "tpm_int.h"
+#include "tpm_util.h"
+
+typedef struct CRBState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+    MemoryRegion cmdmem;
+    char *backend;
+    TPMBackend *tpmbe;
+    TPMBackendCmd cmd;
+    struct crb_regs regs;
+} CRBState;
+
+#define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB)
+
+#define DEBUG_CRB 0
+
+#define DPRINTF(fmt, ...) do {                  \
+        if (DEBUG_CRB) {                        \
+            printf(fmt, ## __VA_ARGS__);        \
+        }                                       \
+    } while (0);
+
+#define CRB_ADDR_LOC_STATE offsetof(struct crb_regs, loc_state)
+#define CRB_ADDR_LOC_CTRL offsetof(struct crb_regs, loc_ctrl)
+#define CRB_ADDR_CTRL_REQ offsetof(struct crb_regs, ctrl_req)
+#define CRB_ADDR_CTRL_CANCEL offsetof(struct crb_regs, ctrl_cancel)
+#define CRB_ADDR_CTRL_START offsetof(struct crb_regs, ctrl_start)
+
+#define CRB_INTF_TYPE_CRB_ACTIVE 0b1
+#define CRB_INTF_VERSION_CRB 0b1
+#define CRB_INTF_CAP_LOCALITY_0_ONLY 0b0
+#define CRB_INTF_CAP_IDLE_FAST 0b0
+#define CRB_INTF_CAP_XFER_SIZE_64 0b11
+#define CRB_INTF_CAP_FIFO_NOT_SUPPORTED 0b0
+#define CRB_INTF_CAP_CRB_SUPPORTED 0b1
+#define CRB_INTF_IF_SELECTOR_CRB 0b1
+#define CRB_INTF_IF_SELECTOR_UNLOCKED 0b0
+
+#define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - sizeof(struct crb_regs))
+
+enum crb_loc_ctrl {
+    CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0),
+    CRB_LOC_CTRL_RELINQUISH = BIT(1),
+    CRB_LOC_CTRL_SEIZE = BIT(2),
+    CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT = BIT(3),
+};
+
+enum crb_ctrl_req {
+    CRB_CTRL_REQ_CMD_READY = BIT(0),
+    CRB_CTRL_REQ_GO_IDLE = BIT(1),
+};
+
+enum crb_ctrl_sts {
+    CRB_CTRL_STS_ERROR = BIT(0),
+    CRB_CTRL_STS_TPM_IDLE = BIT(1),
+};
+
+enum crb_start {
+    CRB_START_INVOKE = BIT(0),
+};
+
+enum crb_cancel {
+    CRB_CANCEL_INVOKE = BIT(0),
+};
+
+static const char *addr_desc(unsigned off)
+{
+    switch (off) {
+#define CASE(off)                               \
+    case offsetof(struct crb_regs, off):        \
+        return G_STRINGIFY(off)
+        CASE(loc_state);
+        CASE(reserved1);
+        CASE(loc_ctrl);
+        CASE(loc_sts);
+        CASE(reserved2);
+        CASE(intf_id);
+        CASE(ctrl_ext);
+        CASE(ctrl_req);
+        CASE(ctrl_sts);
+        CASE(ctrl_cancel);
+        CASE(ctrl_start);
+        CASE(ctrl_int_enable);
+        CASE(ctrl_int_sts);
+        CASE(ctrl_cmd_size);
+        CASE(ctrl_cmd_pa_low);
+        CASE(ctrl_cmd_pa_high);
+        CASE(ctrl_rsp_size);
+        CASE(ctrl_rsp_pa);
+#undef CASE
+    }
+    return NULL;
+}
+
+static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr,
+                                  unsigned size)
+{
+    CRBState *s = CRB(opaque);
+    DPRINTF("CRB read %lx:%s %u\n", addr, addr_desc(addr), size);
+
+    /* all registers are 32-bit aligned */
+    if (addr % 4) {
+        return G_MAXUINT64;
+    }
+    return ((uint32_t *)&s->regs)[addr / 4];
+}
+
+static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
+                               uint64_t val, unsigned size)
+{
+    CRBState *s = CRB(opaque);
+    DPRINTF("CRB write %lx:%s %lu %u\n", addr, addr_desc(addr), val, size);
+
+    switch (addr) {
+    case CRB_ADDR_CTRL_REQ:
+        switch (val) {
+        case CRB_CTRL_REQ_CMD_READY:
+            s->regs.ctrl_sts_bits.tpm_idle = 0;
+            break;
+        case CRB_CTRL_REQ_GO_IDLE:
+            s->regs.ctrl_sts_bits.tpm_idle = 1;
+            break;
+        }
+        break;
+    case CRB_ADDR_CTRL_CANCEL:
+        if (val == CRB_CANCEL_INVOKE && s->regs.ctrl_start & CRB_START_INVOKE) 
{
+            tpm_backend_cancel_cmd(s->tpmbe);
+        }
+        break;
+    case CRB_ADDR_CTRL_START:
+        if (val == CRB_START_INVOKE &&
+            !(s->regs.ctrl_start & CRB_START_INVOKE)) {
+            void *mem = memory_region_get_ram_ptr(&s->cmdmem);
+
+            s->regs.ctrl_start |= CRB_START_INVOKE;
+            s->cmd = (TPMBackendCmd) {
+                .in = mem,
+                .in_len = MIN(tpm_cmd_get_size(mem), CRB_CTRL_CMD_SIZE),
+                .out = mem,
+                .out_len = CRB_CTRL_CMD_SIZE,
+            };
+
+            tpm_backend_deliver_request(s->tpmbe, &s->cmd);
+        }
+        break;
+    case CRB_ADDR_LOC_CTRL:
+        switch (val) {
+        case CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT:
+            /* not loc 3 or 4 */
+            break;
+        case CRB_LOC_CTRL_RELINQUISH:
+            break;
+        case CRB_LOC_CTRL_REQUEST_ACCESS:
+            s->regs.loc_state_bits.loc_assigned = 1;
+            s->regs.loc_state_bits.tpm_reg_valid_sts = 1;
+            break;
+        }
+        break;
+    }
+}
+
+static const MemoryRegionOps tpm_crb_memory_ops = {
+    .read = tpm_crb_mmio_read,
+    .write = tpm_crb_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static void tpm_crb_reset(DeviceState *dev)
+{
+    CRBState *s = CRB(dev);
+
+    s->regs = (struct crb_regs) {
+        .intf_id_bits = {
+            .type = CRB_INTF_TYPE_CRB_ACTIVE,
+            .version = CRB_INTF_VERSION_CRB,
+            .cap_locality = CRB_INTF_CAP_LOCALITY_0_ONLY,
+            .cap_crb_idle_bypass = CRB_INTF_CAP_IDLE_FAST,
+            .cap_data_xfer_size_support = CRB_INTF_CAP_XFER_SIZE_64,
+            .cap_fifo = CRB_INTF_CAP_FIFO_NOT_SUPPORTED,
+            .cap_crb = CRB_INTF_CAP_CRB_SUPPORTED,
+            .cap_if_res = 0b0,
+            .if_selector = CRB_INTF_IF_SELECTOR_CRB,
+            .if_selector_lock = CRB_INTF_IF_SELECTOR_UNLOCKED,
+            .rid = 0b0001,
+            .vid = PCI_VENDOR_ID_IBM,
+            .did = 0b0001,
+        },
+        .ctrl_cmd_size = CRB_CTRL_CMD_SIZE,
+        .ctrl_cmd_pa_low = TPM_CRB_ADDR_BASE + sizeof(struct crb_regs),
+        .ctrl_rsp_size = CRB_CTRL_CMD_SIZE,
+        .ctrl_rsp_pa = TPM_CRB_ADDR_BASE + sizeof(struct crb_regs),
+    };
+
+    tpm_backend_reset(s->tpmbe);
+    tpm_backend_startup_tpm(s->tpmbe);
+}
+
+static void tpm_crb_request_completed(TPMIf *ti)
+{
+    CRBState *s = CRB(ti);
+
+    s->regs.ctrl_start &= ~CRB_START_INVOKE;
+    /* TODO, in case of error: s->regs.ctrl_sts = CRB_CTRL_STS_ERROR */
+}
+
+static enum TPMVersion tpm_crb_get_version(TPMIf *ti)
+{
+    CRBState *s = CRB(ti);
+
+    return tpm_backend_get_tpm_version(s->tpmbe);
+}
+
+static const VMStateDescription vmstate_tpm_crb = {
+    .name = "tpm-crb",
+    .unmigratable = 1,
+};
+
+static Property tpm_crb_properties[] = {
+    DEFINE_PROP_STRING("tpmdev", CRBState, backend),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tpm_crb_realizefn(DeviceState *dev, Error **errp)
+{
+    CRBState *s = CRB(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    if (!tpm_find()) {
+        error_setg(errp, "at most one TPM device is permitted");
+        return;
+    }
+
+    s->tpmbe = qemu_find_tpm_be(s->backend);
+    if (!s->tpmbe) {
+        error_setg(errp, "tpm-crb: backend driver with id '%s' could not be "
+                   "found", s->backend);
+        return;
+    }
+
+    if (tpm_backend_init(s->tpmbe, TPM_IF(s), errp)) {
+        return;
+    }
+
+    memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s,
+        "tpm-crb-mmio", sizeof(struct crb_regs));
+    memory_region_init_ram(&s->cmdmem, OBJECT(s),
+        "tpm-crb-cmd", CRB_CTRL_CMD_SIZE, errp);
+
+    sysbus_init_mmio(sbd, &s->mmio);
+    sysbus_mmio_map(sbd, 0, TPM_CRB_ADDR_BASE);
+    /* allocate ram in bios instead? */
+    memory_region_add_subregion(get_system_memory(),
+        TPM_CRB_ADDR_BASE + sizeof(struct crb_regs), &s->cmdmem);
+}
+
+static void tpm_crb_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    TPMIfClass *tc = TPM_IF_CLASS(klass);
+
+    dc->realize = tpm_crb_realizefn;
+    dc->props = tpm_crb_properties;
+    dc->reset = tpm_crb_reset;
+    dc->vmsd  = &vmstate_tpm_crb;
+    dc->user_creatable = true;
+    tc->model = TPM_MODEL_TPM_CRB;
+    tc->get_version = tpm_crb_get_version;
+    tc->request_completed = tpm_crb_request_completed;
+}
+
+static const TypeInfo tpm_crb_info = {
+    .name = TYPE_TPM_CRB,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(CRBState),
+    .class_init  = tpm_crb_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_TPM_IF },
+        { }
+    }
+};
+
+static void tpm_crb_register(void)
+{
+    type_register_static(&tpm_crb_info);
+    tpm_register_model(TPM_MODEL_TPM_CRB);
+}
+
+type_init(tpm_crb_register)
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index d2ab2f6655..c10afe953a 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -36,6 +36,7 @@ CONFIG_APPLESMC=y
 CONFIG_I8259=y
 CONFIG_PFLASH_CFI01=y
 CONFIG_TPM_TIS=$(CONFIG_TPM)
+CONFIG_TPM_CRB=$(CONFIG_TPM)
 CONFIG_MC146818RTC=y
 CONFIG_PCI_PIIX=y
 CONFIG_WDT_IB700=y
diff --git a/default-configs/x86_64-softmmu.mak 
b/default-configs/x86_64-softmmu.mak
index 9bde2f1c4b..1a6004f3f8 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -36,6 +36,7 @@ CONFIG_APPLESMC=y
 CONFIG_I8259=y
 CONFIG_PFLASH_CFI01=y
 CONFIG_TPM_TIS=$(CONFIG_TPM)
+CONFIG_TPM_CRB=$(CONFIG_TPM)
 CONFIG_MC146818RTC=y
 CONFIG_PCI_PIIX=y
 CONFIG_WDT_IB700=y
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
index 41f0b7a590..5c98af0de4 100644
--- a/hw/tpm/Makefile.objs
+++ b/hw/tpm/Makefile.objs
@@ -1,3 +1,4 @@
 common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
+common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o
 common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
 common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
-- 
2.14.1.146.gd35faa819




reply via email to

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