summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Maydell2018-01-30 16:20:01 +0100
committerPeter Maydell2018-01-30 16:20:01 +0100
commit6521130b0a7f699fdb82446d57df5627bfa7ed3c (patch)
tree4133f36786798486d7222df1dbcc9d1919708116
parentlinux-user/signal.c: Rename MC_* defines (diff)
parenttpm: add CRB device (diff)
downloadqemu-6521130b0a7f699fdb82446d57df5627bfa7ed3c.tar.gz
qemu-6521130b0a7f699fdb82446d57df5627bfa7ed3c.tar.xz
qemu-6521130b0a7f699fdb82446d57df5627bfa7ed3c.zip
Merge remote-tracking branch 'remotes/stefanberger/tags/pull-tpm-2018-01-26-2' into staging
Merge tpm 2018/01/26 v2 # gpg: Signature made Mon 29 Jan 2018 22:20:05 GMT # gpg: using RSA key 0x75AD65802A0B4211 # gpg: Good signature from "Stefan Berger <stefanb@linux.vnet.ibm.com>" # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: B818 B9CA DF90 89C2 D5CE C66B 75AD 6580 2A0B 4211 * remotes/stefanberger/tags/pull-tpm-2018-01-26-2: tpm: add CRB device tpm: report backend request error tpm: replace GThreadPool with AIO threadpool tpm: lookup cancel path under tpm device class tpm: fix alignment issues tpm: Set the flags of the CMD_INIT command to 0 Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--backends/tpm.c59
-rw-r--r--default-configs/i386-softmmu.mak1
-rw-r--r--default-configs/x86_64-softmmu.mak1
-rw-r--r--hw/i386/acpi-build.c34
-rw-r--r--hw/tpm/Makefile.objs1
-rw-r--r--hw/tpm/tpm_crb.c303
-rw-r--r--hw/tpm/tpm_emulator.c39
-rw-r--r--hw/tpm/tpm_passthrough.c71
-rw-r--r--hw/tpm/tpm_tis.c3
-rw-r--r--hw/tpm/tpm_util.c75
-rw-r--r--hw/tpm/tpm_util.h17
-rw-r--r--include/hw/acpi/tpm.h51
-rw-r--r--include/sysemu/tpm.h5
-rw-r--r--include/sysemu/tpm_backend.h15
-rw-r--r--qapi/tpm.json5
-rw-r--r--tests/Makefile.include2
-rw-r--r--tests/tpm-crb-test.c275
17 files changed, 818 insertions, 139 deletions
diff --git a/backends/tpm.c b/backends/tpm.c
index 91222c5164..d617ba7c52 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -19,30 +19,40 @@
#include "sysemu/tpm.h"
#include "qemu/thread.h"
#include "qemu/main-loop.h"
+#include "block/thread-pool.h"
+#include "qemu/error-report.h"
-static void tpm_backend_request_completed_bh(void *opaque)
+static void tpm_backend_request_completed(void *opaque, int ret)
{
TPMBackend *s = TPM_BACKEND(opaque);
TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
- tic->request_completed(s->tpmif);
+ tic->request_completed(s->tpmif, ret);
+
+ /* no need for atomic, as long the BQL is taken */
+ s->cmd = NULL;
+ object_unref(OBJECT(s));
}
-static void tpm_backend_worker_thread(gpointer data, gpointer user_data)
+static int tpm_backend_worker_thread(gpointer data)
{
- TPMBackend *s = TPM_BACKEND(user_data);
+ TPMBackend *s = TPM_BACKEND(data);
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
+ Error *err = NULL;
- k->handle_request(s, (TPMBackendCmd *)data);
+ k->handle_request(s, s->cmd, &err);
+ if (err) {
+ error_report_err(err);
+ return -1;
+ }
- qemu_bh_schedule(s->bh);
+ return 0;
}
-static void tpm_backend_thread_end(TPMBackend *s)
+void tpm_backend_finish_sync(TPMBackend *s)
{
- if (s->thread_pool) {
- g_thread_pool_free(s->thread_pool, FALSE, TRUE);
- s->thread_pool = NULL;
+ while (s->cmd) {
+ aio_poll(qemu_get_aio_context(), true);
}
}
@@ -74,10 +84,7 @@ int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize)
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
/* terminate a running TPM */
- tpm_backend_thread_end(s);
-
- s->thread_pool = g_thread_pool_new(tpm_backend_worker_thread, s, 1, TRUE,
- NULL);
+ tpm_backend_finish_sync(s);
res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0;
@@ -93,7 +100,17 @@ bool tpm_backend_had_startup_error(TPMBackend *s)
void tpm_backend_deliver_request(TPMBackend *s, TPMBackendCmd *cmd)
{
- g_thread_pool_push(s->thread_pool, cmd, NULL);
+ ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context());
+
+ if (s->cmd != NULL) {
+ error_report("There is a TPM request pending");
+ return;
+ }
+
+ s->cmd = cmd;
+ object_ref(OBJECT(s));
+ thread_pool_submit_aio(pool, tpm_backend_worker_thread, s,
+ tpm_backend_request_completed, s);
}
void tpm_backend_reset(TPMBackend *s)
@@ -104,7 +121,7 @@ void tpm_backend_reset(TPMBackend *s)
k->reset(s);
}
- tpm_backend_thread_end(s);
+ tpm_backend_finish_sync(s);
s->had_startup_error = false;
}
@@ -159,28 +176,18 @@ TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
return info;
}
-static void tpm_backend_instance_init(Object *obj)
-{
- TPMBackend *s = TPM_BACKEND(obj);
-
- s->bh = qemu_bh_new(tpm_backend_request_completed_bh, s);
-}
-
static void tpm_backend_instance_finalize(Object *obj)
{
TPMBackend *s = TPM_BACKEND(obj);
object_unref(OBJECT(s->tpmif));
g_free(s->id);
- tpm_backend_thread_end(s);
- qemu_bh_delete(s->bh);
}
static const TypeInfo tpm_backend_info = {
.name = TYPE_TPM_BACKEND,
.parent = TYPE_OBJECT,
.instance_size = sizeof(TPMBackend),
- .instance_init = tpm_backend_instance_init,
.instance_finalize = tpm_backend_instance_finalize,
.class_size = sizeof(TPMBackendClass),
.abstract = true,
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 95ac4b464a..ac27700e79 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -37,6 +37,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 0221236825..b2104ade19 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -37,6 +37,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/i386/acpi-build.c b/hw/i386/acpi-build.c
index dc4b2b9ffe..ed78c4ed9f 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 */
@@ -2285,18 +2301,20 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog)
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);
-
- tpm2_ptr->log_area_minimum_length =
- cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE);
-
- /* log area start address to be filled by Guest linker */
- bios_linker_loader_add_pointer(linker,
- ACPI_BUILD_TABLE_FILE, log_addr_offset, log_addr_size,
- ACPI_BUILD_TPMLOG_FILE, 0);
+ } else if (TPM_IS_CRB(tpm_find())) {
+ tpm2_ptr->control_area_address = cpu_to_le64(TPM_CRB_ADDR_CTRL);
+ tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_CRB);
} else {
g_warn_if_reached();
}
+ tpm2_ptr->log_area_minimum_length =
+ cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE);
+
+ /* log area start address to be filled by Guest linker */
+ bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
+ log_addr_offset, log_addr_size,
+ ACPI_BUILD_TPMLOG_FILE, 0);
build_header(linker, table_data,
(void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL);
}
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
index 7a93b24636..1dc9f8bf2c 100644
--- a/hw/tpm/Makefile.objs
+++ b/hw/tpm/Makefile.objs
@@ -1,4 +1,5 @@
common-obj-y += tpm_util.o
common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
+common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o
common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
new file mode 100644
index 0000000000..687d2557b7
--- /dev/null
+++ b/hw/tpm/tpm_crb.c
@@ -0,0 +1,303 @@
+/*
+ * tpm_crb.c - QEMU's TPM CRB interface emulator
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * Authors:
+ * Marc-André Lureau <marcandre.lureau@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 "exec/address-spaces.h"
+
+#include "hw/qdev-core.h"
+#include "hw/qdev-properties.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/acpi/tpm.h"
+#include "migration/vmstate.h"
+#include "sysemu/tpm_backend.h"
+#include "tpm_int.h"
+#include "tpm_util.h"
+
+typedef struct CRBState {
+ DeviceState parent_obj;
+
+ TPMBackend *tpmbe;
+ TPMBackendCmd cmd;
+ uint32_t regs[TPM_CRB_R_MAX];
+ MemoryRegion mmio;
+ MemoryRegion cmdmem;
+
+ size_t be_buffer_size;
+} 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_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_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER)
+
+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_start {
+ CRB_START_INVOKE = BIT(0),
+};
+
+enum crb_cancel {
+ CRB_CANCEL_INVOKE = BIT(0),
+};
+
+static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ CRBState *s = CRB(opaque);
+ void *regs = (void *)&s->regs + (addr & ~3);
+ unsigned offset = addr & 3;
+ uint32_t val = *(uint32_t *)regs >> (8 * offset);
+
+ DPRINTF("CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 "\n",
+ addr, size, val);
+ return val;
+}
+
+static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ CRBState *s = CRB(opaque);
+ DPRINTF("CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx64 "\n",
+ addr, size, val);
+
+ switch (addr) {
+ case A_CRB_CTRL_REQ:
+ switch (val) {
+ case CRB_CTRL_REQ_CMD_READY:
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
+ tpmIdle, 0);
+ break;
+ case CRB_CTRL_REQ_GO_IDLE:
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
+ tpmIdle, 1);
+ break;
+ }
+ break;
+ case A_CRB_CTRL_CANCEL:
+ if (val == CRB_CANCEL_INVOKE &&
+ s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) {
+ tpm_backend_cancel_cmd(s->tpmbe);
+ }
+ break;
+ case A_CRB_CTRL_START:
+ if (val == CRB_START_INVOKE &&
+ !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE)) {
+ void *mem = memory_region_get_ram_ptr(&s->cmdmem);
+
+ s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE;
+ s->cmd = (TPMBackendCmd) {
+ .in = mem,
+ .in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size),
+ .out = mem,
+ .out_len = s->be_buffer_size,
+ };
+
+ tpm_backend_deliver_request(s->tpmbe, &s->cmd);
+ }
+ break;
+ case A_CRB_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:
+ ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS,
+ Granted, 1);
+ ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS,
+ beenSeized, 0);
+ ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE,
+ locAssigned, 1);
+ ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE,
+ tpmRegValidSts, 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_request_completed(TPMIf *ti, int ret)
+{
+ CRBState *s = CRB(ti);
+
+ s->regs[R_CRB_CTRL_START] &= ~CRB_START_INVOKE;
+ if (ret != 0) {
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
+ tpmSts, 1); /* fatal error */
+ }
+}
+
+static enum TPMVersion tpm_crb_get_version(TPMIf *ti)
+{
+ CRBState *s = CRB(ti);
+
+ return tpm_backend_get_tpm_version(s->tpmbe);
+}
+
+static int tpm_crb_pre_save(void *opaque)
+{
+ CRBState *s = opaque;
+
+ tpm_backend_finish_sync(s->tpmbe);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_tpm_crb = {
+ .name = "tpm-crb",
+ .pre_save = tpm_crb_pre_save,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static Property tpm_crb_properties[] = {
+ DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tpm_crb_realize(DeviceState *dev, Error **errp)
+{
+ CRBState *s = CRB(dev);
+
+ if (!tpm_find()) {
+ error_setg(errp, "at most one TPM device is permitted");
+ return;
+ }
+ if (!s->tpmbe) {
+ error_setg(errp, "'tpmdev' property is required");
+ return;
+ }
+
+ memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s,
+ "tpm-crb-mmio", sizeof(s->regs));
+ memory_region_init_ram(&s->cmdmem, OBJECT(s),
+ "tpm-crb-cmd", CRB_CTRL_CMD_SIZE, errp);
+
+ memory_region_add_subregion(get_system_memory(),
+ TPM_CRB_ADDR_BASE, &s->mmio);
+ memory_region_add_subregion(get_system_memory(),
+ TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem);
+
+ tpm_backend_reset(s->tpmbe);
+
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ InterfaceType, CRB_INTF_TYPE_CRB_ACTIVE);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ InterfaceVersion, CRB_INTF_VERSION_CRB);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ CapLocality, CRB_INTF_CAP_LOCALITY_0_ONLY);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ CapCRBIdleBypass, CRB_INTF_CAP_IDLE_FAST);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ CapDataXferSizeSupport, CRB_INTF_CAP_XFER_SIZE_64);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ CapFIFO, CRB_INTF_CAP_FIFO_NOT_SUPPORTED);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ CapCRB, CRB_INTF_CAP_CRB_SUPPORTED);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ InterfaceSelector, CRB_INTF_IF_SELECTOR_CRB);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ RID, 0b0000);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID2,
+ VID, PCI_VENDOR_ID_IBM);
+
+ s->regs[R_CRB_CTRL_CMD_SIZE] = CRB_CTRL_CMD_SIZE;
+ s->regs[R_CRB_CTRL_CMD_LADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER;
+ s->regs[R_CRB_CTRL_RSP_SIZE] = CRB_CTRL_CMD_SIZE;
+ s->regs[R_CRB_CTRL_RSP_ADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER;
+
+ s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->tpmbe),
+ CRB_CTRL_CMD_SIZE);
+
+ tpm_backend_startup_tpm(s->tpmbe, s->be_buffer_size);
+}
+
+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_realize;
+ dc->props = tpm_crb_properties;
+ 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;
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo tpm_crb_info = {
+ .name = TYPE_TPM_CRB,
+ /* could be TYPE_SYS_BUS_DEVICE (or LPC etc) */
+ .parent = TYPE_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);
+}
+
+type_init(tpm_crb_register)
diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
index 35c78de5a9..710a9ec718 100644
--- a/hw/tpm/tpm_emulator.c
+++ b/hw/tpm/tpm_emulator.c
@@ -120,7 +120,6 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
{
ssize_t ret;
bool is_selftest = false;
- const struct tpm_resp_hdr *hdr = NULL;
if (selftest_done) {
*selftest_done = false;
@@ -132,22 +131,21 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
return -1;
}
- ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, sizeof(*hdr),
- err);
+ ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out,
+ sizeof(struct tpm_resp_hdr), err);
if (ret != 0) {
return -1;
}
- hdr = (struct tpm_resp_hdr *)out;
- out += sizeof(*hdr);
- ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out,
- be32_to_cpu(hdr->len) - sizeof(*hdr) , err);
+ ret = qio_channel_read_all(tpm_emu->data_ioc,
+ (char *)out + sizeof(struct tpm_resp_hdr),
+ tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), err);
if (ret != 0) {
return -1;
}
if (is_selftest) {
- *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
+ *selftest_done = tpm_cmd_get_errcode(out) == 0;
}
return 0;
@@ -185,28 +183,19 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
return 0;
}
-static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
+static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
+ Error **errp)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
- Error *err = NULL;
DPRINTF("processing TPM command");
- if (tpm_emulator_set_locality(tpm_emu, cmd->locty, &err) < 0) {
- goto error;
- }
-
- if (tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
+ if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 ||
+ tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
cmd->out, cmd->out_len,
- &cmd->selftest_done, &err) < 0) {
- goto error;
+ &cmd->selftest_done, errp) < 0) {
+ tpm_util_write_fatal_error_response(cmd->out, cmd->out_len);
}
-
- return;
-
-error:
- tpm_util_write_fatal_error_response(cmd->out, cmd->out_len);
- error_report_err(err);
}
static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
@@ -320,7 +309,9 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
- ptm_init init;
+ ptm_init init = {
+ .u.req.init_flags = 0,
+ };
ptm_res res;
if (buffersize != 0 &&
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index 29142f38bb..a495fe07f4 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -80,14 +80,14 @@ static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
}
return ret;
}
-static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
- const uint8_t *in, uint32_t in_len,
- uint8_t *out, uint32_t out_len,
- bool *selftest_done)
+
+static void tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
+ const uint8_t *in, uint32_t in_len,
+ uint8_t *out, uint32_t out_len,
+ bool *selftest_done, Error **errp)
{
ssize_t ret;
bool is_selftest;
- const struct tpm_resp_hdr *hdr;
/* FIXME: protect shared variables or use other sync mechanism */
tpm_pt->tpm_op_canceled = false;
@@ -99,9 +99,8 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len);
if (ret != in_len) {
if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
- error_report("tpm_passthrough: error while transmitting data "
- "to TPM: %s (%i)",
- strerror(errno), errno);
+ error_setg_errno(errp, errno, "tpm_passthrough: error while "
+ "transmitting data to TPM");
}
goto err_exit;
}
@@ -111,20 +110,18 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
if (ret < 0) {
if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
- error_report("tpm_passthrough: error while reading data from "
- "TPM: %s (%i)",
- strerror(errno), errno);
+ error_setg_errno(errp, errno, "tpm_passthrough: error while "
+ "reading data from TPM");
}
} else if (ret < sizeof(struct tpm_resp_hdr) ||
- be32_to_cpu(((struct tpm_resp_hdr *)out)->len) != ret) {
+ tpm_cmd_get_size(out) != ret) {
ret = -1;
- error_report("tpm_passthrough: received invalid response "
- "packet from TPM");
+ error_setg_errno(errp, errno, "tpm_passthrough: received invalid "
+ "response packet from TPM");
}
if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
- hdr = (struct tpm_resp_hdr *)out;
- *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
+ *selftest_done = tpm_cmd_get_errcode(out) == 0;
}
err_exit:
@@ -133,18 +130,18 @@ err_exit:
}
tpm_pt->tpm_executing = false;
-
- return ret;
}
-static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
+static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
+ Error **errp)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
DPRINTF("tpm_passthrough: processing command %p\n", cmd);
tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len,
- cmd->out, cmd->out_len, &cmd->selftest_done);
+ cmd->out, cmd->out_len, &cmd->selftest_done,
+ errp);
}
static void tpm_passthrough_reset(TPMBackend *tb)
@@ -216,7 +213,8 @@ static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb)
* Unless path or file descriptor set has been provided by user,
* determine the sysfs cancel file following kernel documentation
* in Documentation/ABI/stable/sysfs-class-tpm.
- * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
+ * From /dev/tpm0 create /sys/class/tpm/tpm0/device/cancel
+ * before 4.0: /sys/class/misc/tpm0/device/cancel
*/
static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
{
@@ -227,26 +225,35 @@ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
if (tpm_pt->options->cancel_path) {
fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY);
if (fd < 0) {
- error_report("Could not open TPM cancel path : %s",
+ error_report("tpm_passthrough: Could not open TPM cancel path: %s",
strerror(errno));
}
return fd;
}
dev = strrchr(tpm_pt->tpm_dev, '/');
- if (dev) {
- dev++;
- if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
- dev) < sizeof(path)) {
- fd = qemu_open(path, O_WRONLY);
- if (fd < 0) {
- error_report("tpm_passthrough: Could not open TPM cancel "
- "path %s : %s", path, strerror(errno));
+ if (!dev) {
+ error_report("tpm_passthrough: Bad TPM device path %s",
+ tpm_pt->tpm_dev);
+ return -1;
+ }
+
+ dev++;
+ if (snprintf(path, sizeof(path), "/sys/class/tpm/%s/device/cancel",
+ dev) < sizeof(path)) {
+ fd = qemu_open(path, O_WRONLY);
+ if (fd < 0) {
+ if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
+ dev) < sizeof(path)) {
+ fd = qemu_open(path, O_WRONLY);
}
}
+ }
+
+ if (fd < 0) {
+ error_report("tpm_passthrough: Could not guess TPM cancel path");
} else {
- error_report("tpm_passthrough: Bad TPM device path %s",
- tpm_pt->tpm_dev);
+ tpm_pt->options->cancel_path = g_strdup(path);
}
return fd;
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c
index 8b5eb01a2c..08f41d2707 100644
--- a/hw/tpm/tpm_tis.c
+++ b/hw/tpm/tpm_tis.c
@@ -393,7 +393,7 @@ static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
/*
* Callback from the TPM to indicate that the response was received.
*/
-static void tpm_tis_request_completed(TPMIf *ti)
+static void tpm_tis_request_completed(TPMIf *ti, int ret)
{
TPMState *s = TPM(ti);
uint8_t locty = s->cmd.locty;
@@ -405,6 +405,7 @@ static void tpm_tis_request_completed(TPMIf *ti)
}
}
+ /* FIXME: report error if ret != 0 */
tpm_tis_sts_set(&s->loc[locty],
TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c
index 747075e244..8abde59915 100644
--- a/hw/tpm/tpm_util.c
+++ b/hw/tpm/tpm_util.c
@@ -106,20 +106,16 @@ const PropertyInfo qdev_prop_tpm = {
void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len)
{
if (out_len >= sizeof(struct tpm_resp_hdr)) {
- struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
-
- resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
- resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
- resp->errcode = cpu_to_be32(TPM_FAIL);
+ stw_be_p(out, TPM_TAG_RSP_COMMAND);
+ stl_be_p(out + 2, sizeof(struct tpm_resp_hdr));
+ stl_be_p(out + 6, TPM_FAIL);
}
}
bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len)
{
- struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;
-
- if (in_len >= sizeof(*hdr)) {
- return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
+ if (in_len >= sizeof(struct tpm_req_hdr)) {
+ return tpm_cmd_get_ordinal(in) == TPM_ORD_ContinueSelfTest;
}
return false;
@@ -129,12 +125,11 @@ bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len)
* Send request to a TPM device. We expect a response within one second.
*/
static int tpm_util_request(int fd,
- unsigned char *request,
+ const void *request,
size_t requestlen,
- unsigned char *response,
+ void *response,
size_t responselen)
{
- struct tpm_resp_hdr *resp;
fd_set readfds;
int n;
struct timeval tv = {
@@ -164,9 +159,8 @@ static int tpm_util_request(int fd,
return -EFAULT;
}
- resp = (struct tpm_resp_hdr *)response;
/* check the header */
- if (be32_to_cpu(resp->len) != n) {
+ if (tpm_cmd_get_size(response) != n) {
return -EMSGSIZE;
}
@@ -178,12 +172,11 @@ static int tpm_util_request(int fd,
* (error response is fine).
*/
static int tpm_util_test(int fd,
- unsigned char *request,
+ const void *request,
size_t requestlen,
uint16_t *return_tag)
{
- struct tpm_resp_hdr *resp;
- unsigned char buf[1024];
+ char buf[1024];
ssize_t ret;
ret = tpm_util_request(fd, request, requestlen,
@@ -192,8 +185,7 @@ static int tpm_util_test(int fd,
return ret;
}
- resp = (struct tpm_resp_hdr *)buf;
- *return_tag = be16_to_cpu(resp->tag);
+ *return_tag = tpm_cmd_get_tag(buf);
return 0;
}
@@ -228,7 +220,7 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
int ret;
/* Send TPM 2 command */
- ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2,
+ ret = tpm_util_test(tpm_fd, &test_req_tpm2,
sizeof(test_req_tpm2), &return_tag);
/* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */
if (!ret && return_tag == TPM2_ST_NO_SESSIONS) {
@@ -237,7 +229,7 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
}
/* Send TPM 1.2 command */
- ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req,
+ ret = tpm_util_test(tpm_fd, &test_req,
sizeof(test_req), &return_tag);
if (!ret && return_tag == TPM_TAG_RSP_COMMAND) {
*tpm_version = TPM_VERSION_1_2;
@@ -253,7 +245,6 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
size_t *buffersize)
{
- unsigned char buf[1024];
int ret;
switch (tpm_version) {
@@ -277,26 +268,27 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
struct tpm_resp_hdr hdr;
uint32_t len;
uint32_t buffersize;
- } QEMU_PACKED *tpm_resp = (struct tpm_resp_get_buffer_size *)buf;
+ } QEMU_PACKED tpm_resp;
- ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm_get_buffer_size,
- sizeof(tpm_get_buffer_size), buf, sizeof(buf));
+ ret = tpm_util_request(tpm_fd, &tpm_get_buffer_size,
+ sizeof(tpm_get_buffer_size),
+ &tpm_resp, sizeof(tpm_resp));
if (ret < 0) {
return ret;
}
- if (be32_to_cpu(tpm_resp->hdr.len) != sizeof(*tpm_resp) ||
- be32_to_cpu(tpm_resp->len) != sizeof(uint32_t)) {
+ if (be32_to_cpu(tpm_resp.hdr.len) != sizeof(tpm_resp) ||
+ be32_to_cpu(tpm_resp.len) != sizeof(uint32_t)) {
DPRINTF("tpm_resp->hdr.len = %u, expected = %zu\n",
- be32_to_cpu(tpm_resp->hdr.len), sizeof(*tpm_resp));
+ be32_to_cpu(tpm_resp.hdr.len), sizeof(tpm_resp));
DPRINTF("tpm_resp->len = %u, expected = %zu\n",
- be32_to_cpu(tpm_resp->len), sizeof(uint32_t));
+ be32_to_cpu(tpm_resp.len), sizeof(uint32_t));
error_report("tpm_util: Got unexpected response to "
"TPM_GetCapability; errcode: 0x%x",
- be32_to_cpu(tpm_resp->hdr.errcode));
+ be32_to_cpu(tpm_resp.hdr.errcode));
return -EFAULT;
}
- *buffersize = be32_to_cpu(tpm_resp->buffersize);
+ *buffersize = be32_to_cpu(tpm_resp.buffersize);
break;
}
case TPM_VERSION_2_0: {
@@ -324,27 +316,28 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
uint32_t value1;
uint32_t property2;
uint32_t value2;
- } QEMU_PACKED *tpm2_resp = (struct tpm2_resp_get_buffer_size *)buf;
+ } QEMU_PACKED tpm2_resp;
- ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm2_get_buffer_size,
- sizeof(tpm2_get_buffer_size), buf, sizeof(buf));
+ ret = tpm_util_request(tpm_fd, &tpm2_get_buffer_size,
+ sizeof(tpm2_get_buffer_size),
+ &tpm2_resp, sizeof(tpm2_resp));
if (ret < 0) {
return ret;
}
- if (be32_to_cpu(tpm2_resp->hdr.len) != sizeof(*tpm2_resp) ||
- be32_to_cpu(tpm2_resp->count) != 2) {
+ if (be32_to_cpu(tpm2_resp.hdr.len) != sizeof(tpm2_resp) ||
+ be32_to_cpu(tpm2_resp.count) != 2) {
DPRINTF("tpm2_resp->hdr.len = %u, expected = %zu\n",
- be32_to_cpu(tpm2_resp->hdr.len), sizeof(*tpm2_resp));
+ be32_to_cpu(tpm2_resp.hdr.len), sizeof(tpm2_resp));
DPRINTF("tpm2_resp->len = %u, expected = %u\n",
- be32_to_cpu(tpm2_resp->count), 2);
+ be32_to_cpu(tpm2_resp.count), 2);
error_report("tpm_util: Got unexpected response to "
"TPM2_GetCapability; errcode: 0x%x",
- be32_to_cpu(tpm2_resp->hdr.errcode));
+ be32_to_cpu(tpm2_resp.hdr.errcode));
return -EFAULT;
}
- *buffersize = MAX(be32_to_cpu(tpm2_resp->value1),
- be32_to_cpu(tpm2_resp->value2));
+ *buffersize = MAX(be32_to_cpu(tpm2_resp.value1),
+ be32_to_cpu(tpm2_resp.value2));
break;
}
case TPM_VERSION_UNSPEC:
diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h
index 19b28474ae..f003d15615 100644
--- a/hw/tpm/tpm_util.h
+++ b/hw/tpm/tpm_util.h
@@ -31,9 +31,24 @@ bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len);
int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version);
+static inline uint16_t tpm_cmd_get_tag(const void *b)
+{
+ return lduw_be_p(b);
+}
+
static inline uint32_t tpm_cmd_get_size(const void *b)
{
- return be32_to_cpu(*(const uint32_t *)(b + 2));
+ return ldl_be_p(b + 2);
+}
+
+static inline uint32_t tpm_cmd_get_ordinal(const void *b)
+{
+ return ldl_be_p(b + 6);
+}
+
+static inline uint32_t tpm_cmd_get_errcode(const void *b)
+{
+ return ldl_be_p(b + 6);
}
int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h
index 6d516c6a7f..96fd3a92f7 100644
--- a/include/hw/acpi/tpm.h
+++ b/include/hw/acpi/tpm.h
@@ -16,11 +16,61 @@
#ifndef HW_ACPI_TPM_H
#define HW_ACPI_TPM_H
+#include "hw/registerfields.h"
+
#define TPM_TIS_ADDR_BASE 0xFED40000
#define TPM_TIS_ADDR_SIZE 0x5000
#define TPM_TIS_IRQ 5
+REG32(CRB_LOC_STATE, 0x00)
+ FIELD(CRB_LOC_STATE, tpmEstablished, 0, 1)
+ FIELD(CRB_LOC_STATE, locAssigned, 1, 1)
+ FIELD(CRB_LOC_STATE, activeLocality, 2, 3)
+ FIELD(CRB_LOC_STATE, reserved, 5, 2)
+ FIELD(CRB_LOC_STATE, tpmRegValidSts, 7, 1)
+REG32(CRB_LOC_CTRL, 0x08)
+REG32(CRB_LOC_STS, 0x0C)
+ FIELD(CRB_LOC_STS, Granted, 0, 1)
+ FIELD(CRB_LOC_STS, beenSeized, 1, 1)
+REG32(CRB_INTF_ID, 0x30)
+ FIELD(CRB_INTF_ID, InterfaceType, 0, 4)
+ FIELD(CRB_INTF_ID, InterfaceVersion, 4, 4)
+ FIELD(CRB_INTF_ID, CapLocality, 8, 1)
+ FIELD(CRB_INTF_ID, CapCRBIdleBypass, 9, 1)
+ FIELD(CRB_INTF_ID, Reserved1, 10, 1)
+ FIELD(CRB_INTF_ID, CapDataXferSizeSupport, 11, 2)
+ FIELD(CRB_INTF_ID, CapFIFO, 13, 1)
+ FIELD(CRB_INTF_ID, CapCRB, 14, 1)
+ FIELD(CRB_INTF_ID, CapIFRes, 15, 2)
+ FIELD(CRB_INTF_ID, InterfaceSelector, 17, 2)
+ FIELD(CRB_INTF_ID, IntfSelLock, 19, 1)
+ FIELD(CRB_INTF_ID, Reserved2, 20, 4)
+ FIELD(CRB_INTF_ID, RID, 24, 8)
+REG32(CRB_INTF_ID2, 0x34)
+ FIELD(CRB_INTF_ID2, VID, 0, 16)
+ FIELD(CRB_INTF_ID2, DID, 16, 16)
+REG32(CRB_CTRL_EXT, 0x38)
+REG32(CRB_CTRL_REQ, 0x40)
+REG32(CRB_CTRL_STS, 0x44)
+ FIELD(CRB_CTRL_STS, tpmSts, 0, 1)
+ FIELD(CRB_CTRL_STS, tpmIdle, 1, 1)
+REG32(CRB_CTRL_CANCEL, 0x48)
+REG32(CRB_CTRL_START, 0x4C)
+REG32(CRB_INT_ENABLED, 0x50)
+REG32(CRB_INT_STS, 0x54)
+REG32(CRB_CTRL_CMD_SIZE, 0x58)
+REG32(CRB_CTRL_CMD_LADDR, 0x5C)
+REG32(CRB_CTRL_CMD_HADDR, 0x60)
+REG32(CRB_CTRL_RSP_SIZE, 0x64)
+REG32(CRB_CTRL_RSP_ADDR, 0x68)
+REG32(CRB_DATA_BUFFER, 0x80)
+
+#define TPM_CRB_ADDR_BASE 0xFED40000
+#define TPM_CRB_ADDR_SIZE 0x1000
+#define TPM_CRB_ADDR_CTRL (TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ)
+#define TPM_CRB_R_MAX R_CRB_DATA_BUFFER
+
#define TPM_LOG_AREA_MINIMUM_SIZE (64 * 1024)
#define TPM_TCPA_ACPI_CLASS_CLIENT 0
@@ -30,5 +80,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 852e02687c..233b1a3fc3 100644
--- a/include/sysemu/tpm.h
+++ b/include/sysemu/tpm.h
@@ -41,14 +41,17 @@ typedef struct TPMIfClass {
InterfaceClass parent_class;
enum TpmModel model;
- void (*request_completed)(TPMIf *obj);
+ void (*request_completed)(TPMIf *obj, int ret);
enum TPMVersion (*get_version)(TPMIf *obj);
} TPMIfClass;
#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)
/* returns NULL unless there is exactly one TPM device */
static inline TPMIf *tpm_find(void)
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 0d6c994a62..7e166ef954 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -18,6 +18,7 @@
#include "qapi-types.h"
#include "qemu/option.h"
#include "sysemu/tpm.h"
+#include "qapi/error.h"
#define TYPE_TPM_BACKEND "tpm-backend"
#define TPM_BACKEND(obj) \
@@ -45,9 +46,8 @@ struct TPMBackend {
/*< protected >*/
TPMIf *tpmif;
bool opened;
- GThreadPool *thread_pool;
bool had_startup_error;
- QEMUBH *bh;
+ TPMBackendCmd *cmd;
/* <public> */
char *id;
@@ -85,7 +85,7 @@ struct TPMBackendClass {
TpmTypeOptions *(*get_tpm_options)(TPMBackend *t);
- void (*handle_request)(TPMBackend *s, TPMBackendCmd *cmd);
+ void (*handle_request)(TPMBackend *s, TPMBackendCmd *cmd, Error **errp);
};
/**
@@ -197,6 +197,15 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s);
size_t tpm_backend_get_buffer_size(TPMBackend *s);
/**
+ * tpm_backend_finish_sync:
+ * @s: the backend to call into
+ *
+ * Finish the pending command synchronously (this will call aio_poll()
+ * on qemu main AIOContext until it ends)
+ */
+void tpm_backend_finish_sync(TPMBackend *s);
+
+/**
* tpm_backend_query_tpm:
* @s: the backend
*
diff --git a/qapi/tpm.json b/qapi/tpm.json
index 7093f268fb..d50deef5e9 100644
--- a/qapi/tpm.json
+++ b/qapi/tpm.json
@@ -11,10 +11,11 @@
# An enumeration of TPM models
#
# @tpm-tis: TPM TIS model
+# @tpm-crb: TPM CRB model (since 2.12)
#
# 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/tests/Makefile.include b/tests/Makefile.include
index 851aafe9d1..ca82e0c0cc 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -286,6 +286,7 @@ check-qtest-i386-$(CONFIG_VHOST_USER_NET_TEST_i386) += tests/vhost-user-test$(EX
ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),)
check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF)
endif
+check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF)
check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF)
check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF)
check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF)
@@ -708,6 +709,7 @@ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
tests/io-channel-helpers.o $(test-io-obj-y)
+tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o $(test-io-obj-y)
tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \
tests/io-channel-helpers.o $(test-io-obj-y)
tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \
diff --git a/tests/tpm-crb-test.c b/tests/tpm-crb-test.c
new file mode 100644
index 0000000000..8bf1507e00
--- /dev/null
+++ b/tests/tpm-crb-test.c
@@ -0,0 +1,275 @@
+/*
+ * QTest testcase for TPM CRB
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * Authors:
+ * Marc-André Lureau <marcandre.lureau@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.
+ */
+
+#include "qemu/osdep.h"
+#include <glib/gstdio.h>
+
+#include "hw/acpi/tpm.h"
+#include "hw/tpm/tpm_ioctl.h"
+#include "io/channel-socket.h"
+#include "libqtest.h"
+#include "qapi/error.h"
+
+#define TPM_RC_FAILURE 0x101
+#define TPM2_ST_NO_SESSIONS 0x8001
+
+struct tpm_hdr {
+ uint16_t tag;
+ uint32_t len;
+ uint32_t code; /*ordinal/error */
+ char buffer[];
+} QEMU_PACKED;
+
+typedef struct TestState {
+ CompatGMutex data_mutex;
+ CompatGCond data_cond;
+ SocketAddress *addr;
+ QIOChannel *tpm_ioc;
+ GThread *emu_tpm_thread;
+ struct tpm_hdr *tpm_msg;
+} TestState;
+
+static void test_wait_cond(TestState *s)
+{
+ gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
+
+ g_mutex_lock(&s->data_mutex);
+ if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
+ g_assert_not_reached();
+ }
+ g_mutex_unlock(&s->data_mutex);
+}
+
+static void *emu_tpm_thread(void *data)
+{
+ TestState *s = data;
+ QIOChannel *ioc = s->tpm_ioc;
+
+ s->tpm_msg = g_new(struct tpm_hdr, 1);
+ while (true) {
+ int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len);
+
+ if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) {
+ break;
+ }
+ s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag);
+ s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len);
+ g_assert_cmpint(s->tpm_msg->len, >=, minhlen);
+ g_assert_cmpint(s->tpm_msg->tag, ==, TPM2_ST_NO_SESSIONS);
+
+ s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len);
+ qio_channel_read(ioc, (char *)&s->tpm_msg->code,
+ s->tpm_msg->len - minhlen, &error_abort);
+ s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code);
+
+ /* reply error */
+ s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
+ s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr));
+ s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE);
+ qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len),
+ &error_abort);
+ }
+
+ g_free(s->tpm_msg);
+ s->tpm_msg = NULL;
+ object_unref(OBJECT(s->tpm_ioc));
+ return NULL;
+}
+
+static void *emu_ctrl_thread(void *data)
+{
+ TestState *s = data;
+ QIOChannelSocket *lioc = qio_channel_socket_new();
+ QIOChannel *ioc;
+
+ qio_channel_socket_listen_sync(lioc, s->addr, &error_abort);
+ g_cond_signal(&s->data_cond);
+
+ qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN);
+ ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
+ g_assert(ioc);
+
+ {
+ uint32_t cmd = 0;
+ struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) };
+ int *pfd = NULL;
+ size_t nfd = 0;
+
+ qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort);
+ cmd = be32_to_cpu(cmd);
+ g_assert_cmpint(cmd, ==, CMD_SET_DATAFD);
+ g_assert_cmpint(nfd, ==, 1);
+ s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort));
+ g_free(pfd);
+
+ cmd = 0;
+ qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort);
+
+ s->emu_tpm_thread = g_thread_new(NULL, emu_tpm_thread, s);
+ }
+
+ while (true) {
+ uint32_t cmd;
+ ssize_t ret;
+
+ ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL);
+ if (ret <= 0) {
+ break;
+ }
+
+ cmd = be32_to_cpu(cmd);
+ switch (cmd) {
+ case CMD_GET_CAPABILITY: {
+ ptm_cap cap = cpu_to_be64(0x3fff);
+ qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort);
+ break;
+ }
+ case CMD_INIT: {
+ ptm_init init;
+ qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req),
+ &error_abort);
+ init.u.resp.tpm_result = 0;
+ qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp),
+ &error_abort);
+ break;
+ }
+ case CMD_SHUTDOWN: {
+ ptm_res res = 0;
+ qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort);
+ qio_channel_close(s->tpm_ioc, &error_abort);
+ g_thread_join(s->emu_tpm_thread);
+ break;
+ }
+ case CMD_STOP: {
+ ptm_res res = 0;
+ qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort);
+ break;
+ }
+ case CMD_SET_BUFFERSIZE: {
+ ptm_setbuffersize sbs;
+ qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req),
+ &error_abort);
+ sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096);
+ sbs.u.resp.tpm_result = 0;
+ sbs.u.resp.minsize = cpu_to_be32(128);
+ sbs.u.resp.maxsize = cpu_to_be32(4096);
+ qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp),
+ &error_abort);
+ break;
+ }
+ case CMD_SET_LOCALITY: {
+ ptm_loc loc;
+ /* Note: this time it's not u.req / u.resp... */
+ qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort);
+ g_assert_cmpint(loc.u.req.loc, ==, 0);
+ loc.u.resp.tpm_result = 0;
+ qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort);
+ break;
+ }
+ default:
+ g_debug("unimplemented %u", cmd);
+ g_assert_not_reached();
+ }
+ }
+
+ object_unref(OBJECT(ioc));
+ object_unref(OBJECT(lioc));
+ return NULL;
+}
+
+#define TPM_CMD "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"
+
+static void tpm_crb_test(const void *data)
+{
+ const TestState *s = data;
+ uint32_t intfid = readl(TPM_CRB_ADDR_BASE + A_CRB_INTF_ID);
+ uint32_t csize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE);
+ uint64_t caddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
+ uint32_t rsize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_SIZE);
+ uint64_t raddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
+
+ g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceType), ==, 1);
+ g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceVersion), ==, 1);
+ g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapLocality), ==, 0);
+ g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRBIdleBypass), ==, 0);
+ g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapDataXferSizeSupport),
+ ==, 3);
+ g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapFIFO), ==, 0);
+ g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRB), ==, 1);
+ g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceSelector), ==, 1);
+ g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, RID), ==, 0);
+
+ g_assert_cmpint(csize, >=, 128);
+ g_assert_cmpint(rsize, >=, 128);
+ g_assert_cmpint(caddr, >, TPM_CRB_ADDR_BASE);
+ g_assert_cmpint(raddr, >, TPM_CRB_ADDR_BASE);
+
+ memwrite(caddr, TPM_CMD, sizeof(TPM_CMD));
+
+ uint32_t sts, start = 1;
+ uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
+ writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start);
+ do {
+ start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
+ if ((start & 1) == 0) {
+ break;
+ }
+ } while (g_get_monotonic_time() < end_time);
+ start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
+ g_assert_cmpint(start & 1, ==, 0);
+ sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
+ g_assert_cmpint(sts & 1, ==, 0);
+
+ struct tpm_hdr tpm_msg;
+ memread(raddr, &tpm_msg, sizeof(tpm_msg));
+ g_assert_cmpmem(&tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg));
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-crb-test.XXXXXX", NULL);
+ GThread *thread;
+ TestState test;
+
+ 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);
+
+ thread = g_thread_new(NULL, emu_ctrl_thread, &test);
+ test_wait_cond(&test);
+
+ args = g_strdup_printf(
+ "-chardev socket,id=chr,path=%s "
+ "-tpmdev emulator,id=dev,chardev=chr "
+ "-device tpm-crb,tpmdev=dev",
+ test.addr->u.q_unix.path);
+ qtest_start(args);
+
+ qtest_add_data_func("/tpm-crb/test", &test, tpm_crb_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(tmp_path);
+ g_free(args);
+ return ret;
+}