Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com>
---
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 <marcandre.lur...@redhat.com>
+ *
+ * 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