qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 1/5] Provide support for the CUSE TPM


From: Stefan Berger
Subject: [Qemu-devel] [PATCH 1/5] Provide support for the CUSE TPM
Date: Wed, 15 Apr 2015 18:38:44 -0400

Rather than integrating TPM functionality into QEMU directly
using the TPM emulation of libtpms, we now integrate an external
emulated TPM device. This device is expected to implement a Linux
CUSE interface (CUSE = character device in userspace).

QEMU talks to the CUSE TPM using much functionality of the
passthrough driver. For example, the TPM commands and responses
are sent to the CUSE TPM using the read()/write() interface.
However, some out-of-band control needs to be done using the CUSE
TPM's ioctl's. The CUSE TPM currently defines and implements 14
different ioctls for controlling certain life-cycle aspects of
the emulated TPM. The ioctls can be regarded as a replacement for
direct function calls to a TPM emulator if the TPM were to be
directly integrated into QEMU.

One  of the ioctl's allows to get a bitmask of supported capabilities.
Each returned bit indicates which capabilties have been implemented.
An include file defining the various ioctls is added to QEMU.

The CUSE TPM and associated tools can be found here:

https://github.com/stefanberger/swtpm


To use the external CUSE TPM, the CUSE TPM should be started as follows:

/usr/bin/swtpm_cuse -n vtpm-test

QEMU can then be started using the following parameters:

qemu-system-x86_64 \
        [...] \
        -tpmdev cuse-tpm,id=tpm0,cancel-path=/dev/null,path=/dev/vtpm-test \
        -device tpm-tis,id=tpm0,tpmdev=tpm0 \
        [...]


Signed-off-by: Stefan Berger <address@hidden>
Cc: Eric Blake <address@hidden>
---
 hmp.c                    |   6 ++
 hw/tpm/tpm_int.h         |   1 +
 hw/tpm/tpm_ioctl.h       | 178 +++++++++++++++++++++++++++++++
 hw/tpm/tpm_passthrough.c | 271 +++++++++++++++++++++++++++++++++++++++++++++--
 qapi-schema.json         |  17 ++-
 qemu-options.hx          |  21 +++-
 qmp-commands.hx          |   2 +-
 tpm.c                    |  11 +-
 8 files changed, 490 insertions(+), 17 deletions(-)
 create mode 100644 hw/tpm/tpm_ioctl.h

diff --git a/hmp.c b/hmp.c
index f31ae27..1b1ce8e 100644
--- a/hmp.c
+++ b/hmp.c
@@ -813,6 +813,12 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
                            tpo->has_cancel_path ? ",cancel-path=" : "",
                            tpo->has_cancel_path ? tpo->cancel_path : "");
             break;
+        case TPM_TYPE_OPTIONS_KIND_CUSE_TPM:
+            tpo = ti->options->passthrough;
+            monitor_printf(mon, "%s%s",
+                           tpo->has_path ? ",path=" : "",
+                           tpo->has_path ? tpo->path : "");
+            break;
         case TPM_TYPE_OPTIONS_KIND_MAX:
             break;
         }
diff --git a/hw/tpm/tpm_int.h b/hw/tpm/tpm_int.h
index f2f285b..6b2c9c9 100644
--- a/hw/tpm/tpm_int.h
+++ b/hw/tpm/tpm_int.h
@@ -61,6 +61,7 @@ struct tpm_resp_hdr {
 #define TPM_TAG_RSP_AUTH1_COMMAND 0xc5
 #define TPM_TAG_RSP_AUTH2_COMMAND 0xc6
 
+#define TPM_SUCCESS               0
 #define TPM_FAIL                  9
 
 #define TPM_ORD_ContinueSelfTest  0x53
diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
new file mode 100644
index 0000000..d36e702
--- /dev/null
+++ b/hw/tpm/tpm_ioctl.h
@@ -0,0 +1,178 @@
+/*
+ * tpm_ioctl.h
+ *
+ * This file is licensed under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <stdint.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+/*
+ * Every response from a command involving a TPM command execution must hold
+ * the ptmres_t as the first element.
+ * ptmres_t corresponds to the error code of a command executed by the TPM.
+ */
+
+typedef uint32_t ptmres_t;
+
+/* PTM_GET_TPMESTABLISHED */
+struct ptmest {
+    ptmres_t tpm_result;
+    unsigned char bit; /* TPM established bit */
+};
+
+/* PTM_RESET_PTMESTABLIHSED: reset establishment bit */
+struct ptmreset_est {
+    union {
+        struct {
+            uint8_t loc; /* locality to use */
+        } req;
+        struct {
+            ptmres_t tpm_result;
+        } resp;
+    } u;
+};
+
+/* PTM_INIT */
+struct ptminit {
+    union {
+        struct {
+            uint32_t init_flags; /* see definitions below */
+        } req;
+        struct {
+            ptmres_t tpm_result;
+        } resp;
+    } u;
+};
+
+/* above init_flags */
+#define INIT_FLAG_DELETE_VOLATILE (1 << 0)
+    /* delete volatile state file after reading it */
+
+/* PTM_SET_LOCALITY */
+struct ptmloc {
+    union {
+        struct {
+            uint8_t loc; /* locality to set */
+        } req;
+        struct {
+            ptmres_t tpm_result;
+        } resp;
+    } u;
+};
+
+/* PTM_HASH_DATA: hash given data */
+struct ptmhdata {
+    union {
+        struct {
+            uint32_t length;
+            uint8_t data[4096];
+        } req;
+        struct {
+            ptmres_t tpm_result;
+        } resp;
+    } u;
+};
+
+/*
+ * size of the TPM state blob to transfer; x86_64 can handle 8k, ppc64le only 
~7k
+ * keep the response below a 4k page size
+ */
+#define STATE_BLOB_SIZE (3 * 1024)
+
+/*
+ * Data structure to get state blobs from the TPM. If the size of the
+ * blob exceeds the STATE_BLOB_SIZE, multiple reads with
+ * adjusted offset are necessary. The last packet is indicated by
+ * the length being smaller than the STATE_BLOB_SIZE.
+ */
+struct ptm_getstate {
+    union {
+        struct {
+            uint32_t state_flags; /* may be: STATE_FLAG_DECRYPTED */
+            uint32_t tpm_number;  /* always set to zero */
+            uint8_t type;         /* which blob to pull */
+            uint32_t offset;      /* offset from where to read */
+        } req;
+        struct {
+            ptmres_t tpm_result;
+            uint32_t state_flags; /* may be: STATE_FLAG_ENCRYPTED */
+            uint32_t length;
+            uint8_t  data[STATE_BLOB_SIZE];
+        } resp;
+    } u;
+};
+
+/* TPM state blob types */
+#define PTM_BLOB_TYPE_PERMANENT  1
+#define PTM_BLOB_TYPE_VOLATILE   2
+#define PTM_BLOB_TYPE_SAVESTATE  3
+
+/* state_flags above : */
+#define STATE_FLAG_DECRYPTED     1 /* on input:  get decrypted state */
+#define STATE_FLAG_ENCRYPTED     2 /* on output: state is encrytped */
+
+/*
+ * Data structure to set state blobs in the TPM. If the size of the
+ * blob exceeds the STATE_BLOB_SIZE, multiple 'writes' are necessary.
+ * The last packet is indicated by the length being smaller than the
+ * STATE_BLOB_SIZE.
+ */
+struct ptm_setstate {
+    union {
+        struct {
+            uint32_t state_flags; /* may be STATE_FLAG_ENCRYPTED */
+            uint32_t tpm_number;  /* always set to 0 */
+            uint8_t type;         /* which blob to set */
+            uint32_t length;
+            uint8_t data[STATE_BLOB_SIZE];
+        } req;
+        struct {
+            ptmres_t tpm_result;
+        } resp;
+    } u;
+};
+
+
+typedef uint64_t ptmcap_t;
+typedef struct ptmest  ptmest_t;
+typedef struct ptmreset_est ptmreset_est_t;
+typedef struct ptmloc  ptmloc_t;
+typedef struct ptmhdata ptmhdata_t;
+typedef struct ptminit ptminit_t;
+typedef struct ptm_getstate ptm_getstate_t;
+typedef struct ptm_setstate ptm_setstate_t;
+
+/* capability flags returned by PTM_GET_CAPABILITY */
+#define PTM_CAP_INIT               (1)
+#define PTM_CAP_SHUTDOWN           (1<<1)
+#define PTM_CAP_GET_TPMESTABLISHED (1<<2)
+#define PTM_CAP_SET_LOCALITY       (1<<3)
+#define PTM_CAP_HASHING            (1<<4)
+#define PTM_CAP_CANCEL_TPM_CMD     (1<<5)
+#define PTM_CAP_STORE_VOLATILE     (1<<6)
+#define PTM_CAP_RESET_TPMESTABLISHED (1<<7)
+#define PTM_CAP_GET_STATEBLOB      (1<<8)
+#define PTM_CAP_SET_STATEBLOB      (1<<9)
+#define PTM_CAP_STOP               (1<<10)
+
+enum {
+    PTM_GET_CAPABILITY     = _IOR('P', 0, ptmcap_t),
+    PTM_INIT               = _IOWR('P', 1, ptminit_t),
+    PTM_SHUTDOWN           = _IOR('P', 2, ptmres_t),
+    PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptmest_t),
+    PTM_SET_LOCALITY       = _IOWR('P', 4, ptmloc_t),
+    PTM_HASH_START         = _IOR('P', 5, ptmres_t),
+    PTM_HASH_DATA          = _IOWR('P', 6, ptmhdata_t),
+    PTM_HASH_END           = _IOR('P', 7, ptmres_t),
+    PTM_CANCEL_TPM_CMD     = _IOR('P', 8, ptmres_t),
+    PTM_STORE_VOLATILE     = _IOR('P', 9, ptmres_t),
+    PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptmreset_est_t),
+    PTM_GET_STATEBLOB      = _IOWR('P', 11, ptm_getstate_t),
+    PTM_SET_STATEBLOB      = _IOWR('P', 12, ptm_setstate_t),
+    PTM_STOP               = _IOR('P', 13, ptmres_t),
+};
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index 8d8523a..26abfc9 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -34,6 +34,7 @@
 #include "sysemu/tpm_backend_int.h"
 #include "tpm_tis.h"
 #include "tpm_util.h"
+#include "tpm_ioctl.h"
 
 #define DEBUG_TPM 0
 
@@ -46,6 +47,7 @@
 #define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
 #define TPM_PASSTHROUGH(obj) \
     OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
+#define TYPE_TPM_CUSE "tpm-cuse"
 
 static const TPMDriverOps tpm_passthrough_driver;
 
@@ -72,12 +74,18 @@ struct TPMPassthruState {
     bool had_startup_error;
 
     TPMVersion tpm_version;
+    ptmcap_t cuse_cap; /* capabilties of the CUSE TPM */
+    uint8_t cur_locty_number; /* last set locality */
 };
 
 typedef struct TPMPassthruState TPMPassthruState;
 
 #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
 
+#define TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt) (tpm_pt->cuse_cap != 0)
+
+#define TPM_CUSE_IMPLEMENTS(tpm_tr, cap) ((tpm_pt->cuse_cap & cap) == cap)
+
 /* functions */
 
 static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
@@ -124,7 +132,30 @@ static bool tpm_passthrough_is_selftest(const uint8_t *in, 
uint32_t in_len)
     return false;
 }
 
+static int tpm_passthrough_set_locality(TPMPassthruState *tpm_pt,
+                                        uint8_t locty_number)
+{
+    int n;
+    ptmloc_t loc;
+
+    if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) {
+        if (tpm_pt->cur_locty_number != locty_number) {
+            loc.u.req.loc = locty_number;
+            n = ioctl(tpm_pt->tpm_fd, PTM_SET_LOCALITY, &loc);
+            if (n < 0) {
+                error_report("tpm_cuse: could not set locality on "
+                             "CUSE TPM: %s (%i)",
+                             strerror(errno), errno);
+                return -1;
+            }
+            tpm_pt->cur_locty_number = locty_number;
+        }
+    }
+    return 0;
+}
+
 static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
+                                        uint8_t locality_number,
                                         const uint8_t *in, uint32_t in_len,
                                         uint8_t *out, uint32_t out_len,
                                         bool *selftest_done)
@@ -133,6 +164,11 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState 
*tpm_pt,
     bool is_selftest;
     const struct tpm_resp_hdr *hdr;
 
+    ret = tpm_passthrough_set_locality(tpm_pt, locality_number);
+    if (ret < 0) {
+        goto err_exit;
+    }
+
     tpm_pt->tpm_op_canceled = false;
     tpm_pt->tpm_executing = true;
     *selftest_done = false;
@@ -183,10 +219,12 @@ err_exit:
 }
 
 static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
+                                         uint8_t locality_number,
                                          const TPMLocality *locty_data,
                                          bool *selftest_done)
 {
     return tpm_passthrough_unix_tx_bufs(tpm_pt,
+                                        locality_number,
                                         locty_data->w_buffer.buffer,
                                         locty_data->w_offset,
                                         locty_data->r_buffer.buffer,
@@ -207,6 +245,7 @@ static void tpm_passthrough_worker_thread(gpointer data,
     switch (cmd) {
     case TPM_BACKEND_CMD_PROCESS_CMD:
         tpm_passthrough_unix_transfer(tpm_pt,
+                                      thr_parms->tpm_state->locty_number,
                                       thr_parms->tpm_state->locty_data,
                                       &selftest_done);
 
@@ -223,6 +262,94 @@ static void tpm_passthrough_worker_thread(gpointer data,
 }
 
 /*
+ * Gracefully shut down the external CUSE TPM
+ */
+static void tpm_passthrough_shutdown(TPMPassthruState *tpm_pt)
+{
+    int n;
+    ptmres_t res;
+
+    if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) {
+        n = ioctl(tpm_pt->tpm_fd, PTM_SHUTDOWN, &res);
+        if (n < 0) {
+            error_report("tpm_cuse: Could not cleanly shut down "
+                         "the CUSE TPM: %s (%i)",
+                         strerror(errno), errno);
+        }
+    }
+}
+
+/*
+ * Probe for the CUSE TPM by sending an ioctl() requesting its
+ * capability flags.
+ */
+static int tpm_passthrough_cuse_probe(TPMPassthruState *tpm_pt)
+{
+    int rc = 0;
+    int n;
+    ptmcap_t caps = 0;
+    const char *tpm = NULL;
+
+    n = ioctl(tpm_pt->tpm_fd, PTM_GET_CAPABILITY, &tpm_pt->cuse_cap);
+    if (n < 0) {
+        error_report("Error: CUSE TPM was requested, but probing failed.");
+        rc = -1;
+        goto err_exit;
+    }
+
+    /* check for min. required capabilities */
+    switch (tpm_pt->tpm_version) {
+    case TPM_VERSION_1_2:
+        caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |
+               PTM_CAP_SET_LOCALITY;
+        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;
+        tpm = "2";
+        break;
+    case TPM_VERSION_UNSPEC:
+        error_report("tpm_cuse: %s: TPM version has not been set.",
+                     __func__);
+        return -1;
+    }
+
+    if (!TPM_CUSE_IMPLEMENTS(tpm_pt, caps)) {
+        error_report("tpm_cuse: TPM does not implement minimum set of required 
"
+                     "capabilities for TPM %s (0x%x).", tpm, (int)caps);
+        rc = -1;
+    }
+
+err_exit:
+    return rc;
+}
+
+/*
+ * Initialize the external CUSE TPM
+ */
+static int tpm_passthrough_cuse_init(TPMPassthruState *tpm_pt)
+{
+    int rc = 0;
+    int n;
+    ptminit_t init = {
+        .u.req.init_flags = INIT_FLAG_DELETE_VOLATILE,
+    };
+
+    if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) {
+        n = ioctl(tpm_pt->tpm_fd, PTM_INIT, &init);
+        if (n < 0) {
+            error_report("tpm_cuse: Detected CUSE TPM but could not "
+                         "send INIT: %s (%i)",
+                         strerror(errno), errno);
+            rc = -1;
+        }
+    }
+
+    return rc;
+}
+
+/*
  * Start the TPM (thread). If it had been started before, then terminate
  * and start it again.
  */
@@ -237,6 +364,8 @@ static int tpm_passthrough_startup_tpm(TPMBackend *tb)
                               tpm_passthrough_worker_thread,
                               &tpm_pt->tpm_thread_params);
 
+    tpm_passthrough_cuse_init(tpm_pt);
+
     return 0;
 }
 
@@ -267,14 +396,46 @@ static int tpm_passthrough_init(TPMBackend *tb, TPMState 
*s,
 
 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
 {
+    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
+    ptmest_t est;
+    int n;
+
+    if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) {
+        n = ioctl(tpm_pt->tpm_fd, PTM_GET_TPMESTABLISHED, &est);
+        if (n < 0) {
+            error_report("tpm_cuse: Could not get the TPM established "
+                         "flag from the CUSE TPM: %s (%i)",
+                         strerror(errno), errno);
+            return false;
+        }
+        return (est.bit != 0);
+    }
     return false;
 }
 
 static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
                                                       uint8_t locty)
 {
+    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
+    int n;
+    int rc = 0;
+    ptmreset_est_t ptmreset_est;
+
     /* only a TPM 2.0 will support this */
-    return 0;
+    if (tpm_pt->tpm_version == TPM_VERSION_2_0) {
+        if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) {
+            ptmreset_est.u.req.loc = tpm_pt->cur_locty_number;
+
+            n = ioctl(tpm_pt->tpm_fd, PTM_RESET_TPMESTABLISHED, &ptmreset_est);
+            if (n < 0) {
+                error_report("tpm_cuse: Could not reset the establishment bit "
+                             "failed: %s (%i)",
+                             strerror(errno), errno);
+                rc = -1;
+            }
+        }
+    }
+    return rc;
 }
 
 static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
@@ -306,6 +467,8 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
     int n;
+    ptmres_t res;
+    static int error_printed;
 
     /*
      * As of Linux 3.7 the tpm_tis driver does not properly cancel
@@ -314,17 +477,36 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
      * command, e.g., a command executed on the host.
      */
     if (tpm_pt->tpm_executing) {
-        if (tpm_pt->cancel_fd >= 0) {
-            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;
+        if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) {
+            if (TPM_CUSE_IMPLEMENTS(tpm_pt, PTM_CAP_CANCEL_TPM_CMD)) {
+                n = ioctl(tpm_pt->tpm_fd, PTM_CANCEL_TPM_CMD, &res);
+                if (n < 0) {
+                    error_report("tpm_cuse: Could not cancel command on "
+                                 "CUSE TPM: %s (%i)",
+                                 strerror(errno), errno);
+                } else if (res != TPM_SUCCESS) {
+                    if (!error_printed) {
+                        error_report("TPM error code from command "
+                                     "cancellation of CUSE TPM: 0x%x", res);
+                        error_printed = true;
+                    }
+                } else {
+                    tpm_pt->tpm_op_canceled = true;
+                }
             }
         } else {
-            error_report("Cannot cancel TPM command due to missing "
-                         "TPM sysfs cancel entry");
+            if (tpm_pt->cancel_fd >= 0) {
+                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 "
+                             "TPM sysfs cancel entry");
+            }
         }
     }
 }
@@ -354,6 +536,11 @@ static int tpm_passthrough_open_sysfs_cancel(TPMBackend 
*tb)
     char *dev;
     char path[PATH_MAX];
 
+    if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) {
+        /* not needed, but so we have a fd */
+        return qemu_open("/dev/null", O_WRONLY);
+    }
+
     if (tb->cancel_path) {
         fd = qemu_open(tb->cancel_path, O_WRONLY);
         if (fd < 0) {
@@ -388,12 +575,22 @@ static int tpm_passthrough_handle_device_opts(QemuOpts 
*opts, TPMBackend *tb)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
     const char *value;
+    bool have_cuse = false;
+
+    value = qemu_opt_get(opts, "type");
+    if (value != NULL && !strcmp("cuse-tpm", value)) {
+        have_cuse = true;
+    }
 
     value = qemu_opt_get(opts, "cancel-path");
     tb->cancel_path = g_strdup(value);
 
     value = qemu_opt_get(opts, "path");
     if (!value) {
+        if (have_cuse) {
+            error_report("Missing path to access CUSE TPM");
+            goto err_free_parameters;
+        }
         value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
     }
 
@@ -408,15 +605,26 @@ static int tpm_passthrough_handle_device_opts(QemuOpts 
*opts, TPMBackend *tb)
         goto err_free_parameters;
     }
 
+    tpm_pt->cur_locty_number = ~0;
+
     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;
     }
 
+    if (have_cuse) {
+        if (tpm_passthrough_cuse_probe(tpm_pt)) {
+            goto err_close_tpmdev;
+        }
+    }
+
+
     return 0;
 
  err_close_tpmdev:
+    tpm_passthrough_shutdown(tpm_pt);
+
     qemu_close(tpm_pt->tpm_fd);
     tpm_pt->tpm_fd = -1;
 
@@ -467,6 +675,8 @@ static void tpm_passthrough_destroy(TPMBackend *tb)
 
     tpm_backend_thread_end(&tpm_pt->tbt);
 
+    tpm_passthrough_shutdown(tpm_pt);
+
     qemu_close(tpm_pt->tpm_fd);
     qemu_close(tpm_pt->cancel_fd);
 
@@ -540,3 +750,44 @@ static void tpm_passthrough_register(void)
 }
 
 type_init(tpm_passthrough_register)
+
+/* CUSE TPM */
+static const char *tpm_passthrough_cuse_create_desc(void)
+{
+    return "CUSE TPM backend driver";
+}
+
+static const TPMDriverOps tpm_cuse_driver = {
+    .type                     = TPM_TYPE_CUSE_TPM,
+    .opts                     = tpm_passthrough_cmdline_opts,
+    .desc                     = tpm_passthrough_cuse_create_desc,
+    .create                   = tpm_passthrough_create,
+    .destroy                  = tpm_passthrough_destroy,
+    .init                     = tpm_passthrough_init,
+    .startup_tpm              = tpm_passthrough_startup_tpm,
+    .realloc_buffer           = tpm_passthrough_realloc_buffer,
+    .reset                    = tpm_passthrough_reset,
+    .had_startup_error        = tpm_passthrough_get_startup_error,
+    .deliver_request          = tpm_passthrough_deliver_request,
+    .cancel_cmd               = tpm_passthrough_cancel_cmd,
+    .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
+    .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag,
+    .get_tpm_version          = tpm_passthrough_get_tpm_version,
+};
+
+static const TypeInfo tpm_cuse_info = {
+    .name = TYPE_TPM_CUSE,
+    .parent = TYPE_TPM_BACKEND,
+    .instance_size = sizeof(TPMPassthruState),
+    .class_init = tpm_passthrough_class_init,
+    .instance_init = tpm_passthrough_inst_init,
+    .instance_finalize = tpm_passthrough_inst_finalize,
+};
+
+static void tpm_cuse_register(void)
+{
+    type_register_static(&tpm_cuse_info);
+    tpm_register_driver(&tpm_cuse_driver);
+}
+
+type_init(tpm_cuse_register)
diff --git a/qapi-schema.json b/qapi-schema.json
index ac9594d..0d7c7b6 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2974,10 +2974,11 @@
 # An enumeration of TPM types
 #
 # @passthrough: TPM passthrough type
+# @cuse-tpm: CUSE TPM type
 #
 # Since: 1.5
 ##
-{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }
+{ 'enum': 'TpmType', 'data': [ 'passthrough', 'cuse-tpm' ] }
 
 ##
 # @query-tpm-types:
@@ -3006,6 +3007,17 @@
                                              '*cancel-path' : 'str'} }
 
 ##
+# @TPMCuseOptions:
+#
+# Information about the CUSE TPM type
+#
+# @path: string describing the path used for accessing the TPM device
+#
+# Since: 2.4
+##
+{ 'type': 'TPMCuseOptions', 'data': { 'path' : 'str'}}
+
+##
 # @TpmTypeOptions:
 #
 # A union referencing different TPM backend types' configuration options
@@ -3015,7 +3027,8 @@
 # Since: 1.5
 ##
 { 'union': 'TpmTypeOptions',
-   'data': { 'passthrough' : 'TPMPassthroughOptions' } }
+   'data': { 'passthrough' : 'TPMPassthroughOptions',
+             'cuse-tpm' : 'TPMCuseOptions' } }
 
 ##
 # @TpmInfo:
diff --git a/qemu-options.hx b/qemu-options.hx
index 319d971..65d4ea0 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2539,7 +2539,10 @@ DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
     "-tpmdev passthrough,id=id[,path=path][,cancel-path=path]\n"
     "                use path to provide path to a character device; default 
is /dev/tpm0\n"
     "                use cancel-path to provide path to TPM's cancel sysfs 
entry; if\n"
-    "                not provided it will be searched for in 
/sys/class/misc/tpm?/device\n",
+    "                not provided it will be searched for in 
/sys/class/misc/tpm?/device\n"
+    "-tpmdev cuse-tpm,id=id,path=path\n"
+    "                use path to provide path to a character device to talk to 
the\n"
+    "                TPM emulator providing a CUSE interface\n",
     QEMU_ARCH_ALL)
 STEXI
 
@@ -2548,8 +2551,8 @@ The general form of a TPM device option is:
 
 @item -tpmdev @var{backend} ,address@hidden [,@var{options}]
 @findex -tpmdev
-Backend type must be:
address@hidden
+Backend type must be either one of the following:
address@hidden, @option{cuse-tpm}.
 
 The specific backend type will determine the applicable options.
 The @code{-tpmdev} option creates the TPM backend and requires a
@@ -2599,6 +2602,18 @@ To create a passthrough TPM use the following two 
options:
 Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
 @code{tpmdev=tpm0} in the device option.
 
address@hidden -tpmdev cuse-tpm, address@hidden, address@hidden
+
+(Linux-host only) Enable access to a TPM emulator with a CUSE interface.
+
address@hidden specifies the path to the CUSE TPM character device.
+
+To create a backend device accessing the CUSE TPM emulator using /dev/vtpm
+use the following two options:
address@hidden
+-tpmdev cuse-tpm,id=tpm0,path=/dev/vtpm -device tpm-tis,tpmdev=tpm0
address@hidden example
+
 @end table
 
 ETEXI
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 3a42ad0..a1f45b1 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -3494,7 +3494,7 @@ Arguments: None
 Example:
 
 -> { "execute": "query-tpm-types" }
-<- { "return": [ "passthrough" ] }
+<- { "return": [ "passthrough", "cuse-tpm" ] }
 
 EQMP
 
diff --git a/tpm.c b/tpm.c
index 963b7ee..5443b7b 100644
--- a/tpm.c
+++ b/tpm.c
@@ -25,7 +25,7 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =
 
 
 #define TPM_MAX_MODELS      1
-#define TPM_MAX_DRIVERS     1
+#define TPM_MAX_DRIVERS     2
 
 static TPMDriverOps const *be_drivers[TPM_MAX_DRIVERS] = {
     NULL,
@@ -273,6 +273,15 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
             tpo->has_cancel_path = true;
         }
         break;
+    case TPM_TYPE_CUSE_TPM:
+        res->options->kind = TPM_TYPE_OPTIONS_KIND_CUSE_TPM;
+        tpo = g_new0(TPMPassthroughOptions, 1);
+        res->options->passthrough = tpo;
+        if (drv->path) {
+            tpo->path = g_strdup(drv->path);
+            tpo->has_path = true;
+        }
+        break;
     case TPM_TYPE_MAX:
         break;
     }
-- 
1.9.3




reply via email to

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