qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v7 0/8] Provide support for the software TPM emulator
@ 2017-09-22 12:33 Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 1/8] tpm-backend: Remove unneeded member variable from backend class Amarnath Valluri
                   ` (7 more replies)
  0 siblings, 8 replies; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-22 12:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri

Briefly, Theses set of patches introduces:
- new TPM backend driver to support software TPM emulators(swtpm(1)).
- and few supported fixes/enhancements/cleanup to existing tpm backend code.

The similar idea was initiated earliar(2) by Stefan Berger(CCed) with slightly
different approach, using CUSE. As swtpm has excellent support for unix domain
sockets, hence this implementation uses unix domain sockets to communicate with
swtpm.

When Qemu is configured with 'emulator' tpm backend, it spawns 'swtpm' and
communicates its via Unix domain sockets.

1) https://github.com/stefanberger/swtpm
2) https://lists.nongnu.org/archive/html/qemu-devel/2016-01/msg00089.html

** Changes in V2:
- Made spawnning swtpm optional
- used QIOChannel instead of plain unix sockets
- incorporated other fixes pointed in v1 review

** Changes in v3:
- Addressed review comments made by Stefan Berger and Deniel
P Berrange

** Changes in v4:
- Moved realloc_buffer() to TIS model
- Made TpmInfo related change backwards compatible.

** Changes in v5:
- updated Reviewed-By: tag to git commits
- added migration blocker to TPM emulator backend

** Changes in v6:
- rebased agianst latest master
- Address review comments maded by Stefan Berger

** Changes in v7:
- rebased agianst latest master
- Rewritten emulater backend using chardev backend

Amarnath Valluri (8):
  tpm-backend: Remove unneeded member variable from backend class
  tpm-backend: Move thread handling inside TPMBackend
  tpm-backend: Initialize and free data members in it's own methods
  tpm-backend: Made few interface methods optional
  tmp backend: Add new api to read backend TpmInfo
  tpm-backend: Move realloc_buffer() implementation to tpm-tis model
  tpm-passthrough: move reusable code to utils
  tpm: Added support for TPM emulator

 backends/tpm.c                   | 114 ++++---
 configure                        |  15 +-
 hmp.c                            |  12 +-
 hw/tpm/Makefile.objs             |   1 +
 hw/tpm/tpm_emulator.c            | 649 +++++++++++++++++++++++++++++++++++++++
 hw/tpm/tpm_ioctl.h               | 246 +++++++++++++++
 hw/tpm/tpm_passthrough.c         | 236 ++++----------
 hw/tpm/tpm_tis.c                 |  14 +-
 hw/tpm/tpm_util.c                |  25 ++
 hw/tpm/tpm_util.h                |   4 +
 include/sysemu/tpm_backend.h     |  87 +++---
 include/sysemu/tpm_backend_int.h |  41 ---
 qapi/tpm.json                    |  21 +-
 qemu-options.hx                  |  22 +-
 tpm.c                            |  36 +--
 15 files changed, 1173 insertions(+), 350 deletions(-)
 create mode 100644 hw/tpm/tpm_emulator.c
 create mode 100644 hw/tpm/tpm_ioctl.h
 delete mode 100644 include/sysemu/tpm_backend_int.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v7 1/8] tpm-backend: Remove unneeded member variable from backend class
  2017-09-22 12:33 [Qemu-devel] [PATCH v7 0/8] Provide support for the software TPM emulator Amarnath Valluri
@ 2017-09-22 12:33 ` Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 2/8] tpm-backend: Move thread handling inside TPMBackend Amarnath Valluri
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-22 12:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri

TPMDriverOps inside TPMBackend is not required, as it is supposed to be a class
member. The only possible reason for keeping in TPMBackend was, to get the
backend type in tpm.c where dedicated backend api, tpm_backend_get_type() is
present.

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 hw/tpm/tpm_passthrough.c     | 4 ----
 include/sysemu/tpm_backend.h | 1 -
 tpm.c                        | 2 +-
 3 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index 9234eb3..a0baf5f 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -46,8 +46,6 @@
 #define TPM_PASSTHROUGH(obj) \
     OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
 
-static const TPMDriverOps tpm_passthrough_driver;
-
 /* data structures */
 typedef struct TPMPassthruThreadParams {
     TPMState *tpm_state;
@@ -462,8 +460,6 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
     /* let frontend set the fe_model to proper value */
     tb->fe_model = -1;
 
-    tb->ops = &tpm_passthrough_driver;
-
     if (tpm_passthrough_handle_device_opts(opts, tb)) {
         goto err_exit;
     }
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index b0a9731..3708413 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -50,7 +50,6 @@ struct TPMBackend {
     enum TpmModel fe_model;
     char *path;
     char *cancel_path;
-    const TPMDriverOps *ops;
 
     QLIST_ENTRY(TPMBackend) list;
 };
diff --git a/tpm.c b/tpm.c
index 2d830d0..abedf3f 100644
--- a/tpm.c
+++ b/tpm.c
@@ -211,7 +211,7 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
     res->model = drv->fe_model;
     res->options = g_new0(TpmTypeOptions, 1);
 
-    switch (drv->ops->type) {
+    switch (tpm_backend_get_type(drv)) {
     case TPM_TYPE_PASSTHROUGH:
         res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
         tpo = g_new0(TPMPassthroughOptions, 1);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v7 2/8] tpm-backend: Move thread handling inside TPMBackend
  2017-09-22 12:33 [Qemu-devel] [PATCH v7 0/8] Provide support for the software TPM emulator Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 1/8] tpm-backend: Remove unneeded member variable from backend class Amarnath Valluri
@ 2017-09-22 12:33 ` Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 3/8] tpm-backend: Initialize and free data members in it's own methods Amarnath Valluri
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-22 12:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri

Move thread handling inside TPMBackend, this way backend implementations need
not to maintain their own thread life cycle, instead they needs to implement
'handle_request()' class method that always been called from a thread.

This change made tpm_backend_int.h kind of useless, hence removed it.

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 backends/tpm.c                   | 62 +++++++++++++++++++++++++---------------
 hw/tpm/tpm_passthrough.c         | 58 ++++++-------------------------------
 include/sysemu/tpm_backend.h     | 32 +++++++++++++--------
 include/sysemu/tpm_backend_int.h | 41 --------------------------
 4 files changed, 67 insertions(+), 126 deletions(-)
 delete mode 100644 include/sysemu/tpm_backend_int.h

diff --git a/backends/tpm.c b/backends/tpm.c
index 536f262..ce56c3b 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -18,7 +18,24 @@
 #include "qapi/qmp/qerror.h"
 #include "sysemu/tpm.h"
 #include "qemu/thread.h"
-#include "sysemu/tpm_backend_int.h"
+
+static void tpm_backend_worker_thread(gpointer data, gpointer user_data)
+{
+    TPMBackend *s = TPM_BACKEND(user_data);
+    TPMBackendClass *k  = TPM_BACKEND_GET_CLASS(s);
+
+    assert(k->handle_request != NULL);
+    k->handle_request(s, (TPMBackendCmd)data);
+}
+
+static void tpm_backend_thread_end(TPMBackend *s)
+{
+    if (s->thread_pool) {
+        g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
+        g_thread_pool_free(s->thread_pool, FALSE, TRUE);
+        s->thread_pool = NULL;
+    }
+}
 
 enum TpmType tpm_backend_get_type(TPMBackend *s)
 {
@@ -39,6 +56,8 @@ void tpm_backend_destroy(TPMBackend *s)
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
     k->ops->destroy(s);
+
+    tpm_backend_thread_end(s);
 }
 
 int tpm_backend_init(TPMBackend *s, TPMState *state,
@@ -46,13 +65,23 @@ int tpm_backend_init(TPMBackend *s, TPMState *state,
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    return k->ops->init(s, state, datacb);
+    s->tpm_state = state;
+    s->recv_data_callback = datacb;
+
+    return k->ops->init(s);
 }
 
 int tpm_backend_startup_tpm(TPMBackend *s)
 {
     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);
+    g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
+
     return k->ops->startup_tpm(s);
 }
 
@@ -72,9 +101,8 @@ size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb)
 
 void tpm_backend_deliver_request(TPMBackend *s)
 {
-    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
-
-    k->ops->deliver_request(s);
+    g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD,
+                       NULL);
 }
 
 void tpm_backend_reset(TPMBackend *s)
@@ -82,6 +110,8 @@ void tpm_backend_reset(TPMBackend *s)
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
     k->ops->reset(s);
+
+    tpm_backend_thread_end(s);
 }
 
 void tpm_backend_cancel_cmd(TPMBackend *s)
@@ -156,29 +186,14 @@ static void tpm_backend_instance_init(Object *obj)
                              tpm_backend_prop_get_opened,
                              tpm_backend_prop_set_opened,
                              NULL);
-}
 
-void tpm_backend_thread_deliver_request(TPMBackendThread *tbt)
-{
-   g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL);
 }
 
-void tpm_backend_thread_create(TPMBackendThread *tbt,
-                               GFunc func, gpointer user_data)
+static void tpm_backend_instance_finalize(Object *obj)
 {
-    if (!tbt->pool) {
-        tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL);
-        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
-    }
-}
+    TPMBackend *s = TPM_BACKEND(obj);
 
-void tpm_backend_thread_end(TPMBackendThread *tbt)
-{
-    if (tbt->pool) {
-        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
-        g_thread_pool_free(tbt->pool, FALSE, TRUE);
-        tbt->pool = NULL;
-    }
+    tpm_backend_thread_end(s);
 }
 
 static const TypeInfo tpm_backend_info = {
@@ -186,6 +201,7 @@ static const TypeInfo tpm_backend_info = {
     .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/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index a0baf5f..f50d9cf 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -30,7 +30,6 @@
 #include "tpm_int.h"
 #include "hw/hw.h"
 #include "hw/i386/pc.h"
-#include "sysemu/tpm_backend_int.h"
 #include "tpm_tis.h"
 #include "tpm_util.h"
 
@@ -47,20 +46,9 @@
     OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
 
 /* data structures */
-typedef struct TPMPassthruThreadParams {
-    TPMState *tpm_state;
-
-    TPMRecvDataCB *recv_data_callback;
-    TPMBackend *tb;
-} TPMPassthruThreadParams;
-
 struct TPMPassthruState {
     TPMBackend parent;
 
-    TPMBackendThread tbt;
-
-    TPMPassthruThreadParams tpm_thread_params;
-
     char *tpm_dev;
     int tpm_fd;
     bool tpm_executing;
@@ -214,12 +202,9 @@ static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
                                         selftest_done);
 }
 
-static void tpm_passthrough_worker_thread(gpointer data,
-                                          gpointer user_data)
+static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
 {
-    TPMPassthruThreadParams *thr_parms = user_data;
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
-    TPMBackendCmd cmd = (TPMBackendCmd)data;
+    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
     bool selftest_done = false;
 
     DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
@@ -227,12 +212,12 @@ 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_data,
+                                      tb->tpm_state->locty_data,
                                       &selftest_done);
 
-        thr_parms->recv_data_callback(thr_parms->tpm_state,
-                                      thr_parms->tpm_state->locty_number,
-                                      selftest_done);
+        tb->recv_data_callback(tb->tpm_state,
+                               tb->tpm_state->locty_number,
+                               selftest_done);
         break;
     case TPM_BACKEND_CMD_INIT:
     case TPM_BACKEND_CMD_END:
@@ -248,15 +233,6 @@ static void tpm_passthrough_worker_thread(gpointer data,
  */
 static int tpm_passthrough_startup_tpm(TPMBackend *tb)
 {
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
-
-    /* terminate a running TPM */
-    tpm_backend_thread_end(&tpm_pt->tbt);
-
-    tpm_backend_thread_create(&tpm_pt->tbt,
-                              tpm_passthrough_worker_thread,
-                              &tpm_pt->tpm_thread_params);
-
     return 0;
 }
 
@@ -268,20 +244,11 @@ static void tpm_passthrough_reset(TPMBackend *tb)
 
     tpm_passthrough_cancel_cmd(tb);
 
-    tpm_backend_thread_end(&tpm_pt->tbt);
-
     tpm_pt->had_startup_error = false;
 }
 
-static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
-                                TPMRecvDataCB *recv_data_cb)
+static int tpm_passthrough_init(TPMBackend *tb)
 {
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
-
-    tpm_pt->tpm_thread_params.tpm_state = s;
-    tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb;
-    tpm_pt->tpm_thread_params.tb = tb;
-
     return 0;
 }
 
@@ -315,13 +282,6 @@ static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
     return sb->size;
 }
 
-static void tpm_passthrough_deliver_request(TPMBackend *tb)
-{
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
-
-    tpm_backend_thread_deliver_request(&tpm_pt->tbt);
-}
-
 static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
@@ -483,8 +443,6 @@ static void tpm_passthrough_destroy(TPMBackend *tb)
 
     tpm_passthrough_cancel_cmd(tb);
 
-    tpm_backend_thread_end(&tpm_pt->tbt);
-
     qemu_close(tpm_pt->tpm_fd);
     qemu_close(tpm_pt->cancel_fd);
 
@@ -520,7 +478,6 @@ static const TPMDriverOps tpm_passthrough_driver = {
     .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,
@@ -540,6 +497,7 @@ static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
     TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
 
     tbc->ops = &tpm_passthrough_driver;
+    tbc->handle_request = tpm_passthrough_handle_request;
 }
 
 static const TypeInfo tpm_passthrough_info = {
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 3708413..58308b3 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -29,22 +29,24 @@
 
 typedef struct TPMBackendClass TPMBackendClass;
 typedef struct TPMBackend TPMBackend;
-
 typedef struct TPMDriverOps TPMDriverOps;
+typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done);
 
-struct TPMBackendClass {
-    ObjectClass parent_class;
-
-    const TPMDriverOps *ops;
-
-    void (*opened)(TPMBackend *s, Error **errp);
-};
+typedef enum TPMBackendCmd {
+    TPM_BACKEND_CMD_INIT = 1,
+    TPM_BACKEND_CMD_PROCESS_CMD,
+    TPM_BACKEND_CMD_END,
+    TPM_BACKEND_CMD_TPM_RESET,
+} TPMBackendCmd;
 
 struct TPMBackend {
     Object parent;
 
     /*< protected >*/
     bool opened;
+    TPMState *tpm_state;
+    GThreadPool *thread_pool;
+    TPMRecvDataCB *recv_data_callback;
 
     char *id;
     enum TpmModel fe_model;
@@ -54,7 +56,15 @@ struct TPMBackend {
     QLIST_ENTRY(TPMBackend) list;
 };
 
-typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done);
+struct TPMBackendClass {
+    ObjectClass parent_class;
+
+    const TPMDriverOps *ops;
+
+    void (*opened)(TPMBackend *s, Error **errp);
+
+    void (*handle_request)(TPMBackend *s, TPMBackendCmd cmd);
+};
 
 typedef struct TPMSizedBuffer {
     uint32_t size;
@@ -71,7 +81,7 @@ struct TPMDriverOps {
     void (*destroy)(TPMBackend *t);
 
     /* initialize the backend */
-    int (*init)(TPMBackend *t, TPMState *s, TPMRecvDataCB *datacb);
+    int (*init)(TPMBackend *t);
     /* start up the TPM on the backend */
     int (*startup_tpm)(TPMBackend *t);
     /* returns true if nothing will ever answer TPM requests */
@@ -79,8 +89,6 @@ struct TPMDriverOps {
 
     size_t (*realloc_buffer)(TPMSizedBuffer *sb);
 
-    void (*deliver_request)(TPMBackend *t);
-
     void (*reset)(TPMBackend *t);
 
     void (*cancel_cmd)(TPMBackend *t);
diff --git a/include/sysemu/tpm_backend_int.h b/include/sysemu/tpm_backend_int.h
deleted file mode 100644
index 00639dd..0000000
--- a/include/sysemu/tpm_backend_int.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *  common TPM backend driver functions
- *
- *  Copyright (c) 2012-2013 IBM Corporation
- *  Authors:
- *    Stefan Berger <stefanb@us.ibm.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef TPM_BACKEND_INT_H
-#define TPM_BACKEND_INT_H
-
-typedef struct TPMBackendThread {
-    GThreadPool *pool;
-} TPMBackendThread;
-
-void tpm_backend_thread_deliver_request(TPMBackendThread *tbt);
-void tpm_backend_thread_create(TPMBackendThread *tbt,
-                               GFunc func, gpointer user_data);
-void tpm_backend_thread_end(TPMBackendThread *tbt);
-
-typedef enum TPMBackendCmd {
-    TPM_BACKEND_CMD_INIT = 1,
-    TPM_BACKEND_CMD_PROCESS_CMD,
-    TPM_BACKEND_CMD_END,
-    TPM_BACKEND_CMD_TPM_RESET,
-} TPMBackendCmd;
-
-#endif /* TPM_BACKEND_INT_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v7 3/8] tpm-backend: Initialize and free data members in it's own methods
  2017-09-22 12:33 [Qemu-devel] [PATCH v7 0/8] Provide support for the software TPM emulator Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 1/8] tpm-backend: Remove unneeded member variable from backend class Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 2/8] tpm-backend: Move thread handling inside TPMBackend Amarnath Valluri
@ 2017-09-22 12:33 ` Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 4/8] tpm-backend: Made few interface methods optional Amarnath Valluri
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-22 12:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri

Initialize and free TPMBackend data members in it's own instance_init() and
instance_finalize methods.

Took the opportunity to remove unneeded destroy() method from TpmDriverOps
interface as TPMBackend is a Qemu Object, we can use object_unref() inplace of
tpm_backend_destroy() to free the backend object, hence removed destroy() from
TPMDriverOps interface.

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 backends/tpm.c               | 16 ++++++----------
 hw/tpm/tpm_passthrough.c     | 31 ++++++++++++-------------------
 include/sysemu/tpm_backend.h |  7 -------
 tpm.c                        |  2 +-
 4 files changed, 19 insertions(+), 37 deletions(-)

diff --git a/backends/tpm.c b/backends/tpm.c
index ce56c3b..cf5abf1 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -51,15 +51,6 @@ const char *tpm_backend_get_desc(TPMBackend *s)
     return k->ops->desc();
 }
 
-void tpm_backend_destroy(TPMBackend *s)
-{
-    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
-
-    k->ops->destroy(s);
-
-    tpm_backend_thread_end(s);
-}
-
 int tpm_backend_init(TPMBackend *s, TPMState *state,
                      TPMRecvDataCB *datacb)
 {
@@ -182,17 +173,22 @@ static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp)
 
 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;
 }
 
 static void tpm_backend_instance_finalize(Object *obj)
 {
     TPMBackend *s = TPM_BACKEND(obj);
 
+    g_free(s->id);
+    g_free(s->path);
+    g_free(s->cancel_path);
     tpm_backend_thread_end(s);
 }
 
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index f50d9cf..815a72e 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -417,8 +417,6 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 
     tb->id = g_strdup(id);
-    /* let frontend set the fe_model to proper value */
-    tb->fe_model = -1;
 
     if (tpm_passthrough_handle_device_opts(opts, tb)) {
         goto err_exit;
@@ -432,26 +430,11 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
     return tb;
 
 err_exit:
-    g_free(tb->id);
+    object_unref(obj);
 
     return NULL;
 }
 
-static void tpm_passthrough_destroy(TPMBackend *tb)
-{
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
-
-    tpm_passthrough_cancel_cmd(tb);
-
-    qemu_close(tpm_pt->tpm_fd);
-    qemu_close(tpm_pt->cancel_fd);
-
-    g_free(tb->id);
-    g_free(tb->path);
-    g_free(tb->cancel_path);
-    g_free(tpm_pt->tpm_dev);
-}
-
 static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
     TPM_STANDARD_CMDLINE_OPTS,
     {
@@ -472,7 +455,6 @@ static const TPMDriverOps tpm_passthrough_driver = {
     .opts                     = tpm_passthrough_cmdline_opts,
     .desc                     = tpm_passthrough_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,
@@ -486,10 +468,21 @@ static const TPMDriverOps tpm_passthrough_driver = {
 
 static void tpm_passthrough_inst_init(Object *obj)
 {
+    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
+
+    tpm_pt->tpm_fd = -1;
+    tpm_pt->cancel_fd = -1;
 }
 
 static void tpm_passthrough_inst_finalize(Object *obj)
 {
+    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
+
+    tpm_passthrough_cancel_cmd(TPM_BACKEND(obj));
+
+    qemu_close(tpm_pt->tpm_fd);
+    qemu_close(tpm_pt->cancel_fd);
+    g_free(tpm_pt->tpm_dev);
 }
 
 static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 58308b3..202ec8d 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -78,7 +78,6 @@ struct TPMDriverOps {
     const char *(*desc)(void);
 
     TPMBackend *(*create)(QemuOpts *opts, const char *id);
-    void (*destroy)(TPMBackend *t);
 
     /* initialize the backend */
     int (*init)(TPMBackend *t);
@@ -118,12 +117,6 @@ enum TpmType tpm_backend_get_type(TPMBackend *s);
 const char *tpm_backend_get_desc(TPMBackend *s);
 
 /**
- * tpm_backend_destroy:
- * @s: the backend to destroy
- */
-void tpm_backend_destroy(TPMBackend *s);
-
-/**
  * tpm_backend_init:
  * @s: the backend to initialized
  * @state: TPMState
diff --git a/tpm.c b/tpm.c
index abedf3f..b19b1a3 100644
--- a/tpm.c
+++ b/tpm.c
@@ -157,7 +157,7 @@ void tpm_cleanup(void)
 
     QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
         QLIST_REMOVE(drv, list);
-        tpm_backend_destroy(drv);
+        object_unref(OBJECT(drv));
     }
 }
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v7 4/8] tpm-backend: Made few interface methods optional
  2017-09-22 12:33 [Qemu-devel] [PATCH v7 0/8] Provide support for the software TPM emulator Amarnath Valluri
                   ` (2 preceding siblings ...)
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 3/8] tpm-backend: Initialize and free data members in it's own methods Amarnath Valluri
@ 2017-09-22 12:33 ` Amarnath Valluri
  2017-09-27 12:23   ` [Qemu-devel] [PATCH v8 " Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 5/8] tmp backend: Add new api to read backend TpmInfo Amarnath Valluri
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-22 12:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri

This allows backend implementations left optional interface methods.
For mandatory methods assertion checks added.

Took the opportunity to remove unused tpm_backend_get_desc() method

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
---
 backends/tpm.c               | 29 +++++++++++++++++------------
 hw/tpm/tpm_passthrough.c     | 23 +----------------------
 include/sysemu/tpm_backend.h | 10 +---------
 tpm.c                        |  2 +-
 4 files changed, 20 insertions(+), 44 deletions(-)

diff --git a/backends/tpm.c b/backends/tpm.c
index cf5abf1..c409a46 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -44,13 +44,6 @@ enum TpmType tpm_backend_get_type(TPMBackend *s)
     return k->ops->type;
 }
 
-const char *tpm_backend_get_desc(TPMBackend *s)
-{
-    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
-
-    return k->ops->desc();
-}
-
 int tpm_backend_init(TPMBackend *s, TPMState *state,
                      TPMRecvDataCB *datacb)
 {
@@ -59,7 +52,7 @@ int tpm_backend_init(TPMBackend *s, TPMState *state,
     s->tpm_state = state;
     s->recv_data_callback = datacb;
 
-    return k->ops->init(s);
+    return k->ops->init ? k->ops->init(s) : 0;
 }
 
 int tpm_backend_startup_tpm(TPMBackend *s)
@@ -73,13 +66,15 @@ int tpm_backend_startup_tpm(TPMBackend *s)
                                        NULL);
     g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
 
-    return k->ops->startup_tpm(s);
+    return k->ops->startup_tpm ? k->ops->startup_tpm(s) : 0;
 }
 
 bool tpm_backend_had_startup_error(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
+    assert(k->ops->had_startup_error);
+
     return k->ops->had_startup_error(s);
 }
 
@@ -87,6 +82,8 @@ size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
+    assert(k->ops->realloc_buffer);
+
     return k->ops->realloc_buffer(sb);
 }
 
@@ -100,7 +97,9 @@ void tpm_backend_reset(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    k->ops->reset(s);
+    if (k->ops->reset) {
+        k->ops->reset(s);
+    }
 
     tpm_backend_thread_end(s);
 }
@@ -109,6 +108,8 @@ void tpm_backend_cancel_cmd(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
+    assert(k->ops->cancel_cmd);
+
     k->ops->cancel_cmd(s);
 }
 
@@ -116,20 +117,24 @@ bool tpm_backend_get_tpm_established_flag(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    return k->ops->get_tpm_established_flag(s);
+    return k->ops->get_tpm_established_flag ?
+           k->ops->get_tpm_established_flag(s) : false;
 }
 
 int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    return k->ops->reset_tpm_established_flag(s, locty);
+    return k->ops->reset_tpm_established_flag ?
+           k->ops->reset_tpm_established_flag(s, locty) : 0;
 }
 
 TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
+    assert(k->ops->get_tpm_version);
+
     return k->ops->get_tpm_version(s);
 }
 
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index 815a72e..a0459a6 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -227,15 +227,6 @@ static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
     }
 }
 
-/*
- * Start the TPM (thread). If it had been started before, then terminate
- * and start it again.
- */
-static int tpm_passthrough_startup_tpm(TPMBackend *tb)
-{
-    return 0;
-}
-
 static void tpm_passthrough_reset(TPMBackend *tb)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
@@ -247,11 +238,6 @@ static void tpm_passthrough_reset(TPMBackend *tb)
     tpm_pt->had_startup_error = false;
 }
 
-static int tpm_passthrough_init(TPMBackend *tb)
-{
-    return 0;
-}
-
 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
 {
     return false;
@@ -309,11 +295,6 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
     }
 }
 
-static const char *tpm_passthrough_create_desc(void)
-{
-    return "Passthrough TPM backend driver";
-}
-
 static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
@@ -453,10 +434,8 @@ static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
 static const TPMDriverOps tpm_passthrough_driver = {
     .type                     = TPM_TYPE_PASSTHROUGH,
     .opts                     = tpm_passthrough_cmdline_opts,
-    .desc                     = tpm_passthrough_create_desc,
+    .desc                     = "Passthrough TPM backend driver",
     .create                   = tpm_passthrough_create,
-    .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,
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 202ec8d..13311cf 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -75,7 +75,7 @@ struct TPMDriverOps {
     enum TpmType type;
     const QemuOptDesc *opts;
     /* get a descriptive text of the backend to display to the user */
-    const char *(*desc)(void);
+    const char *desc;
 
     TPMBackend *(*create)(QemuOpts *opts, const char *id);
 
@@ -109,14 +109,6 @@ struct TPMDriverOps {
 enum TpmType tpm_backend_get_type(TPMBackend *s);
 
 /**
- * tpm_backend_get_desc:
- * @s: the backend
- *
- * Returns a human readable description of the backend.
- */
-const char *tpm_backend_get_desc(TPMBackend *s);
-
-/**
  * tpm_backend_init:
  * @s: the backend to initialized
  * @state: TPMState
diff --git a/tpm.c b/tpm.c
index b19b1a3..db14849 100644
--- a/tpm.c
+++ b/tpm.c
@@ -62,7 +62,7 @@ static void tpm_display_backend_drivers(void)
             continue;
         }
         fprintf(stderr, "%12s   %s\n",
-                TpmType_str(i), be_drivers[i]->desc());
+                TpmType_str(i), be_drivers[i]->desc);
     }
     fprintf(stderr, "\n");
 }
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v7 5/8] tmp backend: Add new api to read backend TpmInfo
  2017-09-22 12:33 [Qemu-devel] [PATCH v7 0/8] Provide support for the software TPM emulator Amarnath Valluri
                   ` (3 preceding siblings ...)
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 4/8] tpm-backend: Made few interface methods optional Amarnath Valluri
@ 2017-09-22 12:33 ` Amarnath Valluri
  2017-09-22 15:35   ` Marc-André Lureau
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 6/8] tpm-backend: Move realloc_buffer() implementation to tpm-tis model Amarnath Valluri
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-22 12:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri,
	Dr. David Alan Gilbert

TPM configuration options are backend implementation details and shall not be
part of base TPMBackend object, and these shall not be accessed directly outside
of the class, hence added a new interface method, get_tpm_options() to
TPMDriverOps., which shall be implemented by the derived classes to return
configured tpm options.

A new tpm backend api - tpm_backend_query_tpm() which uses _get_tpm_options() to
prepare TpmInfo.

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 backends/tpm.c               | 22 +++++++++++++--
 hmp.c                        |  7 ++---
 hw/tpm/tpm_passthrough.c     | 64 +++++++++++++++++++++++++++++++-------------
 include/sysemu/tpm_backend.h | 25 +++++++++++++++--
 tpm.c                        | 32 +---------------------
 5 files changed, 93 insertions(+), 57 deletions(-)

diff --git a/backends/tpm.c b/backends/tpm.c
index c409a46..6ade9e4 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -138,6 +138,26 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
     return k->ops->get_tpm_version(s);
 }
 
+TpmTypeOptions *tpm_backend_get_tpm_options(TPMBackend *s)
+{
+    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
+
+    assert(k->ops->get_tpm_options);
+
+    return k->ops->get_tpm_options(s);
+}
+
+TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
+{
+    TPMInfo *info = g_new0(TPMInfo, 1);
+
+    info->id = g_strdup(s->id);
+    info->model = s->fe_model;
+    info->options = tpm_backend_get_tpm_options(s);
+
+    return info;
+}
+
 static bool tpm_backend_prop_get_opened(Object *obj, Error **errp)
 {
     TPMBackend *s = TPM_BACKEND(obj);
@@ -192,8 +212,6 @@ static void tpm_backend_instance_finalize(Object *obj)
     TPMBackend *s = TPM_BACKEND(obj);
 
     g_free(s->id);
-    g_free(s->path);
-    g_free(s->cancel_path);
     tpm_backend_thread_end(s);
 }
 
diff --git a/hmp.c b/hmp.c
index 0fb2bc7..cf62b2e 100644
--- a/hmp.c
+++ b/hmp.c
@@ -31,6 +31,7 @@
 #include "qapi/qmp/qerror.h"
 #include "qapi/string-input-visitor.h"
 #include "qapi/string-output-visitor.h"
+#include "qapi/util.h"
 #include "qapi-visit.h"
 #include "qom/object_interfaces.h"
 #include "ui/console.h"
@@ -1012,10 +1013,10 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
                        c, TpmModel_str(ti->model));
 
         monitor_printf(mon, "  \\ %s: type=%s",
-                       ti->id, TpmTypeOptionsKind_str(ti->options->type));
+                       ti->id, TpmType_str(ti->options->type));
 
         switch (ti->options->type) {
-        case TPM_TYPE_OPTIONS_KIND_PASSTHROUGH:
+        case TPM_TYPE_PASSTHROUGH:
             tpo = ti->options->u.passthrough.data;
             monitor_printf(mon, "%s%s%s%s",
                            tpo->has_path ? ",path=" : "",
@@ -1023,7 +1024,7 @@ 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__MAX:
+        case TPM_TYPE__MAX:
             break;
         }
         monitor_printf(mon, "\n");
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index a0459a6..fb7dad8 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -49,7 +49,8 @@
 struct TPMPassthruState {
     TPMBackend parent;
 
-    char *tpm_dev;
+    TPMPassthroughOptions *options;
+    const char *tpm_dev;
     int tpm_fd;
     bool tpm_executing;
     bool tpm_op_canceled;
@@ -308,15 +309,14 @@ static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
  * in Documentation/ABI/stable/sysfs-class-tpm.
  * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
  */
-static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
+static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
 {
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
     int fd = -1;
     char *dev;
     char path[PATH_MAX];
 
-    if (tb->cancel_path) {
-        fd = qemu_open(tb->cancel_path, O_WRONLY);
+    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",
                          strerror(errno));
@@ -331,7 +331,7 @@ static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
                      dev) < sizeof(path)) {
             fd = qemu_open(path, O_WRONLY);
             if (fd >= 0) {
-                tb->cancel_path = g_strdup(path);
+                tpm_pt->options->cancel_path = g_strdup(path);
             } else {
                 error_report("tpm_passthrough: Could not open TPM cancel "
                              "path %s : %s", path, strerror(errno));
@@ -351,17 +351,18 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
     const char *value;
 
     value = qemu_opt_get(opts, "cancel-path");
-    tb->cancel_path = g_strdup(value);
+    if (value) {
+        tpm_pt->options->cancel_path = g_strdup(value);
+        tpm_pt->options->has_cancel_path = true;
+    }
 
     value = qemu_opt_get(opts, "path");
-    if (!value) {
-        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+    if (value) {
+        tpm_pt->options->has_path = true;
+        tpm_pt->options->path = g_strdup(value);
     }
 
-    tpm_pt->tpm_dev = g_strdup(value);
-
-    tb->path = g_strdup(tpm_pt->tpm_dev);
-
+    tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE;
     tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
     if (tpm_pt->tpm_fd < 0) {
         error_report("Cannot access TPM device using '%s': %s",
@@ -382,10 +383,8 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
     tpm_pt->tpm_fd = -1;
 
  err_free_parameters:
-    g_free(tb->path);
-    tb->path = NULL;
-
-    g_free(tpm_pt->tpm_dev);
+    qapi_free_TPMPassthroughOptions(tpm_pt->options);
+    tpm_pt->options = NULL;
     tpm_pt->tpm_dev = NULL;
 
     return 1;
@@ -403,7 +402,7 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
         goto err_exit;
     }
 
-    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
+    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
     if (tpm_pt->cancel_fd < 0) {
         goto err_exit;
     }
@@ -416,6 +415,31 @@ err_exit:
     return NULL;
 }
 
+static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
+    TpmTypeOptions *options = NULL;
+    TPMPassthroughOptions *poptions = NULL;
+
+    poptions = g_new0(TPMPassthroughOptions, 1);
+
+    if (tpm_pt->options->has_path) {
+        poptions->has_path = true;
+        poptions->path = g_strdup(tpm_pt->options->path);
+    }
+
+    if (tpm_pt->options->has_cancel_path) {
+        poptions->has_cancel_path = true;
+        poptions->cancel_path = g_strdup(tpm_pt->options->cancel_path);
+    }
+
+    options = g_new0(TpmTypeOptions, 1);
+    options->type = TPM_TYPE_PASSTHROUGH;
+    options->u.passthrough.data = poptions;
+
+    return options;
+}
+
 static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
     TPM_STANDARD_CMDLINE_OPTS,
     {
@@ -443,12 +467,14 @@ static const TPMDriverOps tpm_passthrough_driver = {
     .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,
+    .get_tpm_options          = tpm_passthrough_get_tpm_options,
 };
 
 static void tpm_passthrough_inst_init(Object *obj)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
 
+    tpm_pt->options = g_new0(TPMPassthroughOptions, 1);
     tpm_pt->tpm_fd = -1;
     tpm_pt->cancel_fd = -1;
 }
@@ -461,7 +487,7 @@ static void tpm_passthrough_inst_finalize(Object *obj)
 
     qemu_close(tpm_pt->tpm_fd);
     qemu_close(tpm_pt->cancel_fd);
-    g_free(tpm_pt->tpm_dev);
+    qapi_free_TPMPassthroughOptions(tpm_pt->options);
 }
 
 static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 13311cf..809eec2 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -48,10 +48,9 @@ struct TPMBackend {
     GThreadPool *thread_pool;
     TPMRecvDataCB *recv_data_callback;
 
+    /* <public> */
     char *id;
     enum TpmModel fe_model;
-    char *path;
-    char *cancel_path;
 
     QLIST_ENTRY(TPMBackend) list;
 };
@@ -97,6 +96,8 @@ struct TPMDriverOps {
     int (*reset_tpm_established_flag)(TPMBackend *t, uint8_t locty);
 
     TPMVersion (*get_tpm_version)(TPMBackend *t);
+
+    TpmTypeOptions *(*get_tpm_options)(TPMBackend *t);
 };
 
 
@@ -215,6 +216,26 @@ void tpm_backend_open(TPMBackend *s, Error **errp);
  */
 TPMVersion tpm_backend_get_tpm_version(TPMBackend *s);
 
+/**
+ * tpm_backend_get_tpm_options:
+ * @s: the backend
+ *
+ * Get the backend configuration options
+ *
+ * Returns newly allocated TpmTypeOptions
+ */
+TpmTypeOptions *tpm_backend_get_tpm_options(TPMBackend *s);
+
+/**
+ * tpm_backend_query_tpm:
+ * @s: the backend
+ *
+ * Query backend tpm info
+ *
+ * Returns newly allocated TPMInfo
+ */
+TPMInfo *tpm_backend_query_tpm(TPMBackend *s);
+
 TPMBackend *qemu_find_tpm(const char *id);
 
 const TPMDriverOps *tpm_get_backend_driver(const char *type);
diff --git a/tpm.c b/tpm.c
index db14849..3b8c7ed 100644
--- a/tpm.c
+++ b/tpm.c
@@ -202,36 +202,6 @@ static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type)
     return be_drivers[type];
 }
 
-static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
-{
-    TPMInfo *res = g_new0(TPMInfo, 1);
-    TPMPassthroughOptions *tpo;
-
-    res->id = g_strdup(drv->id);
-    res->model = drv->fe_model;
-    res->options = g_new0(TpmTypeOptions, 1);
-
-    switch (tpm_backend_get_type(drv)) {
-    case TPM_TYPE_PASSTHROUGH:
-        res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
-        tpo = g_new0(TPMPassthroughOptions, 1);
-        res->options->u.passthrough.data = tpo;
-        if (drv->path) {
-            tpo->path = g_strdup(drv->path);
-            tpo->has_path = true;
-        }
-        if (drv->cancel_path) {
-            tpo->cancel_path = g_strdup(drv->cancel_path);
-            tpo->has_cancel_path = true;
-        }
-        break;
-    case TPM_TYPE__MAX:
-        break;
-    }
-
-    return res;
-}
-
 /*
  * Walk the list of active TPM backends and collect information about them
  * following the schema description in qapi-schema.json.
@@ -246,7 +216,7 @@ TPMInfoList *qmp_query_tpm(Error **errp)
             continue;
         }
         info = g_new0(TPMInfoList, 1);
-        info->value = qmp_query_tpm_inst(drv);
+        info->value = tpm_backend_query_tpm(drv);
 
         if (!cur_item) {
             head = cur_item = info;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v7 6/8] tpm-backend: Move realloc_buffer() implementation to tpm-tis model
  2017-09-22 12:33 [Qemu-devel] [PATCH v7 0/8] Provide support for the software TPM emulator Amarnath Valluri
                   ` (4 preceding siblings ...)
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 5/8] tmp backend: Add new api to read backend TpmInfo Amarnath Valluri
@ 2017-09-22 12:33 ` Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 7/8] tpm-passthrough: move reusable code to utils Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator Amarnath Valluri
  7 siblings, 0 replies; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-22 12:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri

buffer reallocation is very unlikely to be backend specific. Hence move inside
the tis.

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/tpm.c               |  9 ---------
 hw/tpm/tpm_passthrough.c     | 12 ------------
 hw/tpm/tpm_tis.c             | 14 ++++++++++++--
 include/sysemu/tpm_backend.h | 12 ------------
 4 files changed, 12 insertions(+), 35 deletions(-)

diff --git a/backends/tpm.c b/backends/tpm.c
index 6ade9e4..ffc9d88 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -78,15 +78,6 @@ bool tpm_backend_had_startup_error(TPMBackend *s)
     return k->ops->had_startup_error(s);
 }
 
-size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb)
-{
-    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
-
-    assert(k->ops->realloc_buffer);
-
-    return k->ops->realloc_buffer(sb);
-}
-
 void tpm_backend_deliver_request(TPMBackend *s)
 {
     g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD,
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index fb7dad8..82e7003 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -258,17 +258,6 @@ static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
     return tpm_pt->had_startup_error;
 }
 
-static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
-{
-    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;
-    }
-    return sb->size;
-}
-
 static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
@@ -460,7 +449,6 @@ static const TPMDriverOps tpm_passthrough_driver = {
     .opts                     = tpm_passthrough_cmdline_opts,
     .desc                     = "Passthrough TPM backend driver",
     .create                   = tpm_passthrough_create,
-    .realloc_buffer           = tpm_passthrough_realloc_buffer,
     .reset                    = tpm_passthrough_reset,
     .had_startup_error        = tpm_passthrough_get_startup_error,
     .cancel_cmd               = tpm_passthrough_cancel_cmd,
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c
index a6440fe..d5118e7 100644
--- a/hw/tpm/tpm_tis.c
+++ b/hw/tpm/tpm_tis.c
@@ -963,6 +963,16 @@ static int tpm_tis_do_startup_tpm(TPMState *s)
     return tpm_backend_startup_tpm(s->be_driver);
 }
 
+static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb)
+{
+    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;
+    }
+}
+
 /*
  * Get the TPMVersion of the backend device being used
  */
@@ -1010,9 +1020,9 @@ static void tpm_tis_reset(DeviceState *dev)
         tis->loc[c].state = TPM_TIS_STATE_IDLE;
 
         tis->loc[c].w_offset = 0;
-        tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].w_buffer);
+        tpm_tis_realloc_buffer(&tis->loc[c].w_buffer);
         tis->loc[c].r_offset = 0;
-        tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].r_buffer);
+        tpm_tis_realloc_buffer(&tis->loc[c].r_buffer);
     }
 
     tpm_tis_do_startup_tpm(s);
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 809eec2..6163edf 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -85,8 +85,6 @@ struct TPMDriverOps {
     /* returns true if nothing will ever answer TPM requests */
     bool (*had_startup_error)(TPMBackend *t);
 
-    size_t (*realloc_buffer)(TPMSizedBuffer *sb);
-
     void (*reset)(TPMBackend *t);
 
     void (*cancel_cmd)(TPMBackend *t);
@@ -141,16 +139,6 @@ int tpm_backend_startup_tpm(TPMBackend *s);
 bool tpm_backend_had_startup_error(TPMBackend *s);
 
 /**
- * tpm_backend_realloc_buffer:
- * @s: the backend
- * @sb: the TPMSizedBuffer to re-allocated to the size suitable for the
- *      backend.
- *
- * This function returns the size of the allocated buffer
- */
-size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb);
-
-/**
  * tpm_backend_deliver_request:
  * @s: the backend to send the request to
  *
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v7 7/8] tpm-passthrough: move reusable code to utils
  2017-09-22 12:33 [Qemu-devel] [PATCH v7 0/8] Provide support for the software TPM emulator Amarnath Valluri
                   ` (5 preceding siblings ...)
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 6/8] tpm-backend: Move realloc_buffer() implementation to tpm-tis model Amarnath Valluri
@ 2017-09-22 12:33 ` Amarnath Valluri
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator Amarnath Valluri
  7 siblings, 0 replies; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-22 12:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/tpm/tpm_passthrough.c | 64 ++++--------------------------------------------
 hw/tpm/tpm_util.c        | 25 +++++++++++++++++++
 hw/tpm/tpm_util.h        |  4 +++
 3 files changed, 34 insertions(+), 59 deletions(-)

diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index 82e7003..637a6e4 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -68,27 +68,6 @@ typedef struct TPMPassthruState TPMPassthruState;
 
 static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
 
-static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
-{
-    int ret, remain;
-
-    remain = len;
-    while (remain > 0) {
-        ret = write(fd, buf, remain);
-        if (ret < 0) {
-            if (errno != EINTR && errno != EAGAIN) {
-                return -1;
-            }
-        } else if (ret == 0) {
-            break;
-        } else {
-            buf += ret;
-            remain -= ret;
-        }
-    }
-    return len - remain;
-}
-
 static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
 {
     int ret;
@@ -102,45 +81,12 @@ static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
     }
     return ret;
 }
-
-static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
-{
-    struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf;
-
-    return be32_to_cpu(resp->len);
-}
-
-/*
- * Write an error message in the given output buffer.
- */
-static void tpm_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);
-    }
-}
-
-static bool tpm_passthrough_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);
-    }
-
-    return false;
-}
-
 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)
 {
-    int ret;
+    ssize_t ret;
     bool is_selftest;
     const struct tpm_resp_hdr *hdr;
 
@@ -148,9 +94,9 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
     tpm_pt->tpm_executing = true;
     *selftest_done = false;
 
-    is_selftest = tpm_passthrough_is_selftest(in, in_len);
+    is_selftest = tpm_util_is_selftest(in, in_len);
 
-    ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
+    ret = qemu_write_full(tpm_pt->tpm_fd, (const void *)in, (size_t)in_len);
     if (ret != in_len) {
         if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
             error_report("tpm_passthrough: error while transmitting data "
@@ -170,7 +116,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
                          strerror(errno), errno);
         }
     } else if (ret < sizeof(struct tpm_resp_hdr) ||
-               tpm_passthrough_get_size_from_buffer(out) != ret) {
+               be32_to_cpu(((struct tpm_resp_hdr *)out)->len) != ret) {
         ret = -1;
         error_report("tpm_passthrough: received invalid response "
                      "packet from TPM");
@@ -183,7 +129,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
 
 err_exit:
     if (ret < 0) {
-        tpm_write_fatal_error_response(out, out_len);
+        tpm_util_write_fatal_error_response(out, out_len);
     }
 
     tpm_pt->tpm_executing = false;
diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c
index 7b35429..fb929f6 100644
--- a/hw/tpm/tpm_util.c
+++ b/hw/tpm/tpm_util.c
@@ -24,6 +24,31 @@
 #include "tpm_int.h"
 
 /*
+ * Write an error message in the given output buffer.
+ */
+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);
+    }
+}
+
+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);
+    }
+
+    return false;
+}
+
+/*
  * A basic test of a TPM device. We expect a well formatted response header
  * (error response is fine) within one second.
  */
diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h
index df76245..2f7c961 100644
--- a/hw/tpm/tpm_util.h
+++ b/hw/tpm/tpm_util.h
@@ -24,6 +24,10 @@
 
 #include "sysemu/tpm_backend.h"
 
+void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len);
+
+bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len);
+
 int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version);
 
 #endif /* TPM_TPM_UTIL_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator
  2017-09-22 12:33 [Qemu-devel] [PATCH v7 0/8] Provide support for the software TPM emulator Amarnath Valluri
                   ` (6 preceding siblings ...)
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 7/8] tpm-passthrough: move reusable code to utils Amarnath Valluri
@ 2017-09-22 12:33 ` Amarnath Valluri
  2017-09-22 16:23   ` Stefan Berger
  2017-09-24 18:52   ` Marc-André Lureau
  7 siblings, 2 replies; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-22 12:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri,
	Dr. David Alan Gilbert, Eric Blake, Markus Armbruster

This change introduces a new TPM backend driver that can communicate with
swtpm(software TPM emulator) using unix domain socket interface. QEMU talks to
TPM emulator using socket based chardev backend device.

Swtpm uses two Unix sockets for communications, one for plain TPM commands and
responses, and one for out-of-band control messages. QEMU passes data socket
been used over the control channel.

The swtpm and associated tools can be found here:
    https://github.com/stefanberger/swtpm

The swtpm's control channel protocol specification can be found here:
    https://github.com/stefanberger/swtpm/wiki/Control-Channel-Specification

Usage:
    # setup TPM state directory
    mkdir /tmp/mytpm
    chown -R tss:root /tmp/mytpm
    /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek

    # Ask qemu to use TPM emulator with given tpm state directory
    qemu-system-x86_64 \
        [...] \
        -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \
        -tpmdev emulator,id=tpm0,chardev=chrtpm \
        -device tpm-tis,tpmdev=tpm0 \
        [...]

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
---
 configure             |  15 +-
 hmp.c                 |   5 +
 hw/tpm/Makefile.objs  |   1 +
 hw/tpm/tpm_emulator.c | 649 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/tpm/tpm_ioctl.h    | 246 +++++++++++++++++++
 qapi/tpm.json         |  21 +-
 qemu-options.hx       |  22 +-
 7 files changed, 950 insertions(+), 9 deletions(-)
 create mode 100644 hw/tpm/tpm_emulator.c
 create mode 100644 hw/tpm/tpm_ioctl.h

diff --git a/configure b/configure
index cb0f7ed..ce2df2d 100755
--- a/configure
+++ b/configure
@@ -3461,10 +3461,15 @@ fi
 ##########################################
 # TPM passthrough is only on x86 Linux
 
-if test "$targetos" = Linux && test "$cpu" = i386 -o "$cpu" = x86_64; then
-  tpm_passthrough=$tpm
+if test "$targetos" = Linux; then
+  tpm_emulator=$tpm
+  if test "$cpu" = i386 -o "$cpu" = x86_64; then
+    tpm_passthrough=$tpm
+  else
+    tpm_passthrough=no
+  fi
 else
-  tpm_passthrough=no
+  tpm_emulator=no
 fi
 
 ##########################################
@@ -5359,6 +5364,7 @@ echo "gcov enabled      $gcov"
 echo "TPM support       $tpm"
 echo "libssh2 support   $libssh2"
 echo "TPM passthrough   $tpm_passthrough"
+echo "TPM emulator      $tpm_emulator"
 echo "QOM debugging     $qom_cast_debug"
 echo "Live block migration $live_block_migration"
 echo "lzo support       $lzo"
@@ -5943,6 +5949,9 @@ if test "$tpm" = "yes"; then
   if test "$tpm_passthrough" = "yes"; then
     echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
   fi
+  if test "$tpm_emulator" = "yes"; then
+    echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak
+  fi
 fi
 
 echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
diff --git a/hmp.c b/hmp.c
index cf62b2e..7e69eca 100644
--- a/hmp.c
+++ b/hmp.c
@@ -995,6 +995,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
     Error *err = NULL;
     unsigned int c = 0;
     TPMPassthroughOptions *tpo;
+    TPMEmulatorOptions *teo;
 
     info_list = qmp_query_tpm(&err);
     if (err) {
@@ -1024,6 +1025,10 @@ 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_EMULATOR:
+            teo = ti->options->u.emulator.data;
+            monitor_printf(mon, ",chardev=%s", teo->chardev);
+            break;
         case TPM_TYPE__MAX:
             break;
         }
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
index 64cecc3..41f0b7a 100644
--- a/hw/tpm/Makefile.objs
+++ b/hw/tpm/Makefile.objs
@@ -1,2 +1,3 @@
 common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
 common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
+common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
new file mode 100644
index 0000000..c02bbe2
--- /dev/null
+++ b/hw/tpm/tpm_emulator.c
@@ -0,0 +1,649 @@
+/*
+ *  emulator TPM driver
+ *
+ *  Copyright (c) 2017 Intel Corporation
+ *  Author: Amarnath Valluri <amarnath.valluri@intel.com>
+ *
+ *  Copyright (c) 2010 - 2013 IBM Corporation
+ *  Authors:
+ *    Stefan Berger <stefanb@us.ibm.com>
+ *
+ *  Copyright (C) 2011 IAIK, Graz University of Technology
+ *    Author: Andreas Niederl
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/sockets.h"
+#include "io/channel-socket.h"
+#include "sysemu/tpm_backend.h"
+#include "tpm_int.h"
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "tpm_util.h"
+#include "tpm_ioctl.h"
+#include "migration/blocker.h"
+#include "qapi/error.h"
+#include "chardev/char-fe.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+#define DEBUG_TPM 0
+
+#define DPRINT(fmt, ...) do { \
+    if (DEBUG_TPM) { \
+        fprintf(stderr, fmt, ## __VA_ARGS__); \
+    } \
+} while (0);
+
+#define DPRINTF(fmt, ...) DPRINT("tpm-emulator: "fmt"\n", __VA_ARGS__)
+
+#define TYPE_TPM_EMULATOR "tpm-emulator"
+#define TPM_EMULATOR(obj) \
+    OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
+
+#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))
+
+static const TPMDriverOps tpm_emulator_driver;
+
+/* data structures */
+typedef struct TPMEmulator {
+    TPMBackend parent;
+
+    TPMEmulatorOptions *options;
+    CharBackend ctrl_dev;
+    QIOChannel *data_ioc;
+    bool op_executing;
+    bool op_canceled;
+    bool had_startup_error;
+    TPMVersion tpm_version;
+    ptm_cap caps; /* capabilities of the TPM */
+    uint8_t cur_locty_number; /* last set locality */
+    QemuMutex state_lock;
+    Error *migration_blocker;
+} TPMEmulator;
+
+
+static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg,
+                                size_t msg_len_in, size_t msg_len_out)
+{
+    uint32_t cmd_no = cpu_to_be32(cmd);
+    ssize_t n = sizeof(uint32_t) + msg_len_in;
+    uint8_t *buf = NULL;
+
+    buf = (uint8_t *)malloc(n);
+    memcpy(buf, &cmd_no, sizeof(cmd_no));
+    memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
+
+    n += qemu_chr_fe_write_all(dev, (const uint8_t *)buf, n);
+    free(buf);
+
+    if (n > 0) {
+        if (msg_len_out > 0) {
+            n = qemu_chr_fe_read_all(dev, (uint8_t *)msg, msg_len_out);
+            /* simulate ioctl return value */
+            if (n > 0) {
+                n = 0;
+            }
+        } else {
+            n = 0;
+        }
+    }
+    return n;
+}
+
+static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_pt,
+                                     const uint8_t *in, uint32_t in_len,
+                                     uint8_t *out, uint32_t out_len,
+                                     bool *selftest_done)
+{
+    ssize_t ret;
+    bool is_selftest = false;
+    const struct tpm_resp_hdr *hdr = NULL;
+    Error *err = NULL;
+
+    tpm_pt->op_canceled = false;
+    tpm_pt->op_executing = true;
+    if (selftest_done) {
+        *selftest_done = false;
+        is_selftest = tpm_util_is_selftest(in, in_len);
+    }
+
+    ret = qio_channel_write(tpm_pt->data_ioc, (char *)in, in_len, &err);
+    if (ret != in_len || err) {
+        if (!tpm_pt->op_canceled || errno != ECANCELED) {
+            error_report("tpm-emulator: error while transmitting data "
+                         "to TPM: %s", err ? error_get_pretty(err) : "");
+            error_free(err);
+        }
+        goto err_exit;
+    }
+
+    tpm_pt->op_executing = false;
+
+    ret = qio_channel_read(tpm_pt->data_ioc, (char *)out, out_len, &err);
+    if (ret < 0 || err) {
+        if (!tpm_pt->op_canceled || errno != ECANCELED) {
+            error_report("tpm-emulator: error while reading data from "
+                         "TPM: %s", err ? error_get_pretty(err) : "");
+            error_free(err);
+        }
+    } else if (ret >= sizeof(*hdr)) {
+        hdr = (struct tpm_resp_hdr *)out;
+    }
+
+    if (!hdr || be32_to_cpu(hdr->len) != ret) {
+        error_report("tpm-emulator: received invalid response "
+                     "packet from TPM with length :%ld", ret);
+        ret = -1;
+        goto err_exit;
+    }
+
+    if (is_selftest) {
+        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
+    }
+
+    return 0;
+
+err_exit:
+    if (ret < 0) {
+        tpm_util_write_fatal_error_response(out, out_len);
+    }
+
+    tpm_pt->op_executing = false;
+
+    return ret;
+}
+
+static int tpm_emulator_set_locality(TPMEmulator *tpm_pt, uint8_t locty_number)
+{
+    ptm_loc loc;
+
+    DPRINTF("%s : locality: 0x%x", __func__, locty_number);
+
+    if (tpm_pt->cur_locty_number != locty_number) {
+        DPRINTF("setting locality : 0x%x", locty_number);
+        loc.u.req.loc = locty_number;
+        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SET_LOCALITY, &loc,
+                             sizeof(loc), sizeof(loc)) < 0) {
+            error_report("tpm-emulator: could not set locality : %s",
+                         strerror(errno));
+            return -1;
+        }
+        loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result);
+        if (loc.u.resp.tpm_result != 0) {
+            error_report("tpm-emulator: TPM result for set locality : 0x%x",
+                         loc.u.resp.tpm_result);
+            return -1;
+        }
+        tpm_pt->cur_locty_number = locty_number;
+    }
+    return 0;
+}
+
+static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
+{
+    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
+    TPMLocality *locty = NULL;
+    bool selftest_done = false;
+
+    DPRINTF("processing command type %d", cmd);
+
+    switch (cmd) {
+    case TPM_BACKEND_CMD_PROCESS_CMD:
+        qemu_mutex_lock(&tpm_pt->state_lock);
+        locty = tb->tpm_state->locty_data;
+        if (tpm_emulator_set_locality(tpm_pt,
+                                      tb->tpm_state->locty_number) < 0) {
+            tpm_util_write_fatal_error_response(locty->r_buffer.buffer,
+                                           locty->r_buffer.size);
+        } else {
+            tpm_emulator_unix_tx_bufs(tpm_pt, locty->w_buffer.buffer,
+                                              locty->w_offset,
+                                              locty->r_buffer.buffer,
+                                              locty->r_buffer.size,
+                                              &selftest_done);
+        }
+
+        tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number,
+                               selftest_done);
+        qemu_mutex_unlock(&tpm_pt->state_lock);
+
+        break;
+    case TPM_BACKEND_CMD_INIT:
+    case TPM_BACKEND_CMD_END:
+    case TPM_BACKEND_CMD_TPM_RESET:
+        /* nothing to do */
+        break;
+    }
+}
+
+/*
+ * Gracefully shut down the external unixio TPM
+ */
+static void tpm_emulator_shutdown(TPMEmulator *tpm_pt)
+{
+    ptm_res res;
+
+    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SHUTDOWN, &res, 0,
+                         sizeof(res)) < 0) {
+        error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s",
+                     strerror(errno));
+    } else if (res != 0) {
+        error_report("tpm-emulator: TPM result for sutdown: 0x%x",
+                     be32_to_cpu(res));
+    }
+}
+
+static int tpm_emulator_probe_caps(TPMEmulator *tpm_pt)
+{
+    DPRINTF("%s", __func__);
+    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_GET_CAPABILITY,
+                         &tpm_pt->caps, 0, sizeof(tpm_pt->caps)) < 0) {
+        error_report("tpm-emulator: probing failed : %s", strerror(errno));
+        return -1;
+    }
+
+    tpm_pt->caps = be64_to_cpu(tpm_pt->caps);
+
+    DPRINTF("capbilities : 0x%lx", tpm_pt->caps);
+
+    return 0;
+}
+
+static int tpm_emulator_check_caps(TPMEmulator *tpm_pt)
+{
+    ptm_cap caps = 0;
+    const char *tpm = NULL;
+
+    /* 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-emulator: TPM version has not been set");
+        return -1;
+    }
+
+    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, caps)) {
+        error_report("tpm-emulator: TPM does not implement minimum set of "
+                     "required capabilities for TPM %s (0x%x)", tpm, (int)caps);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int tpm_emulator_startup_tpm(TPMBackend *tb)
+{
+    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
+    ptm_init init;
+    ptm_res res;
+
+    DPRINTF("%s", __func__);
+    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_INIT, &init, sizeof(init),
+                         sizeof(init)) < 0) {
+        error_report("tpm-emulator: could not send INIT: %s",
+                     strerror(errno));
+        goto err_exit;
+    }
+
+    res = be32_to_cpu(init.u.resp.tpm_result);
+    if (res) {
+        error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res);
+        goto err_exit;
+    }
+    return 0;
+
+err_exit:
+    tpm_pt->had_startup_error = true;
+    return -1;
+}
+
+static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
+{
+    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
+    ptm_est est;
+
+    DPRINTF("%s", __func__);
+    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_GET_TPMESTABLISHED, &est, 0,
+                         sizeof(est)) < 0) {
+        error_report("tpm-emulator: Could not get the TPM established flag: %s",
+                     strerror(errno));
+        return false;
+    }
+    DPRINTF("established flag: %0x", est.u.resp.bit);
+
+    return (est.u.resp.bit != 0);
+}
+
+static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
+                                                   uint8_t locty)
+{
+    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
+    ptm_reset_est reset_est;
+    ptm_res res;
+
+    /* only a TPM 2.0 will support this */
+    if (tpm_pt->tpm_version == TPM_VERSION_2_0) {
+        reset_est.u.req.loc = tpm_pt->cur_locty_number;
+
+        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_RESET_TPMESTABLISHED,
+                                 &reset_est, sizeof(reset_est),
+                                 sizeof(reset_est)) < 0) {
+            error_report("tpm-emulator: Could not reset the establishment bit: "
+                          "%s", strerror(errno));
+            return -1;
+        }
+
+        res = be32_to_cpu(reset_est.u.resp.tpm_result);
+        if (res) {
+            error_report("tpm-emulator: TPM result for rest establixhed flag: "
+                         "0x%x", res);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static bool tpm_emulator_had_startup_error(TPMBackend *tb)
+{
+    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
+
+    return tpm_pt->had_startup_error;
+}
+
+static void tpm_emulator_cancel_cmd(TPMBackend *tb)
+{
+    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
+    ptm_res res;
+
+    /*
+     * As of Linux 3.7 the tpm_tis driver does not properly cancel
+     * commands on all TPM manufacturers' TPMs.
+     * Only cancel if we're busy so we don't cancel someone else's
+     * command, e.g., a command executed on the host.
+     */
+    if (tpm_pt->op_executing) {
+        if (TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, PTM_CAP_CANCEL_TPM_CMD)) {
+            if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_CANCEL_TPM_CMD,
+                                     &res, 0, sizeof(res)) < 0) {
+                error_report("tpm-emulator: Could not cancel command: %s",
+                             strerror(errno));
+            } else if (res != 0) {
+                error_report("tpm-emulator: Failed to cancel TPM: 0x%x",
+                             be32_to_cpu(res));
+            } else {
+                tpm_pt->op_canceled = true;
+            }
+        }
+    }
+}
+
+static void tpm_emulator_reset(TPMBackend *tb)
+{
+    DPRINTF("%s", __func__);
+
+    tpm_emulator_cancel_cmd(tb);
+}
+
+static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
+{
+    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
+
+    return tpm_pt->tpm_version;
+}
+
+static void tpm_emulator_block_migration(TPMEmulator *tpm_pt)
+{
+    Error *err = NULL;
+
+    error_setg(&tpm_pt->migration_blocker,
+               "Migration disabled: TPM emulator not yet migratable");
+    migrate_add_blocker(tpm_pt->migration_blocker, &err);
+    if (err) {
+        error_free(err);
+        error_free(tpm_pt->migration_blocker);
+        tpm_pt->migration_blocker = NULL;
+    }
+}
+
+static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_pt)
+{
+    ptm_res res;
+    Error *err = NULL;
+    int fds[2] = { -1, -1 };
+
+    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) {
+        error_report("tpm-emulator: Failed to create socketpair");
+        return -1;
+    }
+
+    qemu_chr_fe_set_msgfds(&tpm_pt->ctrl_dev, fds + 1, 1);
+
+    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SET_DATAFD, &res, 0,
+                    sizeof(res)) || res != 0) {
+        error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s",
+                     strerror(errno));
+        goto err_exit;
+    }
+
+    tpm_pt->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
+    if (err) {
+        error_report("tpm-emulator: Failed to create io channel : %s",
+                       error_get_pretty(err));
+        error_free(err);
+        goto err_exit;
+    }
+
+    return 0;
+
+err_exit:
+    closesocket(fds[0]);
+    closesocket(fds[1]);
+    return -1;
+}
+
+static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_pt, QemuOpts *opts)
+{
+    const char *value;
+
+    value = qemu_opt_get(opts, "chardev");
+    if (value) {
+        Error *err = NULL;
+        Chardev *dev = qemu_chr_find(value);
+
+        tpm_pt->options->chardev = g_strdup(value);
+
+        if (!dev || !qemu_chr_fe_init(&tpm_pt->ctrl_dev, dev, &err)) {
+            error_report("tpm-emulator: No valid chardev found at '%s': %s",
+                         value, err ? error_get_pretty(err) : "");
+            error_free(err);
+            goto err;
+        }
+    }
+
+    if (tpm_emulator_prepare_data_fd(tpm_pt) < 0) {
+        goto err;
+    }
+
+    /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used
+     * by passthrough driver, which not yet using GIOChannel.
+     */
+    if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_pt->data_ioc)->fd,
+                             &tpm_pt->tpm_version)) {
+        error_report("'%s' is not emulating TPM device. Error: %s",
+                      tpm_pt->options->chardev, strerror(errno));
+        goto err;
+    }
+
+    DPRINTF("TPM Version %s", tpm_pt->tpm_version == TPM_VERSION_1_2 ? "1.2" :
+             (tpm_pt->tpm_version == TPM_VERSION_2_0 ?  "2.0" : "Unspecified"));
+
+    if (tpm_emulator_probe_caps(tpm_pt) ||
+        tpm_emulator_check_caps(tpm_pt)) {
+        goto err;
+    }
+
+    tpm_emulator_block_migration(tpm_pt);
+
+    return 0;
+
+err:
+    DPRINT("Startup error\n");
+    return -1;
+}
+
+static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)
+{
+    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;
+    }
+
+    return tb;
+
+err_exit:
+    object_unref(OBJECT(tb));
+
+    return NULL;
+}
+
+static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
+{
+    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
+    TpmTypeOptions *options = NULL;
+    TPMEmulatorOptions *eoptions = NULL;
+
+    eoptions = g_new0(TPMEmulatorOptions, 1);
+    if (!eoptions) {
+        return NULL;
+    }
+    DPRINTF("%s", __func__);
+
+    eoptions->chardev = g_strdup(tpm_pt->options->chardev);
+    options = g_new0(TpmTypeOptions, 1);
+    if (!options) {
+        qapi_free_TPMEmulatorOptions(eoptions);
+        return NULL;
+    }
+
+    options->type = TPM_TYPE_EMULATOR;
+    options->u.emulator.data = eoptions;
+
+    return options;
+}
+
+static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
+    TPM_STANDARD_CMDLINE_OPTS,
+    {
+        .name = "chardev",
+        .type = QEMU_OPT_STRING,
+        .help = "Character device to use for out-of-band control messages",
+    },
+    { /* end of list */ },
+};
+
+static const TPMDriverOps tpm_emulator_driver = {
+    .type                     = TPM_TYPE_EMULATOR,
+    .opts                     = tpm_emulator_cmdline_opts,
+    .desc                     = "TPM emulator backend driver",
+
+    .create                   = tpm_emulator_create,
+    .startup_tpm              = tpm_emulator_startup_tpm,
+    .reset                    = tpm_emulator_reset,
+    .had_startup_error        = tpm_emulator_had_startup_error,
+    .cancel_cmd               = tpm_emulator_cancel_cmd,
+    .get_tpm_established_flag = tpm_emulator_get_tpm_established_flag,
+    .reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag,
+    .get_tpm_version          = tpm_emulator_get_tpm_version,
+    .get_tpm_options          = tpm_emulator_get_tpm_options,
+};
+
+static void tpm_emulator_inst_init(Object *obj)
+{
+    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
+
+    DPRINTF("%s", __func__);
+    tpm_pt->options = g_new0(TPMEmulatorOptions, 1);
+    tpm_pt->op_executing = tpm_pt->op_canceled = false;
+    tpm_pt->had_startup_error = false;
+    tpm_pt->cur_locty_number = ~0;
+    qemu_mutex_init(&tpm_pt->state_lock);
+}
+
+static void tpm_emulator_inst_finalize(Object *obj)
+{
+    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
+
+    tpm_emulator_cancel_cmd(TPM_BACKEND(obj));
+    tpm_emulator_shutdown(tpm_pt);
+
+    if (tpm_pt->data_ioc) {
+        qio_channel_close(tpm_pt->data_ioc, NULL);
+    }
+
+    qemu_chr_fe_deinit(&tpm_pt->ctrl_dev, false);
+
+    if (tpm_pt->options) {
+        qapi_free_TPMEmulatorOptions(tpm_pt->options);
+    }
+
+    if (tpm_pt->migration_blocker) {
+        migrate_del_blocker(tpm_pt->migration_blocker);
+        error_free(tpm_pt->migration_blocker);
+    }
+}
+
+static void tpm_emulator_class_init(ObjectClass *klass, void *data)
+{
+    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
+    tbc->ops = &tpm_emulator_driver;
+    tbc->handle_request = tpm_emulator_handle_request;
+}
+
+static const TypeInfo tpm_emulator_info = {
+    .name = TYPE_TPM_EMULATOR,
+    .parent = TYPE_TPM_BACKEND,
+    .instance_size = sizeof(TPMEmulator),
+    .class_init = tpm_emulator_class_init,
+    .instance_init = tpm_emulator_inst_init,
+    .instance_finalize = tpm_emulator_inst_finalize,
+};
+
+static void tpm_emulator_register(void)
+{
+    type_register_static(&tpm_emulator_info);
+    tpm_register_driver(&tpm_emulator_driver);
+}
+
+type_init(tpm_emulator_register)
diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
new file mode 100644
index 0000000..33564b1
--- /dev/null
+++ b/hw/tpm/tpm_ioctl.h
@@ -0,0 +1,246 @@
+/*
+ * tpm_ioctl.h
+ *
+ * (c) Copyright IBM Corporation 2014, 2015.
+ *
+ * This file is licensed under the terms of the 3-clause BSD license
+ */
+#ifndef _TPM_IOCTL_H_
+#define _TPM_IOCTL_H_
+
+#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 ptm_res as the first element.
+ * ptm_res corresponds to the error code of a command executed by the TPM.
+ */
+
+typedef uint32_t ptm_res;
+
+/* PTM_GET_TPMESTABLISHED: get the establishment bit */
+struct ptm_est {
+    union {
+        struct {
+            ptm_res tpm_result;
+            unsigned char bit; /* TPM established bit */
+        } resp; /* response */
+    } u;
+};
+
+/* PTM_RESET_TPMESTABLISHED: reset establishment bit */
+struct ptm_reset_est {
+    union {
+        struct {
+            uint8_t loc; /* locality to use */
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+        } resp; /* response */
+    } u;
+};
+
+/* PTM_INIT */
+struct ptm_init {
+    union {
+        struct {
+            uint32_t init_flags; /* see definitions below */
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+        } resp; /* response */
+    } u;
+};
+
+/* above init_flags */
+#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0)
+    /* delete volatile state file after reading it */
+
+/* PTM_SET_LOCALITY */
+struct ptm_loc {
+    union {
+        struct {
+            uint8_t loc; /* locality to set */
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+        } resp; /* response */
+    } u;
+};
+
+/* PTM_HASH_DATA: hash given data */
+struct ptm_hdata {
+    union {
+        struct {
+            uint32_t length;
+            uint8_t data[4096];
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+        } resp; /* response */
+    } 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 PTM_STATE_BLOB_SIZE (3 * 1024)
+
+/*
+ * The following is the data structure to get state blobs from the TPM.
+ * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads
+ * with this ioctl and with adjusted offset are necessary. All bytes
+ * must be transferred and the transfer is done once the last byte has been
+ * returned.
+ * It is possible to use the read() interface for reading the data; however, the
+ * first bytes of the state blob will be part of the response to the ioctl(); a
+ * subsequent read() is only necessary if the total length (totlength) exceeds
+ * the number of received bytes. seek() is not supported.
+ */
+struct ptm_getstate {
+    union {
+        struct {
+            uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */
+            uint32_t type;        /* which blob to pull */
+            uint32_t offset;      /* offset from where to read */
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+            uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */
+            uint32_t totlength;   /* total length that will be transferred */
+            uint32_t length;      /* number of bytes in following buffer */
+            uint8_t  data[PTM_STATE_BLOB_SIZE];
+        } resp; /* response */
+    } 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 PTM_STATE_FLAG_DECRYPTED     1 /* on input:  get decrypted state */
+#define PTM_STATE_FLAG_ENCRYPTED     2 /* on output: state is encrypted */
+
+/*
+ * The following is the data structure to set state blobs in the TPM.
+ * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple
+ * 'writes' using this ioctl are necessary. The last packet is indicated
+ * by the length being smaller than the PTM_STATE_BLOB_SIZE.
+ * The very first packet may have a length indicator of '0' enabling
+ * a write() with all the bytes from a buffer. If the write() interface
+ * is used, a final ioctl with a non-full buffer must be made to indicate
+ * that all data were transferred (a write with 0 bytes would not work).
+ */
+struct ptm_setstate {
+    union {
+        struct {
+            uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */
+            uint32_t type;        /* which blob to set */
+            uint32_t length;      /* length of the data;
+                                     use 0 on the first packet to
+                                     transfer using write() */
+            uint8_t data[PTM_STATE_BLOB_SIZE];
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+        } resp; /* response */
+    } u;
+};
+
+/*
+ * PTM_GET_CONFIG: Data structure to get runtime configuration information
+ * such as which keys are applied.
+ */
+struct ptm_getconfig {
+    union {
+        struct {
+            ptm_res tpm_result;
+            uint32_t flags;
+        } resp; /* response */
+    } u;
+};
+
+#define PTM_CONFIG_FLAG_FILE_KEY        0x1
+#define PTM_CONFIG_FLAG_MIGRATION_KEY   0x2
+
+
+typedef uint64_t ptm_cap;
+typedef struct ptm_est ptm_est;
+typedef struct ptm_reset_est ptm_reset_est;
+typedef struct ptm_loc ptm_loc;
+typedef struct ptm_hdata ptm_hdata;
+typedef struct ptm_init ptm_init;
+typedef struct ptm_getstate ptm_getstate;
+typedef struct ptm_setstate ptm_setstate;
+typedef struct ptm_getconfig ptm_getconfig;
+
+/* 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)
+#define PTM_CAP_GET_CONFIG         (1 << 11)
+#define PTM_CAP_SET_DATAFD         (1 << 12)
+
+enum {
+    PTM_GET_CAPABILITY     = _IOR('P', 0, ptm_cap),
+    PTM_INIT               = _IOWR('P', 1, ptm_init),
+    PTM_SHUTDOWN           = _IOR('P', 2, ptm_res),
+    PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est),
+    PTM_SET_LOCALITY       = _IOWR('P', 4, ptm_loc),
+    PTM_HASH_START         = _IOR('P', 5, ptm_res),
+    PTM_HASH_DATA          = _IOWR('P', 6, ptm_hdata),
+    PTM_HASH_END           = _IOR('P', 7, ptm_res),
+    PTM_CANCEL_TPM_CMD     = _IOR('P', 8, ptm_res),
+    PTM_STORE_VOLATILE     = _IOR('P', 9, ptm_res),
+    PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est),
+    PTM_GET_STATEBLOB      = _IOWR('P', 11, ptm_getstate),
+    PTM_SET_STATEBLOB      = _IOWR('P', 12, ptm_setstate),
+    PTM_STOP               = _IOR('P', 13, ptm_res),
+    PTM_GET_CONFIG         = _IOR('P', 14, ptm_getconfig),
+    PTM_SET_DATAFD         = _IOR('P', 15, ptm_res),
+};
+
+/*
+ * Commands used by the non-CUSE TPMs
+ *
+ * All messages container big-endian data.
+ *
+ * The return messages only contain the 'resp' part of the unions
+ * in the data structures above. Besides that the limits in the
+ * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data
+ * and ptm_set_state:u.req.data) are 0xffffffff.
+ */
+enum {
+    CMD_GET_CAPABILITY = 1,
+    CMD_INIT,
+    CMD_SHUTDOWN,
+    CMD_GET_TPMESTABLISHED,
+    CMD_SET_LOCALITY,
+    CMD_HASH_START,
+    CMD_HASH_DATA,
+    CMD_HASH_END,
+    CMD_CANCEL_TPM_CMD,
+    CMD_STORE_VOLATILE,
+    CMD_RESET_TPMESTABLISHED,
+    CMD_GET_STATEBLOB,
+    CMD_SET_STATEBLOB,
+    CMD_STOP,
+    CMD_GET_CONFIG,
+    CMD_SET_DATAFD
+};
+
+#endif /* _TPM_IOCTL_H */
diff --git a/qapi/tpm.json b/qapi/tpm.json
index e8b2d8d..7093f26 100644
--- a/qapi/tpm.json
+++ b/qapi/tpm.json
@@ -39,10 +39,12 @@
 # An enumeration of TPM types
 #
 # @passthrough: TPM passthrough type
+# @emulator: Software Emulator TPM type
+#            Since: 2.11
 #
 # Since: 1.5
 ##
-{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }
+{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] }
 
 ##
 # @query-tpm-types:
@@ -56,7 +58,7 @@
 # Example:
 #
 # -> { "execute": "query-tpm-types" }
-# <- { "return": [ "passthrough" ] }
+# <- { "return": [ "passthrough", "emulator" ] }
 #
 ##
 { 'command': 'query-tpm-types', 'returns': ['TpmType'] }
@@ -77,16 +79,29 @@
                                              '*cancel-path' : 'str'} }
 
 ##
+# @TPMEmulatorOptions:
+#
+# Information about the TPM emulator type
+#
+# @chardev: Name of a unix socket chardev
+#
+# Since: 2.11
+##
+{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } }
+
+##
 # @TpmTypeOptions:
 #
 # A union referencing different TPM backend types' configuration options
 #
 # @type: 'passthrough' The configuration options for the TPM passthrough type
+#        'emulator' The configuration options for TPM emulator backend type
 #
 # Since: 1.5
 ##
 { 'union': 'TpmTypeOptions',
-   'data': { 'passthrough' : 'TPMPassthroughOptions' } }
+   'data': { 'passthrough' : 'TPMPassthroughOptions',
+             'emulator': 'TPMEmulatorOptions' } }
 
 ##
 # @TPMInfo:
diff --git a/qemu-options.hx b/qemu-options.hx
index 77859a2..1e93e53 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3121,7 +3121,9 @@ 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 emulator,id=id,chardev=dev\n"
+    "                configure the TPM device using chardev backend\n",
     QEMU_ARCH_ALL)
 STEXI
 
@@ -3130,8 +3132,8 @@ The general form of a TPM device option is:
 
 @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
 @findex -tpmdev
-Backend type must be:
-@option{passthrough}.
+Backend type must be either one of the following:
+@option{passthrough}, @option{emulator}.
 
 The specific backend type will determine the applicable options.
 The @code{-tpmdev} option creates the TPM backend and requires a
@@ -3181,6 +3183,20 @@ 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.
 
+@item -tpmdev emulator, id=@var{id}, chardev=@var{dev}
+
+(Linux-host only) Enable access to a TPM emulator using Unix domain socket based
+chardev backend.
+
+@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server.
+
+To create a TPM emulator backend device with chardev socket backend:
+@example
+
+-chardev socket,id=chrtpm,path=/tmp/swtpm-ctrl -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
+
+@end example
+
 @end table
 
 ETEXI
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v7 5/8] tmp backend: Add new api to read backend TpmInfo
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 5/8] tmp backend: Add new api to read backend TpmInfo Amarnath Valluri
@ 2017-09-22 15:35   ` Marc-André Lureau
  2017-09-25 12:01     ` Valluri, Amarnath
  2017-09-27 12:16     ` [Qemu-devel] [PATCH v8 " Amarnath Valluri
  0 siblings, 2 replies; 23+ messages in thread
From: Marc-André Lureau @ 2017-09-22 15:35 UTC (permalink / raw)
  To: Amarnath Valluri; +Cc: QEMU, Stefan Berger, Dr. David Alan Gilbert

Hi

On Fri, Sep 22, 2017 at 2:33 PM, Amarnath Valluri
<amarnath.valluri@intel.com> wrote:
> TPM configuration options are backend implementation details and shall not be
> part of base TPMBackend object, and these shall not be accessed directly outside
> of the class, hence added a new interface method, get_tpm_options() to
> TPMDriverOps., which shall be implemented by the derived classes to return
> configured tpm options.
>
> A new tpm backend api - tpm_backend_query_tpm() which uses _get_tpm_options() to
> prepare TpmInfo.
>
> Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
> Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> ---
>  backends/tpm.c               | 22 +++++++++++++--
>  hmp.c                        |  7 ++---
>  hw/tpm/tpm_passthrough.c     | 64 +++++++++++++++++++++++++++++++-------------
>  include/sysemu/tpm_backend.h | 25 +++++++++++++++--
>  tpm.c                        | 32 +---------------------
>  5 files changed, 93 insertions(+), 57 deletions(-)
>
> diff --git a/backends/tpm.c b/backends/tpm.c
> index c409a46..6ade9e4 100644
> --- a/backends/tpm.c
> +++ b/backends/tpm.c
> @@ -138,6 +138,26 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
>      return k->ops->get_tpm_version(s);
>  }
>
> +TpmTypeOptions *tpm_backend_get_tpm_options(TPMBackend *s)
> +{
> +    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
> +
> +    assert(k->ops->get_tpm_options);
> +
> +    return k->ops->get_tpm_options(s);
> +}
> +

Why make this public API?

> +TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
> +{
> +    TPMInfo *info = g_new0(TPMInfo, 1);
> +
> +    info->id = g_strdup(s->id);
> +    info->model = s->fe_model;
> +    info->options = tpm_backend_get_tpm_options(s);

Callback could be called directly from here.

> +
> +    return info;
> +}
> +
>  static bool tpm_backend_prop_get_opened(Object *obj, Error **errp)
>  {
>      TPMBackend *s = TPM_BACKEND(obj);
> @@ -192,8 +212,6 @@ static void tpm_backend_instance_finalize(Object *obj)
>      TPMBackend *s = TPM_BACKEND(obj);
>
>      g_free(s->id);
> -    g_free(s->path);
> -    g_free(s->cancel_path);
>      tpm_backend_thread_end(s);
>  }
>
> diff --git a/hmp.c b/hmp.c
> index 0fb2bc7..cf62b2e 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -31,6 +31,7 @@
>  #include "qapi/qmp/qerror.h"
>  #include "qapi/string-input-visitor.h"
>  #include "qapi/string-output-visitor.h"
> +#include "qapi/util.h"
>  #include "qapi-visit.h"
>  #include "qom/object_interfaces.h"
>  #include "ui/console.h"
> @@ -1012,10 +1013,10 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
>                         c, TpmModel_str(ti->model));
>
>          monitor_printf(mon, "  \\ %s: type=%s",
> -                       ti->id, TpmTypeOptionsKind_str(ti->options->type));
> +                       ti->id, TpmType_str(ti->options->type));
>

Why this change? It is still a TpmTypeOptionsKind no?

>          switch (ti->options->type) {
> -        case TPM_TYPE_OPTIONS_KIND_PASSTHROUGH:
> +        case TPM_TYPE_PASSTHROUGH:
>              tpo = ti->options->u.passthrough.data;
>              monitor_printf(mon, "%s%s%s%s",
>                             tpo->has_path ? ",path=" : "",
> @@ -1023,7 +1024,7 @@ 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__MAX:
> +        case TPM_TYPE__MAX:
>              break;
>          }
>          monitor_printf(mon, "\n");
> diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
> index a0459a6..fb7dad8 100644
> --- a/hw/tpm/tpm_passthrough.c
> +++ b/hw/tpm/tpm_passthrough.c
> @@ -49,7 +49,8 @@
>  struct TPMPassthruState {
>      TPMBackend parent;
>
> -    char *tpm_dev;
> +    TPMPassthroughOptions *options;
> +    const char *tpm_dev;
>      int tpm_fd;
>      bool tpm_executing;
>      bool tpm_op_canceled;
> @@ -308,15 +309,14 @@ static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
>   * in Documentation/ABI/stable/sysfs-class-tpm.
>   * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
>   */
> -static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
> +static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)

I suspect this change could be done in an earlier/separate patch

>  {
> -    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
>      int fd = -1;
>      char *dev;
>      char path[PATH_MAX];
>
> -    if (tb->cancel_path) {
> -        fd = qemu_open(tb->cancel_path, O_WRONLY);
> +    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",
>                           strerror(errno));
> @@ -331,7 +331,7 @@ static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
>                       dev) < sizeof(path)) {
>              fd = qemu_open(path, O_WRONLY);
>              if (fd >= 0) {
> -                tb->cancel_path = g_strdup(path);
> +                tpm_pt->options->cancel_path = g_strdup(path);
>              } else {
>                  error_report("tpm_passthrough: Could not open TPM cancel "
>                               "path %s : %s", path, strerror(errno));
> @@ -351,17 +351,18 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
>      const char *value;
>
>      value = qemu_opt_get(opts, "cancel-path");
> -    tb->cancel_path = g_strdup(value);
> +    if (value) {
> +        tpm_pt->options->cancel_path = g_strdup(value);
> +        tpm_pt->options->has_cancel_path = true;
> +    }
>
>      value = qemu_opt_get(opts, "path");
> -    if (!value) {
> -        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
> +    if (value) {
> +        tpm_pt->options->has_path = true;
> +        tpm_pt->options->path = g_strdup(value);
>      }
>
> -    tpm_pt->tpm_dev = g_strdup(value);
> -
> -    tb->path = g_strdup(tpm_pt->tpm_dev);
> -
> +    tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE;

Isn't that duplicated with tpm_pt->options->path ? And different values...

>      tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
>      if (tpm_pt->tpm_fd < 0) {
>          error_report("Cannot access TPM device using '%s': %s",
> @@ -382,10 +383,8 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
>      tpm_pt->tpm_fd = -1;
>
>   err_free_parameters:
> -    g_free(tb->path);
> -    tb->path = NULL;
> -
> -    g_free(tpm_pt->tpm_dev);
> +    qapi_free_TPMPassthroughOptions(tpm_pt->options);
> +    tpm_pt->options = NULL;
>      tpm_pt->tpm_dev = NULL;
>
>      return 1;
> @@ -403,7 +402,7 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
>          goto err_exit;
>      }
>
> -    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
> +    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
>      if (tpm_pt->cancel_fd < 0) {
>          goto err_exit;
>      }
> @@ -416,6 +415,31 @@ err_exit:
>      return NULL;
>  }
>
> +static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
> +    TpmTypeOptions *options = NULL;
> +    TPMPassthroughOptions *poptions = NULL;
> +
> +    poptions = g_new0(TPMPassthroughOptions, 1);
> +
> +    if (tpm_pt->options->has_path) {
> +        poptions->has_path = true;
> +        poptions->path = g_strdup(tpm_pt->options->path);
> +    }
> +
> +    if (tpm_pt->options->has_cancel_path) {
> +        poptions->has_cancel_path = true;
> +        poptions->cancel_path = g_strdup(tpm_pt->options->cancel_path);
> +    }
> +

That looks like a job for QAPI_CLONE.

> +    options = g_new0(TpmTypeOptions, 1);
> +    options->type = TPM_TYPE_PASSTHROUGH;
> +    options->u.passthrough.data = poptions;
> +
> +    return options;
> +}
> +
>  static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
>      TPM_STANDARD_CMDLINE_OPTS,
>      {
> @@ -443,12 +467,14 @@ static const TPMDriverOps tpm_passthrough_driver = {
>      .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,
> +    .get_tpm_options          = tpm_passthrough_get_tpm_options,
>  };
>
>  static void tpm_passthrough_inst_init(Object *obj)
>  {
>      TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
>
> +    tpm_pt->options = g_new0(TPMPassthroughOptions, 1);

I don't see much point in allocating the struct. Could it just be an
flat member instead?

>      tpm_pt->tpm_fd = -1;
>      tpm_pt->cancel_fd = -1;
>  }
> @@ -461,7 +487,7 @@ static void tpm_passthrough_inst_finalize(Object *obj)
>
>      qemu_close(tpm_pt->tpm_fd);
>      qemu_close(tpm_pt->cancel_fd);
> -    g_free(tpm_pt->tpm_dev);
> +    qapi_free_TPMPassthroughOptions(tpm_pt->options);
>  }
>
>  static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
> diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
> index 13311cf..809eec2 100644
> --- a/include/sysemu/tpm_backend.h
> +++ b/include/sysemu/tpm_backend.h
> @@ -48,10 +48,9 @@ struct TPMBackend {
>      GThreadPool *thread_pool;
>      TPMRecvDataCB *recv_data_callback;
>
> +    /* <public> */
>      char *id;
>      enum TpmModel fe_model;
> -    char *path;
> -    char *cancel_path;
>
>      QLIST_ENTRY(TPMBackend) list;
>  };
> @@ -97,6 +96,8 @@ struct TPMDriverOps {
>      int (*reset_tpm_established_flag)(TPMBackend *t, uint8_t locty);
>
>      TPMVersion (*get_tpm_version)(TPMBackend *t);
> +
> +    TpmTypeOptions *(*get_tpm_options)(TPMBackend *t);
>  };
>
>
> @@ -215,6 +216,26 @@ void tpm_backend_open(TPMBackend *s, Error **errp);
>   */
>  TPMVersion tpm_backend_get_tpm_version(TPMBackend *s);
>
> +/**
> + * tpm_backend_get_tpm_options:
> + * @s: the backend
> + *
> + * Get the backend configuration options
> + *
> + * Returns newly allocated TpmTypeOptions
> + */
> +TpmTypeOptions *tpm_backend_get_tpm_options(TPMBackend *s);
> +
> +/**
> + * tpm_backend_query_tpm:
> + * @s: the backend
> + *
> + * Query backend tpm info
> + *
> + * Returns newly allocated TPMInfo
> + */
> +TPMInfo *tpm_backend_query_tpm(TPMBackend *s);
> +
>  TPMBackend *qemu_find_tpm(const char *id);
>
>  const TPMDriverOps *tpm_get_backend_driver(const char *type);
> diff --git a/tpm.c b/tpm.c
> index db14849..3b8c7ed 100644
> --- a/tpm.c
> +++ b/tpm.c
> @@ -202,36 +202,6 @@ static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type)
>      return be_drivers[type];
>  }
>
> -static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
> -{
> -    TPMInfo *res = g_new0(TPMInfo, 1);
> -    TPMPassthroughOptions *tpo;
> -
> -    res->id = g_strdup(drv->id);
> -    res->model = drv->fe_model;
> -    res->options = g_new0(TpmTypeOptions, 1);
> -
> -    switch (tpm_backend_get_type(drv)) {
> -    case TPM_TYPE_PASSTHROUGH:
> -        res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
> -        tpo = g_new0(TPMPassthroughOptions, 1);
> -        res->options->u.passthrough.data = tpo;
> -        if (drv->path) {
> -            tpo->path = g_strdup(drv->path);
> -            tpo->has_path = true;
> -        }
> -        if (drv->cancel_path) {
> -            tpo->cancel_path = g_strdup(drv->cancel_path);
> -            tpo->has_cancel_path = true;
> -        }
> -        break;
> -    case TPM_TYPE__MAX:
> -        break;
> -    }
> -
> -    return res;
> -}
> -
>  /*
>   * Walk the list of active TPM backends and collect information about them
>   * following the schema description in qapi-schema.json.
> @@ -246,7 +216,7 @@ TPMInfoList *qmp_query_tpm(Error **errp)
>              continue;
>          }
>          info = g_new0(TPMInfoList, 1);
> -        info->value = qmp_query_tpm_inst(drv);
> +        info->value = tpm_backend_query_tpm(drv);
>
>          if (!cur_item) {
>              head = cur_item = info;
> --
> 2.7.4
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator Amarnath Valluri
@ 2017-09-22 16:23   ` Stefan Berger
  2017-09-24 18:52   ` Marc-André Lureau
  1 sibling, 0 replies; 23+ messages in thread
From: Stefan Berger @ 2017-09-22 16:23 UTC (permalink / raw)
  To: Amarnath Valluri, qemu-devel
  Cc: Dr. David Alan Gilbert, Markus Armbruster, Marc-André Lureau

On 09/22/2017 08:33 AM, Amarnath Valluri wrote:
> This change introduces a new TPM backend driver that can communicate with
> swtpm(software TPM emulator) using unix domain socket interface. QEMU talks to
> TPM emulator using socket based chardev backend device.

talks to the TPM emulator using QEMU's socket-based chardev backend device.

>
> Swtpm uses two Unix sockets for communications, one for plain TPM commands and
> responses, and one for out-of-band control messages. QEMU passes data socket
> been used over the control channel.

QEMU passes the data socket to be used over the control channel.

Some comments below. I'll give it a try once we merge the fd passing 
into swtpm.


>
> The swtpm and associated tools can be found here:
>      https://github.com/stefanberger/swtpm
>
> The swtpm's control channel protocol specification can be found here:
>      https://github.com/stefanberger/swtpm/wiki/Control-Channel-Specification
>
> Usage:
>      # setup TPM state directory
>      mkdir /tmp/mytpm
>      chown -R tss:root /tmp/mytpm
>      /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek
>
>      # Ask qemu to use TPM emulator with given tpm state directory
>      qemu-system-x86_64 \
>          [...] \
>          -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \
>          -tpmdev emulator,id=tpm0,chardev=chrtpm \
>          -device tpm-tis,tpmdev=tpm0 \
>          [...]
>
> Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
> ---
>   configure             |  15 +-
>   hmp.c                 |   5 +
>   hw/tpm/Makefile.objs  |   1 +
>   hw/tpm/tpm_emulator.c | 649 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   hw/tpm/tpm_ioctl.h    | 246 +++++++++++++++++++
>   qapi/tpm.json         |  21 +-
>   qemu-options.hx       |  22 +-
>   7 files changed, 950 insertions(+), 9 deletions(-)
>   create mode 100644 hw/tpm/tpm_emulator.c
>   create mode 100644 hw/tpm/tpm_ioctl.h
>
> diff --git a/configure b/configure
> index cb0f7ed..ce2df2d 100755
> --- a/configure
> +++ b/configure
> @@ -3461,10 +3461,15 @@ fi
>   ##########################################
>   # TPM passthrough is only on x86 Linux
>
> -if test "$targetos" = Linux && test "$cpu" = i386 -o "$cpu" = x86_64; then
> -  tpm_passthrough=$tpm
> +if test "$targetos" = Linux; then
> +  tpm_emulator=$tpm
> +  if test "$cpu" = i386 -o "$cpu" = x86_64; then
> +    tpm_passthrough=$tpm
> +  else
> +    tpm_passthrough=no
> +  fi
>   else
> -  tpm_passthrough=no
> +  tpm_emulator=no
>   fi
>
>   ##########################################
> @@ -5359,6 +5364,7 @@ echo "gcov enabled      $gcov"
>   echo "TPM support       $tpm"
>   echo "libssh2 support   $libssh2"
>   echo "TPM passthrough   $tpm_passthrough"
> +echo "TPM emulator      $tpm_emulator"
>   echo "QOM debugging     $qom_cast_debug"
>   echo "Live block migration $live_block_migration"
>   echo "lzo support       $lzo"
> @@ -5943,6 +5949,9 @@ if test "$tpm" = "yes"; then
>     if test "$tpm_passthrough" = "yes"; then
>       echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
>     fi
> +  if test "$tpm_emulator" = "yes"; then
> +    echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak
> +  fi
>   fi
>
>   echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
> diff --git a/hmp.c b/hmp.c
> index cf62b2e..7e69eca 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -995,6 +995,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
>       Error *err = NULL;
>       unsigned int c = 0;
>       TPMPassthroughOptions *tpo;
> +    TPMEmulatorOptions *teo;
>
>       info_list = qmp_query_tpm(&err);
>       if (err) {
> @@ -1024,6 +1025,10 @@ 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_EMULATOR:
> +            teo = ti->options->u.emulator.data;
> +            monitor_printf(mon, ",chardev=%s", teo->chardev);
> +            break;
>           case TPM_TYPE__MAX:
>               break;
>           }
> diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
> index 64cecc3..41f0b7a 100644
> --- a/hw/tpm/Makefile.objs
> +++ b/hw/tpm/Makefile.objs
> @@ -1,2 +1,3 @@
>   common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
>   common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
> +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
> diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
> new file mode 100644
> index 0000000..c02bbe2
> --- /dev/null
> +++ b/hw/tpm/tpm_emulator.c
> @@ -0,0 +1,649 @@
> +/*
> + *  emulator TPM driver

Emulator

> + *
> + *  Copyright (c) 2017 Intel Corporation
> + *  Author: Amarnath Valluri <amarnath.valluri@intel.com>
> + *
> + *  Copyright (c) 2010 - 2013 IBM Corporation
> + *  Authors:
> + *    Stefan Berger <stefanb@us.ibm.com>
> + *
> + *  Copyright (C) 2011 IAIK, Graz University of Technology
> + *    Author: Andreas Niederl
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/error-report.h"
> +#include "qemu/sockets.h"
> +#include "io/channel-socket.h"
> +#include "sysemu/tpm_backend.h"
> +#include "tpm_int.h"
> +#include "hw/hw.h"
> +#include "hw/i386/pc.h"
> +#include "tpm_util.h"
> +#include "tpm_ioctl.h"
> +#include "migration/blocker.h"
> +#include "qapi/error.h"
> +#include "chardev/char-fe.h"
> +
> +#include <fcntl.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <stdio.h>
> +
> +#define DEBUG_TPM 0
> +
> +#define DPRINT(fmt, ...) do { \
> +    if (DEBUG_TPM) { \
> +        fprintf(stderr, fmt, ## __VA_ARGS__); \
> +    } \
> +} while (0);
> +
> +#define DPRINTF(fmt, ...) DPRINT("tpm-emulator: "fmt"\n", __VA_ARGS__)
> +
> +#define TYPE_TPM_EMULATOR "tpm-emulator"
> +#define TPM_EMULATOR(obj) \
> +    OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
> +
> +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))
> +
> +static const TPMDriverOps tpm_emulator_driver;
> +
> +/* data structures */
> +typedef struct TPMEmulator {
> +    TPMBackend parent;
> +
> +    TPMEmulatorOptions *options;
> +    CharBackend ctrl_dev;
> +    QIOChannel *data_ioc;
> +    bool op_executing;
> +    bool op_canceled;
> +    bool had_startup_error;
> +    TPMVersion tpm_version;
> +    ptm_cap caps; /* capabilities of the TPM */
> +    uint8_t cur_locty_number; /* last set locality */
> +    QemuMutex state_lock;
> +    Error *migration_blocker;
> +} TPMEmulator;
> +
> +
> +static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg,
> +                                size_t msg_len_in, size_t msg_len_out)
> +{
> +    uint32_t cmd_no = cpu_to_be32(cmd);
> +    ssize_t n = sizeof(uint32_t) + msg_len_in;
> +    uint8_t *buf = NULL;
> +
> +    buf = (uint8_t *)malloc(n);
> +    memcpy(buf, &cmd_no, sizeof(cmd_no));
> +    memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
> +
> +    n += qemu_chr_fe_write_all(dev, (const uint8_t *)buf, n);
> +    free(buf);
> +
> +    if (n > 0) {
> +        if (msg_len_out > 0) {
> +            n = qemu_chr_fe_read_all(dev, (uint8_t *)msg, msg_len_out);
> +            /* simulate ioctl return value */
> +            if (n > 0) {
> +                n = 0;
> +            }
> +        } else {
> +            n = 0;
> +        }
> +    }
> +    return n;
> +}
> +
> +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_pt,
> +                                     const uint8_t *in, uint32_t in_len,
> +                                     uint8_t *out, uint32_t out_len,
> +                                     bool *selftest_done)
> +{
> +    ssize_t ret;
> +    bool is_selftest = false;
> +    const struct tpm_resp_hdr *hdr = NULL;
> +    Error *err = NULL;
> +
> +    tpm_pt->op_canceled = false;
> +    tpm_pt->op_executing = true;

add an empty line after the above

> +    if (selftest_done) {
> +        *selftest_done = false;
> +        is_selftest = tpm_util_is_selftest(in, in_len);
> +    }
> +
> +    ret = qio_channel_write(tpm_pt->data_ioc, (char *)in, in_len, &err);
> +    if (ret != in_len || err) {
> +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
> +            error_report("tpm-emulator: error while transmitting data "
> +                         "to TPM: %s", err ? error_get_pretty(err) : "");
> +            error_free(err);
> +        }
> +        goto err_exit;
> +    }
> +
> +    tpm_pt->op_executing = false;
> +
> +    ret = qio_channel_read(tpm_pt->data_ioc, (char *)out, out_len, &err);
> +    if (ret < 0 || err) {
> +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
> +            error_report("tpm-emulator: error while reading data from "
> +                         "TPM: %s", err ? error_get_pretty(err) : "");
> +            error_free(err);
> +        }
> +    } else if (ret >= sizeof(*hdr)) {
> +        hdr = (struct tpm_resp_hdr *)out;
> +    }
> +
> +    if (!hdr || be32_to_cpu(hdr->len) != ret) {
> +        error_report("tpm-emulator: received invalid response "
> +                     "packet from TPM with length :%ld", ret);
> +        ret = -1;
> +        goto err_exit;
> +    }
> +
> +    if (is_selftest) {
> +        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
> +    }
> +
> +    return 0;
> +
> +err_exit:
> +    if (ret < 0) {
> +        tpm_util_write_fatal_error_response(out, out_len);
> +    }
> +
> +    tpm_pt->op_executing = false;
> +
> +    return ret;
> +}
> +
> +static int tpm_emulator_set_locality(TPMEmulator *tpm_pt, uint8_t locty_number)
> +{
> +    ptm_loc loc;
> +
> +    DPRINTF("%s : locality: 0x%x", __func__, locty_number);
> +
> +    if (tpm_pt->cur_locty_number != locty_number) {
> +        DPRINTF("setting locality : 0x%x", locty_number);
> +        loc.u.req.loc = locty_number;
> +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SET_LOCALITY, &loc,
> +                             sizeof(loc), sizeof(loc)) < 0) {
> +            error_report("tpm-emulator: could not set locality : %s",
> +                         strerror(errno));
> +            return -1;
> +        }
> +        loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result);
> +        if (loc.u.resp.tpm_result != 0) {
> +            error_report("tpm-emulator: TPM result for set locality : 0x%x",
> +                         loc.u.resp.tpm_result);
> +            return -1;
> +        }
> +        tpm_pt->cur_locty_number = locty_number;
> +    }
> +    return 0;
> +}
> +
> +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    TPMLocality *locty = NULL;
> +    bool selftest_done = false;
> +
> +    DPRINTF("processing command type %d", cmd);
> +
> +    switch (cmd) {
> +    case TPM_BACKEND_CMD_PROCESS_CMD:
> +        qemu_mutex_lock(&tpm_pt->state_lock);
> +        locty = tb->tpm_state->locty_data;
> +        if (tpm_emulator_set_locality(tpm_pt,
> +                                      tb->tpm_state->locty_number) < 0) {
> +            tpm_util_write_fatal_error_response(locty->r_buffer.buffer,
> +                                           locty->r_buffer.size);
> +        } else {
> +            tpm_emulator_unix_tx_bufs(tpm_pt, locty->w_buffer.buffer,
> +                                              locty->w_offset,
> +                                              locty->r_buffer.buffer,
> +                                              locty->r_buffer.size,
> +                                              &selftest_done);
> +        }
> +
> +        tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number,
> +                               selftest_done);
> +        qemu_mutex_unlock(&tpm_pt->state_lock);
> +
> +        break;
> +    case TPM_BACKEND_CMD_INIT:
> +    case TPM_BACKEND_CMD_END:
> +    case TPM_BACKEND_CMD_TPM_RESET:
> +        /* nothing to do */
> +        break;
> +    }
> +}
> +
> +/*
> + * Gracefully shut down the external unixio TPM
> + */
> +static void tpm_emulator_shutdown(TPMEmulator *tpm_pt)
> +{
> +    ptm_res res;
> +
> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SHUTDOWN, &res, 0,
> +                         sizeof(res)) < 0) {
> +        error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s",
> +                     strerror(errno));
> +    } else if (res != 0) {
> +        error_report("tpm-emulator: TPM result for sutdown: 0x%x",
> +                     be32_to_cpu(res));
> +    }
> +}
> +
> +static int tpm_emulator_probe_caps(TPMEmulator *tpm_pt)
> +{
> +    DPRINTF("%s", __func__);
> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_GET_CAPABILITY,
> +                         &tpm_pt->caps, 0, sizeof(tpm_pt->caps)) < 0) {
> +        error_report("tpm-emulator: probing failed : %s", strerror(errno));
> +        return -1;
> +    }
> +
> +    tpm_pt->caps = be64_to_cpu(tpm_pt->caps);
> +
> +    DPRINTF("capbilities : 0x%lx", tpm_pt->caps);
> +
> +    return 0;
> +}
> +
> +static int tpm_emulator_check_caps(TPMEmulator *tpm_pt)
> +{
> +    ptm_cap caps = 0;
> +    const char *tpm = NULL;
> +
> +    /* 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;

Since you are passing the data channel file descriptor via the new 
command, you should be checking for that new command here as well. Add 
it to both 'caps'.

> +        tpm = "2";
> +        break;
> +    case TPM_VERSION_UNSPEC:
> +        error_report("tpm-emulator: TPM version has not been set");
> +        return -1;
> +    }
> +
> +    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, caps)) {
> +        error_report("tpm-emulator: TPM does not implement minimum set of "
> +                     "required capabilities for TPM %s (0x%x)", tpm, (int)caps);
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int tpm_emulator_startup_tpm(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    ptm_init init;
> +    ptm_res res;
> +
> +    DPRINTF("%s", __func__);
> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_INIT, &init, sizeof(init),
> +                         sizeof(init)) < 0) {
> +        error_report("tpm-emulator: could not send INIT: %s",
> +                     strerror(errno));
> +        goto err_exit;
> +    }
> +
> +    res = be32_to_cpu(init.u.resp.tpm_result);
> +    if (res) {
> +        error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res);
> +        goto err_exit;
> +    }
> +    return 0;
> +
> +err_exit:
> +    tpm_pt->had_startup_error = true;
> +    return -1;
> +}
> +
> +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    ptm_est est;
> +
> +    DPRINTF("%s", __func__);
> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_GET_TPMESTABLISHED, &est, 0,
> +                         sizeof(est)) < 0) {
> +        error_report("tpm-emulator: Could not get the TPM established flag: %s",
> +                     strerror(errno));
> +        return false;
> +    }
> +    DPRINTF("established flag: %0x", est.u.resp.bit);
> +
> +    return (est.u.resp.bit != 0);
> +}
> +
> +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
> +                                                   uint8_t locty)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    ptm_reset_est reset_est;
> +    ptm_res res;
> +
> +    /* only a TPM 2.0 will support this */
> +    if (tpm_pt->tpm_version == TPM_VERSION_2_0) {
> +        reset_est.u.req.loc = tpm_pt->cur_locty_number;
> +
> +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_RESET_TPMESTABLISHED,
> +                                 &reset_est, sizeof(reset_est),
> +                                 sizeof(reset_est)) < 0) {
> +            error_report("tpm-emulator: Could not reset the establishment bit: "
> +                          "%s", strerror(errno));
> +            return -1;
> +        }
> +
> +        res = be32_to_cpu(reset_est.u.resp.tpm_result);
> +        if (res) {
> +            error_report("tpm-emulator: TPM result for rest establixhed flag: "
> +                         "0x%x", res);
> +            return -1;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static bool tpm_emulator_had_startup_error(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +
> +    return tpm_pt->had_startup_error;
> +}
> +
> +static void tpm_emulator_cancel_cmd(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    ptm_res res;
> +
> +    /*
> +     * As of Linux 3.7 the tpm_tis driver does not properly cancel
> +     * commands on all TPM manufacturers' TPMs.
> +     * Only cancel if we're busy so we don't cancel someone else's
> +     * command, e.g., a command executed on the host.
> +     */
> +    if (tpm_pt->op_executing) {
> +        if (TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, PTM_CAP_CANCEL_TPM_CMD)) {
> +            if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_CANCEL_TPM_CMD,
> +                                     &res, 0, sizeof(res)) < 0) {
> +                error_report("tpm-emulator: Could not cancel command: %s",
> +                             strerror(errno));
> +            } else if (res != 0) {
> +                error_report("tpm-emulator: Failed to cancel TPM: 0x%x",
> +                             be32_to_cpu(res));
> +            } else {
> +                tpm_pt->op_canceled = true;
> +            }
> +        }
> +    }
> +}
> +
> +static void tpm_emulator_reset(TPMBackend *tb)
> +{
> +    DPRINTF("%s", __func__);
> +
> +    tpm_emulator_cancel_cmd(tb);
> +}
> +
> +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +
> +    return tpm_pt->tpm_version;
> +}
> +
> +static void tpm_emulator_block_migration(TPMEmulator *tpm_pt)
> +{
> +    Error *err = NULL;
> +
> +    error_setg(&tpm_pt->migration_blocker,
> +               "Migration disabled: TPM emulator not yet migratable");
> +    migrate_add_blocker(tpm_pt->migration_blocker, &err);
> +    if (err) {
> +        error_free(err);
> +        error_free(tpm_pt->migration_blocker);
> +        tpm_pt->migration_blocker = NULL;
> +    }
> +}
> +
> +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_pt)
> +{
> +    ptm_res res;
> +    Error *err = NULL;
> +    int fds[2] = { -1, -1 };
> +
> +    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) {
> +        error_report("tpm-emulator: Failed to create socketpair");
> +        return -1;
> +    }
> +
> +    qemu_chr_fe_set_msgfds(&tpm_pt->ctrl_dev, fds + 1, 1);
> +
> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SET_DATAFD, &res, 0,
> +                    sizeof(res)) || res != 0) {
> +        error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s",
> +                     strerror(errno));
> +        goto err_exit;
> +    }
> +
> +    tpm_pt->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
> +    if (err) {
> +        error_report("tpm-emulator: Failed to create io channel : %s",
> +                       error_get_pretty(err));
> +        error_free(err);
> +        goto err_exit;
> +    }
> +
> +    return 0;
> +
> +err_exit:
> +    closesocket(fds[0]);
> +    closesocket(fds[1]);
> +    return -1;
> +}
> +
> +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_pt, QemuOpts *opts)
> +{
> +    const char *value;
> +
> +    value = qemu_opt_get(opts, "chardev");
> +    if (value) {
> +        Error *err = NULL;
> +        Chardev *dev = qemu_chr_find(value);
> +
> +        tpm_pt->options->chardev = g_strdup(value);

Could you set this after the error checks?

> +
> +        if (!dev || !qemu_chr_fe_init(&tpm_pt->ctrl_dev, dev, &err)) {
> +            error_report("tpm-emulator: No valid chardev found at '%s': %s",
> +                         value, err ? error_get_pretty(err) : "");
> +            error_free(err);
> +            goto err;
> +        }
> +    }
> +
> +    if (tpm_emulator_prepare_data_fd(tpm_pt) < 0) {
> +        goto err;
> +    }
> +
> +    /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used
> +     * by passthrough driver, which not yet using GIOChannel.
> +     */
> +    if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_pt->data_ioc)->fd,
> +                             &tpm_pt->tpm_version)) {
> +        error_report("'%s' is not emulating TPM device. Error: %s",
> +                      tpm_pt->options->chardev, strerror(errno));
> +        goto err;
> +    }
> +
> +    DPRINTF("TPM Version %s", tpm_pt->tpm_version == TPM_VERSION_1_2 ? "1.2" :
> +             (tpm_pt->tpm_version == TPM_VERSION_2_0 ?  "2.0" : "Unspecified"));
> +
> +    if (tpm_emulator_probe_caps(tpm_pt) ||
> +        tpm_emulator_check_caps(tpm_pt)) {
> +        goto err;
> +    }
> +
> +    tpm_emulator_block_migration(tpm_pt);
> +
> +    return 0;
> +
> +err:
> +    DPRINT("Startup error\n");
> +    return -1;
> +}
> +
> +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)
> +{
> +    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;
> +    }
> +
> +    return tb;
> +
> +err_exit:
> +    object_unref(OBJECT(tb));
> +
> +    return NULL;
> +}
> +
> +static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    TpmTypeOptions *options = NULL;
> +    TPMEmulatorOptions *eoptions = NULL;
> +
> +    eoptions = g_new0(TPMEmulatorOptions, 1);
> +    if (!eoptions) {
> +        return NULL;
> +    }
> +    DPRINTF("%s", __func__);

You can probably remove this.

> +
> +    eoptions->chardev = g_strdup(tpm_pt->options->chardev);
> +    options = g_new0(TpmTypeOptions, 1);
> +    if (!options) {
> +        qapi_free_TPMEmulatorOptions(eoptions);
> +        return NULL;
> +    }
> +
> +    options->type = TPM_TYPE_EMULATOR;
> +    options->u.emulator.data = eoptions;
> +
> +    return options;
> +}
> +
> +static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
> +    TPM_STANDARD_CMDLINE_OPTS,
> +    {
> +        .name = "chardev",
> +        .type = QEMU_OPT_STRING,
> +        .help = "Character device to use for out-of-band control messages",
> +    },
> +    { /* end of list */ },
> +};
> +
> +static const TPMDriverOps tpm_emulator_driver = {
> +    .type                     = TPM_TYPE_EMULATOR,
> +    .opts                     = tpm_emulator_cmdline_opts,
> +    .desc                     = "TPM emulator backend driver",
> +
> +    .create                   = tpm_emulator_create,
> +    .startup_tpm              = tpm_emulator_startup_tpm,
> +    .reset                    = tpm_emulator_reset,
> +    .had_startup_error        = tpm_emulator_had_startup_error,
> +    .cancel_cmd               = tpm_emulator_cancel_cmd,
> +    .get_tpm_established_flag = tpm_emulator_get_tpm_established_flag,
> +    .reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag,
> +    .get_tpm_version          = tpm_emulator_get_tpm_version,
> +    .get_tpm_options          = tpm_emulator_get_tpm_options,
> +};
> +
> +static void tpm_emulator_inst_init(Object *obj)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
> +
> +    DPRINTF("%s", __func__);
> +    tpm_pt->options = g_new0(TPMEmulatorOptions, 1);
> +    tpm_pt->op_executing = tpm_pt->op_canceled = false;
> +    tpm_pt->had_startup_error = false;
> +    tpm_pt->cur_locty_number = ~0;
> +    qemu_mutex_init(&tpm_pt->state_lock);
> +}
> +
> +static void tpm_emulator_inst_finalize(Object *obj)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
> +
> +    tpm_emulator_cancel_cmd(TPM_BACKEND(obj));
> +    tpm_emulator_shutdown(tpm_pt);
> +
> +    if (tpm_pt->data_ioc) {
> +        qio_channel_close(tpm_pt->data_ioc, NULL);
> +    }
> +
> +    qemu_chr_fe_deinit(&tpm_pt->ctrl_dev, false);
> +
> +    if (tpm_pt->options) {
> +        qapi_free_TPMEmulatorOptions(tpm_pt->options);
> +    }
> +
> +    if (tpm_pt->migration_blocker) {
> +        migrate_del_blocker(tpm_pt->migration_blocker);
> +        error_free(tpm_pt->migration_blocker);
> +    }
> +}
> +
> +static void tpm_emulator_class_init(ObjectClass *klass, void *data)
> +{
> +    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
> +    tbc->ops = &tpm_emulator_driver;
> +    tbc->handle_request = tpm_emulator_handle_request;
> +}
> +
> +static const TypeInfo tpm_emulator_info = {
> +    .name = TYPE_TPM_EMULATOR,
> +    .parent = TYPE_TPM_BACKEND,
> +    .instance_size = sizeof(TPMEmulator),
> +    .class_init = tpm_emulator_class_init,
> +    .instance_init = tpm_emulator_inst_init,
> +    .instance_finalize = tpm_emulator_inst_finalize,
> +};
> +
> +static void tpm_emulator_register(void)
> +{
> +    type_register_static(&tpm_emulator_info);
> +    tpm_register_driver(&tpm_emulator_driver);
> +}
> +
> +type_init(tpm_emulator_register)
> diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
> new file mode 100644
> index 0000000..33564b1
> --- /dev/null
> +++ b/hw/tpm/tpm_ioctl.h
> @@ -0,0 +1,246 @@
> +/*
> + * tpm_ioctl.h
> + *
> + * (c) Copyright IBM Corporation 2014, 2015.
> + *
> + * This file is licensed under the terms of the 3-clause BSD license
> + */
> +#ifndef _TPM_IOCTL_H_
> +#define _TPM_IOCTL_H_
> +
> +#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 ptm_res as the first element.
> + * ptm_res corresponds to the error code of a command executed by the TPM.
> + */
> +
> +typedef uint32_t ptm_res;
> +
> +/* PTM_GET_TPMESTABLISHED: get the establishment bit */
> +struct ptm_est {
> +    union {
> +        struct {
> +            ptm_res tpm_result;
> +            unsigned char bit; /* TPM established bit */
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */
> +struct ptm_reset_est {
> +    union {
> +        struct {
> +            uint8_t loc; /* locality to use */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* PTM_INIT */
> +struct ptm_init {
> +    union {
> +        struct {
> +            uint32_t init_flags; /* see definitions below */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* above init_flags */
> +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0)
> +    /* delete volatile state file after reading it */
> +
> +/* PTM_SET_LOCALITY */
> +struct ptm_loc {
> +    union {
> +        struct {
> +            uint8_t loc; /* locality to set */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* PTM_HASH_DATA: hash given data */
> +struct ptm_hdata {
> +    union {
> +        struct {
> +            uint32_t length;
> +            uint8_t data[4096];
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } 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 PTM_STATE_BLOB_SIZE (3 * 1024)
> +
> +/*
> + * The following is the data structure to get state blobs from the TPM.
> + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads
> + * with this ioctl and with adjusted offset are necessary. All bytes
> + * must be transferred and the transfer is done once the last byte has been
> + * returned.
> + * It is possible to use the read() interface for reading the data; however, the
> + * first bytes of the state blob will be part of the response to the ioctl(); a
> + * subsequent read() is only necessary if the total length (totlength) exceeds
> + * the number of received bytes. seek() is not supported.
> + */
> +struct ptm_getstate {
> +    union {
> +        struct {
> +            uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */
> +            uint32_t type;        /* which blob to pull */
> +            uint32_t offset;      /* offset from where to read */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +            uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */
> +            uint32_t totlength;   /* total length that will be transferred */
> +            uint32_t length;      /* number of bytes in following buffer */
> +            uint8_t  data[PTM_STATE_BLOB_SIZE];
> +        } resp; /* response */
> +    } 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 PTM_STATE_FLAG_DECRYPTED     1 /* on input:  get decrypted state */
> +#define PTM_STATE_FLAG_ENCRYPTED     2 /* on output: state is encrypted */
> +
> +/*
> + * The following is the data structure to set state blobs in the TPM.
> + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple
> + * 'writes' using this ioctl are necessary. The last packet is indicated
> + * by the length being smaller than the PTM_STATE_BLOB_SIZE.
> + * The very first packet may have a length indicator of '0' enabling
> + * a write() with all the bytes from a buffer. If the write() interface
> + * is used, a final ioctl with a non-full buffer must be made to indicate
> + * that all data were transferred (a write with 0 bytes would not work).
> + */
> +struct ptm_setstate {
> +    union {
> +        struct {
> +            uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */
> +            uint32_t type;        /* which blob to set */
> +            uint32_t length;      /* length of the data;
> +                                     use 0 on the first packet to
> +                                     transfer using write() */
> +            uint8_t data[PTM_STATE_BLOB_SIZE];
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/*
> + * PTM_GET_CONFIG: Data structure to get runtime configuration information
> + * such as which keys are applied.
> + */
> +struct ptm_getconfig {
> +    union {
> +        struct {
> +            ptm_res tpm_result;
> +            uint32_t flags;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +#define PTM_CONFIG_FLAG_FILE_KEY        0x1
> +#define PTM_CONFIG_FLAG_MIGRATION_KEY   0x2
> +
> +
> +typedef uint64_t ptm_cap;
> +typedef struct ptm_est ptm_est;
> +typedef struct ptm_reset_est ptm_reset_est;
> +typedef struct ptm_loc ptm_loc;
> +typedef struct ptm_hdata ptm_hdata;
> +typedef struct ptm_init ptm_init;
> +typedef struct ptm_getstate ptm_getstate;
> +typedef struct ptm_setstate ptm_setstate;
> +typedef struct ptm_getconfig ptm_getconfig;
> +
> +/* 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)
> +#define PTM_CAP_GET_CONFIG         (1 << 11)
> +#define PTM_CAP_SET_DATAFD         (1 << 12)
> +
> +enum {
> +    PTM_GET_CAPABILITY     = _IOR('P', 0, ptm_cap),
> +    PTM_INIT               = _IOWR('P', 1, ptm_init),
> +    PTM_SHUTDOWN           = _IOR('P', 2, ptm_res),
> +    PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est),
> +    PTM_SET_LOCALITY       = _IOWR('P', 4, ptm_loc),
> +    PTM_HASH_START         = _IOR('P', 5, ptm_res),
> +    PTM_HASH_DATA          = _IOWR('P', 6, ptm_hdata),
> +    PTM_HASH_END           = _IOR('P', 7, ptm_res),
> +    PTM_CANCEL_TPM_CMD     = _IOR('P', 8, ptm_res),
> +    PTM_STORE_VOLATILE     = _IOR('P', 9, ptm_res),
> +    PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est),
> +    PTM_GET_STATEBLOB      = _IOWR('P', 11, ptm_getstate),
> +    PTM_SET_STATEBLOB      = _IOWR('P', 12, ptm_setstate),
> +    PTM_STOP               = _IOR('P', 13, ptm_res),
> +    PTM_GET_CONFIG         = _IOR('P', 14, ptm_getconfig),
> +    PTM_SET_DATAFD         = _IOR('P', 15, ptm_res),
> +};
> +
> +/*
> + * Commands used by the non-CUSE TPMs
> + *
> + * All messages container big-endian data.
> + *
> + * The return messages only contain the 'resp' part of the unions
> + * in the data structures above. Besides that the limits in the
> + * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data
> + * and ptm_set_state:u.req.data) are 0xffffffff.
> + */
> +enum {
> +    CMD_GET_CAPABILITY = 1,
> +    CMD_INIT,
> +    CMD_SHUTDOWN,
> +    CMD_GET_TPMESTABLISHED,
> +    CMD_SET_LOCALITY,
> +    CMD_HASH_START,
> +    CMD_HASH_DATA,
> +    CMD_HASH_END,
> +    CMD_CANCEL_TPM_CMD,
> +    CMD_STORE_VOLATILE,
> +    CMD_RESET_TPMESTABLISHED,
> +    CMD_GET_STATEBLOB,
> +    CMD_SET_STATEBLOB,
> +    CMD_STOP,
> +    CMD_GET_CONFIG,
> +    CMD_SET_DATAFD
> +};
> +
> +#endif /* _TPM_IOCTL_H */
> diff --git a/qapi/tpm.json b/qapi/tpm.json
> index e8b2d8d..7093f26 100644
> --- a/qapi/tpm.json
> +++ b/qapi/tpm.json
> @@ -39,10 +39,12 @@
>   # An enumeration of TPM types
>   #
>   # @passthrough: TPM passthrough type
> +# @emulator: Software Emulator TPM type
> +#            Since: 2.11
>   #
>   # Since: 1.5
>   ##
> -{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }
> +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] }
>
>   ##
>   # @query-tpm-types:
> @@ -56,7 +58,7 @@
>   # Example:
>   #
>   # -> { "execute": "query-tpm-types" }
> -# <- { "return": [ "passthrough" ] }
> +# <- { "return": [ "passthrough", "emulator" ] }
>   #
>   ##
>   { 'command': 'query-tpm-types', 'returns': ['TpmType'] }
> @@ -77,16 +79,29 @@
>                                                '*cancel-path' : 'str'} }
>
>   ##
> +# @TPMEmulatorOptions:
> +#
> +# Information about the TPM emulator type
> +#
> +# @chardev: Name of a unix socket chardev
> +#
> +# Since: 2.11
> +##
> +{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } }
> +
> +##
>   # @TpmTypeOptions:
>   #
>   # A union referencing different TPM backend types' configuration options
>   #
>   # @type: 'passthrough' The configuration options for the TPM passthrough type
> +#        'emulator' The configuration options for TPM emulator backend type
>   #
>   # Since: 1.5
>   ##
>   { 'union': 'TpmTypeOptions',
> -   'data': { 'passthrough' : 'TPMPassthroughOptions' } }
> +   'data': { 'passthrough' : 'TPMPassthroughOptions',
> +             'emulator': 'TPMEmulatorOptions' } }
>
>   ##
>   # @TPMInfo:
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 77859a2..1e93e53 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -3121,7 +3121,9 @@ 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 emulator,id=id,chardev=dev\n"
> +    "                configure the TPM device using chardev backend\n",
>       QEMU_ARCH_ALL)
>   STEXI
>
> @@ -3130,8 +3132,8 @@ The general form of a TPM device option is:
>
>   @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
>   @findex -tpmdev
> -Backend type must be:
> -@option{passthrough}.
> +Backend type must be either one of the following:
> +@option{passthrough}, @option{emulator}.
>
>   The specific backend type will determine the applicable options.
>   The @code{-tpmdev} option creates the TPM backend and requires a
> @@ -3181,6 +3183,20 @@ 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.
>
> +@item -tpmdev emulator, id=@var{id}, chardev=@var{dev}
> +
> +(Linux-host only) Enable access to a TPM emulator using Unix domain socket based
> +chardev backend.
> +
> +@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server.
> +
> +To create a TPM emulator backend device with chardev socket backend:
> +@example
> +
> +-chardev socket,id=chrtpm,path=/tmp/swtpm-ctrl -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
> +
> +@end example
> +
>   @end table
>
>   ETEXI



     Stefan

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator Amarnath Valluri
  2017-09-22 16:23   ` Stefan Berger
@ 2017-09-24 18:52   ` Marc-André Lureau
  2017-09-25  0:35     ` Stefan Berger
                       ` (2 more replies)
  1 sibling, 3 replies; 23+ messages in thread
From: Marc-André Lureau @ 2017-09-24 18:52 UTC (permalink / raw)
  To: Amarnath Valluri
  Cc: QEMU, Stefan Berger, Dr. David Alan Gilbert, Eric Blake,
	Markus Armbruster

Hi

Thanks for the nice update, removing the exec() code, using chardev
and a private socketpair. Some comments below:

On Fri, Sep 22, 2017 at 2:33 PM, Amarnath Valluri
<amarnath.valluri@intel.com> wrote:
> This change introduces a new TPM backend driver that can communicate with
> swtpm(software TPM emulator) using unix domain socket interface. QEMU talks to
> TPM emulator using socket based chardev backend device.
>
> Swtpm uses two Unix sockets for communications, one for plain TPM commands and
> responses, and one for out-of-band control messages. QEMU passes data socket
> been used over the control channel.
>
> The swtpm and associated tools can be found here:
>     https://github.com/stefanberger/swtpm
>
> The swtpm's control channel protocol specification can be found here:
>     https://github.com/stefanberger/swtpm/wiki/Control-Channel-Specification
>
> Usage:
>     # setup TPM state directory
>     mkdir /tmp/mytpm
>     chown -R tss:root /tmp/mytpm
>     /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek
>
>     # Ask qemu to use TPM emulator with given tpm state directory
>     qemu-system-x86_64 \
>         [...] \
>         -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \
>         -tpmdev emulator,id=tpm0,chardev=chrtpm \
>         -device tpm-tis,tpmdev=tpm0 \
>         [...]
>
> Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
> ---
>  configure             |  15 +-
>  hmp.c                 |   5 +
>  hw/tpm/Makefile.objs  |   1 +
>  hw/tpm/tpm_emulator.c | 649 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/tpm/tpm_ioctl.h    | 246 +++++++++++++++++++
>  qapi/tpm.json         |  21 +-
>  qemu-options.hx       |  22 +-
>  7 files changed, 950 insertions(+), 9 deletions(-)
>  create mode 100644 hw/tpm/tpm_emulator.c
>  create mode 100644 hw/tpm/tpm_ioctl.h
>
> diff --git a/configure b/configure
> index cb0f7ed..ce2df2d 100755
> --- a/configure
> +++ b/configure
> @@ -3461,10 +3461,15 @@ fi
>  ##########################################
>  # TPM passthrough is only on x86 Linux
>
> -if test "$targetos" = Linux && test "$cpu" = i386 -o "$cpu" = x86_64; then
> -  tpm_passthrough=$tpm
> +if test "$targetos" = Linux; then
> +  tpm_emulator=$tpm
> +  if test "$cpu" = i386 -o "$cpu" = x86_64; then
> +    tpm_passthrough=$tpm
> +  else
> +    tpm_passthrough=no
> +  fi
>  else
> -  tpm_passthrough=no
> +  tpm_emulator=no
>  fi
>
>  ##########################################
> @@ -5359,6 +5364,7 @@ echo "gcov enabled      $gcov"
>  echo "TPM support       $tpm"
>  echo "libssh2 support   $libssh2"
>  echo "TPM passthrough   $tpm_passthrough"
> +echo "TPM emulator      $tpm_emulator"
>  echo "QOM debugging     $qom_cast_debug"
>  echo "Live block migration $live_block_migration"
>  echo "lzo support       $lzo"
> @@ -5943,6 +5949,9 @@ if test "$tpm" = "yes"; then
>    if test "$tpm_passthrough" = "yes"; then
>      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
>    fi
> +  if test "$tpm_emulator" = "yes"; then
> +    echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak

It shouldn't require Linux, but posix (and I assume a port to other
systems isn't impossible). same for build-sys / help / comments.

> +  fi
>  fi
>
>  echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
> diff --git a/hmp.c b/hmp.c
> index cf62b2e..7e69eca 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -995,6 +995,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
>      Error *err = NULL;
>      unsigned int c = 0;
>      TPMPassthroughOptions *tpo;
> +    TPMEmulatorOptions *teo;
>
>      info_list = qmp_query_tpm(&err);
>      if (err) {
> @@ -1024,6 +1025,10 @@ 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_EMULATOR:
> +            teo = ti->options->u.emulator.data;
> +            monitor_printf(mon, ",chardev=%s", teo->chardev);
> +            break;
>          case TPM_TYPE__MAX:
>              break;
>          }
> diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
> index 64cecc3..41f0b7a 100644
> --- a/hw/tpm/Makefile.objs
> +++ b/hw/tpm/Makefile.objs
> @@ -1,2 +1,3 @@
>  common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
>  common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
> +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
> diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
> new file mode 100644
> index 0000000..c02bbe2
> --- /dev/null
> +++ b/hw/tpm/tpm_emulator.c
> @@ -0,0 +1,649 @@
> +/*
> + *  emulator TPM driver
> + *
> + *  Copyright (c) 2017 Intel Corporation
> + *  Author: Amarnath Valluri <amarnath.valluri@intel.com>
> + *
> + *  Copyright (c) 2010 - 2013 IBM Corporation
> + *  Authors:
> + *    Stefan Berger <stefanb@us.ibm.com>
> + *
> + *  Copyright (C) 2011 IAIK, Graz University of Technology
> + *    Author: Andreas Niederl
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/error-report.h"
> +#include "qemu/sockets.h"
> +#include "io/channel-socket.h"
> +#include "sysemu/tpm_backend.h"
> +#include "tpm_int.h"
> +#include "hw/hw.h"
> +#include "hw/i386/pc.h"
> +#include "tpm_util.h"
> +#include "tpm_ioctl.h"
> +#include "migration/blocker.h"
> +#include "qapi/error.h"
> +#include "chardev/char-fe.h"
> +
> +#include <fcntl.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <stdio.h>
> +
> +#define DEBUG_TPM 0
> +
> +#define DPRINT(fmt, ...) do { \
> +    if (DEBUG_TPM) { \
> +        fprintf(stderr, fmt, ## __VA_ARGS__); \
> +    } \
> +} while (0);
> +
> +#define DPRINTF(fmt, ...) DPRINT("tpm-emulator: "fmt"\n", __VA_ARGS__)

I would define a single DPRINTF() (& drop DPRINT usage below)


> +
> +#define TYPE_TPM_EMULATOR "tpm-emulator"
> +#define TPM_EMULATOR(obj) \
> +    OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
> +
> +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))
> +
> +static const TPMDriverOps tpm_emulator_driver;
> +
> +/* data structures */
> +typedef struct TPMEmulator {
> +    TPMBackend parent;
> +
> +    TPMEmulatorOptions *options;

Contrary to my comment on previous patch, I realize it is convenient
to have a qapi pointer here, so you can use the free visitor later.

> +    CharBackend ctrl_dev;

ctrl_chr perhaps? or just chr or be (the most common name in other devices).

> +    QIOChannel *data_ioc;
> +    bool op_executing;
> +    bool op_canceled;

I think those 2 fields can be dropped, see below.

> +    bool had_startup_error;

I think some little refactoring could remove the whole
had_startup_error() backend & callback before or after the series.
tpm_backend_startup_tpm() already returns an error code.  device or
common backend code could handle & remember that.

> +    TPMVersion tpm_version;

This is probably worth to consider in common code instead, but let's
not worry about it.

> +    ptm_cap caps; /* capabilities of the TPM */
> +    uint8_t cur_locty_number; /* last set locality */
> +    QemuMutex state_lock;
> +    Error *migration_blocker;
> +} TPMEmulator;
> +
> +
> +static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg,
> +                                size_t msg_len_in, size_t msg_len_out)
> +{
> +    uint32_t cmd_no = cpu_to_be32(cmd);
> +    ssize_t n = sizeof(uint32_t) + msg_len_in;
> +    uint8_t *buf = NULL;
> +
> +    buf = (uint8_t *)malloc(n);

could be g_alloca() instead. Alternatively, why not call 2 write() instead?

none of the casts in this function are necessary, please remove them.

> +    memcpy(buf, &cmd_no, sizeof(cmd_no));
> +    memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
> +
> +    n += qemu_chr_fe_write_all(dev, (const uint8_t *)buf, n);

weird

> +    free(buf);
> +
> +    if (n > 0) {

with the n += above, you'll get interesting behaviour :)

You probably want to check if any write above failed, and return early
for the error case.

Please improve the error handling in this function

> +        if (msg_len_out > 0) {
> +            n = qemu_chr_fe_read_all(dev, (uint8_t *)msg, msg_len_out);
> +            /* simulate ioctl return value */
> +            if (n > 0) {
> +                n = 0;
> +            }
> +        } else {
> +            n = 0;
> +        }
> +    }
> +    return n;
> +}
> +
> +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_pt,

tpm_pt was tpm_passthrough I suppose.

Please rename tpm_pt tpm_emu (or I would suggest "self", but this
isn't common name in qemu code - it's actually common in a close
c-object world., and it is quite convenient...)

> +                                     const uint8_t *in, uint32_t in_len,
> +                                     uint8_t *out, uint32_t out_len,
> +                                     bool *selftest_done)
> +{
> +    ssize_t ret;
> +    bool is_selftest = false;
> +    const struct tpm_resp_hdr *hdr = NULL;
> +    Error *err = NULL;
> +
> +    tpm_pt->op_canceled = false;
> +    tpm_pt->op_executing = true;
> +    if (selftest_done) {
> +        *selftest_done = false;
> +        is_selftest = tpm_util_is_selftest(in, in_len);
> +    }
> +
> +    ret = qio_channel_write(tpm_pt->data_ioc, (char *)in, in_len, &err);

hmm, too bad qio_channel_write() doesn't take a void *

why not write_all()?

> +    if (ret != in_len || err) {
> +        if (!tpm_pt->op_canceled || errno != ECANCELED) {

I don't think ECANCELED make sense for emulator code.

> +            error_report("tpm-emulator: error while transmitting data "
> +                         "to TPM: %s", err ? error_get_pretty(err) : "");
> +            error_free(err);
> +        }
> +        goto err_exit;
> +    }
> +
> +    tpm_pt->op_executing = false;
> +
> +    ret = qio_channel_read(tpm_pt->data_ioc, (char *)out, out_len, &err);
> +    if (ret < 0 || err) {

read_all() ?

> +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
> +            error_report("tpm-emulator: error while reading data from "
> +                         "TPM: %s", err ? error_get_pretty(err) : "");
> +            error_free(err);
> +        }
> +    } else if (ret >= sizeof(*hdr)) {
> +        hdr = (struct tpm_resp_hdr *)out;
> +    }
> +
> +    if (!hdr || be32_to_cpu(hdr->len) != ret) {
> +        error_report("tpm-emulator: received invalid response "
> +                     "packet from TPM with length :%ld", ret);
> +        ret = -1;
> +        goto err_exit;
> +    }
> +
> +    if (is_selftest) {
> +        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
> +    }
> +
> +    return 0;
> +
> +err_exit:
> +    if (ret < 0) {
> +        tpm_util_write_fatal_error_response(out, out_len);
> +    }
> +
> +    tpm_pt->op_executing = false;
> +
> +    return ret;
> +}
> +
> +static int tpm_emulator_set_locality(TPMEmulator *tpm_pt, uint8_t locty_number)
> +{
> +    ptm_loc loc;
> +
> +    DPRINTF("%s : locality: 0x%x", __func__, locty_number);
> +
> +    if (tpm_pt->cur_locty_number != locty_number) {

return early instead if ==, to avoid code indent etc

> +        DPRINTF("setting locality : 0x%x", locty_number);
> +        loc.u.req.loc = locty_number;

This number isn't set in be like the rest of the protocol?

> +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SET_LOCALITY, &loc,
> +                             sizeof(loc), sizeof(loc)) < 0) {
> +            error_report("tpm-emulator: could not set locality : %s",
> +                         strerror(errno));
> +            return -1;
> +        }
> +        loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result);
> +        if (loc.u.resp.tpm_result != 0) {
> +            error_report("tpm-emulator: TPM result for set locality : 0x%x",
> +                         loc.u.resp.tpm_result);
> +            return -1;
> +        }
> +        tpm_pt->cur_locty_number = locty_number;
> +    }
> +    return 0;
> +}
> +
> +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    TPMLocality *locty = NULL;
> +    bool selftest_done = false;
> +
> +    DPRINTF("processing command type %d", cmd);
> +
> +    switch (cmd) {
> +    case TPM_BACKEND_CMD_PROCESS_CMD:
> +        qemu_mutex_lock(&tpm_pt->state_lock);
> +        locty = tb->tpm_state->locty_data;
> +        if (tpm_emulator_set_locality(tpm_pt,
> +                                      tb->tpm_state->locty_number) < 0) {
> +            tpm_util_write_fatal_error_response(locty->r_buffer.buffer,
> +                                           locty->r_buffer.size);

return / goto here instead of else.

> +        } else {
> +            tpm_emulator_unix_tx_bufs(tpm_pt, locty->w_buffer.buffer,
> +                                              locty->w_offset,
> +                                              locty->r_buffer.buffer,
> +                                              locty->r_buffer.size,
> +                                              &selftest_done);

no error handling?

> +        }
> +
> +        tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number,
> +                               selftest_done);
> +        qemu_mutex_unlock(&tpm_pt->state_lock);
> +
> +        break;
> +    case TPM_BACKEND_CMD_INIT:
> +    case TPM_BACKEND_CMD_END:
> +    case TPM_BACKEND_CMD_TPM_RESET:
> +        /* nothing to do */
> +        break;
> +    }
> +}
> +
> +/*
> + * Gracefully shut down the external unixio TPM
> + */
> +static void tpm_emulator_shutdown(TPMEmulator *tpm_pt)
> +{
> +    ptm_res res;
> +
> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SHUTDOWN, &res, 0,
> +                         sizeof(res)) < 0) {
> +        error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s",
> +                     strerror(errno));
> +    } else if (res != 0) {
> +        error_report("tpm-emulator: TPM result for sutdown: 0x%x",
> +                     be32_to_cpu(res));
> +    }
> +}
> +
> +static int tpm_emulator_probe_caps(TPMEmulator *tpm_pt)
> +{
> +    DPRINTF("%s", __func__);
> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_GET_CAPABILITY,
> +                         &tpm_pt->caps, 0, sizeof(tpm_pt->caps)) < 0) {
> +        error_report("tpm-emulator: probing failed : %s", strerror(errno));
> +        return -1;
> +    }
> +
> +    tpm_pt->caps = be64_to_cpu(tpm_pt->caps);
> +
> +    DPRINTF("capbilities : 0x%lx", tpm_pt->caps);
> +
> +    return 0;
> +}
> +
> +static int tpm_emulator_check_caps(TPMEmulator *tpm_pt)
> +{
> +    ptm_cap caps = 0;
> +    const char *tpm = NULL;
> +
> +    /* 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-emulator: TPM version has not been set");
> +        return -1;
> +    }
> +
> +    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, caps)) {
> +        error_report("tpm-emulator: TPM does not implement minimum set of "
> +                     "required capabilities for TPM %s (0x%x)", tpm, (int)caps);
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int tpm_emulator_startup_tpm(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    ptm_init init;
> +    ptm_res res;
> +
> +    DPRINTF("%s", __func__);
> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_INIT, &init, sizeof(init),
> +                         sizeof(init)) < 0) {
> +        error_report("tpm-emulator: could not send INIT: %s",
> +                     strerror(errno));
> +        goto err_exit;
> +    }
> +
> +    res = be32_to_cpu(init.u.resp.tpm_result);
> +    if (res) {
> +        error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res);
> +        goto err_exit;
> +    }
> +    return 0;
> +
> +err_exit:
> +    tpm_pt->had_startup_error = true;
> +    return -1;
> +}
> +
> +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    ptm_est est;
> +
> +    DPRINTF("%s", __func__);
> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_GET_TPMESTABLISHED, &est, 0,
> +                         sizeof(est)) < 0) {
> +        error_report("tpm-emulator: Could not get the TPM established flag: %s",
> +                     strerror(errno));
> +        return false;
> +    }
> +    DPRINTF("established flag: %0x", est.u.resp.bit);
> +
> +    return (est.u.resp.bit != 0);
> +}
> +
> +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
> +                                                   uint8_t locty)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    ptm_reset_est reset_est;
> +    ptm_res res;
> +
> +    /* only a TPM 2.0 will support this */
> +    if (tpm_pt->tpm_version == TPM_VERSION_2_0) {
> +        reset_est.u.req.loc = tpm_pt->cur_locty_number;
> +
> +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_RESET_TPMESTABLISHED,
> +                                 &reset_est, sizeof(reset_est),
> +                                 sizeof(reset_est)) < 0) {
> +            error_report("tpm-emulator: Could not reset the establishment bit: "
> +                          "%s", strerror(errno));
> +            return -1;
> +        }
> +
> +        res = be32_to_cpu(reset_est.u.resp.tpm_result);
> +        if (res) {
> +            error_report("tpm-emulator: TPM result for rest establixhed flag: "
> +                         "0x%x", res);
> +            return -1;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static bool tpm_emulator_had_startup_error(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +
> +    return tpm_pt->had_startup_error;
> +}
> +
> +static void tpm_emulator_cancel_cmd(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    ptm_res res;
> +
> +    /*
> +     * As of Linux 3.7 the tpm_tis driver does not properly cancel
> +     * commands on all TPM manufacturers' TPMs.
> +     * Only cancel if we're busy so we don't cancel someone else's
> +     * command, e.g., a command executed on the host.
> +     */
> +    if (tpm_pt->op_executing) {

The field is set in the worker thread. This is racy. Fortunately this
is not relevant for emulator, I think you can simply drop that check
and the variable. Stefan should confirm though.

> +        if (TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, PTM_CAP_CANCEL_TPM_CMD)) {
> +            if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_CANCEL_TPM_CMD,
> +                                     &res, 0, sizeof(res)) < 0) {
> +                error_report("tpm-emulator: Could not cancel command: %s",
> +                             strerror(errno));
> +            } else if (res != 0) {
> +                error_report("tpm-emulator: Failed to cancel TPM: 0x%x",
> +                             be32_to_cpu(res));
> +            } else {
> +                tpm_pt->op_canceled = true;
> +            }
> +        }
> +    }
> +}
> +
> +static void tpm_emulator_reset(TPMBackend *tb)
> +{
> +    DPRINTF("%s", __func__);
> +
> +    tpm_emulator_cancel_cmd(tb);
> +}
> +
> +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +
> +    return tpm_pt->tpm_version;
> +}
> +
> +static void tpm_emulator_block_migration(TPMEmulator *tpm_pt)
> +{
> +    Error *err = NULL;
> +
> +    error_setg(&tpm_pt->migration_blocker,
> +               "Migration disabled: TPM emulator not yet migratable");
> +    migrate_add_blocker(tpm_pt->migration_blocker, &err);
> +    if (err) {
> +        error_free(err);

probably better to report_err it instead

> +        error_free(tpm_pt->migration_blocker);
> +        tpm_pt->migration_blocker = NULL;

and return an error code.

> +    }
> +}
> +
> +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_pt)
> +{
> +    ptm_res res;
> +    Error *err = NULL;
> +    int fds[2] = { -1, -1 };
> +
> +    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) {
> +        error_report("tpm-emulator: Failed to create socketpair");
> +        return -1;
> +    }
> +
> +    qemu_chr_fe_set_msgfds(&tpm_pt->ctrl_dev, fds + 1, 1);
> +
> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SET_DATAFD, &res, 0,
> +                    sizeof(res)) || res != 0) {

Yay! for making life easier and hiding a protocol detail.

> +        error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s",
> +                     strerror(errno));
> +        goto err_exit;
> +    }
> +
> +    tpm_pt->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
> +    if (err) {
> +        error_report("tpm-emulator: Failed to create io channel : %s",
> +                       error_get_pretty(err));

error_prepend()?

> +        error_free(err);
> +        goto err_exit;
> +    }

close fds[1] ?

> +
> +    return 0;
> +
> +err_exit:
> +    closesocket(fds[0]);
> +    closesocket(fds[1]);
> +    return -1;
> +}
> +
> +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_pt, QemuOpts *opts)
> +{
> +    const char *value;
> +
> +    value = qemu_opt_get(opts, "chardev");
> +    if (value) {
> +        Error *err = NULL;
> +        Chardev *dev = qemu_chr_find(value);
> +
> +        tpm_pt->options->chardev = g_strdup(value);
> +
> +        if (!dev || !qemu_chr_fe_init(&tpm_pt->ctrl_dev, dev, &err)) {
> +            error_report("tpm-emulator: No valid chardev found at '%s': %s",
> +                         value, err ? error_get_pretty(err) : "");
> +            error_free(err);

error_prepend

> +            goto err;
> +        }
> +    }
> +
> +    if (tpm_emulator_prepare_data_fd(tpm_pt) < 0) {
> +        goto err;
> +    }
> +
> +    /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used
> +     * by passthrough driver, which not yet using GIOChannel.
> +     */
> +    if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_pt->data_ioc)->fd,
> +                             &tpm_pt->tpm_version)) {
> +        error_report("'%s' is not emulating TPM device. Error: %s",
> +                      tpm_pt->options->chardev, strerror(errno));
> +        goto err;
> +    }
> +
> +    DPRINTF("TPM Version %s", tpm_pt->tpm_version == TPM_VERSION_1_2 ? "1.2" :
> +             (tpm_pt->tpm_version == TPM_VERSION_2_0 ?  "2.0" : "Unspecified"));
> +
> +    if (tpm_emulator_probe_caps(tpm_pt) ||
> +        tpm_emulator_check_caps(tpm_pt)) {
> +        goto err;
> +    }
> +
> +    tpm_emulator_block_migration(tpm_pt);
> +
> +    return 0;
> +
> +err:
> +    DPRINT("Startup error\n");
> +    return -1;
> +}
> +
> +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)
> +{
> +    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;
> +    }
> +
> +    return tb;
> +
> +err_exit:
> +    object_unref(OBJECT(tb));
> +
> +    return NULL;
> +}
> +
> +static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> +    TpmTypeOptions *options = NULL;
> +    TPMEmulatorOptions *eoptions = NULL;
> +
> +    eoptions = g_new0(TPMEmulatorOptions, 1);
> +    if (!eoptions) {
> +        return NULL;
> +    }
> +    DPRINTF("%s", __func__);
> +
> +    eoptions->chardev = g_strdup(tpm_pt->options->chardev);
> +    options = g_new0(TpmTypeOptions, 1);
> +    if (!options) {
> +        qapi_free_TPMEmulatorOptions(eoptions);
> +        return NULL;
> +    }
> +
> +    options->type = TPM_TYPE_EMULATOR;
> +    options->u.emulator.data = eoptions;

I think this is a job for QAPI_CLONE.

> +
> +    return options;
> +}
> +
> +static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
> +    TPM_STANDARD_CMDLINE_OPTS,
> +    {
> +        .name = "chardev",
> +        .type = QEMU_OPT_STRING,
> +        .help = "Character device to use for out-of-band control messages",
> +    },
> +    { /* end of list */ },
> +};
> +
> +static const TPMDriverOps tpm_emulator_driver = {
> +    .type                     = TPM_TYPE_EMULATOR,
> +    .opts                     = tpm_emulator_cmdline_opts,
> +    .desc                     = "TPM emulator backend driver",
> +
> +    .create                   = tpm_emulator_create,
> +    .startup_tpm              = tpm_emulator_startup_tpm,
> +    .reset                    = tpm_emulator_reset,
> +    .had_startup_error        = tpm_emulator_had_startup_error,
> +    .cancel_cmd               = tpm_emulator_cancel_cmd,
> +    .get_tpm_established_flag = tpm_emulator_get_tpm_established_flag,
> +    .reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag,
> +    .get_tpm_version          = tpm_emulator_get_tpm_version,
> +    .get_tpm_options          = tpm_emulator_get_tpm_options,
> +};
> +
> +static void tpm_emulator_inst_init(Object *obj)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
> +
> +    DPRINTF("%s", __func__);
> +    tpm_pt->options = g_new0(TPMEmulatorOptions, 1);
> +    tpm_pt->op_executing = tpm_pt->op_canceled = false;
> +    tpm_pt->had_startup_error = false;
> +    tpm_pt->cur_locty_number = ~0;
> +    qemu_mutex_init(&tpm_pt->state_lock);
> +}
> +
> +static void tpm_emulator_inst_finalize(Object *obj)
> +{
> +    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
> +
> +    tpm_emulator_cancel_cmd(TPM_BACKEND(obj));
> +    tpm_emulator_shutdown(tpm_pt);
> +
> +    if (tpm_pt->data_ioc) {
> +        qio_channel_close(tpm_pt->data_ioc, NULL);
> +    }
> +
> +    qemu_chr_fe_deinit(&tpm_pt->ctrl_dev, false);
> +
> +    if (tpm_pt->options) {
> +        qapi_free_TPMEmulatorOptions(tpm_pt->options);
> +    }
> +
> +    if (tpm_pt->migration_blocker) {
> +        migrate_del_blocker(tpm_pt->migration_blocker);
> +        error_free(tpm_pt->migration_blocker);
> +    }
> +}
> +
> +static void tpm_emulator_class_init(ObjectClass *klass, void *data)
> +{
> +    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
> +    tbc->ops = &tpm_emulator_driver;
> +    tbc->handle_request = tpm_emulator_handle_request;
> +}
> +
> +static const TypeInfo tpm_emulator_info = {
> +    .name = TYPE_TPM_EMULATOR,
> +    .parent = TYPE_TPM_BACKEND,
> +    .instance_size = sizeof(TPMEmulator),
> +    .class_init = tpm_emulator_class_init,
> +    .instance_init = tpm_emulator_inst_init,
> +    .instance_finalize = tpm_emulator_inst_finalize,
> +};
> +
> +static void tpm_emulator_register(void)
> +{
> +    type_register_static(&tpm_emulator_info);
> +    tpm_register_driver(&tpm_emulator_driver);
> +}
> +
> +type_init(tpm_emulator_register)
> diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
> new file mode 100644
> index 0000000..33564b1
> --- /dev/null
> +++ b/hw/tpm/tpm_ioctl.h

This file is copied from swtpm project. Could swtpm have it installed
on system instead?

> @@ -0,0 +1,246 @@
> +/*
> + * tpm_ioctl.h
> + *
> + * (c) Copyright IBM Corporation 2014, 2015.
> + *
> + * This file is licensed under the terms of the 3-clause BSD license
> + */
> +#ifndef _TPM_IOCTL_H_
> +#define _TPM_IOCTL_H_
> +
> +#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 ptm_res as the first element.
> + * ptm_res corresponds to the error code of a command executed by the TPM.
> + */
> +
> +typedef uint32_t ptm_res;
> +
> +/* PTM_GET_TPMESTABLISHED: get the establishment bit */
> +struct ptm_est {
> +    union {
> +        struct {
> +            ptm_res tpm_result;
> +            unsigned char bit; /* TPM established bit */
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */
> +struct ptm_reset_est {
> +    union {
> +        struct {
> +            uint8_t loc; /* locality to use */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* PTM_INIT */
> +struct ptm_init {
> +    union {
> +        struct {
> +            uint32_t init_flags; /* see definitions below */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* above init_flags */
> +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0)
> +    /* delete volatile state file after reading it */
> +
> +/* PTM_SET_LOCALITY */
> +struct ptm_loc {
> +    union {
> +        struct {
> +            uint8_t loc; /* locality to set */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* PTM_HASH_DATA: hash given data */
> +struct ptm_hdata {
> +    union {
> +        struct {
> +            uint32_t length;
> +            uint8_t data[4096];
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } 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 PTM_STATE_BLOB_SIZE (3 * 1024)
> +
> +/*
> + * The following is the data structure to get state blobs from the TPM.
> + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads
> + * with this ioctl and with adjusted offset are necessary. All bytes
> + * must be transferred and the transfer is done once the last byte has been
> + * returned.
> + * It is possible to use the read() interface for reading the data; however, the
> + * first bytes of the state blob will be part of the response to the ioctl(); a
> + * subsequent read() is only necessary if the total length (totlength) exceeds
> + * the number of received bytes. seek() is not supported.
> + */
> +struct ptm_getstate {
> +    union {
> +        struct {
> +            uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */
> +            uint32_t type;        /* which blob to pull */
> +            uint32_t offset;      /* offset from where to read */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +            uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */
> +            uint32_t totlength;   /* total length that will be transferred */
> +            uint32_t length;      /* number of bytes in following buffer */
> +            uint8_t  data[PTM_STATE_BLOB_SIZE];
> +        } resp; /* response */
> +    } 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 PTM_STATE_FLAG_DECRYPTED     1 /* on input:  get decrypted state */
> +#define PTM_STATE_FLAG_ENCRYPTED     2 /* on output: state is encrypted */
> +
> +/*
> + * The following is the data structure to set state blobs in the TPM.
> + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple
> + * 'writes' using this ioctl are necessary. The last packet is indicated
> + * by the length being smaller than the PTM_STATE_BLOB_SIZE.
> + * The very first packet may have a length indicator of '0' enabling
> + * a write() with all the bytes from a buffer. If the write() interface
> + * is used, a final ioctl with a non-full buffer must be made to indicate
> + * that all data were transferred (a write with 0 bytes would not work).
> + */
> +struct ptm_setstate {
> +    union {
> +        struct {
> +            uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */
> +            uint32_t type;        /* which blob to set */
> +            uint32_t length;      /* length of the data;
> +                                     use 0 on the first packet to
> +                                     transfer using write() */
> +            uint8_t data[PTM_STATE_BLOB_SIZE];
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/*
> + * PTM_GET_CONFIG: Data structure to get runtime configuration information
> + * such as which keys are applied.
> + */
> +struct ptm_getconfig {
> +    union {
> +        struct {
> +            ptm_res tpm_result;
> +            uint32_t flags;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +#define PTM_CONFIG_FLAG_FILE_KEY        0x1
> +#define PTM_CONFIG_FLAG_MIGRATION_KEY   0x2
> +
> +
> +typedef uint64_t ptm_cap;
> +typedef struct ptm_est ptm_est;
> +typedef struct ptm_reset_est ptm_reset_est;
> +typedef struct ptm_loc ptm_loc;
> +typedef struct ptm_hdata ptm_hdata;
> +typedef struct ptm_init ptm_init;
> +typedef struct ptm_getstate ptm_getstate;
> +typedef struct ptm_setstate ptm_setstate;
> +typedef struct ptm_getconfig ptm_getconfig;
> +
> +/* 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)
> +#define PTM_CAP_GET_CONFIG         (1 << 11)
> +#define PTM_CAP_SET_DATAFD         (1 << 12)
> +
> +enum {
> +    PTM_GET_CAPABILITY     = _IOR('P', 0, ptm_cap),
> +    PTM_INIT               = _IOWR('P', 1, ptm_init),
> +    PTM_SHUTDOWN           = _IOR('P', 2, ptm_res),
> +    PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est),
> +    PTM_SET_LOCALITY       = _IOWR('P', 4, ptm_loc),
> +    PTM_HASH_START         = _IOR('P', 5, ptm_res),
> +    PTM_HASH_DATA          = _IOWR('P', 6, ptm_hdata),
> +    PTM_HASH_END           = _IOR('P', 7, ptm_res),
> +    PTM_CANCEL_TPM_CMD     = _IOR('P', 8, ptm_res),
> +    PTM_STORE_VOLATILE     = _IOR('P', 9, ptm_res),
> +    PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est),
> +    PTM_GET_STATEBLOB      = _IOWR('P', 11, ptm_getstate),
> +    PTM_SET_STATEBLOB      = _IOWR('P', 12, ptm_setstate),
> +    PTM_STOP               = _IOR('P', 13, ptm_res),
> +    PTM_GET_CONFIG         = _IOR('P', 14, ptm_getconfig),
> +    PTM_SET_DATAFD         = _IOR('P', 15, ptm_res),
> +};
> +
> +/*
> + * Commands used by the non-CUSE TPMs
> + *
> + * All messages container big-endian data.
> + *
> + * The return messages only contain the 'resp' part of the unions
> + * in the data structures above. Besides that the limits in the
> + * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data
> + * and ptm_set_state:u.req.data) are 0xffffffff.
> + */
> +enum {
> +    CMD_GET_CAPABILITY = 1,
> +    CMD_INIT,
> +    CMD_SHUTDOWN,
> +    CMD_GET_TPMESTABLISHED,
> +    CMD_SET_LOCALITY,
> +    CMD_HASH_START,
> +    CMD_HASH_DATA,
> +    CMD_HASH_END,
> +    CMD_CANCEL_TPM_CMD,
> +    CMD_STORE_VOLATILE,
> +    CMD_RESET_TPMESTABLISHED,
> +    CMD_GET_STATEBLOB,
> +    CMD_SET_STATEBLOB,
> +    CMD_STOP,
> +    CMD_GET_CONFIG,
> +    CMD_SET_DATAFD
> +};
> +
> +#endif /* _TPM_IOCTL_H */
> diff --git a/qapi/tpm.json b/qapi/tpm.json
> index e8b2d8d..7093f26 100644
> --- a/qapi/tpm.json
> +++ b/qapi/tpm.json
> @@ -39,10 +39,12 @@
>  # An enumeration of TPM types
>  #
>  # @passthrough: TPM passthrough type
> +# @emulator: Software Emulator TPM type
> +#            Since: 2.11
>  #
>  # Since: 1.5
>  ##
> -{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }
> +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] }
>
>  ##
>  # @query-tpm-types:
> @@ -56,7 +58,7 @@
>  # Example:
>  #
>  # -> { "execute": "query-tpm-types" }
> -# <- { "return": [ "passthrough" ] }
> +# <- { "return": [ "passthrough", "emulator" ] }
>  #
>  ##
>  { 'command': 'query-tpm-types', 'returns': ['TpmType'] }
> @@ -77,16 +79,29 @@
>                                               '*cancel-path' : 'str'} }
>
>  ##
> +# @TPMEmulatorOptions:
> +#
> +# Information about the TPM emulator type
> +#
> +# @chardev: Name of a unix socket chardev
> +#
> +# Since: 2.11
> +##
> +{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } }
> +
> +##
>  # @TpmTypeOptions:
>  #
>  # A union referencing different TPM backend types' configuration options
>  #
>  # @type: 'passthrough' The configuration options for the TPM passthrough type
> +#        'emulator' The configuration options for TPM emulator backend type
>  #
>  # Since: 1.5
>  ##
>  { 'union': 'TpmTypeOptions',
> -   'data': { 'passthrough' : 'TPMPassthroughOptions' } }
> +   'data': { 'passthrough' : 'TPMPassthroughOptions',
> +             'emulator': 'TPMEmulatorOptions' } }
>
>  ##
>  # @TPMInfo:
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 77859a2..1e93e53 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -3121,7 +3121,9 @@ 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 emulator,id=id,chardev=dev\n"
> +    "                configure the TPM device using chardev backend\n",
>      QEMU_ARCH_ALL)
>  STEXI
>
> @@ -3130,8 +3132,8 @@ The general form of a TPM device option is:
>
>  @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
>  @findex -tpmdev
> -Backend type must be:
> -@option{passthrough}.
> +Backend type must be either one of the following:
> +@option{passthrough}, @option{emulator}.
>
>  The specific backend type will determine the applicable options.
>  The @code{-tpmdev} option creates the TPM backend and requires a
> @@ -3181,6 +3183,20 @@ 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.
>
> +@item -tpmdev emulator, id=@var{id}, chardev=@var{dev}
> +
> +(Linux-host only) Enable access to a TPM emulator using Unix domain socket based
> +chardev backend.
> +
> +@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server.
> +
> +To create a TPM emulator backend device with chardev socket backend:
> +@example
> +
> +-chardev socket,id=chrtpm,path=/tmp/swtpm-ctrl -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
> +
> +@end example
> +
>  @end table
>
>  ETEXI
> --
> 2.7.4
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator
  2017-09-24 18:52   ` Marc-André Lureau
@ 2017-09-25  0:35     ` Stefan Berger
  2017-09-26 12:05     ` Valluri, Amarnath
  2017-09-27 12:18     ` [Qemu-devel] [PATCH v8 " Amarnath Valluri
  2 siblings, 0 replies; 23+ messages in thread
From: Stefan Berger @ 2017-09-25  0:35 UTC (permalink / raw)
  To: Marc-André Lureau, Amarnath Valluri
  Cc: Markus Armbruster, QEMU, Dr. David Alan Gilbert

On 09/24/2017 02:52 PM, Marc-André Lureau wrote:
> Hi
>
> Thanks for the nice update, removing the exec() code, using chardev
> and a private socketpair. Some comments below:
>
> On Fri, Sep 22, 2017 at 2:33 PM, Amarnath Valluri
> <amarnath.valluri@intel.com> wrote:
>> This change introduces a new TPM backend driver that can communicate with
>> swtpm(software TPM emulator) using unix domain socket interface. QEMU talks to
>> TPM emulator using socket based chardev backend device.
>>
>> Swtpm uses two Unix sockets for communications, one for plain TPM commands and
>> responses, and one for out-of-band control messages. QEMU passes data socket
>> been used over the control channel.
>>
>> The swtpm and associated tools can be found here:
>>      https://github.com/stefanberger/swtpm
>>
>> The swtpm's control channel protocol specification can be found here:
>>      https://github.com/stefanberger/swtpm/wiki/Control-Channel-Specification
>>
>> Usage:
>>      # setup TPM state directory
>>      mkdir /tmp/mytpm
>>      chown -R tss:root /tmp/mytpm
>>      /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek
>>
>>      # Ask qemu to use TPM emulator with given tpm state directory
>>      qemu-system-x86_64 \
>>          [...] \
>>          -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \
>>          -tpmdev emulator,id=tpm0,chardev=chrtpm \
>>          -device tpm-tis,tpmdev=tpm0 \
>>          [...]
>>
>> Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
>> ---
>>   configure             |  15 +-
>>   hmp.c                 |   5 +
>>   hw/tpm/Makefile.objs  |   1 +
>>   hw/tpm/tpm_emulator.c | 649 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>   hw/tpm/tpm_ioctl.h    | 246 +++++++++++++++++++
>>   qapi/tpm.json         |  21 +-
>>   qemu-options.hx       |  22 +-
>>   7 files changed, 950 insertions(+), 9 deletions(-)
>>   create mode 100644 hw/tpm/tpm_emulator.c
>>   create mode 100644 hw/tpm/tpm_ioctl.h
>>
>> diff --git a/configure b/configure
>> index cb0f7ed..ce2df2d 100755
>> --- a/configure
>> +++ b/configure
>> @@ -3461,10 +3461,15 @@ fi
>>   ##########################################
>>   # TPM passthrough is only on x86 Linux
>>
>> -if test "$targetos" = Linux && test "$cpu" = i386 -o "$cpu" = x86_64; then
>> -  tpm_passthrough=$tpm
>> +if test "$targetos" = Linux; then
>> +  tpm_emulator=$tpm
>> +  if test "$cpu" = i386 -o "$cpu" = x86_64; then
>> +    tpm_passthrough=$tpm
>> +  else
>> +    tpm_passthrough=no
>> +  fi
>>   else
>> -  tpm_passthrough=no
>> +  tpm_emulator=no
>>   fi
>>
>>   ##########################################
>> @@ -5359,6 +5364,7 @@ echo "gcov enabled      $gcov"
>>   echo "TPM support       $tpm"
>>   echo "libssh2 support   $libssh2"
>>   echo "TPM passthrough   $tpm_passthrough"
>> +echo "TPM emulator      $tpm_emulator"
>>   echo "QOM debugging     $qom_cast_debug"
>>   echo "Live block migration $live_block_migration"
>>   echo "lzo support       $lzo"
>> @@ -5943,6 +5949,9 @@ if test "$tpm" = "yes"; then
>>     if test "$tpm_passthrough" = "yes"; then
>>       echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
>>     fi
>> +  if test "$tpm_emulator" = "yes"; then
>> +    echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak
> It shouldn't require Linux, but posix (and I assume a port to other
> systems isn't impossible). same for build-sys / help / comments.
>
>> +  fi
>>   fi
>>
>>   echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
>> diff --git a/hmp.c b/hmp.c
>> index cf62b2e..7e69eca 100644
>> --- a/hmp.c
>> +++ b/hmp.c
>> @@ -995,6 +995,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
>>       Error *err = NULL;
>>       unsigned int c = 0;
>>       TPMPassthroughOptions *tpo;
>> +    TPMEmulatorOptions *teo;
>>
>>       info_list = qmp_query_tpm(&err);
>>       if (err) {
>> @@ -1024,6 +1025,10 @@ 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_EMULATOR:
>> +            teo = ti->options->u.emulator.data;
>> +            monitor_printf(mon, ",chardev=%s", teo->chardev);
>> +            break;
>>           case TPM_TYPE__MAX:
>>               break;
>>           }
>> diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
>> index 64cecc3..41f0b7a 100644
>> --- a/hw/tpm/Makefile.objs
>> +++ b/hw/tpm/Makefile.objs
>> @@ -1,2 +1,3 @@
>>   common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
>>   common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
>> +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
>> diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
>> new file mode 100644
>> index 0000000..c02bbe2
>> --- /dev/null
>> +++ b/hw/tpm/tpm_emulator.c
>> @@ -0,0 +1,649 @@
>> +/*
>> + *  emulator TPM driver
>> + *
>> + *  Copyright (c) 2017 Intel Corporation
>> + *  Author: Amarnath Valluri <amarnath.valluri@intel.com>
>> + *
>> + *  Copyright (c) 2010 - 2013 IBM Corporation
>> + *  Authors:
>> + *    Stefan Berger <stefanb@us.ibm.com>
>> + *
>> + *  Copyright (C) 2011 IAIK, Graz University of Technology
>> + *    Author: Andreas Niederl
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>
>> + *
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu/error-report.h"
>> +#include "qemu/sockets.h"
>> +#include "io/channel-socket.h"
>> +#include "sysemu/tpm_backend.h"
>> +#include "tpm_int.h"
>> +#include "hw/hw.h"
>> +#include "hw/i386/pc.h"
>> +#include "tpm_util.h"
>> +#include "tpm_ioctl.h"
>> +#include "migration/blocker.h"
>> +#include "qapi/error.h"
>> +#include "chardev/char-fe.h"
>> +
>> +#include <fcntl.h>
>> +#include <sys/types.h>
>> +#include <sys/stat.h>
>> +#include <stdio.h>
>> +
>> +#define DEBUG_TPM 0
>> +
>> +#define DPRINT(fmt, ...) do { \
>> +    if (DEBUG_TPM) { \
>> +        fprintf(stderr, fmt, ## __VA_ARGS__); \
>> +    } \
>> +} while (0);
>> +
>> +#define DPRINTF(fmt, ...) DPRINT("tpm-emulator: "fmt"\n", __VA_ARGS__)
> I would define a single DPRINTF() (& drop DPRINT usage below)
>
>
>> +
>> +#define TYPE_TPM_EMULATOR "tpm-emulator"
>> +#define TPM_EMULATOR(obj) \
>> +    OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
>> +
>> +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))
>> +
>> +static const TPMDriverOps tpm_emulator_driver;
>> +
>> +/* data structures */
>> +typedef struct TPMEmulator {
>> +    TPMBackend parent;
>> +
>> +    TPMEmulatorOptions *options;
> Contrary to my comment on previous patch, I realize it is convenient
> to have a qapi pointer here, so you can use the free visitor later.
>
>> +    CharBackend ctrl_dev;
> ctrl_chr perhaps? or just chr or be (the most common name in other devices).
>
>> +    QIOChannel *data_ioc;
>> +    bool op_executing;
>> +    bool op_canceled;
> I think those 2 fields can be dropped, see below.

I agree. It's not necessary for the emulator driver to keep track of 
these states.


>
>> +    bool had_startup_error;
> I think some little refactoring could remove the whole
> had_startup_error() backend & callback before or after the series.
> tpm_backend_startup_tpm() already returns an error code.  device or
> common backend code could handle & remember that.
>
>> +    TPMVersion tpm_version;
> This is probably worth to consider in common code instead, but let's
> not worry about it.
>
>> +    ptm_cap caps; /* capabilities of the TPM */
>> +    uint8_t cur_locty_number; /* last set locality */
>> +    QemuMutex state_lock;
>> +    Error *migration_blocker;
>> +} TPMEmulator;
>> +
>> +
>> +static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg,
>> +                                size_t msg_len_in, size_t msg_len_out)
>> +{
>> +    uint32_t cmd_no = cpu_to_be32(cmd);
>> +    ssize_t n = sizeof(uint32_t) + msg_len_in;
>> +    uint8_t *buf = NULL;
>> +
>> +    buf = (uint8_t *)malloc(n);
> could be g_alloca() instead. Alternatively, why not call 2 write() instead?

I think for swtpm the commands should arrive in one chunk.


>
> none of the casts in this function are necessary, please remove them.
>
>> +    memcpy(buf, &cmd_no, sizeof(cmd_no));
>> +    memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
>> +
>> +    n += qemu_chr_fe_write_all(dev, (const uint8_t *)buf, n);
> weird
>
>> +    free(buf);
>> +
>> +    if (n > 0) {
> with the n += above, you'll get interesting behaviour :)
>
> You probably want to check if any write above failed, and return early
> for the error case.
>
> Please improve the error handling in this function
>
>> +        if (msg_len_out > 0) {
>> +            n = qemu_chr_fe_read_all(dev, (uint8_t *)msg, msg_len_out);
>> +            /* simulate ioctl return value */
>> +            if (n > 0) {
>> +                n = 0;
>> +            }
>> +        } else {
>> +            n = 0;
>> +        }
>> +    }
>> +    return n;
>> +}
>> +
>> +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_pt,
> tpm_pt was tpm_passthrough I suppose.
>
> Please rename tpm_pt tpm_emu (or I would suggest "self", but this
> isn't common name in qemu code - it's actually common in a close
> c-object world., and it is quite convenient...)
>
>> +                                     const uint8_t *in, uint32_t in_len,
>> +                                     uint8_t *out, uint32_t out_len,
>> +                                     bool *selftest_done)
>> +{
>> +    ssize_t ret;
>> +    bool is_selftest = false;
>> +    const struct tpm_resp_hdr *hdr = NULL;
>> +    Error *err = NULL;
>> +
>> +    tpm_pt->op_canceled = false;
>> +    tpm_pt->op_executing = true;
>> +    if (selftest_done) {
>> +        *selftest_done = false;
>> +        is_selftest = tpm_util_is_selftest(in, in_len);
>> +    }
>> +
>> +    ret = qio_channel_write(tpm_pt->data_ioc, (char *)in, in_len, &err);
> hmm, too bad qio_channel_write() doesn't take a void *
>
> why not write_all()?
>
>> +    if (ret != in_len || err) {
>> +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
> I don't think ECANCELED make sense for emulator code.
>
>> +            error_report("tpm-emulator: error while transmitting data "
>> +                         "to TPM: %s", err ? error_get_pretty(err) : "");
>> +            error_free(err);
>> +        }
>> +        goto err_exit;
>> +    }
>> +
>> +    tpm_pt->op_executing = false;
>> +
>> +    ret = qio_channel_read(tpm_pt->data_ioc, (char *)out, out_len, &err);
>> +    if (ret < 0 || err) {
> read_all() ?
>
>> +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
>> +            error_report("tpm-emulator: error while reading data from "
>> +                         "TPM: %s", err ? error_get_pretty(err) : "");
>> +            error_free(err);
>> +        }
>> +    } else if (ret >= sizeof(*hdr)) {
>> +        hdr = (struct tpm_resp_hdr *)out;
>> +    }
>> +
>> +    if (!hdr || be32_to_cpu(hdr->len) != ret) {
>> +        error_report("tpm-emulator: received invalid response "
>> +                     "packet from TPM with length :%ld", ret);
>> +        ret = -1;
>> +        goto err_exit;
>> +    }
>> +
>> +    if (is_selftest) {
>> +        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
>> +    }
>> +
>> +    return 0;
>> +
>> +err_exit:
>> +    if (ret < 0) {
>> +        tpm_util_write_fatal_error_response(out, out_len);
>> +    }
>> +
>> +    tpm_pt->op_executing = false;
>> +
>> +    return ret;
>> +}
>> +
>> +static int tpm_emulator_set_locality(TPMEmulator *tpm_pt, uint8_t locty_number)
>> +{
>> +    ptm_loc loc;
>> +
>> +    DPRINTF("%s : locality: 0x%x", __func__, locty_number);
>> +
>> +    if (tpm_pt->cur_locty_number != locty_number) {
> return early instead if ==, to avoid code indent etc
>
>> +        DPRINTF("setting locality : 0x%x", locty_number);
>> +        loc.u.req.loc = locty_number;
> This number isn't set in be like the rest of the protocol?
>
>> +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SET_LOCALITY, &loc,
>> +                             sizeof(loc), sizeof(loc)) < 0) {
>> +            error_report("tpm-emulator: could not set locality : %s",
>> +                         strerror(errno));
>> +            return -1;
>> +        }
>> +        loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result);
>> +        if (loc.u.resp.tpm_result != 0) {
>> +            error_report("tpm-emulator: TPM result for set locality : 0x%x",
>> +                         loc.u.resp.tpm_result);
>> +            return -1;
>> +        }
>> +        tpm_pt->cur_locty_number = locty_number;
>> +    }
>> +    return 0;
>> +}
>> +
>> +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
>> +{
>> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> +    TPMLocality *locty = NULL;
>> +    bool selftest_done = false;
>> +
>> +    DPRINTF("processing command type %d", cmd);
>> +
>> +    switch (cmd) {
>> +    case TPM_BACKEND_CMD_PROCESS_CMD:
>> +        qemu_mutex_lock(&tpm_pt->state_lock);
>> +        locty = tb->tpm_state->locty_data;
>> +        if (tpm_emulator_set_locality(tpm_pt,
>> +                                      tb->tpm_state->locty_number) < 0) {
>> +            tpm_util_write_fatal_error_response(locty->r_buffer.buffer,
>> +                                           locty->r_buffer.size);
> return / goto here instead of else.
>
>> +        } else {
>> +            tpm_emulator_unix_tx_bufs(tpm_pt, locty->w_buffer.buffer,
>> +                                              locty->w_offset,
>> +                                              locty->r_buffer.buffer,
>> +                                              locty->r_buffer.size,
>> +                                              &selftest_done);
> no error handling?
>
>> +        }
>> +
>> +        tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number,
>> +                               selftest_done);
>> +        qemu_mutex_unlock(&tpm_pt->state_lock);
>> +
>> +        break;
>> +    case TPM_BACKEND_CMD_INIT:
>> +    case TPM_BACKEND_CMD_END:
>> +    case TPM_BACKEND_CMD_TPM_RESET:
>> +        /* nothing to do */
>> +        break;
>> +    }
>> +}
>> +
>> +/*
>> + * Gracefully shut down the external unixio TPM
>> + */
>> +static void tpm_emulator_shutdown(TPMEmulator *tpm_pt)
>> +{
>> +    ptm_res res;
>> +
>> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SHUTDOWN, &res, 0,
>> +                         sizeof(res)) < 0) {
>> +        error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s",
>> +                     strerror(errno));
>> +    } else if (res != 0) {
>> +        error_report("tpm-emulator: TPM result for sutdown: 0x%x",
>> +                     be32_to_cpu(res));
>> +    }
>> +}
>> +
>> +static int tpm_emulator_probe_caps(TPMEmulator *tpm_pt)
>> +{
>> +    DPRINTF("%s", __func__);
>> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_GET_CAPABILITY,
>> +                         &tpm_pt->caps, 0, sizeof(tpm_pt->caps)) < 0) {
>> +        error_report("tpm-emulator: probing failed : %s", strerror(errno));
>> +        return -1;
>> +    }
>> +
>> +    tpm_pt->caps = be64_to_cpu(tpm_pt->caps);
>> +
>> +    DPRINTF("capbilities : 0x%lx", tpm_pt->caps);
>> +
>> +    return 0;
>> +}
>> +
>> +static int tpm_emulator_check_caps(TPMEmulator *tpm_pt)
>> +{
>> +    ptm_cap caps = 0;
>> +    const char *tpm = NULL;
>> +
>> +    /* 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-emulator: TPM version has not been set");
>> +        return -1;
>> +    }
>> +
>> +    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, caps)) {
>> +        error_report("tpm-emulator: TPM does not implement minimum set of "
>> +                     "required capabilities for TPM %s (0x%x)", tpm, (int)caps);
>> +        return -1;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int tpm_emulator_startup_tpm(TPMBackend *tb)
>> +{
>> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> +    ptm_init init;
>> +    ptm_res res;
>> +
>> +    DPRINTF("%s", __func__);
>> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_INIT, &init, sizeof(init),
>> +                         sizeof(init)) < 0) {
>> +        error_report("tpm-emulator: could not send INIT: %s",
>> +                     strerror(errno));
>> +        goto err_exit;
>> +    }
>> +
>> +    res = be32_to_cpu(init.u.resp.tpm_result);
>> +    if (res) {
>> +        error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res);
>> +        goto err_exit;
>> +    }
>> +    return 0;
>> +
>> +err_exit:
>> +    tpm_pt->had_startup_error = true;
>> +    return -1;
>> +}
>> +
>> +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
>> +{
>> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> +    ptm_est est;
>> +
>> +    DPRINTF("%s", __func__);
>> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_GET_TPMESTABLISHED, &est, 0,
>> +                         sizeof(est)) < 0) {
>> +        error_report("tpm-emulator: Could not get the TPM established flag: %s",
>> +                     strerror(errno));
>> +        return false;
>> +    }
>> +    DPRINTF("established flag: %0x", est.u.resp.bit);
>> +
>> +    return (est.u.resp.bit != 0);
>> +}
>> +
>> +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
>> +                                                   uint8_t locty)
>> +{
>> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> +    ptm_reset_est reset_est;
>> +    ptm_res res;
>> +
>> +    /* only a TPM 2.0 will support this */
>> +    if (tpm_pt->tpm_version == TPM_VERSION_2_0) {
>> +        reset_est.u.req.loc = tpm_pt->cur_locty_number;
>> +
>> +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_RESET_TPMESTABLISHED,
>> +                                 &reset_est, sizeof(reset_est),
>> +                                 sizeof(reset_est)) < 0) {
>> +            error_report("tpm-emulator: Could not reset the establishment bit: "
>> +                          "%s", strerror(errno));
>> +            return -1;
>> +        }
>> +
>> +        res = be32_to_cpu(reset_est.u.resp.tpm_result);
>> +        if (res) {
>> +            error_report("tpm-emulator: TPM result for rest establixhed flag: "
>> +                         "0x%x", res);
>> +            return -1;
>> +        }
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static bool tpm_emulator_had_startup_error(TPMBackend *tb)
>> +{
>> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> +
>> +    return tpm_pt->had_startup_error;
>> +}
>> +
>> +static void tpm_emulator_cancel_cmd(TPMBackend *tb)
>> +{
>> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> +    ptm_res res;
>> +
>> +    /*
>> +     * As of Linux 3.7 the tpm_tis driver does not properly cancel
>> +     * commands on all TPM manufacturers' TPMs.
>> +     * Only cancel if we're busy so we don't cancel someone else's
>> +     * command, e.g., a command executed on the host.
>> +     */
>> +    if (tpm_pt->op_executing) {
> The field is set in the worker thread. This is racy. Fortunately this
> is not relevant for emulator, I think you can simply drop that check
> and the variable. Stefan should confirm though.
>
>> +        if (TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, PTM_CAP_CANCEL_TPM_CMD)) {
>> +            if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_CANCEL_TPM_CMD,
>> +                                     &res, 0, sizeof(res)) < 0) {
>> +                error_report("tpm-emulator: Could not cancel command: %s",
>> +                             strerror(errno));
>> +            } else if (res != 0) {
>> +                error_report("tpm-emulator: Failed to cancel TPM: 0x%x",
>> +                             be32_to_cpu(res));
>> +            } else {
>> +                tpm_pt->op_canceled = true;
>> +            }
>> +        }
>> +    }
>> +}
>> +
>> +static void tpm_emulator_reset(TPMBackend *tb)
>> +{
>> +    DPRINTF("%s", __func__);
>> +
>> +    tpm_emulator_cancel_cmd(tb);
>> +}
>> +
>> +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
>> +{
>> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> +
>> +    return tpm_pt->tpm_version;
>> +}
>> +
>> +static void tpm_emulator_block_migration(TPMEmulator *tpm_pt)
>> +{
>> +    Error *err = NULL;
>> +
>> +    error_setg(&tpm_pt->migration_blocker,
>> +               "Migration disabled: TPM emulator not yet migratable");
>> +    migrate_add_blocker(tpm_pt->migration_blocker, &err);
>> +    if (err) {
>> +        error_free(err);
> probably better to report_err it instead
>
>> +        error_free(tpm_pt->migration_blocker);
>> +        tpm_pt->migration_blocker = NULL;
> and return an error code.
>
>> +    }
>> +}
>> +
>> +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_pt)
>> +{
>> +    ptm_res res;
>> +    Error *err = NULL;
>> +    int fds[2] = { -1, -1 };
>> +
>> +    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) {
>> +        error_report("tpm-emulator: Failed to create socketpair");
>> +        return -1;
>> +    }
>> +
>> +    qemu_chr_fe_set_msgfds(&tpm_pt->ctrl_dev, fds + 1, 1);
>> +
>> +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SET_DATAFD, &res, 0,
>> +                    sizeof(res)) || res != 0) {
> Yay! for making life easier and hiding a protocol detail.
>
>> +        error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s",
>> +                     strerror(errno));
>> +        goto err_exit;
>> +    }
>> +
>> +    tpm_pt->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
>> +    if (err) {
>> +        error_report("tpm-emulator: Failed to create io channel : %s",
>> +                       error_get_pretty(err));
> error_prepend()?
>
>> +        error_free(err);
>> +        goto err_exit;
>> +    }
> close fds[1] ?
>
>> +
>> +    return 0;
>> +
>> +err_exit:
>> +    closesocket(fds[0]);
>> +    closesocket(fds[1]);
>> +    return -1;
>> +}
>> +
>> +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_pt, QemuOpts *opts)
>> +{
>> +    const char *value;
>> +
>> +    value = qemu_opt_get(opts, "chardev");
>> +    if (value) {
>> +        Error *err = NULL;
>> +        Chardev *dev = qemu_chr_find(value);
>> +
>> +        tpm_pt->options->chardev = g_strdup(value);
>> +
>> +        if (!dev || !qemu_chr_fe_init(&tpm_pt->ctrl_dev, dev, &err)) {
>> +            error_report("tpm-emulator: No valid chardev found at '%s': %s",
>> +                         value, err ? error_get_pretty(err) : "");
>> +            error_free(err);
> error_prepend
>
>> +            goto err;
>> +        }
>> +    }
>> +
>> +    if (tpm_emulator_prepare_data_fd(tpm_pt) < 0) {
>> +        goto err;
>> +    }
>> +
>> +    /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used
>> +     * by passthrough driver, which not yet using GIOChannel.
>> +     */
>> +    if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_pt->data_ioc)->fd,
>> +                             &tpm_pt->tpm_version)) {
>> +        error_report("'%s' is not emulating TPM device. Error: %s",
>> +                      tpm_pt->options->chardev, strerror(errno));
>> +        goto err;
>> +    }
>> +
>> +    DPRINTF("TPM Version %s", tpm_pt->tpm_version == TPM_VERSION_1_2 ? "1.2" :
>> +             (tpm_pt->tpm_version == TPM_VERSION_2_0 ?  "2.0" : "Unspecified"));
>> +
>> +    if (tpm_emulator_probe_caps(tpm_pt) ||
>> +        tpm_emulator_check_caps(tpm_pt)) {
>> +        goto err;
>> +    }
>> +
>> +    tpm_emulator_block_migration(tpm_pt);
>> +
>> +    return 0;
>> +
>> +err:
>> +    DPRINT("Startup error\n");
>> +    return -1;
>> +}
>> +
>> +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)
>> +{
>> +    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;
>> +    }
>> +
>> +    return tb;
>> +
>> +err_exit:
>> +    object_unref(OBJECT(tb));
>> +
>> +    return NULL;
>> +}
>> +
>> +static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
>> +{
>> +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> +    TpmTypeOptions *options = NULL;
>> +    TPMEmulatorOptions *eoptions = NULL;
>> +
>> +    eoptions = g_new0(TPMEmulatorOptions, 1);
>> +    if (!eoptions) {
>> +        return NULL;
>> +    }
>> +    DPRINTF("%s", __func__);
>> +
>> +    eoptions->chardev = g_strdup(tpm_pt->options->chardev);
>> +    options = g_new0(TpmTypeOptions, 1);
>> +    if (!options) {
>> +        qapi_free_TPMEmulatorOptions(eoptions);
>> +        return NULL;
>> +    }
>> +
>> +    options->type = TPM_TYPE_EMULATOR;
>> +    options->u.emulator.data = eoptions;
> I think this is a job for QAPI_CLONE.
>
>> +
>> +    return options;
>> +}
>> +
>> +static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
>> +    TPM_STANDARD_CMDLINE_OPTS,
>> +    {
>> +        .name = "chardev",
>> +        .type = QEMU_OPT_STRING,
>> +        .help = "Character device to use for out-of-band control messages",
>> +    },
>> +    { /* end of list */ },
>> +};
>> +
>> +static const TPMDriverOps tpm_emulator_driver = {
>> +    .type                     = TPM_TYPE_EMULATOR,
>> +    .opts                     = tpm_emulator_cmdline_opts,
>> +    .desc                     = "TPM emulator backend driver",
>> +
>> +    .create                   = tpm_emulator_create,
>> +    .startup_tpm              = tpm_emulator_startup_tpm,
>> +    .reset                    = tpm_emulator_reset,
>> +    .had_startup_error        = tpm_emulator_had_startup_error,
>> +    .cancel_cmd               = tpm_emulator_cancel_cmd,
>> +    .get_tpm_established_flag = tpm_emulator_get_tpm_established_flag,
>> +    .reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag,
>> +    .get_tpm_version          = tpm_emulator_get_tpm_version,
>> +    .get_tpm_options          = tpm_emulator_get_tpm_options,
>> +};
>> +
>> +static void tpm_emulator_inst_init(Object *obj)
>> +{
>> +    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
>> +
>> +    DPRINTF("%s", __func__);
>> +    tpm_pt->options = g_new0(TPMEmulatorOptions, 1);
>> +    tpm_pt->op_executing = tpm_pt->op_canceled = false;
>> +    tpm_pt->had_startup_error = false;
>> +    tpm_pt->cur_locty_number = ~0;
>> +    qemu_mutex_init(&tpm_pt->state_lock);
>> +}
>> +
>> +static void tpm_emulator_inst_finalize(Object *obj)
>> +{
>> +    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
>> +
>> +    tpm_emulator_cancel_cmd(TPM_BACKEND(obj));
>> +    tpm_emulator_shutdown(tpm_pt);
>> +
>> +    if (tpm_pt->data_ioc) {
>> +        qio_channel_close(tpm_pt->data_ioc, NULL);
>> +    }
>> +
>> +    qemu_chr_fe_deinit(&tpm_pt->ctrl_dev, false);
>> +
>> +    if (tpm_pt->options) {
>> +        qapi_free_TPMEmulatorOptions(tpm_pt->options);
>> +    }
>> +
>> +    if (tpm_pt->migration_blocker) {
>> +        migrate_del_blocker(tpm_pt->migration_blocker);
>> +        error_free(tpm_pt->migration_blocker);
>> +    }
>> +}
>> +
>> +static void tpm_emulator_class_init(ObjectClass *klass, void *data)
>> +{
>> +    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
>> +    tbc->ops = &tpm_emulator_driver;
>> +    tbc->handle_request = tpm_emulator_handle_request;
>> +}
>> +
>> +static const TypeInfo tpm_emulator_info = {
>> +    .name = TYPE_TPM_EMULATOR,
>> +    .parent = TYPE_TPM_BACKEND,
>> +    .instance_size = sizeof(TPMEmulator),
>> +    .class_init = tpm_emulator_class_init,
>> +    .instance_init = tpm_emulator_inst_init,
>> +    .instance_finalize = tpm_emulator_inst_finalize,
>> +};
>> +
>> +static void tpm_emulator_register(void)
>> +{
>> +    type_register_static(&tpm_emulator_info);
>> +    tpm_register_driver(&tpm_emulator_driver);
>> +}
>> +
>> +type_init(tpm_emulator_register)
>> diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
>> new file mode 100644
>> index 0000000..33564b1
>> --- /dev/null
>> +++ b/hw/tpm/tpm_ioctl.h
> This file is copied from swtpm project. Could swtpm have it installed
> on system instead?


I think we'll have to carry this file here for a while until swtpm is 
packaged and distros provide it.

    Stefan

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v7 5/8] tmp backend: Add new api to read backend TpmInfo
  2017-09-22 15:35   ` Marc-André Lureau
@ 2017-09-25 12:01     ` Valluri, Amarnath
  2017-09-27 12:16     ` [Qemu-devel] [PATCH v8 " Amarnath Valluri
  1 sibling, 0 replies; 23+ messages in thread
From: Valluri, Amarnath @ 2017-09-25 12:01 UTC (permalink / raw)
  To: marcandre.lureau@gmail.com
  Cc: stefanb@linux.vnet.ibm.com, dgilbert@redhat.com,
	qemu-devel@nongnu.org

On Fri, 2017-09-22 at 17:35 +0200, Marc-André Lureau wrote:
> Hi
> 
> On Fri, Sep 22, 2017 at 2:33 PM, Amarnath Valluri
> <amarnath.valluri@intel.com> wrote:
> > 
> > TPM configuration options are backend implementation details and
> > shall not be
> > part of base TPMBackend object, and these shall not be accessed
> > directly outside
> > of the class, hence added a new interface method, get_tpm_options()
> > to
> > TPMDriverOps., which shall be implemented by the derived classes to
> > return
> > configured tpm options.
> > 
> > A new tpm backend api - tpm_backend_query_tpm() which uses
> > _get_tpm_options() to
> > prepare TpmInfo.
> > 
> > Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
> > Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> > ---
> >  backends/tpm.c               | 22 +++++++++++++--
> >  hmp.c                        |  7 ++---
> >  hw/tpm/tpm_passthrough.c     | 64 +++++++++++++++++++++++++++++++-
> > ------------
> >  include/sysemu/tpm_backend.h | 25 +++++++++++++++--
> >  tpm.c                        | 32 +---------------------
> >  5 files changed, 93 insertions(+), 57 deletions(-)
> > 
> > diff --git a/backends/tpm.c b/backends/tpm.c
> > index c409a46..6ade9e4 100644
> > --- a/backends/tpm.c
> > +++ b/backends/tpm.c
> > @@ -138,6 +138,26 @@ TPMVersion
> > tpm_backend_get_tpm_version(TPMBackend *s)
> >      return k->ops->get_tpm_version(s);
> >  }
> > 
> > +TpmTypeOptions *tpm_backend_get_tpm_options(TPMBackend *s)
> > +{
> > +    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
> > +
> > +    assert(k->ops->get_tpm_options);
> > +
> > +    return k->ops->get_tpm_options(s);
> > +}
> > +
> Why make this public API?
Yes, I agree no need of this API, i will remove this
> 
> > 
> > +TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
> > +{
> > +    TPMInfo *info = g_new0(TPMInfo, 1);
> > +
> > +    info->id = g_strdup(s->id);
> > +    info->model = s->fe_model;
> > +    info->options = tpm_backend_get_tpm_options(s);
> Callback could be called directly from here.
> 
> > 
> > +
> > +    return info;
> > +}
> > +
> >  static bool tpm_backend_prop_get_opened(Object *obj, Error **errp)
> >  {
> >      TPMBackend *s = TPM_BACKEND(obj);
> > @@ -192,8 +212,6 @@ static void
> > tpm_backend_instance_finalize(Object *obj)
> >      TPMBackend *s = TPM_BACKEND(obj);
> > 
> >      g_free(s->id);
> > -    g_free(s->path);
> > -    g_free(s->cancel_path);
> >      tpm_backend_thread_end(s);
> >  }
> > 
> > diff --git a/hmp.c b/hmp.c
> > index 0fb2bc7..cf62b2e 100644
> > --- a/hmp.c
> > +++ b/hmp.c
> > @@ -31,6 +31,7 @@
> >  #include "qapi/qmp/qerror.h"
> >  #include "qapi/string-input-visitor.h"
> >  #include "qapi/string-output-visitor.h"
> > +#include "qapi/util.h"
> >  #include "qapi-visit.h"
> >  #include "qom/object_interfaces.h"
> >  #include "ui/console.h"
> > @@ -1012,10 +1013,10 @@ void hmp_info_tpm(Monitor *mon, const QDict
> > *qdict)
> >                         c, TpmModel_str(ti->model));
> > 
> >          monitor_printf(mon, "  \\ %s: type=%s",
> > -                       ti->id, TpmTypeOptionsKind_str(ti->options-
> > >type));
> > +                       ti->id, TpmType_str(ti->options->type));
> > 
> Why this change? It is still a TpmTypeOptionsKind no?
This is was issue with my rebase, i will fix.
> 
> > 
> >          switch (ti->options->type) {
> > -        case TPM_TYPE_OPTIONS_KIND_PASSTHROUGH:
> > +        case TPM_TYPE_PASSTHROUGH:
> >              tpo = ti->options->u.passthrough.data;
> >              monitor_printf(mon, "%s%s%s%s",
> >                             tpo->has_path ? ",path=" : "",
> > @@ -1023,7 +1024,7 @@ 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__MAX:
> > +        case TPM_TYPE__MAX:
> >              break;
> >          }
> >          monitor_printf(mon, "\n");
> > diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
> > index a0459a6..fb7dad8 100644
> > --- a/hw/tpm/tpm_passthrough.c
> > +++ b/hw/tpm/tpm_passthrough.c
> > @@ -49,7 +49,8 @@
> >  struct TPMPassthruState {
> >      TPMBackend parent;
> > 
> > -    char *tpm_dev;
> > +    TPMPassthroughOptions *options;
> > +    const char *tpm_dev;
> >      int tpm_fd;
> >      bool tpm_executing;
> >      bool tpm_op_canceled;
> > @@ -308,15 +309,14 @@ static TPMVersion
> > tpm_passthrough_get_tpm_version(TPMBackend *tb)
> >   * in Documentation/ABI/stable/sysfs-class-tpm.
> >   * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
> >   */
> > -static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
> > +static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState
> > *tpm_pt)
> I suspect this change could be done in an earlier/separate patch
I feel this change is better part of this commit as this is side effect
of rearragend tpm options, now these are part of TPMPasstrhuState so
_open_sysfs_cancel() need not work on TPMBackend.
> 
> > 
> >  {
> > -    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
> >      int fd = -1;
> >      char *dev;
> >      char path[PATH_MAX];
> > 
> > -    if (tb->cancel_path) {
> > -        fd = qemu_open(tb->cancel_path, O_WRONLY);
> > +    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",
> >                           strerror(errno));
> > @@ -331,7 +331,7 @@ static int
> > tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
> >                       dev) < sizeof(path)) {
> >              fd = qemu_open(path, O_WRONLY);
> >              if (fd >= 0) {
> > -                tb->cancel_path = g_strdup(path);
> > +                tpm_pt->options->cancel_path = g_strdup(path);
> >              } else {
> >                  error_report("tpm_passthrough: Could not open TPM
> > cancel "
> >                               "path %s : %s", path,
> > strerror(errno));
> > @@ -351,17 +351,18 @@ static int
> > tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
> >      const char *value;
> > 
> >      value = qemu_opt_get(opts, "cancel-path");
> > -    tb->cancel_path = g_strdup(value);
> > +    if (value) {
> > +        tpm_pt->options->cancel_path = g_strdup(value);
> > +        tpm_pt->options->has_cancel_path = true;
> > +    }
> > 
> >      value = qemu_opt_get(opts, "path");
> > -    if (!value) {
> > -        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
> > +    if (value) {
> > +        tpm_pt->options->has_path = true;
> > +        tpm_pt->options->path = g_strdup(value);
> >      }
> > 
> > -    tpm_pt->tpm_dev = g_strdup(value);
> > -
> > -    tb->path = g_strdup(tpm_pt->tpm_dev);
> > -
> > +    tpm_pt->tpm_dev = value ? value :
> > TPM_PASSTHROUGH_DEFAULT_DEVICE;
> Isn't that duplicated with tpm_pt->options->path ? And different
> values...
This is intentional, options->path holds the configuration value, where
as tpm_dev points the real device path used.
> 
> > 
> >      tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
> >      if (tpm_pt->tpm_fd < 0) {
> >          error_report("Cannot access TPM device using '%s': %s",
> > @@ -382,10 +383,8 @@ static int
> > tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
> >      tpm_pt->tpm_fd = -1;
> > 
> >   err_free_parameters:
> > -    g_free(tb->path);
> > -    tb->path = NULL;
> > -
> > -    g_free(tpm_pt->tpm_dev);
> > +    qapi_free_TPMPassthroughOptions(tpm_pt->options);
> > +    tpm_pt->options = NULL;
> >      tpm_pt->tpm_dev = NULL;
> > 
> >      return 1;
> > @@ -403,7 +402,7 @@ static TPMBackend
> > *tpm_passthrough_create(QemuOpts *opts, const char *id)
> >          goto err_exit;
> >      }
> > 
> > -    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
> > +    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
> >      if (tpm_pt->cancel_fd < 0) {
> >          goto err_exit;
> >      }
> > @@ -416,6 +415,31 @@ err_exit:
> >      return NULL;
> >  }
> > 
> > +static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend
> > *tb)
> > +{
> > +    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
> > +    TpmTypeOptions *options = NULL;
> > +    TPMPassthroughOptions *poptions = NULL;
> > +
> > +    poptions = g_new0(TPMPassthroughOptions, 1);
> > +
> > +    if (tpm_pt->options->has_path) {
> > +        poptions->has_path = true;
> > +        poptions->path = g_strdup(tpm_pt->options->path);
> > +    }
> > +
> > +    if (tpm_pt->options->has_cancel_path) {
> > +        poptions->has_cancel_path = true;
> > +        poptions->cancel_path = g_strdup(tpm_pt->options-
> > >cancel_path);
> > +    }
> > +
> That looks like a job for QAPI_CLONE.
Ok, I was not knowing this, Thanks for pointing it. I will fix.


- Amarnath

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator
  2017-09-24 18:52   ` Marc-André Lureau
  2017-09-25  0:35     ` Stefan Berger
@ 2017-09-26 12:05     ` Valluri, Amarnath
  2017-09-26 12:24       ` Marc-André Lureau
  2017-09-26 13:59       ` Eric Blake
  2017-09-27 12:18     ` [Qemu-devel] [PATCH v8 " Amarnath Valluri
  2 siblings, 2 replies; 23+ messages in thread
From: Valluri, Amarnath @ 2017-09-26 12:05 UTC (permalink / raw)
  To: marcandre.lureau@gmail.com
  Cc: stefanb@linux.vnet.ibm.com, dgilbert@redhat.com,
	qemu-devel@nongnu.org, armbru@redhat.com, eblake@redhat.com

Hi Marc,

Thanks for your time reviewing this patchset. Please find my inline
comments.

On Sun, 2017-09-24 at 20:52 +0200, Marc-André Lureau wrote:
> Hi
> 
> Thanks for the nice update, removing the exec() code, using chardev
> and a private socketpair. Some comments below:
> 
> On Fri, Sep 22, 2017 at 2:33 PM, Amarnath Valluri
> <amarnath.valluri@intel.com> wrote:
> > 
> > This change introduces a new TPM backend driver that can
> > communicate with
> > swtpm(software TPM emulator) using unix domain socket interface.
> > QEMU talks to
> > TPM emulator using socket based chardev backend device.
> > 
> > Swtpm uses two Unix sockets for communications, one for plain TPM
> > commands and
> > responses, and one for out-of-band control messages. QEMU passes
> > data socket
> > been used over the control channel.
> > 
> > The swtpm and associated tools can be found here:
> >     https://github.com/stefanberger/swtpm
> > 
> > The swtpm's control channel protocol specification can be found
> > here:
> >     https://github.com/stefanberger/swtpm/wiki/Control-Channel-Spec
> > ification
> > 
> > Usage:
> >     # setup TPM state directory
> >     mkdir /tmp/mytpm
> >     chown -R tss:root /tmp/mytpm
> >     /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek
> > 
> >     # Ask qemu to use TPM emulator with given tpm state directory
> >     qemu-system-x86_64 \
> >         [...] \
> >         -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \
> >         -tpmdev emulator,id=tpm0,chardev=chrtpm \
> >         -device tpm-tis,tpmdev=tpm0 \
> >         [...]
> > 
> > Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
> > ---
> >  configure             |  15 +-
> >  hmp.c                 |   5 +
> >  hw/tpm/Makefile.objs  |   1 +
> >  hw/tpm/tpm_emulator.c | 649
> > ++++++++++++++++++++++++++++++++++++++++++++++++++
> >  hw/tpm/tpm_ioctl.h    | 246 +++++++++++++++++++
> >  qapi/tpm.json         |  21 +-
> >  qemu-options.hx       |  22 +-
> >  7 files changed, 950 insertions(+), 9 deletions(-)
> >  create mode 100644 hw/tpm/tpm_emulator.c
> >  create mode 100644 hw/tpm/tpm_ioctl.h
> > 
> > diff --git a/configure b/configure
> > index cb0f7ed..ce2df2d 100755
> > --- a/configure
> > +++ b/configure
> > @@ -3461,10 +3461,15 @@ fi
> >  ##########################################
> >  # TPM passthrough is only on x86 Linux
> > 
> > -if test "$targetos" = Linux && test "$cpu" = i386 -o "$cpu" =
> > x86_64; then
> > -  tpm_passthrough=$tpm
> > +if test "$targetos" = Linux; then
> > +  tpm_emulator=$tpm
> > +  if test "$cpu" = i386 -o "$cpu" = x86_64; then
> > +    tpm_passthrough=$tpm
> > +  else
> > +    tpm_passthrough=no
> > +  fi
> >  else
> > -  tpm_passthrough=no
> > +  tpm_emulator=no
> >  fi
> > 
> >  ##########################################
> > @@ -5359,6 +5364,7 @@ echo "gcov enabled      $gcov"
> >  echo "TPM support       $tpm"
> >  echo "libssh2 support   $libssh2"
> >  echo "TPM passthrough   $tpm_passthrough"
> > +echo "TPM emulator      $tpm_emulator"
> >  echo "QOM debugging     $qom_cast_debug"
> >  echo "Live block migration $live_block_migration"
> >  echo "lzo support       $lzo"
> > @@ -5943,6 +5949,9 @@ if test "$tpm" = "yes"; then
> >    if test "$tpm_passthrough" = "yes"; then
> >      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
> >    fi
> > +  if test "$tpm_emulator" = "yes"; then
> > +    echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak
> It shouldn't require Linux, but posix (and I assume a port to other
> systems isn't impossible). same for build-sys / help / comments.
I agree, Can you suggest, what is the Qemu way of limiting this to
'posix'.
> 
> > 
> > +  fi
> >  fi
> > 
> >  echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
> > diff --git a/hmp.c b/hmp.c
> > index cf62b2e..7e69eca 100644
> > --- a/hmp.c
> > +++ b/hmp.c
> > @@ -995,6 +995,7 @@ void hmp_info_tpm(Monitor *mon, const QDict
> > *qdict)
> >      Error *err = NULL;
> >      unsigned int c = 0;
> >      TPMPassthroughOptions *tpo;
> > +    TPMEmulatorOptions *teo;
> > 
> >      info_list = qmp_query_tpm(&err);
> >      if (err) {
> > @@ -1024,6 +1025,10 @@ 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_EMULATOR:
> > +            teo = ti->options->u.emulator.data;
> > +            monitor_printf(mon, ",chardev=%s", teo->chardev);
> > +            break;
> >          case TPM_TYPE__MAX:
> >              break;
> >          }
> > diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
> > index 64cecc3..41f0b7a 100644
> > --- a/hw/tpm/Makefile.objs
> > +++ b/hw/tpm/Makefile.objs
> > @@ -1,2 +1,3 @@
> >  common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
> >  common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
> > tpm_util.o
> > +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
> > diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
> > new file mode 100644
> > index 0000000..c02bbe2
> > --- /dev/null
> > +++ b/hw/tpm/tpm_emulator.c
> > @@ -0,0 +1,649 @@
> > +/*
> > + *  emulator TPM driver
> > + *
> > + *  Copyright (c) 2017 Intel Corporation
> > + *  Author: Amarnath Valluri <amarnath.valluri@intel.com>
> > + *
> > + *  Copyright (c) 2010 - 2013 IBM Corporation
> > + *  Authors:
> > + *    Stefan Berger <stefanb@us.ibm.com>
> > + *
> > + *  Copyright (C) 2011 IAIK, Graz University of Technology
> > + *    Author: Andreas Niederl
> > + *
> > + * This library is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2 of the License, or (at your option) any later
> > version.
> > + *
> > + * This library is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General
> > Public
> > + * License along with this library; if not, see <http://www.gnu.or
> > g/licenses/>
> > + *
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qemu/error-report.h"
> > +#include "qemu/sockets.h"
> > +#include "io/channel-socket.h"
> > +#include "sysemu/tpm_backend.h"
> > +#include "tpm_int.h"
> > +#include "hw/hw.h"
> > +#include "hw/i386/pc.h"
> > +#include "tpm_util.h"
> > +#include "tpm_ioctl.h"
> > +#include "migration/blocker.h"
> > +#include "qapi/error.h"
> > +#include "chardev/char-fe.h"
> > +
> > +#include <fcntl.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <stdio.h>
> > +
> > +#define DEBUG_TPM 0
> > +
> > +#define DPRINT(fmt, ...) do { \
> > +    if (DEBUG_TPM) { \
> > +        fprintf(stderr, fmt, ## __VA_ARGS__); \
> > +    } \
> > +} while (0);
> > +
> > +#define DPRINTF(fmt, ...) DPRINT("tpm-emulator: "fmt"\n",
> > __VA_ARGS__)
> I would define a single DPRINTF() (& drop DPRINT usage below)
Ok, will do
> 
> 
> > 
> > +
> > +#define TYPE_TPM_EMULATOR "tpm-emulator"
> > +#define TPM_EMULATOR(obj) \
> > +    OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
> > +
> > +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps &
> > (cap)) == (cap))
> > +
> > +static const TPMDriverOps tpm_emulator_driver;
> > +
> > +/* data structures */
> > +typedef struct TPMEmulator {
> > +    TPMBackend parent;
> > +
> > +    TPMEmulatorOptions *options;
> Contrary to my comment on previous patch, I realize it is convenient
> to have a qapi pointer here, so you can use the free visitor later.
> 
> > 
> > +    CharBackend ctrl_dev;
> ctrl_chr perhaps? or just chr or be (the most common name in other
> devices).
> 
> > 
> > +    QIOChannel *data_ioc;
> > +    bool op_executing;
> > +    bool op_canceled;
> I think those 2 fields can be dropped, see below.
> 
> > 
> > +    bool had_startup_error;
> I think some little refactoring could remove the whole
> had_startup_error() backend & callback before or after the series.
> tpm_backend_startup_tpm() already returns an error code.  device or
> common backend code could handle & remember that.
Yes, i agree, we can avoid had_startup_error() from DriverOps, i will
do this.
> 
> > 
> > +    TPMVersion tpm_version;
> This is probably worth to consider in common code instead, but let's
> not worry about it.
> 
> > 
> > +    ptm_cap caps; /* capabilities of the TPM */
> > +    uint8_t cur_locty_number; /* last set locality */
> > +    QemuMutex state_lock;
> > +    Error *migration_blocker;
> > +} TPMEmulator;
> > +
> > +
> > +static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long
> > cmd, void *msg,
> > +                                size_t msg_len_in, size_t
> > msg_len_out)
> > +{
> > +    uint32_t cmd_no = cpu_to_be32(cmd);
> > +    ssize_t n = sizeof(uint32_t) + msg_len_in;
> > +    uint8_t *buf = NULL;
> > +
> > +    buf = (uint8_t *)malloc(n);
> could be g_alloca() instead. Alternatively, why not call 2 write()
> instead?
> 
> none of the casts in this function are necessary, please remove them.
Ok, will change to aclloca()
> 
> > 
> > +    memcpy(buf, &cmd_no, sizeof(cmd_no));
> > +    memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
> > +
> > +    n += qemu_chr_fe_write_all(dev, (const uint8_t *)buf, n);
> weird
> 
> > 
> > +    free(buf);
> > +
> > +    if (n > 0) {
> with the n += above, you'll get interesting behaviour :)
Ya, it was typo :)
> 
> You probably want to check if any write above failed, and return
> early
> for the error case.
> 
> Please improve the error handling in this function
> 
> > 
> > +        if (msg_len_out > 0) {
> > +            n = qemu_chr_fe_read_all(dev, (uint8_t *)msg,
> > msg_len_out);
> > +            /* simulate ioctl return value */
> > +            if (n > 0) {
> > +                n = 0;
> > +            }
> > +        } else {
> > +            n = 0;
> > +        }
> > +    }
> > +    return n;
> > +}
> > +
> > +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_pt,
> tpm_pt was tpm_passthrough I suppose.
> 
> Please rename tpm_pt tpm_emu (or I would suggest "self", but this
> isn't common name in qemu code - it's actually common in a close
> c-object world., and it is quite convenient...)
My interpretation about _pt was 'pointer' ;-) i will consider your
suggetion.
> 
> > 
> > +                                     const uint8_t *in, uint32_t
> > in_len,
> > +                                     uint8_t *out, uint32_t
> > out_len,
> > +                                     bool *selftest_done)
> > +{
> > +    ssize_t ret;
> > +    bool is_selftest = false;
> > +    const struct tpm_resp_hdr *hdr = NULL;
> > +    Error *err = NULL;
> > +
> > +    tpm_pt->op_canceled = false;
> > +    tpm_pt->op_executing = true;
> > +    if (selftest_done) {
> > +        *selftest_done = false;
> > +        is_selftest = tpm_util_is_selftest(in, in_len);
> > +    }
> > +
> > +    ret = qio_channel_write(tpm_pt->data_ioc, (char *)in, in_len,
> > &err);
> hmm, too bad qio_channel_write() doesn't take a void *
> 
> why not write_all()?
Agreed
> 
> > 
> > +    if (ret != in_len || err) {
> > +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
> I don't think ECANCELED make sense for emulator code.
> 
> > 
> > +            error_report("tpm-emulator: error while transmitting
> > data "
> > +                         "to TPM: %s", err ? error_get_pretty(err)
> > : "");
> > +            error_free(err);
> > +        }
> > +        goto err_exit;
> > +    }
> > +
> > +    tpm_pt->op_executing = false;
> > +
> > +    ret = qio_channel_read(tpm_pt->data_ioc, (char *)out, out_len,
> > &err);
> > +    if (ret < 0 || err) {
> read_all() ?
The issue with read_all() is it does not return the no of bytes it
read, so i would like to stict to _read()
> 
> > 
> > +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
> > +            error_report("tpm-emulator: error while reading data
> > from "
> > +                         "TPM: %s", err ? error_get_pretty(err) :
> > "");
> > +            error_free(err);
> > +        }
> > +    } else if (ret >= sizeof(*hdr)) {
> > +        hdr = (struct tpm_resp_hdr *)out;
> > +    }
> > +
> > +    if (!hdr || be32_to_cpu(hdr->len) != ret) {
> > +        error_report("tpm-emulator: received invalid response "
> > +                     "packet from TPM with length :%ld", ret);
> > +        ret = -1;
> > +        goto err_exit;
> > +    }
> > +
> > +    if (is_selftest) {
> > +        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
> > +    }
> > +
> > +    return 0;
> > +
> > +err_exit:
> > +    if (ret < 0) {
> > +        tpm_util_write_fatal_error_response(out, out_len);
> > +    }
> > +
> > +    tpm_pt->op_executing = false;
> > +
> > +    return ret;
> > +}
> > +
> > +static int tpm_emulator_set_locality(TPMEmulator *tpm_pt, uint8_t
> > locty_number)
> > +{
> > +    ptm_loc loc;
> > +
> > +    DPRINTF("%s : locality: 0x%x", __func__, locty_number);
> > +
> > +    if (tpm_pt->cur_locty_number != locty_number) {
> return early instead if ==, to avoid code indent etc
Ok
> 
> > 
> > +        DPRINTF("setting locality : 0x%x", locty_number);
> > +        loc.u.req.loc = locty_number;
> This number isn't set in be like the rest of the protocol?
I doubt if i get ur point :(, can you please elaborate.
> 
> > 
> > +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > CMD_SET_LOCALITY, &loc,
> > +                             sizeof(loc), sizeof(loc)) < 0) {
> > +            error_report("tpm-emulator: could not set locality :
> > %s",
> > +                         strerror(errno));
> > +            return -1;
> > +        }
> > +        loc.u.resp.tpm_result =
> > be32_to_cpu(loc.u.resp.tpm_result);
> > +        if (loc.u.resp.tpm_result != 0) {
> > +            error_report("tpm-emulator: TPM result for set
> > locality : 0x%x",
> > +                         loc.u.resp.tpm_result);
> > +            return -1;
> > +        }
> > +        tpm_pt->cur_locty_number = locty_number;
> > +    }
> > +    return 0;
> > +}
> > +
> > +static void tpm_emulator_handle_request(TPMBackend *tb,
> > TPMBackendCmd cmd)
> > +{
> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > +    TPMLocality *locty = NULL;
> > +    bool selftest_done = false;
> > +
> > +    DPRINTF("processing command type %d", cmd);
> > +
> > +    switch (cmd) {
> > +    case TPM_BACKEND_CMD_PROCESS_CMD:
> > +        qemu_mutex_lock(&tpm_pt->state_lock);
> > +        locty = tb->tpm_state->locty_data;
> > +        if (tpm_emulator_set_locality(tpm_pt,
> > +                                      tb->tpm_state->locty_number) 
> > < 0) {
> > +            tpm_util_write_fatal_error_response(locty-
> > >r_buffer.buffer,
> > +                                           locty->r_buffer.size);
> return / goto here instead of else.
We should not retrun from here, we have to propagte the error response
to device, and unlock the state.
> 
> > 
> > +        } else {
> > +            tpm_emulator_unix_tx_bufs(tpm_pt, locty-
> > >w_buffer.buffer,
> > +                                              locty->w_offset,
> > +                                              locty-
> > >r_buffer.buffer,
> > +                                              locty-
> > >r_buffer.size,
> > +                                              &selftest_done);
> no error handling?
The error case is supposed to handle by device, where we are filling
into out buffer, using tpm_util_write_fatal_error_response().
> 
> > 
> > +        }
> > +
> > +        tb->recv_data_callback(tb->tpm_state, tb->tpm_state-
> > >locty_number,
> > +                               selftest_done);
> > +        qemu_mutex_unlock(&tpm_pt->state_lock);
> > +
> > +        break;
> > +    case TPM_BACKEND_CMD_INIT:
> > +    case TPM_BACKEND_CMD_END:
> > +    case TPM_BACKEND_CMD_TPM_RESET:
> > +        /* nothing to do */
> > +        break;
> > +    }
> > +}
> > +
> > +/*
> > + * Gracefully shut down the external unixio TPM
> > + */
> > +static void tpm_emulator_shutdown(TPMEmulator *tpm_pt)
> > +{
> > +    ptm_res res;
> > +
> > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SHUTDOWN,
> > &res, 0,
> > +                         sizeof(res)) < 0) {
> > +        error_report("tpm-emulator: Could not cleanly shutdown the
> > TPM: %s",
> > +                     strerror(errno));
> > +    } else if (res != 0) {
> > +        error_report("tpm-emulator: TPM result for sutdown: 0x%x",
> > +                     be32_to_cpu(res));
> > +    }
> > +}
> > +
> > +static int tpm_emulator_probe_caps(TPMEmulator *tpm_pt)
> > +{
> > +    DPRINTF("%s", __func__);
> > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > CMD_GET_CAPABILITY,
> > +                         &tpm_pt->caps, 0, sizeof(tpm_pt->caps)) <
> > 0) {
> > +        error_report("tpm-emulator: probing failed : %s",
> > strerror(errno));
> > +        return -1;
> > +    }
> > +
> > +    tpm_pt->caps = be64_to_cpu(tpm_pt->caps);
> > +
> > +    DPRINTF("capbilities : 0x%lx", tpm_pt->caps);
> > +
> > +    return 0;
> > +}
> > +
> > +static int tpm_emulator_check_caps(TPMEmulator *tpm_pt)
> > +{
> > +    ptm_cap caps = 0;
> > +    const char *tpm = NULL;
> > +
> > +    /* 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-emulator: TPM version has not been
> > set");
> > +        return -1;
> > +    }
> > +
> > +    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, caps)) {
> > +        error_report("tpm-emulator: TPM does not implement minimum
> > set of "
> > +                     "required capabilities for TPM %s (0x%x)",
> > tpm, (int)caps);
> > +        return -1;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int tpm_emulator_startup_tpm(TPMBackend *tb)
> > +{
> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > +    ptm_init init;
> > +    ptm_res res;
> > +
> > +    DPRINTF("%s", __func__);
> > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_INIT, &init,
> > sizeof(init),
> > +                         sizeof(init)) < 0) {
> > +        error_report("tpm-emulator: could not send INIT: %s",
> > +                     strerror(errno));
> > +        goto err_exit;
> > +    }
> > +
> > +    res = be32_to_cpu(init.u.resp.tpm_result);
> > +    if (res) {
> > +        error_report("tpm-emulator: TPM result for CMD_INIT:
> > 0x%x", res);
> > +        goto err_exit;
> > +    }
> > +    return 0;
> > +
> > +err_exit:
> > +    tpm_pt->had_startup_error = true;
> > +    return -1;
> > +}
> > +
> > +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
> > +{
> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > +    ptm_est est;
> > +
> > +    DPRINTF("%s", __func__);
> > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > CMD_GET_TPMESTABLISHED, &est, 0,
> > +                         sizeof(est)) < 0) {
> > +        error_report("tpm-emulator: Could not get the TPM
> > established flag: %s",
> > +                     strerror(errno));
> > +        return false;
> > +    }
> > +    DPRINTF("established flag: %0x", est.u.resp.bit);
> > +
> > +    return (est.u.resp.bit != 0);
> > +}
> > +
> > +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
> > +                                                   uint8_t locty)
> > +{
> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > +    ptm_reset_est reset_est;
> > +    ptm_res res;
> > +
> > +    /* only a TPM 2.0 will support this */
> > +    if (tpm_pt->tpm_version == TPM_VERSION_2_0) {
> > +        reset_est.u.req.loc = tpm_pt->cur_locty_number;
> > +
> > +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > CMD_RESET_TPMESTABLISHED,
> > +                                 &reset_est, sizeof(reset_est),
> > +                                 sizeof(reset_est)) < 0) {
> > +            error_report("tpm-emulator: Could not reset the
> > establishment bit: "
> > +                          "%s", strerror(errno));
> > +            return -1;
> > +        }
> > +
> > +        res = be32_to_cpu(reset_est.u.resp.tpm_result);
> > +        if (res) {
> > +            error_report("tpm-emulator: TPM result for rest
> > establixhed flag: "
> > +                         "0x%x", res);
> > +            return -1;
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static bool tpm_emulator_had_startup_error(TPMBackend *tb)
> > +{
> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > +
> > +    return tpm_pt->had_startup_error;
> > +}
> > +
> > +static void tpm_emulator_cancel_cmd(TPMBackend *tb)
> > +{
> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > +    ptm_res res;
> > +
> > +    /*
> > +     * As of Linux 3.7 the tpm_tis driver does not properly cancel
> > +     * commands on all TPM manufacturers' TPMs.
> > +     * Only cancel if we're busy so we don't cancel someone else's
> > +     * command, e.g., a command executed on the host.
> > +     */
> > +    if (tpm_pt->op_executing) {
> The field is set in the worker thread. This is racy. Fortunately this
> is not relevant for emulator, I think you can simply drop that check
> and the variable. Stefan should confirm though.
> 
> > 
> > +        if (TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt,
> > PTM_CAP_CANCEL_TPM_CMD)) {
> > +            if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > CMD_CANCEL_TPM_CMD,
> > +                                     &res, 0, sizeof(res)) < 0) {
> > +                error_report("tpm-emulator: Could not cancel
> > command: %s",
> > +                             strerror(errno));
> > +            } else if (res != 0) {
> > +                error_report("tpm-emulator: Failed to cancel TPM:
> > 0x%x",
> > +                             be32_to_cpu(res));
> > +            } else {
> > +                tpm_pt->op_canceled = true;
> > +            }
> > +        }
> > +    }
> > +}
> > +
> > +static void tpm_emulator_reset(TPMBackend *tb)
> > +{
> > +    DPRINTF("%s", __func__);
> > +
> > +    tpm_emulator_cancel_cmd(tb);
> > +}
> > +
> > +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
> > +{
> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > +
> > +    return tpm_pt->tpm_version;
> > +}
> > +
> > +static void tpm_emulator_block_migration(TPMEmulator *tpm_pt)
> > +{
> > +    Error *err = NULL;
> > +
> > +    error_setg(&tpm_pt->migration_blocker,
> > +               "Migration disabled: TPM emulator not yet
> > migratable");
> > +    migrate_add_blocker(tpm_pt->migration_blocker, &err);
> > +    if (err) {
> > +        error_free(err);
> probably better to report_err it instead
Ok
> 
> > 
> > +        error_free(tpm_pt->migration_blocker);
> > +        tpm_pt->migration_blocker = NULL;
> and return an error code.
Will do
> 
> > 
> > +    }
> > +}
> > +
> > +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_pt)
> > +{
> > +    ptm_res res;
> > +    Error *err = NULL;
> > +    int fds[2] = { -1, -1 };
> > +
> > +    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) {
> > +        error_report("tpm-emulator: Failed to create socketpair");
> > +        return -1;
> > +    }
> > +
> > +    qemu_chr_fe_set_msgfds(&tpm_pt->ctrl_dev, fds + 1, 1);
> > +
> > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SET_DATAFD,
> > &res, 0,
> > +                    sizeof(res)) || res != 0) {
> Yay! for making life easier and hiding a protocol detail.
> 
> > 
> > +        error_report("tpm-emulator: Failed to send CMD_SET_DATAFD:
> > %s",
> > +                     strerror(errno));
> > +        goto err_exit;
> > +    }
> > +
> > +    tpm_pt->data_ioc =
> > QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
> > +    if (err) {
> > +        error_report("tpm-emulator: Failed to create io channel :
> > %s",
> > +                       error_get_pretty(err));
> error_prepend()?
Ok
> 
> > 
> > +        error_free(err);
> > +        goto err_exit;
> > +    }
> close fds[1] ?
I guess we are not supposed to close the other end of the
socketpair/pipe, as it is not forked process.
> 
> > 
> > +
> > +    return 0;
> > +
> > +err_exit:
> > +    closesocket(fds[0]);
> > +    closesocket(fds[1]);
> > +    return -1;
> > +}
> > +
> > +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_pt,
> > QemuOpts *opts)
> > +{
> > +    const char *value;
> > +
> > +    value = qemu_opt_get(opts, "chardev");
> > +    if (value) {
> > +        Error *err = NULL;
> > +        Chardev *dev = qemu_chr_find(value);
> > +
> > +        tpm_pt->options->chardev = g_strdup(value);
> > +
> > +        if (!dev || !qemu_chr_fe_init(&tpm_pt->ctrl_dev, dev,
> > &err)) {
> > +            error_report("tpm-emulator: No valid chardev found at
> > '%s': %s",
> > +                         value, err ? error_get_pretty(err) : "");
> > +            error_free(err);
> error_prepend
Ok
> 
> > 
> > +            goto err;
> > +        }
> > +    }
> > +
> > +    if (tpm_emulator_prepare_data_fd(tpm_pt) < 0) {
> > +        goto err;
> > +    }
> > +
> > +    /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as
> > it also used
> > +     * by passthrough driver, which not yet using GIOChannel.
> > +     */
> > +    if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_pt->data_ioc)-
> > >fd,
> > +                             &tpm_pt->tpm_version)) {
> > +        error_report("'%s' is not emulating TPM device. Error:
> > %s",
> > +                      tpm_pt->options->chardev, strerror(errno));
> > +        goto err;
> > +    }
> > +
> > +    DPRINTF("TPM Version %s", tpm_pt->tpm_version ==
> > TPM_VERSION_1_2 ? "1.2" :
> > +             (tpm_pt->tpm_version == TPM_VERSION_2_0 ?  "2.0" :
> > "Unspecified"));
> > +
> > +    if (tpm_emulator_probe_caps(tpm_pt) ||
> > +        tpm_emulator_check_caps(tpm_pt)) {
> > +        goto err;
> > +    }
> > +
> > +    tpm_emulator_block_migration(tpm_pt);
> > +
> > +    return 0;
> > +
> > +err:
> > +    DPRINT("Startup error\n");
> > +    return -1;
> > +}
> > +
> > +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char
> > *id)
> > +{
> > +    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;
> > +    }
> > +
> > +    return tb;
> > +
> > +err_exit:
> > +    object_unref(OBJECT(tb));
> > +
> > +    return NULL;
> > +}
> > +
> > +static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend
> > *tb)
> > +{
> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > +    TpmTypeOptions *options = NULL;
> > +    TPMEmulatorOptions *eoptions = NULL;
> > +
> > +    eoptions = g_new0(TPMEmulatorOptions, 1);
> > +    if (!eoptions) {
> > +        return NULL;
> > +    }
> > +    DPRINTF("%s", __func__);
> > +
> > +    eoptions->chardev = g_strdup(tpm_pt->options->chardev);
> > +    options = g_new0(TpmTypeOptions, 1);
> > +    if (!options) {
> > +        qapi_free_TPMEmulatorOptions(eoptions);
> > +        return NULL;
> > +    }
> > +
> > +    options->type = TPM_TYPE_EMULATOR;
> > +    options->u.emulator.data = eoptions;
> I think this is a job for QAPI_CLONE.
> 
> > 
> > +
> > +    return options;
> > +}
> > +
> > +static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
> > +    TPM_STANDARD_CMDLINE_OPTS,
> > +    {
> > +        .name = "chardev",
> > +        .type = QEMU_OPT_STRING,
> > +        .help = "Character device to use for out-of-band control
> > messages",
> > +    },
> > +    { /* end of list */ },
> > +};
> > +
> > +static const TPMDriverOps tpm_emulator_driver = {
> > +    .type                     = TPM_TYPE_EMULATOR,
> > +    .opts                     = tpm_emulator_cmdline_opts,
> > +    .desc                     = "TPM emulator backend driver",
> > +
> > +    .create                   = tpm_emulator_create,
> > +    .startup_tpm              = tpm_emulator_startup_tpm,
> > +    .reset                    = tpm_emulator_reset,
> > +    .had_startup_error        = tpm_emulator_had_startup_error,
> > +    .cancel_cmd               = tpm_emulator_cancel_cmd,
> > +    .get_tpm_established_flag =
> > tpm_emulator_get_tpm_established_flag,
> > +    .reset_tpm_established_flag =
> > tpm_emulator_reset_tpm_established_flag,
> > +    .get_tpm_version          = tpm_emulator_get_tpm_version,
> > +    .get_tpm_options          = tpm_emulator_get_tpm_options,
> > +};
> > +
> > +static void tpm_emulator_inst_init(Object *obj)
> > +{
> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
> > +
> > +    DPRINTF("%s", __func__);
> > +    tpm_pt->options = g_new0(TPMEmulatorOptions, 1);
> > +    tpm_pt->op_executing = tpm_pt->op_canceled = false;
> > +    tpm_pt->had_startup_error = false;
> > +    tpm_pt->cur_locty_number = ~0;
> > +    qemu_mutex_init(&tpm_pt->state_lock);
> > +}
> > +
> > +static void tpm_emulator_inst_finalize(Object *obj)
> > +{
> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
> > +
> > +    tpm_emulator_cancel_cmd(TPM_BACKEND(obj));
> > +    tpm_emulator_shutdown(tpm_pt);
> > +
> > +    if (tpm_pt->data_ioc) {
> > +        qio_channel_close(tpm_pt->data_ioc, NULL);
> > +    }
> > +
> > +    qemu_chr_fe_deinit(&tpm_pt->ctrl_dev, false);
> > +
> > +    if (tpm_pt->options) {
> > +        qapi_free_TPMEmulatorOptions(tpm_pt->options);
> > +    }
> > +
> > +    if (tpm_pt->migration_blocker) {
> > +        migrate_del_blocker(tpm_pt->migration_blocker);
> > +        error_free(tpm_pt->migration_blocker);
> > +    }
> > +}
> > +
> > +static void tpm_emulator_class_init(ObjectClass *klass, void
> > *data)
> > +{
> > +    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
> > +    tbc->ops = &tpm_emulator_driver;
> > +    tbc->handle_request = tpm_emulator_handle_request;
> > +}
> > +
> > +static const TypeInfo tpm_emulator_info = {
> > +    .name = TYPE_TPM_EMULATOR,
> > +    .parent = TYPE_TPM_BACKEND,
> > +    .instance_size = sizeof(TPMEmulator),
> > +    .class_init = tpm_emulator_class_init,
> > +    .instance_init = tpm_emulator_inst_init,
> > +    .instance_finalize = tpm_emulator_inst_finalize,
> > +};
> > +
> > +static void tpm_emulator_register(void)
> > +{
> > +    type_register_static(&tpm_emulator_info);
> > +    tpm_register_driver(&tpm_emulator_driver);
> > +}
> > +
> > +type_init(tpm_emulator_register)
> > diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
> > new file mode 100644
> > index 0000000..33564b1
> > --- /dev/null
> > +++ b/hw/tpm/tpm_ioctl.h
> This file is copied from swtpm project. Could swtpm have it installed
> on system instead?
> 
> > 
> > @@ -0,0 +1,246 @@
> > +/*
> > + * tpm_ioctl.h
> > + *
> > + * (c) Copyright IBM Corporation 2014, 2015.
> > + *
> > + * This file is licensed under the terms of the 3-clause BSD
> > license
> > + */
> > +#ifndef _TPM_IOCTL_H_
> > +#define _TPM_IOCTL_H_
> > +
> > +#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 ptm_res as the first element.
> > + * ptm_res corresponds to the error code of a command executed by
> > the TPM.
> > + */
> > +
> > +typedef uint32_t ptm_res;
> > +
> > +/* PTM_GET_TPMESTABLISHED: get the establishment bit */
> > +struct ptm_est {
> > +    union {
> > +        struct {
> > +            ptm_res tpm_result;
> > +            unsigned char bit; /* TPM established bit */
> > +        } resp; /* response */
> > +    } u;
> > +};
> > +
> > +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */
> > +struct ptm_reset_est {
> > +    union {
> > +        struct {
> > +            uint8_t loc; /* locality to use */
> > +        } req; /* request */
> > +        struct {
> > +            ptm_res tpm_result;
> > +        } resp; /* response */
> > +    } u;
> > +};
> > +
> > +/* PTM_INIT */
> > +struct ptm_init {
> > +    union {
> > +        struct {
> > +            uint32_t init_flags; /* see definitions below */
> > +        } req; /* request */
> > +        struct {
> > +            ptm_res tpm_result;
> > +        } resp; /* response */
> > +    } u;
> > +};
> > +
> > +/* above init_flags */
> > +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0)
> > +    /* delete volatile state file after reading it */
> > +
> > +/* PTM_SET_LOCALITY */
> > +struct ptm_loc {
> > +    union {
> > +        struct {
> > +            uint8_t loc; /* locality to set */
> > +        } req; /* request */
> > +        struct {
> > +            ptm_res tpm_result;
> > +        } resp; /* response */
> > +    } u;
> > +};
> > +
> > +/* PTM_HASH_DATA: hash given data */
> > +struct ptm_hdata {
> > +    union {
> > +        struct {
> > +            uint32_t length;
> > +            uint8_t data[4096];
> > +        } req; /* request */
> > +        struct {
> > +            ptm_res tpm_result;
> > +        } resp; /* response */
> > +    } 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 PTM_STATE_BLOB_SIZE (3 * 1024)
> > +
> > +/*
> > + * The following is the data structure to get state blobs from the
> > TPM.
> > + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE,
> > multiple reads
> > + * with this ioctl and with adjusted offset are necessary. All
> > bytes
> > + * must be transferred and the transfer is done once the last byte
> > has been
> > + * returned.
> > + * It is possible to use the read() interface for reading the
> > data; however, the
> > + * first bytes of the state blob will be part of the response to
> > the ioctl(); a
> > + * subsequent read() is only necessary if the total length
> > (totlength) exceeds
> > + * the number of received bytes. seek() is not supported.
> > + */
> > +struct ptm_getstate {
> > +    union {
> > +        struct {
> > +            uint32_t state_flags; /* may be:
> > PTM_STATE_FLAG_DECRYPTED */
> > +            uint32_t type;        /* which blob to pull */
> > +            uint32_t offset;      /* offset from where to read */
> > +        } req; /* request */
> > +        struct {
> > +            ptm_res tpm_result;
> > +            uint32_t state_flags; /* may be:
> > PTM_STATE_FLAG_ENCRYPTED */
> > +            uint32_t totlength;   /* total length that will be
> > transferred */
> > +            uint32_t length;      /* number of bytes in following
> > buffer */
> > +            uint8_t  data[PTM_STATE_BLOB_SIZE];
> > +        } resp; /* response */
> > +    } 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 PTM_STATE_FLAG_DECRYPTED     1 /* on input:  get decrypted
> > state */
> > +#define PTM_STATE_FLAG_ENCRYPTED     2 /* on output: state is
> > encrypted */
> > +
> > +/*
> > + * The following is the data structure to set state blobs in the
> > TPM.
> > + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE,
> > multiple
> > + * 'writes' using this ioctl are necessary. The last packet is
> > indicated
> > + * by the length being smaller than the PTM_STATE_BLOB_SIZE.
> > + * The very first packet may have a length indicator of '0'
> > enabling
> > + * a write() with all the bytes from a buffer. If the write()
> > interface
> > + * is used, a final ioctl with a non-full buffer must be made to
> > indicate
> > + * that all data were transferred (a write with 0 bytes would not
> > work).
> > + */
> > +struct ptm_setstate {
> > +    union {
> > +        struct {
> > +            uint32_t state_flags; /* may be
> > PTM_STATE_FLAG_ENCRYPTED */
> > +            uint32_t type;        /* which blob to set */
> > +            uint32_t length;      /* length of the data;
> > +                                     use 0 on the first packet to
> > +                                     transfer using write() */
> > +            uint8_t data[PTM_STATE_BLOB_SIZE];
> > +        } req; /* request */
> > +        struct {
> > +            ptm_res tpm_result;
> > +        } resp; /* response */
> > +    } u;
> > +};
> > +
> > +/*
> > + * PTM_GET_CONFIG: Data structure to get runtime configuration
> > information
> > + * such as which keys are applied.
> > + */
> > +struct ptm_getconfig {
> > +    union {
> > +        struct {
> > +            ptm_res tpm_result;
> > +            uint32_t flags;
> > +        } resp; /* response */
> > +    } u;
> > +};
> > +
> > +#define PTM_CONFIG_FLAG_FILE_KEY        0x1
> > +#define PTM_CONFIG_FLAG_MIGRATION_KEY   0x2
> > +
> > +
> > +typedef uint64_t ptm_cap;
> > +typedef struct ptm_est ptm_est;
> > +typedef struct ptm_reset_est ptm_reset_est;
> > +typedef struct ptm_loc ptm_loc;
> > +typedef struct ptm_hdata ptm_hdata;
> > +typedef struct ptm_init ptm_init;
> > +typedef struct ptm_getstate ptm_getstate;
> > +typedef struct ptm_setstate ptm_setstate;
> > +typedef struct ptm_getconfig ptm_getconfig;
> > +
> > +/* 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)
> > +#define PTM_CAP_GET_CONFIG         (1 << 11)
> > +#define PTM_CAP_SET_DATAFD         (1 << 12)
> > +
> > +enum {
> > +    PTM_GET_CAPABILITY     = _IOR('P', 0, ptm_cap),
> > +    PTM_INIT               = _IOWR('P', 1, ptm_init),
> > +    PTM_SHUTDOWN           = _IOR('P', 2, ptm_res),
> > +    PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est),
> > +    PTM_SET_LOCALITY       = _IOWR('P', 4, ptm_loc),
> > +    PTM_HASH_START         = _IOR('P', 5, ptm_res),
> > +    PTM_HASH_DATA          = _IOWR('P', 6, ptm_hdata),
> > +    PTM_HASH_END           = _IOR('P', 7, ptm_res),
> > +    PTM_CANCEL_TPM_CMD     = _IOR('P', 8, ptm_res),
> > +    PTM_STORE_VOLATILE     = _IOR('P', 9, ptm_res),
> > +    PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est),
> > +    PTM_GET_STATEBLOB      = _IOWR('P', 11, ptm_getstate),
> > +    PTM_SET_STATEBLOB      = _IOWR('P', 12, ptm_setstate),
> > +    PTM_STOP               = _IOR('P', 13, ptm_res),
> > +    PTM_GET_CONFIG         = _IOR('P', 14, ptm_getconfig),
> > +    PTM_SET_DATAFD         = _IOR('P', 15, ptm_res),
> > +};
> > +
> > +/*
> > + * Commands used by the non-CUSE TPMs
> > + *
> > + * All messages container big-endian data.
> > + *
> > + * The return messages only contain the 'resp' part of the unions
> > + * in the data structures above. Besides that the limits in the
> > + * buffers above (ptm_hdata:u.req.data and
> > ptm_get_state:u.resp.data
> > + * and ptm_set_state:u.req.data) are 0xffffffff.
> > + */
> > +enum {
> > +    CMD_GET_CAPABILITY = 1,
> > +    CMD_INIT,
> > +    CMD_SHUTDOWN,
> > +    CMD_GET_TPMESTABLISHED,
> > +    CMD_SET_LOCALITY,
> > +    CMD_HASH_START,
> > +    CMD_HASH_DATA,
> > +    CMD_HASH_END,
> > +    CMD_CANCEL_TPM_CMD,
> > +    CMD_STORE_VOLATILE,
> > +    CMD_RESET_TPMESTABLISHED,
> > +    CMD_GET_STATEBLOB,
> > +    CMD_SET_STATEBLOB,
> > +    CMD_STOP,
> > +    CMD_GET_CONFIG,
> > +    CMD_SET_DATAFD
> > +};
> > +
> > +#endif /* _TPM_IOCTL_H */
> > diff --git a/qapi/tpm.json b/qapi/tpm.json
> > index e8b2d8d..7093f26 100644
> > --- a/qapi/tpm.json
> > +++ b/qapi/tpm.json
> > @@ -39,10 +39,12 @@
> >  # An enumeration of TPM types
> >  #
> >  # @passthrough: TPM passthrough type
> > +# @emulator: Software Emulator TPM type
> > +#            Since: 2.11
> >  #
> >  # Since: 1.5
> >  ##
> > -{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }
> > +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] }
> > 
> >  ##
> >  # @query-tpm-types:
> > @@ -56,7 +58,7 @@
> >  # Example:
> >  #
> >  # -> { "execute": "query-tpm-types" }
> > -# <- { "return": [ "passthrough" ] }
> > +# <- { "return": [ "passthrough", "emulator" ] }
> >  #
> >  ##
> >  { 'command': 'query-tpm-types', 'returns': ['TpmType'] }
> > @@ -77,16 +79,29 @@
> >                                               '*cancel-path' :
> > 'str'} }
> > 
> >  ##
> > +# @TPMEmulatorOptions:
> > +#
> > +# Information about the TPM emulator type
> > +#
> > +# @chardev: Name of a unix socket chardev
> > +#
> > +# Since: 2.11
> > +##
> > +{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } }
> > +
> > +##
> >  # @TpmTypeOptions:
> >  #
> >  # A union referencing different TPM backend types' configuration
> > options
> >  #
> >  # @type: 'passthrough' The configuration options for the TPM
> > passthrough type
> > +#        'emulator' The configuration options for TPM emulator
> > backend type
> >  #
> >  # Since: 1.5
> >  ##
> >  { 'union': 'TpmTypeOptions',
> > -   'data': { 'passthrough' : 'TPMPassthroughOptions' } }
> > +   'data': { 'passthrough' : 'TPMPassthroughOptions',
> > +             'emulator': 'TPMEmulatorOptions' } }
> > 
> >  ##
> >  # @TPMInfo:
> > diff --git a/qemu-options.hx b/qemu-options.hx
> > index 77859a2..1e93e53 100644
> > --- a/qemu-options.hx
> > +++ b/qemu-options.hx
> > @@ -3121,7 +3121,9 @@ 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 emulator,id=id,chardev=dev\n"
> > +    "                configure the TPM device using chardev
> > backend\n",
> >      QEMU_ARCH_ALL)
> >  STEXI
> > 
> > @@ -3130,8 +3132,8 @@ The general form of a TPM device option is:
> > 
> >  @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
> >  @findex -tpmdev
> > -Backend type must be:
> > -@option{passthrough}.
> > +Backend type must be either one of the following:
> > +@option{passthrough}, @option{emulator}.
> > 
> >  The specific backend type will determine the applicable options.
> >  The @code{-tpmdev} option creates the TPM backend and requires a
> > @@ -3181,6 +3183,20 @@ 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.
> > 
> > +@item -tpmdev emulator, id=@var{id}, chardev=@var{dev}
> > +
> > +(Linux-host only) Enable access to a TPM emulator using Unix
> > domain socket based
> > +chardev backend.
> > +
> > +@option{chardev} specifies the unique ID of a character device
> > backend that provides connection to the software TPM server.
> > +
> > +To create a TPM emulator backend device with chardev socket
> > backend:
> > +@example
> > +
> > +-chardev socket,id=chrtpm,path=/tmp/swtpm-ctrl -tpmdev
> > emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
> > +
> > +@end example
> > +
> >  @end table
> > 
> >  ETEXI
> > --
> > 2.7.4
> > 
> 
> 
- Amarnath

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator
  2017-09-26 12:05     ` Valluri, Amarnath
@ 2017-09-26 12:24       ` Marc-André Lureau
  2017-09-26 13:28         ` Valluri, Amarnath
  2017-09-26 13:59       ` Eric Blake
  1 sibling, 1 reply; 23+ messages in thread
From: Marc-André Lureau @ 2017-09-26 12:24 UTC (permalink / raw)
  To: Valluri, Amarnath
  Cc: stefanb@linux.vnet.ibm.com, dgilbert@redhat.com,
	qemu-devel@nongnu.org, armbru@redhat.com, eblake@redhat.com

Hi

On Tue, Sep 26, 2017 at 2:05 PM, Valluri, Amarnath
<amarnath.valluri@intel.com> wrote:
> Hi Marc,
>
> Thanks for your time reviewing this patchset. Please find my inline
> comments.
>
> On Sun, 2017-09-24 at 20:52 +0200, Marc-André Lureau wrote:
>> Hi
>>
>> Thanks for the nice update, removing the exec() code, using chardev
>> and a private socketpair. Some comments below:
>>
>> On Fri, Sep 22, 2017 at 2:33 PM, Amarnath Valluri
>> <amarnath.valluri@intel.com> wrote:
>> >
>> > This change introduces a new TPM backend driver that can
>> > communicate with
>> > swtpm(software TPM emulator) using unix domain socket interface.
>> > QEMU talks to
>> > TPM emulator using socket based chardev backend device.
>> >
>> > Swtpm uses two Unix sockets for communications, one for plain TPM
>> > commands and
>> > responses, and one for out-of-band control messages. QEMU passes
>> > data socket
>> > been used over the control channel.
>> >
>> > The swtpm and associated tools can be found here:
>> >     https://github.com/stefanberger/swtpm
>> >
>> > The swtpm's control channel protocol specification can be found
>> > here:
>> >     https://github.com/stefanberger/swtpm/wiki/Control-Channel-Spec
>> > ification
>> >
>> > Usage:
>> >     # setup TPM state directory
>> >     mkdir /tmp/mytpm
>> >     chown -R tss:root /tmp/mytpm
>> >     /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek
>> >
>> >     # Ask qemu to use TPM emulator with given tpm state directory
>> >     qemu-system-x86_64 \
>> >         [...] \
>> >         -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \
>> >         -tpmdev emulator,id=tpm0,chardev=chrtpm \
>> >         -device tpm-tis,tpmdev=tpm0 \
>> >         [...]
>> >
>> > Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
>> > ---
>> >  configure             |  15 +-
>> >  hmp.c                 |   5 +
>> >  hw/tpm/Makefile.objs  |   1 +
>> >  hw/tpm/tpm_emulator.c | 649
>> > ++++++++++++++++++++++++++++++++++++++++++++++++++
>> >  hw/tpm/tpm_ioctl.h    | 246 +++++++++++++++++++
>> >  qapi/tpm.json         |  21 +-
>> >  qemu-options.hx       |  22 +-
>> >  7 files changed, 950 insertions(+), 9 deletions(-)
>> >  create mode 100644 hw/tpm/tpm_emulator.c
>> >  create mode 100644 hw/tpm/tpm_ioctl.h
>> >
>> > diff --git a/configure b/configure
>> > index cb0f7ed..ce2df2d 100755
>> > --- a/configure
>> > +++ b/configure
>> > @@ -3461,10 +3461,15 @@ fi
>> >  ##########################################
>> >  # TPM passthrough is only on x86 Linux
>> >
>> > -if test "$targetos" = Linux && test "$cpu" = i386 -o "$cpu" =
>> > x86_64; then
>> > -  tpm_passthrough=$tpm
>> > +if test "$targetos" = Linux; then
>> > +  tpm_emulator=$tpm
>> > +  if test "$cpu" = i386 -o "$cpu" = x86_64; then
>> > +    tpm_passthrough=$tpm
>> > +  else
>> > +    tpm_passthrough=no
>> > +  fi
>> >  else
>> > -  tpm_passthrough=no
>> > +  tpm_emulator=no
>> >  fi
>> >
>> >  ##########################################
>> > @@ -5359,6 +5364,7 @@ echo "gcov enabled      $gcov"
>> >  echo "TPM support       $tpm"
>> >  echo "libssh2 support   $libssh2"
>> >  echo "TPM passthrough   $tpm_passthrough"
>> > +echo "TPM emulator      $tpm_emulator"
>> >  echo "QOM debugging     $qom_cast_debug"
>> >  echo "Live block migration $live_block_migration"
>> >  echo "lzo support       $lzo"
>> > @@ -5943,6 +5949,9 @@ if test "$tpm" = "yes"; then
>> >    if test "$tpm_passthrough" = "yes"; then
>> >      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
>> >    fi
>> > +  if test "$tpm_emulator" = "yes"; then
>> > +    echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak
>> It shouldn't require Linux, but posix (and I assume a port to other
>> systems isn't impossible). same for build-sys / help / comments.
> I agree, Can you suggest, what is the Qemu way of limiting this to
> 'posix'.
>>

there is CONFIG_POSIX

>> >
>> > +  fi
>> >  fi
>> >
>> >  echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
>> > diff --git a/hmp.c b/hmp.c
>> > index cf62b2e..7e69eca 100644
>> > --- a/hmp.c
>> > +++ b/hmp.c
>> > @@ -995,6 +995,7 @@ void hmp_info_tpm(Monitor *mon, const QDict
>> > *qdict)
>> >      Error *err = NULL;
>> >      unsigned int c = 0;
>> >      TPMPassthroughOptions *tpo;
>> > +    TPMEmulatorOptions *teo;
>> >
>> >      info_list = qmp_query_tpm(&err);
>> >      if (err) {
>> > @@ -1024,6 +1025,10 @@ 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_EMULATOR:
>> > +            teo = ti->options->u.emulator.data;
>> > +            monitor_printf(mon, ",chardev=%s", teo->chardev);
>> > +            break;
>> >          case TPM_TYPE__MAX:
>> >              break;
>> >          }
>> > diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
>> > index 64cecc3..41f0b7a 100644
>> > --- a/hw/tpm/Makefile.objs
>> > +++ b/hw/tpm/Makefile.objs
>> > @@ -1,2 +1,3 @@
>> >  common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
>> >  common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
>> > tpm_util.o
>> > +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
>> > diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
>> > new file mode 100644
>> > index 0000000..c02bbe2
>> > --- /dev/null
>> > +++ b/hw/tpm/tpm_emulator.c
>> > @@ -0,0 +1,649 @@
>> > +/*
>> > + *  emulator TPM driver
>> > + *
>> > + *  Copyright (c) 2017 Intel Corporation
>> > + *  Author: Amarnath Valluri <amarnath.valluri@intel.com>
>> > + *
>> > + *  Copyright (c) 2010 - 2013 IBM Corporation
>> > + *  Authors:
>> > + *    Stefan Berger <stefanb@us.ibm.com>
>> > + *
>> > + *  Copyright (C) 2011 IAIK, Graz University of Technology
>> > + *    Author: Andreas Niederl
>> > + *
>> > + * This library is free software; you can redistribute it and/or
>> > + * modify it under the terms of the GNU Lesser General Public
>> > + * License as published by the Free Software Foundation; either
>> > + * version 2 of the License, or (at your option) any later
>> > version.
>> > + *
>> > + * This library is distributed in the hope that it will be useful,
>> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> > GNU
>> > + * Lesser General Public License for more details.
>> > + *
>> > + * You should have received a copy of the GNU Lesser General
>> > Public
>> > + * License along with this library; if not, see <http://www.gnu.or
>> > g/licenses/>
>> > + *
>> > + */
>> > +
>> > +#include "qemu/osdep.h"
>> > +#include "qemu/error-report.h"
>> > +#include "qemu/sockets.h"
>> > +#include "io/channel-socket.h"
>> > +#include "sysemu/tpm_backend.h"
>> > +#include "tpm_int.h"
>> > +#include "hw/hw.h"
>> > +#include "hw/i386/pc.h"
>> > +#include "tpm_util.h"
>> > +#include "tpm_ioctl.h"
>> > +#include "migration/blocker.h"
>> > +#include "qapi/error.h"
>> > +#include "chardev/char-fe.h"
>> > +
>> > +#include <fcntl.h>
>> > +#include <sys/types.h>
>> > +#include <sys/stat.h>
>> > +#include <stdio.h>
>> > +
>> > +#define DEBUG_TPM 0
>> > +
>> > +#define DPRINT(fmt, ...) do { \
>> > +    if (DEBUG_TPM) { \
>> > +        fprintf(stderr, fmt, ## __VA_ARGS__); \
>> > +    } \
>> > +} while (0);
>> > +
>> > +#define DPRINTF(fmt, ...) DPRINT("tpm-emulator: "fmt"\n",
>> > __VA_ARGS__)
>> I would define a single DPRINTF() (& drop DPRINT usage below)
> Ok, will do
>>
>>
>> >
>> > +
>> > +#define TYPE_TPM_EMULATOR "tpm-emulator"
>> > +#define TPM_EMULATOR(obj) \
>> > +    OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
>> > +
>> > +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps &
>> > (cap)) == (cap))
>> > +
>> > +static const TPMDriverOps tpm_emulator_driver;
>> > +
>> > +/* data structures */
>> > +typedef struct TPMEmulator {
>> > +    TPMBackend parent;
>> > +
>> > +    TPMEmulatorOptions *options;
>> Contrary to my comment on previous patch, I realize it is convenient
>> to have a qapi pointer here, so you can use the free visitor later.
>>
>> >
>> > +    CharBackend ctrl_dev;
>> ctrl_chr perhaps? or just chr or be (the most common name in other
>> devices).
>>
>> >
>> > +    QIOChannel *data_ioc;
>> > +    bool op_executing;
>> > +    bool op_canceled;
>> I think those 2 fields can be dropped, see below.
>>
>> >
>> > +    bool had_startup_error;
>> I think some little refactoring could remove the whole
>> had_startup_error() backend & callback before or after the series.
>> tpm_backend_startup_tpm() already returns an error code.  device or
>> common backend code could handle & remember that.
> Yes, i agree, we can avoid had_startup_error() from DriverOps, i will
> do this.
>>
>> >
>> > +    TPMVersion tpm_version;
>> This is probably worth to consider in common code instead, but let's
>> not worry about it.
>>
>> >
>> > +    ptm_cap caps; /* capabilities of the TPM */
>> > +    uint8_t cur_locty_number; /* last set locality */
>> > +    QemuMutex state_lock;
>> > +    Error *migration_blocker;
>> > +} TPMEmulator;
>> > +
>> > +
>> > +static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long
>> > cmd, void *msg,
>> > +                                size_t msg_len_in, size_t
>> > msg_len_out)
>> > +{
>> > +    uint32_t cmd_no = cpu_to_be32(cmd);
>> > +    ssize_t n = sizeof(uint32_t) + msg_len_in;
>> > +    uint8_t *buf = NULL;
>> > +
>> > +    buf = (uint8_t *)malloc(n);
>> could be g_alloca() instead. Alternatively, why not call 2 write()
>> instead?
>>
>> none of the casts in this function are necessary, please remove them.
> Ok, will change to aclloca()

I just realized this is SOCK_SEQPACKET, ok

>>
>> >
>> > +    memcpy(buf, &cmd_no, sizeof(cmd_no));
>> > +    memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
>> > +
>> > +    n += qemu_chr_fe_write_all(dev, (const uint8_t *)buf, n);
>> weird
>>
>> >
>> > +    free(buf);
>> > +
>> > +    if (n > 0) {
>> with the n += above, you'll get interesting behaviour :)
> Ya, it was typo :)
>>
>> You probably want to check if any write above failed, and return
>> early
>> for the error case.
>>
>> Please improve the error handling in this function
>>
>> >
>> > +        if (msg_len_out > 0) {
>> > +            n = qemu_chr_fe_read_all(dev, (uint8_t *)msg,
>> > msg_len_out);
>> > +            /* simulate ioctl return value */
>> > +            if (n > 0) {
>> > +                n = 0;
>> > +            }
>> > +        } else {
>> > +            n = 0;
>> > +        }
>> > +    }
>> > +    return n;
>> > +}
>> > +
>> > +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_pt,
>> tpm_pt was tpm_passthrough I suppose.
>>
>> Please rename tpm_pt tpm_emu (or I would suggest "self", but this
>> isn't common name in qemu code - it's actually common in a close
>> c-object world., and it is quite convenient...)
> My interpretation about _pt was 'pointer' ;-) i will consider your
> suggetion.
>>
>> >
>> > +                                     const uint8_t *in, uint32_t
>> > in_len,
>> > +                                     uint8_t *out, uint32_t
>> > out_len,
>> > +                                     bool *selftest_done)
>> > +{
>> > +    ssize_t ret;
>> > +    bool is_selftest = false;
>> > +    const struct tpm_resp_hdr *hdr = NULL;
>> > +    Error *err = NULL;
>> > +
>> > +    tpm_pt->op_canceled = false;
>> > +    tpm_pt->op_executing = true;
>> > +    if (selftest_done) {
>> > +        *selftest_done = false;
>> > +        is_selftest = tpm_util_is_selftest(in, in_len);
>> > +    }
>> > +
>> > +    ret = qio_channel_write(tpm_pt->data_ioc, (char *)in, in_len,
>> > &err);
>> hmm, too bad qio_channel_write() doesn't take a void *
>>
>> why not write_all()?
> Agreed
>>
>> >
>> > +    if (ret != in_len || err) {
>> > +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
>> I don't think ECANCELED make sense for emulator code.
>>
>> >
>> > +            error_report("tpm-emulator: error while transmitting
>> > data "
>> > +                         "to TPM: %s", err ? error_get_pretty(err)
>> > : "");
>> > +            error_free(err);
>> > +        }
>> > +        goto err_exit;
>> > +    }
>> > +
>> > +    tpm_pt->op_executing = false;
>> > +
>> > +    ret = qio_channel_read(tpm_pt->data_ioc, (char *)out, out_len,
>> > &err);
>> > +    if (ret < 0 || err) {
>> read_all() ?
> The issue with read_all() is it does not return the no of bytes it
> read, so i would like to stict to _read()

ok

>>
>> >
>> > +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
>> > +            error_report("tpm-emulator: error while reading data
>> > from "
>> > +                         "TPM: %s", err ? error_get_pretty(err) :
>> > "");
>> > +            error_free(err);
>> > +        }
>> > +    } else if (ret >= sizeof(*hdr)) {
>> > +        hdr = (struct tpm_resp_hdr *)out;
>> > +    }
>> > +
>> > +    if (!hdr || be32_to_cpu(hdr->len) != ret) {
>> > +        error_report("tpm-emulator: received invalid response "
>> > +                     "packet from TPM with length :%ld", ret);
>> > +        ret = -1;
>> > +        goto err_exit;
>> > +    }
>> > +
>> > +    if (is_selftest) {
>> > +        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
>> > +    }
>> > +
>> > +    return 0;
>> > +
>> > +err_exit:
>> > +    if (ret < 0) {
>> > +        tpm_util_write_fatal_error_response(out, out_len);
>> > +    }
>> > +
>> > +    tpm_pt->op_executing = false;
>> > +
>> > +    return ret;
>> > +}
>> > +
>> > +static int tpm_emulator_set_locality(TPMEmulator *tpm_pt, uint8_t
>> > locty_number)
>> > +{
>> > +    ptm_loc loc;
>> > +
>> > +    DPRINTF("%s : locality: 0x%x", __func__, locty_number);
>> > +
>> > +    if (tpm_pt->cur_locty_number != locty_number) {
>> return early instead if ==, to avoid code indent etc
> Ok
>>
>> >
>> > +        DPRINTF("setting locality : 0x%x", locty_number);
>> > +        loc.u.req.loc = locty_number;
>> This number isn't set in be like the rest of the protocol?
> I doubt if i get ur point :(, can you please elaborate.

In the rest of the protocol, it uses big-endian. I suppose you should
cpu_to_be32(locty_number).

>>
>> >
>> > +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
>> > CMD_SET_LOCALITY, &loc,
>> > +                             sizeof(loc), sizeof(loc)) < 0) {
>> > +            error_report("tpm-emulator: could not set locality :
>> > %s",
>> > +                         strerror(errno));
>> > +            return -1;
>> > +        }
>> > +        loc.u.resp.tpm_result =
>> > be32_to_cpu(loc.u.resp.tpm_result);
>> > +        if (loc.u.resp.tpm_result != 0) {
>> > +            error_report("tpm-emulator: TPM result for set
>> > locality : 0x%x",
>> > +                         loc.u.resp.tpm_result);
>> > +            return -1;
>> > +        }
>> > +        tpm_pt->cur_locty_number = locty_number;
>> > +    }
>> > +    return 0;
>> > +}
>> > +
>> > +static void tpm_emulator_handle_request(TPMBackend *tb,
>> > TPMBackendCmd cmd)
>> > +{
>> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> > +    TPMLocality *locty = NULL;
>> > +    bool selftest_done = false;
>> > +
>> > +    DPRINTF("processing command type %d", cmd);
>> > +
>> > +    switch (cmd) {
>> > +    case TPM_BACKEND_CMD_PROCESS_CMD:
>> > +        qemu_mutex_lock(&tpm_pt->state_lock);
>> > +        locty = tb->tpm_state->locty_data;
>> > +        if (tpm_emulator_set_locality(tpm_pt,
>> > +                                      tb->tpm_state->locty_number)
>> > < 0) {
>> > +            tpm_util_write_fatal_error_response(locty-
>> > >r_buffer.buffer,
>> > +                                           locty->r_buffer.size);
>> return / goto here instead of else.
> We should not retrun from here, we have to propagte the error response
> to device, and unlock the state.
>>
>> >
>> > +        } else {
>> > +            tpm_emulator_unix_tx_bufs(tpm_pt, locty-
>> > >w_buffer.buffer,
>> > +                                              locty->w_offset,
>> > +                                              locty-
>> > >r_buffer.buffer,
>> > +                                              locty-
>> > >r_buffer.size,
>> > +                                              &selftest_done);
>> no error handling?
> The error case is supposed to handle by device, where we are filling
> into out buffer, using tpm_util_write_fatal_error_response().
>>
>> >
>> > +        }
>> > +
>> > +        tb->recv_data_callback(tb->tpm_state, tb->tpm_state-
>> > >locty_number,
>> > +                               selftest_done);
>> > +        qemu_mutex_unlock(&tpm_pt->state_lock);
>> > +
>> > +        break;
>> > +    case TPM_BACKEND_CMD_INIT:
>> > +    case TPM_BACKEND_CMD_END:
>> > +    case TPM_BACKEND_CMD_TPM_RESET:
>> > +        /* nothing to do */
>> > +        break;
>> > +    }
>> > +}
>> > +
>> > +/*
>> > + * Gracefully shut down the external unixio TPM
>> > + */
>> > +static void tpm_emulator_shutdown(TPMEmulator *tpm_pt)
>> > +{
>> > +    ptm_res res;
>> > +
>> > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SHUTDOWN,
>> > &res, 0,
>> > +                         sizeof(res)) < 0) {
>> > +        error_report("tpm-emulator: Could not cleanly shutdown the
>> > TPM: %s",
>> > +                     strerror(errno));
>> > +    } else if (res != 0) {
>> > +        error_report("tpm-emulator: TPM result for sutdown: 0x%x",
>> > +                     be32_to_cpu(res));
>> > +    }
>> > +}
>> > +
>> > +static int tpm_emulator_probe_caps(TPMEmulator *tpm_pt)
>> > +{
>> > +    DPRINTF("%s", __func__);
>> > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
>> > CMD_GET_CAPABILITY,
>> > +                         &tpm_pt->caps, 0, sizeof(tpm_pt->caps)) <
>> > 0) {
>> > +        error_report("tpm-emulator: probing failed : %s",
>> > strerror(errno));
>> > +        return -1;
>> > +    }
>> > +
>> > +    tpm_pt->caps = be64_to_cpu(tpm_pt->caps);
>> > +
>> > +    DPRINTF("capbilities : 0x%lx", tpm_pt->caps);
>> > +
>> > +    return 0;
>> > +}
>> > +
>> > +static int tpm_emulator_check_caps(TPMEmulator *tpm_pt)
>> > +{
>> > +    ptm_cap caps = 0;
>> > +    const char *tpm = NULL;
>> > +
>> > +    /* 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-emulator: TPM version has not been
>> > set");
>> > +        return -1;
>> > +    }
>> > +
>> > +    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, caps)) {
>> > +        error_report("tpm-emulator: TPM does not implement minimum
>> > set of "
>> > +                     "required capabilities for TPM %s (0x%x)",
>> > tpm, (int)caps);
>> > +        return -1;
>> > +    }
>> > +
>> > +    return 0;
>> > +}
>> > +
>> > +static int tpm_emulator_startup_tpm(TPMBackend *tb)
>> > +{
>> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> > +    ptm_init init;
>> > +    ptm_res res;
>> > +
>> > +    DPRINTF("%s", __func__);
>> > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_INIT, &init,
>> > sizeof(init),
>> > +                         sizeof(init)) < 0) {
>> > +        error_report("tpm-emulator: could not send INIT: %s",
>> > +                     strerror(errno));
>> > +        goto err_exit;
>> > +    }
>> > +
>> > +    res = be32_to_cpu(init.u.resp.tpm_result);
>> > +    if (res) {
>> > +        error_report("tpm-emulator: TPM result for CMD_INIT:
>> > 0x%x", res);
>> > +        goto err_exit;
>> > +    }
>> > +    return 0;
>> > +
>> > +err_exit:
>> > +    tpm_pt->had_startup_error = true;
>> > +    return -1;
>> > +}
>> > +
>> > +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
>> > +{
>> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> > +    ptm_est est;
>> > +
>> > +    DPRINTF("%s", __func__);
>> > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
>> > CMD_GET_TPMESTABLISHED, &est, 0,
>> > +                         sizeof(est)) < 0) {
>> > +        error_report("tpm-emulator: Could not get the TPM
>> > established flag: %s",
>> > +                     strerror(errno));
>> > +        return false;
>> > +    }
>> > +    DPRINTF("established flag: %0x", est.u.resp.bit);
>> > +
>> > +    return (est.u.resp.bit != 0);
>> > +}
>> > +
>> > +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
>> > +                                                   uint8_t locty)
>> > +{
>> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> > +    ptm_reset_est reset_est;
>> > +    ptm_res res;
>> > +
>> > +    /* only a TPM 2.0 will support this */
>> > +    if (tpm_pt->tpm_version == TPM_VERSION_2_0) {
>> > +        reset_est.u.req.loc = tpm_pt->cur_locty_number;
>> > +
>> > +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
>> > CMD_RESET_TPMESTABLISHED,
>> > +                                 &reset_est, sizeof(reset_est),
>> > +                                 sizeof(reset_est)) < 0) {
>> > +            error_report("tpm-emulator: Could not reset the
>> > establishment bit: "
>> > +                          "%s", strerror(errno));
>> > +            return -1;
>> > +        }
>> > +
>> > +        res = be32_to_cpu(reset_est.u.resp.tpm_result);
>> > +        if (res) {
>> > +            error_report("tpm-emulator: TPM result for rest
>> > establixhed flag: "
>> > +                         "0x%x", res);
>> > +            return -1;
>> > +        }
>> > +    }
>> > +
>> > +    return 0;
>> > +}
>> > +
>> > +static bool tpm_emulator_had_startup_error(TPMBackend *tb)
>> > +{
>> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> > +
>> > +    return tpm_pt->had_startup_error;
>> > +}
>> > +
>> > +static void tpm_emulator_cancel_cmd(TPMBackend *tb)
>> > +{
>> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> > +    ptm_res res;
>> > +
>> > +    /*
>> > +     * As of Linux 3.7 the tpm_tis driver does not properly cancel
>> > +     * commands on all TPM manufacturers' TPMs.
>> > +     * Only cancel if we're busy so we don't cancel someone else's
>> > +     * command, e.g., a command executed on the host.
>> > +     */
>> > +    if (tpm_pt->op_executing) {
>> The field is set in the worker thread. This is racy. Fortunately this
>> is not relevant for emulator, I think you can simply drop that check
>> and the variable. Stefan should confirm though.
>>
>> >
>> > +        if (TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt,
>> > PTM_CAP_CANCEL_TPM_CMD)) {
>> > +            if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
>> > CMD_CANCEL_TPM_CMD,
>> > +                                     &res, 0, sizeof(res)) < 0) {
>> > +                error_report("tpm-emulator: Could not cancel
>> > command: %s",
>> > +                             strerror(errno));
>> > +            } else if (res != 0) {
>> > +                error_report("tpm-emulator: Failed to cancel TPM:
>> > 0x%x",
>> > +                             be32_to_cpu(res));
>> > +            } else {
>> > +                tpm_pt->op_canceled = true;
>> > +            }
>> > +        }
>> > +    }
>> > +}
>> > +
>> > +static void tpm_emulator_reset(TPMBackend *tb)
>> > +{
>> > +    DPRINTF("%s", __func__);
>> > +
>> > +    tpm_emulator_cancel_cmd(tb);
>> > +}
>> > +
>> > +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
>> > +{
>> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> > +
>> > +    return tpm_pt->tpm_version;
>> > +}
>> > +
>> > +static void tpm_emulator_block_migration(TPMEmulator *tpm_pt)
>> > +{
>> > +    Error *err = NULL;
>> > +
>> > +    error_setg(&tpm_pt->migration_blocker,
>> > +               "Migration disabled: TPM emulator not yet
>> > migratable");
>> > +    migrate_add_blocker(tpm_pt->migration_blocker, &err);
>> > +    if (err) {
>> > +        error_free(err);
>> probably better to report_err it instead
> Ok
>>
>> >
>> > +        error_free(tpm_pt->migration_blocker);
>> > +        tpm_pt->migration_blocker = NULL;
>> and return an error code.
> Will do
>>
>> >
>> > +    }
>> > +}
>> > +
>> > +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_pt)
>> > +{
>> > +    ptm_res res;
>> > +    Error *err = NULL;
>> > +    int fds[2] = { -1, -1 };
>> > +
>> > +    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) {
>> > +        error_report("tpm-emulator: Failed to create socketpair");
>> > +        return -1;
>> > +    }
>> > +
>> > +    qemu_chr_fe_set_msgfds(&tpm_pt->ctrl_dev, fds + 1, 1);
>> > +
>> > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SET_DATAFD,
>> > &res, 0,
>> > +                    sizeof(res)) || res != 0) {
>> Yay! for making life easier and hiding a protocol detail.
>>
>> >
>> > +        error_report("tpm-emulator: Failed to send CMD_SET_DATAFD:
>> > %s",
>> > +                     strerror(errno));
>> > +        goto err_exit;
>> > +    }
>> > +
>> > +    tpm_pt->data_ioc =
>> > QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
>> > +    if (err) {
>> > +        error_report("tpm-emulator: Failed to create io channel :
>> > %s",
>> > +                       error_get_pretty(err));
>> error_prepend()?
> Ok
>>
>> >
>> > +        error_free(err);
>> > +        goto err_exit;
>> > +    }
>> close fds[1] ?
> I guess we are not supposed to close the other end of the
> socketpair/pipe, as it is not forked process.

You will close this end, not the other end.

>>
>> >
>> > +
>> > +    return 0;
>> > +
>> > +err_exit:
>> > +    closesocket(fds[0]);
>> > +    closesocket(fds[1]);
>> > +    return -1;
>> > +}
>> > +
>> > +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_pt,
>> > QemuOpts *opts)
>> > +{
>> > +    const char *value;
>> > +
>> > +    value = qemu_opt_get(opts, "chardev");
>> > +    if (value) {
>> > +        Error *err = NULL;
>> > +        Chardev *dev = qemu_chr_find(value);
>> > +
>> > +        tpm_pt->options->chardev = g_strdup(value);
>> > +
>> > +        if (!dev || !qemu_chr_fe_init(&tpm_pt->ctrl_dev, dev,
>> > &err)) {
>> > +            error_report("tpm-emulator: No valid chardev found at
>> > '%s': %s",
>> > +                         value, err ? error_get_pretty(err) : "");
>> > +            error_free(err);
>> error_prepend
> Ok
>>
>> >
>> > +            goto err;
>> > +        }
>> > +    }
>> > +
>> > +    if (tpm_emulator_prepare_data_fd(tpm_pt) < 0) {
>> > +        goto err;
>> > +    }
>> > +
>> > +    /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as
>> > it also used
>> > +     * by passthrough driver, which not yet using GIOChannel.
>> > +     */
>> > +    if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_pt->data_ioc)-
>> > >fd,
>> > +                             &tpm_pt->tpm_version)) {
>> > +        error_report("'%s' is not emulating TPM device. Error:
>> > %s",
>> > +                      tpm_pt->options->chardev, strerror(errno));
>> > +        goto err;
>> > +    }
>> > +
>> > +    DPRINTF("TPM Version %s", tpm_pt->tpm_version ==
>> > TPM_VERSION_1_2 ? "1.2" :
>> > +             (tpm_pt->tpm_version == TPM_VERSION_2_0 ?  "2.0" :
>> > "Unspecified"));
>> > +
>> > +    if (tpm_emulator_probe_caps(tpm_pt) ||
>> > +        tpm_emulator_check_caps(tpm_pt)) {
>> > +        goto err;
>> > +    }
>> > +
>> > +    tpm_emulator_block_migration(tpm_pt);
>> > +
>> > +    return 0;
>> > +
>> > +err:
>> > +    DPRINT("Startup error\n");
>> > +    return -1;
>> > +}
>> > +
>> > +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char
>> > *id)
>> > +{
>> > +    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;
>> > +    }
>> > +
>> > +    return tb;
>> > +
>> > +err_exit:
>> > +    object_unref(OBJECT(tb));
>> > +
>> > +    return NULL;
>> > +}
>> > +
>> > +static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend
>> > *tb)
>> > +{
>> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
>> > +    TpmTypeOptions *options = NULL;
>> > +    TPMEmulatorOptions *eoptions = NULL;
>> > +
>> > +    eoptions = g_new0(TPMEmulatorOptions, 1);
>> > +    if (!eoptions) {
>> > +        return NULL;
>> > +    }
>> > +    DPRINTF("%s", __func__);
>> > +
>> > +    eoptions->chardev = g_strdup(tpm_pt->options->chardev);
>> > +    options = g_new0(TpmTypeOptions, 1);
>> > +    if (!options) {
>> > +        qapi_free_TPMEmulatorOptions(eoptions);
>> > +        return NULL;
>> > +    }
>> > +
>> > +    options->type = TPM_TYPE_EMULATOR;
>> > +    options->u.emulator.data = eoptions;
>> I think this is a job for QAPI_CLONE.
>>
>> >
>> > +
>> > +    return options;
>> > +}
>> > +
>> > +static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
>> > +    TPM_STANDARD_CMDLINE_OPTS,
>> > +    {
>> > +        .name = "chardev",
>> > +        .type = QEMU_OPT_STRING,
>> > +        .help = "Character device to use for out-of-band control
>> > messages",
>> > +    },
>> > +    { /* end of list */ },
>> > +};
>> > +
>> > +static const TPMDriverOps tpm_emulator_driver = {
>> > +    .type                     = TPM_TYPE_EMULATOR,
>> > +    .opts                     = tpm_emulator_cmdline_opts,
>> > +    .desc                     = "TPM emulator backend driver",
>> > +
>> > +    .create                   = tpm_emulator_create,
>> > +    .startup_tpm              = tpm_emulator_startup_tpm,
>> > +    .reset                    = tpm_emulator_reset,
>> > +    .had_startup_error        = tpm_emulator_had_startup_error,
>> > +    .cancel_cmd               = tpm_emulator_cancel_cmd,
>> > +    .get_tpm_established_flag =
>> > tpm_emulator_get_tpm_established_flag,
>> > +    .reset_tpm_established_flag =
>> > tpm_emulator_reset_tpm_established_flag,
>> > +    .get_tpm_version          = tpm_emulator_get_tpm_version,
>> > +    .get_tpm_options          = tpm_emulator_get_tpm_options,
>> > +};
>> > +
>> > +static void tpm_emulator_inst_init(Object *obj)
>> > +{
>> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
>> > +
>> > +    DPRINTF("%s", __func__);
>> > +    tpm_pt->options = g_new0(TPMEmulatorOptions, 1);
>> > +    tpm_pt->op_executing = tpm_pt->op_canceled = false;
>> > +    tpm_pt->had_startup_error = false;
>> > +    tpm_pt->cur_locty_number = ~0;
>> > +    qemu_mutex_init(&tpm_pt->state_lock);
>> > +}
>> > +
>> > +static void tpm_emulator_inst_finalize(Object *obj)
>> > +{
>> > +    TPMEmulator *tpm_pt = TPM_EMULATOR(obj);
>> > +
>> > +    tpm_emulator_cancel_cmd(TPM_BACKEND(obj));
>> > +    tpm_emulator_shutdown(tpm_pt);
>> > +
>> > +    if (tpm_pt->data_ioc) {
>> > +        qio_channel_close(tpm_pt->data_ioc, NULL);
>> > +    }
>> > +
>> > +    qemu_chr_fe_deinit(&tpm_pt->ctrl_dev, false);
>> > +
>> > +    if (tpm_pt->options) {
>> > +        qapi_free_TPMEmulatorOptions(tpm_pt->options);
>> > +    }
>> > +
>> > +    if (tpm_pt->migration_blocker) {
>> > +        migrate_del_blocker(tpm_pt->migration_blocker);
>> > +        error_free(tpm_pt->migration_blocker);
>> > +    }
>> > +}
>> > +
>> > +static void tpm_emulator_class_init(ObjectClass *klass, void
>> > *data)
>> > +{
>> > +    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
>> > +    tbc->ops = &tpm_emulator_driver;
>> > +    tbc->handle_request = tpm_emulator_handle_request;
>> > +}
>> > +
>> > +static const TypeInfo tpm_emulator_info = {
>> > +    .name = TYPE_TPM_EMULATOR,
>> > +    .parent = TYPE_TPM_BACKEND,
>> > +    .instance_size = sizeof(TPMEmulator),
>> > +    .class_init = tpm_emulator_class_init,
>> > +    .instance_init = tpm_emulator_inst_init,
>> > +    .instance_finalize = tpm_emulator_inst_finalize,
>> > +};
>> > +
>> > +static void tpm_emulator_register(void)
>> > +{
>> > +    type_register_static(&tpm_emulator_info);
>> > +    tpm_register_driver(&tpm_emulator_driver);
>> > +}
>> > +
>> > +type_init(tpm_emulator_register)
>> > diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
>> > new file mode 100644
>> > index 0000000..33564b1
>> > --- /dev/null
>> > +++ b/hw/tpm/tpm_ioctl.h
>> This file is copied from swtpm project. Could swtpm have it installed
>> on system instead?
>>
>> >
>> > @@ -0,0 +1,246 @@
>> > +/*
>> > + * tpm_ioctl.h
>> > + *
>> > + * (c) Copyright IBM Corporation 2014, 2015.
>> > + *
>> > + * This file is licensed under the terms of the 3-clause BSD
>> > license
>> > + */
>> > +#ifndef _TPM_IOCTL_H_
>> > +#define _TPM_IOCTL_H_
>> > +
>> > +#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 ptm_res as the first element.
>> > + * ptm_res corresponds to the error code of a command executed by
>> > the TPM.
>> > + */
>> > +
>> > +typedef uint32_t ptm_res;
>> > +
>> > +/* PTM_GET_TPMESTABLISHED: get the establishment bit */
>> > +struct ptm_est {
>> > +    union {
>> > +        struct {
>> > +            ptm_res tpm_result;
>> > +            unsigned char bit; /* TPM established bit */
>> > +        } resp; /* response */
>> > +    } u;
>> > +};
>> > +
>> > +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */
>> > +struct ptm_reset_est {
>> > +    union {
>> > +        struct {
>> > +            uint8_t loc; /* locality to use */
>> > +        } req; /* request */
>> > +        struct {
>> > +            ptm_res tpm_result;
>> > +        } resp; /* response */
>> > +    } u;
>> > +};
>> > +
>> > +/* PTM_INIT */
>> > +struct ptm_init {
>> > +    union {
>> > +        struct {
>> > +            uint32_t init_flags; /* see definitions below */
>> > +        } req; /* request */
>> > +        struct {
>> > +            ptm_res tpm_result;
>> > +        } resp; /* response */
>> > +    } u;
>> > +};
>> > +
>> > +/* above init_flags */
>> > +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0)
>> > +    /* delete volatile state file after reading it */
>> > +
>> > +/* PTM_SET_LOCALITY */
>> > +struct ptm_loc {
>> > +    union {
>> > +        struct {
>> > +            uint8_t loc; /* locality to set */
>> > +        } req; /* request */
>> > +        struct {
>> > +            ptm_res tpm_result;
>> > +        } resp; /* response */
>> > +    } u;
>> > +};
>> > +
>> > +/* PTM_HASH_DATA: hash given data */
>> > +struct ptm_hdata {
>> > +    union {
>> > +        struct {
>> > +            uint32_t length;
>> > +            uint8_t data[4096];
>> > +        } req; /* request */
>> > +        struct {
>> > +            ptm_res tpm_result;
>> > +        } resp; /* response */
>> > +    } 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 PTM_STATE_BLOB_SIZE (3 * 1024)
>> > +
>> > +/*
>> > + * The following is the data structure to get state blobs from the
>> > TPM.
>> > + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE,
>> > multiple reads
>> > + * with this ioctl and with adjusted offset are necessary. All
>> > bytes
>> > + * must be transferred and the transfer is done once the last byte
>> > has been
>> > + * returned.
>> > + * It is possible to use the read() interface for reading the
>> > data; however, the
>> > + * first bytes of the state blob will be part of the response to
>> > the ioctl(); a
>> > + * subsequent read() is only necessary if the total length
>> > (totlength) exceeds
>> > + * the number of received bytes. seek() is not supported.
>> > + */
>> > +struct ptm_getstate {
>> > +    union {
>> > +        struct {
>> > +            uint32_t state_flags; /* may be:
>> > PTM_STATE_FLAG_DECRYPTED */
>> > +            uint32_t type;        /* which blob to pull */
>> > +            uint32_t offset;      /* offset from where to read */
>> > +        } req; /* request */
>> > +        struct {
>> > +            ptm_res tpm_result;
>> > +            uint32_t state_flags; /* may be:
>> > PTM_STATE_FLAG_ENCRYPTED */
>> > +            uint32_t totlength;   /* total length that will be
>> > transferred */
>> > +            uint32_t length;      /* number of bytes in following
>> > buffer */
>> > +            uint8_t  data[PTM_STATE_BLOB_SIZE];
>> > +        } resp; /* response */
>> > +    } 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 PTM_STATE_FLAG_DECRYPTED     1 /* on input:  get decrypted
>> > state */
>> > +#define PTM_STATE_FLAG_ENCRYPTED     2 /* on output: state is
>> > encrypted */
>> > +
>> > +/*
>> > + * The following is the data structure to set state blobs in the
>> > TPM.
>> > + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE,
>> > multiple
>> > + * 'writes' using this ioctl are necessary. The last packet is
>> > indicated
>> > + * by the length being smaller than the PTM_STATE_BLOB_SIZE.
>> > + * The very first packet may have a length indicator of '0'
>> > enabling
>> > + * a write() with all the bytes from a buffer. If the write()
>> > interface
>> > + * is used, a final ioctl with a non-full buffer must be made to
>> > indicate
>> > + * that all data were transferred (a write with 0 bytes would not
>> > work).
>> > + */
>> > +struct ptm_setstate {
>> > +    union {
>> > +        struct {
>> > +            uint32_t state_flags; /* may be
>> > PTM_STATE_FLAG_ENCRYPTED */
>> > +            uint32_t type;        /* which blob to set */
>> > +            uint32_t length;      /* length of the data;
>> > +                                     use 0 on the first packet to
>> > +                                     transfer using write() */
>> > +            uint8_t data[PTM_STATE_BLOB_SIZE];
>> > +        } req; /* request */
>> > +        struct {
>> > +            ptm_res tpm_result;
>> > +        } resp; /* response */
>> > +    } u;
>> > +};
>> > +
>> > +/*
>> > + * PTM_GET_CONFIG: Data structure to get runtime configuration
>> > information
>> > + * such as which keys are applied.
>> > + */
>> > +struct ptm_getconfig {
>> > +    union {
>> > +        struct {
>> > +            ptm_res tpm_result;
>> > +            uint32_t flags;
>> > +        } resp; /* response */
>> > +    } u;
>> > +};
>> > +
>> > +#define PTM_CONFIG_FLAG_FILE_KEY        0x1
>> > +#define PTM_CONFIG_FLAG_MIGRATION_KEY   0x2
>> > +
>> > +
>> > +typedef uint64_t ptm_cap;
>> > +typedef struct ptm_est ptm_est;
>> > +typedef struct ptm_reset_est ptm_reset_est;
>> > +typedef struct ptm_loc ptm_loc;
>> > +typedef struct ptm_hdata ptm_hdata;
>> > +typedef struct ptm_init ptm_init;
>> > +typedef struct ptm_getstate ptm_getstate;
>> > +typedef struct ptm_setstate ptm_setstate;
>> > +typedef struct ptm_getconfig ptm_getconfig;
>> > +
>> > +/* 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)
>> > +#define PTM_CAP_GET_CONFIG         (1 << 11)
>> > +#define PTM_CAP_SET_DATAFD         (1 << 12)
>> > +
>> > +enum {
>> > +    PTM_GET_CAPABILITY     = _IOR('P', 0, ptm_cap),
>> > +    PTM_INIT               = _IOWR('P', 1, ptm_init),
>> > +    PTM_SHUTDOWN           = _IOR('P', 2, ptm_res),
>> > +    PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est),
>> > +    PTM_SET_LOCALITY       = _IOWR('P', 4, ptm_loc),
>> > +    PTM_HASH_START         = _IOR('P', 5, ptm_res),
>> > +    PTM_HASH_DATA          = _IOWR('P', 6, ptm_hdata),
>> > +    PTM_HASH_END           = _IOR('P', 7, ptm_res),
>> > +    PTM_CANCEL_TPM_CMD     = _IOR('P', 8, ptm_res),
>> > +    PTM_STORE_VOLATILE     = _IOR('P', 9, ptm_res),
>> > +    PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est),
>> > +    PTM_GET_STATEBLOB      = _IOWR('P', 11, ptm_getstate),
>> > +    PTM_SET_STATEBLOB      = _IOWR('P', 12, ptm_setstate),
>> > +    PTM_STOP               = _IOR('P', 13, ptm_res),
>> > +    PTM_GET_CONFIG         = _IOR('P', 14, ptm_getconfig),
>> > +    PTM_SET_DATAFD         = _IOR('P', 15, ptm_res),
>> > +};
>> > +
>> > +/*
>> > + * Commands used by the non-CUSE TPMs
>> > + *
>> > + * All messages container big-endian data.
>> > + *
>> > + * The return messages only contain the 'resp' part of the unions
>> > + * in the data structures above. Besides that the limits in the
>> > + * buffers above (ptm_hdata:u.req.data and
>> > ptm_get_state:u.resp.data
>> > + * and ptm_set_state:u.req.data) are 0xffffffff.
>> > + */
>> > +enum {
>> > +    CMD_GET_CAPABILITY = 1,
>> > +    CMD_INIT,
>> > +    CMD_SHUTDOWN,
>> > +    CMD_GET_TPMESTABLISHED,
>> > +    CMD_SET_LOCALITY,
>> > +    CMD_HASH_START,
>> > +    CMD_HASH_DATA,
>> > +    CMD_HASH_END,
>> > +    CMD_CANCEL_TPM_CMD,
>> > +    CMD_STORE_VOLATILE,
>> > +    CMD_RESET_TPMESTABLISHED,
>> > +    CMD_GET_STATEBLOB,
>> > +    CMD_SET_STATEBLOB,
>> > +    CMD_STOP,
>> > +    CMD_GET_CONFIG,
>> > +    CMD_SET_DATAFD
>> > +};
>> > +
>> > +#endif /* _TPM_IOCTL_H */
>> > diff --git a/qapi/tpm.json b/qapi/tpm.json
>> > index e8b2d8d..7093f26 100644
>> > --- a/qapi/tpm.json
>> > +++ b/qapi/tpm.json
>> > @@ -39,10 +39,12 @@
>> >  # An enumeration of TPM types
>> >  #
>> >  # @passthrough: TPM passthrough type
>> > +# @emulator: Software Emulator TPM type
>> > +#            Since: 2.11
>> >  #
>> >  # Since: 1.5
>> >  ##
>> > -{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }
>> > +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] }
>> >
>> >  ##
>> >  # @query-tpm-types:
>> > @@ -56,7 +58,7 @@
>> >  # Example:
>> >  #
>> >  # -> { "execute": "query-tpm-types" }
>> > -# <- { "return": [ "passthrough" ] }
>> > +# <- { "return": [ "passthrough", "emulator" ] }
>> >  #
>> >  ##
>> >  { 'command': 'query-tpm-types', 'returns': ['TpmType'] }
>> > @@ -77,16 +79,29 @@
>> >                                               '*cancel-path' :
>> > 'str'} }
>> >
>> >  ##
>> > +# @TPMEmulatorOptions:
>> > +#
>> > +# Information about the TPM emulator type
>> > +#
>> > +# @chardev: Name of a unix socket chardev
>> > +#
>> > +# Since: 2.11
>> > +##
>> > +{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } }
>> > +
>> > +##
>> >  # @TpmTypeOptions:
>> >  #
>> >  # A union referencing different TPM backend types' configuration
>> > options
>> >  #
>> >  # @type: 'passthrough' The configuration options for the TPM
>> > passthrough type
>> > +#        'emulator' The configuration options for TPM emulator
>> > backend type
>> >  #
>> >  # Since: 1.5
>> >  ##
>> >  { 'union': 'TpmTypeOptions',
>> > -   'data': { 'passthrough' : 'TPMPassthroughOptions' } }
>> > +   'data': { 'passthrough' : 'TPMPassthroughOptions',
>> > +             'emulator': 'TPMEmulatorOptions' } }
>> >
>> >  ##
>> >  # @TPMInfo:
>> > diff --git a/qemu-options.hx b/qemu-options.hx
>> > index 77859a2..1e93e53 100644
>> > --- a/qemu-options.hx
>> > +++ b/qemu-options.hx
>> > @@ -3121,7 +3121,9 @@ 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 emulator,id=id,chardev=dev\n"
>> > +    "                configure the TPM device using chardev
>> > backend\n",
>> >      QEMU_ARCH_ALL)
>> >  STEXI
>> >
>> > @@ -3130,8 +3132,8 @@ The general form of a TPM device option is:
>> >
>> >  @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
>> >  @findex -tpmdev
>> > -Backend type must be:
>> > -@option{passthrough}.
>> > +Backend type must be either one of the following:
>> > +@option{passthrough}, @option{emulator}.
>> >
>> >  The specific backend type will determine the applicable options.
>> >  The @code{-tpmdev} option creates the TPM backend and requires a
>> > @@ -3181,6 +3183,20 @@ 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.
>> >
>> > +@item -tpmdev emulator, id=@var{id}, chardev=@var{dev}
>> > +
>> > +(Linux-host only) Enable access to a TPM emulator using Unix
>> > domain socket based
>> > +chardev backend.
>> > +
>> > +@option{chardev} specifies the unique ID of a character device
>> > backend that provides connection to the software TPM server.
>> > +
>> > +To create a TPM emulator backend device with chardev socket
>> > backend:
>> > +@example
>> > +
>> > +-chardev socket,id=chrtpm,path=/tmp/swtpm-ctrl -tpmdev
>> > emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
>> > +
>> > +@end example
>> > +
>> >  @end table
>> >
>> >  ETEXI
>> > --
>> > 2.7.4
>> >
>>
>>
> - Amarnath



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator
  2017-09-26 12:24       ` Marc-André Lureau
@ 2017-09-26 13:28         ` Valluri, Amarnath
  2017-09-26 13:55           ` Marc-André Lureau
  0 siblings, 1 reply; 23+ messages in thread
From: Valluri, Amarnath @ 2017-09-26 13:28 UTC (permalink / raw)
  To: marcandre.lureau@gmail.com
  Cc: stefanb@linux.vnet.ibm.com, dgilbert@redhat.com,
	qemu-devel@nongnu.org, armbru@redhat.com, eblake@redhat.com

On Tue, 2017-09-26 at 14:24 +0200, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Sep 26, 2017 at 2:05 PM, Valluri, Amarnath
> <amarnath.valluri@intel.com> wrote:
> > 
> > Hi Marc,
> > 
> > Thanks for your time reviewing this patchset. Please find my inline
> > comments.
> > 
> > On Sun, 2017-09-24 at 20:52 +0200, Marc-André Lureau wrote:
> > > 
> > > Hi
> > > 
> > > Thanks for the nice update, removing the exec() code, using
> > > chardev
> > > and a private socketpair. Some comments below:
> > > 
> > > On Fri, Sep 22, 2017 at 2:33 PM, Amarnath Valluri
> > > <amarnath.valluri@intel.com> wrote:
> > > > 
> > > > 
> > > > This change introduces a new TPM backend driver that can
> > > > communicate with
> > > > swtpm(software TPM emulator) using unix domain socket
> > > > interface.
> > > > QEMU talks to
> > > > TPM emulator using socket based chardev backend device.
> > > > 
> > > > Swtpm uses two Unix sockets for communications, one for plain
> > > > TPM
> > > > commands and
> > > > responses, and one for out-of-band control messages. QEMU
> > > > passes
> > > > data socket
> > > > been used over the control channel.
> > > > 
> > > > The swtpm and associated tools can be found here:
> > > >     https://github.com/stefanberger/swtpm
> > > > 
> > > > The swtpm's control channel protocol specification can be found
> > > > here:
> > > >     https://github.com/stefanberger/swtpm/wiki/Control-Channel-
> > > > Spec
> > > > ification
> > > > 
> > > > Usage:
> > > >     # setup TPM state directory
> > > >     mkdir /tmp/mytpm
> > > >     chown -R tss:root /tmp/mytpm
> > > >     /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek
> > > > 
> > > >     # Ask qemu to use TPM emulator with given tpm state
> > > > directory
> > > >     qemu-system-x86_64 \
> > > >         [...] \
> > > >         -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \
> > > >         -tpmdev emulator,id=tpm0,chardev=chrtpm \
> > > >         -device tpm-tis,tpmdev=tpm0 \
> > > >         [...]
> > > > 
> > > > Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
> > > > ---
> > > >  configure             |  15 +-
> > > >  hmp.c                 |   5 +
> > > >  hw/tpm/Makefile.objs  |   1 +
> > > >  hw/tpm/tpm_emulator.c | 649
> > > > ++++++++++++++++++++++++++++++++++++++++++++++++++
> > > >  hw/tpm/tpm_ioctl.h    | 246 +++++++++++++++++++
> > > >  qapi/tpm.json         |  21 +-
> > > >  qemu-options.hx       |  22 +-
> > > >  7 files changed, 950 insertions(+), 9 deletions(-)
> > > >  create mode 100644 hw/tpm/tpm_emulator.c
> > > >  create mode 100644 hw/tpm/tpm_ioctl.h
> > > > 
> > > > diff --git a/configure b/configure
> > > > index cb0f7ed..ce2df2d 100755
> > > > --- a/configure
> > > > +++ b/configure
> > > > @@ -3461,10 +3461,15 @@ fi
> > > >  ##########################################
> > > >  # TPM passthrough is only on x86 Linux
> > > > 
> > > > -if test "$targetos" = Linux && test "$cpu" = i386 -o "$cpu" =
> > > > x86_64; then
> > > > -  tpm_passthrough=$tpm
> > > > +if test "$targetos" = Linux; then
> > > > +  tpm_emulator=$tpm
> > > > +  if test "$cpu" = i386 -o "$cpu" = x86_64; then
> > > > +    tpm_passthrough=$tpm
> > > > +  else
> > > > +    tpm_passthrough=no
> > > > +  fi
> > > >  else
> > > > -  tpm_passthrough=no
> > > > +  tpm_emulator=no
> > > >  fi
> > > > 
> > > >  ##########################################
> > > > @@ -5359,6 +5364,7 @@ echo "gcov enabled      $gcov"
> > > >  echo "TPM support       $tpm"
> > > >  echo "libssh2 support   $libssh2"
> > > >  echo "TPM passthrough   $tpm_passthrough"
> > > > +echo "TPM emulator      $tpm_emulator"
> > > >  echo "QOM debugging     $qom_cast_debug"
> > > >  echo "Live block migration $live_block_migration"
> > > >  echo "lzo support       $lzo"
> > > > @@ -5943,6 +5949,9 @@ if test "$tpm" = "yes"; then
> > > >    if test "$tpm_passthrough" = "yes"; then
> > > >      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
> > > >    fi
> > > > +  if test "$tpm_emulator" = "yes"; then
> > > > +    echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak
> > > It shouldn't require Linux, but posix (and I assume a port to
> > > other
> > > systems isn't impossible). same for build-sys / help / comments.
> > I agree, Can you suggest, what is the Qemu way of limiting this to
> > 'posix'.
> > > 
> > > 
> there is CONFIG_POSIX
Ok, thanks. I will check
> 
> > 
> > > 
> > > > 
> > > > 
> > > > +  fi
> > > >  fi
> > > > 
> > > >  echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
> > > > diff --git a/hmp.c b/hmp.c
> > > > index cf62b2e..7e69eca 100644
> > > > --- a/hmp.c
> > > > +++ b/hmp.c
> > > > @@ -995,6 +995,7 @@ void hmp_info_tpm(Monitor *mon, const QDict
> > > > *qdict)
> > > >      Error *err = NULL;
> > > >      unsigned int c = 0;
> > > >      TPMPassthroughOptions *tpo;
> > > > +    TPMEmulatorOptions *teo;
> > > > 
> > > >      info_list = qmp_query_tpm(&err);
> > > >      if (err) {
> > > > @@ -1024,6 +1025,10 @@ 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_EMULATOR:
> > > > +            teo = ti->options->u.emulator.data;
> > > > +            monitor_printf(mon, ",chardev=%s", teo->chardev);
> > > > +            break;
> > > >          case TPM_TYPE__MAX:
> > > >              break;
> > > >          }
> > > > diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
> > > > index 64cecc3..41f0b7a 100644
> > > > --- a/hw/tpm/Makefile.objs
> > > > +++ b/hw/tpm/Makefile.objs
> > > > @@ -1,2 +1,3 @@
> > > >  common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
> > > >  common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
> > > > tpm_util.o
> > > > +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
> > > > diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
> > > > new file mode 100644
> > > > index 0000000..c02bbe2
> > > > --- /dev/null
> > > > +++ b/hw/tpm/tpm_emulator.c
> > > > @@ -0,0 +1,649 @@
> > > > +/*
> > > > + *  emulator TPM driver
> > > > + *
> > > > + *  Copyright (c) 2017 Intel Corporation
> > > > + *  Author: Amarnath Valluri <amarnath.valluri@intel.com>
> > > > + *
> > > > + *  Copyright (c) 2010 - 2013 IBM Corporation
> > > > + *  Authors:
> > > > + *    Stefan Berger <stefanb@us.ibm.com>
> > > > + *
> > > > + *  Copyright (C) 2011 IAIK, Graz University of Technology
> > > > + *    Author: Andreas Niederl
> > > > + *
> > > > + * This library is free software; you can redistribute it
> > > > and/or
> > > > + * modify it under the terms of the GNU Lesser General Public
> > > > + * License as published by the Free Software Foundation;
> > > > either
> > > > + * version 2 of the License, or (at your option) any later
> > > > version.
> > > > + *
> > > > + * This library is distributed in the hope that it will be
> > > > useful,
> > > > + * but WITHOUT ANY WARRANTY; without even the implied warranty
> > > > of
> > > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
> > > > the
> > > > GNU
> > > > + * Lesser General Public License for more details.
> > > > + *
> > > > + * You should have received a copy of the GNU Lesser General
> > > > Public
> > > > + * License along with this library; if not, see <http://www.gn
> > > > u.or
> > > > g/licenses/>
> > > > + *
> > > > + */
> > > > +
> > > > +#include "qemu/osdep.h"
> > > > +#include "qemu/error-report.h"
> > > > +#include "qemu/sockets.h"
> > > > +#include "io/channel-socket.h"
> > > > +#include "sysemu/tpm_backend.h"
> > > > +#include "tpm_int.h"
> > > > +#include "hw/hw.h"
> > > > +#include "hw/i386/pc.h"
> > > > +#include "tpm_util.h"
> > > > +#include "tpm_ioctl.h"
> > > > +#include "migration/blocker.h"
> > > > +#include "qapi/error.h"
> > > > +#include "chardev/char-fe.h"
> > > > +
> > > > +#include <fcntl.h>
> > > > +#include <sys/types.h>
> > > > +#include <sys/stat.h>
> > > > +#include <stdio.h>
> > > > +
> > > > +#define DEBUG_TPM 0
> > > > +
> > > > +#define DPRINT(fmt, ...) do { \
> > > > +    if (DEBUG_TPM) { \
> > > > +        fprintf(stderr, fmt, ## __VA_ARGS__); \
> > > > +    } \
> > > > +} while (0);
> > > > +
> > > > +#define DPRINTF(fmt, ...) DPRINT("tpm-emulator: "fmt"\n",
> > > > __VA_ARGS__)
> > > I would define a single DPRINTF() (& drop DPRINT usage below)
> > Ok, will do
> > > 
> > > 
> > > 
> > > > 
> > > > 
> > > > +
> > > > +#define TYPE_TPM_EMULATOR "tpm-emulator"
> > > > +#define TPM_EMULATOR(obj) \
> > > > +    OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
> > > > +
> > > > +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps &
> > > > (cap)) == (cap))
> > > > +
> > > > +static const TPMDriverOps tpm_emulator_driver;
> > > > +
> > > > +/* data structures */
> > > > +typedef struct TPMEmulator {
> > > > +    TPMBackend parent;
> > > > +
> > > > +    TPMEmulatorOptions *options;
> > > Contrary to my comment on previous patch, I realize it is
> > > convenient
> > > to have a qapi pointer here, so you can use the free visitor
> > > later.
> > > 
> > > > 
> > > > 
> > > > +    CharBackend ctrl_dev;
> > > ctrl_chr perhaps? or just chr or be (the most common name in
> > > other
> > > devices).
> > > 
> > > > 
> > > > 
> > > > +    QIOChannel *data_ioc;
> > > > +    bool op_executing;
> > > > +    bool op_canceled;
> > > I think those 2 fields can be dropped, see below.
> > > 
> > > > 
> > > > 
> > > > +    bool had_startup_error;
> > > I think some little refactoring could remove the whole
> > > had_startup_error() backend & callback before or after the
> > > series.
> > > tpm_backend_startup_tpm() already returns an error code.  device
> > > or
> > > common backend code could handle & remember that.
> > Yes, i agree, we can avoid had_startup_error() from DriverOps, i
> > will
> > do this.
> > > 
> > > 
> > > > 
> > > > 
> > > > +    TPMVersion tpm_version;
> > > This is probably worth to consider in common code instead, but
> > > let's
> > > not worry about it.
> > > 
> > > > 
> > > > 
> > > > +    ptm_cap caps; /* capabilities of the TPM */
> > > > +    uint8_t cur_locty_number; /* last set locality */
> > > > +    QemuMutex state_lock;
> > > > +    Error *migration_blocker;
> > > > +} TPMEmulator;
> > > > +
> > > > +
> > > > +static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned
> > > > long
> > > > cmd, void *msg,
> > > > +                                size_t msg_len_in, size_t
> > > > msg_len_out)
> > > > +{
> > > > +    uint32_t cmd_no = cpu_to_be32(cmd);
> > > > +    ssize_t n = sizeof(uint32_t) + msg_len_in;
> > > > +    uint8_t *buf = NULL;
> > > > +
> > > > +    buf = (uint8_t *)malloc(n);
> > > could be g_alloca() instead. Alternatively, why not call 2
> > > write()
> > > instead?
> > > 
> > > none of the casts in this function are necessary, please remove
> > > them.
> > Ok, will change to aclloca()
> I just realized this is SOCK_SEQPACKET, ok
> 
> > 
> > > 
> > > 
> > > > 
> > > > 
> > > > +    memcpy(buf, &cmd_no, sizeof(cmd_no));
> > > > +    memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
> > > > +
> > > > +    n += qemu_chr_fe_write_all(dev, (const uint8_t *)buf, n);
> > > weird
> > > 
> > > > 
> > > > 
> > > > +    free(buf);
> > > > +
> > > > +    if (n > 0) {
> > > with the n += above, you'll get interesting behaviour :)
> > Ya, it was typo :)
> > > 
> > > 
> > > You probably want to check if any write above failed, and return
> > > early
> > > for the error case.
> > > 
> > > Please improve the error handling in this function
> > > 
> > > > 
> > > > 
> > > > +        if (msg_len_out > 0) {
> > > > +            n = qemu_chr_fe_read_all(dev, (uint8_t *)msg,
> > > > msg_len_out);
> > > > +            /* simulate ioctl return value */
> > > > +            if (n > 0) {
> > > > +                n = 0;
> > > > +            }
> > > > +        } else {
> > > > +            n = 0;
> > > > +        }
> > > > +    }
> > > > +    return n;
> > > > +}
> > > > +
> > > > +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_pt,
> > > tpm_pt was tpm_passthrough I suppose.
> > > 
> > > Please rename tpm_pt tpm_emu (or I would suggest "self", but this
> > > isn't common name in qemu code - it's actually common in a close
> > > c-object world., and it is quite convenient...)
> > My interpretation about _pt was 'pointer' ;-) i will consider your
> > suggetion.
> > > 
> > > 
> > > > 
> > > > 
> > > > +                                     const uint8_t *in,
> > > > uint32_t
> > > > in_len,
> > > > +                                     uint8_t *out, uint32_t
> > > > out_len,
> > > > +                                     bool *selftest_done)
> > > > +{
> > > > +    ssize_t ret;
> > > > +    bool is_selftest = false;
> > > > +    const struct tpm_resp_hdr *hdr = NULL;
> > > > +    Error *err = NULL;
> > > > +
> > > > +    tpm_pt->op_canceled = false;
> > > > +    tpm_pt->op_executing = true;
> > > > +    if (selftest_done) {
> > > > +        *selftest_done = false;
> > > > +        is_selftest = tpm_util_is_selftest(in, in_len);
> > > > +    }
> > > > +
> > > > +    ret = qio_channel_write(tpm_pt->data_ioc, (char *)in,
> > > > in_len,
> > > > &err);
> > > hmm, too bad qio_channel_write() doesn't take a void *
> > > 
> > > why not write_all()?
> > Agreed
> > > 
> > > 
> > > > 
> > > > 
> > > > +    if (ret != in_len || err) {
> > > > +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
> > > I don't think ECANCELED make sense for emulator code.
> > > 
> > > > 
> > > > 
> > > > +            error_report("tpm-emulator: error while
> > > > transmitting
> > > > data "
> > > > +                         "to TPM: %s", err ?
> > > > error_get_pretty(err)
> > > > : "");
> > > > +            error_free(err);
> > > > +        }
> > > > +        goto err_exit;
> > > > +    }
> > > > +
> > > > +    tpm_pt->op_executing = false;
> > > > +
> > > > +    ret = qio_channel_read(tpm_pt->data_ioc, (char *)out,
> > > > out_len,
> > > > &err);
> > > > +    if (ret < 0 || err) {
> > > read_all() ?
> > The issue with read_all() is it does not return the no of bytes it
> > read, so i would like to stict to _read()
> ok
> 
> > 
> > > 
> > > 
> > > > 
> > > > 
> > > > +        if (!tpm_pt->op_canceled || errno != ECANCELED) {
> > > > +            error_report("tpm-emulator: error while reading
> > > > data
> > > > from "
> > > > +                         "TPM: %s", err ?
> > > > error_get_pretty(err) :
> > > > "");
> > > > +            error_free(err);
> > > > +        }
> > > > +    } else if (ret >= sizeof(*hdr)) {
> > > > +        hdr = (struct tpm_resp_hdr *)out;
> > > > +    }
> > > > +
> > > > +    if (!hdr || be32_to_cpu(hdr->len) != ret) {
> > > > +        error_report("tpm-emulator: received invalid response
> > > > "
> > > > +                     "packet from TPM with length :%ld", ret);
> > > > +        ret = -1;
> > > > +        goto err_exit;
> > > > +    }
> > > > +
> > > > +    if (is_selftest) {
> > > > +        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
> > > > +    }
> > > > +
> > > > +    return 0;
> > > > +
> > > > +err_exit:
> > > > +    if (ret < 0) {
> > > > +        tpm_util_write_fatal_error_response(out, out_len);
> > > > +    }
> > > > +
> > > > +    tpm_pt->op_executing = false;
> > > > +
> > > > +    return ret;
> > > > +}
> > > > +
> > > > +static int tpm_emulator_set_locality(TPMEmulator *tpm_pt,
> > > > uint8_t
> > > > locty_number)
> > > > +{
> > > > +    ptm_loc loc;
> > > > +
> > > > +    DPRINTF("%s : locality: 0x%x", __func__, locty_number);
> > > > +
> > > > +    if (tpm_pt->cur_locty_number != locty_number) {
> > > return early instead if ==, to avoid code indent etc
> > Ok
> > > 
> > > 
> > > > 
> > > > 
> > > > +        DPRINTF("setting locality : 0x%x", locty_number);
> > > > +        loc.u.req.loc = locty_number;
> > > This number isn't set in be like the rest of the protocol?
> > I doubt if i get ur point :(, can you please elaborate.
> In the rest of the protocol, it uses big-endian. I suppose you should
> cpu_to_be32(locty_number).
> 
> > 
> > > 
> > > 
> > > > 
> > > > 
> > > > +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > > > CMD_SET_LOCALITY, &loc,
> > > > +                             sizeof(loc), sizeof(loc)) < 0) {
> > > > +            error_report("tpm-emulator: could not set locality
> > > > :
> > > > %s",
> > > > +                         strerror(errno));
> > > > +            return -1;
> > > > +        }
> > > > +        loc.u.resp.tpm_result =
> > > > be32_to_cpu(loc.u.resp.tpm_result);
> > > > +        if (loc.u.resp.tpm_result != 0) {
> > > > +            error_report("tpm-emulator: TPM result for set
> > > > locality : 0x%x",
> > > > +                         loc.u.resp.tpm_result);
> > > > +            return -1;
> > > > +        }
> > > > +        tpm_pt->cur_locty_number = locty_number;
> > > > +    }
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static void tpm_emulator_handle_request(TPMBackend *tb,
> > > > TPMBackendCmd cmd)
> > > > +{
> > > > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > > > +    TPMLocality *locty = NULL;
> > > > +    bool selftest_done = false;
> > > > +
> > > > +    DPRINTF("processing command type %d", cmd);
> > > > +
> > > > +    switch (cmd) {
> > > > +    case TPM_BACKEND_CMD_PROCESS_CMD:
> > > > +        qemu_mutex_lock(&tpm_pt->state_lock);
> > > > +        locty = tb->tpm_state->locty_data;
> > > > +        if (tpm_emulator_set_locality(tpm_pt,
> > > > +                                      tb->tpm_state-
> > > > >locty_number)
> > > > < 0) {
> > > > +            tpm_util_write_fatal_error_response(locty-
> > > > > 
> > > > > r_buffer.buffer,
> > > > +                                           locty-
> > > > >r_buffer.size);
> > > return / goto here instead of else.
> > We should not retrun from here, we have to propagte the error
> > response
> > to device, and unlock the state.
> > > 
> > > 
> > > > 
> > > > 
> > > > +        } else {
> > > > +            tpm_emulator_unix_tx_bufs(tpm_pt, locty-
> > > > > 
> > > > > w_buffer.buffer,
> > > > +                                              locty->w_offset,
> > > > +                                              locty-
> > > > > 
> > > > > r_buffer.buffer,
> > > > +                                              locty-
> > > > > 
> > > > > r_buffer.size,
> > > > +                                              &selftest_done);
> > > no error handling?
> > The error case is supposed to handle by device, where we are
> > filling
> > into out buffer, using tpm_util_write_fatal_error_response().
> > > 
> > > 
> > > > 
> > > > 
> > > > +        }
> > > > +
> > > > +        tb->recv_data_callback(tb->tpm_state, tb->tpm_state-
> > > > > 
> > > > > locty_number,
> > > > +                               selftest_done);
> > > > +        qemu_mutex_unlock(&tpm_pt->state_lock);
> > > > +
> > > > +        break;
> > > > +    case TPM_BACKEND_CMD_INIT:
> > > > +    case TPM_BACKEND_CMD_END:
> > > > +    case TPM_BACKEND_CMD_TPM_RESET:
> > > > +        /* nothing to do */
> > > > +        break;
> > > > +    }
> > > > +}
> > > > +
> > > > +/*
> > > > + * Gracefully shut down the external unixio TPM
> > > > + */
> > > > +static void tpm_emulator_shutdown(TPMEmulator *tpm_pt)
> > > > +{
> > > > +    ptm_res res;
> > > > +
> > > > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_SHUTDOWN,
> > > > &res, 0,
> > > > +                         sizeof(res)) < 0) {
> > > > +        error_report("tpm-emulator: Could not cleanly shutdown
> > > > the
> > > > TPM: %s",
> > > > +                     strerror(errno));
> > > > +    } else if (res != 0) {
> > > > +        error_report("tpm-emulator: TPM result for sutdown:
> > > > 0x%x",
> > > > +                     be32_to_cpu(res));
> > > > +    }
> > > > +}
> > > > +
> > > > +static int tpm_emulator_probe_caps(TPMEmulator *tpm_pt)
> > > > +{
> > > > +    DPRINTF("%s", __func__);
> > > > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > > > CMD_GET_CAPABILITY,
> > > > +                         &tpm_pt->caps, 0, sizeof(tpm_pt-
> > > > >caps)) <
> > > > 0) {
> > > > +        error_report("tpm-emulator: probing failed : %s",
> > > > strerror(errno));
> > > > +        return -1;
> > > > +    }
> > > > +
> > > > +    tpm_pt->caps = be64_to_cpu(tpm_pt->caps);
> > > > +
> > > > +    DPRINTF("capbilities : 0x%lx", tpm_pt->caps);
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int tpm_emulator_check_caps(TPMEmulator *tpm_pt)
> > > > +{
> > > > +    ptm_cap caps = 0;
> > > > +    const char *tpm = NULL;
> > > > +
> > > > +    /* 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-emulator: TPM version has not been
> > > > set");
> > > > +        return -1;
> > > > +    }
> > > > +
> > > > +    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, caps)) {
> > > > +        error_report("tpm-emulator: TPM does not implement
> > > > minimum
> > > > set of "
> > > > +                     "required capabilities for TPM %s
> > > > (0x%x)",
> > > > tpm, (int)caps);
> > > > +        return -1;
> > > > +    }
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int tpm_emulator_startup_tpm(TPMBackend *tb)
> > > > +{
> > > > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > > > +    ptm_init init;
> > > > +    ptm_res res;
> > > > +
> > > > +    DPRINTF("%s", __func__);
> > > > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev, CMD_INIT,
> > > > &init,
> > > > sizeof(init),
> > > > +                         sizeof(init)) < 0) {
> > > > +        error_report("tpm-emulator: could not send INIT: %s",
> > > > +                     strerror(errno));
> > > > +        goto err_exit;
> > > > +    }
> > > > +
> > > > +    res = be32_to_cpu(init.u.resp.tpm_result);
> > > > +    if (res) {
> > > > +        error_report("tpm-emulator: TPM result for CMD_INIT:
> > > > 0x%x", res);
> > > > +        goto err_exit;
> > > > +    }
> > > > +    return 0;
> > > > +
> > > > +err_exit:
> > > > +    tpm_pt->had_startup_error = true;
> > > > +    return -1;
> > > > +}
> > > > +
> > > > +static bool tpm_emulator_get_tpm_established_flag(TPMBackend
> > > > *tb)
> > > > +{
> > > > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > > > +    ptm_est est;
> > > > +
> > > > +    DPRINTF("%s", __func__);
> > > > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > > > CMD_GET_TPMESTABLISHED, &est, 0,
> > > > +                         sizeof(est)) < 0) {
> > > > +        error_report("tpm-emulator: Could not get the TPM
> > > > established flag: %s",
> > > > +                     strerror(errno));
> > > > +        return false;
> > > > +    }
> > > > +    DPRINTF("established flag: %0x", est.u.resp.bit);
> > > > +
> > > > +    return (est.u.resp.bit != 0);
> > > > +}
> > > > +
> > > > +static int tpm_emulator_reset_tpm_established_flag(TPMBackend
> > > > *tb,
> > > > +                                                   uint8_t
> > > > locty)
> > > > +{
> > > > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > > > +    ptm_reset_est reset_est;
> > > > +    ptm_res res;
> > > > +
> > > > +    /* only a TPM 2.0 will support this */
> > > > +    if (tpm_pt->tpm_version == TPM_VERSION_2_0) {
> > > > +        reset_est.u.req.loc = tpm_pt->cur_locty_number;
> > > > +
> > > > +        if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > > > CMD_RESET_TPMESTABLISHED,
> > > > +                                 &reset_est,
> > > > sizeof(reset_est),
> > > > +                                 sizeof(reset_est)) < 0) {
> > > > +            error_report("tpm-emulator: Could not reset the
> > > > establishment bit: "
> > > > +                          "%s", strerror(errno));
> > > > +            return -1;
> > > > +        }
> > > > +
> > > > +        res = be32_to_cpu(reset_est.u.resp.tpm_result);
> > > > +        if (res) {
> > > > +            error_report("tpm-emulator: TPM result for rest
> > > > establixhed flag: "
> > > > +                         "0x%x", res);
> > > > +            return -1;
> > > > +        }
> > > > +    }
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static bool tpm_emulator_had_startup_error(TPMBackend *tb)
> > > > +{
> > > > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > > > +
> > > > +    return tpm_pt->had_startup_error;
> > > > +}
> > > > +
> > > > +static void tpm_emulator_cancel_cmd(TPMBackend *tb)
> > > > +{
> > > > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > > > +    ptm_res res;
> > > > +
> > > > +    /*
> > > > +     * As of Linux 3.7 the tpm_tis driver does not properly
> > > > cancel
> > > > +     * commands on all TPM manufacturers' TPMs.
> > > > +     * Only cancel if we're busy so we don't cancel someone
> > > > else's
> > > > +     * command, e.g., a command executed on the host.
> > > > +     */
> > > > +    if (tpm_pt->op_executing) {
> > > The field is set in the worker thread. This is racy. Fortunately
> > > this
> > > is not relevant for emulator, I think you can simply drop that
> > > check
> > > and the variable. Stefan should confirm though.
> > > 
> > > > 
> > > > 
> > > > +        if (TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_pt,
> > > > PTM_CAP_CANCEL_TPM_CMD)) {
> > > > +            if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > > > CMD_CANCEL_TPM_CMD,
> > > > +                                     &res, 0, sizeof(res)) <
> > > > 0) {
> > > > +                error_report("tpm-emulator: Could not cancel
> > > > command: %s",
> > > > +                             strerror(errno));
> > > > +            } else if (res != 0) {
> > > > +                error_report("tpm-emulator: Failed to cancel
> > > > TPM:
> > > > 0x%x",
> > > > +                             be32_to_cpu(res));
> > > > +            } else {
> > > > +                tpm_pt->op_canceled = true;
> > > > +            }
> > > > +        }
> > > > +    }
> > > > +}
> > > > +
> > > > +static void tpm_emulator_reset(TPMBackend *tb)
> > > > +{
> > > > +    DPRINTF("%s", __func__);
> > > > +
> > > > +    tpm_emulator_cancel_cmd(tb);
> > > > +}
> > > > +
> > > > +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
> > > > +{
> > > > +    TPMEmulator *tpm_pt = TPM_EMULATOR(tb);
> > > > +
> > > > +    return tpm_pt->tpm_version;
> > > > +}
> > > > +
> > > > +static void tpm_emulator_block_migration(TPMEmulator *tpm_pt)
> > > > +{
> > > > +    Error *err = NULL;
> > > > +
> > > > +    error_setg(&tpm_pt->migration_blocker,
> > > > +               "Migration disabled: TPM emulator not yet
> > > > migratable");
> > > > +    migrate_add_blocker(tpm_pt->migration_blocker, &err);
> > > > +    if (err) {
> > > > +        error_free(err);
> > > probably better to report_err it instead
> > Ok
> > > 
> > > 
> > > > 
> > > > 
> > > > +        error_free(tpm_pt->migration_blocker);
> > > > +        tpm_pt->migration_blocker = NULL;
> > > and return an error code.
> > Will do
> > > 
> > > 
> > > > 
> > > > 
> > > > +    }
> > > > +}
> > > > +
> > > > +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_pt)
> > > > +{
> > > > +    ptm_res res;
> > > > +    Error *err = NULL;
> > > > +    int fds[2] = { -1, -1 };
> > > > +
> > > > +    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) {
> > > > +        error_report("tpm-emulator: Failed to create
> > > > socketpair");
> > > > +        return -1;
> > > > +    }
> > > > +
> > > > +    qemu_chr_fe_set_msgfds(&tpm_pt->ctrl_dev, fds + 1, 1);
> > > > +
> > > > +    if (tpm_emulator_ctrlcmd(&tpm_pt->ctrl_dev,
> > > > CMD_SET_DATAFD,
> > > > &res, 0,
> > > > +                    sizeof(res)) || res != 0) {
> > > Yay! for making life easier and hiding a protocol detail.
> > > 
> > > > 
> > > > 
> > > > +        error_report("tpm-emulator: Failed to send
> > > > CMD_SET_DATAFD:
> > > > %s",
> > > > +                     strerror(errno));
> > > > +        goto err_exit;
> > > > +    }
> > > > +
> > > > +    tpm_pt->data_ioc =
> > > > QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
> > > > +    if (err) {
> > > > +        error_report("tpm-emulator: Failed to create io
> > > > channel :
> > > > %s",
> > > > +                       error_get_pretty(err));
> > > error_prepend()?
> > Ok
> > > 
> > > 
> > > > 
> > > > 
> > > > +        error_free(err);
> > > > +        goto err_exit;
> > > > +    }
> > > close fds[1] ?
> > I guess we are not supposed to close the other end of the
> > socketpair/pipe, as it is not forked process.
> You will close this end, not the other end.
fds[1] is handed over to other end, fds[0] is used by qemu, so we
cannot close unless error case i guess.

- Amarnath

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator
  2017-09-26 13:28         ` Valluri, Amarnath
@ 2017-09-26 13:55           ` Marc-André Lureau
  0 siblings, 0 replies; 23+ messages in thread
From: Marc-André Lureau @ 2017-09-26 13:55 UTC (permalink / raw)
  To: Valluri, Amarnath
  Cc: stefanb@linux.vnet.ibm.com, dgilbert@redhat.com,
	qemu-devel@nongnu.org, armbru@redhat.com, eblake@redhat.com

Hi

On Tue, Sep 26, 2017 at 3:28 PM, Valluri, Amarnath
<amarnath.valluri@intel.com> wrote:
> On Tue, 2017-09-26 at 14:24 +0200, Marc-André Lureau wrote:
>> > > close fds[1] ?
>> > I guess we are not supposed to close the other end of the
>> > socketpair/pipe, as it is not forked process.
>> You will close this end, not the other end.
> fds[1] is handed over to other end, fds[0] is used by qemu, so we
> cannot close unless error case i guess.

You should be able to close fds[1] in qemu, and keep the communication
working between the two processes. If you don't close, it will leak a
fd in qemu.

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator
  2017-09-26 12:05     ` Valluri, Amarnath
  2017-09-26 12:24       ` Marc-André Lureau
@ 2017-09-26 13:59       ` Eric Blake
  1 sibling, 0 replies; 23+ messages in thread
From: Eric Blake @ 2017-09-26 13:59 UTC (permalink / raw)
  To: Valluri, Amarnath, marcandre.lureau@gmail.com
  Cc: stefanb@linux.vnet.ibm.com, dgilbert@redhat.com,
	qemu-devel@nongnu.org, armbru@redhat.com

[-- Attachment #1: Type: text/plain, Size: 1106 bytes --]

On 09/26/2017 07:05 AM, Valluri, Amarnath wrote:

>>> +
>>> +#define DPRINT(fmt, ...) do { \
>>> +    if (DEBUG_TPM) { \
>>> +        fprintf(stderr, fmt, ## __VA_ARGS__); \
>>> +    } \
>>> +} while (0);

Do not include the trailing ';' directly in the macro definition. The
whole point of wrapping things in a do{}while(0) is so that you can do:

if (foo)
    DPRINT(...);
else
    ...

(of course, that violates our coding style, as we require {} everywhere,
but it makes your macro appropriate for copying and pasting to other
projects that have different styles).


>>> +    ret = qio_channel_read(tpm_pt->data_ioc, (char *)out, out_len,
>>> &err);
>>> +    if (ret < 0 || err) {
>> read_all() ?
> The issue with read_all() is it does not return the no of bytes it
> read, so i would like to stict to _read()

But you KNOW the number of bytes read if read_all() succeeded - it read
as many bytes as you requested!

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 619 bytes --]

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v8 5/8] tmp backend: Add new api to read backend TpmInfo
  2017-09-22 15:35   ` Marc-André Lureau
  2017-09-25 12:01     ` Valluri, Amarnath
@ 2017-09-27 12:16     ` Amarnath Valluri
  1 sibling, 0 replies; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-27 12:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri

TPM configuration options are backend implementation details and shall not be
part of base TPMBackend object, and these shall not be accessed directly outside
of the class, hence added a new interface method, get_tpm_options() to
TPMDriverOps., which shall be implemented by the derived classes to return
configured tpm options.

A new tpm backend api - tpm_backend_query_tpm() which uses _get_tpm_options() to
prepare TpmInfo.

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 backends/tpm.c               | 15 +++++++++++--
 hw/tpm/tpm_passthrough.c     | 51 +++++++++++++++++++++++++++-----------------
 include/sysemu/tpm_backend.h | 15 +++++++++++--
 tpm.c                        | 32 +--------------------------
 4 files changed, 59 insertions(+), 54 deletions(-)

diff --git a/backends/tpm.c b/backends/tpm.c
index 8911597..de313c9 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -142,6 +142,19 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
     return k->ops->get_tpm_version(s);
 }
 
+TPMInfo *tpm_backend_query_tpm(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;
+    info->options = k->ops->get_tpm_options ?
+                    k->ops->get_tpm_options(s) : NULL;
+
+    return info;
+}
+
 static bool tpm_backend_prop_get_opened(Object *obj, Error **errp)
 {
     TPMBackend *s = TPM_BACKEND(obj);
@@ -196,8 +209,6 @@ static void tpm_backend_instance_finalize(Object *obj)
     TPMBackend *s = TPM_BACKEND(obj);
 
     g_free(s->id);
-    g_free(s->path);
-    g_free(s->cancel_path);
     tpm_backend_thread_end(s);
 }
 
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index 4c21e52..84fc49a 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -30,6 +30,7 @@
 #include "tpm_int.h"
 #include "hw/hw.h"
 #include "hw/i386/pc.h"
+#include "qapi/clone-visitor.h"
 #include "tpm_tis.h"
 #include "tpm_util.h"
 
@@ -49,7 +50,8 @@
 struct TPMPassthruState {
     TPMBackend parent;
 
-    char *tpm_dev;
+    TPMPassthroughOptions *options;
+    const char *tpm_dev;
     int tpm_fd;
     bool tpm_executing;
     bool tpm_op_canceled;
@@ -296,15 +298,14 @@ static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
  * in Documentation/ABI/stable/sysfs-class-tpm.
  * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
  */
-static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
+static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
 {
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
     int fd = -1;
     char *dev;
     char path[PATH_MAX];
 
-    if (tb->cancel_path) {
-        fd = qemu_open(tb->cancel_path, O_WRONLY);
+    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",
                          strerror(errno));
@@ -319,7 +320,7 @@ static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
                      dev) < sizeof(path)) {
             fd = qemu_open(path, O_WRONLY);
             if (fd >= 0) {
-                tb->cancel_path = g_strdup(path);
+                tpm_pt->options->cancel_path = g_strdup(path);
             } else {
                 error_report("tpm_passthrough: Could not open TPM cancel "
                              "path %s : %s", path, strerror(errno));
@@ -339,17 +340,18 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
     const char *value;
 
     value = qemu_opt_get(opts, "cancel-path");
-    tb->cancel_path = g_strdup(value);
+    if (value) {
+        tpm_pt->options->cancel_path = g_strdup(value);
+        tpm_pt->options->has_cancel_path = true;
+    }
 
     value = qemu_opt_get(opts, "path");
-    if (!value) {
-        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+    if (value) {
+        tpm_pt->options->has_path = true;
+        tpm_pt->options->path = g_strdup(value);
     }
 
-    tpm_pt->tpm_dev = g_strdup(value);
-
-    tb->path = g_strdup(tpm_pt->tpm_dev);
-
+    tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE;
     tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
     if (tpm_pt->tpm_fd < 0) {
         error_report("Cannot access TPM device using '%s': %s",
@@ -370,10 +372,8 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
     tpm_pt->tpm_fd = -1;
 
  err_free_parameters:
-    g_free(tb->path);
-    tb->path = NULL;
-
-    g_free(tpm_pt->tpm_dev);
+    qapi_free_TPMPassthroughOptions(tpm_pt->options);
+    tpm_pt->options = NULL;
     tpm_pt->tpm_dev = NULL;
 
     return 1;
@@ -391,7 +391,7 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
         goto err_exit;
     }
 
-    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
+    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
     if (tpm_pt->cancel_fd < 0) {
         goto err_exit;
     }
@@ -404,6 +404,17 @@ err_exit:
     return NULL;
 }
 
+static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb)
+{
+    TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
+
+    options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
+    options->u.passthrough.data = QAPI_CLONE(TPMPassthroughOptions,
+                                             TPM_PASSTHROUGH(tb)->options);
+
+    return options;
+}
+
 static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
     TPM_STANDARD_CMDLINE_OPTS,
     {
@@ -430,12 +441,14 @@ static const TPMDriverOps tpm_passthrough_driver = {
     .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,
+    .get_tpm_options          = tpm_passthrough_get_tpm_options,
 };
 
 static void tpm_passthrough_inst_init(Object *obj)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
 
+    tpm_pt->options = g_new0(TPMPassthroughOptions, 1);
     tpm_pt->tpm_fd = -1;
     tpm_pt->cancel_fd = -1;
 }
@@ -448,7 +461,7 @@ static void tpm_passthrough_inst_finalize(Object *obj)
 
     qemu_close(tpm_pt->tpm_fd);
     qemu_close(tpm_pt->cancel_fd);
-    g_free(tpm_pt->tpm_dev);
+    qapi_free_TPMPassthroughOptions(tpm_pt->options);
 }
 
 static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 9ea7072..e96c191 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -49,10 +49,9 @@ struct TPMBackend {
     TPMRecvDataCB *recv_data_callback;
     bool had_startup_error;
 
+    /* <public> */
     char *id;
     enum TpmModel fe_model;
-    char *path;
-    char *cancel_path;
 
     QLIST_ENTRY(TPMBackend) list;
 };
@@ -96,6 +95,8 @@ struct TPMDriverOps {
     int (*reset_tpm_established_flag)(TPMBackend *t, uint8_t locty);
 
     TPMVersion (*get_tpm_version)(TPMBackend *t);
+
+    TpmTypeOptions *(*get_tpm_options)(TPMBackend *t);
 };
 
 
@@ -214,6 +215,16 @@ void tpm_backend_open(TPMBackend *s, Error **errp);
  */
 TPMVersion tpm_backend_get_tpm_version(TPMBackend *s);
 
+/**
+ * tpm_backend_query_tpm:
+ * @s: the backend
+ *
+ * Query backend tpm info
+ *
+ * Returns newly allocated TPMInfo
+ */
+TPMInfo *tpm_backend_query_tpm(TPMBackend *s);
+
 TPMBackend *qemu_find_tpm(const char *id);
 
 const TPMDriverOps *tpm_get_backend_driver(const char *type);
diff --git a/tpm.c b/tpm.c
index db14849..3b8c7ed 100644
--- a/tpm.c
+++ b/tpm.c
@@ -202,36 +202,6 @@ static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type)
     return be_drivers[type];
 }
 
-static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
-{
-    TPMInfo *res = g_new0(TPMInfo, 1);
-    TPMPassthroughOptions *tpo;
-
-    res->id = g_strdup(drv->id);
-    res->model = drv->fe_model;
-    res->options = g_new0(TpmTypeOptions, 1);
-
-    switch (tpm_backend_get_type(drv)) {
-    case TPM_TYPE_PASSTHROUGH:
-        res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
-        tpo = g_new0(TPMPassthroughOptions, 1);
-        res->options->u.passthrough.data = tpo;
-        if (drv->path) {
-            tpo->path = g_strdup(drv->path);
-            tpo->has_path = true;
-        }
-        if (drv->cancel_path) {
-            tpo->cancel_path = g_strdup(drv->cancel_path);
-            tpo->has_cancel_path = true;
-        }
-        break;
-    case TPM_TYPE__MAX:
-        break;
-    }
-
-    return res;
-}
-
 /*
  * Walk the list of active TPM backends and collect information about them
  * following the schema description in qapi-schema.json.
@@ -246,7 +216,7 @@ TPMInfoList *qmp_query_tpm(Error **errp)
             continue;
         }
         info = g_new0(TPMInfoList, 1);
-        info->value = qmp_query_tpm_inst(drv);
+        info->value = tpm_backend_query_tpm(drv);
 
         if (!cur_item) {
             head = cur_item = info;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v8 8/8] tpm: Added support for TPM emulator
  2017-09-24 18:52   ` Marc-André Lureau
  2017-09-25  0:35     ` Stefan Berger
  2017-09-26 12:05     ` Valluri, Amarnath
@ 2017-09-27 12:18     ` Amarnath Valluri
  2017-09-27 14:18       ` Marc-André Lureau
  2 siblings, 1 reply; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-27 12:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri,
	Dr. David Alan Gilbert, Eric Blake, Markus Armbruster,
	Paolo Bonzini

This change introduces a new TPM backend driver that can communicate with
swtpm(software TPM emulator) using unix domain socket interface. QEMU talks to
TPM emulator using QEMU's socket-based chardev backend device.

Swtpm uses two Unix sockets for communications, one for plain TPM commands and
responses, and one for out-of-band control messages. QEMU passes data socket to
be used over the control channel.

The swtpm and associated tools can be found here:
    https://github.com/stefanberger/swtpm

The swtpm's control channel protocol specification can be found here:
    https://github.com/stefanberger/swtpm/wiki/Control-Channel-Specification

Usage:
    # setup TPM state directory
    mkdir /tmp/mytpm
    chown -R tss:root /tmp/mytpm
    /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek

    # Ask qemu to use TPM emulator with given tpm state directory
    qemu-system-x86_64 \
        [...] \
        -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \
        -tpmdev emulator,id=tpm0,chardev=chrtpm \
        -device tpm-tis,tpmdev=tpm0 \
        [...]

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
---
 configure             |  13 +-
 hmp.c                 |   5 +
 hw/tpm/Makefile.objs  |   1 +
 hw/tpm/tpm_emulator.c | 613 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/tpm/tpm_ioctl.h    | 246 ++++++++++++++++++++
 qapi/tpm.json         |  21 +-
 qemu-options.hx       |  22 +-
 vl.c                  |   1 +
 8 files changed, 915 insertions(+), 7 deletions(-)
 create mode 100644 hw/tpm/tpm_emulator.c
 create mode 100644 hw/tpm/tpm_ioctl.h

diff --git a/configure b/configure
index cb0f7ed..a1b956e 100755
--- a/configure
+++ b/configure
@@ -3467,6 +3467,12 @@ else
   tpm_passthrough=no
 fi
 
+# TPM emulator is for all posix systems
+if test "$mingw32" != "yes"; then
+  tpm_emulator=$tpm
+else
+  tpm_emulator=no
+fi
 ##########################################
 # attr probe
 
@@ -5359,6 +5365,7 @@ echo "gcov enabled      $gcov"
 echo "TPM support       $tpm"
 echo "libssh2 support   $libssh2"
 echo "TPM passthrough   $tpm_passthrough"
+echo "TPM emulator      $tpm_emulator"
 echo "QOM debugging     $qom_cast_debug"
 echo "Live block migration $live_block_migration"
 echo "lzo support       $lzo"
@@ -5937,12 +5944,16 @@ if test "$live_block_migration" = "yes" ; then
   echo "CONFIG_LIVE_BLOCK_MIGRATION=y" >> $config_host_mak
 fi
 
-# TPM passthrough support?
 if test "$tpm" = "yes"; then
   echo 'CONFIG_TPM=$(CONFIG_SOFTMMU)' >> $config_host_mak
+  # TPM passthrough support?
   if test "$tpm_passthrough" = "yes"; then
     echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
   fi
+  # TPM emulator support?
+  if test "$tpm_emulator" = "yes"; then
+    echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak
+  fi
 fi
 
 echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
diff --git a/hmp.c b/hmp.c
index 0fb2bc7..9cd8179 100644
--- a/hmp.c
+++ b/hmp.c
@@ -994,6 +994,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
     Error *err = NULL;
     unsigned int c = 0;
     TPMPassthroughOptions *tpo;
+    TPMEmulatorOptions *teo;
 
     info_list = qmp_query_tpm(&err);
     if (err) {
@@ -1023,6 +1024,10 @@ 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_EMULATOR:
+            teo = ti->options->u.emulator.data;
+            monitor_printf(mon, ",chardev=%s", teo->chardev);
+            break;
         case TPM_TYPE_OPTIONS_KIND__MAX:
             break;
         }
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
index 64cecc3..41f0b7a 100644
--- a/hw/tpm/Makefile.objs
+++ b/hw/tpm/Makefile.objs
@@ -1,2 +1,3 @@
 common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
 common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
+common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
new file mode 100644
index 0000000..f7ad0a7
--- /dev/null
+++ b/hw/tpm/tpm_emulator.c
@@ -0,0 +1,613 @@
+/*
+ *  Emulator TPM driver
+ *
+ *  Copyright (c) 2017 Intel Corporation
+ *  Author: Amarnath Valluri <amarnath.valluri@intel.com>
+ *
+ *  Copyright (c) 2010 - 2013 IBM Corporation
+ *  Authors:
+ *    Stefan Berger <stefanb@us.ibm.com>
+ *
+ *  Copyright (C) 2011 IAIK, Graz University of Technology
+ *    Author: Andreas Niederl
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/sockets.h"
+#include "io/channel-socket.h"
+#include "sysemu/tpm_backend.h"
+#include "tpm_int.h"
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "tpm_util.h"
+#include "tpm_ioctl.h"
+#include "migration/blocker.h"
+#include "qapi/error.h"
+#include "qapi/clone-visitor.h"
+#include "chardev/char-fe.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+#define DEBUG_TPM 0
+
+#define DPRINTF(fmt, ...) do { \
+    if (DEBUG_TPM) { \
+        fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \
+    } \
+} while (0)
+
+#define TYPE_TPM_EMULATOR "tpm-emulator"
+#define TPM_EMULATOR(obj) \
+    OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
+
+#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))
+
+static const TPMDriverOps tpm_emulator_driver;
+
+/* data structures */
+typedef struct TPMEmulator {
+    TPMBackend parent;
+
+    TPMEmulatorOptions *options;
+    CharBackend ctrl_chr;
+    QIOChannel *data_ioc;
+    TPMVersion tpm_version;
+    ptm_cap caps; /* capabilities of the TPM */
+    uint8_t cur_locty_number; /* last set locality */
+    QemuMutex state_lock;
+    Error *migration_blocker;
+} TPMEmulator;
+
+
+static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg,
+                                size_t msg_len_in, size_t msg_len_out)
+{
+    uint32_t cmd_no = cpu_to_be32(cmd);
+    ssize_t n = sizeof(uint32_t) + msg_len_in;
+    uint8_t *buf = NULL;
+
+    buf = g_alloca(n);
+    memcpy(buf, &cmd_no, sizeof(cmd_no));
+    memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
+
+    n = qemu_chr_fe_write_all(dev, buf, n);
+    if (n <= 0) {
+        return -1;
+    }
+
+    /* return if no reply expected */
+    if (msg_len_out <= 0) {
+        return 0;
+    }
+
+    n = qemu_chr_fe_read_all(dev, msg, msg_len_out);
+    if (n < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
+                                     const uint8_t *in, uint32_t in_len,
+                                     uint8_t *out, uint32_t out_len,
+                                     bool *selftest_done)
+{
+    ssize_t ret;
+    bool is_selftest = false;
+    const struct tpm_resp_hdr *hdr = NULL;
+    Error *err = NULL;
+
+    if (selftest_done) {
+        *selftest_done = false;
+        is_selftest = tpm_util_is_selftest(in, in_len);
+    }
+
+    ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, &err);
+    if (ret != 0) {
+        if (err) {
+            error_prepend(&err, "tpm-emulator: error while transmitting data:");
+        } else {
+            error_report("tpm-emulator: Unknown error while transmitting data");
+        }
+
+        goto err_exit;
+    }
+
+    ret = qio_channel_read(tpm_emu->data_ioc, (char *)out, out_len, &err);
+    if (ret < 0) {
+        if (err) {
+            error_prepend(&err, "tpm-emulator: error while reading data:");
+        } else {
+            error_report("tpm-emulator: Unknown error while reading data");
+        }
+    } else if (ret >= sizeof(*hdr)) {
+        hdr = (struct tpm_resp_hdr *)out;
+    }
+
+    if (!hdr || be32_to_cpu(hdr->len) != ret) {
+        error_report("tpm-emulator: received invalid response "
+                     "packet from TPM with length :%ld", ret);
+        ret = -1;
+        goto err_exit;
+    }
+
+    if (is_selftest) {
+        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
+    }
+
+    return 0;
+
+err_exit:
+    if (ret < 0) {
+        tpm_util_write_fatal_error_response(out, out_len);
+    }
+
+    return ret;
+}
+
+static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number)
+{
+    ptm_loc loc;
+
+    DPRINTF("%s : locality: 0x%x", __func__, locty_number);
+
+    if (tpm_emu->cur_locty_number == locty_number) {
+        return 0;
+    }
+
+    DPRINTF("setting locality : 0x%x", locty_number);
+    loc.u.req.loc = locty_number;
+    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_LOCALITY, &loc,
+                             sizeof(loc), sizeof(loc)) < 0) {
+        error_report("tpm-emulator: could not set locality : %s",
+                     strerror(errno));
+        return -1;
+    }
+
+    loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result);
+    if (loc.u.resp.tpm_result != 0) {
+        error_report("tpm-emulator: TPM result for set locality : 0x%x",
+                     loc.u.resp.tpm_result);
+        return -1;
+    }
+
+    tpm_emu->cur_locty_number = locty_number;
+
+    return 0;
+}
+
+static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+    TPMLocality *locty = NULL;
+    bool selftest_done = false;
+
+    DPRINTF("processing command type %d", cmd);
+
+    switch (cmd) {
+    case TPM_BACKEND_CMD_PROCESS_CMD:
+        qemu_mutex_lock(&tpm_emu->state_lock);
+        locty = tb->tpm_state->locty_data;
+        if (tpm_emulator_set_locality(tpm_emu,
+                                      tb->tpm_state->locty_number) < 0) {
+            tpm_util_write_fatal_error_response(locty->r_buffer.buffer,
+                                                locty->r_buffer.size);
+        } else {
+            tpm_emulator_unix_tx_bufs(tpm_emu, locty->w_buffer.buffer,
+                                              locty->w_offset,
+                                              locty->r_buffer.buffer,
+                                              locty->r_buffer.size,
+                                              &selftest_done);
+        }
+
+        tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number,
+                               selftest_done);
+        qemu_mutex_unlock(&tpm_emu->state_lock);
+
+        break;
+    case TPM_BACKEND_CMD_INIT:
+    case TPM_BACKEND_CMD_END:
+    case TPM_BACKEND_CMD_TPM_RESET:
+        /* nothing to do */
+        break;
+    }
+}
+
+static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
+{
+    DPRINTF("%s", __func__);
+    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_CAPABILITY,
+                         &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) {
+        error_report("tpm-emulator: probing failed : %s", strerror(errno));
+        return -1;
+    }
+
+    tpm_emu->caps = be64_to_cpu(tpm_emu->caps);
+
+    DPRINTF("capbilities : 0x%lx", tpm_emu->caps);
+
+    return 0;
+}
+
+static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
+{
+    ptm_cap caps = 0;
+    const char *tpm = NULL;
+
+    /* check for min. required capabilities */
+    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;
+        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;
+        tpm = "2";
+        break;
+    case TPM_VERSION_UNSPEC:
+        error_report("tpm-emulator: TPM version has not been set");
+        return -1;
+    }
+
+    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) {
+        error_report("tpm-emulator: TPM does not implement minimum set of "
+                     "required capabilities for TPM %s (0x%x)", tpm, (int)caps);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int tpm_emulator_startup_tpm(TPMBackend *tb)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+    ptm_init init;
+    ptm_res res;
+
+    DPRINTF("%s", __func__);
+    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_INIT, &init, sizeof(init),
+                         sizeof(init)) < 0) {
+        error_report("tpm-emulator: could not send INIT: %s",
+                     strerror(errno));
+        goto err_exit;
+    }
+
+    res = be32_to_cpu(init.u.resp.tpm_result);
+    if (res) {
+        error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res);
+        goto err_exit;
+    }
+    return 0;
+
+err_exit:
+    return -1;
+}
+
+static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+    ptm_est est;
+
+    DPRINTF("%s", __func__);
+    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_TPMESTABLISHED, &est,
+                             0, sizeof(est)) < 0) {
+        error_report("tpm-emulator: Could not get the TPM established flag: %s",
+                     strerror(errno));
+        return false;
+    }
+    DPRINTF("established flag: %0x", est.u.resp.bit);
+
+    return (est.u.resp.bit != 0);
+}
+
+static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
+                                                   uint8_t locty)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+    ptm_reset_est reset_est;
+    ptm_res res;
+
+    /* only a TPM 2.0 will support this */
+    if (tpm_emu->tpm_version != TPM_VERSION_2_0) {
+        return 0;
+    }
+
+    reset_est.u.req.loc = tpm_emu->cur_locty_number;
+    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_RESET_TPMESTABLISHED,
+                             &reset_est, sizeof(reset_est),
+                             sizeof(reset_est)) < 0) {
+        error_report("tpm-emulator: Could not reset the establishment bit: %s",
+                     strerror(errno));
+        return -1;
+    }
+
+    res = be32_to_cpu(reset_est.u.resp.tpm_result);
+    if (res) {
+        error_report("tpm-emulator: TPM result for rest establixhed flag: 0x%x",
+                     res);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void tpm_emulator_cancel_cmd(TPMBackend *tb)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+    ptm_res res;
+
+    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) {
+        DPRINTF("Backend does not support CANCEL_TPM_CMD");
+        return;
+    }
+
+    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_CANCEL_TPM_CMD, &res, 0,
+                             sizeof(res)) < 0) {
+        error_report("tpm-emulator: Could not cancel command: %s",
+                     strerror(errno));
+    } else if (res != 0) {
+        error_report("tpm-emulator: Failed to cancel TPM: 0x%x",
+                     be32_to_cpu(res));
+    }
+}
+
+static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+
+    return tpm_emu->tpm_version;
+}
+
+static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)
+{
+    Error *err = NULL;
+
+    error_setg(&tpm_emu->migration_blocker,
+               "Migration disabled: TPM emulator not yet migratable");
+    migrate_add_blocker(tpm_emu->migration_blocker, &err);
+    if (err) {
+        error_report_err(err);
+        error_free(tpm_emu->migration_blocker);
+        tpm_emu->migration_blocker = NULL;
+
+        return -1;
+    }
+
+    return 0;
+}
+
+static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu)
+{
+    ptm_res res;
+    Error *err = NULL;
+    int fds[2] = { -1, -1 };
+
+    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) {
+        error_report("tpm-emulator: Failed to create socketpair");
+        return -1;
+    }
+
+    qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1);
+
+    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_DATAFD, &res, 0,
+                    sizeof(res)) || res != 0) {
+        error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s",
+                     strerror(errno));
+        goto err_exit;
+    }
+
+    tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
+    if (err) {
+        error_prepend(&err, "tpm-emulator: Failed to create io channel: ");
+        goto err_exit;
+    }
+
+    closesocket(fds[1]);
+
+    return 0;
+
+err_exit:
+    closesocket(fds[0]);
+    closesocket(fds[1]);
+    return -1;
+}
+
+static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)
+{
+    const char *value;
+
+    value = qemu_opt_get(opts, "chardev");
+    if (value) {
+        Error *err = NULL;
+        Chardev *dev = qemu_chr_find(value);
+
+        if (!dev) {
+            error_report("tpm-emulator: tpm chardev '%s' not found.", value);
+            goto err;
+        }
+
+        if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) {
+            error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':",
+                          value);
+            goto err;
+        }
+
+        tpm_emu->options->chardev = g_strdup(value);
+    }
+
+    if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) {
+        goto err;
+    }
+
+    /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used
+     * by passthrough driver, which not yet using GIOChannel.
+     */
+    if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd,
+                             &tpm_emu->tpm_version)) {
+        error_report("'%s' is not emulating TPM device. Error: %s",
+                      tpm_emu->options->chardev, strerror(errno));
+        goto err;
+    }
+
+    DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" :
+            (tpm_emu->tpm_version == TPM_VERSION_2_0 ?  "2.0" : "Unspecified"));
+
+    if (tpm_emulator_probe_caps(tpm_emu) ||
+        tpm_emulator_check_caps(tpm_emu)) {
+        goto err;
+    }
+
+    return tpm_emulator_block_migration(tpm_emu);
+
+err:
+    DPRINTF("Startup error");
+    return -1;
+}
+
+static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)
+{
+    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;
+    }
+
+    return tb;
+
+err_exit:
+    object_unref(OBJECT(tb));
+
+    return NULL;
+}
+
+static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+    TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
+
+    options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR;
+    options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options);
+
+    return options;
+}
+
+static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
+    TPM_STANDARD_CMDLINE_OPTS,
+    {
+        .name = "chardev",
+        .type = QEMU_OPT_STRING,
+        .help = "Character device to use for out-of-band control messages",
+    },
+    { /* end of list */ },
+};
+
+static const TPMDriverOps tpm_emulator_driver = {
+    .type                     = TPM_TYPE_EMULATOR,
+    .opts                     = tpm_emulator_cmdline_opts,
+    .desc                     = "TPM emulator backend driver",
+
+    .create                   = tpm_emulator_create,
+    .startup_tpm              = tpm_emulator_startup_tpm,
+    .cancel_cmd               = tpm_emulator_cancel_cmd,
+    .get_tpm_established_flag = tpm_emulator_get_tpm_established_flag,
+    .reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag,
+    .get_tpm_version          = tpm_emulator_get_tpm_version,
+    .get_tpm_options          = tpm_emulator_get_tpm_options,
+};
+
+static void tpm_emulator_inst_init(Object *obj)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
+
+    DPRINTF("%s", __func__);
+    tpm_emu->options = g_new0(TPMEmulatorOptions, 1);
+    tpm_emu->cur_locty_number = ~0;
+    qemu_mutex_init(&tpm_emu->state_lock);
+}
+
+/*
+ * Gracefully shut down the external TPM
+ */
+static void tpm_emulator_shutdown(TPMEmulator *tpm_emu)
+{
+    ptm_res res;
+
+    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SHUTDOWN, &res, 0,
+                             sizeof(res)) < 0) {
+        error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s",
+                     strerror(errno));
+    } else if (res != 0) {
+        error_report("tpm-emulator: TPM result for sutdown: 0x%x",
+                     be32_to_cpu(res));
+    }
+}
+
+static void tpm_emulator_inst_finalize(Object *obj)
+{
+    TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
+
+    tpm_emulator_shutdown(tpm_emu);
+
+    if (tpm_emu->data_ioc) {
+        qio_channel_close(tpm_emu->data_ioc, NULL);
+    }
+
+    qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false);
+
+    if (tpm_emu->options) {
+        qapi_free_TPMEmulatorOptions(tpm_emu->options);
+    }
+
+    if (tpm_emu->migration_blocker) {
+        migrate_del_blocker(tpm_emu->migration_blocker);
+        error_free(tpm_emu->migration_blocker);
+    }
+}
+
+static void tpm_emulator_class_init(ObjectClass *klass, void *data)
+{
+    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
+    tbc->ops = &tpm_emulator_driver;
+    tbc->handle_request = tpm_emulator_handle_request;
+}
+
+static const TypeInfo tpm_emulator_info = {
+    .name = TYPE_TPM_EMULATOR,
+    .parent = TYPE_TPM_BACKEND,
+    .instance_size = sizeof(TPMEmulator),
+    .class_init = tpm_emulator_class_init,
+    .instance_init = tpm_emulator_inst_init,
+    .instance_finalize = tpm_emulator_inst_finalize,
+};
+
+static void tpm_emulator_register(void)
+{
+    type_register_static(&tpm_emulator_info);
+    tpm_register_driver(&tpm_emulator_driver);
+}
+
+type_init(tpm_emulator_register)
diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
new file mode 100644
index 0000000..33564b1
--- /dev/null
+++ b/hw/tpm/tpm_ioctl.h
@@ -0,0 +1,246 @@
+/*
+ * tpm_ioctl.h
+ *
+ * (c) Copyright IBM Corporation 2014, 2015.
+ *
+ * This file is licensed under the terms of the 3-clause BSD license
+ */
+#ifndef _TPM_IOCTL_H_
+#define _TPM_IOCTL_H_
+
+#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 ptm_res as the first element.
+ * ptm_res corresponds to the error code of a command executed by the TPM.
+ */
+
+typedef uint32_t ptm_res;
+
+/* PTM_GET_TPMESTABLISHED: get the establishment bit */
+struct ptm_est {
+    union {
+        struct {
+            ptm_res tpm_result;
+            unsigned char bit; /* TPM established bit */
+        } resp; /* response */
+    } u;
+};
+
+/* PTM_RESET_TPMESTABLISHED: reset establishment bit */
+struct ptm_reset_est {
+    union {
+        struct {
+            uint8_t loc; /* locality to use */
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+        } resp; /* response */
+    } u;
+};
+
+/* PTM_INIT */
+struct ptm_init {
+    union {
+        struct {
+            uint32_t init_flags; /* see definitions below */
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+        } resp; /* response */
+    } u;
+};
+
+/* above init_flags */
+#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0)
+    /* delete volatile state file after reading it */
+
+/* PTM_SET_LOCALITY */
+struct ptm_loc {
+    union {
+        struct {
+            uint8_t loc; /* locality to set */
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+        } resp; /* response */
+    } u;
+};
+
+/* PTM_HASH_DATA: hash given data */
+struct ptm_hdata {
+    union {
+        struct {
+            uint32_t length;
+            uint8_t data[4096];
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+        } resp; /* response */
+    } 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 PTM_STATE_BLOB_SIZE (3 * 1024)
+
+/*
+ * The following is the data structure to get state blobs from the TPM.
+ * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads
+ * with this ioctl and with adjusted offset are necessary. All bytes
+ * must be transferred and the transfer is done once the last byte has been
+ * returned.
+ * It is possible to use the read() interface for reading the data; however, the
+ * first bytes of the state blob will be part of the response to the ioctl(); a
+ * subsequent read() is only necessary if the total length (totlength) exceeds
+ * the number of received bytes. seek() is not supported.
+ */
+struct ptm_getstate {
+    union {
+        struct {
+            uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */
+            uint32_t type;        /* which blob to pull */
+            uint32_t offset;      /* offset from where to read */
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+            uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */
+            uint32_t totlength;   /* total length that will be transferred */
+            uint32_t length;      /* number of bytes in following buffer */
+            uint8_t  data[PTM_STATE_BLOB_SIZE];
+        } resp; /* response */
+    } 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 PTM_STATE_FLAG_DECRYPTED     1 /* on input:  get decrypted state */
+#define PTM_STATE_FLAG_ENCRYPTED     2 /* on output: state is encrypted */
+
+/*
+ * The following is the data structure to set state blobs in the TPM.
+ * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple
+ * 'writes' using this ioctl are necessary. The last packet is indicated
+ * by the length being smaller than the PTM_STATE_BLOB_SIZE.
+ * The very first packet may have a length indicator of '0' enabling
+ * a write() with all the bytes from a buffer. If the write() interface
+ * is used, a final ioctl with a non-full buffer must be made to indicate
+ * that all data were transferred (a write with 0 bytes would not work).
+ */
+struct ptm_setstate {
+    union {
+        struct {
+            uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */
+            uint32_t type;        /* which blob to set */
+            uint32_t length;      /* length of the data;
+                                     use 0 on the first packet to
+                                     transfer using write() */
+            uint8_t data[PTM_STATE_BLOB_SIZE];
+        } req; /* request */
+        struct {
+            ptm_res tpm_result;
+        } resp; /* response */
+    } u;
+};
+
+/*
+ * PTM_GET_CONFIG: Data structure to get runtime configuration information
+ * such as which keys are applied.
+ */
+struct ptm_getconfig {
+    union {
+        struct {
+            ptm_res tpm_result;
+            uint32_t flags;
+        } resp; /* response */
+    } u;
+};
+
+#define PTM_CONFIG_FLAG_FILE_KEY        0x1
+#define PTM_CONFIG_FLAG_MIGRATION_KEY   0x2
+
+
+typedef uint64_t ptm_cap;
+typedef struct ptm_est ptm_est;
+typedef struct ptm_reset_est ptm_reset_est;
+typedef struct ptm_loc ptm_loc;
+typedef struct ptm_hdata ptm_hdata;
+typedef struct ptm_init ptm_init;
+typedef struct ptm_getstate ptm_getstate;
+typedef struct ptm_setstate ptm_setstate;
+typedef struct ptm_getconfig ptm_getconfig;
+
+/* 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)
+#define PTM_CAP_GET_CONFIG         (1 << 11)
+#define PTM_CAP_SET_DATAFD         (1 << 12)
+
+enum {
+    PTM_GET_CAPABILITY     = _IOR('P', 0, ptm_cap),
+    PTM_INIT               = _IOWR('P', 1, ptm_init),
+    PTM_SHUTDOWN           = _IOR('P', 2, ptm_res),
+    PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est),
+    PTM_SET_LOCALITY       = _IOWR('P', 4, ptm_loc),
+    PTM_HASH_START         = _IOR('P', 5, ptm_res),
+    PTM_HASH_DATA          = _IOWR('P', 6, ptm_hdata),
+    PTM_HASH_END           = _IOR('P', 7, ptm_res),
+    PTM_CANCEL_TPM_CMD     = _IOR('P', 8, ptm_res),
+    PTM_STORE_VOLATILE     = _IOR('P', 9, ptm_res),
+    PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est),
+    PTM_GET_STATEBLOB      = _IOWR('P', 11, ptm_getstate),
+    PTM_SET_STATEBLOB      = _IOWR('P', 12, ptm_setstate),
+    PTM_STOP               = _IOR('P', 13, ptm_res),
+    PTM_GET_CONFIG         = _IOR('P', 14, ptm_getconfig),
+    PTM_SET_DATAFD         = _IOR('P', 15, ptm_res),
+};
+
+/*
+ * Commands used by the non-CUSE TPMs
+ *
+ * All messages container big-endian data.
+ *
+ * The return messages only contain the 'resp' part of the unions
+ * in the data structures above. Besides that the limits in the
+ * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data
+ * and ptm_set_state:u.req.data) are 0xffffffff.
+ */
+enum {
+    CMD_GET_CAPABILITY = 1,
+    CMD_INIT,
+    CMD_SHUTDOWN,
+    CMD_GET_TPMESTABLISHED,
+    CMD_SET_LOCALITY,
+    CMD_HASH_START,
+    CMD_HASH_DATA,
+    CMD_HASH_END,
+    CMD_CANCEL_TPM_CMD,
+    CMD_STORE_VOLATILE,
+    CMD_RESET_TPMESTABLISHED,
+    CMD_GET_STATEBLOB,
+    CMD_SET_STATEBLOB,
+    CMD_STOP,
+    CMD_GET_CONFIG,
+    CMD_SET_DATAFD
+};
+
+#endif /* _TPM_IOCTL_H */
diff --git a/qapi/tpm.json b/qapi/tpm.json
index e8b2d8d..7093f26 100644
--- a/qapi/tpm.json
+++ b/qapi/tpm.json
@@ -39,10 +39,12 @@
 # An enumeration of TPM types
 #
 # @passthrough: TPM passthrough type
+# @emulator: Software Emulator TPM type
+#            Since: 2.11
 #
 # Since: 1.5
 ##
-{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }
+{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] }
 
 ##
 # @query-tpm-types:
@@ -56,7 +58,7 @@
 # Example:
 #
 # -> { "execute": "query-tpm-types" }
-# <- { "return": [ "passthrough" ] }
+# <- { "return": [ "passthrough", "emulator" ] }
 #
 ##
 { 'command': 'query-tpm-types', 'returns': ['TpmType'] }
@@ -77,16 +79,29 @@
                                              '*cancel-path' : 'str'} }
 
 ##
+# @TPMEmulatorOptions:
+#
+# Information about the TPM emulator type
+#
+# @chardev: Name of a unix socket chardev
+#
+# Since: 2.11
+##
+{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } }
+
+##
 # @TpmTypeOptions:
 #
 # A union referencing different TPM backend types' configuration options
 #
 # @type: 'passthrough' The configuration options for the TPM passthrough type
+#        'emulator' The configuration options for TPM emulator backend type
 #
 # Since: 1.5
 ##
 { 'union': 'TpmTypeOptions',
-   'data': { 'passthrough' : 'TPMPassthroughOptions' } }
+   'data': { 'passthrough' : 'TPMPassthroughOptions',
+             'emulator': 'TPMEmulatorOptions' } }
 
 ##
 # @TPMInfo:
diff --git a/qemu-options.hx b/qemu-options.hx
index 77859a2..846b3fc 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3121,7 +3121,9 @@ 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 emulator,id=id,chardev=dev\n"
+    "                configure the TPM device using chardev backend\n",
     QEMU_ARCH_ALL)
 STEXI
 
@@ -3130,8 +3132,8 @@ The general form of a TPM device option is:
 
 @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
 @findex -tpmdev
-Backend type must be:
-@option{passthrough}.
+Backend type must be either one of the following:
+@option{passthrough}, @option{emulator}.
 
 The specific backend type will determine the applicable options.
 The @code{-tpmdev} option creates the TPM backend and requires a
@@ -3181,6 +3183,20 @@ 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.
 
+@item -tpmdev emulator, id=@var{id}, chardev=@var{dev}
+
+(Linux-host only) Enable access to a TPM emulator using Unix domain socket based
+chardev backend.
+
+@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server.
+
+To create a TPM emulator backend device with chardev socket backend:
+@example
+
+-chardev socket,id=chrtpm,path=/tmp/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
+
+@end example
+
 @end table
 
 ETEXI
diff --git a/vl.c b/vl.c
index 9bb5058..ed86209 100644
--- a/vl.c
+++ b/vl.c
@@ -4893,6 +4893,7 @@ int main(int argc, char **argv, char **envp)
     res_free();
 
     /* vhost-user must be cleaned up before chardevs.  */
+    tpm_cleanup();
     net_cleanup();
     audio_cleanup();
     monitor_cleanup();
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH v8 4/8] tpm-backend: Made few interface methods optional
  2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 4/8] tpm-backend: Made few interface methods optional Amarnath Valluri
@ 2017-09-27 12:23   ` Amarnath Valluri
  0 siblings, 0 replies; 23+ messages in thread
From: Amarnath Valluri @ 2017-09-27 12:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stefan Berger, Marc-André Lureau, Amarnath Valluri

This allows backend implementations left optional interface methods.
For mandatory methods assertion checks added.

Took the opportunity to remove unused methods:
 - tpm_backend_get_desc()
 - TPMDriverOps->handle_startup_error

Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
---
 backends/tpm.c               | 39 ++++++++++++++++++++++++---------------
 hw/tpm/tpm_passthrough.c     | 36 +-----------------------------------
 include/sysemu/tpm_backend.h | 13 ++-----------
 tpm.c                        |  2 +-
 4 files changed, 28 insertions(+), 62 deletions(-)

diff --git a/backends/tpm.c b/backends/tpm.c
index cf5abf1..8911597 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -44,13 +44,6 @@ enum TpmType tpm_backend_get_type(TPMBackend *s)
     return k->ops->type;
 }
 
-const char *tpm_backend_get_desc(TPMBackend *s)
-{
-    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
-
-    return k->ops->desc();
-}
-
 int tpm_backend_init(TPMBackend *s, TPMState *state,
                      TPMRecvDataCB *datacb)
 {
@@ -58,12 +51,14 @@ int tpm_backend_init(TPMBackend *s, TPMState *state,
 
     s->tpm_state = state;
     s->recv_data_callback = datacb;
+    s->had_startup_error = false;
 
-    return k->ops->init(s);
+    return k->ops->init ? k->ops->init(s) : 0;
 }
 
 int tpm_backend_startup_tpm(TPMBackend *s)
 {
+    int res = 0;
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
     /* terminate a running TPM */
@@ -73,20 +68,24 @@ int tpm_backend_startup_tpm(TPMBackend *s)
                                        NULL);
     g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
 
-    return k->ops->startup_tpm(s);
+    res = k->ops->startup_tpm ? k->ops->startup_tpm(s) : 0;
+
+    s->had_startup_error = (res != 0);
+
+    return res;
 }
 
 bool tpm_backend_had_startup_error(TPMBackend *s)
 {
-    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
-
-    return k->ops->had_startup_error(s);
+    return s->had_startup_error;
 }
 
 size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
+    assert(k->ops->realloc_buffer);
+
     return k->ops->realloc_buffer(sb);
 }
 
@@ -100,15 +99,21 @@ void tpm_backend_reset(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    k->ops->reset(s);
+    if (k->ops->reset) {
+        k->ops->reset(s);
+    }
 
     tpm_backend_thread_end(s);
+
+    s->had_startup_error = false;
 }
 
 void tpm_backend_cancel_cmd(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
+    assert(k->ops->cancel_cmd);
+
     k->ops->cancel_cmd(s);
 }
 
@@ -116,20 +121,24 @@ bool tpm_backend_get_tpm_established_flag(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    return k->ops->get_tpm_established_flag(s);
+    return k->ops->get_tpm_established_flag ?
+           k->ops->get_tpm_established_flag(s) : false;
 }
 
 int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    return k->ops->reset_tpm_established_flag(s, locty);
+    return k->ops->reset_tpm_established_flag ?
+           k->ops->reset_tpm_established_flag(s, locty) : 0;
 }
 
 TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
 {
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
+    assert(k->ops->get_tpm_version);
+
     return k->ops->get_tpm_version(s);
 }
 
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index 815a72e..4c21e52 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -54,7 +54,6 @@ struct TPMPassthruState {
     bool tpm_executing;
     bool tpm_op_canceled;
     int cancel_fd;
-    bool had_startup_error;
 
     TPMVersion tpm_version;
 };
@@ -227,29 +226,11 @@ static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
     }
 }
 
-/*
- * Start the TPM (thread). If it had been started before, then terminate
- * and start it again.
- */
-static int tpm_passthrough_startup_tpm(TPMBackend *tb)
-{
-    return 0;
-}
-
 static void tpm_passthrough_reset(TPMBackend *tb)
 {
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
-
     DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
 
     tpm_passthrough_cancel_cmd(tb);
-
-    tpm_pt->had_startup_error = false;
-}
-
-static int tpm_passthrough_init(TPMBackend *tb)
-{
-    return 0;
 }
 
 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
@@ -264,13 +245,6 @@ static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
     return 0;
 }
 
-static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
-{
-    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
-
-    return tpm_pt->had_startup_error;
-}
-
 static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
 {
     size_t wanted_size = 4096; /* Linux tpm.c buffer size */
@@ -309,11 +283,6 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
     }
 }
 
-static const char *tpm_passthrough_create_desc(void)
-{
-    return "Passthrough TPM backend driver";
-}
-
 static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
@@ -453,13 +422,10 @@ static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
 static const TPMDriverOps tpm_passthrough_driver = {
     .type                     = TPM_TYPE_PASSTHROUGH,
     .opts                     = tpm_passthrough_cmdline_opts,
-    .desc                     = tpm_passthrough_create_desc,
+    .desc                     = "Passthrough TPM backend driver",
     .create                   = tpm_passthrough_create,
-    .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,
     .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,
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 202ec8d..9ea7072 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -47,6 +47,7 @@ struct TPMBackend {
     TPMState *tpm_state;
     GThreadPool *thread_pool;
     TPMRecvDataCB *recv_data_callback;
+    bool had_startup_error;
 
     char *id;
     enum TpmModel fe_model;
@@ -75,7 +76,7 @@ struct TPMDriverOps {
     enum TpmType type;
     const QemuOptDesc *opts;
     /* get a descriptive text of the backend to display to the user */
-    const char *(*desc)(void);
+    const char *desc;
 
     TPMBackend *(*create)(QemuOpts *opts, const char *id);
 
@@ -83,8 +84,6 @@ struct TPMDriverOps {
     int (*init)(TPMBackend *t);
     /* start up the TPM on the backend */
     int (*startup_tpm)(TPMBackend *t);
-    /* returns true if nothing will ever answer TPM requests */
-    bool (*had_startup_error)(TPMBackend *t);
 
     size_t (*realloc_buffer)(TPMSizedBuffer *sb);
 
@@ -109,14 +108,6 @@ struct TPMDriverOps {
 enum TpmType tpm_backend_get_type(TPMBackend *s);
 
 /**
- * tpm_backend_get_desc:
- * @s: the backend
- *
- * Returns a human readable description of the backend.
- */
-const char *tpm_backend_get_desc(TPMBackend *s);
-
-/**
  * tpm_backend_init:
  * @s: the backend to initialized
  * @state: TPMState
diff --git a/tpm.c b/tpm.c
index b19b1a3..db14849 100644
--- a/tpm.c
+++ b/tpm.c
@@ -62,7 +62,7 @@ static void tpm_display_backend_drivers(void)
             continue;
         }
         fprintf(stderr, "%12s   %s\n",
-                TpmType_str(i), be_drivers[i]->desc());
+                TpmType_str(i), be_drivers[i]->desc);
     }
     fprintf(stderr, "\n");
 }
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH v8 8/8] tpm: Added support for TPM emulator
  2017-09-27 12:18     ` [Qemu-devel] [PATCH v8 " Amarnath Valluri
@ 2017-09-27 14:18       ` Marc-André Lureau
  0 siblings, 0 replies; 23+ messages in thread
From: Marc-André Lureau @ 2017-09-27 14:18 UTC (permalink / raw)
  To: Amarnath Valluri
  Cc: QEMU, Stefan Berger, Dr. David Alan Gilbert, Eric Blake,
	Markus Armbruster, Paolo Bonzini

Hi

On Wed, Sep 27, 2017 at 2:18 PM, Amarnath Valluri
<amarnath.valluri@intel.com> wrote:
> This change introduces a new TPM backend driver that can communicate with
> swtpm(software TPM emulator) using unix domain socket interface. QEMU talks to
> TPM emulator using QEMU's socket-based chardev backend device.
>
> Swtpm uses two Unix sockets for communications, one for plain TPM commands and
> responses, and one for out-of-band control messages. QEMU passes data socket to
> be used over the control channel.
>
> The swtpm and associated tools can be found here:
>     https://github.com/stefanberger/swtpm
>
> The swtpm's control channel protocol specification can be found here:
>     https://github.com/stefanberger/swtpm/wiki/Control-Channel-Specification
>
> Usage:
>     # setup TPM state directory
>     mkdir /tmp/mytpm
>     chown -R tss:root /tmp/mytpm
>     /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek
>
>     # Ask qemu to use TPM emulator with given tpm state directory
>     qemu-system-x86_64 \
>         [...] \
>         -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \
>         -tpmdev emulator,id=tpm0,chardev=chrtpm \
>         -device tpm-tis,tpmdev=tpm0 \
>         [...]
>
> Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>

Apparently you didn't send the whole series this time. Could you
resend the whole v9? (this helps with patchew and automated testing)

(fwiw, I am working on further cleanups of the tpm code in general,
but I think we should merge your series first)

Some comments below
Thanks

> ---
>  configure             |  13 +-
>  hmp.c                 |   5 +
>  hw/tpm/Makefile.objs  |   1 +
>  hw/tpm/tpm_emulator.c | 613 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/tpm/tpm_ioctl.h    | 246 ++++++++++++++++++++
>  qapi/tpm.json         |  21 +-
>  qemu-options.hx       |  22 +-
>  vl.c                  |   1 +
>  8 files changed, 915 insertions(+), 7 deletions(-)
>  create mode 100644 hw/tpm/tpm_emulator.c
>  create mode 100644 hw/tpm/tpm_ioctl.h
>
> diff --git a/configure b/configure
> index cb0f7ed..a1b956e 100755
> --- a/configure
> +++ b/configure
> @@ -3467,6 +3467,12 @@ else
>    tpm_passthrough=no
>  fi
>
> +# TPM emulator is for all posix systems
> +if test "$mingw32" != "yes"; then
> +  tpm_emulator=$tpm
> +else
> +  tpm_emulator=no
> +fi
>  ##########################################
>  # attr probe
>
> @@ -5359,6 +5365,7 @@ echo "gcov enabled      $gcov"
>  echo "TPM support       $tpm"
>  echo "libssh2 support   $libssh2"
>  echo "TPM passthrough   $tpm_passthrough"
> +echo "TPM emulator      $tpm_emulator"
>  echo "QOM debugging     $qom_cast_debug"
>  echo "Live block migration $live_block_migration"
>  echo "lzo support       $lzo"
> @@ -5937,12 +5944,16 @@ if test "$live_block_migration" = "yes" ; then
>    echo "CONFIG_LIVE_BLOCK_MIGRATION=y" >> $config_host_mak
>  fi
>
> -# TPM passthrough support?
>  if test "$tpm" = "yes"; then
>    echo 'CONFIG_TPM=$(CONFIG_SOFTMMU)' >> $config_host_mak
> +  # TPM passthrough support?
>    if test "$tpm_passthrough" = "yes"; then
>      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
>    fi
> +  # TPM emulator support?
> +  if test "$tpm_emulator" = "yes"; then
> +    echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak
> +  fi
>  fi
>
>  echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
> diff --git a/hmp.c b/hmp.c
> index 0fb2bc7..9cd8179 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -994,6 +994,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
>      Error *err = NULL;
>      unsigned int c = 0;
>      TPMPassthroughOptions *tpo;
> +    TPMEmulatorOptions *teo;
>
>      info_list = qmp_query_tpm(&err);
>      if (err) {
> @@ -1023,6 +1024,10 @@ 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_EMULATOR:
> +            teo = ti->options->u.emulator.data;
> +            monitor_printf(mon, ",chardev=%s", teo->chardev);
> +            break;
>          case TPM_TYPE_OPTIONS_KIND__MAX:
>              break;
>          }
> diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
> index 64cecc3..41f0b7a 100644
> --- a/hw/tpm/Makefile.objs
> +++ b/hw/tpm/Makefile.objs
> @@ -1,2 +1,3 @@
>  common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
>  common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
> +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
> diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
> new file mode 100644
> index 0000000..f7ad0a7
> --- /dev/null
> +++ b/hw/tpm/tpm_emulator.c
> @@ -0,0 +1,613 @@
> +/*
> + *  Emulator TPM driver
> + *
> + *  Copyright (c) 2017 Intel Corporation
> + *  Author: Amarnath Valluri <amarnath.valluri@intel.com>
> + *
> + *  Copyright (c) 2010 - 2013 IBM Corporation
> + *  Authors:
> + *    Stefan Berger <stefanb@us.ibm.com>
> + *
> + *  Copyright (C) 2011 IAIK, Graz University of Technology
> + *    Author: Andreas Niederl
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/error-report.h"
> +#include "qemu/sockets.h"
> +#include "io/channel-socket.h"
> +#include "sysemu/tpm_backend.h"
> +#include "tpm_int.h"
> +#include "hw/hw.h"
> +#include "hw/i386/pc.h"
> +#include "tpm_util.h"
> +#include "tpm_ioctl.h"
> +#include "migration/blocker.h"
> +#include "qapi/error.h"
> +#include "qapi/clone-visitor.h"
> +#include "chardev/char-fe.h"
> +
> +#include <fcntl.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <stdio.h>
> +
> +#define DEBUG_TPM 0
> +
> +#define DPRINTF(fmt, ...) do { \
> +    if (DEBUG_TPM) { \
> +        fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \
> +    } \
> +} while (0)
> +
> +#define TYPE_TPM_EMULATOR "tpm-emulator"
> +#define TPM_EMULATOR(obj) \
> +    OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
> +
> +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))
> +
> +static const TPMDriverOps tpm_emulator_driver;
> +
> +/* data structures */
> +typedef struct TPMEmulator {
> +    TPMBackend parent;
> +
> +    TPMEmulatorOptions *options;
> +    CharBackend ctrl_chr;
> +    QIOChannel *data_ioc;
> +    TPMVersion tpm_version;
> +    ptm_cap caps; /* capabilities of the TPM */
> +    uint8_t cur_locty_number; /* last set locality */
> +    QemuMutex state_lock;
> +    Error *migration_blocker;
> +} TPMEmulator;
> +
> +
> +static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg,
> +                                size_t msg_len_in, size_t msg_len_out)
> +{
> +    uint32_t cmd_no = cpu_to_be32(cmd);
> +    ssize_t n = sizeof(uint32_t) + msg_len_in;
> +    uint8_t *buf = NULL;
> +
> +    buf = g_alloca(n);
> +    memcpy(buf, &cmd_no, sizeof(cmd_no));
> +    memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
> +
> +    n = qemu_chr_fe_write_all(dev, buf, n);
> +    if (n <= 0) {
> +        return -1;
> +    }
> +
> +    /* return if no reply expected */
> +    if (msg_len_out <= 0) {

size_t cannot be < 0

> +        return 0;
> +    }
> +
> +    n = qemu_chr_fe_read_all(dev, msg, msg_len_out);
> +    if (n < 0) {

You probably want to return an error if n == 0 here.

> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
> +                                     const uint8_t *in, uint32_t in_len,
> +                                     uint8_t *out, uint32_t out_len,
> +                                     bool *selftest_done)
> +{
> +    ssize_t ret;
> +    bool is_selftest = false;
> +    const struct tpm_resp_hdr *hdr = NULL;
> +    Error *err = NULL;

You better pass the Error as argument to this function, and do
error_report() from the caller.

> +
> +    if (selftest_done) {
> +        *selftest_done = false;
> +        is_selftest = tpm_util_is_selftest(in, in_len);
> +    }
> +
> +    ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, &err);
> +    if (ret != 0) {
> +        if (err) {

You should be able to prepare unconditionally (no need for if err, and
error_report)

> +            error_prepend(&err, "tpm-emulator: error while transmitting data:");

This won't report the error, nor free it

> +        } else {
> +            error_report("tpm-emulator: Unknown error while transmitting data");
> +        }
> +
> +        goto err_exit;
> +    }
> +
> +    ret = qio_channel_read(tpm_emu->data_ioc, (char *)out, out_len, &err);
> +    if (ret < 0) {
> +        if (err) {

same here

> +            error_prepend(&err, "tpm-emulator: error while reading data:");
> +        } else {
> +            error_report("tpm-emulator: Unknown error while reading data");
> +        }
> +    } else if (ret >= sizeof(*hdr)) {
> +        hdr = (struct tpm_resp_hdr *)out;
> +    }
> +
> +    if (!hdr || be32_to_cpu(hdr->len) != ret) {
> +        error_report("tpm-emulator: received invalid response "
> +                     "packet from TPM with length :%ld", ret);
> +        ret = -1;
> +        goto err_exit;
> +    }
> +
> +    if (is_selftest) {
> +        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
> +    }
> +
> +    return 0;
> +
> +err_exit:
> +    if (ret < 0) {
> +        tpm_util_write_fatal_error_response(out, out_len);
> +    }
> +
> +    return ret;
> +}
> +
> +static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number)
> +{
> +    ptm_loc loc;
> +
> +    DPRINTF("%s : locality: 0x%x", __func__, locty_number);
> +
> +    if (tpm_emu->cur_locty_number == locty_number) {
> +        return 0;
> +    }
> +
> +    DPRINTF("setting locality : 0x%x", locty_number);
> +    loc.u.req.loc = locty_number;
> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_LOCALITY, &loc,
> +                             sizeof(loc), sizeof(loc)) < 0) {
> +        error_report("tpm-emulator: could not set locality : %s",
> +                     strerror(errno));
> +        return -1;
> +    }
> +
> +    loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result);
> +    if (loc.u.resp.tpm_result != 0) {
> +        error_report("tpm-emulator: TPM result for set locality : 0x%x",
> +                     loc.u.resp.tpm_result);
> +        return -1;
> +    }
> +
> +    tpm_emu->cur_locty_number = locty_number;
> +
> +    return 0;
> +}
> +
> +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
> +{
> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
> +    TPMLocality *locty = NULL;
> +    bool selftest_done = false;
> +
> +    DPRINTF("processing command type %d", cmd);
> +
> +    switch (cmd) {
> +    case TPM_BACKEND_CMD_PROCESS_CMD:
> +        qemu_mutex_lock(&tpm_emu->state_lock);
> +        locty = tb->tpm_state->locty_data;
> +        if (tpm_emulator_set_locality(tpm_emu,
> +                                      tb->tpm_state->locty_number) < 0) {
> +            tpm_util_write_fatal_error_response(locty->r_buffer.buffer,
> +                                                locty->r_buffer.size);
> +        } else {
> +            tpm_emulator_unix_tx_bufs(tpm_emu, locty->w_buffer.buffer,
> +                                              locty->w_offset,
> +                                              locty->r_buffer.buffer,
> +                                              locty->r_buffer.size,
> +                                              &selftest_done);
> +        }
> +
> +        tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number,
> +                               selftest_done);
> +        qemu_mutex_unlock(&tpm_emu->state_lock);
> +
> +        break;
> +    case TPM_BACKEND_CMD_INIT:
> +    case TPM_BACKEND_CMD_END:
> +    case TPM_BACKEND_CMD_TPM_RESET:
> +        /* nothing to do */
> +        break;
> +    }
> +}
> +
> +static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
> +{
> +    DPRINTF("%s", __func__);
> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_CAPABILITY,
> +                         &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) {
> +        error_report("tpm-emulator: probing failed : %s", strerror(errno));
> +        return -1;
> +    }
> +
> +    tpm_emu->caps = be64_to_cpu(tpm_emu->caps);
> +
> +    DPRINTF("capbilities : 0x%lx", tpm_emu->caps);
> +
> +    return 0;
> +}
> +
> +static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
> +{
> +    ptm_cap caps = 0;
> +    const char *tpm = NULL;
> +
> +    /* check for min. required capabilities */
> +    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;
> +        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;
> +        tpm = "2";
> +        break;
> +    case TPM_VERSION_UNSPEC:
> +        error_report("tpm-emulator: TPM version has not been set");
> +        return -1;
> +    }
> +
> +    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) {
> +        error_report("tpm-emulator: TPM does not implement minimum set of "
> +                     "required capabilities for TPM %s (0x%x)", tpm, (int)caps);
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int tpm_emulator_startup_tpm(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
> +    ptm_init init;
> +    ptm_res res;
> +
> +    DPRINTF("%s", __func__);
> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_INIT, &init, sizeof(init),
> +                         sizeof(init)) < 0) {
> +        error_report("tpm-emulator: could not send INIT: %s",
> +                     strerror(errno));
> +        goto err_exit;
> +    }
> +
> +    res = be32_to_cpu(init.u.resp.tpm_result);
> +    if (res) {
> +        error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res);
> +        goto err_exit;
> +    }
> +    return 0;
> +
> +err_exit:
> +    return -1;
> +}
> +
> +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
> +    ptm_est est;
> +
> +    DPRINTF("%s", __func__);
> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_TPMESTABLISHED, &est,
> +                             0, sizeof(est)) < 0) {
> +        error_report("tpm-emulator: Could not get the TPM established flag: %s",
> +                     strerror(errno));
> +        return false;
> +    }
> +    DPRINTF("established flag: %0x", est.u.resp.bit);
> +
> +    return (est.u.resp.bit != 0);
> +}
> +
> +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
> +                                                   uint8_t locty)
> +{
> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
> +    ptm_reset_est reset_est;
> +    ptm_res res;
> +
> +    /* only a TPM 2.0 will support this */
> +    if (tpm_emu->tpm_version != TPM_VERSION_2_0) {
> +        return 0;
> +    }
> +
> +    reset_est.u.req.loc = tpm_emu->cur_locty_number;
> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_RESET_TPMESTABLISHED,
> +                             &reset_est, sizeof(reset_est),
> +                             sizeof(reset_est)) < 0) {
> +        error_report("tpm-emulator: Could not reset the establishment bit: %s",
> +                     strerror(errno));
> +        return -1;
> +    }
> +
> +    res = be32_to_cpu(reset_est.u.resp.tpm_result);
> +    if (res) {
> +        error_report("tpm-emulator: TPM result for rest establixhed flag: 0x%x",
> +                     res);
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static void tpm_emulator_cancel_cmd(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
> +    ptm_res res;
> +
> +    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) {
> +        DPRINTF("Backend does not support CANCEL_TPM_CMD");
> +        return;
> +    }
> +
> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_CANCEL_TPM_CMD, &res, 0,
> +                             sizeof(res)) < 0) {
> +        error_report("tpm-emulator: Could not cancel command: %s",
> +                     strerror(errno));
> +    } else if (res != 0) {
> +        error_report("tpm-emulator: Failed to cancel TPM: 0x%x",
> +                     be32_to_cpu(res));
> +    }
> +}
> +
> +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
> +
> +    return tpm_emu->tpm_version;
> +}
> +
> +static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)
> +{
> +    Error *err = NULL;
> +
> +    error_setg(&tpm_emu->migration_blocker,
> +               "Migration disabled: TPM emulator not yet migratable");
> +    migrate_add_blocker(tpm_emu->migration_blocker, &err);
> +    if (err) {
> +        error_report_err(err);
> +        error_free(tpm_emu->migration_blocker);
> +        tpm_emu->migration_blocker = NULL;
> +
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu)
> +{
> +    ptm_res res;
> +    Error *err = NULL;
> +    int fds[2] = { -1, -1 };
> +
> +    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) {
> +        error_report("tpm-emulator: Failed to create socketpair");
> +        return -1;
> +    }
> +
> +    qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1);
> +
> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_DATAFD, &res, 0,
> +                    sizeof(res)) || res != 0) {
> +        error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s",
> +                     strerror(errno));
> +        goto err_exit;
> +    }
> +
> +    tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
> +    if (err) {
> +        error_prepend(&err, "tpm-emulator: Failed to create io channel: ");
> +        goto err_exit;
> +    }
> +
> +    closesocket(fds[1]);
> +
> +    return 0;
> +
> +err_exit:
> +    closesocket(fds[0]);
> +    closesocket(fds[1]);
> +    return -1;
> +}
> +
> +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)
> +{
> +    const char *value;
> +
> +    value = qemu_opt_get(opts, "chardev");
> +    if (value) {
> +        Error *err = NULL;
> +        Chardev *dev = qemu_chr_find(value);
> +
> +        if (!dev) {
> +            error_report("tpm-emulator: tpm chardev '%s' not found.", value);
> +            goto err;
> +        }
> +
> +        if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) {
> +            error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':",
> +                          value);
> +            goto err;
> +        }
> +
> +        tpm_emu->options->chardev = g_strdup(value);
> +    }
> +
> +    if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) {
> +        goto err;
> +    }
> +
> +    /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used
> +     * by passthrough driver, which not yet using GIOChannel.
> +     */
> +    if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd,
> +                             &tpm_emu->tpm_version)) {
> +        error_report("'%s' is not emulating TPM device. Error: %s",
> +                      tpm_emu->options->chardev, strerror(errno));
> +        goto err;
> +    }
> +
> +    DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" :
> +            (tpm_emu->tpm_version == TPM_VERSION_2_0 ?  "2.0" : "Unspecified"));
> +
> +    if (tpm_emulator_probe_caps(tpm_emu) ||
> +        tpm_emulator_check_caps(tpm_emu)) {
> +        goto err;
> +    }
> +
> +    return tpm_emulator_block_migration(tpm_emu);
> +
> +err:
> +    DPRINTF("Startup error");
> +    return -1;
> +}
> +
> +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)
> +{
> +    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;
> +    }
> +
> +    return tb;
> +
> +err_exit:
> +    object_unref(OBJECT(tb));
> +
> +    return NULL;
> +}
> +
> +static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
> +{
> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
> +    TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
> +
> +    options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR;
> +    options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options);
> +
> +    return options;
> +}
> +
> +static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
> +    TPM_STANDARD_CMDLINE_OPTS,
> +    {
> +        .name = "chardev",
> +        .type = QEMU_OPT_STRING,
> +        .help = "Character device to use for out-of-band control messages",
> +    },
> +    { /* end of list */ },
> +};
> +
> +static const TPMDriverOps tpm_emulator_driver = {
> +    .type                     = TPM_TYPE_EMULATOR,
> +    .opts                     = tpm_emulator_cmdline_opts,
> +    .desc                     = "TPM emulator backend driver",
> +
> +    .create                   = tpm_emulator_create,
> +    .startup_tpm              = tpm_emulator_startup_tpm,
> +    .cancel_cmd               = tpm_emulator_cancel_cmd,
> +    .get_tpm_established_flag = tpm_emulator_get_tpm_established_flag,
> +    .reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag,
> +    .get_tpm_version          = tpm_emulator_get_tpm_version,
> +    .get_tpm_options          = tpm_emulator_get_tpm_options,
> +};
> +
> +static void tpm_emulator_inst_init(Object *obj)
> +{
> +    TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
> +
> +    DPRINTF("%s", __func__);
> +    tpm_emu->options = g_new0(TPMEmulatorOptions, 1);
> +    tpm_emu->cur_locty_number = ~0;
> +    qemu_mutex_init(&tpm_emu->state_lock);
> +}
> +
> +/*
> + * Gracefully shut down the external TPM
> + */
> +static void tpm_emulator_shutdown(TPMEmulator *tpm_emu)
> +{
> +    ptm_res res;
> +
> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SHUTDOWN, &res, 0,
> +                             sizeof(res)) < 0) {
> +        error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s",
> +                     strerror(errno));
> +    } else if (res != 0) {
> +        error_report("tpm-emulator: TPM result for sutdown: 0x%x",
> +                     be32_to_cpu(res));
> +    }
> +}
> +
> +static void tpm_emulator_inst_finalize(Object *obj)
> +{
> +    TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
> +
> +    tpm_emulator_shutdown(tpm_emu);
> +
> +    if (tpm_emu->data_ioc) {
> +        qio_channel_close(tpm_emu->data_ioc, NULL);
> +    }
> +
> +    qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false);
> +
> +    if (tpm_emu->options) {
> +        qapi_free_TPMEmulatorOptions(tpm_emu->options);
> +    }
> +
> +    if (tpm_emu->migration_blocker) {
> +        migrate_del_blocker(tpm_emu->migration_blocker);
> +        error_free(tpm_emu->migration_blocker);
> +    }
> +}
> +
> +static void tpm_emulator_class_init(ObjectClass *klass, void *data)
> +{
> +    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
> +    tbc->ops = &tpm_emulator_driver;
> +    tbc->handle_request = tpm_emulator_handle_request;
> +}
> +
> +static const TypeInfo tpm_emulator_info = {
> +    .name = TYPE_TPM_EMULATOR,
> +    .parent = TYPE_TPM_BACKEND,
> +    .instance_size = sizeof(TPMEmulator),
> +    .class_init = tpm_emulator_class_init,
> +    .instance_init = tpm_emulator_inst_init,
> +    .instance_finalize = tpm_emulator_inst_finalize,
> +};
> +
> +static void tpm_emulator_register(void)
> +{
> +    type_register_static(&tpm_emulator_info);
> +    tpm_register_driver(&tpm_emulator_driver);
> +}
> +
> +type_init(tpm_emulator_register)
> diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
> new file mode 100644
> index 0000000..33564b1
> --- /dev/null
> +++ b/hw/tpm/tpm_ioctl.h
> @@ -0,0 +1,246 @@
> +/*
> + * tpm_ioctl.h
> + *
> + * (c) Copyright IBM Corporation 2014, 2015.
> + *
> + * This file is licensed under the terms of the 3-clause BSD license
> + */
> +#ifndef _TPM_IOCTL_H_
> +#define _TPM_IOCTL_H_
> +
> +#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 ptm_res as the first element.
> + * ptm_res corresponds to the error code of a command executed by the TPM.
> + */
> +
> +typedef uint32_t ptm_res;
> +
> +/* PTM_GET_TPMESTABLISHED: get the establishment bit */
> +struct ptm_est {
> +    union {
> +        struct {
> +            ptm_res tpm_result;
> +            unsigned char bit; /* TPM established bit */
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */
> +struct ptm_reset_est {
> +    union {
> +        struct {
> +            uint8_t loc; /* locality to use */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* PTM_INIT */
> +struct ptm_init {
> +    union {
> +        struct {
> +            uint32_t init_flags; /* see definitions below */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* above init_flags */
> +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0)
> +    /* delete volatile state file after reading it */
> +
> +/* PTM_SET_LOCALITY */
> +struct ptm_loc {
> +    union {
> +        struct {
> +            uint8_t loc; /* locality to set */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/* PTM_HASH_DATA: hash given data */
> +struct ptm_hdata {
> +    union {
> +        struct {
> +            uint32_t length;
> +            uint8_t data[4096];
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } 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 PTM_STATE_BLOB_SIZE (3 * 1024)
> +
> +/*
> + * The following is the data structure to get state blobs from the TPM.
> + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads
> + * with this ioctl and with adjusted offset are necessary. All bytes
> + * must be transferred and the transfer is done once the last byte has been
> + * returned.
> + * It is possible to use the read() interface for reading the data; however, the
> + * first bytes of the state blob will be part of the response to the ioctl(); a
> + * subsequent read() is only necessary if the total length (totlength) exceeds
> + * the number of received bytes. seek() is not supported.
> + */
> +struct ptm_getstate {
> +    union {
> +        struct {
> +            uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */
> +            uint32_t type;        /* which blob to pull */
> +            uint32_t offset;      /* offset from where to read */
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +            uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */
> +            uint32_t totlength;   /* total length that will be transferred */
> +            uint32_t length;      /* number of bytes in following buffer */
> +            uint8_t  data[PTM_STATE_BLOB_SIZE];
> +        } resp; /* response */
> +    } 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 PTM_STATE_FLAG_DECRYPTED     1 /* on input:  get decrypted state */
> +#define PTM_STATE_FLAG_ENCRYPTED     2 /* on output: state is encrypted */
> +
> +/*
> + * The following is the data structure to set state blobs in the TPM.
> + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple
> + * 'writes' using this ioctl are necessary. The last packet is indicated
> + * by the length being smaller than the PTM_STATE_BLOB_SIZE.
> + * The very first packet may have a length indicator of '0' enabling
> + * a write() with all the bytes from a buffer. If the write() interface
> + * is used, a final ioctl with a non-full buffer must be made to indicate
> + * that all data were transferred (a write with 0 bytes would not work).
> + */
> +struct ptm_setstate {
> +    union {
> +        struct {
> +            uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */
> +            uint32_t type;        /* which blob to set */
> +            uint32_t length;      /* length of the data;
> +                                     use 0 on the first packet to
> +                                     transfer using write() */
> +            uint8_t data[PTM_STATE_BLOB_SIZE];
> +        } req; /* request */
> +        struct {
> +            ptm_res tpm_result;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +/*
> + * PTM_GET_CONFIG: Data structure to get runtime configuration information
> + * such as which keys are applied.
> + */
> +struct ptm_getconfig {
> +    union {
> +        struct {
> +            ptm_res tpm_result;
> +            uint32_t flags;
> +        } resp; /* response */
> +    } u;
> +};
> +
> +#define PTM_CONFIG_FLAG_FILE_KEY        0x1
> +#define PTM_CONFIG_FLAG_MIGRATION_KEY   0x2
> +
> +
> +typedef uint64_t ptm_cap;
> +typedef struct ptm_est ptm_est;
> +typedef struct ptm_reset_est ptm_reset_est;
> +typedef struct ptm_loc ptm_loc;
> +typedef struct ptm_hdata ptm_hdata;
> +typedef struct ptm_init ptm_init;
> +typedef struct ptm_getstate ptm_getstate;
> +typedef struct ptm_setstate ptm_setstate;
> +typedef struct ptm_getconfig ptm_getconfig;
> +
> +/* 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)
> +#define PTM_CAP_GET_CONFIG         (1 << 11)
> +#define PTM_CAP_SET_DATAFD         (1 << 12)
> +
> +enum {
> +    PTM_GET_CAPABILITY     = _IOR('P', 0, ptm_cap),
> +    PTM_INIT               = _IOWR('P', 1, ptm_init),
> +    PTM_SHUTDOWN           = _IOR('P', 2, ptm_res),
> +    PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est),
> +    PTM_SET_LOCALITY       = _IOWR('P', 4, ptm_loc),
> +    PTM_HASH_START         = _IOR('P', 5, ptm_res),
> +    PTM_HASH_DATA          = _IOWR('P', 6, ptm_hdata),
> +    PTM_HASH_END           = _IOR('P', 7, ptm_res),
> +    PTM_CANCEL_TPM_CMD     = _IOR('P', 8, ptm_res),
> +    PTM_STORE_VOLATILE     = _IOR('P', 9, ptm_res),
> +    PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est),
> +    PTM_GET_STATEBLOB      = _IOWR('P', 11, ptm_getstate),
> +    PTM_SET_STATEBLOB      = _IOWR('P', 12, ptm_setstate),
> +    PTM_STOP               = _IOR('P', 13, ptm_res),
> +    PTM_GET_CONFIG         = _IOR('P', 14, ptm_getconfig),
> +    PTM_SET_DATAFD         = _IOR('P', 15, ptm_res),
> +};
> +
> +/*
> + * Commands used by the non-CUSE TPMs
> + *
> + * All messages container big-endian data.
> + *
> + * The return messages only contain the 'resp' part of the unions
> + * in the data structures above. Besides that the limits in the
> + * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data
> + * and ptm_set_state:u.req.data) are 0xffffffff.
> + */
> +enum {
> +    CMD_GET_CAPABILITY = 1,
> +    CMD_INIT,
> +    CMD_SHUTDOWN,
> +    CMD_GET_TPMESTABLISHED,
> +    CMD_SET_LOCALITY,
> +    CMD_HASH_START,
> +    CMD_HASH_DATA,
> +    CMD_HASH_END,
> +    CMD_CANCEL_TPM_CMD,
> +    CMD_STORE_VOLATILE,
> +    CMD_RESET_TPMESTABLISHED,
> +    CMD_GET_STATEBLOB,
> +    CMD_SET_STATEBLOB,
> +    CMD_STOP,
> +    CMD_GET_CONFIG,
> +    CMD_SET_DATAFD
> +};
> +
> +#endif /* _TPM_IOCTL_H */
> diff --git a/qapi/tpm.json b/qapi/tpm.json
> index e8b2d8d..7093f26 100644
> --- a/qapi/tpm.json
> +++ b/qapi/tpm.json
> @@ -39,10 +39,12 @@
>  # An enumeration of TPM types
>  #
>  # @passthrough: TPM passthrough type
> +# @emulator: Software Emulator TPM type
> +#            Since: 2.11
>  #
>  # Since: 1.5
>  ##
> -{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }
> +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] }
>
>  ##
>  # @query-tpm-types:
> @@ -56,7 +58,7 @@
>  # Example:
>  #
>  # -> { "execute": "query-tpm-types" }
> -# <- { "return": [ "passthrough" ] }
> +# <- { "return": [ "passthrough", "emulator" ] }
>  #
>  ##
>  { 'command': 'query-tpm-types', 'returns': ['TpmType'] }
> @@ -77,16 +79,29 @@
>                                               '*cancel-path' : 'str'} }
>
>  ##
> +# @TPMEmulatorOptions:
> +#
> +# Information about the TPM emulator type
> +#
> +# @chardev: Name of a unix socket chardev
> +#
> +# Since: 2.11
> +##
> +{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } }
> +
> +##
>  # @TpmTypeOptions:
>  #
>  # A union referencing different TPM backend types' configuration options
>  #
>  # @type: 'passthrough' The configuration options for the TPM passthrough type
> +#        'emulator' The configuration options for TPM emulator backend type
>  #
>  # Since: 1.5
>  ##
>  { 'union': 'TpmTypeOptions',
> -   'data': { 'passthrough' : 'TPMPassthroughOptions' } }
> +   'data': { 'passthrough' : 'TPMPassthroughOptions',
> +             'emulator': 'TPMEmulatorOptions' } }
>
>  ##
>  # @TPMInfo:
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 77859a2..846b3fc 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -3121,7 +3121,9 @@ 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 emulator,id=id,chardev=dev\n"
> +    "                configure the TPM device using chardev backend\n",
>      QEMU_ARCH_ALL)
>  STEXI
>
> @@ -3130,8 +3132,8 @@ The general form of a TPM device option is:
>
>  @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
>  @findex -tpmdev
> -Backend type must be:
> -@option{passthrough}.
> +Backend type must be either one of the following:
> +@option{passthrough}, @option{emulator}.
>
>  The specific backend type will determine the applicable options.
>  The @code{-tpmdev} option creates the TPM backend and requires a
> @@ -3181,6 +3183,20 @@ 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.
>
> +@item -tpmdev emulator, id=@var{id}, chardev=@var{dev}
> +
> +(Linux-host only) Enable access to a TPM emulator using Unix domain socket based
> +chardev backend.
> +
> +@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server.
> +
> +To create a TPM emulator backend device with chardev socket backend:
> +@example
> +
> +-chardev socket,id=chrtpm,path=/tmp/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
> +
> +@end example
> +
>  @end table
>
>  ETEXI
> diff --git a/vl.c b/vl.c
> index 9bb5058..ed86209 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -4893,6 +4893,7 @@ int main(int argc, char **argv, char **envp)
>      res_free();
>
>      /* vhost-user must be cleaned up before chardevs.  */
> +    tpm_cleanup();
>      net_cleanup();
>      audio_cleanup();
>      monitor_cleanup();
> --
> 2.7.4
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2017-09-27 14:18 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-09-22 12:33 [Qemu-devel] [PATCH v7 0/8] Provide support for the software TPM emulator Amarnath Valluri
2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 1/8] tpm-backend: Remove unneeded member variable from backend class Amarnath Valluri
2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 2/8] tpm-backend: Move thread handling inside TPMBackend Amarnath Valluri
2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 3/8] tpm-backend: Initialize and free data members in it's own methods Amarnath Valluri
2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 4/8] tpm-backend: Made few interface methods optional Amarnath Valluri
2017-09-27 12:23   ` [Qemu-devel] [PATCH v8 " Amarnath Valluri
2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 5/8] tmp backend: Add new api to read backend TpmInfo Amarnath Valluri
2017-09-22 15:35   ` Marc-André Lureau
2017-09-25 12:01     ` Valluri, Amarnath
2017-09-27 12:16     ` [Qemu-devel] [PATCH v8 " Amarnath Valluri
2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 6/8] tpm-backend: Move realloc_buffer() implementation to tpm-tis model Amarnath Valluri
2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 7/8] tpm-passthrough: move reusable code to utils Amarnath Valluri
2017-09-22 12:33 ` [Qemu-devel] [PATCH v7 8/8] tpm: Added support for TPM emulator Amarnath Valluri
2017-09-22 16:23   ` Stefan Berger
2017-09-24 18:52   ` Marc-André Lureau
2017-09-25  0:35     ` Stefan Berger
2017-09-26 12:05     ` Valluri, Amarnath
2017-09-26 12:24       ` Marc-André Lureau
2017-09-26 13:28         ` Valluri, Amarnath
2017-09-26 13:55           ` Marc-André Lureau
2017-09-26 13:59       ` Eric Blake
2017-09-27 12:18     ` [Qemu-devel] [PATCH v8 " Amarnath Valluri
2017-09-27 14:18       ` Marc-André Lureau

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).