summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--backends/tpm.c92
-rw-r--r--hw/core/qdev-properties-system.c64
-rw-r--r--hw/i386/acpi-build.c14
-rw-r--r--hw/tpm/tpm_emulator.c98
-rw-r--r--hw/tpm/tpm_int.h31
-rw-r--r--hw/tpm/tpm_ioctl.h28
-rw-r--r--hw/tpm/tpm_passthrough.c90
-rw-r--r--hw/tpm/tpm_tis.c101
-rw-r--r--hw/tpm/tpm_util.c155
-rw-r--r--hw/tpm/tpm_util.h11
-rw-r--r--include/hw/qdev-properties.h3
-rw-r--r--include/sysemu/tpm.h48
-rw-r--r--include/sysemu/tpm_backend.h50
-rw-r--r--tpm.c34
14 files changed, 553 insertions, 266 deletions
diff --git a/backends/tpm.c b/backends/tpm.c
index 5763f6f369..91222c5164 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -17,16 +17,25 @@
 #include "qapi/error.h"
 #include "qapi/qmp/qerror.h"
 #include "sysemu/tpm.h"
-#include "hw/tpm/tpm_int.h"
 #include "qemu/thread.h"
+#include "qemu/main-loop.h"
+
+static void tpm_backend_request_completed_bh(void *opaque)
+{
+    TPMBackend *s = TPM_BACKEND(opaque);
+    TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
+
+    tic->request_completed(s->tpmif);
+}
 
 static void tpm_backend_worker_thread(gpointer data, gpointer user_data)
 {
     TPMBackend *s = TPM_BACKEND(user_data);
-    TPMBackendClass *k  = TPM_BACKEND_GET_CLASS(s);
+    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    assert(k->handle_request != NULL);
     k->handle_request(s, (TPMBackendCmd *)data);
+
+    qemu_bh_schedule(s->bh);
 }
 
 static void tpm_backend_thread_end(TPMBackend *s)
@@ -44,15 +53,22 @@ enum TpmType tpm_backend_get_type(TPMBackend *s)
     return k->type;
 }
 
-int tpm_backend_init(TPMBackend *s, TPMState *state)
+int tpm_backend_init(TPMBackend *s, TPMIf *tpmif, Error **errp)
 {
-    s->tpm_state = state;
+    if (s->tpmif) {
+        error_setg(errp, "TPM backend '%s' is already initialized", s->id);
+        return -1;
+    }
+
+    s->tpmif = tpmif;
+    object_ref(OBJECT(tpmif));
+
     s->had_startup_error = false;
 
     return 0;
 }
 
-int tpm_backend_startup_tpm(TPMBackend *s)
+int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize)
 {
     int res = 0;
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
@@ -63,7 +79,7 @@ int tpm_backend_startup_tpm(TPMBackend *s)
     s->thread_pool = g_thread_pool_new(tpm_backend_worker_thread, s, 1, TRUE,
                                        NULL);
 
-    res = k->startup_tpm ? k->startup_tpm(s) : 0;
+    res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0;
 
     s->had_startup_error = (res != 0);
 
@@ -97,8 +113,6 @@ void tpm_backend_cancel_cmd(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    assert(k->cancel_cmd);
-
     k->cancel_cmd(s);
 }
 
@@ -122,80 +136,44 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    assert(k->get_tpm_version);
-
     return k->get_tpm_version(s);
 }
 
-TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
+size_t tpm_backend_get_buffer_size(TPMBackend *s)
 {
-    TPMInfo *info = g_new0(TPMInfo, 1);
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    info->id = g_strdup(s->id);
-    info->model = s->fe_model;
-    if (k->get_tpm_options) {
-        info->options = k->get_tpm_options(s);
-    }
-
-    return info;
+    return k->get_buffer_size(s);
 }
 
-static bool tpm_backend_prop_get_opened(Object *obj, Error **errp)
-{
-    TPMBackend *s = TPM_BACKEND(obj);
-
-    return s->opened;
-}
-
-void tpm_backend_open(TPMBackend *s, Error **errp)
-{
-    object_property_set_bool(OBJECT(s), true, "opened", errp);
-}
-
-static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp)
+TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
 {
-    TPMBackend *s = TPM_BACKEND(obj);
+    TPMInfo *info = g_new0(TPMInfo, 1);
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
-    Error *local_err = NULL;
-
-    if (value == s->opened) {
-        return;
-    }
-
-    if (!value && s->opened) {
-        error_setg(errp, QERR_PERMISSION_DENIED);
-        return;
-    }
+    TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
 
-    if (k->opened) {
-        k->opened(s, &local_err);
-        if (local_err) {
-            error_propagate(errp, local_err);
-            return;
-        }
-    }
+    info->id = g_strdup(s->id);
+    info->model = tic->model;
+    info->options = k->get_tpm_options(s);
 
-    s->opened = true;
+    return info;
 }
 
 static void tpm_backend_instance_init(Object *obj)
 {
     TPMBackend *s = TPM_BACKEND(obj);
 
-    object_property_add_bool(obj, "opened",
-                             tpm_backend_prop_get_opened,
-                             tpm_backend_prop_set_opened,
-                             NULL);
-    s->fe_model = -1;
+    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 = {
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index ec10da7424..c17364655c 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -21,6 +21,7 @@
 #include "net/hub.h"
 #include "qapi/visitor.h"
 #include "chardev/char-fe.h"
+#include "sysemu/tpm_backend.h"
 #include "sysemu/iothread.h"
 
 static void get_pointer(Object *obj, Visitor *v, Property *prop,
@@ -236,6 +237,69 @@ const PropertyInfo qdev_prop_chr = {
     .release = release_chr,
 };
 
+/* --- character device --- */
+
+static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
+                    Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    TPMBackend **be = qdev_get_prop_ptr(dev, opaque);
+    char *p;
+
+    p = g_strdup(*be ? (*be)->id : "");
+    visit_type_str(v, name, &p, errp);
+    g_free(p);
+}
+
+static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
+                    Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Error *local_err = NULL;
+    Property *prop = opaque;
+    TPMBackend *s, **be = qdev_get_prop_ptr(dev, prop);
+    char *str;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_str(v, name, &str, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    s = qemu_find_tpm_be(str);
+    if (s == NULL) {
+        error_setg(errp, "Property '%s.%s' can't find value '%s'",
+                   object_get_typename(obj), prop->name, str);
+    } else if (tpm_backend_init(s, TPM_IF(obj), errp) == 0) {
+        *be = s; /* weak reference, avoid cyclic ref */
+    }
+    g_free(str);
+}
+
+static void release_tpm(Object *obj, const char *name, void *opaque)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    TPMBackend **be = qdev_get_prop_ptr(dev, prop);
+
+    if (*be) {
+        tpm_backend_reset(*be);
+    }
+}
+
+const PropertyInfo qdev_prop_tpm = {
+    .name  = "str",
+    .description = "ID of a tpm to use as a backend",
+    .get   = get_tpm,
+    .set   = set_tpm,
+    .release = release_tpm,
+};
+
 /* --- netdev device --- */
 static void get_netdev(Object *obj, Visitor *v, const char *name,
                        void *opaque, Error **errp)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 73519ab3ac..dd1420b410 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -208,7 +208,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
     }
 
     info->has_hpet = hpet_find();
-    info->tpm_version = tpm_get_version();
+    info->tpm_version = tpm_get_version(tpm_find());
     info->pvpanic_port = pvpanic_port();
     info->applesmc_io_base = applesmc_port();
 }
@@ -2038,7 +2038,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
         }
     }
 
-    if (misc->tpm_version != TPM_VERSION_UNSPEC) {
+    if (TPM_IS_TIS(tpm_find())) {
         aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE,
                    TPM_TIS_ADDR_SIZE, AML_READ_WRITE));
     }
@@ -2204,7 +2204,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
             /* Scan all PCI buses. Generate tables to support hotplug. */
             build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en);
 
-            if (misc->tpm_version != TPM_VERSION_UNSPEC) {
+            if (TPM_IS_TIS(tpm_find())) {
                 dev = aml_device("ISA.TPM");
                 aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31")));
                 aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
@@ -2281,8 +2281,12 @@ build_tpm2(GArray *table_data, BIOSLinker *linker)
     tpm2_ptr = acpi_data_push(table_data, sizeof *tpm2_ptr);
 
     tpm2_ptr->platform_class = cpu_to_le16(TPM2_ACPI_CLASS_CLIENT);
-    tpm2_ptr->control_area_address = cpu_to_le64(0);
-    tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
+    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 {
+        g_warn_if_reached();
+    }
 
     build_header(linker, table_data,
                  (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL);
diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
index e1a68104d6..3ae8bf6c5a 100644
--- a/hw/tpm/tpm_emulator.c
+++ b/hw/tpm/tpm_emulator.c
@@ -186,7 +186,6 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
 static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
 {
     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
-    TPMIfClass *tic = TPM_IF_GET_CLASS(tb->tpm_state);
     Error *err = NULL;
 
     DPRINTF("processing TPM command");
@@ -201,7 +200,6 @@ static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
         goto error;
     }
 
-    tic->request_completed(TPM_IF(tb->tpm_state));
     return;
 
 error:
@@ -234,13 +232,14 @@ static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
     switch (tpm_emu->tpm_version) {
     case TPM_VERSION_1_2:
         caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |
-               PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD;
+               PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD | PTM_CAP_STOP |
+               PTM_CAP_SET_BUFFERSIZE;
         tpm = "1.2";
         break;
     case TPM_VERSION_2_0:
         caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |
                PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED |
-               PTM_CAP_SET_DATAFD;
+               PTM_CAP_SET_DATAFD | PTM_CAP_STOP | PTM_CAP_SET_BUFFERSIZE;
         tpm = "2";
         break;
     case TPM_VERSION_UNSPEC:
@@ -257,12 +256,76 @@ static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
     return 0;
 }
 
-static int tpm_emulator_startup_tpm(TPMBackend *tb)
+static int tpm_emulator_stop_tpm(TPMBackend *tb)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+    ptm_res res;
+
+    if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, sizeof(res)) < 0) {
+        error_report("tpm-emulator: Could not stop TPM: %s",
+                     strerror(errno));
+        return -1;
+    }
+
+    res = be32_to_cpu(res);
+    if (res) {
+        error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x", res);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int tpm_emulator_set_buffer_size(TPMBackend *tb,
+                                        size_t wanted_size,
+                                        size_t *actual_size)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+    ptm_setbuffersize psbs;
+
+    if (tpm_emulator_stop_tpm(tb) < 0) {
+        return -1;
+    }
+
+    psbs.u.req.buffersize = cpu_to_be32(wanted_size);
+
+    if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs,
+                             sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) {
+        error_report("tpm-emulator: Could not set buffer size: %s",
+                     strerror(errno));
+        return -1;
+    }
+
+    psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result);
+    if (psbs.u.resp.tpm_result != 0) {
+        error_report("tpm-emulator: TPM result for set buffer size : 0x%x",
+                     psbs.u.resp.tpm_result);
+        return -1;
+    }
+
+    if (actual_size) {
+        *actual_size = be32_to_cpu(psbs.u.resp.buffersize);
+    }
+
+    DPRINTF("buffer size: %u, min: %u, max: %u\n",
+            be32_to_cpu(psbs.u.resp.buffersize),
+            be32_to_cpu(psbs.u.resp.minsize),
+            be32_to_cpu(psbs.u.resp.maxsize));
+
+    return 0;
+}
+
+static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
 {
     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
     ptm_init init;
     ptm_res res;
 
+    if (buffersize != 0 &&
+        tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) {
+        goto err_exit;
+    }
+
     DPRINTF("%s", __func__);
     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
                              sizeof(init)) < 0) {
@@ -340,6 +403,7 @@ static void tpm_emulator_cancel_cmd(TPMBackend *tb)
         return;
     }
 
+    /* FIXME: make the function non-blocking, or it may block a VCPU */
     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0,
                              sizeof(res)) < 0) {
         error_report("tpm-emulator: Could not cancel command: %s",
@@ -357,6 +421,17 @@ static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
     return tpm_emu->tpm_version;
 }
 
+static size_t tpm_emulator_get_buffer_size(TPMBackend *tb)
+{
+    size_t actual_size;
+
+    if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) {
+        return 4096;
+    }
+
+    return actual_size;
+}
+
 static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)
 {
     Error *err = NULL;
@@ -465,22 +540,16 @@ err:
     return -1;
 }
 
-static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)
+static TPMBackend *tpm_emulator_create(QemuOpts *opts)
 {
     TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR));
 
-    tb->id = g_strdup(id);
-
     if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) {
-        goto err_exit;
+        object_unref(OBJECT(tb));
+        return NULL;
     }
 
     return tb;
-
-err_exit:
-    object_unref(OBJECT(tb));
-
-    return NULL;
 }
 
 static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
@@ -563,6 +632,7 @@ static void tpm_emulator_class_init(ObjectClass *klass, void *data)
     tbc->get_tpm_established_flag = tpm_emulator_get_tpm_established_flag;
     tbc->reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag;
     tbc->get_tpm_version = tpm_emulator_get_tpm_version;
+    tbc->get_buffer_size = tpm_emulator_get_buffer_size;
     tbc->get_tpm_options = tpm_emulator_get_tpm_options;
 
     tbc->handle_request = tpm_emulator_handle_request;
diff --git a/hw/tpm/tpm_int.h b/hw/tpm/tpm_int.h
index 9c045b6691..abbca5191a 100644
--- a/hw/tpm/tpm_int.h
+++ b/hw/tpm/tpm_int.h
@@ -13,28 +13,8 @@
 #define TPM_TPM_INT_H
 
 #include "qemu/osdep.h"
-#include "qom/object.h"
 
-#define TYPE_TPM_IF "tpm-if"
-#define TPM_IF_CLASS(klass) \
-    OBJECT_CLASS_CHECK(TPMIfClass, (klass), TYPE_TPM_IF)
-#define TPM_IF_GET_CLASS(obj) \
-    OBJECT_GET_CLASS(TPMIfClass, (obj), TYPE_TPM_IF)
-#define TPM_IF(obj) \
-    INTERFACE_CHECK(TPMIf, (obj), TYPE_TPM_IF)
-
-typedef struct TPMIf {
-    Object parent_obj;
-} TPMIf;
-
-typedef struct TPMIfClass {
-    InterfaceClass parent_class;
-
-    /* run in thread pool by backend */
-    void (*request_completed)(TPMIf *obj);
-} TPMIfClass;
-
-#define TPM_STANDARD_CMDLINE_OPTS               \
+#define TPM_STANDARD_CMDLINE_OPTS \
     { \
         .name = "type", \
         .type = QEMU_OPT_STRING, \
@@ -65,11 +45,20 @@ struct tpm_resp_hdr {
 
 #define TPM_ORD_ContinueSelfTest  0x53
 #define TPM_ORD_GetTicks          0xf1
+#define TPM_ORD_GetCapability     0x65
 
+#define TPM_CAP_PROPERTY          0x05
+
+#define TPM_CAP_PROP_INPUT_BUFFER 0x124
 
 /* TPM2 defines */
 #define TPM2_ST_NO_SESSIONS       0x8001
 
 #define TPM2_CC_ReadClock         0x00000181
+#define TPM2_CC_GetCapability     0x0000017a
+
+#define TPM2_CAP_TPM_PROPERTIES   0x6
+
+#define TPM2_PT_MAX_COMMAND_SIZE  0x11e
 
 #endif /* TPM_TPM_INT_H */
diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
index 33564b11de..54c8d345ad 100644
--- a/hw/tpm/tpm_ioctl.h
+++ b/hw/tpm/tpm_ioctl.h
@@ -169,6 +169,28 @@ struct ptm_getconfig {
 #define PTM_CONFIG_FLAG_FILE_KEY        0x1
 #define PTM_CONFIG_FLAG_MIGRATION_KEY   0x2
 
+/*
+ * PTM_SET_BUFFERSIZE: Set the buffer size to be used by the TPM.
+ * A 0 on input queries for the current buffer size. Any other
+ * number will try to set the buffer size. The returned number is
+ * the buffer size that will be used, which can be larger than the
+ * requested one, if it was below the minimum, or smaller than the
+ * requested one, if it was above the maximum.
+ */
+struct ptm_setbuffersize {
+    union {
+        struct {
+            uint32_t buffersize; /* 0 to query for current buffer size */
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+            uint32_t buffersize; /* buffer size in use */
+            uint32_t minsize; /* min. supported buffer size */
+            uint32_t maxsize; /* max. supported buffer size */
+        } resp; /* response */
+    } u;
+};
+
 
 typedef uint64_t ptm_cap;
 typedef struct ptm_est ptm_est;
@@ -179,6 +201,7 @@ typedef struct ptm_init ptm_init;
 typedef struct ptm_getstate ptm_getstate;
 typedef struct ptm_setstate ptm_setstate;
 typedef struct ptm_getconfig ptm_getconfig;
+typedef struct ptm_setbuffersize ptm_setbuffersize;
 
 /* capability flags returned by PTM_GET_CAPABILITY */
 #define PTM_CAP_INIT               (1)
@@ -194,6 +217,7 @@ typedef struct ptm_getconfig ptm_getconfig;
 #define PTM_CAP_STOP               (1 << 10)
 #define PTM_CAP_GET_CONFIG         (1 << 11)
 #define PTM_CAP_SET_DATAFD         (1 << 12)
+#define PTM_CAP_SET_BUFFERSIZE     (1 << 13)
 
 enum {
     PTM_GET_CAPABILITY     = _IOR('P', 0, ptm_cap),
@@ -212,6 +236,7 @@ enum {
     PTM_STOP               = _IOR('P', 13, ptm_res),
     PTM_GET_CONFIG         = _IOR('P', 14, ptm_getconfig),
     PTM_SET_DATAFD         = _IOR('P', 15, ptm_res),
+    PTM_SET_BUFFERSIZE     = _IOWR('P', 16, ptm_setbuffersize),
 };
 
 /*
@@ -240,7 +265,8 @@ enum {
     CMD_SET_STATEBLOB,
     CMD_STOP,
     CMD_GET_CONFIG,
-    CMD_SET_DATAFD
+    CMD_SET_DATAFD,
+    CMD_SET_BUFFERSIZE,
 };
 
 #endif /* _TPM_IOCTL_H */
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index c440aff4b2..487aae2043 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -57,6 +57,7 @@ struct TPMPassthruState {
     int cancel_fd;
 
     TPMVersion tpm_version;
+    size_t tpm_buffersize;
 };
 
 typedef struct TPMPassthruState TPMPassthruState;
@@ -89,6 +90,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
     bool is_selftest;
     const struct tpm_resp_hdr *hdr;
 
+    /* FIXME: protect shared variables or use other sync mechanism */
     tpm_pt->tpm_op_canceled = false;
     tpm_pt->tpm_executing = true;
     *selftest_done = false;
@@ -139,14 +141,11 @@ err_exit:
 static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
-    TPMIfClass *tic = TPM_IF_GET_CLASS(tb->tpm_state);
 
     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);
-
-    tic->request_completed(TPM_IF(tb->tpm_state));
 }
 
 static void tpm_passthrough_reset(TPMBackend *tb)
@@ -181,12 +180,11 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
      */
     if (tpm_pt->tpm_executing) {
         if (tpm_pt->cancel_fd >= 0) {
+            tpm_pt->tpm_op_canceled = true;
             n = write(tpm_pt->cancel_fd, "-", 1);
             if (n != 1) {
                 error_report("Canceling TPM command failed: %s",
                              strerror(errno));
-            } else {
-                tpm_pt->tpm_op_canceled = true;
             }
         } else {
             error_report("Cannot cancel TPM command due to missing "
@@ -202,6 +200,19 @@ static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
     return tpm_pt->tpm_version;
 }
 
+static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
+    int ret;
+
+    ret = tpm_util_get_buffer_size(tpm_pt->tpm_fd, tpm_pt->tpm_version,
+                                   &tpm_pt->tpm_buffersize);
+    if (ret < 0) {
+        tpm_pt->tpm_buffersize = 4096;
+    }
+    return tpm_pt->tpm_buffersize;
+}
+
 /*
  * Unless path or file descriptor set has been provided by user,
  * determine the sysfs cancel file following kernel documentation
@@ -229,9 +240,7 @@ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
         if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
                      dev) < sizeof(path)) {
             fd = qemu_open(path, O_WRONLY);
-            if (fd >= 0) {
-                tpm_pt->options->cancel_path = g_strdup(path);
-            } else {
+            if (fd < 0) {
                 error_report("tpm_passthrough: Could not open TPM cancel "
                              "path %s : %s", path, strerror(errno));
             }
@@ -244,9 +253,9 @@ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
     return fd;
 }
 
-static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
+static int
+tpm_passthrough_handle_device_opts(TPMPassthruState *tpm_pt, QemuOpts *opts)
 {
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
     const char *value;
 
     value = qemu_opt_get(opts, "cancel-path");
@@ -266,52 +275,47 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
     if (tpm_pt->tpm_fd < 0) {
         error_report("Cannot access TPM device using '%s': %s",
                      tpm_pt->tpm_dev, strerror(errno));
-        goto err_free_parameters;
+        return -1;
     }
 
     if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) {
         error_report("'%s' is not a TPM device.",
                      tpm_pt->tpm_dev);
-        goto err_close_tpmdev;
+        return -1;
     }
 
-    return 0;
-
- err_close_tpmdev:
-    qemu_close(tpm_pt->tpm_fd);
-    tpm_pt->tpm_fd = -1;
-
- err_free_parameters:
-    qapi_free_TPMPassthroughOptions(tpm_pt->options);
-    tpm_pt->options = NULL;
-    tpm_pt->tpm_dev = NULL;
+    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
+    if (tpm_pt->cancel_fd < 0) {
+        return -1;
+    }
 
-    return 1;
+    return 0;
 }
 
-static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
+static TPMBackend *tpm_passthrough_create(QemuOpts *opts)
 {
     Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
-    TPMBackend *tb = TPM_BACKEND(obj);
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 
-    tb->id = g_strdup(id);
-
-    if (tpm_passthrough_handle_device_opts(opts, tb)) {
-        goto err_exit;
+    if (tpm_passthrough_handle_device_opts(TPM_PASSTHROUGH(obj), opts)) {
+        object_unref(obj);
+        return NULL;
     }
 
-    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
-    if (tpm_pt->cancel_fd < 0) {
-        goto err_exit;
-    }
+    return TPM_BACKEND(obj);
+}
 
-    return tb;
+static int tpm_passthrough_startup_tpm(TPMBackend *tb, size_t buffersize)
+{
+    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 
-err_exit:
-    object_unref(obj);
+    if (buffersize && buffersize < tpm_pt->tpm_buffersize) {
+        error_report("Requested buffer size of %zu is smaller than host TPM's "
+                     "fixed buffer size of %zu",
+                     buffersize, tpm_pt->tpm_buffersize);
+        return -1;
+    }
 
-    return NULL;
+    return 0;
 }
 
 static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb)
@@ -355,8 +359,12 @@ static void tpm_passthrough_inst_finalize(Object *obj)
 
     tpm_passthrough_cancel_cmd(TPM_BACKEND(obj));
 
-    qemu_close(tpm_pt->tpm_fd);
-    qemu_close(tpm_pt->cancel_fd);
+    if (tpm_pt->tpm_fd >= 0) {
+        qemu_close(tpm_pt->tpm_fd);
+    }
+    if (tpm_pt->cancel_fd >= 0) {
+        qemu_close(tpm_pt->cancel_fd);
+    }
     qapi_free_TPMPassthroughOptions(tpm_pt->options);
 }
 
@@ -368,12 +376,14 @@ static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
     tbc->opts = tpm_passthrough_cmdline_opts;
     tbc->desc = "Passthrough TPM backend driver";
     tbc->create = tpm_passthrough_create;
+    tbc->startup_tpm = tpm_passthrough_startup_tpm;
     tbc->reset = tpm_passthrough_reset;
     tbc->cancel_cmd = tpm_passthrough_cancel_cmd;
     tbc->get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag;
     tbc->reset_tpm_established_flag =
         tpm_passthrough_reset_tpm_established_flag;
     tbc->get_tpm_version = tpm_passthrough_get_tpm_version;
+    tbc->get_buffer_size = tpm_passthrough_get_buffer_size;
     tbc->get_tpm_options = tpm_passthrough_get_tpm_options;
     tbc->handle_request = tpm_passthrough_handle_request;
 }
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c
index 42d647d363..b8e811b086 100644
--- a/hw/tpm/tpm_tis.c
+++ b/hw/tpm/tpm_tis.c
@@ -24,17 +24,13 @@
 
 #include "qemu/osdep.h"
 #include "hw/isa/isa.h"
-#include "sysemu/tpm_backend.h"
-#include "tpm_int.h"
-#include "sysemu/block-backend.h"
-#include "exec/address-spaces.h"
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/pci/pci_ids.h"
 #include "qapi/error.h"
-#include "qemu-common.h"
-#include "qemu/main-loop.h"
+
 #include "hw/acpi/tpm.h"
+#include "hw/pci/pci_ids.h"
+#include "sysemu/tpm_backend.h"
+#include "tpm_int.h"
+#include "tpm_util.h"
 
 #define TPM_TIS_NUM_LOCALITIES      5     /* per spec */
 #define TPM_TIS_LOCALITY_SHIFT      12
@@ -72,11 +68,10 @@ typedef struct TPMLocality {
     TPMSizedBuffer r_buffer;
 } TPMLocality;
 
-struct TPMState {
+typedef struct TPMState {
     ISADevice busdev;
     MemoryRegion mmio;
 
-    QEMUBH *bh;
     uint32_t offset;
     uint8_t buf[TPM_TIS_BUFFER_MAX];
 
@@ -89,13 +84,13 @@ struct TPMState {
     qemu_irq irq;
     uint32_t irq_num;
 
-    uint8_t     locty_number;
     TPMBackendCmd cmd;
 
-    char *backend;
     TPMBackend *be_driver;
     TPMVersion be_tpm_version;
-};
+
+    size_t be_buffer_size;
+} TPMState;
 
 #define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS)
 
@@ -222,7 +217,7 @@ static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
 
 static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
 {
-    return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
+    return tpm_cmd_get_size(sb->buffer);
 }
 
 static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
@@ -411,10 +406,20 @@ static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
     tpm_tis_abort(s, locty);
 }
 
-static void tpm_tis_receive_bh(void *opaque)
+/*
+ * Callback from the TPM to indicate that the response was received.
+ */
+static void tpm_tis_request_completed(TPMIf *ti)
 {
-    TPMState *s = opaque;
+    TPMState *s = TPM(ti);
     uint8_t locty = s->cmd.locty;
+    uint8_t l;
+
+    if (s->cmd.selftest_done) {
+        for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
+            s->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE;
+        }
+    }
 
     tpm_tis_sts_set(&s->loc[locty],
                     TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
@@ -432,23 +437,6 @@ static void tpm_tis_receive_bh(void *opaque)
                       TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
 }
 
-static void tpm_tis_request_completed(TPMIf *ti)
-{
-    TPMState *s = TPM(ti);
-
-    bool is_selftest_done = s->cmd.selftest_done;
-    uint8_t locty = s->cmd.locty;
-    uint8_t l;
-
-    if (is_selftest_done) {
-        for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
-            s->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE;
-        }
-    }
-
-    qemu_bh_schedule(s->bh);
-}
-
 /*
  * Read a byte of response data
  */
@@ -986,15 +974,14 @@ static const MemoryRegionOps tpm_tis_memory_ops = {
     },
 };
 
-static int tpm_tis_do_startup_tpm(TPMState *s)
+static int tpm_tis_do_startup_tpm(TPMState *s, uint32_t buffersize)
 {
-    return tpm_backend_startup_tpm(s->be_driver);
+    return tpm_backend_startup_tpm(s->be_driver, buffersize);
 }
 
-static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb)
+static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb,
+                                   size_t wanted_size)
 {
-    size_t wanted_size = 4096; /* Linux tpm.c buffer size */
-
     if (sb->size != wanted_size) {
         sb->buffer = g_realloc(sb->buffer, wanted_size);
         sb->size = wanted_size;
@@ -1004,9 +991,9 @@ static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb)
 /*
  * Get the TPMVersion of the backend device being used
  */
-TPMVersion tpm_tis_get_tpm_version(Object *obj)
+static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti)
 {
-    TPMState *s = TPM(obj);
+    TPMState *s = TPM(ti);
 
     if (tpm_backend_had_startup_error(s->be_driver)) {
         return TPM_VERSION_UNSPEC;
@@ -1025,6 +1012,7 @@ static void tpm_tis_reset(DeviceState *dev)
     int c;
 
     s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
+    s->be_buffer_size = tpm_backend_get_buffer_size(s->be_driver);
 
     tpm_backend_reset(s->be_driver);
 
@@ -1051,12 +1039,12 @@ static void tpm_tis_reset(DeviceState *dev)
         s->loc[c].state = TPM_TIS_STATE_IDLE;
 
         s->loc[c].w_offset = 0;
-        tpm_tis_realloc_buffer(&s->loc[c].w_buffer);
+        tpm_tis_realloc_buffer(&s->loc[c].w_buffer, s->be_buffer_size);
         s->loc[c].r_offset = 0;
-        tpm_tis_realloc_buffer(&s->loc[c].r_buffer);
+        tpm_tis_realloc_buffer(&s->loc[c].r_buffer, s->be_buffer_size);
     }
 
-    tpm_tis_do_startup_tpm(s);
+    tpm_tis_do_startup_tpm(s, 0);
 }
 
 static const VMStateDescription vmstate_tpm_tis = {
@@ -1066,7 +1054,7 @@ static const VMStateDescription vmstate_tpm_tis = {
 
 static Property tpm_tis_properties[] = {
     DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ),
-    DEFINE_PROP_STRING("tpmdev", TPMState, backend),
+    DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -1074,29 +1062,21 @@ static void tpm_tis_realizefn(DeviceState *dev, Error **errp)
 {
     TPMState *s = TPM(dev);
 
-    s->be_driver = qemu_find_tpm(s->backend);
-    if (!s->be_driver) {
-        error_setg(errp, "tpm_tis: backend driver with id %s could not be "
-                   "found", s->backend);
+    if (!tpm_find()) {
+        error_setg(errp, "at most one TPM device is permitted");
         return;
     }
 
-    s->be_driver->fe_model = TPM_MODEL_TPM_TIS;
-
-    if (tpm_backend_init(s->be_driver, s)) {
-        error_setg(errp, "tpm_tis: backend driver with id %s could not be "
-                   "initialized", s->backend);
+    if (!s->be_driver) {
+        error_setg(errp, "'tpmdev' property is required");
         return;
     }
-
     if (s->irq_num > 15) {
-        error_setg(errp, "tpm_tis: IRQ %d for TPM TIS is outside valid range "
-                   "of 0 to 15", s->irq_num);
+        error_setg(errp, "IRQ %d is outside valid range of 0 to 15",
+                   s->irq_num);
         return;
     }
 
-    s->bh = qemu_bh_new(tpm_tis_receive_bh, s);
-
     isa_init_irq(&s->busdev, &s->irq, s->irq_num);
 
     memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)),
@@ -1121,6 +1101,8 @@ static void tpm_tis_class_init(ObjectClass *klass, void *data)
     dc->props = tpm_tis_properties;
     dc->reset = tpm_tis_reset;
     dc->vmsd  = &vmstate_tpm_tis;
+    tc->model = TPM_MODEL_TPM_TIS;
+    tc->get_version = tpm_tis_get_tpm_version;
     tc->request_completed = tpm_tis_request_completed;
 }
 
@@ -1139,7 +1121,6 @@ static const TypeInfo tpm_tis_info = {
 static void tpm_tis_register(void)
 {
     type_register_static(&tpm_tis_info);
-    tpm_register_model(TPM_MODEL_TPM_TIS);
 }
 
 type_init(tpm_tis_register)
diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c
index daf1faa63d..a317243a7e 100644
--- a/hw/tpm/tpm_util.c
+++ b/hw/tpm/tpm_util.c
@@ -20,10 +20,19 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/error-report.h"
 #include "tpm_util.h"
 #include "tpm_int.h"
 #include "exec/memory.h"
 
+#define DEBUG_TPM 0
+
+#define DPRINTF(fmt, ...) do { \
+    if (DEBUG_TPM) { \
+        fprintf(stderr, "tpm-util:"fmt"\n", ## __VA_ARGS__); \
+    } \
+} while (0)
+
 /*
  * Write an error message in the given output buffer.
  */
@@ -50,13 +59,13 @@ bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len)
 }
 
 /*
- * A basic test of a TPM device. We expect a well formatted response header
- * (error response is fine) within one second.
+ * Send request to a TPM device. We expect a response within one second.
  */
-static int tpm_util_test(int fd,
-                         unsigned char *request,
-                         size_t requestlen,
-                         uint16_t *return_tag)
+static int tpm_util_request(int fd,
+                            unsigned char *request,
+                            size_t requestlen,
+                            unsigned char *response,
+                            size_t responselen)
 {
     struct tpm_resp_hdr *resp;
     fd_set readfds;
@@ -65,7 +74,6 @@ static int tpm_util_test(int fd,
         .tv_sec = 1,
         .tv_usec = 0,
     };
-    unsigned char buf[1024];
 
     n = write(fd, request, requestlen);
     if (n < 0) {
@@ -84,17 +92,40 @@ static int tpm_util_test(int fd,
         return -errno;
     }
 
-    n = read(fd, &buf, sizeof(buf));
+    n = read(fd, response, responselen);
     if (n < sizeof(struct tpm_resp_hdr)) {
         return -EFAULT;
     }
 
-    resp = (struct tpm_resp_hdr *)buf;
+    resp = (struct tpm_resp_hdr *)response;
     /* check the header */
     if (be32_to_cpu(resp->len) != n) {
         return -EMSGSIZE;
     }
 
+    return 0;
+}
+
+/*
+ * A basic test of a TPM device. We expect a well formatted response header
+ * (error response is fine).
+ */
+static int tpm_util_test(int fd,
+                         unsigned char *request,
+                         size_t requestlen,
+                         uint16_t *return_tag)
+{
+    struct tpm_resp_hdr *resp;
+    unsigned char buf[1024];
+    ssize_t ret;
+
+    ret = tpm_util_request(fd, request, requestlen,
+                           buf, sizeof(buf));
+    if (ret < 0) {
+        return ret;
+    }
+
+    resp = (struct tpm_resp_hdr *)buf;
     *return_tag = be16_to_cpu(resp->tag);
 
     return 0;
@@ -151,3 +182,109 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
 
     return 1;
 }
+
+int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
+                             size_t *buffersize)
+{
+    unsigned char buf[1024];
+    int ret;
+
+    switch (tpm_version) {
+    case TPM_VERSION_1_2: {
+        const struct tpm_req_get_buffer_size {
+            struct tpm_req_hdr hdr;
+            uint32_t capability;
+            uint32_t len;
+            uint32_t subcap;
+        } QEMU_PACKED tpm_get_buffer_size = {
+            .hdr = {
+                .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
+                .len = cpu_to_be32(sizeof(tpm_get_buffer_size)),
+                .ordinal = cpu_to_be32(TPM_ORD_GetCapability),
+            },
+            .capability = cpu_to_be32(TPM_CAP_PROPERTY),
+            .len = cpu_to_be32(sizeof(uint32_t)),
+            .subcap = cpu_to_be32(TPM_CAP_PROP_INPUT_BUFFER),
+        };
+        struct tpm_resp_get_buffer_size {
+            struct tpm_resp_hdr hdr;
+            uint32_t len;
+            uint32_t buffersize;
+        } QEMU_PACKED *tpm_resp = (struct tpm_resp_get_buffer_size *)buf;
+
+        ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm_get_buffer_size,
+                               sizeof(tpm_get_buffer_size), buf, sizeof(buf));
+        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)) {
+            DPRINTF("tpm_resp->hdr.len = %u, expected = %zu\n",
+                    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));
+            error_report("tpm_util: Got unexpected response to "
+                         "TPM_GetCapability; errcode: 0x%x",
+                         be32_to_cpu(tpm_resp->hdr.errcode));
+            return -EFAULT;
+        }
+        *buffersize = be32_to_cpu(tpm_resp->buffersize);
+        break;
+    }
+    case TPM_VERSION_2_0: {
+        const struct tpm2_req_get_buffer_size {
+            struct tpm_req_hdr hdr;
+            uint32_t capability;
+            uint32_t property;
+            uint32_t count;
+        } QEMU_PACKED tpm2_get_buffer_size = {
+            .hdr = {
+                .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+                .len = cpu_to_be32(sizeof(tpm2_get_buffer_size)),
+                .ordinal = cpu_to_be32(TPM2_CC_GetCapability),
+            },
+            .capability = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES),
+            .property = cpu_to_be32(TPM2_PT_MAX_COMMAND_SIZE),
+            .count = cpu_to_be32(2), /* also get TPM2_PT_MAX_RESPONSE_SIZE */
+        };
+        struct tpm2_resp_get_buffer_size {
+            struct tpm_resp_hdr hdr;
+            uint8_t more;
+            uint32_t capability;
+            uint32_t count;
+            uint32_t property1;
+            uint32_t value1;
+            uint32_t property2;
+            uint32_t value2;
+        } QEMU_PACKED *tpm2_resp = (struct tpm2_resp_get_buffer_size *)buf;
+
+        ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm2_get_buffer_size,
+                               sizeof(tpm2_get_buffer_size), buf, sizeof(buf));
+        if (ret < 0) {
+            return ret;
+        }
+
+        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));
+            DPRINTF("tpm2_resp->len = %u, expected = %u\n",
+                    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));
+            return -EFAULT;
+        }
+        *buffersize = MAX(be32_to_cpu(tpm2_resp->value1),
+                          be32_to_cpu(tpm2_resp->value2));
+        break;
+    }
+    case TPM_VERSION_UNSPEC:
+        return -EFAULT;
+    }
+
+    DPRINTF("buffersize of device: %zu\n", *buffersize);
+
+    return 0;
+}
diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h
index 2f7c96146d..1c17e3913b 100644
--- a/hw/tpm/tpm_util.h
+++ b/hw/tpm/tpm_util.h
@@ -22,7 +22,8 @@
 #ifndef TPM_TPM_UTIL_H
 #define TPM_TPM_UTIL_H
 
-#include "sysemu/tpm_backend.h"
+#include "sysemu/tpm.h"
+#include "qemu/bswap.h"
 
 void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len);
 
@@ -30,4 +31,12 @@ 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 uint32_t tpm_cmd_get_size(const void *b)
+{
+    return be32_to_cpu(*(const uint32_t *)(b + 2));
+}
+
+int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
+                             size_t *buffersize);
+
 #endif /* TPM_TPM_UTIL_H */
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index e2321f1cc1..4d24cdf8d6 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -17,6 +17,7 @@ extern const PropertyInfo qdev_prop_int64;
 extern const PropertyInfo qdev_prop_size;
 extern const PropertyInfo qdev_prop_string;
 extern const PropertyInfo qdev_prop_chr;
+extern const PropertyInfo qdev_prop_tpm;
 extern const PropertyInfo qdev_prop_ptr;
 extern const PropertyInfo qdev_prop_macaddr;
 extern const PropertyInfo qdev_prop_on_off_auto;
@@ -186,6 +187,8 @@ extern const PropertyInfo qdev_prop_link;
 
 #define DEFINE_PROP_CHR(_n, _s, _f)             \
     DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharBackend)
+#define DEFINE_PROP_TPMBE(_n, _s, _f)                     \
+    DEFINE_PROP(_n, _s, _f, qdev_prop_tpm, TPMBackend *)
 #define DEFINE_PROP_STRING(_n, _s, _f)             \
     DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*)
 #define DEFINE_PROP_NETDEV(_n, _s, _f)             \
diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h
index d7a2bd8556..852e02687c 100644
--- a/include/sysemu/tpm.h
+++ b/include/sysemu/tpm.h
@@ -12,35 +12,59 @@
 #ifndef QEMU_TPM_H
 #define QEMU_TPM_H
 
-#include "qemu/option.h"
 #include "qom/object.h"
-
-typedef struct TPMState TPMState;
+#include "qapi-types.h"
 
 int tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
 int tpm_init(void);
 void tpm_cleanup(void);
 
-typedef enum  TPMVersion {
+typedef enum TPMVersion {
     TPM_VERSION_UNSPEC = 0,
     TPM_VERSION_1_2 = 1,
     TPM_VERSION_2_0 = 2,
 } TPMVersion;
 
-TPMVersion tpm_tis_get_tpm_version(Object *obj);
+#define TYPE_TPM_IF "tpm-if"
+#define TPM_IF_CLASS(klass)                                 \
+    OBJECT_CLASS_CHECK(TPMIfClass, (klass), TYPE_TPM_IF)
+#define TPM_IF_GET_CLASS(obj)                           \
+    OBJECT_GET_CLASS(TPMIfClass, (obj), TYPE_TPM_IF)
+#define TPM_IF(obj)                             \
+    INTERFACE_CHECK(TPMIf, (obj), TYPE_TPM_IF)
+
+typedef struct TPMIf {
+    Object parent_obj;
+} TPMIf;
+
+typedef struct TPMIfClass {
+    InterfaceClass parent_class;
+
+    enum TpmModel model;
+    void (*request_completed)(TPMIf *obj);
+    enum TPMVersion (*get_version)(TPMIf *obj);
+} TPMIfClass;
 
 #define TYPE_TPM_TIS                "tpm-tis"
 
-static inline TPMVersion tpm_get_version(void)
+#define TPM_IS_TIS(chr)                             \
+    object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS)
+
+/* returns NULL unless there is exactly one TPM device */
+static inline TPMIf *tpm_find(void)
 {
-#ifdef CONFIG_TPM
-    Object *obj = object_resolve_path_type("", TYPE_TPM_TIS, NULL);
+    Object *obj = object_resolve_path_type("", TYPE_TPM_IF, NULL);
 
-    if (obj) {
-        return tpm_tis_get_tpm_version(obj);
+    return TPM_IF(obj);
+}
+
+static inline TPMVersion tpm_get_version(TPMIf *ti)
+{
+    if (!ti) {
+        return TPM_VERSION_UNSPEC;
     }
-#endif
-    return TPM_VERSION_UNSPEC;
+
+    return TPM_IF_GET_CLASS(ti)->get_version(ti);
 }
 
 #endif /* QEMU_TPM_H */
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 03ea5a3400..0d6c994a62 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -43,14 +43,14 @@ struct TPMBackend {
     Object parent;
 
     /*< protected >*/
+    TPMIf *tpmif;
     bool opened;
-    TPMState *tpm_state;
     GThreadPool *thread_pool;
     bool had_startup_error;
+    QEMUBH *bh;
 
     /* <public> */
     char *id;
-    enum TpmModel fe_model;
 
     QLIST_ENTRY(TPMBackend) list;
 };
@@ -63,24 +63,27 @@ struct TPMBackendClass {
     /* get a descriptive text of the backend to display to the user */
     const char *desc;
 
-    TPMBackend *(*create)(QemuOpts *opts, const char *id);
+    TPMBackend *(*create)(QemuOpts *opts);
 
-    /* start up the TPM on the backend */
-    int (*startup_tpm)(TPMBackend *t);
+    /* start up the TPM on the backend - optional */
+    int (*startup_tpm)(TPMBackend *t, size_t buffersize);
 
+    /* optional */
     void (*reset)(TPMBackend *t);
 
     void (*cancel_cmd)(TPMBackend *t);
 
+    /* optional */
     bool (*get_tpm_established_flag)(TPMBackend *t);
 
+    /* optional */
     int (*reset_tpm_established_flag)(TPMBackend *t, uint8_t locty);
 
     TPMVersion (*get_tpm_version)(TPMBackend *t);
 
-    TpmTypeOptions *(*get_tpm_options)(TPMBackend *t);
+    size_t (*get_buffer_size)(TPMBackend *t);
 
-    void (*opened)(TPMBackend *s, Error **errp);
+    TpmTypeOptions *(*get_tpm_options)(TPMBackend *t);
 
     void (*handle_request)(TPMBackend *s, TPMBackendCmd *cmd);
 };
@@ -96,22 +99,25 @@ enum TpmType tpm_backend_get_type(TPMBackend *s);
 /**
  * tpm_backend_init:
  * @s: the backend to initialized
- * @state: TPMState
+ * @tpmif: TPM interface
  * @datacb: callback for sending data to frontend
+ * @errp: a pointer to return the #Error object if an error occurs.
  *
  * Initialize the backend with the given variables.
  *
  * Returns 0 on success.
  */
-int tpm_backend_init(TPMBackend *s, TPMState *state);
+int tpm_backend_init(TPMBackend *s, TPMIf *tpmif, Error **errp);
 
 /**
  * tpm_backend_startup_tpm:
  * @s: the backend whose TPM support is to be started
+ * @buffersize: the buffer size the TPM is supposed to use,
+ *              0 to leave it as-is
  *
  * Returns 0 on success.
  */
-int tpm_backend_startup_tpm(TPMBackend *s);
+int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize);
 
 /**
  * tpm_backend_had_startup_error:
@@ -171,16 +177,6 @@ bool tpm_backend_get_tpm_established_flag(TPMBackend *s);
 int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty);
 
 /**
- * tpm_backend_open:
- * @s: the backend to open
- * @errp: a pointer to return the #Error object if an error occurs.
- *
- * This function will open the backend if it is not already open.  Calling this
- * function on an already opened backend will not result in an error.
- */
-void tpm_backend_open(TPMBackend *s, Error **errp);
-
-/**
  * tpm_backend_get_tpm_version:
  * @s: the backend to call into
  *
@@ -191,6 +187,16 @@ void tpm_backend_open(TPMBackend *s, Error **errp);
 TPMVersion tpm_backend_get_tpm_version(TPMBackend *s);
 
 /**
+ * tpm_backend_get_buffer_size:
+ * @s: the backend to call into
+ *
+ * Get the TPM's buffer size.
+ *
+ * Returns buffer size.
+ */
+size_t tpm_backend_get_buffer_size(TPMBackend *s);
+
+/**
  * tpm_backend_query_tpm:
  * @s: the backend
  *
@@ -200,8 +206,6 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s);
  */
 TPMInfo *tpm_backend_query_tpm(TPMBackend *s);
 
-TPMBackend *qemu_find_tpm(const char *id);
-
-void tpm_register_model(enum TpmModel model);
+TPMBackend *qemu_find_tpm_be(const char *id);
 
 #endif
diff --git a/tpm.c b/tpm.c
index ab5d29e91e..61a434185a 100644
--- a/tpm.c
+++ b/tpm.c
@@ -23,13 +23,6 @@
 static QLIST_HEAD(, TPMBackend) tpm_backends =
     QLIST_HEAD_INITIALIZER(tpm_backends);
 
-static bool tpm_models[TPM_MODEL__MAX];
-
-void tpm_register_model(enum TpmModel model)
-{
-    tpm_models[model] = true;
-}
-
 static const TPMBackendClass *
 tpm_be_find_by_type(enum TpmType type)
 {
@@ -69,7 +62,7 @@ static void tpm_display_backend_drivers(void)
 /*
  * Find the TPM with the given Id
  */
-TPMBackend *qemu_find_tpm(const char *id)
+TPMBackend *qemu_find_tpm_be(const char *id)
 {
     TPMBackend *drv;
 
@@ -127,17 +120,12 @@ static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp)
         return 1;
     }
 
-    drv = be->create(opts, id);
+    drv = be->create(opts);
     if (!drv) {
         return 1;
     }
 
-    tpm_backend_open(drv, &local_err);
-    if (local_err) {
-        error_report_err(local_err);
-        return 1;
-    }
-
+    drv->id = g_strdup(id);
     QLIST_INSERT_HEAD(&tpm_backends, drv, list);
 
     return 0;
@@ -200,9 +188,10 @@ TPMInfoList *qmp_query_tpm(Error **errp)
     TPMInfoList *info, *head = NULL, *cur_item = NULL;
 
     QLIST_FOREACH(drv, &tpm_backends, list) {
-        if (!tpm_models[drv->fe_model]) {
+        if (!drv->tpmif) {
             continue;
         }
+
         info = g_new0(TPMInfoList, 1);
         info->value = tpm_backend_query_tpm(drv);
 
@@ -240,18 +229,16 @@ TpmTypeList *qmp_query_tpm_types(Error **errp)
 
     return head;
 }
-
 TpmModelList *qmp_query_tpm_models(Error **errp)
 {
-    unsigned int i = 0;
     TpmModelList *head = NULL, *prev = NULL, *cur_item;
+    GSList *e, *l = object_class_get_list(TYPE_TPM_IF, false);
+
+    for (e = l; e; e = e->next) {
+        TPMIfClass *c = TPM_IF_CLASS(e->data);
 
-    for (i = 0; i < TPM_MODEL__MAX; i++) {
-        if (!tpm_models[i]) {
-            continue;
-        }
         cur_item = g_new0(TpmModelList, 1);
-        cur_item->value = i;
+        cur_item->value = c->model;
 
         if (prev) {
             prev->next = cur_item;
@@ -261,6 +248,7 @@ TpmModelList *qmp_query_tpm_models(Error **errp)
         }
         prev = cur_item;
     }
+    g_slist_free(l);
 
     return head;
 }