All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26
@ 2018-01-26 18:49 Stefan Berger
  2018-01-26 18:49 ` [Qemu-devel] [PULL 1/5] tpm: Set the flags of the CMD_INIT command to 0 Stefan Berger
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Stefan Berger @ 2018-01-26 18:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanb, marcandre.lureau, peter.maydell

The patches in this pull request fix a bug in the initialization of
a variable, adapt the cancel path used by the passthrough device to the
sysfs path of more recent versions of the Linux kernel, and do some other
cleanups preparing for support of migration. Finally, we add the CRB
interface emulation, which is used with a TPM 2.

   Stefan

The following changes since commit 0f79bfe38a2cf0f43c7ea4959da7f8ebd7858f3d:

  Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-2.12-pull-request' into staging (2018-01-25 09:53:53 +0000)

are available in the git repository at:

  git://github.com/stefanberger/qemu-tpm.git tags/pull-tpm-2018-01-26-1

for you to fetch changes up to b510b21e072a0a9218f37051c885e95824d06bea:

  tpm: add CRB device (2018-01-26 10:12:02 -0500)

----------------------------------------------------------------
Merge tpm 2018/01/26 v1

----------------------------------------------------------------
Marc-André Lureau (4):
      tpm: lookup cancel path under tpm device class
      tpm: replace GThreadPool with AIO threadpool
      tpm: report backend request error
      tpm: add CRB device

Stefan Berger (1):
      tpm: Set the flags of the CMD_INIT command to 0

 backends/tpm.c                     |  59 +++++++++--------
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/i386/acpi-build.c               |  34 +++++++---
 hw/tpm/Makefile.objs               |   1 +
 hw/tpm/tpm_crb.c                   | 303 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/tpm/tpm_emulator.c              |  25 +++-----
 hw/tpm/tpm_passthrough.c           |  65 +++++++++++--------
 hw/tpm/tpm_tis.c                   |   3 +-
 include/hw/acpi/tpm.h              |  51 +++++++++++++++
 include/sysemu/tpm.h               |   5 +-
 include/sysemu/tpm_backend.h       |  15 ++++-
 qapi/tpm.json                      |   5 +-
 tests/Makefile.include             |   2 +
 tests/tpm-crb-test.c               | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 15 files changed, 760 insertions(+), 85 deletions(-)
 create mode 100644 hw/tpm/tpm_crb.c
 create mode 100644 tests/tpm-crb-test.c

-- 
2.5.5

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

* [Qemu-devel] [PULL 1/5] tpm: Set the flags of the CMD_INIT command to 0
  2018-01-26 18:49 [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26 Stefan Berger
@ 2018-01-26 18:49 ` Stefan Berger
  2018-01-26 18:49 ` [Qemu-devel] [PULL 2/5] tpm: lookup cancel path under tpm device class Stefan Berger
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Stefan Berger @ 2018-01-26 18:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanb, marcandre.lureau, peter.maydell

The flags of the CMD_INIT control channel command were not
initialized properly. Fix this and set to 0.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/tpm/tpm_emulator.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
index 35c78de..532d3e3 100644
--- a/hw/tpm/tpm_emulator.c
+++ b/hw/tpm/tpm_emulator.c
@@ -320,7 +320,9 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
 static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
 {
     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
-    ptm_init init;
+    ptm_init init = {
+        .u.req.init_flags = 0,
+    };
     ptm_res res;
 
     if (buffersize != 0 &&
-- 
2.5.5

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

* [Qemu-devel] [PULL 2/5] tpm: lookup cancel path under tpm device class
  2018-01-26 18:49 [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26 Stefan Berger
  2018-01-26 18:49 ` [Qemu-devel] [PULL 1/5] tpm: Set the flags of the CMD_INIT command to 0 Stefan Berger
@ 2018-01-26 18:49 ` Stefan Berger
  2018-01-26 18:49 ` [Qemu-devel] [PULL 3/5] tpm: replace GThreadPool with AIO threadpool Stefan Berger
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Stefan Berger @ 2018-01-26 18:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanb, marcandre.lureau, peter.maydell

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Since Linux commit 313d21eeab9282e, tpm devices have their own device
class "tpm" and the cancel path must be looked up under
/sys/class/tpm/ instead of /sys/class/misc/.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 hw/tpm/tpm_passthrough.c | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index 29142f3..f2563ac 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -216,7 +216,8 @@ static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb)
  * Unless path or file descriptor set has been provided by user,
  * determine the sysfs cancel file following kernel documentation
  * in Documentation/ABI/stable/sysfs-class-tpm.
- * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
+ * From /dev/tpm0 create /sys/class/tpm/tpm0/device/cancel
+ * before 4.0: /sys/class/misc/tpm0/device/cancel
  */
 static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
 {
@@ -227,26 +228,35 @@ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
     if (tpm_pt->options->cancel_path) {
         fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY);
         if (fd < 0) {
-            error_report("Could not open TPM cancel path : %s",
+            error_report("tpm_passthrough: Could not open TPM cancel path: %s",
                          strerror(errno));
         }
         return fd;
     }
 
     dev = strrchr(tpm_pt->tpm_dev, '/');
-    if (dev) {
-        dev++;
-        if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
-                     dev) < sizeof(path)) {
-            fd = qemu_open(path, O_WRONLY);
-            if (fd < 0) {
-                error_report("tpm_passthrough: Could not open TPM cancel "
-                             "path %s : %s", path, strerror(errno));
+    if (!dev) {
+        error_report("tpm_passthrough: Bad TPM device path %s",
+                     tpm_pt->tpm_dev);
+        return -1;
+    }
+
+    dev++;
+    if (snprintf(path, sizeof(path), "/sys/class/tpm/%s/device/cancel",
+                 dev) < sizeof(path)) {
+        fd = qemu_open(path, O_WRONLY);
+        if (fd < 0) {
+            if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
+                         dev) < sizeof(path)) {
+                fd = qemu_open(path, O_WRONLY);
             }
         }
+    }
+
+    if (fd < 0) {
+        error_report("tpm_passthrough: Could not guess TPM cancel path");
     } else {
-       error_report("tpm_passthrough: Bad TPM device path %s",
-                    tpm_pt->tpm_dev);
+        tpm_pt->options->cancel_path = g_strdup(path);
     }
 
     return fd;
-- 
2.5.5

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

* [Qemu-devel] [PULL 3/5] tpm: replace GThreadPool with AIO threadpool
  2018-01-26 18:49 [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26 Stefan Berger
  2018-01-26 18:49 ` [Qemu-devel] [PULL 1/5] tpm: Set the flags of the CMD_INIT command to 0 Stefan Berger
  2018-01-26 18:49 ` [Qemu-devel] [PULL 2/5] tpm: lookup cancel path under tpm device class Stefan Berger
@ 2018-01-26 18:49 ` Stefan Berger
  2018-01-26 18:49 ` [Qemu-devel] [PULL 4/5] tpm: report backend request error Stefan Berger
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Stefan Berger @ 2018-01-26 18:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanb, marcandre.lureau, peter.maydell

From: Marc-André Lureau <marcandre.lureau@redhat.com>

The TPM backend uses a GThreadPool to handle IO in a seperate
thread. However, GThreadPool isn't integrated with Qemu main loops,
making it unnecessarily complicated to deal with.

Qemu has a AIO threadpool, that is better integrated with loops and
various IO functions, provides completion BH by default etc.

Remove the only user of GThreadPool from qemu, use AIO threadpool.

Note that the backend:
- no longer accepts queing multiple requests (unneeded so far)
- increase ref to itself when handling a command, for extra safety
- tpm_backend_thread_end() is renamed tpm_backend_finish_sync() and
will wait for completion of BH (request_completed), which will help
migration handling.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 backends/tpm.c               | 52 +++++++++++++++++++++++---------------------
 include/sysemu/tpm_backend.h | 12 ++++++++--
 2 files changed, 37 insertions(+), 27 deletions(-)

diff --git a/backends/tpm.c b/backends/tpm.c
index 91222c5..143807a 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -19,30 +19,35 @@
 #include "sysemu/tpm.h"
 #include "qemu/thread.h"
 #include "qemu/main-loop.h"
+#include "block/thread-pool.h"
+#include "qemu/error-report.h"
 
-static void tpm_backend_request_completed_bh(void *opaque)
+static void tpm_backend_request_completed(void *opaque, int ret)
 {
     TPMBackend *s = TPM_BACKEND(opaque);
     TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
 
     tic->request_completed(s->tpmif);
+
+    /* no need for atomic, as long the BQL is taken */
+    s->cmd = NULL;
+    object_unref(OBJECT(s));
 }
 
-static void tpm_backend_worker_thread(gpointer data, gpointer user_data)
+static int tpm_backend_worker_thread(gpointer data)
 {
-    TPMBackend *s = TPM_BACKEND(user_data);
+    TPMBackend *s = TPM_BACKEND(data);
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
-    k->handle_request(s, (TPMBackendCmd *)data);
+    k->handle_request(s, s->cmd);
 
-    qemu_bh_schedule(s->bh);
+    return 0;
 }
 
-static void tpm_backend_thread_end(TPMBackend *s)
+void tpm_backend_finish_sync(TPMBackend *s)
 {
-    if (s->thread_pool) {
-        g_thread_pool_free(s->thread_pool, FALSE, TRUE);
-        s->thread_pool = NULL;
+    while (s->cmd) {
+        aio_poll(qemu_get_aio_context(), true);
     }
 }
 
@@ -74,10 +79,7 @@ int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize)
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
 
     /* terminate a running TPM */
-    tpm_backend_thread_end(s);
-
-    s->thread_pool = g_thread_pool_new(tpm_backend_worker_thread, s, 1, TRUE,
-                                       NULL);
+    tpm_backend_finish_sync(s);
 
     res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0;
 
@@ -93,7 +95,17 @@ bool tpm_backend_had_startup_error(TPMBackend *s)
 
 void tpm_backend_deliver_request(TPMBackend *s, TPMBackendCmd *cmd)
 {
-    g_thread_pool_push(s->thread_pool, cmd, NULL);
+    ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context());
+
+    if (s->cmd != NULL) {
+        error_report("There is a TPM request pending");
+        return;
+    }
+
+    s->cmd = cmd;
+    object_ref(OBJECT(s));
+    thread_pool_submit_aio(pool, tpm_backend_worker_thread, s,
+                           tpm_backend_request_completed, s);
 }
 
 void tpm_backend_reset(TPMBackend *s)
@@ -104,7 +116,7 @@ void tpm_backend_reset(TPMBackend *s)
         k->reset(s);
     }
 
-    tpm_backend_thread_end(s);
+    tpm_backend_finish_sync(s);
 
     s->had_startup_error = false;
 }
@@ -159,28 +171,18 @@ TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
     return info;
 }
 
-static void tpm_backend_instance_init(Object *obj)
-{
-    TPMBackend *s = TPM_BACKEND(obj);
-
-    s->bh = qemu_bh_new(tpm_backend_request_completed_bh, s);
-}
-
 static void tpm_backend_instance_finalize(Object *obj)
 {
     TPMBackend *s = TPM_BACKEND(obj);
 
     object_unref(OBJECT(s->tpmif));
     g_free(s->id);
-    tpm_backend_thread_end(s);
-    qemu_bh_delete(s->bh);
 }
 
 static const TypeInfo tpm_backend_info = {
     .name = TYPE_TPM_BACKEND,
     .parent = TYPE_OBJECT,
     .instance_size = sizeof(TPMBackend),
-    .instance_init = tpm_backend_instance_init,
     .instance_finalize = tpm_backend_instance_finalize,
     .class_size = sizeof(TPMBackendClass),
     .abstract = true,
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 0d6c994..a69593e 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -45,9 +45,8 @@ struct TPMBackend {
     /*< protected >*/
     TPMIf *tpmif;
     bool opened;
-    GThreadPool *thread_pool;
     bool had_startup_error;
-    QEMUBH *bh;
+    TPMBackendCmd *cmd;
 
     /* <public> */
     char *id;
@@ -197,6 +196,15 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s);
 size_t tpm_backend_get_buffer_size(TPMBackend *s);
 
 /**
+ * tpm_backend_finish_sync:
+ * @s: the backend to call into
+ *
+ * Finish the pending command synchronously (this will call aio_poll()
+ * on qemu main AIOContext until it ends)
+ */
+void tpm_backend_finish_sync(TPMBackend *s);
+
+/**
  * tpm_backend_query_tpm:
  * @s: the backend
  *
-- 
2.5.5

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

* [Qemu-devel] [PULL 4/5] tpm: report backend request error
  2018-01-26 18:49 [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26 Stefan Berger
                   ` (2 preceding siblings ...)
  2018-01-26 18:49 ` [Qemu-devel] [PULL 3/5] tpm: replace GThreadPool with AIO threadpool Stefan Berger
@ 2018-01-26 18:49 ` Stefan Berger
  2018-01-26 18:49 ` [Qemu-devel] [PULL 5/5] tpm: add CRB device Stefan Berger
  2018-01-29 11:37 ` [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26 Peter Maydell
  5 siblings, 0 replies; 7+ messages in thread
From: Stefan Berger @ 2018-01-26 18:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanb, marcandre.lureau, peter.maydell

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Use an Error** for request to let the caller handle error reporting.

This will also allow to inform the frontend of a backend error.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 backends/tpm.c               |  9 +++++++--
 hw/tpm/tpm_emulator.c        | 21 ++++++---------------
 hw/tpm/tpm_passthrough.c     | 31 +++++++++++++++----------------
 hw/tpm/tpm_tis.c             |  3 ++-
 include/sysemu/tpm.h         |  2 +-
 include/sysemu/tpm_backend.h |  3 ++-
 6 files changed, 33 insertions(+), 36 deletions(-)

diff --git a/backends/tpm.c b/backends/tpm.c
index 143807a..d617ba7 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -27,7 +27,7 @@ static void tpm_backend_request_completed(void *opaque, int ret)
     TPMBackend *s = TPM_BACKEND(opaque);
     TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
 
-    tic->request_completed(s->tpmif);
+    tic->request_completed(s->tpmif, ret);
 
     /* no need for atomic, as long the BQL is taken */
     s->cmd = NULL;
@@ -38,8 +38,13 @@ static int tpm_backend_worker_thread(gpointer data)
 {
     TPMBackend *s = TPM_BACKEND(data);
     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
+    Error *err = NULL;
 
-    k->handle_request(s, s->cmd);
+    k->handle_request(s, s->cmd, &err);
+    if (err) {
+        error_report_err(err);
+        return -1;
+    }
 
     return 0;
 }
diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
index 532d3e3..8d91779 100644
--- a/hw/tpm/tpm_emulator.c
+++ b/hw/tpm/tpm_emulator.c
@@ -185,28 +185,19 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
     return 0;
 }
 
-static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
+static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
+                                        Error **errp)
 {
     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
-    Error *err = NULL;
 
     DPRINTF("processing TPM command");
 
-    if (tpm_emulator_set_locality(tpm_emu, cmd->locty, &err) < 0) {
-        goto error;
-    }
-
-    if (tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
+    if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 ||
+        tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
                                   cmd->out, cmd->out_len,
-                                  &cmd->selftest_done, &err) < 0) {
-        goto error;
+                                  &cmd->selftest_done, errp) < 0) {
+        tpm_util_write_fatal_error_response(cmd->out, cmd->out_len);
     }
-
-    return;
-
-error:
-    tpm_util_write_fatal_error_response(cmd->out, cmd->out_len);
-    error_report_err(err);
 }
 
 static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index f2563ac..37dce15 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -80,10 +80,11 @@ static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
     }
     return ret;
 }
-static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
-                                        const uint8_t *in, uint32_t in_len,
-                                        uint8_t *out, uint32_t out_len,
-                                        bool *selftest_done)
+
+static void tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
+                                         const uint8_t *in, uint32_t in_len,
+                                         uint8_t *out, uint32_t out_len,
+                                         bool *selftest_done, Error **errp)
 {
     ssize_t ret;
     bool is_selftest;
@@ -99,9 +100,8 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
     ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len);
     if (ret != in_len) {
         if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
-            error_report("tpm_passthrough: error while transmitting data "
-                         "to TPM: %s (%i)",
-                         strerror(errno), errno);
+            error_setg_errno(errp, errno, "tpm_passthrough: error while "
+                             "transmitting data to TPM");
         }
         goto err_exit;
     }
@@ -111,15 +111,14 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
     ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
     if (ret < 0) {
         if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
-            error_report("tpm_passthrough: error while reading data from "
-                         "TPM: %s (%i)",
-                         strerror(errno), errno);
+            error_setg_errno(errp, errno, "tpm_passthrough: error while "
+                             "reading data from TPM");
         }
     } else if (ret < sizeof(struct tpm_resp_hdr) ||
                be32_to_cpu(((struct tpm_resp_hdr *)out)->len) != ret) {
         ret = -1;
-        error_report("tpm_passthrough: received invalid response "
-                     "packet from TPM");
+        error_setg_errno(errp, errno, "tpm_passthrough: received invalid "
+                     "response packet from TPM");
     }
 
     if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
@@ -133,18 +132,18 @@ err_exit:
     }
 
     tpm_pt->tpm_executing = false;
-
-    return ret;
 }
 
-static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
+static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
+                                           Error **errp)
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 
     DPRINTF("tpm_passthrough: processing command %p\n", cmd);
 
     tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len,
-                                 cmd->out, cmd->out_len, &cmd->selftest_done);
+                                 cmd->out, cmd->out_len, &cmd->selftest_done,
+                                 errp);
 }
 
 static void tpm_passthrough_reset(TPMBackend *tb)
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c
index 8b5eb01..08f41d2 100644
--- a/hw/tpm/tpm_tis.c
+++ b/hw/tpm/tpm_tis.c
@@ -393,7 +393,7 @@ static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
 /*
  * Callback from the TPM to indicate that the response was received.
  */
-static void tpm_tis_request_completed(TPMIf *ti)
+static void tpm_tis_request_completed(TPMIf *ti, int ret)
 {
     TPMState *s = TPM(ti);
     uint8_t locty = s->cmd.locty;
@@ -405,6 +405,7 @@ static void tpm_tis_request_completed(TPMIf *ti)
         }
     }
 
+    /* FIXME: report error if ret != 0 */
     tpm_tis_sts_set(&s->loc[locty],
                     TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
     s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h
index 852e026..ac04a9d 100644
--- a/include/sysemu/tpm.h
+++ b/include/sysemu/tpm.h
@@ -41,7 +41,7 @@ typedef struct TPMIfClass {
     InterfaceClass parent_class;
 
     enum TpmModel model;
-    void (*request_completed)(TPMIf *obj);
+    void (*request_completed)(TPMIf *obj, int ret);
     enum TPMVersion (*get_version)(TPMIf *obj);
 } TPMIfClass;
 
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index a69593e..7e166ef 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -18,6 +18,7 @@
 #include "qapi-types.h"
 #include "qemu/option.h"
 #include "sysemu/tpm.h"
+#include "qapi/error.h"
 
 #define TYPE_TPM_BACKEND "tpm-backend"
 #define TPM_BACKEND(obj) \
@@ -84,7 +85,7 @@ struct TPMBackendClass {
 
     TpmTypeOptions *(*get_tpm_options)(TPMBackend *t);
 
-    void (*handle_request)(TPMBackend *s, TPMBackendCmd *cmd);
+    void (*handle_request)(TPMBackend *s, TPMBackendCmd *cmd, Error **errp);
 };
 
 /**
-- 
2.5.5

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

* [Qemu-devel] [PULL 5/5] tpm: add CRB device
  2018-01-26 18:49 [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26 Stefan Berger
                   ` (3 preceding siblings ...)
  2018-01-26 18:49 ` [Qemu-devel] [PULL 4/5] tpm: report backend request error Stefan Berger
@ 2018-01-26 18:49 ` Stefan Berger
  2018-01-29 11:37 ` [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26 Peter Maydell
  5 siblings, 0 replies; 7+ messages in thread
From: Stefan Berger @ 2018-01-26 18:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanb, marcandre.lureau, peter.maydell

From: Marc-André Lureau <marcandre.lureau@redhat.com>

tpm_crb is a device for TPM 2.0 Command Response Buffer (CRB)
Interface as defined in TCG PC Client Platform TPM Profile (PTP)
Specification Family “2.0” Level 00 Revision 01.03 v22.

The PTP allows device implementation to switch between TIS and CRB
model at run time, but given that CRB is a simpler device to
implement, I chose to implement it as a different device.

The device doesn't implement other locality than 0 for now (my laptop
TPM doesn't either, so I assume this isn't so bad)

Tested with some success with Linux upstream and Windows 10, seabios &
modified ovmf. The device is recognized and correctly transmit
command/response with passthrough & emu. However, we are missing PPI
ACPI part atm.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/i386/acpi-build.c               |  34 ++++-
 hw/tpm/Makefile.objs               |   1 +
 hw/tpm/tpm_crb.c                   | 303 +++++++++++++++++++++++++++++++++++++
 include/hw/acpi/tpm.h              |  51 +++++++
 include/sysemu/tpm.h               |   3 +
 qapi/tpm.json                      |   5 +-
 tests/Makefile.include             |   2 +
 tests/tpm-crb-test.c               | 275 +++++++++++++++++++++++++++++++++
 10 files changed, 666 insertions(+), 10 deletions(-)
 create mode 100644 hw/tpm/tpm_crb.c
 create mode 100644 tests/tpm-crb-test.c

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 95ac4b4..ac27700 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -37,6 +37,7 @@ CONFIG_APPLESMC=y
 CONFIG_I8259=y
 CONFIG_PFLASH_CFI01=y
 CONFIG_TPM_TIS=$(CONFIG_TPM)
+CONFIG_TPM_CRB=$(CONFIG_TPM)
 CONFIG_MC146818RTC=y
 CONFIG_PCI_PIIX=y
 CONFIG_WDT_IB700=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 0221236..b2104ad 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -37,6 +37,7 @@ CONFIG_APPLESMC=y
 CONFIG_I8259=y
 CONFIG_PFLASH_CFI01=y
 CONFIG_TPM_TIS=$(CONFIG_TPM)
+CONFIG_TPM_CRB=$(CONFIG_TPM)
 CONFIG_MC146818RTC=y
 CONFIG_PCI_PIIX=y
 CONFIG_WDT_IB700=y
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index dc4b2b9..ed78c4e 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -2224,6 +2224,22 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
             aml_append(sb_scope, scope);
         }
     }
+
+    if (TPM_IS_CRB(tpm_find())) {
+        dev = aml_device("TPM");
+        aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
+        crs = aml_resource_template();
+        aml_append(crs, aml_memory32_fixed(TPM_CRB_ADDR_BASE,
+                                           TPM_CRB_ADDR_SIZE, AML_READ_WRITE));
+        aml_append(dev, aml_name_decl("_CRS", crs));
+
+        method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+        aml_append(method, aml_return(aml_int(0x0f)));
+        aml_append(dev, method);
+
+        aml_append(sb_scope, dev);
+    }
+
     aml_append(dsdt, sb_scope);
 
     /* copy AML table into ACPI tables blob and patch header there */
@@ -2285,18 +2301,20 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog)
     if (TPM_IS_TIS(tpm_find())) {
         tpm2_ptr->control_area_address = cpu_to_le64(0);
         tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
-
-        tpm2_ptr->log_area_minimum_length =
-            cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE);
-
-        /* log area start address to be filled by Guest linker */
-        bios_linker_loader_add_pointer(linker,
-            ACPI_BUILD_TABLE_FILE, log_addr_offset, log_addr_size,
-            ACPI_BUILD_TPMLOG_FILE, 0);
+    } else if (TPM_IS_CRB(tpm_find())) {
+        tpm2_ptr->control_area_address = cpu_to_le64(TPM_CRB_ADDR_CTRL);
+        tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_CRB);
     } else {
         g_warn_if_reached();
     }
 
+    tpm2_ptr->log_area_minimum_length =
+        cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE);
+
+    /* log area start address to be filled by Guest linker */
+    bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
+                                   log_addr_offset, log_addr_size,
+                                   ACPI_BUILD_TPMLOG_FILE, 0);
     build_header(linker, table_data,
                  (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL);
 }
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
index 7a93b24..1dc9f8b 100644
--- a/hw/tpm/Makefile.objs
+++ b/hw/tpm/Makefile.objs
@@ -1,4 +1,5 @@
 common-obj-y += tpm_util.o
 common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
+common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o
 common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
 common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
new file mode 100644
index 0000000..687d255
--- /dev/null
+++ b/hw/tpm/tpm_crb.c
@@ -0,0 +1,303 @@
+/*
+ * tpm_crb.c - QEMU's TPM CRB interface emulator
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * Authors:
+ *   Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * tpm_crb is a device for TPM 2.0 Command Response Buffer (CRB) Interface
+ * as defined in TCG PC Client Platform TPM Profile (PTP) Specification
+ * Family “2.0” Level 00 Revision 01.03 v22
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "exec/address-spaces.h"
+
+#include "hw/qdev-core.h"
+#include "hw/qdev-properties.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/acpi/tpm.h"
+#include "migration/vmstate.h"
+#include "sysemu/tpm_backend.h"
+#include "tpm_int.h"
+#include "tpm_util.h"
+
+typedef struct CRBState {
+    DeviceState parent_obj;
+
+    TPMBackend *tpmbe;
+    TPMBackendCmd cmd;
+    uint32_t regs[TPM_CRB_R_MAX];
+    MemoryRegion mmio;
+    MemoryRegion cmdmem;
+
+    size_t be_buffer_size;
+} CRBState;
+
+#define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB)
+
+#define DEBUG_CRB 0
+
+#define DPRINTF(fmt, ...) do {                  \
+        if (DEBUG_CRB) {                        \
+            printf(fmt, ## __VA_ARGS__);        \
+        }                                       \
+    } while (0)
+
+#define CRB_INTF_TYPE_CRB_ACTIVE 0b1
+#define CRB_INTF_VERSION_CRB 0b1
+#define CRB_INTF_CAP_LOCALITY_0_ONLY 0b0
+#define CRB_INTF_CAP_IDLE_FAST 0b0
+#define CRB_INTF_CAP_XFER_SIZE_64 0b11
+#define CRB_INTF_CAP_FIFO_NOT_SUPPORTED 0b0
+#define CRB_INTF_CAP_CRB_SUPPORTED 0b1
+#define CRB_INTF_IF_SELECTOR_CRB 0b1
+
+#define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER)
+
+enum crb_loc_ctrl {
+    CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0),
+    CRB_LOC_CTRL_RELINQUISH = BIT(1),
+    CRB_LOC_CTRL_SEIZE = BIT(2),
+    CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT = BIT(3),
+};
+
+enum crb_ctrl_req {
+    CRB_CTRL_REQ_CMD_READY = BIT(0),
+    CRB_CTRL_REQ_GO_IDLE = BIT(1),
+};
+
+enum crb_start {
+    CRB_START_INVOKE = BIT(0),
+};
+
+enum crb_cancel {
+    CRB_CANCEL_INVOKE = BIT(0),
+};
+
+static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr,
+                                  unsigned size)
+{
+    CRBState *s = CRB(opaque);
+    void *regs = (void *)&s->regs + (addr & ~3);
+    unsigned offset = addr & 3;
+    uint32_t val = *(uint32_t *)regs >> (8 * offset);
+
+    DPRINTF("CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 "\n",
+            addr, size, val);
+    return val;
+}
+
+static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
+                               uint64_t val, unsigned size)
+{
+    CRBState *s = CRB(opaque);
+    DPRINTF("CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx64 "\n",
+            addr, size, val);
+
+    switch (addr) {
+    case A_CRB_CTRL_REQ:
+        switch (val) {
+        case CRB_CTRL_REQ_CMD_READY:
+            ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
+                             tpmIdle, 0);
+            break;
+        case CRB_CTRL_REQ_GO_IDLE:
+            ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
+                             tpmIdle, 1);
+            break;
+        }
+        break;
+    case A_CRB_CTRL_CANCEL:
+        if (val == CRB_CANCEL_INVOKE &&
+            s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) {
+            tpm_backend_cancel_cmd(s->tpmbe);
+        }
+        break;
+    case A_CRB_CTRL_START:
+        if (val == CRB_START_INVOKE &&
+            !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE)) {
+            void *mem = memory_region_get_ram_ptr(&s->cmdmem);
+
+            s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE;
+            s->cmd = (TPMBackendCmd) {
+                .in = mem,
+                .in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size),
+                .out = mem,
+                .out_len = s->be_buffer_size,
+            };
+
+            tpm_backend_deliver_request(s->tpmbe, &s->cmd);
+        }
+        break;
+    case A_CRB_LOC_CTRL:
+        switch (val) {
+        case CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT:
+            /* not loc 3 or 4 */
+            break;
+        case CRB_LOC_CTRL_RELINQUISH:
+            break;
+        case CRB_LOC_CTRL_REQUEST_ACCESS:
+            ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS,
+                             Granted, 1);
+            ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS,
+                             beenSeized, 0);
+            ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE,
+                             locAssigned, 1);
+            ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE,
+                             tpmRegValidSts, 1);
+            break;
+        }
+        break;
+    }
+}
+
+static const MemoryRegionOps tpm_crb_memory_ops = {
+    .read = tpm_crb_mmio_read,
+    .write = tpm_crb_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static void tpm_crb_request_completed(TPMIf *ti, int ret)
+{
+    CRBState *s = CRB(ti);
+
+    s->regs[R_CRB_CTRL_START] &= ~CRB_START_INVOKE;
+    if (ret != 0) {
+        ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
+                         tpmSts, 1); /* fatal error */
+    }
+}
+
+static enum TPMVersion tpm_crb_get_version(TPMIf *ti)
+{
+    CRBState *s = CRB(ti);
+
+    return tpm_backend_get_tpm_version(s->tpmbe);
+}
+
+static int tpm_crb_pre_save(void *opaque)
+{
+    CRBState *s = opaque;
+
+    tpm_backend_finish_sync(s->tpmbe);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_tpm_crb = {
+    .name = "tpm-crb",
+    .pre_save = tpm_crb_pre_save,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static Property tpm_crb_properties[] = {
+    DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tpm_crb_realize(DeviceState *dev, Error **errp)
+{
+    CRBState *s = CRB(dev);
+
+    if (!tpm_find()) {
+        error_setg(errp, "at most one TPM device is permitted");
+        return;
+    }
+    if (!s->tpmbe) {
+        error_setg(errp, "'tpmdev' property is required");
+        return;
+    }
+
+    memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s,
+        "tpm-crb-mmio", sizeof(s->regs));
+    memory_region_init_ram(&s->cmdmem, OBJECT(s),
+        "tpm-crb-cmd", CRB_CTRL_CMD_SIZE, errp);
+
+    memory_region_add_subregion(get_system_memory(),
+        TPM_CRB_ADDR_BASE, &s->mmio);
+    memory_region_add_subregion(get_system_memory(),
+        TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem);
+
+    tpm_backend_reset(s->tpmbe);
+
+    ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+                     InterfaceType, CRB_INTF_TYPE_CRB_ACTIVE);
+    ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+                     InterfaceVersion, CRB_INTF_VERSION_CRB);
+    ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+                     CapLocality, CRB_INTF_CAP_LOCALITY_0_ONLY);
+    ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+                     CapCRBIdleBypass, CRB_INTF_CAP_IDLE_FAST);
+    ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+                     CapDataXferSizeSupport, CRB_INTF_CAP_XFER_SIZE_64);
+    ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+                     CapFIFO, CRB_INTF_CAP_FIFO_NOT_SUPPORTED);
+    ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+                     CapCRB, CRB_INTF_CAP_CRB_SUPPORTED);
+    ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+                     InterfaceSelector, CRB_INTF_IF_SELECTOR_CRB);
+    ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+                     RID, 0b0000);
+    ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID2,
+                     VID, PCI_VENDOR_ID_IBM);
+
+    s->regs[R_CRB_CTRL_CMD_SIZE] = CRB_CTRL_CMD_SIZE;
+    s->regs[R_CRB_CTRL_CMD_LADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER;
+    s->regs[R_CRB_CTRL_RSP_SIZE] = CRB_CTRL_CMD_SIZE;
+    s->regs[R_CRB_CTRL_RSP_ADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER;
+
+    s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->tpmbe),
+                            CRB_CTRL_CMD_SIZE);
+
+    tpm_backend_startup_tpm(s->tpmbe, s->be_buffer_size);
+}
+
+static void tpm_crb_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    TPMIfClass *tc = TPM_IF_CLASS(klass);
+
+    dc->realize = tpm_crb_realize;
+    dc->props = tpm_crb_properties;
+    dc->vmsd  = &vmstate_tpm_crb;
+    dc->user_creatable = true;
+    tc->model = TPM_MODEL_TPM_CRB;
+    tc->get_version = tpm_crb_get_version;
+    tc->request_completed = tpm_crb_request_completed;
+
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo tpm_crb_info = {
+    .name = TYPE_TPM_CRB,
+    /* could be TYPE_SYS_BUS_DEVICE (or LPC etc) */
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(CRBState),
+    .class_init  = tpm_crb_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_TPM_IF },
+        { }
+    }
+};
+
+static void tpm_crb_register(void)
+{
+    type_register_static(&tpm_crb_info);
+}
+
+type_init(tpm_crb_register)
diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h
index 6d516c6..96fd3a9 100644
--- a/include/hw/acpi/tpm.h
+++ b/include/hw/acpi/tpm.h
@@ -16,11 +16,61 @@
 #ifndef HW_ACPI_TPM_H
 #define HW_ACPI_TPM_H
 
+#include "hw/registerfields.h"
+
 #define TPM_TIS_ADDR_BASE           0xFED40000
 #define TPM_TIS_ADDR_SIZE           0x5000
 
 #define TPM_TIS_IRQ                 5
 
+REG32(CRB_LOC_STATE, 0x00)
+  FIELD(CRB_LOC_STATE, tpmEstablished, 0, 1)
+  FIELD(CRB_LOC_STATE, locAssigned, 1, 1)
+  FIELD(CRB_LOC_STATE, activeLocality, 2, 3)
+  FIELD(CRB_LOC_STATE, reserved, 5, 2)
+  FIELD(CRB_LOC_STATE, tpmRegValidSts, 7, 1)
+REG32(CRB_LOC_CTRL, 0x08)
+REG32(CRB_LOC_STS, 0x0C)
+  FIELD(CRB_LOC_STS, Granted, 0, 1)
+  FIELD(CRB_LOC_STS, beenSeized, 1, 1)
+REG32(CRB_INTF_ID, 0x30)
+  FIELD(CRB_INTF_ID, InterfaceType, 0, 4)
+  FIELD(CRB_INTF_ID, InterfaceVersion, 4, 4)
+  FIELD(CRB_INTF_ID, CapLocality, 8, 1)
+  FIELD(CRB_INTF_ID, CapCRBIdleBypass, 9, 1)
+  FIELD(CRB_INTF_ID, Reserved1, 10, 1)
+  FIELD(CRB_INTF_ID, CapDataXferSizeSupport, 11, 2)
+  FIELD(CRB_INTF_ID, CapFIFO, 13, 1)
+  FIELD(CRB_INTF_ID, CapCRB, 14, 1)
+  FIELD(CRB_INTF_ID, CapIFRes, 15, 2)
+  FIELD(CRB_INTF_ID, InterfaceSelector, 17, 2)
+  FIELD(CRB_INTF_ID, IntfSelLock, 19, 1)
+  FIELD(CRB_INTF_ID, Reserved2, 20, 4)
+  FIELD(CRB_INTF_ID, RID, 24, 8)
+REG32(CRB_INTF_ID2, 0x34)
+  FIELD(CRB_INTF_ID2, VID, 0, 16)
+  FIELD(CRB_INTF_ID2, DID, 16, 16)
+REG32(CRB_CTRL_EXT, 0x38)
+REG32(CRB_CTRL_REQ, 0x40)
+REG32(CRB_CTRL_STS, 0x44)
+  FIELD(CRB_CTRL_STS, tpmSts, 0, 1)
+  FIELD(CRB_CTRL_STS, tpmIdle, 1, 1)
+REG32(CRB_CTRL_CANCEL, 0x48)
+REG32(CRB_CTRL_START, 0x4C)
+REG32(CRB_INT_ENABLED, 0x50)
+REG32(CRB_INT_STS, 0x54)
+REG32(CRB_CTRL_CMD_SIZE, 0x58)
+REG32(CRB_CTRL_CMD_LADDR, 0x5C)
+REG32(CRB_CTRL_CMD_HADDR, 0x60)
+REG32(CRB_CTRL_RSP_SIZE, 0x64)
+REG32(CRB_CTRL_RSP_ADDR, 0x68)
+REG32(CRB_DATA_BUFFER, 0x80)
+
+#define TPM_CRB_ADDR_BASE           0xFED40000
+#define TPM_CRB_ADDR_SIZE           0x1000
+#define TPM_CRB_ADDR_CTRL           (TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ)
+#define TPM_CRB_R_MAX               R_CRB_DATA_BUFFER
+
 #define TPM_LOG_AREA_MINIMUM_SIZE   (64 * 1024)
 
 #define TPM_TCPA_ACPI_CLASS_CLIENT  0
@@ -30,5 +80,6 @@
 #define TPM2_ACPI_CLASS_SERVER      1
 
 #define TPM2_START_METHOD_MMIO      6
+#define TPM2_START_METHOD_CRB       7
 
 #endif /* HW_ACPI_TPM_H */
diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h
index ac04a9d..233b1a3 100644
--- a/include/sysemu/tpm.h
+++ b/include/sysemu/tpm.h
@@ -46,9 +46,12 @@ typedef struct TPMIfClass {
 } TPMIfClass;
 
 #define TYPE_TPM_TIS                "tpm-tis"
+#define TYPE_TPM_CRB                "tpm-crb"
 
 #define TPM_IS_TIS(chr)                             \
     object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS)
+#define TPM_IS_CRB(chr)                             \
+    object_dynamic_cast(OBJECT(chr), TYPE_TPM_CRB)
 
 /* returns NULL unless there is exactly one TPM device */
 static inline TPMIf *tpm_find(void)
diff --git a/qapi/tpm.json b/qapi/tpm.json
index 7093f26..d50deef 100644
--- a/qapi/tpm.json
+++ b/qapi/tpm.json
@@ -11,10 +11,11 @@
 # An enumeration of TPM models
 #
 # @tpm-tis: TPM TIS model
+# @tpm-crb: TPM CRB model (since 2.12)
 #
 # Since: 1.5
 ##
-{ 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] }
+{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb' ] }
 
 ##
 # @query-tpm-models:
@@ -28,7 +29,7 @@
 # Example:
 #
 # -> { "execute": "query-tpm-models" }
-# <- { "return": [ "tpm-tis" ] }
+# <- { "return": [ "tpm-tis", "tpm-crb" ] }
 #
 ##
 { 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 851aafe..ca82e0c 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -286,6 +286,7 @@ check-qtest-i386-$(CONFIG_VHOST_USER_NET_TEST_i386) += tests/vhost-user-test$(EX
 ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),)
 check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF)
 endif
+check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF)
 check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF)
 check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF)
 check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF)
@@ -708,6 +709,7 @@ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
 tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
 tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
         tests/io-channel-helpers.o $(test-io-obj-y)
+tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o $(test-io-obj-y)
 tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \
         tests/io-channel-helpers.o $(test-io-obj-y)
 tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \
diff --git a/tests/tpm-crb-test.c b/tests/tpm-crb-test.c
new file mode 100644
index 0000000..8bf1507
--- /dev/null
+++ b/tests/tpm-crb-test.c
@@ -0,0 +1,275 @@
+/*
+ * QTest testcase for TPM CRB
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * Authors:
+ *   Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include <glib/gstdio.h>
+
+#include "hw/acpi/tpm.h"
+#include "hw/tpm/tpm_ioctl.h"
+#include "io/channel-socket.h"
+#include "libqtest.h"
+#include "qapi/error.h"
+
+#define TPM_RC_FAILURE 0x101
+#define TPM2_ST_NO_SESSIONS 0x8001
+
+struct tpm_hdr {
+    uint16_t tag;
+    uint32_t len;
+    uint32_t code; /*ordinal/error */
+    char buffer[];
+} QEMU_PACKED;
+
+typedef struct TestState {
+    CompatGMutex data_mutex;
+    CompatGCond data_cond;
+    SocketAddress *addr;
+    QIOChannel *tpm_ioc;
+    GThread *emu_tpm_thread;
+    struct tpm_hdr *tpm_msg;
+} TestState;
+
+static void test_wait_cond(TestState *s)
+{
+    gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
+
+    g_mutex_lock(&s->data_mutex);
+    if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
+        g_assert_not_reached();
+    }
+    g_mutex_unlock(&s->data_mutex);
+}
+
+static void *emu_tpm_thread(void *data)
+{
+    TestState *s = data;
+    QIOChannel *ioc = s->tpm_ioc;
+
+    s->tpm_msg = g_new(struct tpm_hdr, 1);
+    while (true) {
+        int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len);
+
+        if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) {
+            break;
+        }
+        s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag);
+        s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len);
+        g_assert_cmpint(s->tpm_msg->len, >=, minhlen);
+        g_assert_cmpint(s->tpm_msg->tag, ==, TPM2_ST_NO_SESSIONS);
+
+        s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len);
+        qio_channel_read(ioc, (char *)&s->tpm_msg->code,
+                         s->tpm_msg->len - minhlen, &error_abort);
+        s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code);
+
+        /* reply error */
+        s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
+        s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr));
+        s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE);
+        qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len),
+                          &error_abort);
+    }
+
+    g_free(s->tpm_msg);
+    s->tpm_msg = NULL;
+    object_unref(OBJECT(s->tpm_ioc));
+    return NULL;
+}
+
+static void *emu_ctrl_thread(void *data)
+{
+    TestState *s = data;
+    QIOChannelSocket *lioc = qio_channel_socket_new();
+    QIOChannel *ioc;
+
+    qio_channel_socket_listen_sync(lioc, s->addr, &error_abort);
+    g_cond_signal(&s->data_cond);
+
+    qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN);
+    ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
+    g_assert(ioc);
+
+    {
+        uint32_t cmd = 0;
+        struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) };
+        int *pfd = NULL;
+        size_t nfd = 0;
+
+        qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort);
+        cmd = be32_to_cpu(cmd);
+        g_assert_cmpint(cmd, ==, CMD_SET_DATAFD);
+        g_assert_cmpint(nfd, ==, 1);
+        s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort));
+        g_free(pfd);
+
+        cmd = 0;
+        qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort);
+
+        s->emu_tpm_thread = g_thread_new(NULL, emu_tpm_thread, s);
+    }
+
+    while (true) {
+        uint32_t cmd;
+        ssize_t ret;
+
+        ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL);
+        if (ret <= 0) {
+            break;
+        }
+
+        cmd = be32_to_cpu(cmd);
+        switch (cmd) {
+        case CMD_GET_CAPABILITY: {
+            ptm_cap cap = cpu_to_be64(0x3fff);
+            qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort);
+            break;
+        }
+        case CMD_INIT: {
+            ptm_init init;
+            qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req),
+                              &error_abort);
+            init.u.resp.tpm_result = 0;
+            qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp),
+                              &error_abort);
+            break;
+        }
+        case CMD_SHUTDOWN: {
+            ptm_res res = 0;
+            qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort);
+            qio_channel_close(s->tpm_ioc, &error_abort);
+            g_thread_join(s->emu_tpm_thread);
+            break;
+        }
+        case CMD_STOP: {
+            ptm_res res = 0;
+            qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort);
+            break;
+        }
+        case CMD_SET_BUFFERSIZE: {
+            ptm_setbuffersize sbs;
+            qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req),
+                             &error_abort);
+            sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096);
+            sbs.u.resp.tpm_result = 0;
+            sbs.u.resp.minsize = cpu_to_be32(128);
+            sbs.u.resp.maxsize = cpu_to_be32(4096);
+            qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp),
+                              &error_abort);
+            break;
+        }
+        case CMD_SET_LOCALITY: {
+            ptm_loc loc;
+            /* Note: this time it's not u.req / u.resp... */
+            qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort);
+            g_assert_cmpint(loc.u.req.loc, ==, 0);
+            loc.u.resp.tpm_result = 0;
+            qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort);
+            break;
+        }
+        default:
+            g_debug("unimplemented %u", cmd);
+            g_assert_not_reached();
+        }
+    }
+
+    object_unref(OBJECT(ioc));
+    object_unref(OBJECT(lioc));
+    return NULL;
+}
+
+#define TPM_CMD "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"
+
+static void tpm_crb_test(const void *data)
+{
+    const TestState *s = data;
+    uint32_t intfid = readl(TPM_CRB_ADDR_BASE + A_CRB_INTF_ID);
+    uint32_t csize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE);
+    uint64_t caddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
+    uint32_t rsize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_SIZE);
+    uint64_t raddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
+
+    g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceType), ==, 1);
+    g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceVersion), ==, 1);
+    g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapLocality), ==, 0);
+    g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRBIdleBypass), ==, 0);
+    g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapDataXferSizeSupport),
+                    ==, 3);
+    g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapFIFO), ==, 0);
+    g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRB), ==, 1);
+    g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceSelector), ==, 1);
+    g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, RID), ==, 0);
+
+    g_assert_cmpint(csize, >=, 128);
+    g_assert_cmpint(rsize, >=, 128);
+    g_assert_cmpint(caddr, >, TPM_CRB_ADDR_BASE);
+    g_assert_cmpint(raddr, >, TPM_CRB_ADDR_BASE);
+
+    memwrite(caddr, TPM_CMD, sizeof(TPM_CMD));
+
+    uint32_t sts, start = 1;
+    uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
+    writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start);
+    do {
+        start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
+        if ((start & 1) == 0) {
+            break;
+        }
+    } while (g_get_monotonic_time() < end_time);
+    start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
+    g_assert_cmpint(start & 1, ==, 0);
+    sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
+    g_assert_cmpint(sts & 1, ==, 0);
+
+    struct tpm_hdr tpm_msg;
+    memread(raddr, &tpm_msg, sizeof(tpm_msg));
+    g_assert_cmpmem(&tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg));
+}
+
+int main(int argc, char **argv)
+{
+    int ret;
+    char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-crb-test.XXXXXX", NULL);
+    GThread *thread;
+    TestState test;
+
+    module_call_init(MODULE_INIT_QOM);
+    g_test_init(&argc, &argv, NULL);
+
+    test.addr = g_new0(SocketAddress, 1);
+    test.addr->type = SOCKET_ADDRESS_TYPE_UNIX;
+    test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL);
+    g_mutex_init(&test.data_mutex);
+    g_cond_init(&test.data_cond);
+
+    thread = g_thread_new(NULL, emu_ctrl_thread, &test);
+    test_wait_cond(&test);
+
+    args = g_strdup_printf(
+        "-chardev socket,id=chr,path=%s "
+        "-tpmdev emulator,id=dev,chardev=chr "
+        "-device tpm-crb,tpmdev=dev",
+        test.addr->u.q_unix.path);
+    qtest_start(args);
+
+    qtest_add_data_func("/tpm-crb/test", &test, tpm_crb_test);
+    ret = g_test_run();
+
+    qtest_end();
+
+    g_thread_join(thread);
+    g_unlink(test.addr->u.q_unix.path);
+    qapi_free_SocketAddress(test.addr);
+    g_rmdir(tmp_path);
+    g_free(tmp_path);
+    g_free(args);
+    return ret;
+}
-- 
2.5.5

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

* Re: [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26
  2018-01-26 18:49 [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26 Stefan Berger
                   ` (4 preceding siblings ...)
  2018-01-26 18:49 ` [Qemu-devel] [PULL 5/5] tpm: add CRB device Stefan Berger
@ 2018-01-29 11:37 ` Peter Maydell
  5 siblings, 0 replies; 7+ messages in thread
From: Peter Maydell @ 2018-01-29 11:37 UTC (permalink / raw)
  To: Stefan Berger; +Cc: QEMU Developers, Marc-André Lureau

On 26 January 2018 at 18:49, Stefan Berger <stefanb@linux.vnet.ibm.com> wrote:
> The patches in this pull request fix a bug in the initialization of
> a variable, adapt the cancel path used by the passthrough device to the
> sysfs path of more recent versions of the Linux kernel, and do some other
> cleanups preparing for support of migration. Finally, we add the CRB
> interface emulation, which is used with a TPM 2.
>
>    Stefan
>
> The following changes since commit 0f79bfe38a2cf0f43c7ea4959da7f8ebd7858f3d:
>
>   Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-2.12-pull-request' into staging (2018-01-25 09:53:53 +0000)
>
> are available in the git repository at:
>
>   git://github.com/stefanberger/qemu-tpm.git tags/pull-tpm-2018-01-26-1
>
> for you to fetch changes up to b510b21e072a0a9218f37051c885e95824d06bea:
>
>   tpm: add CRB device (2018-01-26 10:12:02 -0500)
>
> ----------------------------------------------------------------
> Merge tpm 2018/01/26 v1
>
> ----------------------------------------------------------------

Hi. The new tpm-crb-test fails on sparc host:

TEST: tests/tpm-crb-test... (pid=230409)
  /i386/tpm-crb/test:
Broken pipe
FAIL
GTester: last random seed: R02S29cea50247fe1efa59ee885a26d51a85
(pid=230423)
FAIL: tests/tpm-crb-test

and generates a new clang sanitizer runtime warning:

/home/petmay01/linaro/qemu-for-merges/hw/tpm/tpm_util.h:36:24: runtime
error: load of misaligned address 0x7fdc24c00002 for type 'const
uint32_t' (aka 'const unsigned int'), which requires 4 byte alignment
0x7fdc24c00002: note: pointer points here
<memory cannot be printed>

Chances are good these are the same thing, because the sparc architecture
does not allow misaligned loads and will segfault if you try them.
This function looks like it's the immediate culprit:

static inline uint32_t tpm_cmd_get_size(const void *b)
{
    return be32_to_cpu(*(const uint32_t *)(b + 2));
}

I suspect that this function should read
    return ldl_be_p(b + 2);

This is likely not the only problem with misaligned data in the
tpm code -- for instance the cast here in tpm_util_is_selftest()
looks odd:

bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len)
{
    struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;

As a general rule you can't take an arbitrary pointer into a byte
buffer and try to interpret it as a structure or a pointer to
a larger-than-bytesize-data simply by casting the pointer.
It might be worth reviewing all the tpm code for bugs
of this nature.

thanks
-- PMM

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

end of thread, other threads:[~2018-01-29 11:37 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-26 18:49 [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26 Stefan Berger
2018-01-26 18:49 ` [Qemu-devel] [PULL 1/5] tpm: Set the flags of the CMD_INIT command to 0 Stefan Berger
2018-01-26 18:49 ` [Qemu-devel] [PULL 2/5] tpm: lookup cancel path under tpm device class Stefan Berger
2018-01-26 18:49 ` [Qemu-devel] [PULL 3/5] tpm: replace GThreadPool with AIO threadpool Stefan Berger
2018-01-26 18:49 ` [Qemu-devel] [PULL 4/5] tpm: report backend request error Stefan Berger
2018-01-26 18:49 ` [Qemu-devel] [PULL 5/5] tpm: add CRB device Stefan Berger
2018-01-29 11:37 ` [Qemu-devel] [PULL 0/5] Merge tpm 2018/01/26 Peter Maydell

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.