All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration
@ 2011-12-14 13:43 Stefan Berger
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 1/7] Support for TPM command line options Stefan Berger
                   ` (7 more replies)
  0 siblings, 8 replies; 41+ messages in thread
From: Stefan Berger @ 2011-12-14 13:43 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

The following series of patches adds TPM (Trusted Platform Module) support
to Qemu. An emulator for the TIS (TPM Interface Spec) interface is
added that provides the basis for accessing a 'backend' implementing the actual
TPM functionality. The TIS emulator serves as a 'frontend' enabling for
example Linux's TPM TIS (tpm_tis) driver.

In this series I am posting a backend implementation that makes use of the
host's TPM through a passthrough driver, which on Linux is accessed
using /dev/tpm0.

v14:
 - applies to checkout of da5361c (Dec 12)
 - implemented Anthony Liguori's suggestions
 - dropping the version log on individual patches

v13:
 - applies to checkout of 61a5872 (Dec 12)
 - only allowing character devices as fd parameter
 - fixing error path in tpm_tis_init

v12:
 - applies to checkout of ebffe2a (Oct 11)
 - added documentation for fd parameter
 - nits

v11:
 - applies to checkout of 46f3069 (Sep 28)
 - some filing on the documentation
 - small nits fixed

v10:
 - applies to checkout of 1ce9ce6 (Sep 27)
 - addressed Michael Tsirkin's comments on v9

v9:
 - addressed Michael Tsirkin's and other reviewers' comments
 - only posting Andreas Niederl's passthrough driver as the backend driver

v8:
 - applies to checkout of f0fb8b7 (Aug 30)
 - fixing compilation error pointed out by Andreas Niederl
 - adding patch that allows to feed an initial state into the libtpms TPM
 - following memory API changes (glib) where necessary

v7:
 - applies to checkout of b9c6cbf (Aug 9)
 - measuring the modules if multiboot is used
 - coding style fixes

v6:
 - applies to checkout of 75ef849 (July 2nd)
 - some fixes and improvements to existing patches; see individual patches
 - added a patch with a null driver responding to all TPM requests with
   a response indicating failure; this backend has no dependencies and
   can alwayy be built;
 - added a patch to support the hashing of kernel, ramfs and command line
   if those were passed to Qemu using -kernel, -initrd and -append
   respectively. Measurements are taken, logged, and passed to SeaBIOS using
   the firmware interface.
 - libtpms revision 7 now requires 83kb of block storage due to having more
   NVRAM space

v5:
 - applies to checkout of 1fddfba1
 - adding support for split command line using the -tpmdev ... -device ...
   options while keeping the -tpm option
 - support for querying the device models using -tpm model=?
 - support for monitor 'info tpm'
 - adding documentation of command line options for man page and web page
 - increasing room for ACPI tables that qemu reserves to 128kb (from 64kb)
 - adding (experimental) support for block migration
 - adding (experimental) support for taking measurements when kernel,
   initrd and kernel command line are directly passed to Qemu

v4:
 - applies to checkout of d2d979c6
 - more coding style fixes
 - adding patch for supporting blob encryption (in addition to the existing
   QCoW2-level encryption)
   - this allows for graceful termination of a migration if the target
     is detected to have a wrong key
   - tested with big and little endian hosts
 - main thread releases mutex while checking for work to do on behalf of
   backend
 - introducing file locking (fcntl) on the block layer for serializing access
   to shared (QCoW2) files (used during migration)

v3:
 - Building a null driver at patch 5/8 that responds to all requests
   with an error response; subsequently this driver is transformed to the
   libtpms-based driver for real TPM functionality
 - Reworked the threading; dropped the patch for qemu_thread_join; the
   main thread synchronizing with the TPM thread termination may need
   to write data to the block storage while waiting for the thread to 
   terminate; did not previously show a problem but is safer
 - A lot of testing based on recent git checkout 4b4a72e5 (4/10):
   - migration of i686 VM from x86_64 host to i686 host to ppc64 host while
     running tests inside the VM
   - tests with S3 suspend/resume
   - tests with snapshots
   - multiple-hour tests with VM suspend/resume (using virsh save/restore)
     while running a TPM test suite inside the VM
   All tests passed; [not all of them were done on the ppc64 host]

v2:
 - splitting some of the patches into smaller ones for easier review
 - fixes in individual patches

Regards,
    Stefan


Stefan Berger (7):
  Support for TPM command line options
  Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  Add a debug register
  Build the TPM frontend code
  Add a TPM Passthrough backend driver implementation
  Introduce --enable-tpm-passthrough configure option
  Add fd parameter for TPM passthrough driver

 Makefile.target      |    3 +
 configure            |   28 ++
 hmp-commands.hx      |    2 +
 hmp.c                |   28 ++
 hmp.h                |    1 +
 hw/tpm_passthrough.c |  528 ++++++++++++++++++++++++++++++
 hw/tpm_tis.c         |  877 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/tpm_tis.h         |   80 +++++
 monitor.c            |    8 +
 qapi-schema.json     |   29 ++
 qemu-config.c        |   25 ++
 qemu-options.hx      |   67 ++++
 tpm.c                |  230 +++++++++++++
 tpm.h                |  119 +++++++
 vl.c                 |   20 ++
 15 files changed, 2045 insertions(+), 0 deletions(-)
 create mode 100644 hw/tpm_passthrough.c
 create mode 100644 hw/tpm_tis.c
 create mode 100644 hw/tpm_tis.h
 create mode 100644 tpm.c
 create mode 100644 tpm.h

-- 
1.7.6.4

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

* [Qemu-devel] [PATCH V14 1/7] Support for TPM command line options
  2011-12-14 13:43 [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
@ 2011-12-14 13:43 ` Stefan Berger
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2011-12-14 13:43 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

This patch adds support for TPM command line options.
The command line options supported here are

./qemu-... -tpmdev passthrough,path=<path to TPM device>,id=<id>
           -device tpm-tis,tpmdev=<id>

and

./qemu-... -tpmdev ?

where the latter works similar to -soundhw ? and shows a list of
available TPM backends (for example 'passthrough').

Using the type parameter, the backend is chosen, i.e., 'passthrough' for the
passthrough driver. The interpretation of the other parameters along
with determining whether enough parameters were provided is pushed into
the backend driver, which needs to implement the interface function
'create' and return a TPMDriver structure if the VM can be started or 'NULL'
if not enough or bad parameters were provided.

Monitor support for 'info tpm' has been added. It for example prints the
following:

(qemu) info tpm
TPM devices:
 tpm0: model=tpm-tis
  \ tpm0: type=passthrough,path=/dev/tpm0

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 hmp-commands.hx  |    2 +
 hmp.c            |   28 +++++++
 hmp.h            |    1 +
 hw/tpm_tis.h     |   80 ++++++++++++++++++++
 monitor.c        |    8 ++
 qapi-schema.json |   29 +++++++
 qemu-config.c    |   20 +++++
 qemu-options.hx  |   30 ++++++++
 tpm.c            |  214 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tpm.h            |   86 ++++++++++++++++++++++
 vl.c             |   20 +++++
 11 files changed, 518 insertions(+), 0 deletions(-)
 create mode 100644 hw/tpm_tis.h
 create mode 100644 tpm.c
 create mode 100644 tpm.h

diff --git a/hmp-commands.hx b/hmp-commands.hx
index fdbed15..4506f56 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1352,6 +1352,8 @@ show device tree
 show qdev device model list
 @item info roms
 show roms
+@item info tpm
+show the TPM device
 @end table
 ETEXI
 
diff --git a/hmp.c b/hmp.c
index e7659d5..8865e41 100644
--- a/hmp.c
+++ b/hmp.c
@@ -507,6 +507,34 @@ void hmp_info_pci(Monitor *mon)
     qapi_free_PciInfoList(info);
 }
 
+void hmp_info_tpm(Monitor *mon)
+{
+    TPMInfoList *info_list, *info;
+    Error *err = NULL;
+    unsigned int c = 0;
+
+    info_list = qmp_query_tpm(&err);
+    if (err) {
+        monitor_printf(mon, "TPM device not supported\n");
+        error_free(err);
+        return;
+    }
+
+    monitor_printf(mon, "TPM device:\n");
+
+    for (info = info_list; info; info = info->next) {
+        TPMInfo *ti = info->value;
+        monitor_printf(mon, " tpm%d: model=%s\n",
+                       c, ti->model);
+        monitor_printf(mon, "  \\ %s: type=%s%s%s\n",
+                       ti->id, ti->type,
+                       ti->parameters ? "," : "",
+                       ti->parameters ? ti->parameters : "");
+        c++;
+    }
+    qapi_free_TPMInfoList(info_list);
+}
+
 void hmp_quit(Monitor *mon, const QDict *qdict)
 {
     monitor_suspend(mon);
diff --git a/hmp.h b/hmp.h
index 093242d..73be269 100644
--- a/hmp.h
+++ b/hmp.h
@@ -32,6 +32,7 @@ void hmp_info_vnc(Monitor *mon);
 void hmp_info_spice(Monitor *mon);
 void hmp_info_balloon(Monitor *mon);
 void hmp_info_pci(Monitor *mon);
+void hmp_info_tpm(Monitor *mon);
 void hmp_quit(Monitor *mon, const QDict *qdict);
 void hmp_stop(Monitor *mon, const QDict *qdict);
 void hmp_system_reset(Monitor *mon, const QDict *qdict);
diff --git a/hw/tpm_tis.h b/hw/tpm_tis.h
new file mode 100644
index 0000000..1270242
--- /dev/null
+++ b/hw/tpm_tis.h
@@ -0,0 +1,80 @@
+/*
+ * tpm_tis.c - QEMU's TPM TIS interface emulator
+ *
+ * Copyright (C) 2006,2010,2011 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger <stefanb@us.ibm.com>
+ *  David Safford <safford@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ *
+ * Implementation of the TIS interface according to specs found at
+ * http://www.trustedcomputiggroup.org
+ *
+ */
+#ifndef HW_TPM_TIS_H
+#define HW_TPM_TIS_H
+
+#include "isa.h"
+#include "qemu-thread.h"
+#include "qemu-common.h"
+
+#define TPM_TIS_ADDR_BASE           0xFED40000
+
+#define TPM_TIS_NUM_LOCALITIES      5     /* per spec */
+#define TPM_TIS_NO_LOCALITY         0xff
+
+#define TPM_TIS_IS_VALID_LOCTY(x)   ((x) < TPM_TIS_NUM_LOCALITIES)
+
+#define TPM_TIS_IRQ                 5
+
+#define TPM_TIS_BUFFER_MAX          4096
+
+
+typedef struct TPMSizedBuffer {
+    uint32_t size;
+    uint8_t  *buffer;
+} TPMSizedBuffer;
+
+typedef enum {
+    TPM_TIS_STATUS_IDLE = 0,
+    TPM_TIS_STATUS_READY,
+    TPM_TIS_STATUS_COMPLETION,
+    TPM_TIS_STATUS_EXECUTION,
+    TPM_TIS_STATUS_RECEPTION,
+} TPMTISStatus;
+
+/* locality data  -- all fields are persisted */
+typedef struct TPMLocality {
+    TPMTISStatus status;
+    uint8_t access;
+    uint8_t sts;
+    uint32_t inte;
+    uint32_t ints;
+
+    uint16_t w_offset;
+    uint16_t r_offset;
+    TPMSizedBuffer w_buffer;
+    TPMSizedBuffer r_buffer;
+} TPMLocality;
+
+typedef struct TPMTISState {
+    uint32_t offset;
+    uint8_t buf[TPM_TIS_BUFFER_MAX];
+
+    uint8_t active_locty;
+    uint8_t aborting_locty;
+    uint8_t next_locty;
+
+    TPMLocality loc[TPM_TIS_NUM_LOCALITIES];
+
+    qemu_irq irq;
+    uint32_t irq_num;
+} TPMTISState;
+
+#endif /* HW_TPM_TIS_H */
diff --git a/monitor.c b/monitor.c
index 7334401..50693a3 100644
--- a/monitor.c
+++ b/monitor.c
@@ -47,6 +47,7 @@
 #include "migration.h"
 #include "kvm.h"
 #include "acl.h"
+#include "tpm.h"
 #include "qint.h"
 #include "qfloat.h"
 #include "qlist.h"
@@ -2736,6 +2737,13 @@ static mon_cmd_t info_cmds[] = {
         .mhandler.info = do_trace_print_events,
     },
     {
+        .name       = "tpm",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show the TPM device",
+        .mhandler.info = hmp_info_tpm,
+    },
+    {
         .name       = NULL,
     },
 };
diff --git a/qapi-schema.json b/qapi-schema.json
index f358b49..0bfc678 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1168,3 +1168,32 @@
 # Since: 0.14.0
 ##
 { 'command': 'migrate_set_speed', 'data': {'value': 'int'} }
+
+##
+# @TPMInfo:
+#
+# Information about the TPM
+#
+# @model: The TPM frontend model, i.e., tpm-tis
+#
+# @id: The ID of the TPM
+#
+# @type: The type of TPM backend, i.e., passthrough
+#
+# @parameters: Additional parameters of the TPM backend device
+#
+# Since: 1.1
+##
+{ 'type': 'TPMInfo',
+  'data': {'model': 'str', 'id': 'str', 'type': 'str', 'parameters': 'str' } }
+
+##
+# @query-tpm
+#
+# Return information about the TPM device.
+#
+# Returns: @TPMInfo on success
+#
+# Since: 1.1
+##
+{ 'command': 'query-tpm', 'returns': ['TPMInfo'] }
diff --git a/qemu-config.c b/qemu-config.c
index 18f3020..cc4c31d 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -549,6 +549,25 @@ QemuOptsList qemu_boot_opts = {
     },
 };
 
+static QemuOptsList qemu_tpmdev_opts = {
+    .name = "tpmdev",
+    .implied_opt_name = "type",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpmdev_opts.head),
+    .desc = {
+        {
+            .name = "type",
+            .type = QEMU_OPT_STRING,
+            .help = "Type of TPM backend",
+        },
+        {
+            .name = "path",
+            .type = QEMU_OPT_STRING,
+            .help = "Persistent storage for TPM state",
+        },
+        { /* end of list */ }
+    },
+};
+
 static QemuOptsList *vm_config_groups[32] = {
     &qemu_drive_opts,
     &qemu_chardev_opts,
@@ -563,6 +582,7 @@ static QemuOptsList *vm_config_groups[32] = {
     &qemu_option_rom_opts,
     &qemu_machine_opts,
     &qemu_boot_opts,
+    &qemu_tpmdev_opts,
     NULL,
 };
 
diff --git a/qemu-options.hx b/qemu-options.hx
index b3db10c..40a63b1 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1903,6 +1903,36 @@ ETEXI
 
 DEFHEADING()
 
+DEFHEADING(TPM device options:)
+
+DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
+    "-tpmdev [<type>],id=str[,option][,option][,...]\n",
+    QEMU_ARCH_ALL)
+STEXI
+
+The general form of a TPM device option is:
+@table @option
+
+@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
+@findex -tpmdev
+Backend type must be:
+
+The specific backend type will determine the applicable options.
+The @code{-tpmdev} options requires a @code{-device} option.
+
+Options to each backend are described below.
+
+Use ? to print all available TPM backend types.
+@example
+qemu -tpmdev ?
+@end example
+
+@end table
+
+ETEXI
+
+DEFHEADING()
+
 DEFHEADING(Linux/Multiboot boot specific:)
 STEXI
 
diff --git a/tpm.c b/tpm.c
new file mode 100644
index 0000000..408d319
--- /dev/null
+++ b/tpm.c
@@ -0,0 +1,214 @@
+/*
+ * TPM configuration
+ *
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger    <stefanb@us.ibm.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.
+ *
+ * Based on net.c
+ */
+#include "config.h"
+
+#include "monitor.h"
+#include "qerror.h"
+#include "tpm.h"
+#include "qmp-commands.h"
+
+static QLIST_HEAD(, TPMBackend) tpm_backends =
+    QLIST_HEAD_INITIALIZER(tpm_backends);
+
+#ifdef CONFIG_TPM
+
+static const TPMDriverOps *bes[] = {
+    NULL,
+};
+
+const TPMDriverOps *tpm_get_backend_driver(const char *id)
+{
+    int i;
+
+    for (i = 0; bes[i] != NULL; i++) {
+        if (!strcmp(bes[i]->id, id)) {
+            break;
+        }
+    }
+
+    return bes[i];
+}
+
+/* Walk the list of available TPM backend drivers and display them on the
+ * screen.
+ */
+void tpm_display_backend_drivers(void)
+{
+    int i;
+
+    fprintf(stderr, "Supported TPM types (choose only one):\n");
+
+    for (i = 0; bes[i] != NULL; i++) {
+        fprintf(stderr, "%12s   %s\n", bes[i]->id, bes[i]->desc());
+    }
+    fprintf(stderr, "\n");
+}
+
+/* Find the TPM with the given Id
+ */
+TPMBackend *qemu_find_tpm(const char *id)
+{
+    TPMBackend *drv;
+
+    QLIST_FOREACH(drv, &tpm_backends, list) {
+        if (!strcmp(drv->id, id)) {
+            return drv;
+        }
+    }
+
+    return NULL;
+}
+
+static int configure_tpm(QemuOpts *opts)
+{
+    const char *value;
+    const char *id;
+    const TPMDriverOps *be;
+    TPMBackend *drv;
+
+    if (!QLIST_EMPTY(&tpm_backends)) {
+        error_report("Only one TPM is allowed.\n");
+        return 1;
+    }
+
+    id = qemu_opts_id(opts);
+    if (id == NULL) {
+        qerror_report(QERR_MISSING_PARAMETER, "id");
+        return 1;
+    }
+
+    value = qemu_opt_get(opts, "type");
+    if (!value) {
+        qerror_report(QERR_MISSING_PARAMETER, "type");
+        tpm_display_backend_drivers();
+        return 1;
+    }
+
+    be = tpm_get_backend_driver(value);
+    if (be == NULL) {
+        qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
+                      "a tpm backend type");
+        tpm_display_backend_drivers();
+        return 1;
+    }
+
+    drv = be->create(opts, id);
+    if (!drv) {
+        return 1;
+    }
+
+    QLIST_INSERT_HEAD(&tpm_backends, drv, list);
+
+    return 0;
+}
+
+static int tpm_init_tpmdev(QemuOpts *opts, void *dummy)
+{
+    return configure_tpm(opts);
+}
+
+/* Walk the list of TPM backend drivers that are in use and call their
+ * destroy function to have them cleaned up.
+ */
+void tpm_cleanup(void)
+{
+    TPMBackend *drv, *next;
+
+    QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
+        QLIST_REMOVE(drv, list);
+        drv->ops->destroy(drv);
+    }
+}
+
+/* Initialize the TPM. Process the tpmdev command line options describing the
+ * TPM backend.
+ */
+int tpm_init(void)
+{
+    if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
+                          tpm_init_tpmdev, NULL, 1) != 0) {
+        return -1;
+    }
+
+    atexit(tpm_cleanup);
+
+    return 0;
+}
+
+/* Parse the TPM configuration options.
+ * It is possible to pass an option '-tpmdev none' to not activate any TPM.
+ * To display all available TPM backends the user may use '-tpmdev ?'
+ */
+int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
+{
+    QemuOpts *opts;
+
+    if (strcmp("none", optarg) != 0) {
+        if (*optarg == '?') {
+            tpm_display_backend_drivers();
+            return -1;
+        }
+        opts = qemu_opts_parse(opts_list, optarg, 1);
+        if (!opts) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+#endif /* CONFIG_TPM */
+
+static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
+{
+    TPMInfo *res = g_malloc0(sizeof(*res));
+
+    if (res) {
+        res->model = g_strdup(drv->fe_model);
+        res->id = g_strdup(drv->id);
+        if (drv->parameters) {
+            res->parameters = g_strdup(drv->parameters);
+        }
+        res->type = g_strdup(drv->ops->id);
+    }
+
+    return res;
+}
+
+/* Walk the list of active TPM backends and collect information about them
+ * following the schema description in qapi-schema.json.
+ */
+TPMInfoList *qmp_query_tpm(Error **errp)
+{
+    TPMBackend *drv;
+    TPMInfoList *info, *head = NULL, *cur_item = NULL;
+
+    QLIST_FOREACH(drv, &tpm_backends, list) {
+        info = g_malloc0(sizeof(*info));
+
+        if (!info) {
+            break;
+        }
+
+        info->value = qmp_query_tpm_inst(drv);
+
+        if (!cur_item) {
+            head = cur_item = info;
+        } else {
+            cur_item->next = info;
+            cur_item = info;
+        }
+    }
+
+    return head;
+}
diff --git a/tpm.h b/tpm.h
new file mode 100644
index 0000000..075de3f
--- /dev/null
+++ b/tpm.h
@@ -0,0 +1,86 @@
+/*
+ * TPM configuration
+ *
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger    <stefanb@us.ibm.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.
+ */
+#ifndef QEMU_TPM_H
+#define QEMU_TPM_H
+
+#include "memory.h"
+#include "hw/tpm_tis.h"
+
+struct TPMDriverOps;
+typedef struct TPMDriverOps TPMDriverOps;
+
+typedef struct TPMBackend {
+    char *id;
+    const char *fe_model;
+    char *parameters;
+    const TPMDriverOps *ops;
+
+    QLIST_ENTRY(TPMBackend) list;
+} TPMBackend;
+
+/* overall state of the TPM interface */
+typedef struct TPMState {
+    ISADevice busdev;
+    MemoryRegion mmio;
+
+    union {
+        TPMTISState tis;
+    } s;
+
+    uint8_t     command_locty;
+    TPMLocality *cmd_locty;
+
+    QemuMutex state_lock;
+    QemuCond  from_tpm_cond;
+    QemuCond  to_tpm_cond;
+    bool      to_tpm_execute;
+
+    bool      tpm_initialized;
+
+    char *backend;
+    TPMBackend *be_driver;
+} TPMState;
+
+typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty);
+
+struct TPMDriverOps {
+    const char *id;
+    /* get a descriptive text of the backend to display to the user */
+    const char *(*desc)(void);
+
+    TPMBackend *(*create)(QemuOpts *opts, const char *id);
+    void (*destroy)(TPMBackend *t);
+
+    /* initialize the backend */
+    int (*init)(TPMBackend *t, TPMState *s, TPMRecvDataCB *datacb);
+    /* 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);
+
+    void (*reset)(TPMBackend *t);
+
+    bool (*get_tpm_established_flag)(TPMBackend *t);
+};
+
+#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
+
+int tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
+int tpm_init(void);
+void tpm_cleanup(void);
+TPMBackend *qemu_find_tpm(const char *id);
+void tpm_display_backend_drivers(void);
+const TPMDriverOps *tpm_get_backend_driver(const char *id);
+
+#endif /* QEMU_TPM_H */
diff --git a/vl.c b/vl.c
index 5372a96..2c8ca80 100644
--- a/vl.c
+++ b/vl.c
@@ -139,6 +139,7 @@ int main(int argc, char **argv)
 #include "block.h"
 #include "blockdev.h"
 #include "block-migration.h"
+#include "tpm.h"
 #include "dma.h"
 #include "audio/audio.h"
 #include "migration.h"
@@ -2550,6 +2551,16 @@ int main(int argc, char **argv, char **envp)
                 ram_size = value;
                 break;
             }
+            case QEMU_OPTION_tpmdev:
+#ifdef CONFIG_TPM
+                if (tpm_config_parse(qemu_find_opts("tpmdev"), optarg) < 0) {
+                    exit(1);
+                }
+#else
+                fprintf(stderr, "TPM support is disabled\n");
+                exit(1);
+#endif
+                break;
             case QEMU_OPTION_mempath:
                 mem_path = optarg;
                 break;
@@ -3243,6 +3254,12 @@ int main(int argc, char **argv, char **envp)
         exit(1);
     }
 
+#ifdef CONFIG_TPM
+    if (tpm_init() < 0) {
+        exit(1);
+    }
+#endif
+
     /* init the bluetooth world */
     if (foreach_device_config(DEV_BT, bt_parse))
         exit(1);
@@ -3487,6 +3504,9 @@ int main(int argc, char **argv, char **envp)
     pause_all_vcpus();
     net_cleanup();
     res_free();
+#ifdef CONFIG_TPM
+    tpm_cleanup();
+#endif
 
     return 0;
 }
-- 
1.7.6.4

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

* [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-12-14 13:43 [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 1/7] Support for TPM command line options Stefan Berger
@ 2011-12-14 13:43 ` Stefan Berger
  2012-02-20  8:51   ` Michael S. Tsirkin
  2012-02-20 22:02   ` Michael S. Tsirkin
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 3/7] Add a debug register Stefan Berger
                   ` (5 subsequent siblings)
  7 siblings, 2 replies; 41+ messages in thread
From: Stefan Berger @ 2011-12-14 13:43 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

This patch adds the main code of the TPM frontend driver, the TPM TIS
interface, to Qemu. The code is largely based on the previous implementation
for Xen but has been significantly extended to meet the standard's
requirements, such as the support for changing of localities and all the
functionality of the available flags.

Communication with the backend (i.e., for Xen or the libtpms-based one)
is cleanly separated through an interface which the backend driver needs
to implement.

The TPM TIS driver's backend was previously chosen in the code added
to arch_init. The frontend holds a pointer to the chosen backend (interface).

Communication with the backend is largely based on signals and conditions.
Whenever the frontend has collected a complete packet, it will signal
the backend, which then starts processing the command. Once the result
has been returned, the backend invokes a callback function
(tis_tpm_receive_cb()).

The one tricky part is support for VM suspend while the TPM is processing
a command. In this case the frontend driver is waiting for the backend
to return the result of the last command before shutting down. It waits
on a condition for a signal from the backend, which is delivered in
tis_tpm_receive_cb().

Testing the proper functioning of the different flags and localities
cannot be done from user space when running in Linux for example, since
access to the address space of the TPM TIS interface is not possible. Also
the Linux driver itself does not exercise all functionality. So, for
testing there is a fairly extensive test suite as part of the SeaBIOS patches
since from within the BIOS one can have full access to all the TPM's registers.


Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 hw/tpm_tis.c |  807 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 807 insertions(+), 0 deletions(-)
 create mode 100644 hw/tpm_tis.c

diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
new file mode 100644
index 0000000..bcbc987
--- /dev/null
+++ b/hw/tpm_tis.c
@@ -0,0 +1,807 @@
+/*
+ * tpm_tis.c - QEMU's TPM TIS interface emulator
+ *
+ * Copyright (C) 2006,2010,2011 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger <stefanb@us.ibm.com>
+ *  David Safford <safford@us.ibm.com>
+ *
+ * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ *
+ * Implementation of the TIS interface according to specs found at
+ * http://www.trustedcomputiggroup.org.
+ * In the developers menu choose the PC Client section then find the TIS
+ * specification.
+ */
+
+#include "tpm.h"
+#include "block.h"
+#include "exec-memory.h"
+#include "hw/hw.h"
+#include "hw/pc.h"
+#include "hw/tpm_tis.h"
+#include "qemu-error.h"
+#include "qemu-common.h"
+
+/*#define DEBUG_TIS */
+
+#ifdef DEBUG_TIS
+#define dprintf(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+    do { } while (0)
+#endif
+
+/* whether the STS interrupt is supported */
+/*#define RAISE_STS_IRQ */
+
+/* tis registers */
+#define TPM_TIS_REG_ACCESS                0x00
+#define TPM_TIS_REG_INT_ENABLE            0x08
+#define TPM_TIS_REG_INT_VECTOR            0x0c
+#define TPM_TIS_REG_INT_STATUS            0x10
+#define TPM_TIS_REG_INTF_CAPABILITY       0x14
+#define TPM_TIS_REG_STS                   0x18
+#define TPM_TIS_REG_DATA_FIFO             0x24
+#define TPM_TIS_REG_DID_VID               0xf00
+#define TPM_TIS_REG_RID                   0xf04
+
+#define TPM_TIS_STS_VALID                 (1 << 7)
+#define TPM_TIS_STS_COMMAND_READY         (1 << 6)
+#define TPM_TIS_STS_TPM_GO                (1 << 5)
+#define TPM_TIS_STS_DATA_AVAILABLE        (1 << 4)
+#define TPM_TIS_STS_EXPECT                (1 << 3)
+#define TPM_TIS_STS_RESPONSE_RETRY        (1 << 1)
+
+#define TPM_TIS_ACCESS_TPM_REG_VALID_STS  (1 << 7)
+#define TPM_TIS_ACCESS_ACTIVE_LOCALITY    (1 << 5)
+#define TPM_TIS_ACCESS_BEEN_SEIZED        (1 << 4)
+#define TPM_TIS_ACCESS_SEIZE              (1 << 3)
+#define TPM_TIS_ACCESS_PENDING_REQUEST    (1 << 2)
+#define TPM_TIS_ACCESS_REQUEST_USE        (1 << 1)
+#define TPM_TIS_ACCESS_TPM_ESTABLISHMENT  (1 << 0)
+
+#define TPM_TIS_INT_ENABLED               (1 << 31)
+#define TPM_TIS_INT_DATA_AVAILABLE        (1 << 0)
+#define TPM_TIS_INT_STS_VALID             (1 << 1)
+#define TPM_TIS_INT_LOCALITY_CHANGED      (1 << 2)
+#define TPM_TIS_INT_COMMAND_READY         (1 << 7)
+
+#ifndef RAISE_STS_IRQ
+
+# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
+                                       TPM_TIS_INT_DATA_AVAILABLE   | \
+                                       TPM_TIS_INT_COMMAND_READY)
+
+#else
+
+# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
+                                       TPM_TIS_INT_DATA_AVAILABLE   | \
+                                       TPM_TIS_INT_STS_VALID | \
+                                       TPM_TIS_INT_COMMAND_READY)
+
+#endif
+
+#define TPM_TIS_CAPABILITIES_SUPPORTED   ((1 << 4) | \
+                                          TPM_TIS_INTERRUPTS_SUPPORTED)
+
+#define TPM_TIS_TPM_DID       0x0001
+#define TPM_TIS_TPM_VID       0x0001
+#define TPM_TIS_TPM_RID       0x0001
+
+#define TPM_TIS_NO_DATA_BYTE  0xff
+
+/* utility functions */
+
+static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
+{
+    return (uint8_t)((addr >> 12) & 0x7);
+}
+
+static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+    return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
+}
+
+static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
+{
+#ifdef DEBUG_TIS
+    uint32_t len, i;
+
+    len = tpm_tis_get_size_from_buffer(sb);
+    dprintf("tpm_tis: %s length = %d\n", string, len);
+    for (i = 0; i < len; i++) {
+        if (i && !(i & 16)) {
+            dprintf("\n");
+        }
+        dprintf("%.2X ", sb->buffer[i]);
+    }
+    dprintf("\n");
+#endif
+}
+
+
+/*
+ * Send a TPM request.
+ * Call this with the state_lock held so we can sync with the receive
+ * callback.
+ */
+static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
+{
+    TPMTISState *tis = &s->s.tis;
+
+    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
+
+    s->command_locty = locty;
+    s->cmd_locty     = &tis->loc[locty];
+
+    /* w_offset serves as length indicator for length of data;
+       it's reset when the response comes back */
+    tis->loc[locty].status = TPM_TIS_STATUS_EXECUTION;
+    tis->loc[locty].sts &= ~TPM_TIS_STS_EXPECT;
+
+    s->to_tpm_execute = true;
+    qemu_cond_signal(&s->to_tpm_cond);
+}
+
+/* raise an interrupt if allowed */
+static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
+{
+    TPMTISState *tis = &s->s.tis;
+
+    if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
+        return;
+    }
+
+    if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
+        (tis->loc[locty].inte & irqmask)) {
+        dprintf("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
+        qemu_irq_raise(s->s.tis.irq);
+        tis->loc[locty].ints |= irqmask;
+    }
+}
+
+static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
+{
+    uint8_t l;
+
+    for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
+        if (l == locty) {
+            continue;
+        }
+        if ((s->s.tis.loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
+{
+    TPMTISState *tis = &s->s.tis;
+    int change = (s->s.tis.active_locty != new_active_locty);
+
+    if (change && TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) {
+        /* reset flags on the old active locality */
+        tis->loc[s->s.tis.active_locty].access &=
+                 ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|TPM_TIS_ACCESS_REQUEST_USE);
+        if (TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
+            tis->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE) {
+            tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
+        }
+    }
+
+    tis->active_locty = new_active_locty;
+
+    dprintf("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty);
+
+    if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
+        /* set flags on the new active locality */
+        tis->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
+        tis->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
+                                               TPM_TIS_ACCESS_SEIZE);
+    }
+
+    if (change) {
+        tpm_tis_raise_irq(s, tis->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
+    }
+}
+
+/* abort -- this function switches the locality */
+static void tpm_tis_abort(TPMState *s, uint8_t locty)
+{
+    TPMTISState *tis = &s->s.tis;
+
+    tis->loc[locty].r_offset = 0;
+    tis->loc[locty].w_offset = 0;
+
+    dprintf("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty);
+
+    /*
+     * Need to react differently depending on who's aborting now and
+     * which locality will become active afterwards.
+     */
+    if (tis->aborting_locty == tis->next_locty) {
+        tis->loc[tis->aborting_locty].status = TPM_TIS_STATUS_READY;
+        tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_READY;
+        tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY);
+    }
+
+    /* locality after abort is another one than the current one */
+    tpm_tis_new_active_locality(s, tis->next_locty);
+
+    tis->next_locty = TPM_TIS_NO_LOCALITY;
+    /* nobody's aborting a command anymore */
+    tis->aborting_locty = TPM_TIS_NO_LOCALITY;
+}
+
+/* prepare aborting current command */
+static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
+{
+    TPMTISState *tis = &s->s.tis;
+    uint8_t busy_locty;
+
+    tis->aborting_locty = locty;
+    tis->next_locty = newlocty;  /* locality after successful abort */
+
+    /*
+     * only abort a command using an interrupt if currently executing
+     * a command AND if there's a valid connection to the vTPM.
+     */
+    for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
+        if (tis->loc[busy_locty].status == TPM_TIS_STATUS_EXECUTION) {
+            /* there is currently no way to interrupt the TPM's operations
+               while it's executing a command; once the TPM is done and
+               returns the buffer, it will switch to the next_locty; */
+            dprintf("tpm_tis: Locality %d is busy - deferring abort\n",
+                    busy_locty);
+            return;
+        }
+    }
+
+    tpm_tis_abort(s, locty);
+}
+
+/*
+ * Callback from the TPM to indicate that the response was received.
+ */
+static void tpm_tis_receive_cb(TPMState *s, uint8_t locty)
+{
+    TPMTISState *tis = &s->s.tis;
+
+    qemu_mutex_lock(&s->state_lock);
+
+    tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE;
+    tis->loc[locty].status = TPM_TIS_STATUS_COMPLETION;
+    tis->loc[locty].r_offset = 0;
+    tis->loc[locty].w_offset = 0;
+
+    if (TPM_TIS_IS_VALID_LOCTY(tis->next_locty)) {
+        tpm_tis_abort(s, locty);
+    }
+
+    qemu_cond_signal(&s->from_tpm_cond);
+
+    qemu_mutex_unlock(&s->state_lock);
+
+#ifndef RAISE_STS_IRQ
+    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_DATA_AVAILABLE);
+#else
+    tpm_tis_raise_irq(s, locty,
+                      TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
+#endif
+}
+
+/*
+ * read a byte of response data
+ * call this with s->state_lock held
+ */
+static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
+{
+    TPMTISState *tis = &s->s.tis;
+    uint32_t ret = TPM_TIS_NO_DATA_BYTE;
+    uint16_t len;
+
+    if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
+        len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
+
+        ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++];
+        if (tis->loc[locty].r_offset >= len) {
+            /* got last byte */
+            tis->loc[locty].sts = TPM_TIS_STS_VALID;
+#ifdef RAISE_STS_IRQ
+            tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
+#endif
+        }
+        dprintf("tpm_tis: tpm_tis_data_read byte 0x%02x   [%d]\n",
+                ret, tis->loc[locty].r_offset-1);
+    }
+
+    return ret;
+}
+
+/*
+ * Read a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
+                                  unsigned size)
+{
+    TPMState *s = opaque;
+    TPMTISState *tis = &s->s.tis;
+    uint16_t offset = addr & 0xffc;
+    uint8_t shift = (addr & 0x3) * 8;
+    uint32_t val = 0xffffffff;
+    uint8_t locty = tpm_tis_locality_from_addr(addr);
+
+    qemu_mutex_lock(&s->state_lock);
+
+    if (s->be_driver->ops->had_startup_error(s->be_driver)) {
+        qemu_mutex_unlock(&s->state_lock);
+        return val;
+    }
+
+    switch (offset) {
+    case TPM_TIS_REG_ACCESS:
+        /* never show the SEIZE flag even though we use it internally */
+        val = tis->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
+        /* the pending flag is alawys calculated */
+        if (tpm_tis_check_request_use_except(s, locty)) {
+            val |= TPM_TIS_ACCESS_PENDING_REQUEST;
+        }
+        val |= !s->be_driver->ops->get_tpm_established_flag(s->be_driver);
+        break;
+    case TPM_TIS_REG_INT_ENABLE:
+        val = tis->loc[locty].inte;
+        break;
+    case TPM_TIS_REG_INT_VECTOR:
+        val = tis->irq_num;
+        break;
+    case TPM_TIS_REG_INT_STATUS:
+        val = tis->loc[locty].ints;
+        break;
+    case TPM_TIS_REG_INTF_CAPABILITY:
+        val = TPM_TIS_CAPABILITIES_SUPPORTED;
+        break;
+    case TPM_TIS_REG_STS:
+        if (tis->active_locty == locty) {
+            if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
+                val =  (tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer)
+                        - tis->loc[locty].r_offset) << 8 | tis->loc[locty].sts;
+            } else {
+                val = (tis->loc[locty].w_buffer.size -
+                       tis->loc[locty].w_offset) << 8 | tis->loc[locty].sts;
+            }
+        }
+        break;
+    case TPM_TIS_REG_DATA_FIFO:
+        if (tis->active_locty == locty) {
+            switch (tis->loc[locty].status) {
+            case TPM_TIS_STATUS_COMPLETION:
+                val = tpm_tis_data_read(s, locty);
+                break;
+            default:
+                val = TPM_TIS_NO_DATA_BYTE;
+                break;
+            }
+        }
+        break;
+    case TPM_TIS_REG_DID_VID:
+        val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
+        break;
+    case TPM_TIS_REG_RID:
+        val = TPM_TIS_TPM_RID;
+        break;
+    }
+
+    qemu_mutex_unlock(&s->state_lock);
+
+    if (shift) {
+        val >>= shift;
+    }
+
+    dprintf("tpm_tis:  read.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
+
+    return val;
+}
+
+/*
+ * Write a value to a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static void tpm_tis_mmio_write_intern(void *opaque, target_phys_addr_t addr,
+                                      uint64_t val, unsigned size,
+                                      bool hw_access)
+{
+    TPMState *s = opaque;
+    TPMTISState *tis = &s->s.tis;
+    uint16_t off = addr & 0xfff;
+    uint8_t locty = tpm_tis_locality_from_addr(addr);
+    uint8_t active_locty, l;
+    int c, set_new_locty = 1;
+    uint16_t len;
+
+    dprintf("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
+
+    qemu_mutex_lock(&s->state_lock);
+
+    if (s->be_driver->ops->had_startup_error(s->be_driver)) {
+        qemu_mutex_unlock(&s->state_lock);
+        return;
+    }
+
+    switch (off) {
+    case TPM_TIS_REG_ACCESS:
+
+        if ((val & TPM_TIS_ACCESS_SEIZE)) {
+            val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
+                     TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+        }
+
+        active_locty = tis->active_locty;
+
+        if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
+            /* give up locality if currently owned */
+            if (tis->active_locty == locty) {
+                dprintf("tpm_tis: Releasing locality %d\n", locty);
+
+                uint8_t newlocty = TPM_TIS_NO_LOCALITY;
+                /* anybody wants the locality ? */
+                for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
+                    if ((tis->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
+                        dprintf("tpm_tis: Locality %d requests use.\n", c);
+                        newlocty = c;
+                        break;
+                    }
+                }
+                dprintf("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
+                        "Next active locality: %d\n",
+                        newlocty);
+
+                if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
+                    set_new_locty = 0;
+                    tpm_tis_prep_abort(s, locty, newlocty);
+                } else {
+                    active_locty = TPM_TIS_NO_LOCALITY;
+                }
+            } else {
+                /* not currently the owner; clear a pending request */
+                tis->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
+            }
+        }
+
+        if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
+            tis->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
+        }
+
+        if ((val & TPM_TIS_ACCESS_SEIZE)) {
+            /* allow seize if a locality is active and the requesting
+               locality is higher than the one that's active
+               OR
+               allow seize for requesting locality if no locality is
+               active */
+            while ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty) &&
+                    locty > tis->active_locty) ||
+                   (!TPM_TIS_IS_VALID_LOCTY(tis->active_locty))) {
+
+                /* already a pending SEIZE ? */
+                if ((tis->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
+                    break;
+                }
+
+                /* check for ongoing seize by a higher locality */
+                for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
+                    if ((tis->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
+                        break;
+                    }
+                }
+
+                /* cancel any seize by a lower locality */
+                for (l = 0; l < locty - 1; l++) {
+                    tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
+                }
+
+                tis->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
+                dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
+                        "Locality %d seized from locality %d\n",
+                        locty, tis->active_locty);
+                dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
+                set_new_locty = 0;
+                tpm_tis_prep_abort(s, tis->active_locty, locty);
+                break;
+            }
+        }
+
+        if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
+            if (tis->active_locty != locty) {
+                if (TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) {
+                    tis->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
+                } else {
+                    /* no locality active -> make this one active now */
+                    active_locty = locty;
+                }
+            }
+        }
+
+        if (set_new_locty) {
+            tpm_tis_new_active_locality(s, active_locty);
+        }
+
+        break;
+    case TPM_TIS_REG_INT_ENABLE:
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        tis->loc[locty].inte = (val & (TPM_TIS_INT_ENABLED | (0x3 << 3) |
+                                     TPM_TIS_INTERRUPTS_SUPPORTED));
+        break;
+    case TPM_TIS_REG_INT_VECTOR:
+        /* hard wired -- ignore */
+        break;
+    case TPM_TIS_REG_INT_STATUS:
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        /* clearing of interrupt flags */
+        if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
+            (tis->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
+            tis->loc[locty].ints &= ~val;
+            if (tis->loc[locty].ints == 0) {
+                qemu_irq_lower(tis->irq);
+                dprintf("tpm_tis: Lowering IRQ\n");
+            }
+        }
+        tis->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
+        break;
+    case TPM_TIS_REG_STS:
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
+                TPM_TIS_STS_RESPONSE_RETRY);
+
+        if (val == TPM_TIS_STS_COMMAND_READY) {
+            switch (tis->loc[locty].status) {
+
+            case TPM_TIS_STATUS_READY:
+                tis->loc[locty].w_offset = 0;
+                tis->loc[locty].r_offset = 0;
+            break;
+
+            case TPM_TIS_STATUS_IDLE:
+                tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
+                tis->loc[locty].status = TPM_TIS_STATUS_READY;
+                tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
+            break;
+
+            case TPM_TIS_STATUS_EXECUTION:
+            case TPM_TIS_STATUS_RECEPTION:
+                /* abort currently running command */
+                dprintf("tpm_tis: %s: Initiating abort.\n",
+                        __func__);
+                tpm_tis_prep_abort(s, locty, locty);
+            break;
+
+            case TPM_TIS_STATUS_COMPLETION:
+                tis->loc[locty].w_offset = 0;
+                tis->loc[locty].r_offset = 0;
+                /* shortcut to ready state with C/R set */
+                tis->loc[locty].status = TPM_TIS_STATUS_READY;
+                if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
+                    tis->loc[locty].sts   = TPM_TIS_STS_COMMAND_READY;
+                    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
+                }
+            break;
+
+            }
+        } else if (val == TPM_TIS_STS_TPM_GO) {
+            switch (tis->loc[locty].status) {
+            case TPM_TIS_STATUS_RECEPTION:
+                tpm_tis_tpm_send(s, locty);
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
+            switch (tis->loc[locty].status) {
+            case TPM_TIS_STATUS_COMPLETION:
+                tis->loc[locty].r_offset = 0;
+                tis->loc[locty].sts = TPM_TIS_STS_VALID |
+                                      TPM_TIS_STS_DATA_AVAILABLE;
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        }
+        break;
+    case TPM_TIS_REG_DATA_FIFO:
+        /* data fifo */
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        if (tis->loc[locty].status == TPM_TIS_STATUS_IDLE ||
+            tis->loc[locty].status == TPM_TIS_STATUS_EXECUTION ||
+            tis->loc[locty].status == TPM_TIS_STATUS_COMPLETION) {
+            /* drop the byte */
+        } else {
+            dprintf("tpm_tis: Byte to send to TPM: %02x\n", (uint8_t)val);
+            if (tis->loc[locty].status == TPM_TIS_STATUS_READY) {
+                tis->loc[locty].status = TPM_TIS_STATUS_RECEPTION;
+                tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID;
+            }
+
+            if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
+                if (tis->loc[locty].w_offset < tis->loc[locty].w_buffer.size) {
+                    tis->loc[locty].w_buffer.
+                        buffer[tis->loc[locty].w_offset++] = (uint8_t)val;
+                } else {
+                    tis->loc[locty].sts = TPM_TIS_STS_VALID;
+                }
+            }
+
+            /* check for complete packet */
+            if (tis->loc[locty].w_offset > 5 &&
+                (tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
+                /* we have a packet length - see if we have all of it */
+#ifdef RAISE_STS_IRQ
+                bool needIrq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID);
+#endif
+                len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
+                if (len > tis->loc[locty].w_offset) {
+                    tis->loc[locty].sts = TPM_TIS_STS_EXPECT |
+                                          TPM_TIS_STS_VALID;
+                } else {
+                    /* packet complete */
+                    tis->loc[locty].sts = TPM_TIS_STS_VALID;
+                }
+#ifdef RAISE_STS_IRQ
+                if (needIrq) {
+                    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
+                }
+#endif
+            }
+        }
+        break;
+    }
+
+    qemu_mutex_unlock(&s->state_lock);
+}
+
+static void tpm_tis_mmio_write(void *opaque, target_phys_addr_t addr,
+                               uint64_t val, unsigned size)
+{
+    return tpm_tis_mmio_write_intern(opaque, addr, val, size, false);
+}
+
+static const MemoryRegionOps tpm_tis_memory_ops = {
+    .read = tpm_tis_mmio_read,
+    .write = tpm_tis_mmio_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static int tpm_tis_do_startup_tpm(TPMState *s)
+{
+    return s->be_driver->ops->startup_tpm(s->be_driver);
+}
+
+/*
+ * This function is called when the machine starts, resets or due to
+ * S3 resume.
+ */
+static void tpm_tis_reset(DeviceState *d)
+{
+    TPMState *s = container_of(d, TPMState, busdev.qdev);
+    TPMTISState *tis = &s->s.tis;
+    int c;
+
+    s->tpm_initialized = false;
+
+    s->be_driver->ops->reset(s->be_driver);
+
+    tis->active_locty = TPM_TIS_NO_LOCALITY;
+    tis->next_locty = TPM_TIS_NO_LOCALITY;
+    tis->aborting_locty = TPM_TIS_NO_LOCALITY;
+
+    for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
+        tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
+        tis->loc[c].sts = 0;
+        tis->loc[c].inte = (1 << 3);
+        tis->loc[c].ints = 0;
+        tis->loc[c].status = TPM_TIS_STATUS_IDLE;
+
+        tis->loc[c].w_offset = 0;
+        s->be_driver->ops->realloc_buffer(&tis->loc[c].w_buffer);
+        tis->loc[c].r_offset = 0;
+        s->be_driver->ops->realloc_buffer(&tis->loc[c].r_buffer);
+    }
+
+    tpm_tis_do_startup_tpm(s);
+}
+
+static int tpm_tis_init(ISADevice *dev)
+{
+    TPMState *s = DO_UPCAST(TPMState, busdev, dev);
+    TPMTISState *tis = &s->s.tis;
+    int rc;
+
+    qemu_mutex_init(&s->state_lock);
+    qemu_cond_init(&s->from_tpm_cond);
+    qemu_cond_init(&s->to_tpm_cond);
+
+    s->be_driver = qemu_find_tpm(s->backend);
+    if (!s->be_driver) {
+        error_report("tpm_tis: backend driver with id %s could not be "
+                     "found.n\n", s->backend);
+        return -1;
+    }
+
+    s->be_driver->fe_model = "tpm-tis";
+
+    if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) {
+        return -1;
+    }
+
+    isa_init_irq(dev, &tis->irq, tis->irq_num);
+
+    memory_region_init_io(&s->mmio, &tpm_tis_memory_ops, s, "tpm-tis-mmio",
+                          0x1000 * TPM_TIS_NUM_LOCALITIES);
+    memory_region_add_subregion(get_system_memory(), TPM_TIS_ADDR_BASE,
+                                &s->mmio);
+
+    rc = tpm_tis_do_startup_tpm(s);
+    if (rc != 0) {
+        goto err_destroy_memory;
+    }
+
+    return 0;
+
+ err_destroy_memory:
+    memory_region_destroy(&s->mmio);
+
+    return -1;
+}
+
+static const VMStateDescription vmstate_tpm_tis = {
+    .name = "tpm",
+    .unmigratable = 1,
+};
+
+static ISADeviceInfo tpm_tis_device_info = {
+    .init         = tpm_tis_init,
+    .qdev.name    = "tpm-tis",
+    .qdev.size    = sizeof(TPMState),
+    .qdev.vmsd    = &vmstate_tpm_tis,
+    .qdev.reset   = tpm_tis_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("irq", TPMState,
+                           s.tis.irq_num, TPM_TIS_IRQ),
+        DEFINE_PROP_STRING("tpmdev", TPMState, backend),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void tpm_tis_register_device(void)
+{
+    isa_qdev_register(&tpm_tis_device_info);
+}
+
+device_init(tpm_tis_register_device)
-- 
1.7.6.4

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

* [Qemu-devel] [PATCH V14 3/7] Add a debug register
  2011-12-14 13:43 [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 1/7] Support for TPM command line options Stefan Berger
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2011-12-14 13:43 ` Stefan Berger
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 4/7] Build the TPM frontend code Stefan Berger
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2011-12-14 13:43 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

This patch uses the possibility to add a vendor-specific register and
adds a debug register useful for dumping the TIS's internal state. This
register is only active in a debug build (#define DEBUG_TIS).

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 hw/tpm_tis.c |   70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 70 insertions(+), 0 deletions(-)

diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
index bcbc987..1577a85 100644
--- a/hw/tpm_tis.c
+++ b/hw/tpm_tis.c
@@ -54,6 +54,9 @@
 #define TPM_TIS_REG_DID_VID               0xf00
 #define TPM_TIS_REG_RID                   0xf04
 
+/* vendor-specific registers */
+#define TPM_TIS_REG_DEBUG                 0xf90
+
 #define TPM_TIS_STS_VALID                 (1 << 7)
 #define TPM_TIS_STS_COMMAND_READY         (1 << 6)
 #define TPM_TIS_STS_TPM_GO                (1 << 5)
@@ -99,6 +102,11 @@
 
 #define TPM_TIS_NO_DATA_BYTE  0xff
 
+/* local prototypes */
+
+static uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
+                                  unsigned size);
+
 /* utility functions */
 
 static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
@@ -329,6 +337,63 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
     return ret;
 }
 
+#ifdef DEBUG_TIS
+static void tpm_tis_dump_state(void *opaque, target_phys_addr_t addr)
+{
+    static const unsigned regs[] = {
+        TPM_TIS_REG_ACCESS,
+        TPM_TIS_REG_INT_ENABLE,
+        TPM_TIS_REG_INT_VECTOR,
+        TPM_TIS_REG_INT_STATUS,
+        TPM_TIS_REG_INTF_CAPABILITY,
+        TPM_TIS_REG_STS,
+        TPM_TIS_REG_DID_VID,
+        TPM_TIS_REG_RID,
+        0xfff};
+    int idx;
+    uint8_t locty = tpm_tis_locality_from_addr(addr);
+    target_phys_addr_t base = addr & ~0xfff;
+    TPMState *s = opaque;
+    TPMTISState *tis = &s->s.tis;
+
+    dprintf("tpm_tis: active locality      : %d\n"
+            "tpm_tis: state of locality %d : %d\n"
+            "tpm_tis: register dump:\n",
+            tis->active_locty,
+            locty, tis->loc[locty].status);
+
+    for (idx = 0; regs[idx] != 0xfff; idx++) {
+        dprintf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
+                (uint32_t)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
+    }
+
+    dprintf("tpm_tis: read offset   : %d\n"
+            "tpm_tis: result buffer : ",
+            tis->loc[locty].r_offset);
+    for (idx = 0;
+         idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
+         idx++) {
+        dprintf("%c%02x%s",
+                tis->loc[locty].r_offset == idx ? '>' : ' ',
+                tis->loc[locty].r_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    }
+    dprintf("\n"
+            "tpm_tis: write offset  : %d\n"
+            "tpm_tis: request buffer: ",
+            tis->loc[locty].w_offset);
+    for (idx = 0;
+         idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
+         idx++) {
+        dprintf("%c%02x%s",
+                tis->loc[locty].w_offset == idx ? '>' : ' ',
+                tis->loc[locty].w_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    }
+    dprintf("\n");
+}
+#endif
+
 /*
  * Read a register of the TIS interface
  * See specs pages 33-63 for description of the registers
@@ -401,6 +466,11 @@ static uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
     case TPM_TIS_REG_RID:
         val = TPM_TIS_TPM_RID;
         break;
+#ifdef DEBUG_TIS
+    case TPM_TIS_REG_DEBUG:
+        tpm_tis_dump_state(opaque, addr);
+        break;
+#endif
     }
 
     qemu_mutex_unlock(&s->state_lock);
-- 
1.7.6.4

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

* [Qemu-devel] [PATCH V14 4/7] Build the TPM frontend code
  2011-12-14 13:43 [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (2 preceding siblings ...)
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 3/7] Add a debug register Stefan Berger
@ 2011-12-14 13:43 ` Stefan Berger
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2011-12-14 13:43 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

Build the TPM frontend code that has been added so far.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 Makefile.target |    2 ++
 configure       |   11 +++++++++++
 2 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/Makefile.target b/Makefile.target
index 3df3057..0e1c032 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -205,6 +205,8 @@ obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o
 obj-$(CONFIG_KVM) += kvm.o kvm-all.o
 obj-$(CONFIG_NO_KVM) += kvm-stub.o
 obj-y += memory.o
+obj-y += tpm.o
+obj-$(CONFIG_TPM) += tpm_tis.o
 LIBS+=-lz
 
 QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
diff --git a/configure b/configure
index cc5ae87..385feb4 100755
--- a/configure
+++ b/configure
@@ -185,6 +185,7 @@ opengl=""
 zlib="yes"
 guest_agent="yes"
 libiscsi=""
+tpm="no"
 
 # parse CC options first
 for opt do
@@ -784,6 +785,8 @@ for opt do
   ;;
   --disable-guest-agent) guest_agent="no"
   ;;
+  --enable-tpm) tpm="yes"
+  ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
@@ -1070,6 +1073,7 @@ echo "  --disable-usb-redir      disable usb network redirection support"
 echo "  --enable-usb-redir       enable usb network redirection support"
 echo "  --disable-guest-agent    disable building of the QEMU Guest Agent"
 echo "  --enable-guest-agent     enable building of the QEMU Guest Agent"
+echo "  --enable-tpm             enable TPM support"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -2845,6 +2849,7 @@ echo "usb net redir     $usb_redir"
 echo "OpenGL support    $opengl"
 echo "libiscsi support  $libiscsi"
 echo "build guest agent $guest_agent"
+echo "TPM support       $tpm"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3714,6 +3719,12 @@ if test "$gprof" = "yes" ; then
   fi
 fi
 
+if test "$tpm" = "yes"; then
+  if test "$target_softmmu" = "yes" ; then
+    echo "CONFIG_TPM=y" >> $config_host_mak
+  fi
+fi
+
 if test "$ARCH" = "tci"; then
   linker_script=""
 else
-- 
1.7.6.4

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

* [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation
  2011-12-14 13:43 [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (3 preceding siblings ...)
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 4/7] Build the TPM frontend code Stefan Berger
@ 2011-12-14 13:43 ` Stefan Berger
  2012-02-20 19:51   ` Michael S. Tsirkin
                     ` (2 more replies)
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
                   ` (2 subsequent siblings)
  7 siblings, 3 replies; 41+ messages in thread
From: Stefan Berger @ 2011-12-14 13:43 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

>From Andreas Niederl's original posting with adaptations where necessary:

This patch is based of off version 9 of Stefan Berger's patch series
  "Qemu Trusted Platform Module (TPM) integration"
and adds a new backend driver for it.

This patch adds a passthrough backend driver for passing commands sent to the
emulated TPM device directly to a TPM device opened on the host machine.

Thus it is possible to use a hardware TPM device in a system running on QEMU,
providing the ability to access a TPM in a special state (e.g. after a Trusted
Boot).

This functionality is being used in the acTvSM Trusted Virtualization Platform
which is available on [1].

Usage example:
  qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
                     -device tpm-tis,tpmdev=tpm0 \
                     -cdrom test.iso -boot d

Some notes about the host TPM:
The TPM needs to be enabled and activated. If that's not the case one
has to go through the BIOS/UEFI and enable and activate that TPM for TPM
commands to work as expected.
It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
command line or 'modprobe tpm_tis force=1' in case of using it as a module.

Regards,
Andreas Niederl, Stefan Berger

[1] http://trustedjava.sourceforge.net/

Signed-off-by: Andreas Niederl <andreas.niederl@iaik.tugraz.at>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 Makefile.target      |    1 +
 configure            |    3 +
 hw/tpm_passthrough.c |  493 ++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-options.hx      |   33 ++++
 tpm.c                |   16 ++
 tpm.h                |   33 ++++
 6 files changed, 579 insertions(+), 0 deletions(-)
 create mode 100644 hw/tpm_passthrough.c

diff --git a/Makefile.target b/Makefile.target
index 0e1c032..2744a5c 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -207,6 +207,7 @@ obj-$(CONFIG_NO_KVM) += kvm-stub.o
 obj-y += memory.o
 obj-y += tpm.o
 obj-$(CONFIG_TPM) += tpm_tis.o
+obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
 LIBS+=-lz
 
 QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
diff --git a/configure b/configure
index 385feb4..25995bc 100755
--- a/configure
+++ b/configure
@@ -3721,6 +3721,9 @@ fi
 
 if test "$tpm" = "yes"; then
   if test "$target_softmmu" = "yes" ; then
+    if test "$linux" = "yes" ; then
+      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
+    fi
     echo "CONFIG_TPM=y" >> $config_host_mak
   fi
 fi
diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
new file mode 100644
index 0000000..76e4e35
--- /dev/null
+++ b/hw/tpm_passthrough.c
@@ -0,0 +1,493 @@
+/*
+ *  passthrough TPM driver
+ *
+ *  Copyright (c) 2010, 2011 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-common.h"
+#include "qemu-error.h"
+#include "tpm.h"
+#include "hw/hw.h"
+#include "hw/tpm_tis.h"
+#include "hw/pc.h"
+
+/* #define DEBUG_TPM */
+
+#ifdef DEBUG_TPM
+#define dprintf(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+    do { } while (0)
+#endif
+
+/* data structures */
+
+typedef struct TPMPassthruThreadParams {
+    TPMState *tpm_state;
+
+    TPMRecvDataCB *recv_data_callback;
+    TPMBackend *tb;
+} TPMPassthruThreadParams;
+
+struct TPMPassthruState {
+    QemuThread thread;
+    bool thread_terminate;
+    bool thread_running;
+
+    TPMPassthruThreadParams tpm_thread_params;
+
+    char tpm_dev[64];
+    int tpm_fd;
+    bool had_startup_error;
+};
+
+#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
+
+/* borrowed from qemu-char.c */
+static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
+{
+    int ret, len1;
+
+    len1 = len;
+    while (len1 > 0) {
+        ret = write(fd, buf, len1);
+        if (ret < 0) {
+            if (errno != EINTR && errno != EAGAIN) {
+                return -1;
+            }
+        } else if (ret == 0) {
+            break;
+        } else {
+            buf  += ret;
+            len1 -= ret;
+        }
+    }
+    return len - len1;
+}
+
+static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
+{
+    int ret, len1;
+    uint8_t *buf1;
+
+    len1 = len;
+    buf1 = buf;
+    while ((len1 > 0) && (ret = read(fd, buf1, len1)) != 0) {
+        if (ret < 0) {
+            if (errno != EINTR && errno != EAGAIN) {
+                return -1;
+            }
+        } else {
+            buf1 += ret;
+            len1 -= ret;
+        }
+    }
+    return len - len1;
+}
+
+static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
+{
+    return be32_to_cpu(*(uint32_t *)&buf[2]);
+}
+
+static int tpm_passthrough_unix_tx_bufs(int tpm_fd,
+                                        const uint8_t *in, uint32_t in_len,
+                                        uint8_t *out, uint32_t out_len)
+{
+    int ret;
+
+    ret = tpm_passthrough_unix_write(tpm_fd, in, in_len);
+    if (ret < 0) {
+        error_report("tpm_passthrough: error while transmitting data "
+                     "to TPM: %s (%i)\n",
+                     strerror(errno), errno);
+        goto err_exit;
+    }
+
+    ret = tpm_passthrough_unix_read(tpm_fd, out, out_len);
+    if (ret < 0) {
+        error_report("tpm_passthrough: error while reading data from "
+                     "TPM: %s (%i)\n",
+                     strerror(errno), errno);
+    } else if (ret < sizeof(struct tpm_resp_hdr) ||
+               tpm_passthrough_get_size_from_buffer(out) != ret) {
+        ret = -1;
+        error_report("tpm_passthrough: received invalid response "
+                     "packet from TPM\n");
+    }
+
+err_exit:
+    if (ret < 0) {
+        tpm_write_fatal_error_response(out, out_len);
+    }
+
+    return ret;
+}
+
+static int tpm_passthrough_unix_transfer(int tpm_fd,
+                                         const TPMLocality *cmd_locty)
+{
+    return tpm_passthrough_unix_tx_bufs(tpm_fd,
+                                        cmd_locty->w_buffer.buffer,
+                                        cmd_locty->w_offset,
+                                        cmd_locty->r_buffer.buffer,
+                                        cmd_locty->r_buffer.size);
+}
+
+static void *tpm_passthrough_thread(void *d)
+{
+    TPMPassthruThreadParams *thr_parms = d;
+    TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
+    uint32_t in_len;
+
+    dprintf("tpm_passthrough: THREAD IS STARTING\n");
+
+    /* start command processing */
+    while (!tpm_pt->thread_terminate) {
+        /* receive and handle commands */
+        in_len = 0;
+        do {
+            dprintf("tpm_passthrough: waiting for commands...\n");
+
+            if (tpm_pt->thread_terminate) {
+                break;
+            }
+
+            qemu_mutex_lock(&thr_parms->tpm_state->state_lock);
+
+            /* in case we were to slow and missed the signal, the
+               to_tpm_execute boolean tells us about a pending command */
+            if (!thr_parms->tpm_state->to_tpm_execute) {
+                qemu_cond_wait(&thr_parms->tpm_state->to_tpm_cond,
+                               &thr_parms->tpm_state->state_lock);
+            }
+
+            thr_parms->tpm_state->to_tpm_execute = false;
+
+            qemu_mutex_unlock(&thr_parms->tpm_state->state_lock);
+
+            if (tpm_pt->thread_terminate) {
+                break;
+            }
+
+            in_len = thr_parms->tpm_state->cmd_locty->w_offset;
+
+            tpm_passthrough_unix_transfer(tpm_pt->tpm_fd,
+                                          thr_parms->tpm_state->cmd_locty);
+
+            thr_parms->recv_data_callback(thr_parms->tpm_state,
+                                          thr_parms->tpm_state->command_locty);
+        } while (in_len > 0);
+    }
+
+    dprintf("tpm_passthrough: THREAD IS ENDING\n");
+
+    tpm_pt->thread_running = false;
+
+    return NULL;
+}
+
+static void tpm_passthrough_terminate_tpm_thread(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    if (!tpm_pt->thread_running) {
+        return;
+    }
+
+    dprintf("tpm_passthrough: TERMINATING RUNNING TPM THREAD\n");
+
+    if (!tpm_pt->thread_terminate) {
+        tpm_pt->thread_terminate = true;
+
+        qemu_mutex_lock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
+        qemu_cond_signal(&tpm_pt->tpm_thread_params.tpm_state->to_tpm_cond);
+        qemu_mutex_unlock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
+
+        if (tpm_pt->thread_running) {
+            qemu_thread_join(&tpm_pt->thread);
+        }
+        memset(&tpm_pt->thread, 0, sizeof(tpm_pt->thread));
+    }
+}
+
+/**
+ * Start the TPM (thread). If it had been started before, then terminate
+ * and start it again.
+ */
+static int tpm_passthrough_do_startup_tpm(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    /* terminate a running TPM */
+    tpm_passthrough_terminate_tpm_thread(tb);
+
+    /* reset the flag so the thread keeps on running */
+    tpm_pt->thread_terminate = false;
+
+    qemu_thread_create(&tpm_pt->thread, tpm_passthrough_thread,
+                       &tpm_pt->tpm_thread_params, 0);
+
+    tpm_pt->thread_running = true;
+
+    return 0;
+}
+
+static int tpm_passthrough_startup_tpm(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+    int rc;
+
+    rc = tpm_passthrough_do_startup_tpm(tb);
+    if (rc) {
+        tpm_pt->had_startup_error = true;
+    }
+    return rc;
+}
+
+static void tpm_passthrough_reset(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    dprintf("tpm_passthrough: CALL TO TPM_RESET!\n");
+
+    tpm_passthrough_terminate_tpm_thread(tb);
+
+    tpm_pt->had_startup_error = false;
+}
+
+static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
+                                TPMRecvDataCB *recv_data_cb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    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;
+
+    tpm_pt->thread_running = false;
+
+    return 0;
+}
+
+static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
+{
+    return false;
+}
+
+static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    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);
+        if (sb->buffer != NULL) {
+            sb->size = wanted_size;
+        } else {
+            sb->size = 0;
+        }
+    }
+    return sb->size;
+}
+
+static const char *tpm_passthrough_create_desc(void)
+{
+    return "Passthrough TPM backend driver";
+}
+
+/* A basic test of a TPM device. We expect a well formatted response header
+ * (error response is fine) within one second.
+ */
+static int tpm_passthrough_test_tpmdev(int fd)
+{
+    struct tpm_req_hdr req = {
+        .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
+        .len = cpu_to_be32(sizeof(req)),
+        .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
+    };
+    struct tpm_resp_hdr *resp;
+    fd_set readfds;
+    int n;
+    struct timeval tv = {
+        .tv_sec = 1,
+        .tv_usec = 0,
+    };
+    unsigned char buf[1024];
+
+    n = write(fd, &req, sizeof(req));
+    if (n < 0) {
+        return errno;
+    }
+    if (n != sizeof(req)) {
+        return EFAULT;
+    }
+
+    FD_ZERO(&readfds);
+    FD_SET(fd, &readfds);
+
+    /* wait for a second */
+    n = select(fd + 1, &readfds, NULL, NULL, &tv);
+    if (n != 1) {
+        return errno;
+    }
+
+    n = read(fd, &buf, sizeof(buf));
+    if (n < sizeof(struct tpm_resp_hdr)) {
+        return EFAULT;
+    }
+
+    resp = (struct tpm_resp_hdr *)buf;
+    /* check the header */
+    if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND ||
+        be32_to_cpu(resp->len) != n) {
+        return EBADMSG;
+    }
+
+    return 0;
+}
+
+static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
+{
+    const char *value;
+    char buf[64];
+    int n;
+
+    value = qemu_opt_get(opts, "path");
+    if (!value) {
+        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+    }
+
+    n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
+                 "%s", value);
+
+    if (n >= sizeof(tb->s.tpm_pt->tpm_dev)) {
+        error_report("TPM device path is too long.\n");
+        goto err_exit;
+    }
+
+    snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
+
+    tb->parameters = g_strdup(buf);
+
+    if (tb->parameters == NULL) {
+        return 1;
+    }
+
+    tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
+    if (tb->s.tpm_pt->tpm_fd < 0) {
+        error_report("Cannot access TPM device using '%s'.\n",
+                     tb->s.tpm_pt->tpm_dev);
+        goto err_exit;
+    }
+
+    if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) {
+        error_report("'%s' is not a TPM device.\n",
+                     tb->s.tpm_pt->tpm_dev);
+        goto err_close_tpmdev;
+    }
+
+    return 0;
+
+ err_close_tpmdev:
+    close(tb->s.tpm_pt->tpm_fd);
+    tb->s.tpm_pt->tpm_fd = -1;
+
+ err_exit:
+    g_free(tb->parameters);
+    tb->parameters = NULL;
+
+    return 1;
+}
+
+
+static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
+{
+    TPMBackend *tb;
+
+    tb = g_malloc0(sizeof(TPMBackend));
+    if (tb == NULL) {
+        error_report("tpm_passthrough: Could not allocate memory.\n");
+        return NULL;
+    }
+
+    tb->s.tpm_pt = g_malloc0(sizeof(TPMPassthruState));
+    if (tb->s.tpm_pt == NULL) {
+        error_report("tpm_passthrough: Could not allocate memory.\n");
+        g_free(tb);
+        return NULL;
+    }
+
+    tb->id = g_strdup(id);
+    if (tb->id == NULL) {
+        error_report("tpm_passthrough: Could not allocate memory.\n");
+        goto err_exit;
+    }
+
+    tb->ops = &tpm_passthrough_driver;
+
+    if (tpm_passthrough_handle_device_opts(opts, tb)) {
+        goto err_exit;
+    }
+
+    return tb;
+
+err_exit:
+    g_free(tb->id);
+    g_free(tb->s.tpm_pt);
+    g_free(tb);
+
+    return NULL;
+}
+
+static void tpm_passthrough_destroy(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    tpm_passthrough_terminate_tpm_thread(tb);
+
+    close(tpm_pt->tpm_fd);
+
+    g_free(tb->id);
+    g_free(tb->parameters);
+    g_free(tb->s.tpm_pt);
+    g_free(tb);
+}
+
+const TPMDriverOps tpm_passthrough_driver = {
+    .id                       = "passthrough",
+    .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,
+    .reset                    = tpm_passthrough_reset,
+    .had_startup_error        = tpm_passthrough_get_startup_error,
+    .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
+};
diff --git a/qemu-options.hx b/qemu-options.hx
index 40a63b1..10ae499 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1916,6 +1916,7 @@ 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}.
 
 The specific backend type will determine the applicable options.
 The @code{-tpmdev} options requires a @code{-device} option.
@@ -1927,6 +1928,38 @@ Use ? to print all available TPM backend types.
 qemu -tpmdev ?
 @end example
 
+@item -tpmdev passthrough, id=@var{id}, path=@var{path}
+
+(Linux-host only) Enable access to the host's TPM using the passthrough
+driver.
+
+@option{path} specifies the path to the host's TPM device, i.e., on
+a Linux host this would be @code{/dev/tpm0}.
+@option{path} is optional and by default @code{/dev/tpm0} is used.
+
+Some notes about using the host's TPM with the passthrough driver:
+
+The TPM device accessed by the passthrough driver must not be
+used by any other application on the host.
+
+Since the host's firmware (BIOS/UEFI) has already initialized the TPM,
+the VM's firmware (BIOS/UEFI) will not be able to initialize the
+TPM again and may therefore not show a TPM-specific menu that would
+otherwise allow the user to configure the TPM, e.g., allow the user to
+enable/disable or activate/deactivate the TPM.
+Further, if TPM ownership is released from within a VM then the host's TPM
+will get disabled and deactivated. To enable and activate the
+TPM again afterwards, the host has to be rebooted and the user is
+required to enter the firmware's menu to enable and activate the TPM.
+If the TPM is left disabled and/or deactivated most TPM commands will fail.
+
+To create a passthrough TPM use the following two options:
+@example
+-tpmdev passthrough,id=tpm0 -device tpm-tis,tpmdev=tpm0
+@end example
+Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
+@code{tpmdev=tpm0} in the device option.
+
 @end table
 
 ETEXI
diff --git a/tpm.c b/tpm.c
index 408d319..ee38107 100644
--- a/tpm.c
+++ b/tpm.c
@@ -24,9 +24,25 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =
 #ifdef CONFIG_TPM
 
 static const TPMDriverOps *bes[] = {
+#ifdef CONFIG_TPM_PASSTHROUGH
+    &tpm_passthrough_driver,
+#endif
     NULL,
 };
 
+/* Write an error message in the given output buffer.
+ */
+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);
+    }
+}
+
 const TPMDriverOps *tpm_get_backend_driver(const char *id)
 {
     int i;
diff --git a/tpm.h b/tpm.h
index 075de3f..1b278cc 100644
--- a/tpm.h
+++ b/tpm.h
@@ -18,12 +18,18 @@
 struct TPMDriverOps;
 typedef struct TPMDriverOps TPMDriverOps;
 
+typedef struct TPMPassthruState TPMPassthruState;
+
 typedef struct TPMBackend {
     char *id;
     const char *fe_model;
     char *parameters;
     const TPMDriverOps *ops;
 
+    union {
+        TPMPassthruState *tpm_pt;
+    } s;
+
     QLIST_ENTRY(TPMBackend) list;
 } TPMBackend;
 
@@ -76,11 +82,38 @@ struct TPMDriverOps {
 
 #define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
 
+struct tpm_req_hdr {
+    uint16_t tag;
+    uint32_t len;
+    uint32_t ordinal;
+} __attribute__((packed));
+
+struct tpm_resp_hdr {
+    uint16_t tag;
+    uint32_t len;
+    uint32_t errcode;
+} __attribute__((packed));
+
+#define TPM_TAG_RQU_COMMAND       0xc1
+#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2
+#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3
+
+#define TPM_TAG_RSP_COMMAND       0xc4
+#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5
+#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6
+
+#define TPM_FAIL                  9
+
+#define TPM_ORD_GetTicks          0xf1
+
 int tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
 int tpm_init(void);
 void tpm_cleanup(void);
 TPMBackend *qemu_find_tpm(const char *id);
 void tpm_display_backend_drivers(void);
 const TPMDriverOps *tpm_get_backend_driver(const char *id);
+void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len);
+
+extern const TPMDriverOps tpm_passthrough_driver;
 
 #endif /* QEMU_TPM_H */
-- 
1.7.6.4

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

* [Qemu-devel] [PATCH V14 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-14 13:43 [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (4 preceding siblings ...)
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2011-12-14 13:43 ` Stefan Berger
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
  2012-01-12 16:59 ` [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Paul Moore
  7 siblings, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2011-12-14 13:43 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

Introduce --enable-tpm-passthrough configure option.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 configure |   16 +++++++++++++++-
 1 files changed, 15 insertions(+), 1 deletions(-)

diff --git a/configure b/configure
index 25995bc..ffb599e 100755
--- a/configure
+++ b/configure
@@ -186,6 +186,7 @@ zlib="yes"
 guest_agent="yes"
 libiscsi=""
 tpm="no"
+tpm_passthrough="no"
 
 # parse CC options first
 for opt do
@@ -787,11 +788,20 @@ for opt do
   ;;
   --enable-tpm) tpm="yes"
   ;;
+  --enable-tpm-passthrough) tpm_passthrough="yes"
+  ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
 done
 
+if test "$tpm" = "no" ; then
+    if test "$tpm_passthrough" = "yes"; then
+        echo "ERROR: --enable-tpm-passthrough requires --enable-tpm"
+        exit 1
+    fi
+fi
+
 #
 # If cpu ~= sparc and  sparc_cpu hasn't been defined, plug in the right
 # QEMU_CFLAGS/LDFLAGS (assume sparc_v8plus for 32-bit and sparc_v9 for 64-bit)
@@ -1074,6 +1084,7 @@ echo "  --enable-usb-redir       enable usb network redirection support"
 echo "  --disable-guest-agent    disable building of the QEMU Guest Agent"
 echo "  --enable-guest-agent     enable building of the QEMU Guest Agent"
 echo "  --enable-tpm             enable TPM support"
+echo "  --enable-tpm-passthrough enable TPM passthrough driver"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -2850,6 +2861,7 @@ echo "OpenGL support    $opengl"
 echo "libiscsi support  $libiscsi"
 echo "build guest agent $guest_agent"
 echo "TPM support       $tpm"
+echo "TPM passthrough   $tpm_passthrough"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3722,7 +3734,9 @@ fi
 if test "$tpm" = "yes"; then
   if test "$target_softmmu" = "yes" ; then
     if test "$linux" = "yes" ; then
-      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
+      if test "$tpm_passthrough" = "yes" ; then
+        echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
+      fi
     fi
     echo "CONFIG_TPM=y" >> $config_host_mak
   fi
-- 
1.7.6.4

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

* [Qemu-devel] [PATCH V14 7/7] Add fd parameter for TPM passthrough driver
  2011-12-14 13:43 [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (5 preceding siblings ...)
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
@ 2011-12-14 13:43 ` Stefan Berger
  2012-01-12 16:59 ` [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Paul Moore
  7 siblings, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2011-12-14 13:43 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

Enable the passing of a file descriptor via fd=<..> to access the host's
TPM device using the TPM passthrough driver.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 hw/tpm_passthrough.c |   73 +++++++++++++++++++++++++++++++++++++-------------
 qemu-config.c        |    5 +++
 qemu-options.hx      |    6 +++-
 3 files changed, 64 insertions(+), 20 deletions(-)

diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
index 76e4e35..ab8831a 100644
--- a/hw/tpm_passthrough.c
+++ b/hw/tpm_passthrough.c
@@ -377,33 +377,68 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
     const char *value;
     char buf[64];
     int n;
+    struct stat statbuf;
 
-    value = qemu_opt_get(opts, "path");
-    if (!value) {
-        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
-    }
+    value = qemu_opt_get(opts, "fd");
+    if (value) {
+        if (qemu_opt_get(opts, "path")) {
+            error_report("fd= is invalid with path=");
+            return -1;
+        }
+
+        tb->s.tpm_pt->tpm_fd = qemu_parse_fd(value);
+        if (tb->s.tpm_pt->tpm_fd < 0) {
+            error_report("Illegal file descriptor for TPM device.\n");
+            return -1;
+        }
 
-    n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
-                 "%s", value);
+        snprintf(buf, sizeof(buf), "fd=%d", tb->s.tpm_pt->tpm_fd);
 
-    if (n >= sizeof(tb->s.tpm_pt->tpm_dev)) {
-        error_report("TPM device path is too long.\n");
-        goto err_exit;
-    }
+        tb->parameters = g_strdup(buf);
 
-    snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
+        if (tb->parameters == NULL) {
+            goto err_close_tpmdev;
+        }
+    } else {
+        value = qemu_opt_get(opts, "path");
+        if (!value) {
+            value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+        }
+
+        n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
+                     "%s", value);
+
+        if (n >= sizeof(tb->s.tpm_pt->tpm_dev)) {
+            error_report("TPM device path is too long.\n");
+            goto err_exit;
+        }
 
-    tb->parameters = g_strdup(buf);
+        snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
 
-    if (tb->parameters == NULL) {
-        return 1;
+        tb->parameters = g_strdup(buf);
+
+        if (tb->parameters == NULL) {
+            return 1;
+        }
+
+        tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
+        if (tb->s.tpm_pt->tpm_fd < 0) {
+            error_report("Cannot access TPM device using '%s'.\n",
+                         tb->s.tpm_pt->tpm_dev);
+            goto err_exit;
+        }
     }
 
-    tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
-    if (tb->s.tpm_pt->tpm_fd < 0) {
-        error_report("Cannot access TPM device using '%s'.\n",
-                     tb->s.tpm_pt->tpm_dev);
-        goto err_exit;
+    if (fstat(tb->s.tpm_pt->tpm_fd, &statbuf) != 0) {
+        error_report("Cannot determine file descriptor type for TPM "
+                     "device: %s", strerror(errno));
+        goto err_close_tpmdev;
+    }
+
+    /* only allow character devices for now */
+    if (!S_ISCHR(statbuf.st_mode)) {
+        error_report("TPM file descriptor is not a character device");
+        goto err_close_tpmdev;
     }
 
     if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) {
diff --git a/qemu-config.c b/qemu-config.c
index cc4c31d..e49f4d8 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -564,6 +564,11 @@ static QemuOptsList qemu_tpmdev_opts = {
             .type = QEMU_OPT_STRING,
             .help = "Persistent storage for TPM state",
         },
+        {
+            .name = "fd",
+            .type = QEMU_OPT_STRING,
+            .help = "Filedescriptor for accessing the TPM",
+        },
         { /* end of list */ }
     },
 };
diff --git a/qemu-options.hx b/qemu-options.hx
index 10ae499..8b3fba8 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1928,7 +1928,7 @@ Use ? to print all available TPM backend types.
 qemu -tpmdev ?
 @end example
 
-@item -tpmdev passthrough, id=@var{id}, path=@var{path}
+@item -tpmdev passthrough, id=@var{id}, path=@var{path}, fd=@var{h}
 
 (Linux-host only) Enable access to the host's TPM using the passthrough
 driver.
@@ -1937,6 +1937,10 @@ driver.
 a Linux host this would be @code{/dev/tpm0}.
 @option{path} is optional and by default @code{/dev/tpm0} is used.
 
+@option{fd} specifies the file descriptor of the host's TPM device.
+@option{fd} and @option{path} are mutually exclusive.
+@option{fd} is optional.
+
 Some notes about using the host's TPM with the passthrough driver:
 
 The TPM device accessed by the passthrough driver must not be
-- 
1.7.6.4

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

* Re: [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration
  2011-12-14 13:43 [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (6 preceding siblings ...)
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
@ 2012-01-12 16:59 ` Paul Moore
  2012-01-16 19:21   ` Paul Moore
  7 siblings, 1 reply; 41+ messages in thread
From: Paul Moore @ 2012-01-12 16:59 UTC (permalink / raw)
  To: Stefan Berger, anthony; +Cc: mst, qemu-devel, andreas.niederl

On Wednesday, December 14, 2011 08:43:15 AM Stefan Berger wrote:
> The following series of patches adds TPM (Trusted Platform Module) support
> to Qemu. An emulator for the TIS (TPM Interface Spec) interface is
> added that provides the basis for accessing a 'backend' implementing the
> actual TPM functionality. The TIS emulator serves as a 'frontend' enabling
> for example Linux's TPM TIS (tpm_tis) driver.

Hi Stefan,

Thanks for updating these, I apologize for not tracking the updates very 
closely over the past several revisions but I do have the v14 revision up and 
running and while my testing so far has been fairly basic and crude, 
everything appears to be working as it should.  I'm going to try and throw 
some more comprehensive tests at it and I'll let you know if anything goes 
awry.

To the rest of the QEMU crew,

The comments on the patchset seems to be dwindling, we're on the 14th revision 
and it looks like most of the issues have been addressed - any hope on getting 
this merged in the near future?

Thanks.

-- 
paul moore
virtualization @ redhat

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

* Re: [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration
  2012-01-12 16:59 ` [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Paul Moore
@ 2012-01-16 19:21   ` Paul Moore
  0 siblings, 0 replies; 41+ messages in thread
From: Paul Moore @ 2012-01-16 19:21 UTC (permalink / raw)
  To: qemu-devel, Stefan Berger, anthony; +Cc: andreas.niederl, mst

On Thursday, January 12, 2012 11:59:54 AM Paul Moore wrote:
> On Wednesday, December 14, 2011 08:43:15 AM Stefan Berger wrote:
> > The following series of patches adds TPM (Trusted Platform Module)
> > support to Qemu. An emulator for the TIS (TPM Interface Spec) interface
> > is added that provides the basis for accessing a 'backend' implementing
> > the actual TPM functionality. The TIS emulator serves as a 'frontend'
> > enabling for example Linux's TPM TIS (tpm_tis) driver.
> 
> Hi Stefan,
> 
> Thanks for updating these, I apologize for not tracking the updates very
> closely over the past several revisions but I do have the v14 revision up
> and running and while my testing so far has been fairly basic and crude,
> everything appears to be working as it should.  I'm going to try and throw
> some more comprehensive tests at it and I'll let you know if anything goes
> awry.

An update on the testing ... I ran the TPM relevant portion of the TrouSerS 
TSS API test suite against this patchset and I am happy to report that it 
completed without error (168 passed tests, 3 not applicable, 0 failures, 0 
segfaults).

Version 0.3 of the test suite was used.

> To the rest of the QEMU crew,
> 
> The comments on the patchset seems to be dwindling, we're on the 14th
> revision and it looks like most of the issues have been addressed - any
> hope on getting this merged in the near future?
> 
> Thanks.

-- 
paul moore
virtualization @ redhat

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2012-02-20  8:51   ` Michael S. Tsirkin
  2012-02-20 15:48     ` Stefan Berger
  2012-02-20 22:02   ` Michael S. Tsirkin
  1 sibling, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-20  8:51 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
> This patch adds the main code of the TPM frontend driver, the TPM TIS
> interface, to Qemu. The code is largely based on the previous implementation
> for Xen but has been significantly extended to meet the standard's
> requirements, such as the support for changing of localities and all the
> functionality of the available flags.
> 
> Communication with the backend (i.e., for Xen or the libtpms-based one)
> is cleanly separated through an interface which the backend driver needs
> to implement.
> 
> The TPM TIS driver's backend was previously chosen in the code added
> to arch_init. The frontend holds a pointer to the chosen backend (interface).
> 
> Communication with the backend is largely based on signals and conditions.
> Whenever the frontend has collected a complete packet, it will signal
> the backend, which then starts processing the command. Once the result
> has been returned, the backend invokes a callback function
> (tis_tpm_receive_cb()).
> 
> The one tricky part is support for VM suspend while the TPM is processing
> a command. In this case the frontend driver is waiting for the backend
> to return the result of the last command before shutting down. It waits
> on a condition for a signal from the backend, which is delivered in
> tis_tpm_receive_cb().
> 
> Testing the proper functioning of the different flags and localities
> cannot be done from user space when running in Linux for example, since
> access to the address space of the TPM TIS interface is not possible. Also
> the Linux driver itself does not exercise all functionality. So, for
> testing there is a fairly extensive test suite as part of the SeaBIOS patches
> since from within the BIOS one can have full access to all the TPM's registers.
> 
> 
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>


looks good overall
some questions on erro handling and behaviour with
a malicious guest, below.

> ---
>  hw/tpm_tis.c |  807 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 807 insertions(+), 0 deletions(-)
>  create mode 100644 hw/tpm_tis.c
> 
> diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
> new file mode 100644
> index 0000000..bcbc987
> --- /dev/null
> +++ b/hw/tpm_tis.c
> @@ -0,0 +1,807 @@
> +/*
> + * tpm_tis.c - QEMU's TPM TIS interface emulator
> + *
> + * Copyright (C) 2006,2010,2011 IBM Corporation
> + *
> + * Authors:
> + *  Stefan Berger <stefanb@us.ibm.com>
> + *  David Safford <safford@us.ibm.com>
> + *
> + * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation, version 2 of the
> + * License.
> + *
> + *
> + * Implementation of the TIS interface according to specs found at
> + * http://www.trustedcomputiggroup.org.
> + * In the developers menu choose the PC Client section then find the TIS
> + * specification.
> + */
> +
> +#include "tpm.h"
> +#include "block.h"
> +#include "exec-memory.h"
> +#include "hw/hw.h"
> +#include "hw/pc.h"
> +#include "hw/tpm_tis.h"
> +#include "qemu-error.h"
> +#include "qemu-common.h"
> +
> +/*#define DEBUG_TIS */
> +
> +#ifdef DEBUG_TIS
> +#define dprintf(fmt, ...) \
> +    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define dprintf(fmt, ...) \
> +    do { } while (0)
> +#endif
> +
> +/* whether the STS interrupt is supported */
> +/*#define RAISE_STS_IRQ */
> +
> +/* tis registers */
> +#define TPM_TIS_REG_ACCESS                0x00
> +#define TPM_TIS_REG_INT_ENABLE            0x08
> +#define TPM_TIS_REG_INT_VECTOR            0x0c
> +#define TPM_TIS_REG_INT_STATUS            0x10
> +#define TPM_TIS_REG_INTF_CAPABILITY       0x14
> +#define TPM_TIS_REG_STS                   0x18
> +#define TPM_TIS_REG_DATA_FIFO             0x24
> +#define TPM_TIS_REG_DID_VID               0xf00
> +#define TPM_TIS_REG_RID                   0xf04
> +
> +#define TPM_TIS_STS_VALID                 (1 << 7)
> +#define TPM_TIS_STS_COMMAND_READY         (1 << 6)
> +#define TPM_TIS_STS_TPM_GO                (1 << 5)
> +#define TPM_TIS_STS_DATA_AVAILABLE        (1 << 4)
> +#define TPM_TIS_STS_EXPECT                (1 << 3)
> +#define TPM_TIS_STS_RESPONSE_RETRY        (1 << 1)
> +
> +#define TPM_TIS_ACCESS_TPM_REG_VALID_STS  (1 << 7)
> +#define TPM_TIS_ACCESS_ACTIVE_LOCALITY    (1 << 5)
> +#define TPM_TIS_ACCESS_BEEN_SEIZED        (1 << 4)
> +#define TPM_TIS_ACCESS_SEIZE              (1 << 3)
> +#define TPM_TIS_ACCESS_PENDING_REQUEST    (1 << 2)
> +#define TPM_TIS_ACCESS_REQUEST_USE        (1 << 1)
> +#define TPM_TIS_ACCESS_TPM_ESTABLISHMENT  (1 << 0)
> +
> +#define TPM_TIS_INT_ENABLED               (1 << 31)
> +#define TPM_TIS_INT_DATA_AVAILABLE        (1 << 0)
> +#define TPM_TIS_INT_STS_VALID             (1 << 1)
> +#define TPM_TIS_INT_LOCALITY_CHANGED      (1 << 2)
> +#define TPM_TIS_INT_COMMAND_READY         (1 << 7)
> +
> +#ifndef RAISE_STS_IRQ
> +
> +# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
> +                                       TPM_TIS_INT_DATA_AVAILABLE   | \
> +                                       TPM_TIS_INT_COMMAND_READY)
> +
> +#else
> +
> +# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
> +                                       TPM_TIS_INT_DATA_AVAILABLE   | \
> +                                       TPM_TIS_INT_STS_VALID | \
> +                                       TPM_TIS_INT_COMMAND_READY)
> +

let's keep these as #define

> +#endif
> +
> +#define TPM_TIS_CAPABILITIES_SUPPORTED   ((1 << 4) | \
> +                                          TPM_TIS_INTERRUPTS_SUPPORTED)
> +
> +#define TPM_TIS_TPM_DID       0x0001
> +#define TPM_TIS_TPM_VID       0x0001
> +#define TPM_TIS_TPM_RID       0x0001
> +
> +#define TPM_TIS_NO_DATA_BYTE  0xff
> +
> +/* utility functions */
> +
> +static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
> +{
> +    return (uint8_t)((addr >> 12) & 0x7);
> +}
> +

note this can give a number 0..6

> +static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
> +{
> +    return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
> +}
> +
> +static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
> +{
> +#ifdef DEBUG_TIS
> +    uint32_t len, i;
> +
> +    len = tpm_tis_get_size_from_buffer(sb);
> +    dprintf("tpm_tis: %s length = %d\n", string, len);
> +    for (i = 0; i < len; i++) {
> +        if (i && !(i & 16)) {
> +            dprintf("\n");
> +        }
> +        dprintf("%.2X ", sb->buffer[i]);
> +    }
> +    dprintf("\n");
> +#endif
> +}
> +
> +
> +/*
> + * Send a TPM request.
> + * Call this with the state_lock held so we can sync with the receive
> + * callback.
> + */
> +static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
> +{
> +    TPMTISState *tis = &s->s.tis;
> +
> +    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
> +
> +    s->command_locty = locty;
> +    s->cmd_locty     = &tis->loc[locty];
> +
> +    /* w_offset serves as length indicator for length of data;
> +       it's reset when the response comes back */
> +    tis->loc[locty].status = TPM_TIS_STATUS_EXECUTION;
> +    tis->loc[locty].sts &= ~TPM_TIS_STS_EXPECT;
> +
> +    s->to_tpm_execute = true;
> +    qemu_cond_signal(&s->to_tpm_cond);
> +}
> +
> +/* raise an interrupt if allowed */
> +static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
> +{
> +    TPMTISState *tis = &s->s.tis;
> +
> +    if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
> +        return;
> +    }
> +
> +    if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
> +        (tis->loc[locty].inte & irqmask)) {
> +        dprintf("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
> +        qemu_irq_raise(s->s.tis.irq);
> +        tis->loc[locty].ints |= irqmask;
> +    }
> +}
> +
> +static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
> +{
> +    uint8_t l;
> +
> +    for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
> +        if (l == locty) {
> +            continue;
> +        }
> +        if ((s->s.tis.loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
> +            return 1;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
> +{
> +    TPMTISState *tis = &s->s.tis;
> +    int change = (s->s.tis.active_locty != new_active_locty);
> +
> +    if (change && TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) {
> +        /* reset flags on the old active locality */
> +        tis->loc[s->s.tis.active_locty].access &=
> +                 ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|TPM_TIS_ACCESS_REQUEST_USE);
> +        if (TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
> +            tis->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE) {
> +            tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
> +        }
> +    }
> +
> +    tis->active_locty = new_active_locty;

should this happen with invalid locality?

> +
> +    dprintf("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty);
> +
> +    if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
> +        /* set flags on the new active locality */
> +        tis->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
> +        tis->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
> +                                               TPM_TIS_ACCESS_SEIZE);
> +    }
> +
> +    if (change) {
> +        tpm_tis_raise_irq(s, tis->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
> +    }
> +}
> +
> +/* abort -- this function switches the locality */
> +static void tpm_tis_abort(TPMState *s, uint8_t locty)
> +{
> +    TPMTISState *tis = &s->s.tis;
> +
> +    tis->loc[locty].r_offset = 0;
> +    tis->loc[locty].w_offset = 0;
> +
> +    dprintf("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty);
> +
> +    /*
> +     * Need to react differently depending on who's aborting now and
> +     * which locality will become active afterwards.
> +     */
> +    if (tis->aborting_locty == tis->next_locty) {
> +        tis->loc[tis->aborting_locty].status = TPM_TIS_STATUS_READY;
> +        tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_READY;
> +        tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY);
> +    }
> +
> +    /* locality after abort is another one than the current one */
> +    tpm_tis_new_active_locality(s, tis->next_locty);
> +
> +    tis->next_locty = TPM_TIS_NO_LOCALITY;
> +    /* nobody's aborting a command anymore */
> +    tis->aborting_locty = TPM_TIS_NO_LOCALITY;
> +}
> +
> +/* prepare aborting current command */
> +static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
> +{
> +    TPMTISState *tis = &s->s.tis;
> +    uint8_t busy_locty;
> +
> +    tis->aborting_locty = locty;
> +    tis->next_locty = newlocty;  /* locality after successful abort */
> +
> +    /*
> +     * only abort a command using an interrupt if currently executing
> +     * a command AND if there's a valid connection to the vTPM.
> +     */
> +    for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
> +        if (tis->loc[busy_locty].status == TPM_TIS_STATUS_EXECUTION) {
> +            /* there is currently no way to interrupt the TPM's operations
> +               while it's executing a command; once the TPM is done and
> +               returns the buffer, it will switch to the next_locty; */
> +            dprintf("tpm_tis: Locality %d is busy - deferring abort\n",
> +                    busy_locty);
> +            return;
> +        }
> +    }
> +
> +    tpm_tis_abort(s, locty);
> +}
> +
> +/*
> + * Callback from the TPM to indicate that the response was received.
> + */
> +static void tpm_tis_receive_cb(TPMState *s, uint8_t locty)
> +{
> +    TPMTISState *tis = &s->s.tis;
> +
> +    qemu_mutex_lock(&s->state_lock);
> +
> +    tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE;
> +    tis->loc[locty].status = TPM_TIS_STATUS_COMPLETION;
> +    tis->loc[locty].r_offset = 0;
> +    tis->loc[locty].w_offset = 0;
> +
> +    if (TPM_TIS_IS_VALID_LOCTY(tis->next_locty)) {
> +        tpm_tis_abort(s, locty);
> +    }
> +
> +    qemu_cond_signal(&s->from_tpm_cond);
> +
> +    qemu_mutex_unlock(&s->state_lock);
> +
> +#ifndef RAISE_STS_IRQ
> +    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_DATA_AVAILABLE);
> +#else
> +    tpm_tis_raise_irq(s, locty,
> +                      TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
> +#endif
> +}
> +
> +/*
> + * read a byte of response data
> + * call this with s->state_lock held
> + */
> +static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
> +{
> +    TPMTISState *tis = &s->s.tis;
> +    uint32_t ret = TPM_TIS_NO_DATA_BYTE;
> +    uint16_t len;
> +
> +    if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
> +        len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
> +
> +        ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++];

what prevents this from being called when r_offset == len,
with a malicious guest?

> +        if (tis->loc[locty].r_offset >= len) {
> +            /* got last byte */
> +            tis->loc[locty].sts = TPM_TIS_STS_VALID;
> +#ifdef RAISE_STS_IRQ
> +            tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
> +#endif
> +        }
> +        dprintf("tpm_tis: tpm_tis_data_read byte 0x%02x   [%d]\n",
> +                ret, tis->loc[locty].r_offset-1);
> +    }
> +
> +    return ret;
> +}
> +
> +/*
> + * Read a register of the TIS interface
> + * See specs pages 33-63 for description of the registers
> + */
> +static uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
> +                                  unsigned size)
> +{
> +    TPMState *s = opaque;
> +    TPMTISState *tis = &s->s.tis;
> +    uint16_t offset = addr & 0xffc;
> +    uint8_t shift = (addr & 0x3) * 8;
> +    uint32_t val = 0xffffffff;
> +    uint8_t locty = tpm_tis_locality_from_addr(addr);

Apparently locty can be 0..6 but loc array only has 5 entries.
What if a guest accesses an invalid one?

> +
> +    qemu_mutex_lock(&s->state_lock);
> +
> +    if (s->be_driver->ops->had_startup_error(s->be_driver)) {
> +        qemu_mutex_unlock(&s->state_lock);
> +        return val;
> +    }
> +
> +    switch (offset) {
> +    case TPM_TIS_REG_ACCESS:
> +        /* never show the SEIZE flag even though we use it internally */
> +        val = tis->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
> +        /* the pending flag is alawys calculated */
> +        if (tpm_tis_check_request_use_except(s, locty)) {
> +            val |= TPM_TIS_ACCESS_PENDING_REQUEST;
> +        }
> +        val |= !s->be_driver->ops->get_tpm_established_flag(s->be_driver);
> +        break;
> +    case TPM_TIS_REG_INT_ENABLE:
> +        val = tis->loc[locty].inte;
> +        break;
> +    case TPM_TIS_REG_INT_VECTOR:
> +        val = tis->irq_num;
> +        break;
> +    case TPM_TIS_REG_INT_STATUS:
> +        val = tis->loc[locty].ints;
> +        break;
> +    case TPM_TIS_REG_INTF_CAPABILITY:
> +        val = TPM_TIS_CAPABILITIES_SUPPORTED;
> +        break;
> +    case TPM_TIS_REG_STS:
> +        if (tis->active_locty == locty) {
> +            if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
> +                val =  (tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer)
> +                        - tis->loc[locty].r_offset) << 8 | tis->loc[locty].sts;
> +            } else {
> +                val = (tis->loc[locty].w_buffer.size -
> +                       tis->loc[locty].w_offset) << 8 | tis->loc[locty].sts;
> +            }
> +        }
> +        break;
> +    case TPM_TIS_REG_DATA_FIFO:
> +        if (tis->active_locty == locty) {
> +            switch (tis->loc[locty].status) {
> +            case TPM_TIS_STATUS_COMPLETION:
> +                val = tpm_tis_data_read(s, locty);
> +                break;
> +            default:
> +                val = TPM_TIS_NO_DATA_BYTE;
> +                break;
> +            }
> +        }
> +        break;
> +    case TPM_TIS_REG_DID_VID:
> +        val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
> +        break;
> +    case TPM_TIS_REG_RID:
> +        val = TPM_TIS_TPM_RID;
> +        break;
> +    }
> +
> +    qemu_mutex_unlock(&s->state_lock);
> +
> +    if (shift) {
> +        val >>= shift;
> +    }
> +
> +    dprintf("tpm_tis:  read.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
> +
> +    return val;
> +}
> +
> +/*
> + * Write a value to a register of the TIS interface
> + * See specs pages 33-63 for description of the registers
> + */
> +static void tpm_tis_mmio_write_intern(void *opaque, target_phys_addr_t addr,
> +                                      uint64_t val, unsigned size,
> +                                      bool hw_access)
> +{
> +    TPMState *s = opaque;
> +    TPMTISState *tis = &s->s.tis;
> +    uint16_t off = addr & 0xfff;
> +    uint8_t locty = tpm_tis_locality_from_addr(addr);
> +    uint8_t active_locty, l;
> +    int c, set_new_locty = 1;
> +    uint16_t len;
> +
> +    dprintf("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
> +
> +    qemu_mutex_lock(&s->state_lock);
> +
> +    if (s->be_driver->ops->had_startup_error(s->be_driver)) {
> +        qemu_mutex_unlock(&s->state_lock);
> +        return;
> +    }
> +
> +    switch (off) {
> +    case TPM_TIS_REG_ACCESS:
> +
> +        if ((val & TPM_TIS_ACCESS_SEIZE)) {
> +            val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
> +                     TPM_TIS_ACCESS_ACTIVE_LOCALITY);
> +        }
> +
> +        active_locty = tis->active_locty;
> +
> +        if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
> +            /* give up locality if currently owned */
> +            if (tis->active_locty == locty) {
> +                dprintf("tpm_tis: Releasing locality %d\n", locty);
> +
> +                uint8_t newlocty = TPM_TIS_NO_LOCALITY;
> +                /* anybody wants the locality ? */
> +                for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
> +                    if ((tis->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
> +                        dprintf("tpm_tis: Locality %d requests use.\n", c);
> +                        newlocty = c;
> +                        break;
> +                    }
> +                }
> +                dprintf("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
> +                        "Next active locality: %d\n",
> +                        newlocty);
> +
> +                if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
> +                    set_new_locty = 0;
> +                    tpm_tis_prep_abort(s, locty, newlocty);
> +                } else {
> +                    active_locty = TPM_TIS_NO_LOCALITY;
> +                }
> +            } else {
> +                /* not currently the owner; clear a pending request */
> +                tis->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
> +            }
> +        }
> +
> +        if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
> +            tis->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
> +        }
> +
> +        if ((val & TPM_TIS_ACCESS_SEIZE)) {
> +            /* allow seize if a locality is active and the requesting
> +               locality is higher than the one that's active
> +               OR
> +               allow seize for requesting locality if no locality is
> +               active */

/*
 * multiline comments should look like this
 */

> +            while ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty) &&
> +                    locty > tis->active_locty) ||
> +                   (!TPM_TIS_IS_VALID_LOCTY(tis->active_locty))) {

Clearer as:
(locty > tis->active_locty ||
!TPM_TIS_IS_VALID_LOCTY(tis->active_locty))

> +
> +                /* already a pending SEIZE ? */
> +                if ((tis->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
> +                    break;
> +                }
> +
> +                /* check for ongoing seize by a higher locality */
> +                for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
> +                    if ((tis->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
> +                        break;
> +                    }
> +                }
> +
> +                /* cancel any seize by a lower locality */
> +                for (l = 0; l < locty - 1; l++) {
> +                    tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
> +                }
> +
> +                tis->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
> +                dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
> +                        "Locality %d seized from locality %d\n",
> +                        locty, tis->active_locty);
> +                dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
> +                set_new_locty = 0;
> +                tpm_tis_prep_abort(s, tis->active_locty, locty);
> +                break;
> +            }
> +        }
> +
> +        if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
> +            if (tis->active_locty != locty) {
> +                if (TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) {
> +                    tis->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
> +                } else {
> +                    /* no locality active -> make this one active now */
> +                    active_locty = locty;
> +                }
> +            }
> +        }
> +
> +        if (set_new_locty) {
> +            tpm_tis_new_active_locality(s, active_locty);
> +        }
> +
> +        break;
> +    case TPM_TIS_REG_INT_ENABLE:
> +        if (tis->active_locty != locty) {
> +            break;
> +        }
> +
> +        tis->loc[locty].inte = (val & (TPM_TIS_INT_ENABLED | (0x3 << 3) |
> +                                     TPM_TIS_INTERRUPTS_SUPPORTED));
> +        break;
> +    case TPM_TIS_REG_INT_VECTOR:
> +        /* hard wired -- ignore */
> +        break;
> +    case TPM_TIS_REG_INT_STATUS:
> +        if (tis->active_locty != locty) {
> +            break;
> +        }
> +
> +        /* clearing of interrupt flags */
> +        if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
> +            (tis->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
> +            tis->loc[locty].ints &= ~val;
> +            if (tis->loc[locty].ints == 0) {
> +                qemu_irq_lower(tis->irq);
> +                dprintf("tpm_tis: Lowering IRQ\n");
> +            }
> +        }
> +        tis->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
> +        break;
> +    case TPM_TIS_REG_STS:
> +        if (tis->active_locty != locty) {
> +            break;
> +        }
> +
> +        val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
> +                TPM_TIS_STS_RESPONSE_RETRY);
> +
> +        if (val == TPM_TIS_STS_COMMAND_READY) {
> +            switch (tis->loc[locty].status) {
> +
> +            case TPM_TIS_STATUS_READY:
> +                tis->loc[locty].w_offset = 0;
> +                tis->loc[locty].r_offset = 0;
> +            break;
> +
> +            case TPM_TIS_STATUS_IDLE:
> +                tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
> +                tis->loc[locty].status = TPM_TIS_STATUS_READY;
> +                tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
> +            break;
> +
> +            case TPM_TIS_STATUS_EXECUTION:
> +            case TPM_TIS_STATUS_RECEPTION:
> +                /* abort currently running command */
> +                dprintf("tpm_tis: %s: Initiating abort.\n",
> +                        __func__);
> +                tpm_tis_prep_abort(s, locty, locty);
> +            break;
> +
> +            case TPM_TIS_STATUS_COMPLETION:
> +                tis->loc[locty].w_offset = 0;
> +                tis->loc[locty].r_offset = 0;
> +                /* shortcut to ready state with C/R set */
> +                tis->loc[locty].status = TPM_TIS_STATUS_READY;
> +                if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
> +                    tis->loc[locty].sts   = TPM_TIS_STS_COMMAND_READY;
> +                    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
> +                }
> +            break;
> +
> +            }
> +        } else if (val == TPM_TIS_STS_TPM_GO) {
> +            switch (tis->loc[locty].status) {
> +            case TPM_TIS_STATUS_RECEPTION:
> +                tpm_tis_tpm_send(s, locty);
> +                break;
> +            default:
> +                /* ignore */
> +                break;
> +            }
> +        } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
> +            switch (tis->loc[locty].status) {
> +            case TPM_TIS_STATUS_COMPLETION:
> +                tis->loc[locty].r_offset = 0;
> +                tis->loc[locty].sts = TPM_TIS_STS_VALID |
> +                                      TPM_TIS_STS_DATA_AVAILABLE;
> +                break;
> +            default:
> +                /* ignore */
> +                break;
> +            }
> +        }
> +        break;
> +    case TPM_TIS_REG_DATA_FIFO:
> +        /* data fifo */
> +        if (tis->active_locty != locty) {
> +            break;
> +        }
> +
> +        if (tis->loc[locty].status == TPM_TIS_STATUS_IDLE ||
> +            tis->loc[locty].status == TPM_TIS_STATUS_EXECUTION ||
> +            tis->loc[locty].status == TPM_TIS_STATUS_COMPLETION) {
> +            /* drop the byte */
> +        } else {
> +            dprintf("tpm_tis: Byte to send to TPM: %02x\n", (uint8_t)val);
> +            if (tis->loc[locty].status == TPM_TIS_STATUS_READY) {
> +                tis->loc[locty].status = TPM_TIS_STATUS_RECEPTION;
> +                tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID;
> +            }
> +
> +            if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
> +                if (tis->loc[locty].w_offset < tis->loc[locty].w_buffer.size) {
> +                    tis->loc[locty].w_buffer.
> +                        buffer[tis->loc[locty].w_offset++] = (uint8_t)val;
> +                } else {
> +                    tis->loc[locty].sts = TPM_TIS_STS_VALID;
> +                }
> +            }
> +
> +            /* check for complete packet */
> +            if (tis->loc[locty].w_offset > 5 &&
> +                (tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
> +                /* we have a packet length - see if we have all of it */
> +#ifdef RAISE_STS_IRQ
> +                bool needIrq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID);
> +#endif
> +                len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
> +                if (len > tis->loc[locty].w_offset) {
> +                    tis->loc[locty].sts = TPM_TIS_STS_EXPECT |
> +                                          TPM_TIS_STS_VALID;
> +                } else {
> +                    /* packet complete */
> +                    tis->loc[locty].sts = TPM_TIS_STS_VALID;
> +                }
> +#ifdef RAISE_STS_IRQ
> +                if (needIrq) {
> +                    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
> +                }
> +#endif
> +            }
> +        }
> +        break;
> +    }
> +
> +    qemu_mutex_unlock(&s->state_lock);
> +}
> +
> +static void tpm_tis_mmio_write(void *opaque, target_phys_addr_t addr,
> +                               uint64_t val, unsigned size)
> +{
> +    return tpm_tis_mmio_write_intern(opaque, addr, val, size, false);
> +}
> +
> +static const MemoryRegionOps tpm_tis_memory_ops = {
> +    .read = tpm_tis_mmio_read,
> +    .write = tpm_tis_mmio_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

Are you sure? Most devices are BIG or LITTLE.

> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static int tpm_tis_do_startup_tpm(TPMState *s)
> +{
> +    return s->be_driver->ops->startup_tpm(s->be_driver);
> +}
> +
> +/*
> + * This function is called when the machine starts, resets or due to
> + * S3 resume.
> + */
> +static void tpm_tis_reset(DeviceState *d)
> +{
> +    TPMState *s = container_of(d, TPMState, busdev.qdev);
> +    TPMTISState *tis = &s->s.tis;
> +    int c;
> +
> +    s->tpm_initialized = false;
> +
> +    s->be_driver->ops->reset(s->be_driver);
> +
> +    tis->active_locty = TPM_TIS_NO_LOCALITY;
> +    tis->next_locty = TPM_TIS_NO_LOCALITY;
> +    tis->aborting_locty = TPM_TIS_NO_LOCALITY;
> +
> +    for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
> +        tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
> +        tis->loc[c].sts = 0;
> +        tis->loc[c].inte = (1 << 3);
> +        tis->loc[c].ints = 0;
> +        tis->loc[c].status = TPM_TIS_STATUS_IDLE;
> +
> +        tis->loc[c].w_offset = 0;
> +        s->be_driver->ops->realloc_buffer(&tis->loc[c].w_buffer);
> +        tis->loc[c].r_offset = 0;
> +        s->be_driver->ops->realloc_buffer(&tis->loc[c].r_buffer);
> +    }
> +
> +    tpm_tis_do_startup_tpm(s);
> +}
> +
> +static int tpm_tis_init(ISADevice *dev)
> +{
> +    TPMState *s = DO_UPCAST(TPMState, busdev, dev);

Let's stay consistent DO_UPCAST versus container_of.

> +    TPMTISState *tis = &s->s.tis;
> +    int rc;
> +
> +    qemu_mutex_init(&s->state_lock);
> +    qemu_cond_init(&s->from_tpm_cond);
> +    qemu_cond_init(&s->to_tpm_cond);
> +
> +    s->be_driver = qemu_find_tpm(s->backend);
> +    if (!s->be_driver) {
> +        error_report("tpm_tis: backend driver with id %s could not be "
> +                     "found.n\n", s->backend);
> +        return -1;
goto forward to have a single exit point?
> +    }
> +
> +    s->be_driver->fe_model = "tpm-tis";
> +
> +    if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) {
> +        return -1;

goto forward?

> +    }
> +
> +    isa_init_irq(dev, &tis->irq, tis->irq_num);

probably should validate irq_num so an illegal value
gives us an error not an assert.

> +
> +    memory_region_init_io(&s->mmio, &tpm_tis_memory_ops, s, "tpm-tis-mmio",
> +                          0x1000 * TPM_TIS_NUM_LOCALITIES);
> +    memory_region_add_subregion(get_system_memory(), TPM_TIS_ADDR_BASE,
> +                                &s->mmio);
> +
> +    rc = tpm_tis_do_startup_tpm(s);
> +    if (rc != 0) {
> +        goto err_destroy_memory;
> +    }
> +
> +    return 0;
> +
> + err_destroy_memory:

You must delete mmio as subregion before destroy.

> +    memory_region_destroy(&s->mmio);
> +
> +    return -1;
> +}
> +
> +static const VMStateDescription vmstate_tpm_tis = {
> +    .name = "tpm",
> +    .unmigratable = 1,

I think the passthrough backend is what makes
it unmigrateable, no?
If so in the future it would be better to get that info from backend.
For now I guess we are ok as is, but pls add a comment.

> +};
> +
> +static ISADeviceInfo tpm_tis_device_info = {
> +    .init         = tpm_tis_init,
> +    .qdev.name    = "tpm-tis",
> +    .qdev.size    = sizeof(TPMState),
> +    .qdev.vmsd    = &vmstate_tpm_tis,
> +    .qdev.reset   = tpm_tis_reset,
> +    .qdev.props = (Property[]) {
> +        DEFINE_PROP_UINT32("irq", TPMState,
> +                           s.tis.irq_num, TPM_TIS_IRQ),
> +        DEFINE_PROP_STRING("tpmdev", TPMState, backend),
> +        DEFINE_PROP_END_OF_LIST(),
> +    },
> +};
> +
> +static void tpm_tis_register_device(void)
> +{
> +    isa_qdev_register(&tpm_tis_device_info);

So this needs to be rebased to the QOM.

Also a question: looking at the linux drivers
there's pnp support but we don't have
that yet, right? So you need to force
non-pnp mode in guest?

> +}
> +
> +device_init(tpm_tis_register_device)
> -- 
> 1.7.6.4
> 

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-20  8:51   ` Michael S. Tsirkin
@ 2012-02-20 15:48     ` Stefan Berger
  2012-02-20 19:37       ` Michael S. Tsirkin
  2012-02-23 20:47       ` Stefan Berger
  0 siblings, 2 replies; 41+ messages in thread
From: Stefan Berger @ 2012-02-20 15:48 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/20/2012 03:51 AM, Michael S. Tsirkin wrote:
> On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
>> This patch adds the main code of the TPM frontend driver, the TPM TIS
>> interface, to Qemu. The code is largely based on the previous implementation
>> for Xen but has been significantly extended to meet the standard's
>> requirements, such as the support for changing of localities and all the
>> functionality of the available flags.
>>
> looks good overall
> some questions on erro handling and behaviour with
> a malicious guest, below.
>

[...]
>> +#ifndef RAISE_STS_IRQ
>> +
>> +# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
>> +                                       TPM_TIS_INT_DATA_AVAILABLE   | \
>> +                                       TPM_TIS_INT_COMMAND_READY)
>> +
>> +#else
>> +
>> +# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
>> +                                       TPM_TIS_INT_DATA_AVAILABLE   | \
>> +                                       TPM_TIS_INT_STS_VALID | \
>> +                                       TPM_TIS_INT_COMMAND_READY)
>> +
> let's keep these as #define
>

Will fix it.
>> +/* utility functions */
>> +
>> +static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
>> +{
>> +    return (uint8_t)((addr>>  12)&  0x7);
>> +}
>> +
> note this can give a number 0..6

Yes, but that's ok. We are only registering the MMIO region [fed4 0000, 
fed4 5000[, so anything larger than 4 cannot actually come from this 
function.


>> +static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
>> +{
>> +    TPMTISState *tis =&s->s.tis;
>> +    int change = (s->s.tis.active_locty != new_active_locty);
>> +
>> +    if (change&&  TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) {
>> +        /* reset flags on the old active locality */
>> +        tis->loc[s->s.tis.active_locty].access&=
>> +                 ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|TPM_TIS_ACCESS_REQUEST_USE);
>> +        if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)&&
>> +            tis->loc[new_active_locty].access&  TPM_TIS_ACCESS_SEIZE) {
>> +            tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
>> +        }
>> +    }
>> +
>> +    tis->active_locty = new_active_locty;
> should this happen with invalid locality?
>



>> + * read a byte of response data
>> + * call this with s->state_lock held
>> + */
>> +static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
>> +{
>> +    TPMTISState *tis =&s->s.tis;
>> +    uint32_t ret = TPM_TIS_NO_DATA_BYTE;
>> +    uint16_t len;
>> +
>> +    if ((tis->loc[locty].sts&  TPM_TIS_STS_DATA_AVAILABLE)) {
>> +        len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
>> +
>> +        ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++];
> what prevents this from being called when r_offset == len,
> with a malicious guest?
>

The device user has no control over the index of the byte stream he 
wants to read. Once he starts reading the bytes from the device starting 
at index 0, the index automatically increases, as seen above. The flag 
TPM_TIS_STS_DATA_AVAILABLE is set for as long as there are bytes 
available. The user can see this flag in the status register. Then once...

>> +        if (tis->loc[locty].r_offset>= len) {
>> +            /* got last byte */
>> +            tis->loc[locty].sts = TPM_TIS_STS_VALID;

... all bytes have been read the status flag changes and no more bytes 
can be read. The tis code here also goes by these flags as you can see.


>> +/*
>> + * Read a register of the TIS interface
>> + * See specs pages 33-63 for description of the registers
>> + */
>> +static uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
>> +                                  unsigned size)
>> +{
>> +    TPMState *s = opaque;
>> +    TPMTISState *tis =&s->s.tis;
>> +    uint16_t offset = addr&  0xffc;
>> +    uint8_t shift = (addr&  0x3) * 8;
>> +    uint32_t val = 0xffffffff;
>> +    uint8_t locty = tpm_tis_locality_from_addr(addr);
> Apparently locty can be 0..6 but loc array only has 5 entries.
> What if a guest accesses an invalid one?

See comment at the beginning. Any locality beyond value '4' cannot be 
reached since it's not within the MMIO region of the device.

>> +            /* allow seize if a locality is active and the requesting
>> +               locality is higher than the one that's active
>> +               OR
>> +               allow seize for requesting locality if no locality is
>> +               active */
> /*
>   * multiline comments should look like this
>   */
>

  I will fix 'them'.


>> +            while ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty)&&
>> +                    locty>  tis->active_locty) ||
>> +                   (!TPM_TIS_IS_VALID_LOCTY(tis->active_locty))) {
> Clearer as:
> (locty>  tis->active_locty ||
> !TPM_TIS_IS_VALID_LOCTY(tis->active_locty))
>

Ok.

>> +static const MemoryRegionOps tpm_tis_memory_ops = {
>> +    .read = tpm_tis_mmio_read,
>> +    .write = tpm_tis_mmio_write,
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
> Are you sure? Most devices are BIG or LITTLE.
>

Right, it should be BIG endian. I'll fix it. Luckily there are only a 
few 32bit accesses (and no 16bit access) also in SeaBIOS and those that 
did accessed it via 32 bit didn't fail due to the mix-up (excluding the 
test suite).

>> +    .valid = {
>> +        .min_access_size = 1,
>> +        .max_access_size = 4,
>> +    },
>> +};
>> +
>> +static int tpm_tis_do_startup_tpm(TPMState *s)
>> +{
>> +    return s->be_driver->ops->startup_tpm(s->be_driver);
>> +}
>> +
>> +/*
>> + * This function is called when the machine starts, resets or due to
>> + * S3 resume.
>> + */
>> +static void tpm_tis_reset(DeviceState *d)
>> +{
>> +    TPMState *s = container_of(d, TPMState, busdev.qdev);
>> +    TPMTISState *tis =&s->s.tis;
>> +    int c;
>> +
>> +    s->tpm_initialized = false;
>> +
>> +    s->be_driver->ops->reset(s->be_driver);
>> +
>> +    tis->active_locty = TPM_TIS_NO_LOCALITY;
>> +    tis->next_locty = TPM_TIS_NO_LOCALITY;
>> +    tis->aborting_locty = TPM_TIS_NO_LOCALITY;
>> +
>> +    for (c = 0; c<  TPM_TIS_NUM_LOCALITIES; c++) {
>> +        tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
>> +        tis->loc[c].sts = 0;
>> +        tis->loc[c].inte = (1<<  3);
>> +        tis->loc[c].ints = 0;
>> +        tis->loc[c].status = TPM_TIS_STATUS_IDLE;
>> +
>> +        tis->loc[c].w_offset = 0;
>> +        s->be_driver->ops->realloc_buffer(&tis->loc[c].w_buffer);
>> +        tis->loc[c].r_offset = 0;
>> +        s->be_driver->ops->realloc_buffer(&tis->loc[c].r_buffer);
>> +    }
>> +
>> +    tpm_tis_do_startup_tpm(s);
>> +}
>> +
>> +static int tpm_tis_init(ISADevice *dev)
>> +{
>> +    TPMState *s = DO_UPCAST(TPMState, busdev, dev);
> Let's stay consistent DO_UPCAST versus container_of.

Ok.

>> +    TPMTISState *tis =&s->s.tis;
>> +    int rc;
>> +
>> +    qemu_mutex_init(&s->state_lock);
>> +    qemu_cond_init(&s->from_tpm_cond);
>> +    qemu_cond_init(&s->to_tpm_cond);
>> +
>> +    s->be_driver = qemu_find_tpm(s->backend);
>> +    if (!s->be_driver) {
>> +        error_report("tpm_tis: backend driver with id %s could not be "
>> +                     "found.n\n", s->backend);
>> +        return -1;
> goto forward to have a single exit point?

Will fix it.

>> +    }
>> +
>> +    s->be_driver->fe_model = "tpm-tis";
>> +
>> +    if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) {
>> +        return -1;
> goto forward?
>
>> +    }
>> +
>> +    isa_init_irq(dev,&tis->irq, tis->irq_num);
> probably should validate irq_num so an illegal value
> gives us an error not an assert.
>

Ok.

>> +
>> +    memory_region_init_io(&s->mmio,&tpm_tis_memory_ops, s, "tpm-tis-mmio",
>> +                          0x1000 * TPM_TIS_NUM_LOCALITIES);
>> +    memory_region_add_subregion(get_system_memory(), TPM_TIS_ADDR_BASE,
>> +&s->mmio);
>> +
>> +    rc = tpm_tis_do_startup_tpm(s);
>> +    if (rc != 0) {
>> +        goto err_destroy_memory;
>> +    }
>> +
>> +    return 0;
>> +
>> + err_destroy_memory:
> You must delete mmio as subregion before destroy.
>

Will fix it.

>> +    memory_region_destroy(&s->mmio);
>> +
>> +    return -1;
>> +}
>> +
>> +static const VMStateDescription vmstate_tpm_tis = {
>> +    .name = "tpm",
>> +    .unmigratable = 1,
> I think the passthrough backend is what makes
> it unmigrateable, no?
> If so in the future it would be better to get that info from backend.
> For now I guess we are ok as is, but pls add a comment.
>

In the patches following this passthrough driver I am pushing this into 
the backend where I then register a migration blocker. While only the 
passthrough driver is here, this should be fine.

>> +};
>> +
>> +static ISADeviceInfo tpm_tis_device_info = {
>> +    .init         = tpm_tis_init,
>> +    .qdev.name    = "tpm-tis",
>> +    .qdev.size    = sizeof(TPMState),
>> +    .qdev.vmsd    =&vmstate_tpm_tis,
>> +    .qdev.reset   = tpm_tis_reset,
>> +    .qdev.props = (Property[]) {
>> +        DEFINE_PROP_UINT32("irq", TPMState,
>> +                           s.tis.irq_num, TPM_TIS_IRQ),
>> +        DEFINE_PROP_STRING("tpmdev", TPMState, backend),
>> +        DEFINE_PROP_END_OF_LIST(),
>> +    },
>> +};
>> +
>> +static void tpm_tis_register_device(void)
>> +{
>> +    isa_qdev_register(&tpm_tis_device_info);
> So this needs to be rebased to the QOM.
>

I followed the tree.

> Also a question: looking at the linux drivers
> there's pnp support but we don't have
> that yet, right? So you need to force
> non-pnp mode in guest?
>

You only need to force non-pnp mode in the guest if you don't have 
SeaBIOS with the TPM support. There I create the SSDT for the TPM device 
that then triggers the pnp driver mode inside Linux.


Thanks for the review.

    Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-20 15:48     ` Stefan Berger
@ 2012-02-20 19:37       ` Michael S. Tsirkin
  2012-02-20 19:58         ` Stefan Berger
  2012-02-23 20:47       ` Stefan Berger
  1 sibling, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-20 19:37 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Mon, Feb 20, 2012 at 10:48:09AM -0500, Stefan Berger wrote:
> On 02/20/2012 03:51 AM, Michael S. Tsirkin wrote:
> >On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
> >>This patch adds the main code of the TPM frontend driver, the TPM TIS
> >>interface, to Qemu. The code is largely based on the previous implementation
> >>for Xen but has been significantly extended to meet the standard's
> >>requirements, such as the support for changing of localities and all the
> >>functionality of the available flags.
> >>
> >looks good overall
> >some questions on erro handling and behaviour with
> >a malicious guest, below.
> >
> 
> [...]
> >>+#ifndef RAISE_STS_IRQ
> >>+
> >>+# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
> >>+                                       TPM_TIS_INT_DATA_AVAILABLE   | \
> >>+                                       TPM_TIS_INT_COMMAND_READY)
> >>+
> >>+#else
> >>+
> >>+# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
> >>+                                       TPM_TIS_INT_DATA_AVAILABLE   | \
> >>+                                       TPM_TIS_INT_STS_VALID | \
> >>+                                       TPM_TIS_INT_COMMAND_READY)
> >>+
> >let's keep these as #define
> >
> 
> Will fix it.
> >>+/* utility functions */
> >>+
> >>+static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
> >>+{
> >>+    return (uint8_t)((addr>>  12)&  0x7);
> >>+}
> >>+
> >note this can give a number 0..6
> 
> Yes, but that's ok. We are only registering the MMIO region [fed4
> 0000, fed4 5000[, so anything larger than 4 cannot actually come
> from this function.

I see 12 matches 0x1000 where region is created.
I think there should be a macro like TPM_TIS_LOCALITY_SHIFT
and then you can register region of size
 NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT.

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

* Re: [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2012-02-20 19:51   ` Michael S. Tsirkin
  2012-02-20 20:25     ` Stefan Berger
  2012-02-20 20:01   ` Michael S. Tsirkin
  2012-02-20 21:30   ` Michael S. Tsirkin
  2 siblings, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-20 19:51 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Wed, Dec 14, 2011 at 08:43:20AM -0500, Stefan Berger wrote:
> >From Andreas Niederl's original posting with adaptations where necessary:
> 
> This patch is based of off version 9 of Stefan Berger's patch series
>   "Qemu Trusted Platform Module (TPM) integration"
> and adds a new backend driver for it.
> 
> This patch adds a passthrough backend driver for passing commands sent to the
> emulated TPM device directly to a TPM device opened on the host machine.
> 
> Thus it is possible to use a hardware TPM device in a system running on QEMU,
> providing the ability to access a TPM in a special state (e.g. after a Trusted
> Boot).
> 
> This functionality is being used in the acTvSM Trusted Virtualization Platform
> which is available on [1].
> 
> Usage example:
>   qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
>                      -device tpm-tis,tpmdev=tpm0 \
>                      -cdrom test.iso -boot d
> 
> Some notes about the host TPM:
> The TPM needs to be enabled and activated. If that's not the case one
> has to go through the BIOS/UEFI and enable and activate that TPM for TPM
> commands to work as expected.
> It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
> command line or 'modprobe tpm_tis force=1' in case of using it as a module.
> 
> Regards,
> Andreas Niederl, Stefan Berger
> 
> [1] http://trustedjava.sourceforge.net/
> 
> Signed-off-by: Andreas Niederl <andreas.niederl@iaik.tugraz.at>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

So this was mentioned by Blue Swirl and Anthony and
I have to agree: it's not clear why this wants its own
thread.
I would go further and claim that forcing a threaded build for a
periferal just looks wrong. What's going on, select+unblocking access
does not work for tpm?
If this behaves like  a normal device
we won't need locking ...


> ---
>  Makefile.target      |    1 +
>  configure            |    3 +
>  hw/tpm_passthrough.c |  493 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  qemu-options.hx      |   33 ++++
>  tpm.c                |   16 ++
>  tpm.h                |   33 ++++
>  6 files changed, 579 insertions(+), 0 deletions(-)
>  create mode 100644 hw/tpm_passthrough.c
> 
> diff --git a/Makefile.target b/Makefile.target
> index 0e1c032..2744a5c 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -207,6 +207,7 @@ obj-$(CONFIG_NO_KVM) += kvm-stub.o
>  obj-y += memory.o
>  obj-y += tpm.o
>  obj-$(CONFIG_TPM) += tpm_tis.o
> +obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
>  LIBS+=-lz
>  
>  QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
> diff --git a/configure b/configure
> index 385feb4..25995bc 100755
> --- a/configure
> +++ b/configure
> @@ -3721,6 +3721,9 @@ fi
>  
>  if test "$tpm" = "yes"; then
>    if test "$target_softmmu" = "yes" ; then
> +    if test "$linux" = "yes" ; then
> +      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
> +    fi
>      echo "CONFIG_TPM=y" >> $config_host_mak
>    fi
>  fi
> diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
> new file mode 100644
> index 0000000..76e4e35
> --- /dev/null
> +++ b/hw/tpm_passthrough.c
> @@ -0,0 +1,493 @@
> +/*
> + *  passthrough TPM driver
> + *
> + *  Copyright (c) 2010, 2011 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-common.h"
> +#include "qemu-error.h"
> +#include "tpm.h"
> +#include "hw/hw.h"
> +#include "hw/tpm_tis.h"
> +#include "hw/pc.h"
> +
> +/* #define DEBUG_TPM */
> +
> +#ifdef DEBUG_TPM
> +#define dprintf(fmt, ...) \
> +    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define dprintf(fmt, ...) \
> +    do { } while (0)
> +#endif
> +
> +/* data structures */
> +
> +typedef struct TPMPassthruThreadParams {
> +    TPMState *tpm_state;
> +
> +    TPMRecvDataCB *recv_data_callback;
> +    TPMBackend *tb;
> +} TPMPassthruThreadParams;
> +
> +struct TPMPassthruState {
> +    QemuThread thread;
> +    bool thread_terminate;
> +    bool thread_running;
> +
> +    TPMPassthruThreadParams tpm_thread_params;
> +
> +    char tpm_dev[64];
> +    int tpm_fd;
> +    bool had_startup_error;
> +};
> +
> +#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
> +
> +/* borrowed from qemu-char.c */
> +static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
> +{
> +    int ret, len1;
> +
> +    len1 = len;
> +    while (len1 > 0) {
> +        ret = write(fd, buf, len1);
> +        if (ret < 0) {
> +            if (errno != EINTR && errno != EAGAIN) {
> +                return -1;
> +            }
> +        } else if (ret == 0) {
> +            break;
> +        } else {
> +            buf  += ret;
> +            len1 -= ret;
> +        }
> +    }
> +    return len - len1;
> +}
> +
> +static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
> +{
> +    int ret, len1;
> +    uint8_t *buf1;
> +
> +    len1 = len;
> +    buf1 = buf;
> +    while ((len1 > 0) && (ret = read(fd, buf1, len1)) != 0) {
> +        if (ret < 0) {
> +            if (errno != EINTR && errno != EAGAIN) {
> +                return -1;
> +            }
> +        } else {
> +            buf1 += ret;
> +            len1 -= ret;
> +        }
> +    }
> +    return len - len1;
> +}
> +
> +static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
> +{
> +    return be32_to_cpu(*(uint32_t *)&buf[2]);
> +}
> +
> +static int tpm_passthrough_unix_tx_bufs(int tpm_fd,
> +                                        const uint8_t *in, uint32_t in_len,
> +                                        uint8_t *out, uint32_t out_len)
> +{
> +    int ret;
> +
> +    ret = tpm_passthrough_unix_write(tpm_fd, in, in_len);
> +    if (ret < 0) {
> +        error_report("tpm_passthrough: error while transmitting data "
> +                     "to TPM: %s (%i)\n",
> +                     strerror(errno), errno);
> +        goto err_exit;
> +    }
> +
> +    ret = tpm_passthrough_unix_read(tpm_fd, out, out_len);
> +    if (ret < 0) {
> +        error_report("tpm_passthrough: error while reading data from "
> +                     "TPM: %s (%i)\n",
> +                     strerror(errno), errno);
> +    } else if (ret < sizeof(struct tpm_resp_hdr) ||
> +               tpm_passthrough_get_size_from_buffer(out) != ret) {
> +        ret = -1;
> +        error_report("tpm_passthrough: received invalid response "
> +                     "packet from TPM\n");
> +    }
> +
> +err_exit:
> +    if (ret < 0) {
> +        tpm_write_fatal_error_response(out, out_len);
> +    }
> +
> +    return ret;
> +}
> +
> +static int tpm_passthrough_unix_transfer(int tpm_fd,
> +                                         const TPMLocality *cmd_locty)
> +{
> +    return tpm_passthrough_unix_tx_bufs(tpm_fd,
> +                                        cmd_locty->w_buffer.buffer,
> +                                        cmd_locty->w_offset,
> +                                        cmd_locty->r_buffer.buffer,
> +                                        cmd_locty->r_buffer.size);
> +}
> +
> +static void *tpm_passthrough_thread(void *d)
> +{
> +    TPMPassthruThreadParams *thr_parms = d;
> +    TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
> +    uint32_t in_len;
> +
> +    dprintf("tpm_passthrough: THREAD IS STARTING\n");
> +
> +    /* start command processing */
> +    while (!tpm_pt->thread_terminate) {
> +        /* receive and handle commands */
> +        in_len = 0;
> +        do {
> +            dprintf("tpm_passthrough: waiting for commands...\n");
> +
> +            if (tpm_pt->thread_terminate) {
> +                break;
> +            }
> +
> +            qemu_mutex_lock(&thr_parms->tpm_state->state_lock);
> +
> +            /* in case we were to slow and missed the signal, the
> +               to_tpm_execute boolean tells us about a pending command */
> +            if (!thr_parms->tpm_state->to_tpm_execute) {
> +                qemu_cond_wait(&thr_parms->tpm_state->to_tpm_cond,
> +                               &thr_parms->tpm_state->state_lock);
> +            }
> +
> +            thr_parms->tpm_state->to_tpm_execute = false;
> +
> +            qemu_mutex_unlock(&thr_parms->tpm_state->state_lock);
> +
> +            if (tpm_pt->thread_terminate) {
> +                break;
> +            }
> +
> +            in_len = thr_parms->tpm_state->cmd_locty->w_offset;
> +
> +            tpm_passthrough_unix_transfer(tpm_pt->tpm_fd,
> +                                          thr_parms->tpm_state->cmd_locty);
> +
> +            thr_parms->recv_data_callback(thr_parms->tpm_state,
> +                                          thr_parms->tpm_state->command_locty);
> +        } while (in_len > 0);
> +    }
> +
> +    dprintf("tpm_passthrough: THREAD IS ENDING\n");
> +
> +    tpm_pt->thread_running = false;
> +
> +    return NULL;
> +}
> +
> +static void tpm_passthrough_terminate_tpm_thread(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    if (!tpm_pt->thread_running) {
> +        return;
> +    }
> +
> +    dprintf("tpm_passthrough: TERMINATING RUNNING TPM THREAD\n");
> +
> +    if (!tpm_pt->thread_terminate) {
> +        tpm_pt->thread_terminate = true;
> +
> +        qemu_mutex_lock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
> +        qemu_cond_signal(&tpm_pt->tpm_thread_params.tpm_state->to_tpm_cond);
> +        qemu_mutex_unlock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
> +
> +        if (tpm_pt->thread_running) {
> +            qemu_thread_join(&tpm_pt->thread);
> +        }
> +        memset(&tpm_pt->thread, 0, sizeof(tpm_pt->thread));
> +    }
> +}
> +
> +/**
> + * Start the TPM (thread). If it had been started before, then terminate
> + * and start it again.
> + */
> +static int tpm_passthrough_do_startup_tpm(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    /* terminate a running TPM */
> +    tpm_passthrough_terminate_tpm_thread(tb);
> +
> +    /* reset the flag so the thread keeps on running */
> +    tpm_pt->thread_terminate = false;
> +
> +    qemu_thread_create(&tpm_pt->thread, tpm_passthrough_thread,
> +                       &tpm_pt->tpm_thread_params, 0);
> +
> +    tpm_pt->thread_running = true;
> +
> +    return 0;
> +}
> +
> +static int tpm_passthrough_startup_tpm(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +    int rc;
> +
> +    rc = tpm_passthrough_do_startup_tpm(tb);
> +    if (rc) {
> +        tpm_pt->had_startup_error = true;
> +    }
> +    return rc;
> +}
> +
> +static void tpm_passthrough_reset(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    dprintf("tpm_passthrough: CALL TO TPM_RESET!\n");
> +
> +    tpm_passthrough_terminate_tpm_thread(tb);
> +
> +    tpm_pt->had_startup_error = false;
> +}
> +
> +static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
> +                                TPMRecvDataCB *recv_data_cb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    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;
> +
> +    tpm_pt->thread_running = false;
> +
> +    return 0;
> +}
> +
> +static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
> +{
> +    return false;
> +}
> +
> +static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    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);
> +        if (sb->buffer != NULL) {
> +            sb->size = wanted_size;
> +        } else {
> +            sb->size = 0;
> +        }
> +    }
> +    return sb->size;
> +}
> +
> +static const char *tpm_passthrough_create_desc(void)
> +{
> +    return "Passthrough TPM backend driver";
> +}
> +
> +/* A basic test of a TPM device. We expect a well formatted response header
> + * (error response is fine) within one second.
> + */
> +static int tpm_passthrough_test_tpmdev(int fd)
> +{
> +    struct tpm_req_hdr req = {
> +        .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
> +        .len = cpu_to_be32(sizeof(req)),
> +        .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
> +    };
> +    struct tpm_resp_hdr *resp;
> +    fd_set readfds;
> +    int n;
> +    struct timeval tv = {
> +        .tv_sec = 1,
> +        .tv_usec = 0,
> +    };
> +    unsigned char buf[1024];
> +
> +    n = write(fd, &req, sizeof(req));
> +    if (n < 0) {
> +        return errno;
> +    }
> +    if (n != sizeof(req)) {
> +        return EFAULT;
> +    }
> +
> +    FD_ZERO(&readfds);
> +    FD_SET(fd, &readfds);
> +
> +    /* wait for a second */
> +    n = select(fd + 1, &readfds, NULL, NULL, &tv);
> +    if (n != 1) {
> +        return errno;
> +    }
> +
> +    n = read(fd, &buf, sizeof(buf));
> +    if (n < sizeof(struct tpm_resp_hdr)) {
> +        return EFAULT;
> +    }
> +
> +    resp = (struct tpm_resp_hdr *)buf;
> +    /* check the header */
> +    if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND ||
> +        be32_to_cpu(resp->len) != n) {
> +        return EBADMSG;
> +    }
> +
> +    return 0;
> +}
> +
> +static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
> +{
> +    const char *value;
> +    char buf[64];
> +    int n;
> +
> +    value = qemu_opt_get(opts, "path");
> +    if (!value) {
> +        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
> +    }
> +
> +    n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
> +                 "%s", value);
> +
> +    if (n >= sizeof(tb->s.tpm_pt->tpm_dev)) {
> +        error_report("TPM device path is too long.\n");
> +        goto err_exit;
> +    }
> +
> +    snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
> +
> +    tb->parameters = g_strdup(buf);
> +
> +    if (tb->parameters == NULL) {
> +        return 1;
> +    }
> +
> +    tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
> +    if (tb->s.tpm_pt->tpm_fd < 0) {
> +        error_report("Cannot access TPM device using '%s'.\n",
> +                     tb->s.tpm_pt->tpm_dev);
> +        goto err_exit;
> +    }
> +
> +    if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) {
> +        error_report("'%s' is not a TPM device.\n",
> +                     tb->s.tpm_pt->tpm_dev);
> +        goto err_close_tpmdev;
> +    }
> +
> +    return 0;
> +
> + err_close_tpmdev:
> +    close(tb->s.tpm_pt->tpm_fd);
> +    tb->s.tpm_pt->tpm_fd = -1;
> +
> + err_exit:
> +    g_free(tb->parameters);
> +    tb->parameters = NULL;
> +
> +    return 1;
> +}
> +
> +
> +static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
> +{
> +    TPMBackend *tb;
> +
> +    tb = g_malloc0(sizeof(TPMBackend));
> +    if (tb == NULL) {
> +        error_report("tpm_passthrough: Could not allocate memory.\n");
> +        return NULL;
> +    }
> +
> +    tb->s.tpm_pt = g_malloc0(sizeof(TPMPassthruState));
> +    if (tb->s.tpm_pt == NULL) {
> +        error_report("tpm_passthrough: Could not allocate memory.\n");
> +        g_free(tb);
> +        return NULL;
> +    }
> +
> +    tb->id = g_strdup(id);
> +    if (tb->id == NULL) {
> +        error_report("tpm_passthrough: Could not allocate memory.\n");
> +        goto err_exit;
> +    }
> +
> +    tb->ops = &tpm_passthrough_driver;
> +
> +    if (tpm_passthrough_handle_device_opts(opts, tb)) {
> +        goto err_exit;
> +    }
> +
> +    return tb;
> +
> +err_exit:
> +    g_free(tb->id);
> +    g_free(tb->s.tpm_pt);
> +    g_free(tb);
> +
> +    return NULL;
> +}
> +
> +static void tpm_passthrough_destroy(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    tpm_passthrough_terminate_tpm_thread(tb);
> +
> +    close(tpm_pt->tpm_fd);
> +
> +    g_free(tb->id);
> +    g_free(tb->parameters);
> +    g_free(tb->s.tpm_pt);
> +    g_free(tb);
> +}
> +
> +const TPMDriverOps tpm_passthrough_driver = {
> +    .id                       = "passthrough",
> +    .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,
> +    .reset                    = tpm_passthrough_reset,
> +    .had_startup_error        = tpm_passthrough_get_startup_error,
> +    .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
> +};
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 40a63b1..10ae499 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1916,6 +1916,7 @@ 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}.
>  
>  The specific backend type will determine the applicable options.
>  The @code{-tpmdev} options requires a @code{-device} option.
> @@ -1927,6 +1928,38 @@ Use ? to print all available TPM backend types.
>  qemu -tpmdev ?
>  @end example
>  
> +@item -tpmdev passthrough, id=@var{id}, path=@var{path}
> +
> +(Linux-host only) Enable access to the host's TPM using the passthrough
> +driver.
> +
> +@option{path} specifies the path to the host's TPM device, i.e., on
> +a Linux host this would be @code{/dev/tpm0}.
> +@option{path} is optional and by default @code{/dev/tpm0} is used.
> +
> +Some notes about using the host's TPM with the passthrough driver:
> +
> +The TPM device accessed by the passthrough driver must not be
> +used by any other application on the host.
> +
> +Since the host's firmware (BIOS/UEFI) has already initialized the TPM,
> +the VM's firmware (BIOS/UEFI) will not be able to initialize the
> +TPM again and may therefore not show a TPM-specific menu that would
> +otherwise allow the user to configure the TPM, e.g., allow the user to
> +enable/disable or activate/deactivate the TPM.
> +Further, if TPM ownership is released from within a VM then the host's TPM
> +will get disabled and deactivated. To enable and activate the
> +TPM again afterwards, the host has to be rebooted and the user is
> +required to enter the firmware's menu to enable and activate the TPM.
> +If the TPM is left disabled and/or deactivated most TPM commands will fail.
> +
> +To create a passthrough TPM use the following two options:
> +@example
> +-tpmdev passthrough,id=tpm0 -device tpm-tis,tpmdev=tpm0
> +@end example
> +Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
> +@code{tpmdev=tpm0} in the device option.
> +
>  @end table
>  
>  ETEXI
> diff --git a/tpm.c b/tpm.c
> index 408d319..ee38107 100644
> --- a/tpm.c
> +++ b/tpm.c
> @@ -24,9 +24,25 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =
>  #ifdef CONFIG_TPM
>  
>  static const TPMDriverOps *bes[] = {
> +#ifdef CONFIG_TPM_PASSTHROUGH
> +    &tpm_passthrough_driver,
> +#endif
>      NULL,
>  };
>  
> +/* Write an error message in the given output buffer.
> + */
> +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);
> +    }
> +}
> +
>  const TPMDriverOps *tpm_get_backend_driver(const char *id)
>  {
>      int i;
> diff --git a/tpm.h b/tpm.h
> index 075de3f..1b278cc 100644
> --- a/tpm.h
> +++ b/tpm.h
> @@ -18,12 +18,18 @@
>  struct TPMDriverOps;
>  typedef struct TPMDriverOps TPMDriverOps;
>  
> +typedef struct TPMPassthruState TPMPassthruState;
> +
>  typedef struct TPMBackend {
>      char *id;
>      const char *fe_model;
>      char *parameters;
>      const TPMDriverOps *ops;
>  
> +    union {
> +        TPMPassthruState *tpm_pt;
> +    } s;
> +
>      QLIST_ENTRY(TPMBackend) list;
>  } TPMBackend;
>  
> @@ -76,11 +82,38 @@ struct TPMDriverOps {
>  
>  #define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
>  
> +struct tpm_req_hdr {
> +    uint16_t tag;
> +    uint32_t len;
> +    uint32_t ordinal;
> +} __attribute__((packed));
> +
> +struct tpm_resp_hdr {
> +    uint16_t tag;
> +    uint32_t len;
> +    uint32_t errcode;
> +} __attribute__((packed));
> +
> +#define TPM_TAG_RQU_COMMAND       0xc1
> +#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2
> +#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3
> +
> +#define TPM_TAG_RSP_COMMAND       0xc4
> +#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5
> +#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6
> +
> +#define TPM_FAIL                  9
> +
> +#define TPM_ORD_GetTicks          0xf1
> +
>  int tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
>  int tpm_init(void);
>  void tpm_cleanup(void);
>  TPMBackend *qemu_find_tpm(const char *id);
>  void tpm_display_backend_drivers(void);
>  const TPMDriverOps *tpm_get_backend_driver(const char *id);
> +void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len);
> +
> +extern const TPMDriverOps tpm_passthrough_driver;
>  
>  #endif /* QEMU_TPM_H */
> -- 
> 1.7.6.4
> 

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-20 19:37       ` Michael S. Tsirkin
@ 2012-02-20 19:58         ` Stefan Berger
  0 siblings, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2012-02-20 19:58 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/20/2012 02:37 PM, Michael S. Tsirkin wrote:
> On Mon, Feb 20, 2012 at 10:48:09AM -0500, Stefan Berger wrote:
>> On 02/20/2012 03:51 AM, Michael S. Tsirkin wrote:
>>> On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
>>>
>> Yes, but that's ok. We are only registering the MMIO region [fed4
>> 0000, fed4 5000[, so anything larger than 4 cannot actually come
>> from this function.
> I see 12 matches 0x1000 where region is created.
> I think there should be a macro like TPM_TIS_LOCALITY_SHIFT
> and then you can register region of size
>   NUM_LOCALITIES<<  TPM_TIS_LOCALITY_SHIFT.
>
>

Done.

    Stefan

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

* Re: [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
  2012-02-20 19:51   ` Michael S. Tsirkin
@ 2012-02-20 20:01   ` Michael S. Tsirkin
  2012-02-20 21:12     ` Stefan Berger
  2012-02-20 21:30   ` Michael S. Tsirkin
  2 siblings, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-20 20:01 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Wed, Dec 14, 2011 at 08:43:20AM -0500, Stefan Berger wrote:
> >From Andreas Niederl's original posting with adaptations where necessary:
> 
> This patch is based of off version 9 of Stefan Berger's patch series
>   "Qemu Trusted Platform Module (TPM) integration"
> and adds a new backend driver for it.
> 
> This patch adds a passthrough backend driver for passing commands sent to the
> emulated TPM device directly to a TPM device opened on the host machine.
> 
> Thus it is possible to use a hardware TPM device in a system running on QEMU,
> providing the ability to access a TPM in a special state (e.g. after a Trusted
> Boot).
> 
> This functionality is being used in the acTvSM Trusted Virtualization Platform
> which is available on [1].
> 
> Usage example:
>   qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
>                      -device tpm-tis,tpmdev=tpm0 \
>                      -cdrom test.iso -boot d
> 
> Some notes about the host TPM:
> The TPM needs to be enabled and activated. If that's not the case one
> has to go through the BIOS/UEFI and enable and activate that TPM for TPM
> commands to work as expected.
> It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
> command line or 'modprobe tpm_tis force=1' in case of using it as a module.
> 
> Regards,
> Andreas Niederl, Stefan Berger
> 
> [1] http://trustedjava.sourceforge.net/
> 
> Signed-off-by: Andreas Niederl <andreas.niederl@iaik.tugraz.at>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> ---
>  Makefile.target      |    1 +
>  configure            |    3 +
>  hw/tpm_passthrough.c |  493 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  qemu-options.hx      |   33 ++++
>  tpm.c                |   16 ++
>  tpm.h                |   33 ++++
>  6 files changed, 579 insertions(+), 0 deletions(-)
>  create mode 100644 hw/tpm_passthrough.c
> 
> diff --git a/Makefile.target b/Makefile.target
> index 0e1c032..2744a5c 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -207,6 +207,7 @@ obj-$(CONFIG_NO_KVM) += kvm-stub.o
>  obj-y += memory.o
>  obj-y += tpm.o
>  obj-$(CONFIG_TPM) += tpm_tis.o
> +obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
>  LIBS+=-lz
>  
>  QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
> diff --git a/configure b/configure
> index 385feb4..25995bc 100755
> --- a/configure
> +++ b/configure
> @@ -3721,6 +3721,9 @@ fi
>  
>  if test "$tpm" = "yes"; then
>    if test "$target_softmmu" = "yes" ; then
> +    if test "$linux" = "yes" ; then
> +      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
> +    fi
>      echo "CONFIG_TPM=y" >> $config_host_mak
>    fi
>  fi
> diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
> new file mode 100644
> index 0000000..76e4e35
> --- /dev/null
> +++ b/hw/tpm_passthrough.c
> @@ -0,0 +1,493 @@
> +/*
> + *  passthrough TPM driver
> + *
> + *  Copyright (c) 2010, 2011 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-common.h"
> +#include "qemu-error.h"
> +#include "tpm.h"
> +#include "hw/hw.h"
> +#include "hw/tpm_tis.h"
> +#include "hw/pc.h"
> +
> +/* #define DEBUG_TPM */
> +
> +#ifdef DEBUG_TPM
> +#define dprintf(fmt, ...) \
> +    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define dprintf(fmt, ...) \
> +    do { } while (0)
> +#endif
> +
> +/* data structures */
> +
> +typedef struct TPMPassthruThreadParams {
> +    TPMState *tpm_state;
> +
> +    TPMRecvDataCB *recv_data_callback;
> +    TPMBackend *tb;
> +} TPMPassthruThreadParams;
> +
> +struct TPMPassthruState {
> +    QemuThread thread;
> +    bool thread_terminate;
> +    bool thread_running;
> +
> +    TPMPassthruThreadParams tpm_thread_params;
> +
> +    char tpm_dev[64];

why not strdup and avoid length limits?


> +    int tpm_fd;
> +    bool had_startup_error;
> +};
> +
> +#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
> +
> +/* borrowed from qemu-char.c */
> +static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
> +{
> +    int ret, len1;
> +
> +    len1 = len;
> +    while (len1 > 0) {
> +        ret = write(fd, buf, len1);
> +        if (ret < 0) {
> +            if (errno != EINTR && errno != EAGAIN) {
> +                return -1;
> +            }
> +        } else if (ret == 0) {
> +            break;
> +        } else {
> +            buf  += ret;
> +            len1 -= ret;
> +        }
> +    }
> +    return len - len1;
> +}
> +
> +static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
> +{
> +    int ret, len1;
> +    uint8_t *buf1;
> +
> +    len1 = len;
> +    buf1 = buf;
> +    while ((len1 > 0) && (ret = read(fd, buf1, len1)) != 0) {
> +        if (ret < 0) {
> +            if (errno != EINTR && errno != EAGAIN) {
> +                return -1;
> +            }
> +        } else {
> +            buf1 += ret;
> +            len1 -= ret;
> +        }
> +    }
> +    return len - len1;
> +}
> +
> +static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
> +{
> +    return be32_to_cpu(*(uint32_t *)&buf[2]);
> +}
> +
> +static int tpm_passthrough_unix_tx_bufs(int tpm_fd,
> +                                        const uint8_t *in, uint32_t in_len,
> +                                        uint8_t *out, uint32_t out_len)
> +{
> +    int ret;
> +
> +    ret = tpm_passthrough_unix_write(tpm_fd, in, in_len);
> +    if (ret < 0) {
> +        error_report("tpm_passthrough: error while transmitting data "
> +                     "to TPM: %s (%i)\n",
> +                     strerror(errno), errno);
> +        goto err_exit;
> +    }
> +
> +    ret = tpm_passthrough_unix_read(tpm_fd, out, out_len);
> +    if (ret < 0) {
> +        error_report("tpm_passthrough: error while reading data from "
> +                     "TPM: %s (%i)\n",
> +                     strerror(errno), errno);
> +    } else if (ret < sizeof(struct tpm_resp_hdr) ||
> +               tpm_passthrough_get_size_from_buffer(out) != ret) {
> +        ret = -1;
> +        error_report("tpm_passthrough: received invalid response "
> +                     "packet from TPM\n");
> +    }
> +
> +err_exit:
> +    if (ret < 0) {
> +        tpm_write_fatal_error_response(out, out_len);
> +    }
> +
> +    return ret;
> +}
> +
> +static int tpm_passthrough_unix_transfer(int tpm_fd,
> +                                         const TPMLocality *cmd_locty)
> +{
> +    return tpm_passthrough_unix_tx_bufs(tpm_fd,
> +                                        cmd_locty->w_buffer.buffer,
> +                                        cmd_locty->w_offset,
> +                                        cmd_locty->r_buffer.buffer,
> +                                        cmd_locty->r_buffer.size);
> +}
> +
> +static void *tpm_passthrough_thread(void *d)
> +{
> +    TPMPassthruThreadParams *thr_parms = d;
> +    TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
> +    uint32_t in_len;
> +
> +    dprintf("tpm_passthrough: THREAD IS STARTING\n");
> +
> +    /* start command processing */
> +    while (!tpm_pt->thread_terminate) {
> +        /* receive and handle commands */
> +        in_len = 0;
> +        do {
> +            dprintf("tpm_passthrough: waiting for commands...\n");
> +
> +            if (tpm_pt->thread_terminate) {
> +                break;
> +            }
> +
> +            qemu_mutex_lock(&thr_parms->tpm_state->state_lock);
> +
> +            /* in case we were to slow and missed the signal, the



too slow


> +               to_tpm_execute boolean tells us about a pending command */
> +            if (!thr_parms->tpm_state->to_tpm_execute) {
> +                qemu_cond_wait(&thr_parms->tpm_state->to_tpm_cond,
> +                               &thr_parms->tpm_state->state_lock);
> +            }
> +
> +            thr_parms->tpm_state->to_tpm_execute = false;
> +
> +            qemu_mutex_unlock(&thr_parms->tpm_state->state_lock);
> +
> +            if (tpm_pt->thread_terminate) {
> +                break;
> +            }
> +
> +            in_len = thr_parms->tpm_state->cmd_locty->w_offset;
> +
> +            tpm_passthrough_unix_transfer(tpm_pt->tpm_fd,
> +                                          thr_parms->tpm_state->cmd_locty);
> +
> +            thr_parms->recv_data_callback(thr_parms->tpm_state,
> +                                          thr_parms->tpm_state->command_locty);
> +        } while (in_len > 0);
> +    }
> +
> +    dprintf("tpm_passthrough: THREAD IS ENDING\n");
> +
> +    tpm_pt->thread_running = false;
> +
> +    return NULL;
> +}
> +
> +static void tpm_passthrough_terminate_tpm_thread(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    if (!tpm_pt->thread_running) {
> +        return;
> +    }
> +
> +    dprintf("tpm_passthrough: TERMINATING RUNNING TPM THREAD\n");
> +
> +    if (!tpm_pt->thread_terminate) {
> +        tpm_pt->thread_terminate = true;
> +
> +        qemu_mutex_lock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
> +        qemu_cond_signal(&tpm_pt->tpm_thread_params.tpm_state->to_tpm_cond);
> +        qemu_mutex_unlock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);

why lock?

> +
> +        if (tpm_pt->thread_running) {
> +            qemu_thread_join(&tpm_pt->thread);
> +        }
> +        memset(&tpm_pt->thread, 0, sizeof(tpm_pt->thread));
> +    }
> +}
> +
> +/**
> + * Start the TPM (thread). If it had been started before, then terminate
> + * and start it again.
> + */
> +static int tpm_passthrough_do_startup_tpm(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    /* terminate a running TPM */
> +    tpm_passthrough_terminate_tpm_thread(tb);
> +
> +    /* reset the flag so the thread keeps on running */
> +    tpm_pt->thread_terminate = false;
> +
> +    qemu_thread_create(&tpm_pt->thread, tpm_passthrough_thread,
> +                       &tpm_pt->tpm_thread_params, 0);
> +
> +    tpm_pt->thread_running = true;
> +
> +    return 0;
> +}
> +
> +static int tpm_passthrough_startup_tpm(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +    int rc;
> +
> +    rc = tpm_passthrough_do_startup_tpm(tb);
> +    if (rc) {
> +        tpm_pt->had_startup_error = true;
> +    }
> +    return rc;
> +}
> +
> +static void tpm_passthrough_reset(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    dprintf("tpm_passthrough: CALL TO TPM_RESET!\n");
> +
> +    tpm_passthrough_terminate_tpm_thread(tb);
> +
> +    tpm_pt->had_startup_error = false;
> +}
> +
> +static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
> +                                TPMRecvDataCB *recv_data_cb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    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;
> +
> +    tpm_pt->thread_running = false;
> +
> +    return 0;
> +}
> +
> +static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
> +{
> +    return false;
> +}
> +
> +static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    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 */

what does the comment mean?


> +
> +    if (sb->size != wanted_size) {
> +        sb->buffer = g_realloc(sb->buffer, wanted_size);
> +        if (sb->buffer != NULL) {
> +            sb->size = wanted_size;
> +        } else {
> +            sb->size = 0;
> +        }
> +    }
> +    return sb->size;
> +}
> +
> +static const char *tpm_passthrough_create_desc(void)
> +{
> +    return "Passthrough TPM backend driver";
> +}
> +
> +/* A basic test of a TPM device. We expect a well formatted response header
> + * (error response is fine) within one second.
> + */
> +static int tpm_passthrough_test_tpmdev(int fd)
> +{
> +    struct tpm_req_hdr req = {
> +        .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
> +        .len = cpu_to_be32(sizeof(req)),
> +        .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
> +    };
> +    struct tpm_resp_hdr *resp;
> +    fd_set readfds;
> +    int n;
> +    struct timeval tv = {
> +        .tv_sec = 1,
> +        .tv_usec = 0,
> +    };
> +    unsigned char buf[1024];
> +
> +    n = write(fd, &req, sizeof(req));
> +    if (n < 0) {
> +        return errno;
> +    }
> +    if (n != sizeof(req)) {
> +        return EFAULT;
> +    }
> +
> +    FD_ZERO(&readfds);
> +    FD_SET(fd, &readfds);
> +
> +    /* wait for a second */
> +    n = select(fd + 1, &readfds, NULL, NULL, &tv);
> +    if (n != 1) {
> +        return errno;
> +    }
> +
> +    n = read(fd, &buf, sizeof(buf));

so why read the whole buf? hoiw do we know 1024 is enough?
read the heade then discard the rest
until empty.

> +    if (n < sizeof(struct tpm_resp_hdr)) {
> +        return EFAULT;
> +    }
> +
> +    resp = (struct tpm_resp_hdr *)buf;
> +    /* check the header */
> +    if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND ||
> +        be32_to_cpu(resp->len) != n) {
> +        return EBADMSG;
> +    }
> +
> +    return 0;
> +}
> +
> +static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
> +{
> +    const char *value;
> +    char buf[64];
> +    int n;
> +
> +    value = qemu_opt_get(opts, "path");
> +    if (!value) {
> +        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
> +    }
> +
> +    n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
> +                 "%s", value);
> +
> +    if (n >= sizeof(tb->s.tpm_pt->tpm_dev)) {
> +        error_report("TPM device path is too long.\n");
> +        goto err_exit;
> +    }
> +
> +    snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
> +
> +    tb->parameters = g_strdup(buf);
> +
> +    if (tb->parameters == NULL) {
> +        return 1;
> +    }
> +
> +    tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
> +    if (tb->s.tpm_pt->tpm_fd < 0) {
> +        error_report("Cannot access TPM device using '%s'.\n",
> +                     tb->s.tpm_pt->tpm_dev);
> +        goto err_exit;
> +    }
> +
> +    if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) {
> +        error_report("'%s' is not a TPM device.\n",
> +                     tb->s.tpm_pt->tpm_dev);
> +        goto err_close_tpmdev;
> +    }
> +
> +    return 0;
> +
> + err_close_tpmdev:
> +    close(tb->s.tpm_pt->tpm_fd);
> +    tb->s.tpm_pt->tpm_fd = -1;
> +
> + err_exit:
> +    g_free(tb->parameters);
> +    tb->parameters = NULL;
> +
> +    return 1;
> +}
> +
> +
> +static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
> +{
> +    TPMBackend *tb;
> +
> +    tb = g_malloc0(sizeof(TPMBackend));
> +    if (tb == NULL) {
> +        error_report("tpm_passthrough: Could not allocate memory.\n");
> +        return NULL;
> +    }
> +
> +    tb->s.tpm_pt = g_malloc0(sizeof(TPMPassthruState));
> +    if (tb->s.tpm_pt == NULL) {
> +        error_report("tpm_passthrough: Could not allocate memory.\n");
> +        g_free(tb);
> +        return NULL;
> +    }
> +
> +    tb->id = g_strdup(id);
> +    if (tb->id == NULL) {
> +        error_report("tpm_passthrough: Could not allocate memory.\n");
> +        goto err_exit;
> +    }
> +
> +    tb->ops = &tpm_passthrough_driver;
> +
> +    if (tpm_passthrough_handle_device_opts(opts, tb)) {
> +        goto err_exit;
> +    }
> +
> +    return tb;
> +
> +err_exit:
> +    g_free(tb->id);
> +    g_free(tb->s.tpm_pt);
> +    g_free(tb);
> +
> +    return NULL;
> +}
> +
> +static void tpm_passthrough_destroy(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    tpm_passthrough_terminate_tpm_thread(tb);
> +
> +    close(tpm_pt->tpm_fd);
> +
> +    g_free(tb->id);
> +    g_free(tb->parameters);
> +    g_free(tb->s.tpm_pt);
> +    g_free(tb);
> +}
> +
> +const TPMDriverOps tpm_passthrough_driver = {
> +    .id                       = "passthrough",
> +    .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,
> +    .reset                    = tpm_passthrough_reset,
> +    .had_startup_error        = tpm_passthrough_get_startup_error,
> +    .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
> +};
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 40a63b1..10ae499 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1916,6 +1916,7 @@ 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}.
>  
>  The specific backend type will determine the applicable options.
>  The @code{-tpmdev} options requires a @code{-device} option.
> @@ -1927,6 +1928,38 @@ Use ? to print all available TPM backend types.
>  qemu -tpmdev ?
>  @end example
>  
> +@item -tpmdev passthrough, id=@var{id}, path=@var{path}
> +
> +(Linux-host only) Enable access to the host's TPM using the passthrough
> +driver.
> +
> +@option{path} specifies the path to the host's TPM device, i.e., on
> +a Linux host this would be @code{/dev/tpm0}.
> +@option{path} is optional and by default @code{/dev/tpm0} is used.
> +
> +Some notes about using the host's TPM with the passthrough driver:
> +
> +The TPM device accessed by the passthrough driver must not be
> +used by any other application on the host.
> +
> +Since the host's firmware (BIOS/UEFI) has already initialized the TPM,
> +the VM's firmware (BIOS/UEFI) will not be able to initialize the
> +TPM again and may therefore not show a TPM-specific menu that would
> +otherwise allow the user to configure the TPM, e.g., allow the user to
> +enable/disable or activate/deactivate the TPM.
> +Further, if TPM ownership is released from within a VM then the host's TPM
> +will get disabled and deactivated. To enable and activate the
> +TPM again afterwards, the host has to be rebooted and the user is
> +required to enter the firmware's menu to enable and activate the TPM.
> +If the TPM is left disabled and/or deactivated most TPM commands will fail.
> +
> +To create a passthrough TPM use the following two options:
> +@example
> +-tpmdev passthrough,id=tpm0 -device tpm-tis,tpmdev=tpm0
> +@end example
> +Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
> +@code{tpmdev=tpm0} in the device option.
> +
>  @end table
>  
>  ETEXI
> diff --git a/tpm.c b/tpm.c
> index 408d319..ee38107 100644
> --- a/tpm.c
> +++ b/tpm.c
> @@ -24,9 +24,25 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =
>  #ifdef CONFIG_TPM
>  
>  static const TPMDriverOps *bes[] = {
> +#ifdef CONFIG_TPM_PASSTHROUGH
> +    &tpm_passthrough_driver,
> +#endif
>      NULL,
>  };
>  
> +/* Write an error message in the given output buffer.
> + */
> +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);
> +    }
> +}
> +
>  const TPMDriverOps *tpm_get_backend_driver(const char *id)
>  {
>      int i;
> diff --git a/tpm.h b/tpm.h
> index 075de3f..1b278cc 100644
> --- a/tpm.h
> +++ b/tpm.h
> @@ -18,12 +18,18 @@
>  struct TPMDriverOps;
>  typedef struct TPMDriverOps TPMDriverOps;
>  
> +typedef struct TPMPassthruState TPMPassthruState;
> +
>  typedef struct TPMBackend {
>      char *id;
>      const char *fe_model;
>      char *parameters;
>      const TPMDriverOps *ops;
>  
> +    union {
> +        TPMPassthruState *tpm_pt;
> +    } s;
> +
>      QLIST_ENTRY(TPMBackend) list;
>  } TPMBackend;
>  
> @@ -76,11 +82,38 @@ struct TPMDriverOps {
>  
>  #define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
>  
> +struct tpm_req_hdr {
> +    uint16_t tag;
> +    uint32_t len;
> +    uint32_t ordinal;
> +} __attribute__((packed));
> +
> +struct tpm_resp_hdr {
> +    uint16_t tag;
> +    uint32_t len;
> +    uint32_t errcode;
> +} __attribute__((packed));
> +
> +#define TPM_TAG_RQU_COMMAND       0xc1
> +#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2
> +#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3
> +
> +#define TPM_TAG_RSP_COMMAND       0xc4
> +#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5
> +#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6
> +
> +#define TPM_FAIL                  9
> +
> +#define TPM_ORD_GetTicks          0xf1
> +
>  int tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
>  int tpm_init(void);
>  void tpm_cleanup(void);
>  TPMBackend *qemu_find_tpm(const char *id);
>  void tpm_display_backend_drivers(void);
>  const TPMDriverOps *tpm_get_backend_driver(const char *id);
> +void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len);
> +
> +extern const TPMDriverOps tpm_passthrough_driver;
>  
>  #endif /* QEMU_TPM_H */
> -- 
> 1.7.6.4
> 

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

* Re: [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation
  2012-02-20 19:51   ` Michael S. Tsirkin
@ 2012-02-20 20:25     ` Stefan Berger
  2012-02-20 21:15       ` Michael S. Tsirkin
  0 siblings, 1 reply; 41+ messages in thread
From: Stefan Berger @ 2012-02-20 20:25 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/20/2012 02:51 PM, Michael S. Tsirkin wrote:
> On Wed, Dec 14, 2011 at 08:43:20AM -0500, Stefan Berger wrote:
>> > From Andreas Niederl's original posting with adaptations where necessary:
>>
>> This patch is based of off version 9 of Stefan Berger's patch series
>>    "Qemu Trusted Platform Module (TPM) integration"
>> and adds a new backend driver for it.
>>
>> This patch adds a passthrough backend driver for passing commands sent to the
>> emulated TPM device directly to a TPM device opened on the host machine.
>>
>> Thus it is possible to use a hardware TPM device in a system running on QEMU,
>> providing the ability to access a TPM in a special state (e.g. after a Trusted
>> Boot).
>>
>> This functionality is being used in the acTvSM Trusted Virtualization Platform
>> which is available on [1].
>>
>> Usage example:
>>    qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
>>                       -device tpm-tis,tpmdev=tpm0 \
>>                       -cdrom test.iso -boot d
>>
>> Some notes about the host TPM:
>> The TPM needs to be enabled and activated. If that's not the case one
>> has to go through the BIOS/UEFI and enable and activate that TPM for TPM
>> commands to work as expected.
>> It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
>> command line or 'modprobe tpm_tis force=1' in case of using it as a module.
>>
>> Regards,
>> Andreas Niederl, Stefan Berger
>>
>> [1] http://trustedjava.sourceforge.net/
>>
>> Signed-off-by: Andreas Niederl<andreas.niederl@iaik.tugraz.at>
>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
> So this was mentioned by Blue Swirl and Anthony and
> I have to agree: it's not clear why this wants its own
> thread.

This is not only due to how the Linux TPM driver works but also, as 
previously mentioned, due to how I would like the libtpms driver to 
work. The former does not support select() (yes, this is probably a TPM 
driver problem but it has been around for ages and didn't matter so far 
because only the TSS (TrouSerS) was meant to access the TPM). The latter 
will certainly support creation of 2048 bit RSA keys and disappear into 
e crypto function that also isn't select() able, unless you end up 
introducing another thread here. And you most probably don't want the 
main thread to be busy for several seconds (depending on availability of 
CPU cycles) to have that key created. So in effect having this thread 
allows us to have a common architecture for both passthrough and libtpms 
backends.


> I would go further and claim that forcing a threaded build for a
> periferal just looks wrong. What's going on, select+unblocking access
> does not work for tpm?

Yep.

> If this behaves like  a normal device
> we won't need locking ...
>

Stefan

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

* Re: [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation
  2012-02-20 20:01   ` Michael S. Tsirkin
@ 2012-02-20 21:12     ` Stefan Berger
  0 siblings, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2012-02-20 21:12 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/20/2012 03:01 PM, Michael S. Tsirkin wrote:
> On Wed, Dec 14, 2011 at 08:43:20AM -0500, Stefan Berger wrote:
>> +struct TPMPassthruState {
>> +    QemuThread thread;
>> +    bool thread_terminate;
>> +    bool thread_running;
>> +
>> +    TPMPassthruThreadParams tpm_thread_params;
>> +
>> +    char tpm_dev[64];
> why not strdup and avoid length limits?
>
>

Fixed.


>> +
>> +            /* in case we were to slow and missed the signal, the
>
>
> too slow

Fixed.

> +
>> +        qemu_mutex_lock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
>> +        qemu_cond_signal(&tpm_pt->tpm_thread_params.tpm_state->to_tpm_cond);
>> +        qemu_mutex_unlock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
> why lock?
>

Before the other thread calls the function to wait for the condition it 
has to grab the lock. Since the signal is not sticky, it could happen 
that it had grabbed the lock but wasn't waiting for the condition, yet, 
thus missing the signal yet still waiting for it soon after and then it 
may end up waiting forever. The lock prevents this. Check example here:

https://computing.llnl.gov/tutorials/pthreads/#ConVarSignal

>> +static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
>> +{
>> +    size_t wanted_size = 4096; /* Linux tpm.c buffer size */
> what does the comment mean?
>

It basically means that it allocates the same size of buffer as the 
Linux driver does. If the TPM was to send a response (there are no 
requests of even close that size) then the Linux driver can accommodate 
the size of the response, we can also accomodate it.


>> + * (error response is fine) within one second.
>> + */
>> +static int tpm_passthrough_test_tpmdev(int fd)
>> +{
>> +    struct tpm_req_hdr req = {
>> +        .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
>> +        .len = cpu_to_be32(sizeof(req)),
>> +        .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
>> +    };
>> +    struct tpm_resp_hdr *resp;
>> +    fd_set readfds;
>> +    int n;
>> +    struct timeval tv = {
>> +        .tv_sec = 1,
>> +        .tv_usec = 0,
>> +    };
>> +    unsigned char buf[1024];
>> +
>> +    n = write(fd,&req, sizeof(req));
>> +    if (n<  0) {
>> +        return errno;
>> +    }
>> +    if (n != sizeof(req)) {
>> +        return EFAULT;
>> +    }
>> +
>> +    FD_ZERO(&readfds);
>> +    FD_SET(fd,&readfds);
>> +
>> +    /* wait for a second */
>> +    n = select(fd + 1,&readfds, NULL, NULL,&tv);
>> +    if (n != 1) {
>> +        return errno;
>> +    }
>> +
>> +    n = read(fd,&buf, sizeof(buf));
> so why read the whole buf? hoiw do we know 1024 is enough?

The size of the response for this particular command can easily be read 
into 1024 bytes, even less than 50 would be enough. I could try to come 
up with the exact number, but that then probably causes more 
questions... 1024 bytes is ample space.

> read the heade then discard the rest
> until empty.
>

I don't follow... ?

FYI: The TPM only lets you read the buffer in one go. If you don't bring 
a buffer with enough size when doing the read from /dev/tpm0, the rest 
of the result is gone. No 2nd read on the same response buffer.

Even though /dev/tpm0 blocks in the write(), the above select() is there 
to prepare for usage with sockets. It doesn't hurt at the moment, but it 
also doesn't do the expected wait for 1 second...


    Stefan

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

* Re: [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation
  2012-02-20 20:25     ` Stefan Berger
@ 2012-02-20 21:15       ` Michael S. Tsirkin
  2012-02-21  1:03         ` Stefan Berger
  0 siblings, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-20 21:15 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Mon, Feb 20, 2012 at 03:25:37PM -0500, Stefan Berger wrote:
> On 02/20/2012 02:51 PM, Michael S. Tsirkin wrote:
> >On Wed, Dec 14, 2011 at 08:43:20AM -0500, Stefan Berger wrote:
> >>> From Andreas Niederl's original posting with adaptations where necessary:
> >>
> >>This patch is based of off version 9 of Stefan Berger's patch series
> >>   "Qemu Trusted Platform Module (TPM) integration"
> >>and adds a new backend driver for it.
> >>
> >>This patch adds a passthrough backend driver for passing commands sent to the
> >>emulated TPM device directly to a TPM device opened on the host machine.
> >>
> >>Thus it is possible to use a hardware TPM device in a system running on QEMU,
> >>providing the ability to access a TPM in a special state (e.g. after a Trusted
> >>Boot).
> >>
> >>This functionality is being used in the acTvSM Trusted Virtualization Platform
> >>which is available on [1].
> >>
> >>Usage example:
> >>   qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
> >>                      -device tpm-tis,tpmdev=tpm0 \
> >>                      -cdrom test.iso -boot d
> >>
> >>Some notes about the host TPM:
> >>The TPM needs to be enabled and activated. If that's not the case one
> >>has to go through the BIOS/UEFI and enable and activate that TPM for TPM
> >>commands to work as expected.
> >>It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
> >>command line or 'modprobe tpm_tis force=1' in case of using it as a module.
> >>
> >>Regards,
> >>Andreas Niederl, Stefan Berger
> >>
> >>[1] http://trustedjava.sourceforge.net/
> >>
> >>Signed-off-by: Andreas Niederl<andreas.niederl@iaik.tugraz.at>
> >>Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
> >So this was mentioned by Blue Swirl and Anthony and
> >I have to agree: it's not clear why this wants its own
> >thread.
> 
> This is not only due to how the Linux TPM driver works but also, as
> previously mentioned, due to how I would like the libtpms driver to
> work. The former does not support select() (yes, this is probably a
> TPM driver problem but it has been around for ages and didn't matter
> so far because only the TSS (TrouSerS) was meant to access the TPM).
> The latter will certainly support creation of 2048 bit RSA keys and
> disappear into e crypto function that also isn't select() able,
> unless you end up introducing another thread here.
> And you most
> probably don't want the main thread to be busy for several seconds
> (depending on availability of CPU cycles) to have that key created.
> So in effect having this thread allows us to have a common
> architecture for both passthrough and libtpms backends.
> 

OK, so let's say this is a work around linux tpm
backend limitations. But it is unfortunate that this
spills into frontend code.
How about taking the global qemu lock instead?

Also, you need to be more careful with
running your thread, e.g. it must be stopped on
vmstop, etc. It is probably a good idea to use
a genetic thread pool with all that logic.


> >I would go further and claim that forcing a threaded build for a
> >periferal just looks wrong. What's going on, select+unblocking access
> >does not work for tpm?
> 
> Yep.

Then maybe it's better to make tpm *depend* on threads,
not force them.

> >If this behaves like  a normal device
> >we won't need locking ...
> >
> 
> Stefan

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

* Re: [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
  2012-02-20 19:51   ` Michael S. Tsirkin
  2012-02-20 20:01   ` Michael S. Tsirkin
@ 2012-02-20 21:30   ` Michael S. Tsirkin
  2012-02-21  0:30     ` Stefan Berger
  2 siblings, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-20 21:30 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Wed, Dec 14, 2011 at 08:43:20AM -0500, Stefan Berger wrote:
> >From Andreas Niederl's original posting with adaptations where necessary:
> 
> This patch is based of off version 9 of Stefan Berger's patch series
>   "Qemu Trusted Platform Module (TPM) integration"
> and adds a new backend driver for it.
> 
> This patch adds a passthrough backend driver for passing commands sent to the
> emulated TPM device directly to a TPM device opened on the host machine.
> 
> Thus it is possible to use a hardware TPM device in a system running on QEMU,
> providing the ability to access a TPM in a special state (e.g. after a Trusted
> Boot).
> 
> This functionality is being used in the acTvSM Trusted Virtualization Platform
> which is available on [1].
> 
> Usage example:
>   qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
>                      -device tpm-tis,tpmdev=tpm0 \
>                      -cdrom test.iso -boot d
> 
> Some notes about the host TPM:
> The TPM needs to be enabled and activated. If that's not the case one
> has to go through the BIOS/UEFI and enable and activate that TPM for TPM
> commands to work as expected.
> It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
> command line or 'modprobe tpm_tis force=1' in case of using it as a module.
> 
> Regards,
> Andreas Niederl, Stefan Berger
> 
> [1] http://trustedjava.sourceforge.net/
> 
> Signed-off-by: Andreas Niederl <andreas.niederl@iaik.tugraz.at>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> ---
>  Makefile.target      |    1 +
>  configure            |    3 +
>  hw/tpm_passthrough.c |  493 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  qemu-options.hx      |   33 ++++
>  tpm.c                |   16 ++
>  tpm.h                |   33 ++++
>  6 files changed, 579 insertions(+), 0 deletions(-)
>  create mode 100644 hw/tpm_passthrough.c
> 
> diff --git a/Makefile.target b/Makefile.target
> index 0e1c032..2744a5c 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -207,6 +207,7 @@ obj-$(CONFIG_NO_KVM) += kvm-stub.o
>  obj-y += memory.o
>  obj-y += tpm.o
>  obj-$(CONFIG_TPM) += tpm_tis.o
> +obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
>  LIBS+=-lz
>  
>  QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
> diff --git a/configure b/configure
> index 385feb4..25995bc 100755
> --- a/configure
> +++ b/configure
> @@ -3721,6 +3721,9 @@ fi
>  
>  if test "$tpm" = "yes"; then
>    if test "$target_softmmu" = "yes" ; then
> +    if test "$linux" = "yes" ; then
> +      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
> +    fi
>      echo "CONFIG_TPM=y" >> $config_host_mak
>    fi
>  fi
> diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
> new file mode 100644
> index 0000000..76e4e35
> --- /dev/null
> +++ b/hw/tpm_passthrough.c
> @@ -0,0 +1,493 @@
> +/*
> + *  passthrough TPM driver
> + *
> + *  Copyright (c) 2010, 2011 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-common.h"
> +#include "qemu-error.h"
> +#include "tpm.h"
> +#include "hw/hw.h"
> +#include "hw/tpm_tis.h"
> +#include "hw/pc.h"
> +
> +/* #define DEBUG_TPM */
> +
> +#ifdef DEBUG_TPM
> +#define dprintf(fmt, ...) \
> +    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define dprintf(fmt, ...) \
> +    do { } while (0)
> +#endif
> +
> +/* data structures */
> +
> +typedef struct TPMPassthruThreadParams {
> +    TPMState *tpm_state;
> +
> +    TPMRecvDataCB *recv_data_callback;
> +    TPMBackend *tb;
> +} TPMPassthruThreadParams;
> +
> +struct TPMPassthruState {
> +    QemuThread thread;
> +    bool thread_terminate;
> +    bool thread_running;
> +
> +    TPMPassthruThreadParams tpm_thread_params;
> +
> +    char tpm_dev[64];
> +    int tpm_fd;
> +    bool had_startup_error;
> +};
> +
> +#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
> +
> +/* borrowed from qemu-char.c */
> +static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
> +{
> +    int ret, len1;
> +
> +    len1 = len;
> +    while (len1 > 0) {
> +        ret = write(fd, buf, len1);
> +        if (ret < 0) {
> +            if (errno != EINTR && errno != EAGAIN) {
> +                return -1;
> +            }
> +        } else if (ret == 0) {
> +            break;
> +        } else {
> +            buf  += ret;
> +            len1 -= ret;
> +        }
> +    }
> +    return len - len1;
> +}
> +
> +static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
> +{
> +    int ret, len1;
> +    uint8_t *buf1;
> +
> +    len1 = len;
> +    buf1 = buf;
> +    while ((len1 > 0) && (ret = read(fd, buf1, len1)) != 0) {
> +        if (ret < 0) {
> +            if (errno != EINTR && errno != EAGAIN) {
> +                return -1;
> +            }
> +        } else {
> +            buf1 += ret;
> +            len1 -= ret;
> +        }
> +    }
> +    return len - len1;
> +}
> +
> +static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
> +{
> +    return be32_to_cpu(*(uint32_t *)&buf[2]);
> +}
> +
> +static int tpm_passthrough_unix_tx_bufs(int tpm_fd,
> +                                        const uint8_t *in, uint32_t in_len,
> +                                        uint8_t *out, uint32_t out_len)
> +{
> +    int ret;
> +
> +    ret = tpm_passthrough_unix_write(tpm_fd, in, in_len);
> +    if (ret < 0) {
> +        error_report("tpm_passthrough: error while transmitting data "
> +                     "to TPM: %s (%i)\n",
> +                     strerror(errno), errno);
> +        goto err_exit;
> +    }
> +
> +    ret = tpm_passthrough_unix_read(tpm_fd, out, out_len);
> +    if (ret < 0) {
> +        error_report("tpm_passthrough: error while reading data from "
> +                     "TPM: %s (%i)\n",
> +                     strerror(errno), errno);
> +    } else if (ret < sizeof(struct tpm_resp_hdr) ||
> +               tpm_passthrough_get_size_from_buffer(out) != ret) {
> +        ret = -1;
> +        error_report("tpm_passthrough: received invalid response "
> +                     "packet from TPM\n");
> +    }
> +
> +err_exit:
> +    if (ret < 0) {
> +        tpm_write_fatal_error_response(out, out_len);
> +    }
> +
> +    return ret;
> +}
> +
> +static int tpm_passthrough_unix_transfer(int tpm_fd,
> +                                         const TPMLocality *cmd_locty)
> +{
> +    return tpm_passthrough_unix_tx_bufs(tpm_fd,
> +                                        cmd_locty->w_buffer.buffer,
> +                                        cmd_locty->w_offset,
> +                                        cmd_locty->r_buffer.buffer,
> +                                        cmd_locty->r_buffer.size);
> +}
> +
> +static void *tpm_passthrough_thread(void *d)
> +{
> +    TPMPassthruThreadParams *thr_parms = d;
> +    TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
> +    uint32_t in_len;
> +
> +    dprintf("tpm_passthrough: THREAD IS STARTING\n");
> +
> +    /* start command processing */
> +    while (!tpm_pt->thread_terminate) {

IMO you want to checvk thread_terminate undet a lock/

> +        /* receive and handle commands */
> +        in_len = 0;
> +        do {
> +            dprintf("tpm_passthrough: waiting for commands...\n");
> +
> +            if (tpm_pt->thread_terminate) {
> +                break;
> +            }
> +

This will have to be duplicate in each backend, no?
So I would say make this logic part of backend
and expose some api.
You can then call it from backend or invoke
backend callbacks from frontend.

> +            qemu_mutex_lock(&thr_parms->tpm_state->state_lock);
> +
> +            /* in case we were to slow and missed the signal, the
> +               to_tpm_execute boolean tells us about a pending command */
> +            if (!thr_parms->tpm_state->to_tpm_execute) {
> +                qemu_cond_wait(&thr_parms->tpm_state->to_tpm_cond,
> +                               &thr_parms->tpm_state->state_lock);
> +            }
> +
> +            thr_parms->tpm_state->to_tpm_execute = false;
> +
> +            qemu_mutex_unlock(&thr_parms->tpm_state->state_lock);
> +
> +            if (tpm_pt->thread_terminate) {
> +                break;
> +            }
> +
> +            in_len = thr_parms->tpm_state->cmd_locty->w_offset;
> +
> +            tpm_passthrough_unix_transfer(tpm_pt->tpm_fd,
> +                                          thr_parms->tpm_state->cmd_locty);
> +
> +            thr_parms->recv_data_callback(thr_parms->tpm_state,
> +                                          thr_parms->tpm_state->command_locty);
> +        } while (in_len > 0);
> +    }
> +
> +    dprintf("tpm_passthrough: THREAD IS ENDING\n");
> +
> +    tpm_pt->thread_running = false;
> +
> +    return NULL;
> +}
> +
> +static void tpm_passthrough_terminate_tpm_thread(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    if (!tpm_pt->thread_running) {
> +        return;
> +    }
> +
> +    dprintf("tpm_passthrough: TERMINATING RUNNING TPM THREAD\n");
> +
> +    if (!tpm_pt->thread_terminate) {
> +        tpm_pt->thread_terminate = true;
> +
> +        qemu_mutex_lock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
> +        qemu_cond_signal(&tpm_pt->tpm_thread_params.tpm_state->to_tpm_cond);
> +        qemu_mutex_unlock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
> +
> +        if (tpm_pt->thread_running) {
> +            qemu_thread_join(&tpm_pt->thread);
> +        }
> +        memset(&tpm_pt->thread, 0, sizeof(tpm_pt->thread));
> +    }
> +}
> +
> +/**
> + * Start the TPM (thread). If it had been started before, then terminate
> + * and start it again.
> + */
> +static int tpm_passthrough_do_startup_tpm(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    /* terminate a running TPM */
> +    tpm_passthrough_terminate_tpm_thread(tb);
> +
> +    /* reset the flag so the thread keeps on running */
> +    tpm_pt->thread_terminate = false;
> +
> +    qemu_thread_create(&tpm_pt->thread, tpm_passthrough_thread,
> +                       &tpm_pt->tpm_thread_params, 0);
> +
> +    tpm_pt->thread_running = true;
> +
> +    return 0;
> +}
> +
> +static int tpm_passthrough_startup_tpm(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +    int rc;
> +
> +    rc = tpm_passthrough_do_startup_tpm(tb);
> +    if (rc) {
> +        tpm_pt->had_startup_error = true;
> +    }
> +    return rc;
> +}
> +
> +static void tpm_passthrough_reset(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    dprintf("tpm_passthrough: CALL TO TPM_RESET!\n");
> +
> +    tpm_passthrough_terminate_tpm_thread(tb);
> +
> +    tpm_pt->had_startup_error = false;
> +}
> +
> +static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
> +                                TPMRecvDataCB *recv_data_cb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    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;
> +
> +    tpm_pt->thread_running = false;
> +
> +    return 0;
> +}
> +
> +static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
> +{
> +    return false;
> +}
> +
> +static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    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);
> +        if (sb->buffer != NULL) {
> +            sb->size = wanted_size;
> +        } else {
> +            sb->size = 0;
> +        }
> +    }
> +    return sb->size;
> +}
> +
> +static const char *tpm_passthrough_create_desc(void)
> +{
> +    return "Passthrough TPM backend driver";
> +}
> +
> +/* A basic test of a TPM device. We expect a well formatted response header
> + * (error response is fine) within one second.
> + */
> +static int tpm_passthrough_test_tpmdev(int fd)
> +{
> +    struct tpm_req_hdr req = {
> +        .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
> +        .len = cpu_to_be32(sizeof(req)),
> +        .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
> +    };
> +    struct tpm_resp_hdr *resp;
> +    fd_set readfds;
> +    int n;
> +    struct timeval tv = {
> +        .tv_sec = 1,
> +        .tv_usec = 0,
> +    };
> +    unsigned char buf[1024];
> +
> +    n = write(fd, &req, sizeof(req));
> +    if (n < 0) {
> +        return errno;
> +    }
> +    if (n != sizeof(req)) {
> +        return EFAULT;
> +    }
> +
> +    FD_ZERO(&readfds);
> +    FD_SET(fd, &readfds);
> +
> +    /* wait for a second */
> +    n = select(fd + 1, &readfds, NULL, NULL, &tv);
> +    if (n != 1) {
> +        return errno;
> +    }
> +
> +    n = read(fd, &buf, sizeof(buf));
> +    if (n < sizeof(struct tpm_resp_hdr)) {
> +        return EFAULT;
> +    }
> +
> +    resp = (struct tpm_resp_hdr *)buf;
> +    /* check the header */
> +    if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND ||
> +        be32_to_cpu(resp->len) != n) {
> +        return EBADMSG;
> +    }
> +
> +    return 0;
> +}
> +
> +static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
> +{
> +    const char *value;
> +    char buf[64];
> +    int n;
> +
> +    value = qemu_opt_get(opts, "path");
> +    if (!value) {
> +        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
> +    }
> +
> +    n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
> +                 "%s", value);
> +
> +    if (n >= sizeof(tb->s.tpm_pt->tpm_dev)) {
> +        error_report("TPM device path is too long.\n");
> +        goto err_exit;
> +    }
> +
> +    snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
> +
> +    tb->parameters = g_strdup(buf);
> +
> +    if (tb->parameters == NULL) {
> +        return 1;
> +    }
> +
> +    tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
> +    if (tb->s.tpm_pt->tpm_fd < 0) {
> +        error_report("Cannot access TPM device using '%s'.\n",
> +                     tb->s.tpm_pt->tpm_dev);
> +        goto err_exit;
> +    }
> +
> +    if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) {
> +        error_report("'%s' is not a TPM device.\n",
> +                     tb->s.tpm_pt->tpm_dev);
> +        goto err_close_tpmdev;
> +    }
> +
> +    return 0;
> +
> + err_close_tpmdev:
> +    close(tb->s.tpm_pt->tpm_fd);
> +    tb->s.tpm_pt->tpm_fd = -1;
> +
> + err_exit:
> +    g_free(tb->parameters);
> +    tb->parameters = NULL;
> +
> +    return 1;
> +}
> +
> +
> +static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
> +{
> +    TPMBackend *tb;
> +
> +    tb = g_malloc0(sizeof(TPMBackend));
> +    if (tb == NULL) {
> +        error_report("tpm_passthrough: Could not allocate memory.\n");
> +        return NULL;
> +    }
> +
> +    tb->s.tpm_pt = g_malloc0(sizeof(TPMPassthruState));
> +    if (tb->s.tpm_pt == NULL) {
> +        error_report("tpm_passthrough: Could not allocate memory.\n");
> +        g_free(tb);
> +        return NULL;
> +    }
> +
> +    tb->id = g_strdup(id);
> +    if (tb->id == NULL) {
> +        error_report("tpm_passthrough: Could not allocate memory.\n");
> +        goto err_exit;
> +    }
> +
> +    tb->ops = &tpm_passthrough_driver;
> +
> +    if (tpm_passthrough_handle_device_opts(opts, tb)) {
> +        goto err_exit;
> +    }
> +
> +    return tb;
> +
> +err_exit:
> +    g_free(tb->id);
> +    g_free(tb->s.tpm_pt);
> +    g_free(tb);
> +
> +    return NULL;
> +}
> +
> +static void tpm_passthrough_destroy(TPMBackend *tb)
> +{
> +    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> +    tpm_passthrough_terminate_tpm_thread(tb);
> +
> +    close(tpm_pt->tpm_fd);
> +
> +    g_free(tb->id);
> +    g_free(tb->parameters);
> +    g_free(tb->s.tpm_pt);
> +    g_free(tb);
> +}
> +
> +const TPMDriverOps tpm_passthrough_driver = {
> +    .id                       = "passthrough",
> +    .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,
> +    .reset                    = tpm_passthrough_reset,
> +    .had_startup_error        = tpm_passthrough_get_startup_error,
> +    .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
> +};
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 40a63b1..10ae499 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1916,6 +1916,7 @@ 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}.
>  
>  The specific backend type will determine the applicable options.
>  The @code{-tpmdev} options requires a @code{-device} option.
> @@ -1927,6 +1928,38 @@ Use ? to print all available TPM backend types.
>  qemu -tpmdev ?
>  @end example
>  
> +@item -tpmdev passthrough, id=@var{id}, path=@var{path}
> +
> +(Linux-host only) Enable access to the host's TPM using the passthrough
> +driver.
> +
> +@option{path} specifies the path to the host's TPM device, i.e., on
> +a Linux host this would be @code{/dev/tpm0}.
> +@option{path} is optional and by default @code{/dev/tpm0} is used.
> +
> +Some notes about using the host's TPM with the passthrough driver:
> +
> +The TPM device accessed by the passthrough driver must not be
> +used by any other application on the host.
> +
> +Since the host's firmware (BIOS/UEFI) has already initialized the TPM,
> +the VM's firmware (BIOS/UEFI) will not be able to initialize the
> +TPM again and may therefore not show a TPM-specific menu that would
> +otherwise allow the user to configure the TPM, e.g., allow the user to
> +enable/disable or activate/deactivate the TPM.
> +Further, if TPM ownership is released from within a VM then the host's TPM
> +will get disabled and deactivated. To enable and activate the
> +TPM again afterwards, the host has to be rebooted and the user is
> +required to enter the firmware's menu to enable and activate the TPM.
> +If the TPM is left disabled and/or deactivated most TPM commands will fail.
> +
> +To create a passthrough TPM use the following two options:
> +@example
> +-tpmdev passthrough,id=tpm0 -device tpm-tis,tpmdev=tpm0
> +@end example
> +Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
> +@code{tpmdev=tpm0} in the device option.
> +
>  @end table
>  
>  ETEXI
> diff --git a/tpm.c b/tpm.c
> index 408d319..ee38107 100644
> --- a/tpm.c
> +++ b/tpm.c
> @@ -24,9 +24,25 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =
>  #ifdef CONFIG_TPM
>  
>  static const TPMDriverOps *bes[] = {
> +#ifdef CONFIG_TPM_PASSTHROUGH
> +    &tpm_passthrough_driver,
> +#endif
>      NULL,
>  };
>  
> +/* Write an error message in the given output buffer.
> + */
> +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);
> +    }
> +}
> +
>  const TPMDriverOps *tpm_get_backend_driver(const char *id)
>  {
>      int i;
> diff --git a/tpm.h b/tpm.h
> index 075de3f..1b278cc 100644
> --- a/tpm.h
> +++ b/tpm.h
> @@ -18,12 +18,18 @@
>  struct TPMDriverOps;
>  typedef struct TPMDriverOps TPMDriverOps;
>  
> +typedef struct TPMPassthruState TPMPassthruState;
> +
>  typedef struct TPMBackend {
>      char *id;
>      const char *fe_model;
>      char *parameters;
>      const TPMDriverOps *ops;
>  
> +    union {
> +        TPMPassthruState *tpm_pt;
> +    } s;
> +
>      QLIST_ENTRY(TPMBackend) list;
>  } TPMBackend;
>  
> @@ -76,11 +82,38 @@ struct TPMDriverOps {
>  
>  #define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
>  
> +struct tpm_req_hdr {
> +    uint16_t tag;
> +    uint32_t len;
> +    uint32_t ordinal;
> +} __attribute__((packed));
> +
> +struct tpm_resp_hdr {
> +    uint16_t tag;
> +    uint32_t len;
> +    uint32_t errcode;
> +} __attribute__((packed));
> +
> +#define TPM_TAG_RQU_COMMAND       0xc1
> +#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2
> +#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3
> +
> +#define TPM_TAG_RSP_COMMAND       0xc4
> +#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5
> +#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6
> +
> +#define TPM_FAIL                  9
> +
> +#define TPM_ORD_GetTicks          0xf1
> +
>  int tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
>  int tpm_init(void);
>  void tpm_cleanup(void);
>  TPMBackend *qemu_find_tpm(const char *id);
>  void tpm_display_backend_drivers(void);
>  const TPMDriverOps *tpm_get_backend_driver(const char *id);
> +void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len);
> +
> +extern const TPMDriverOps tpm_passthrough_driver;
>  
>  #endif /* QEMU_TPM_H */
> -- 
> 1.7.6.4
> 

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
  2012-02-20  8:51   ` Michael S. Tsirkin
@ 2012-02-20 22:02   ` Michael S. Tsirkin
  2012-02-21  0:43     ` Stefan Berger
  1 sibling, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-20 22:02 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
> +/*
> + * Send a TPM request.
> + * Call this with the state_lock held so we can sync with the receive
> + * callback.
> + */
> +static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
> +{
> +    TPMTISState *tis = &s->s.tis;
> +
> +    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
> +
> +    s->command_locty = locty;
> +    s->cmd_locty     = &tis->loc[locty];
> +
> +    /* w_offset serves as length indicator for length of data;
> +       it's reset when the response comes back */
> +    tis->loc[locty].status = TPM_TIS_STATUS_EXECUTION;
> +    tis->loc[locty].sts &= ~TPM_TIS_STS_EXPECT;
> +
> +    s->to_tpm_execute = true;
> +    qemu_cond_signal(&s->to_tpm_cond);
> +}

What happens IIUC is that frondend sets to_tpm_execute
and signals a condition, and backend clears it
and waits on a condition.

So how about moving all the signalling
and locking out to backend, and have frontend
invoke a callback to signal it?

The whole threading thing then becomes a work-around
for a backend that does not support select,
instead of spilling out into frontend?

-- 
MST

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

* Re: [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation
  2012-02-20 21:30   ` Michael S. Tsirkin
@ 2012-02-21  0:30     ` Stefan Berger
  0 siblings, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2012-02-21  0:30 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/20/2012 04:30 PM, Michael S. Tsirkin wrote:
> On Wed, Dec 14, 2011 at 08:43:20AM -0500, Stefan Berger wrote:
>> +
>> +static void *tpm_passthrough_thread(void *d)
>> +{
>> +    TPMPassthruThreadParams *thr_parms = d;
>> +    TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
>> +    uint32_t in_len;
>> +
>> +    dprintf("tpm_passthrough: THREAD IS STARTING\n");
>> +
>> +    /* start command processing */
>> +    while (!tpm_pt->thread_terminate) {
> IMO you want to checvk thread_terminate undet a lock/
>

The way the termination flag is used along with the condition it isn't 
necessary to call it with a lock held. It may run into the condition but 
then be woken up with a signal and then again check the flag and terminate.

>> +        /* receive and handle commands */
>> +        in_len = 0;
>> +        do {
>> +            dprintf("tpm_passthrough: waiting for commands...\n");
>> +
>> +            if (tpm_pt->thread_terminate) {
>> +                break;
>> +            }
>> +
> This will have to be duplicate in each backend, no?

There is this sequence of code around the condition that helps us 
waiting for the front-end to send a command that is indeed repeated in 
every backend. I isolated that now but would like to keep the general 
thread more flexible and inside the individual backends. There is quite 
some code that is different in every backend.


> So I would say make this logic part of backend
> and expose some api.

Ok, a single function for now:

void tpm_backend_wait_for_command(TPMState *tpm_state);


> You can then call it from backend or invoke
> backend callbacks from frontend.
>

  I was only going to invoke it from within the backend thread.


    Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-20 22:02   ` Michael S. Tsirkin
@ 2012-02-21  0:43     ` Stefan Berger
  2012-02-21  3:18       ` Michael S. Tsirkin
  0 siblings, 1 reply; 41+ messages in thread
From: Stefan Berger @ 2012-02-21  0:43 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/20/2012 05:02 PM, Michael S. Tsirkin wrote:
> On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
>> +/*
>> + * Send a TPM request.
>> + * Call this with the state_lock held so we can sync with the receive
>> + * callback.
>> + */
>> +static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
>> +{
>> +    TPMTISState *tis =&s->s.tis;
>> +
>> +    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
>> +
>> +    s->command_locty = locty;
>> +    s->cmd_locty     =&tis->loc[locty];
>> +
>> +    /* w_offset serves as length indicator for length of data;
>> +       it's reset when the response comes back */
>> +    tis->loc[locty].status = TPM_TIS_STATUS_EXECUTION;
>> +    tis->loc[locty].sts&= ~TPM_TIS_STS_EXPECT;
>> +
>> +    s->to_tpm_execute = true;
>> +    qemu_cond_signal(&s->to_tpm_cond);
>> +}
> What happens IIUC is that frondend sets to_tpm_execute
> and signals a condition, and backend clears it
> and waits on a condition.
>
> So how about moving all the signalling
> and locking out to backend, and have frontend
> invoke a callback to signal it?
>
> The whole threading thing then becomes a work-around
> for a backend that does not support select,
> instead of spilling out into frontend?
>

How do I get the lock calls (qemu_mutex_lock(&s->state_lock)) out of the 
frontend? Do you want me to add callbacks to the backend interface for 
locking (s->be_driver->ops->state_lock(s)) and one for unlocking 
(s->be_driver->ops->state_unlock(tpm_be)) of the state that really 
belongs to the front-end (state is 's') and invoke it as shown in 
parenthesis and still keep s->state_lock around? Ideally the locks would 
end up being 'nop's' if select() was available, but in the end all 
backend will need to support that lock.

[The lock protects the common structure so that the thread in the 
backend can deliver the response to a request while the OS for example 
polls the hardware interface for its current state.]


    Stefan

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

* Re: [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation
  2012-02-20 21:15       ` Michael S. Tsirkin
@ 2012-02-21  1:03         ` Stefan Berger
  2012-03-21 23:27           ` Anthony Liguori
  0 siblings, 1 reply; 41+ messages in thread
From: Stefan Berger @ 2012-02-21  1:03 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/20/2012 04:15 PM, Michael S. Tsirkin wrote:
> On Mon, Feb 20, 2012 at 03:25:37PM -0500, Stefan Berger wrote:
>> On 02/20/2012 02:51 PM, Michael S. Tsirkin wrote:
>>> On Wed, Dec 14, 2011 at 08:43:20AM -0500, Stefan Berger wrote:
>>>>>  From Andreas Niederl's original posting with adaptations where necessary:
>>>> This patch is based of off version 9 of Stefan Berger's patch series
>>>>    "Qemu Trusted Platform Module (TPM) integration"
>>>> and adds a new backend driver for it.
>>>>
>>>> This patch adds a passthrough backend driver for passing commands sent to the
>>>> emulated TPM device directly to a TPM device opened on the host machine.
>>>>
>>>> Thus it is possible to use a hardware TPM device in a system running on QEMU,
>>>> providing the ability to access a TPM in a special state (e.g. after a Trusted
>>>> Boot).
>>>>
>>>> This functionality is being used in the acTvSM Trusted Virtualization Platform
>>>> which is available on [1].
>>>>
>>>> Usage example:
>>>>    qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
>>>>                       -device tpm-tis,tpmdev=tpm0 \
>>>>                       -cdrom test.iso -boot d
>>>>
>>>> Some notes about the host TPM:
>>>> The TPM needs to be enabled and activated. If that's not the case one
>>>> has to go through the BIOS/UEFI and enable and activate that TPM for TPM
>>>> commands to work as expected.
>>>> It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
>>>> command line or 'modprobe tpm_tis force=1' in case of using it as a module.
>>>>
>>>> Regards,
>>>> Andreas Niederl, Stefan Berger
>>>>
>>>> [1] http://trustedjava.sourceforge.net/
>>>>
>>>> Signed-off-by: Andreas Niederl<andreas.niederl@iaik.tugraz.at>
>>>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>>> So this was mentioned by Blue Swirl and Anthony and
>>> I have to agree: it's not clear why this wants its own
>>> thread.
>> This is not only due to how the Linux TPM driver works but also, as
>> previously mentioned, due to how I would like the libtpms driver to
>> work. The former does not support select() (yes, this is probably a
>> TPM driver problem but it has been around for ages and didn't matter
>> so far because only the TSS (TrouSerS) was meant to access the TPM).
>> The latter will certainly support creation of 2048 bit RSA keys and
>> disappear into e crypto function that also isn't select() able,
>> unless you end up introducing another thread here.
>> And you most
>> probably don't want the main thread to be busy for several seconds
>> (depending on availability of CPU cycles) to have that key created.
>> So in effect having this thread allows us to have a common
>> architecture for both passthrough and libtpms backends.
>>
> OK, so let's say this is a work around linux tpm
> backend limitations. But it is unfortunate that this
> spills into frontend code.
> How about taking the global qemu lock instead?
>
> Also, you need to be more careful with
> running your thread, e.g. it must be stopped on
> vmstop, etc. It is probably a good idea to use
> a genetic thread pool with all that logic.

Some backends may be able to react to a pthread_kill() 
(qemu_thread_kill() ?) which should be able to deliver a SIG_TERM to a 
thread. The passthrough thread, however, is not going to be able to 
react to this if it is stuck in the write() call to /dev/tpm0. I am 
sending the passthrough backend a signal to terminate and then do a 
qemu_thread_join() to wait for it to end. The libtpms backend on the 
other hand may be able to support a pthread_kill().
Why would I need a thread pool ?

    Stefan



>
>>> I would go further and claim that forcing a threaded build for a
>>> periferal just looks wrong. What's going on, select+unblocking access
>>> does not work for tpm?
>> Yep.
> Then maybe it's better to make tpm *depend* on threads,
> not force them.

And how would that change the code ?


    Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-21  0:43     ` Stefan Berger
@ 2012-02-21  3:18       ` Michael S. Tsirkin
  2012-02-21 11:19         ` Stefan Berger
  0 siblings, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-21  3:18 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Mon, Feb 20, 2012 at 07:43:05PM -0500, Stefan Berger wrote:
> On 02/20/2012 05:02 PM, Michael S. Tsirkin wrote:
> >On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
> >>+/*
> >>+ * Send a TPM request.
> >>+ * Call this with the state_lock held so we can sync with the receive
> >>+ * callback.
> >>+ */
> >>+static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
> >>+{
> >>+    TPMTISState *tis =&s->s.tis;
> >>+
> >>+    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
> >>+
> >>+    s->command_locty = locty;
> >>+    s->cmd_locty     =&tis->loc[locty];
> >>+
> >>+    /* w_offset serves as length indicator for length of data;
> >>+       it's reset when the response comes back */
> >>+    tis->loc[locty].status = TPM_TIS_STATUS_EXECUTION;
> >>+    tis->loc[locty].sts&= ~TPM_TIS_STS_EXPECT;
> >>+
> >>+    s->to_tpm_execute = true;
> >>+    qemu_cond_signal(&s->to_tpm_cond);
> >>+}
> >What happens IIUC is that frondend sets to_tpm_execute
> >and signals a condition, and backend clears it
> >and waits on a condition.
> >
> >So how about moving all the signalling
> >and locking out to backend, and have frontend
> >invoke a callback to signal it?
> >
> >The whole threading thing then becomes a work-around
> >for a backend that does not support select,
> >instead of spilling out into frontend?
> >
> 
> How do I get the lock calls (qemu_mutex_lock(&s->state_lock)) out of
> the frontend? Do you want me to add callbacks to the backend
> interface for locking (s->be_driver->ops->state_lock(s)) and one for
> unlocking (s->be_driver->ops->state_unlock(tpm_be)) of the state
> that really belongs to the front-end (state is 's') and invoke it as
> shown in parenthesis and still keep s->state_lock around? Ideally
> the locks would end up being 'nop's' if select() was available, but
> in the end all backend will need to support that lock.
> 
> [The lock protects the common structure so that the thread in the
> backend can deliver the response to a request while the OS for
> example polls the hardware interface for its current state.]
> 
> 
>    Stefan


Well, this is just an idea, please do not take this as
a request or anything like that. Maybe it is a dumb one.

Maybe something like what you describe.

Alternatively, I imagined that you can pass a copy
or pointer of the necessary state to the backend,
which queues the command and wakes the worker.
In the reverse direction, backend queues a response
and when OS polls you dequeue it and update state.

Can this work?
-- 
MST

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-21  3:18       ` Michael S. Tsirkin
@ 2012-02-21 11:19         ` Stefan Berger
  2012-02-21 12:18           ` Michael S. Tsirkin
  0 siblings, 1 reply; 41+ messages in thread
From: Stefan Berger @ 2012-02-21 11:19 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/20/2012 10:18 PM, Michael S. Tsirkin wrote:
> On Mon, Feb 20, 2012 at 07:43:05PM -0500, Stefan Berger wrote:
>> On 02/20/2012 05:02 PM, Michael S. Tsirkin wrote:
>>> On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
>>>> +/*
>>>> + * Send a TPM request.
>>>> + * Call this with the state_lock held so we can sync with the receive
>>>> + * callback.
>>>> + */
>>>> +static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
>>>> +{
>>>> +    TPMTISState *tis =&s->s.tis;
>>>> +
>>>> +    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
>>>> +
>>>> +    s->command_locty = locty;
>>>> +    s->cmd_locty     =&tis->loc[locty];
>>>> +
>>>> +    /* w_offset serves as length indicator for length of data;
>>>> +       it's reset when the response comes back */
>>>> +    tis->loc[locty].status = TPM_TIS_STATUS_EXECUTION;
>>>> +    tis->loc[locty].sts&= ~TPM_TIS_STS_EXPECT;
>>>> +
>>>> +    s->to_tpm_execute = true;
>>>> +    qemu_cond_signal(&s->to_tpm_cond);
>>>> +}
>>> What happens IIUC is that frondend sets to_tpm_execute
>>> and signals a condition, and backend clears it
>>> and waits on a condition.
>>>
>>> So how about moving all the signalling
>>> and locking out to backend, and have frontend
>>> invoke a callback to signal it?
>>>
>>> The whole threading thing then becomes a work-around
>>> for a backend that does not support select,
>>> instead of spilling out into frontend?
>>>
>> How do I get the lock calls (qemu_mutex_lock(&s->state_lock)) out of
>> the frontend? Do you want me to add callbacks to the backend
>> interface for locking (s->be_driver->ops->state_lock(s)) and one for
>> unlocking (s->be_driver->ops->state_unlock(tpm_be)) of the state
>> that really belongs to the front-end (state is 's') and invoke it as
>> shown in parenthesis and still keep s->state_lock around? Ideally
>> the locks would end up being 'nop's' if select() was available, but
>> in the end all backend will need to support that lock.
>>
>> [The lock protects the common structure so that the thread in the
>> backend can deliver the response to a request while the OS for
>> example polls the hardware interface for its current state.]
>>
>>
>>     Stefan
>
> Well, this is just an idea, please do not take this as
> a request or anything like that. Maybe it is a dumb one.
>
> Maybe something like what you describe.

I am starting to wonder what we're trying to achieve? We have a 
producer-consumer problem here with different threads. Both threads need 
to have some locking constructs along with the signalling (condition). 
The backend needs to be written in a certain way to work with the 
frontend, locking and signalling is a part of this. So I don't see it 
makes much sense to move all that code around, especially since there is 
only one backend right now. Maybe something really great can be done 
once there is a 2nd backend.

> Alternatively, I imagined that you can pass a copy
> or pointer of the necessary state to the backend,
> which queues the command and wakes the worker.
> In the reverse direction, backend queues a response
> and when OS polls you dequeue it and update state.
>

The OS doesn't necessarily need to poll. It is just one mode of 
operation of the OS, the other being interrupt-driven where the backend 
raises the interrupt once it has delivered the response to the frontend.


    Stefan

> Can this work?

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-21 11:19         ` Stefan Berger
@ 2012-02-21 12:18           ` Michael S. Tsirkin
  2012-02-21 15:05             ` Stefan Berger
  0 siblings, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-21 12:18 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Tue, Feb 21, 2012 at 06:19:26AM -0500, Stefan Berger wrote:
> On 02/20/2012 10:18 PM, Michael S. Tsirkin wrote:
> >On Mon, Feb 20, 2012 at 07:43:05PM -0500, Stefan Berger wrote:
> >>On 02/20/2012 05:02 PM, Michael S. Tsirkin wrote:
> >>>On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
> >>>>+/*
> >>>>+ * Send a TPM request.
> >>>>+ * Call this with the state_lock held so we can sync with the receive
> >>>>+ * callback.
> >>>>+ */
> >>>>+static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
> >>>>+{
> >>>>+    TPMTISState *tis =&s->s.tis;
> >>>>+
> >>>>+    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
> >>>>+
> >>>>+    s->command_locty = locty;
> >>>>+    s->cmd_locty     =&tis->loc[locty];
> >>>>+
> >>>>+    /* w_offset serves as length indicator for length of data;
> >>>>+       it's reset when the response comes back */
> >>>>+    tis->loc[locty].status = TPM_TIS_STATUS_EXECUTION;
> >>>>+    tis->loc[locty].sts&= ~TPM_TIS_STS_EXPECT;
> >>>>+
> >>>>+    s->to_tpm_execute = true;
> >>>>+    qemu_cond_signal(&s->to_tpm_cond);
> >>>>+}
> >>>What happens IIUC is that frondend sets to_tpm_execute
> >>>and signals a condition, and backend clears it
> >>>and waits on a condition.
> >>>
> >>>So how about moving all the signalling
> >>>and locking out to backend, and have frontend
> >>>invoke a callback to signal it?
> >>>
> >>>The whole threading thing then becomes a work-around
> >>>for a backend that does not support select,
> >>>instead of spilling out into frontend?
> >>>
> >>How do I get the lock calls (qemu_mutex_lock(&s->state_lock)) out of
> >>the frontend? Do you want me to add callbacks to the backend
> >>interface for locking (s->be_driver->ops->state_lock(s)) and one for
> >>unlocking (s->be_driver->ops->state_unlock(tpm_be)) of the state
> >>that really belongs to the front-end (state is 's') and invoke it as
> >>shown in parenthesis and still keep s->state_lock around? Ideally
> >>the locks would end up being 'nop's' if select() was available, but
> >>in the end all backend will need to support that lock.
> >>
> >>[The lock protects the common structure so that the thread in the
> >>backend can deliver the response to a request while the OS for
> >>example polls the hardware interface for its current state.]
> >>
> >>
> >>    Stefan
> >
> >Well, this is just an idea, please do not take this as
> >a request or anything like that. Maybe it is a dumb one.
> >
> >Maybe something like what you describe.
> 
> I am starting to wonder what we're trying to achieve? We have a
> producer-consumer problem here with different threads. Both threads
> need to have some locking constructs along with the signalling
> (condition). The backend needs to be written in a certain way to
> work with the frontend, locking and signalling is a part of this. So
> I don't see it makes much sense to move all that code around,
> especially since there is only one backend right now. Maybe
> something really great can be done once there is a 2nd backend.

There are three reasons I think where I think code
could be improved:

1. Your backend does not expose a reentrant asynchronous API,
   but another backend might.
   So it might be a better idea to hide this detail, and build a
   reentrant asynchronous API on top of what the OS supplies.
2. Your backend looks into the frontend data structures.
   This will make it impossible to implement another frontend.
3. I personally find it very hard to follow inter-thread
   communication based on shared memory and conditions
   if it is spread around between 2 different patches
   and different files. This can alternatively be addressed
   by documenting the synchronization/locking strategy.


> >Alternatively, I imagined that you can pass a copy
> >or pointer of the necessary state to the backend,
> >which queues the command and wakes the worker.
> >In the reverse direction, backend queues a response
> >and when OS polls you dequeue it and update state.
> >
> 
> The OS doesn't necessarily need to poll. It is just one mode of
> operation of the OS, the other being interrupt-driven where the
> backend raises the interrupt once it has delivered the response to
> the frontend.
> 
> 
>    Stefan

So you will also need to signal the frontend when it
must interrupt the guest. This is not a problem,
for example you can use a qemu_eventfd object for this.

> 
> >Can this work?

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-21 12:18           ` Michael S. Tsirkin
@ 2012-02-21 15:05             ` Stefan Berger
  2012-02-21 19:58               ` Michael S. Tsirkin
  0 siblings, 1 reply; 41+ messages in thread
From: Stefan Berger @ 2012-02-21 15:05 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/21/2012 07:18 AM, Michael S. Tsirkin wrote:
> On Tue, Feb 21, 2012 at 06:19:26AM -0500, Stefan Berger wrote:
>> On 02/20/2012 10:18 PM, Michael S. Tsirkin wrote:
>>> On Mon, Feb 20, 2012 at 07:43:05PM -0500, Stefan Berger wrote:
>>>> On 02/20/2012 05:02 PM, Michael S. Tsirkin wrote:
>>>>> On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
>>> Alternatively, I imagined that you can pass a copy
>>> or pointer of the necessary state to the backend,
>>> which queues the command and wakes the worker.
>>> In the reverse direction, backend queues a response
>>> and when OS polls you dequeue it and update state.
>>>
>> The OS doesn't necessarily need to poll. It is just one mode of
>> operation of the OS, the other being interrupt-driven where the
>> backend raises the interrupt once it has delivered the response to
>> the frontend.
>>
>>
>>     Stefan
> So you will also need to signal the frontend when it
> must interrupt the guest. This is not a problem,
> for example you can use a qemu_eventfd object for this.
>

When the backend delivers the response it checks whether the interface 
is used in interrupt mode and raises the interrupt. The backend enters 
the frontend code with a callback. In this function also a signal is 
sent that may wake up the main thread that, upon suspend, may be waiting 
for the last command to be processed and be sleeping on a condition 
variable.

I now added a function to the backend interface that is invoked by the 
frontend to notify the backend of a TPM request. The backend code can 
then either notify a thread (passthrough and libtpms driver) or create a 
response right away and invoke that callback to the front-end to deliver 
the response (null driver). How frontend and backend handle 
notifications is isolated to the frontend and backend with some backends 
(libtpms, passthough) sharing the code for how the notification is done.

Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-21 15:05             ` Stefan Berger
@ 2012-02-21 19:58               ` Michael S. Tsirkin
  2012-02-21 22:30                 ` Stefan Berger
  0 siblings, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-21 19:58 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Tue, Feb 21, 2012 at 10:05:26AM -0500, Stefan Berger wrote:
> On 02/21/2012 07:18 AM, Michael S. Tsirkin wrote:
> >On Tue, Feb 21, 2012 at 06:19:26AM -0500, Stefan Berger wrote:
> >>On 02/20/2012 10:18 PM, Michael S. Tsirkin wrote:
> >>>On Mon, Feb 20, 2012 at 07:43:05PM -0500, Stefan Berger wrote:
> >>>>On 02/20/2012 05:02 PM, Michael S. Tsirkin wrote:
> >>>>>On Wed, Dec 14, 2011 at 08:43:17AM -0500, Stefan Berger wrote:
> >>>Alternatively, I imagined that you can pass a copy
> >>>or pointer of the necessary state to the backend,
> >>>which queues the command and wakes the worker.
> >>>In the reverse direction, backend queues a response
> >>>and when OS polls you dequeue it and update state.
> >>>
> >>The OS doesn't necessarily need to poll. It is just one mode of
> >>operation of the OS, the other being interrupt-driven where the
> >>backend raises the interrupt once it has delivered the response to
> >>the frontend.
> >>
> >>
> >>    Stefan
> >So you will also need to signal the frontend when it
> >must interrupt the guest. This is not a problem,
> >for example you can use a qemu_eventfd object for this.
> >
> 
> When the backend delivers the response it checks whether the
> interface is used in interrupt mode and raises the interrupt.

IMO it's the frontend that should send interrupts.
Yes it kind of works for isa anyway, but e.g. pci
needs to update configuration etc.

> The
> backend enters the frontend code with a callback. In this function
> also a signal is sent that may wake up the main thread that, upon
> suspend, may be waiting for the last command to be processed and be
> sleeping on a condition variable.
> 
> I now added a function to the backend interface that is invoked by
> the frontend to notify the backend of a TPM request. The backend
> code can then either notify a thread (passthrough and libtpms
> driver) or create a response right away and invoke that callback to
> the front-end to deliver the response (null driver). How frontend
> and backend handle notifications is isolated to the frontend and
> backend with some backends (libtpms, passthough) sharing the code
> for how the notification is done.
> 
> Stefan

Right. And all the locking/threading can then be internal to the backend.

-- 
MST

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-21 19:58               ` Michael S. Tsirkin
@ 2012-02-21 22:30                 ` Stefan Berger
  2012-02-21 23:08                   ` Michael S. Tsirkin
  0 siblings, 1 reply; 41+ messages in thread
From: Stefan Berger @ 2012-02-21 22:30 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/21/2012 02:58 PM, Michael S. Tsirkin wrote:
> On Tue, Feb 21, 2012 at 10:05:26AM -0500, Stefan Berger wrote:
>> On 02/21/2012 07:18 AM, Michael S. Tsirkin wrote:
>>>
>> When the backend delivers the response it checks whether the
>> interface is used in interrupt mode and raises the interrupt.
> IMO it's the frontend that should send interrupts.
> Yes it kind of works for isa anyway, but e.g. pci
> needs to update configuration etc.
>

The code that causes the interrupt to be raised is in the frontend. The 
function doing that is invoked via callback from the backend. This 
should be ok?

>> The
>> backend enters the frontend code with a callback. In this function
>> also a signal is sent that may wake up the main thread that, upon
>> suspend, may be waiting for the last command to be processed and be
>> sleeping on a condition variable.
>>
>> I now added a function to the backend interface that is invoked by
>> the frontend to notify the backend of a TPM request. The backend
>> code can then either notify a thread (passthrough and libtpms
>> driver) or create a response right away and invoke that callback to
>> the front-end to deliver the response (null driver). How frontend
>> and backend handle notifications is isolated to the frontend and
>> backend with some backends (libtpms, passthough) sharing the code
>> for how the notification is done.
>>
>> Stefan
> Right. And all the locking/threading can then be internal to the backend.
>

Do you really want to replace code like this in the frontend:

qemu_mutex_lock(&s->state_lock)
[...]
qemu_mutex_unlock(&s->state_lock)


with


s->be_driver->ops->state_lock(s->be_driver)
[...]
s->be_driver->ops->state_unlock(s->be_driver))


where the backend starts protecting frontend data structures ?

At the moment there are two backends that need threading: the libtpms 
and passthrough backends. Both will require locking of datastructures 
that belong to the frontend. Only the null driver doesn't need a thread 
and the main thread can call into the backend, create the response and 
call via callback into the frontend to deliver the repsonse. If 
structures are protected via mutxes then only the NULL driver (which we 
don't want anyway) may end up grabbing mutexes that really aren't 
necessary while the two other backends need them. I don't see the 
mitextes as problematic. The frontend at least protects its data 
structures for the callbacks and other API calls it offers and they 
simply are thread-safe.

     Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-21 22:30                 ` Stefan Berger
@ 2012-02-21 23:08                   ` Michael S. Tsirkin
  2012-02-22  0:21                     ` Stefan Berger
                                       ` (2 more replies)
  0 siblings, 3 replies; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-21 23:08 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Tue, Feb 21, 2012 at 05:30:32PM -0500, Stefan Berger wrote:
> On 02/21/2012 02:58 PM, Michael S. Tsirkin wrote:
> >On Tue, Feb 21, 2012 at 10:05:26AM -0500, Stefan Berger wrote:
> >>On 02/21/2012 07:18 AM, Michael S. Tsirkin wrote:
> >>>
> >>When the backend delivers the response it checks whether the
> >>interface is used in interrupt mode and raises the interrupt.
> >IMO it's the frontend that should send interrupts.
> >Yes it kind of works for isa anyway, but e.g. pci
> >needs to update configuration etc.
> >
> 
> The code that causes the interrupt to be raised is in the frontend.
> The function doing that is invoked via callback from the backend.
> This should be ok?

Absolutely.

> >>The
> >>backend enters the frontend code with a callback. In this function
> >>also a signal is sent that may wake up the main thread that, upon
> >>suspend, may be waiting for the last command to be processed and be
> >>sleeping on a condition variable.
> >>
> >>I now added a function to the backend interface that is invoked by
> >>the frontend to notify the backend of a TPM request. The backend
> >>code can then either notify a thread (passthrough and libtpms
> >>driver) or create a response right away and invoke that callback to
> >>the front-end to deliver the response (null driver). How frontend
> >>and backend handle notifications is isolated to the frontend and
> >>backend with some backends (libtpms, passthough) sharing the code
> >>for how the notification is done.
> >>
> >>Stefan
> >Right. And all the locking/threading can then be internal to the backend.
> >
> 
> Do you really want to replace code like this in the frontend:
> 
> qemu_mutex_lock(&s->state_lock)
> [...]
> qemu_mutex_unlock(&s->state_lock)
> 
> 
> with
> 
> 
> s->be_driver->ops->state_lock(s->be_driver)
> [...]
> s->be_driver->ops->state_unlock(s->be_driver))
> 
> 
> where the backend starts protecting frontend data structures ?

It's ugly I hope you can do something saner:
ops->send_command(....)
with command encapsulating the relevant info.

> At the moment there are two backends that need threading: the
> libtpms and passthrough backends. Both will require locking of
> datastructures that belong to the frontend. Only the null driver
> doesn't need a thread and the main thread can call into the backend,
> create the response and call via callback into the frontend to
> deliver the repsonse. If structures are protected via mutxes then
> only the NULL driver (which we don't want anyway) may end up
> grabbing mutexes that really aren't necessary while the two other
> backends need them. I don't see the mitextes as problematic. The
> frontend at least protects its data structures for the callbacks and
> other API calls it offers and they simply are thread-safe.
> 
>     Stefan

Worst case, you can take a qemu mutex. Is tpm very
performance-sensitive to make contention on that
lock a problem?

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-21 23:08                   ` Michael S. Tsirkin
@ 2012-02-22  0:21                     ` Stefan Berger
  2012-02-22  4:34                       ` Michael S. Tsirkin
  2012-02-22 17:55                     ` Stefan Berger
  2012-03-02 12:02                     ` Stefan Berger
  2 siblings, 1 reply; 41+ messages in thread
From: Stefan Berger @ 2012-02-22  0:21 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, Andreas Niederl

On 02/21/2012 06:08 PM, Michael S. Tsirkin wrote:
> On Tue, Feb 21, 2012 at 05:30:32PM -0500, Stefan Berger wrote:
>
>
>> At the moment there are two backends that need threading: the
>> libtpms and passthrough backends. Both will require locking of
>> datastructures that belong to the frontend. Only the null driver
>> doesn't need a thread and the main thread can call into the backend,
>> create the response and call via callback into the frontend to
>> deliver the repsonse. If structures are protected via mutxes then
>> only the NULL driver (which we don't want anyway) may end up
>> grabbing mutexes that really aren't necessary while the two other
>> backends need them. I don't see the mitextes as problematic. The
>> frontend at least protects its data structures for the callbacks and
>> other API calls it offers and they simply are thread-safe.
>>
>>      Stefan
> Worst case, you can take a qemu mutex. Is tpm very
> performance-sensitive to make contention on that
> lock a problem?
>
We have to lock a common data structure 'somehow'. I don't see a way 
around it. The locking times are short since no major computations are 
done while the lock is held.
Considering that the TPM TIS interface is a non-DMA, byte-by-byte 
send/receive interface, the performance problems, if at all a problem, 
are to be found somewhere else : VMExits for example; if interface is 
used in polling mode, then the interval between polls.

    Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-22  0:21                     ` Stefan Berger
@ 2012-02-22  4:34                       ` Michael S. Tsirkin
  2012-02-22 15:03                         ` Stefan Berger
  0 siblings, 1 reply; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-02-22  4:34 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, Andreas Niederl

On Tue, Feb 21, 2012 at 07:21:28PM -0500, Stefan Berger wrote:
> On 02/21/2012 06:08 PM, Michael S. Tsirkin wrote:
> >On Tue, Feb 21, 2012 at 05:30:32PM -0500, Stefan Berger wrote:
> >
> >
> >>At the moment there are two backends that need threading: the
> >>libtpms and passthrough backends. Both will require locking of
> >>datastructures that belong to the frontend. Only the null driver
> >>doesn't need a thread and the main thread can call into the backend,
> >>create the response and call via callback into the frontend to
> >>deliver the repsonse. If structures are protected via mutxes then
> >>only the NULL driver (which we don't want anyway) may end up
> >>grabbing mutexes that really aren't necessary while the two other
> >>backends need them. I don't see the mitextes as problematic. The
> >>frontend at least protects its data structures for the callbacks and
> >>other API calls it offers and they simply are thread-safe.
> >>
> >>     Stefan
> >Worst case, you can take a qemu mutex. Is tpm very
> >performance-sensitive to make contention on that
> >lock a problem?
> >
> We have to lock a common data structure 'somehow'. I don't see a way
> around it.

Naturally. But it could be an implementation detail of
the backend.

> The locking times are short since no major computations
> are done while the lock is held.
> Considering that the TPM TIS interface is a non-DMA, byte-by-byte
> send/receive interface, the performance problems, if at all a
> problem, are to be found somewhere else : VMExits for example; if
> interface is used in polling mode, then the interval between polls.
> 
>    Stefan

In that case, you can take the qemu lock in your backend
and avoid locking in the frontend completely.

-- 
MST

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-22  4:34                       ` Michael S. Tsirkin
@ 2012-02-22 15:03                         ` Stefan Berger
  0 siblings, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2012-02-22 15:03 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, Andreas Niederl

On 02/21/2012 11:34 PM, Michael S. Tsirkin wrote:
> On Tue, Feb 21, 2012 at 07:21:28PM -0500, Stefan Berger wrote:
>> On 02/21/2012 06:08 PM, Michael S. Tsirkin wrote:
>>
>
>> The locking times are short since no major computations
>> are done while the lock is held.
>> Considering that the TPM TIS interface is a non-DMA, byte-by-byte
>> send/receive interface, the performance problems, if at all a
>> problem, are to be found somewhere else : VMExits for example; if
>> interface is used in polling mode, then the interval between polls.
>>
>>     Stefan
> In that case, you can take the qemu lock in your backend
> and avoid locking in the frontend completely.
>
Maybe it's just me, but I prefer to have locks around the code that has 
the criticial section, rather than entering a function with locks held, 
especially since it would be in a different file (backend) where the 
lock then is grabbed.
Also I do need a lock in the frontend for the condition that is used there.

     Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-21 23:08                   ` Michael S. Tsirkin
  2012-02-22  0:21                     ` Stefan Berger
@ 2012-02-22 17:55                     ` Stefan Berger
  2012-03-02 12:02                     ` Stefan Berger
  2 siblings, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2012-02-22 17:55 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/21/2012 06:08 PM, Michael S. Tsirkin wrote:
> On Tue, Feb 21, 2012 at 05:30:32PM -0500, Stefan Berger wrote:
>> On 02/21/2012 02:58 PM, Michael S. Tsirkin wrote:
>
> Worst case, you can take a qemu mutex. Is tpm very
> performance-sensitive to make contention on that
> lock a problem?
>

FYI: Some performance measurements with the libtpms backend and linux 
3.3.0-rc3 with IMA enabled. IMA takes ~1324 measurements while booting 
and sends TPM_Extend()s. I implemented a 'fast-path' for TPM ordinals 
that are considered 'short' in terms of duration of execution and where 
this TPM_Extend operation is one of them. In this case the Qemu main 
thread enters the TPM code rather than using the thread. Here are the 
measurements with the 1st one taken when dmesg states 'mtrr: no MTRR for 
fc000..'  and the 2nd one 'eth0: no IPv6 routers present'. Both may not 
be a great way to measure but at least show some 'trends':

                                                                       
                                               average    percentage

no IMA                        14.17 / 21.93    14.28 / 21.24    12.83 / 
20.18        13.76 / 21.12    100 / 100
w/ fast path + IRQ:     15.50 / 22.72    14.58 / 21.41    15.30 / 
22.37        15.12 / 22.17    110 / 105
w/ fast path + poll:     16.17 / 22.96    14.67 / 21.66    14.43 / 
22.17        15.09 / 22.26    110 / 105

no fast path + IRQ:    16.93 / 23.51    13.94 / 20.72    16.39 / 
23.48        15.75 / 22.57    114 / 107
no fast path + poll:    17.03 / 23.95    18.11 / 25.38    16.67 / 
24.08        17.27 / 24.47    126 / 119


      Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-20 15:48     ` Stefan Berger
  2012-02-20 19:37       ` Michael S. Tsirkin
@ 2012-02-23 20:47       ` Stefan Berger
  1 sibling, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2012-02-23 20:47 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/20/2012 10:48 AM, Stefan Berger wrote:
> On 02/20/2012 03:51 AM, Michael S. Tsirkin wrote:
>
>
>>> +static const MemoryRegionOps tpm_tis_memory_ops = {
>>> +    .read = tpm_tis_mmio_read,
>>> +    .write = tpm_tis_mmio_write,
>>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>> Are you sure? Most devices are BIG or LITTLE.
>>
>
> Right, it should be BIG endian. I'll fix it. Luckily there are only a 
> few 32bit accesses (and no 16bit access) also in 

Replace that with 'LITTLE endian'.

    Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-02-21 23:08                   ` Michael S. Tsirkin
  2012-02-22  0:21                     ` Stefan Berger
  2012-02-22 17:55                     ` Stefan Berger
@ 2012-03-02 12:02                     ` Stefan Berger
  2012-03-04 22:59                       ` Michael S. Tsirkin
  2 siblings, 1 reply; 41+ messages in thread
From: Stefan Berger @ 2012-03-02 12:02 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, andreas.niederl

On 02/21/2012 06:08 PM, Michael S. Tsirkin wrote:
> On Tue, Feb 21, 2012 at 05:30:32PM -0500, Stefan Berger wrote:
>> On 02/21/2012 02:58 PM, Michael S. Tsirkin wrote:
>>> On Tue, Feb 21, 2012 at 10:05:26AM -0500, Stefan Berger wrote:
>>>> On 02/21/2012 07:18 AM, Michael S. Tsirkin wrote:
>>>> When the backend delivers the response it checks whether the
>>>> interface is used in interrupt mode and raises the interrupt.
>>> IMO it's the frontend that should send interrupts.
>>> Yes it kind of works for isa anyway, but e.g. pci
>>> needs to update configuration etc.
>>>
>> The code that causes the interrupt to be raised is in the frontend.
>> The function doing that is invoked via callback from the backend.
>> This should be ok?
> Absolutely.
>
>>>> The
>>>> backend enters the frontend code with a callback. In this function
>>>> also a signal is sent that may wake up the main thread that, upon
>>>> suspend, may be waiting for the last command to be processed and be
>>>> sleeping on a condition variable.
>>>>
>>>> I now added a function to the backend interface that is invoked by
>>>> the frontend to notify the backend of a TPM request. The backend
>>>> code can then either notify a thread (passthrough and libtpms
>>>> driver) or create a response right away and invoke that callback to
>>>> the front-end to deliver the response (null driver). How frontend
>>>> and backend handle notifications is isolated to the frontend and
>>>> backend with some backends (libtpms, passthough) sharing the code
>>>> for how the notification is done.
>>>>
>>>> Stefan
>>> Right. And all the locking/threading can then be internal to the backend.
>>>
>> Do you really want to replace code like this in the frontend:
>>
>> qemu_mutex_lock(&s->state_lock)
>> [...]
>> qemu_mutex_unlock(&s->state_lock)
>>
>>
>> with
>>
>>
>> s->be_driver->ops->state_lock(s->be_driver)
>> [...]
>> s->be_driver->ops->state_unlock(s->be_driver))
>>
>>
>> where the backend starts protecting frontend data structures ?
> It's ugly I hope you can do something saner:
> ops->send_command(....)
> with command encapsulating the relevant info.
>
>> At the moment there are two backends that need threading: the
>> libtpms and passthrough backends. Both will require locking of
>> datastructures that belong to the frontend. Only the null driver
>> doesn't need a thread and the main thread can call into the backend,
>> create the response and call via callback into the frontend to
>> deliver the repsonse. If structures are protected via mutxes then
>> only the NULL driver (which we don't want anyway) may end up
>> grabbing mutexes that really aren't necessary while the two other
>> backends need them. I don't see the mitextes as problematic. The
>> frontend at least protects its data structures for the callbacks and
>> other API calls it offers and they simply are thread-safe.
>>
>>      Stefan
> Worst case, you can take a qemu mutex. Is tpm very
> performance-sensitive to make contention on that
> lock a problem?
>

Having instrumented the code with qemu_mutex_trylock() and a counter for 
failed attempts with subsequent qemu_mutex_lock() practically shows no 
lock contention at all for either polling or IRQ mode being used by the 
Linux driver.

IRQ mode: 4 failed lock attempts out of 1726208 locks -> 0.00%
polling mode: 2 failed lock attempts out of 1545216 locks -> 0.00%

I used the libtpms based backend with and ran IMA and a test suite in 
the VM.


     Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-03-02 12:02                     ` Stefan Berger
@ 2012-03-04 22:59                       ` Michael S. Tsirkin
  2012-03-05 15:44                         ` Stefan Berger
  2012-03-05 15:46                         ` Stefan Berger
  0 siblings, 2 replies; 41+ messages in thread
From: Michael S. Tsirkin @ 2012-03-04 22:59 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, andreas.niederl

On Fri, Mar 02, 2012 at 07:02:21AM -0500, Stefan Berger wrote:
> On 02/21/2012 06:08 PM, Michael S. Tsirkin wrote:
> >On Tue, Feb 21, 2012 at 05:30:32PM -0500, Stefan Berger wrote:
> >>On 02/21/2012 02:58 PM, Michael S. Tsirkin wrote:
> >>>On Tue, Feb 21, 2012 at 10:05:26AM -0500, Stefan Berger wrote:
> >>>>On 02/21/2012 07:18 AM, Michael S. Tsirkin wrote:
> >>>>When the backend delivers the response it checks whether the
> >>>>interface is used in interrupt mode and raises the interrupt.
> >>>IMO it's the frontend that should send interrupts.
> >>>Yes it kind of works for isa anyway, but e.g. pci
> >>>needs to update configuration etc.
> >>>
> >>The code that causes the interrupt to be raised is in the frontend.
> >>The function doing that is invoked via callback from the backend.
> >>This should be ok?
> >Absolutely.
> >
> >>>>The
> >>>>backend enters the frontend code with a callback. In this function
> >>>>also a signal is sent that may wake up the main thread that, upon
> >>>>suspend, may be waiting for the last command to be processed and be
> >>>>sleeping on a condition variable.
> >>>>
> >>>>I now added a function to the backend interface that is invoked by
> >>>>the frontend to notify the backend of a TPM request. The backend
> >>>>code can then either notify a thread (passthrough and libtpms
> >>>>driver) or create a response right away and invoke that callback to
> >>>>the front-end to deliver the response (null driver). How frontend
> >>>>and backend handle notifications is isolated to the frontend and
> >>>>backend with some backends (libtpms, passthough) sharing the code
> >>>>for how the notification is done.
> >>>>
> >>>>Stefan
> >>>Right. And all the locking/threading can then be internal to the backend.
> >>>
> >>Do you really want to replace code like this in the frontend:
> >>
> >>qemu_mutex_lock(&s->state_lock)
> >>[...]
> >>qemu_mutex_unlock(&s->state_lock)
> >>
> >>
> >>with
> >>
> >>
> >>s->be_driver->ops->state_lock(s->be_driver)
> >>[...]
> >>s->be_driver->ops->state_unlock(s->be_driver))
> >>
> >>
> >>where the backend starts protecting frontend data structures ?
> >It's ugly I hope you can do something saner:
> >ops->send_command(....)
> >with command encapsulating the relevant info.
> >
> >>At the moment there are two backends that need threading: the
> >>libtpms and passthrough backends. Both will require locking of
> >>datastructures that belong to the frontend. Only the null driver
> >>doesn't need a thread and the main thread can call into the backend,
> >>create the response and call via callback into the frontend to
> >>deliver the repsonse. If structures are protected via mutxes then
> >>only the NULL driver (which we don't want anyway) may end up
> >>grabbing mutexes that really aren't necessary while the two other
> >>backends need them. I don't see the mitextes as problematic. The
> >>frontend at least protects its data structures for the callbacks and
> >>other API calls it offers and they simply are thread-safe.
> >>
> >>     Stefan
> >Worst case, you can take a qemu mutex. Is tpm very
> >performance-sensitive to make contention on that
> >lock a problem?
> >
> 
> Having instrumented the code with qemu_mutex_trylock() and a counter
> for failed attempts with subsequent qemu_mutex_lock() practically
> shows no lock contention at all for either polling or IRQ mode being
> used by the Linux driver.
> 
> IRQ mode: 4 failed lock attempts out of 1726208 locks -> 0.00%
> polling mode: 2 failed lock attempts out of 1545216 locks -> 0.00%
> 
> I used the libtpms based backend with and ran IMA and a test suite
> in the VM.
> 
> 
>     Stefan

so maybe you can just do qemu_mutex_lock_iothread similar
to what kvm does.

-- 
MST

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-03-04 22:59                       ` Michael S. Tsirkin
@ 2012-03-05 15:44                         ` Stefan Berger
  2012-03-05 15:46                         ` Stefan Berger
  1 sibling, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2012-03-05 15:44 UTC (permalink / raw)
  To: qemu-devel

On 03/04/2012 05:59 PM, Michael S. Tsirkin wrote:
> On Fri, Mar 02, 2012 at 07:02:21AM -0500, Stefan Berger wrote:
>> Having instrumented the code with qemu_mutex_trylock() and a counter
>> for failed attempts with subsequent qemu_mutex_lock() practically
>> shows no lock contention at all for either polling or IRQ mode being
>> used by the Linux driver.
>>
>> IRQ mode: 4 failed lock attempts out of 1726208 locks ->  0.00%
>> polling mode: 2 failed lock attempts out of 1545216 locks ->  0.00%
>>
>> I used the libtpms based backend with and ran IMA and a test suite
>> in the VM.
>>
>>
>>      Stefan
> so maybe you can just do qemu_mutex_lock_iothread similar
> to what kvm does.
>

I have tried that now. I ended up having to remove the locking from the 
TIS except for one place where the result is delivered from the backend 
to the frontend via the thread. The reason why I had to remove the lock 
is because the pthread library detects a deadlock, likely due to 
attempted double-locking of the global lock that is not allowed to be 
locked more than once. That by itself is not the scary part but here's 
the stacktrace while I still had the lock in there:

#0  0x00007ffff37e2215 in __GI_raise (sig=6)
     at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1  0x00007ffff37e3b2b in __GI_abort () at abort.c:93
#2  0x00005555555c1069 in error_exit (err=<optimized out>, msg=
     0x55555583f350 "qemu_mutex_lock") at qemu-thread-posix.c:25
#3  0x00005555556daf10 in qemu_mutex_lock (mutex=<optimized out>)
     at qemu-thread-posix.c:56
#4  0x00005555557a6d43 in tpm_tis_mmio_read (opaque=0x555556e06a40, 
addr=3840,
     size=<optimized out>) at 
/home/stefanb/qemu/qemu-git.pt/hw/tpm_tis.c:448
#5  0x000055555575e157 in memory_region_read_accessor (opaque=<optimized 
out>,
     addr=<optimized out>, value=0x7fffef596c60, size=<optimized out>, 
shift=0,
     mask=4294967295) at /home/stefanb/qemu/qemu-git.pt/memory.c:259
#6  0x000055555575e270 in access_with_adjusted_size (addr=3840, value=
     0x7fffef596c60, size=4, access_size_min=<optimized out>,
     access_size_max=<optimized out>, access=
     0x55555575e120 <memory_region_read_accessor>, opaque=0x555556e06af0)
     at /home/stefanb/qemu/qemu-git.pt/memory.c:304
#7  0x0000555555762b80 in memory_region_dispatch_read1 (size=4, 
addr=3840, mr=
     0x555556e06af0) at /home/stefanb/qemu/qemu-git.pt/memory.c:928
#8  memory_region_dispatch_read (size=4, addr=3840, mr=0x555556e06af0)
     at /home/stefanb/qemu/qemu-git.pt/memory.c:960
#9  io_mem_read (io_index=<optimized out>, addr=3840, size=4)
     at /home/stefanb/qemu/qemu-git.pt/memory.c:1558
#10 0x000055555573a876 in cpu_physical_memory_rw (addr=4275310336, buf=
     0x7ffff7ff4028 "", len=4, is_write=0)
     at /home/stefanb/qemu/qemu-git.pt/exec.c:3620
#11 0x0000555555755225 in kvm_cpu_exec (env=0x555556561100)
     at /home/stefanb/qemu/qemu-git.pt/kvm-all.c:1173
#12 0x0000555555731201 in qemu_kvm_cpu_thread_fn (arg=0x555556561100)
     at /home/stefanb/qemu/qemu-git.pt/cpus.c:732
#13 0x00007ffff64a0b41 in start_thread (arg=0x7fffef597700)
     at pthread_create.c:305
#14 0x00007ffff388c49d in clone ()
     at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115

It may say qemu_mutex_lock but it is really calling 
qemu_mutex_lock_iothread.
The initial lock happends in kvm_cpu_exec (#11). The subsequent lock 
then occurs in tpm_tis_mmio_read (#4), so far away from the first lock. 
Well, if something in the code there changes we may end up having a race 
in the TPM TIS driver if we end up factoring the lock out. So personally 
I would prefer local locking where we can withstand changes in other 
parts of the code unless one can be sure that that global lock is never 
going to go away and that all code paths reaching the TIS driver 
function that need the locking have that global lock held.

     Stefan

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

* Re: [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-03-04 22:59                       ` Michael S. Tsirkin
  2012-03-05 15:44                         ` Stefan Berger
@ 2012-03-05 15:46                         ` Stefan Berger
  1 sibling, 0 replies; 41+ messages in thread
From: Stefan Berger @ 2012-03-05 15:46 UTC (permalink / raw)
  To: qemu-devel, Michael S. Tsirkin; +Cc: Andreas Niederl

Resending with people properly cc'ed.

On 03/04/2012 05:59 PM, Michael S. Tsirkin wrote:
> On Fri, Mar 02, 2012 at 07:02:21AM -0500, Stefan Berger wrote:
>> Having instrumented the code with qemu_mutex_trylock() and a counter
>> for failed attempts with subsequent qemu_mutex_lock() practically
>> shows no lock contention at all for either polling or IRQ mode being
>> used by the Linux driver.
>>
>> IRQ mode: 4 failed lock attempts out of 1726208 locks ->  0.00%
>> polling mode: 2 failed lock attempts out of 1545216 locks ->  0.00%
>>
>> I used the libtpms based backend with and ran IMA and a test suite
>> in the VM.
>>
>>
>>      Stefan
> so maybe you can just do qemu_mutex_lock_iothread similar
> to what kvm does.
>

I have tried that now. I ended up having to remove the locking from the 
TIS except for one place where the result is delivered from the backend 
to the frontend via the thread. The reason why I had to remove the lock 
is because the pthread library detects a deadlock, likely due to 
attempted double-locking of the global lock that is not allowed to be 
locked more than once. That by itself is not the scary part but here's 
the stacktrace while I still had the lock in there:

#0  0x00007ffff37e2215 in __GI_raise (sig=6)
     at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1  0x00007ffff37e3b2b in __GI_abort () at abort.c:93
#2  0x00005555555c1069 in error_exit (err=<optimized out>, msg=
     0x55555583f350 "qemu_mutex_lock") at qemu-thread-posix.c:25
#3  0x00005555556daf10 in qemu_mutex_lock (mutex=<optimized out>)
     at qemu-thread-posix.c:56
#4  0x00005555557a6d43 in tpm_tis_mmio_read (opaque=0x555556e06a40, 
addr=3840,
     size=<optimized out>) at 
/home/stefanb/qemu/qemu-git.pt/hw/tpm_tis.c:448
#5  0x000055555575e157 in memory_region_read_accessor (opaque=<optimized 
out>,
     addr=<optimized out>, value=0x7fffef596c60, size=<optimized out>, 
shift=0,
     mask=4294967295) at /home/stefanb/qemu/qemu-git.pt/memory.c:259
#6  0x000055555575e270 in access_with_adjusted_size (addr=3840, value=
     0x7fffef596c60, size=4, access_size_min=<optimized out>,
     access_size_max=<optimized out>, access=
     0x55555575e120 <memory_region_read_accessor>, opaque=0x555556e06af0)
     at /home/stefanb/qemu/qemu-git.pt/memory.c:304
#7  0x0000555555762b80 in memory_region_dispatch_read1 (size=4, 
addr=3840, mr=
     0x555556e06af0) at /home/stefanb/qemu/qemu-git.pt/memory.c:928
#8  memory_region_dispatch_read (size=4, addr=3840, mr=0x555556e06af0)
     at /home/stefanb/qemu/qemu-git.pt/memory.c:960
#9  io_mem_read (io_index=<optimized out>, addr=3840, size=4)
     at /home/stefanb/qemu/qemu-git.pt/memory.c:1558
#10 0x000055555573a876 in cpu_physical_memory_rw (addr=4275310336, buf=
     0x7ffff7ff4028 "", len=4, is_write=0)
     at /home/stefanb/qemu/qemu-git.pt/exec.c:3620
#11 0x0000555555755225 in kvm_cpu_exec (env=0x555556561100)
     at /home/stefanb/qemu/qemu-git.pt/kvm-all.c:1173
#12 0x0000555555731201 in qemu_kvm_cpu_thread_fn (arg=0x555556561100)
     at /home/stefanb/qemu/qemu-git.pt/cpus.c:732
#13 0x00007ffff64a0b41 in start_thread (arg=0x7fffef597700)
     at pthread_create.c:305
#14 0x00007ffff388c49d in clone ()
     at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115

It may say qemu_mutex_lock but it is really calling 
qemu_mutex_lock_iothread.
The initial lock happends in kvm_cpu_exec (#11). The subsequent lock 
then occurs in tpm_tis_mmio_read (#4), so far away from the first lock. 
Well, if something in the code there changes we may end up having a race 
in the TPM TIS driver if we end up factoring the lock out. So personally 
I would prefer local locking where we can withstand changes in other 
parts of the code unless one can be sure that that global lock is never 
going to go away and that all code paths reaching the TIS driver 
function that need the locking have that global lock held.

     Stefan

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

* Re: [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation
  2012-02-21  1:03         ` Stefan Berger
@ 2012-03-21 23:27           ` Anthony Liguori
  0 siblings, 0 replies; 41+ messages in thread
From: Anthony Liguori @ 2012-03-21 23:27 UTC (permalink / raw)
  To: Stefan Berger; +Cc: andreas.niederl, qemu-devel, Michael S. Tsirkin

On 02/20/2012 07:03 PM, Stefan Berger wrote:
> On 02/20/2012 04:15 PM, Michael S. Tsirkin wrote:
>> On Mon, Feb 20, 2012 at 03:25:37PM -0500, Stefan Berger wrote:
>>> On 02/20/2012 02:51 PM, Michael S. Tsirkin wrote:
>>>> On Wed, Dec 14, 2011 at 08:43:20AM -0500, Stefan Berger wrote:
>>>>>> From Andreas Niederl's original posting with adaptations where necessary:
>>>>> This patch is based of off version 9 of Stefan Berger's patch series
>>>>> "Qemu Trusted Platform Module (TPM) integration"
>>>>> and adds a new backend driver for it.
>>>>>
>>>>> This patch adds a passthrough backend driver for passing commands sent to the
>>>>> emulated TPM device directly to a TPM device opened on the host machine.
>>>>>
>>>>> Thus it is possible to use a hardware TPM device in a system running on QEMU,
>>>>> providing the ability to access a TPM in a special state (e.g. after a Trusted
>>>>> Boot).
>>>>>
>>>>> This functionality is being used in the acTvSM Trusted Virtualization Platform
>>>>> which is available on [1].
>>>>>
>>>>> Usage example:
>>>>> qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
>>>>> -device tpm-tis,tpmdev=tpm0 \
>>>>> -cdrom test.iso -boot d
>>>>>
>>>>> Some notes about the host TPM:
>>>>> The TPM needs to be enabled and activated. If that's not the case one
>>>>> has to go through the BIOS/UEFI and enable and activate that TPM for TPM
>>>>> commands to work as expected.
>>>>> It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
>>>>> command line or 'modprobe tpm_tis force=1' in case of using it as a module.
>>>>>
>>>>> Regards,
>>>>> Andreas Niederl, Stefan Berger
>>>>>
>>>>> [1] http://trustedjava.sourceforge.net/
>>>>>
>>>>> Signed-off-by: Andreas Niederl<andreas.niederl@iaik.tugraz.at>
>>>>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>>>> So this was mentioned by Blue Swirl and Anthony and
>>>> I have to agree: it's not clear why this wants its own
>>>> thread.
>>> This is not only due to how the Linux TPM driver works but also, as
>>> previously mentioned, due to how I would like the libtpms driver to
>>> work. The former does not support select() (yes, this is probably a
>>> TPM driver problem but it has been around for ages and didn't matter
>>> so far because only the TSS (TrouSerS) was meant to access the TPM).
>>> The latter will certainly support creation of 2048 bit RSA keys and
>>> disappear into e crypto function that also isn't select() able,
>>> unless you end up introducing another thread here.
>>> And you most
>>> probably don't want the main thread to be busy for several seconds
>>> (depending on availability of CPU cycles) to have that key created.
>>> So in effect having this thread allows us to have a common
>>> architecture for both passthrough and libtpms backends.
>>>
>> OK, so let's say this is a work around linux tpm
>> backend limitations. But it is unfortunate that this
>> spills into frontend code.
>> How about taking the global qemu lock instead?
>>
>> Also, you need to be more careful with
>> running your thread, e.g. it must be stopped on
>> vmstop, etc. It is probably a good idea to use
>> a genetic thread pool with all that logic.
>
> Some backends may be able to react to a pthread_kill() (qemu_thread_kill() ?)
> which should be able to deliver a SIG_TERM to a thread. The passthrough thread,
> however, is not going to be able to react to this if it is stuck in the write()
> call to /dev/tpm0. I am sending the passthrough backend a signal to terminate
> and then do a qemu_thread_join() to wait for it to end. The libtpms backend on
> the other hand may be able to support a pthread_kill().
> Why would I need a thread pool ?

The model for handling this in QEMU is to use a thread pool.  We do this for 
AIO, virtio-9p, VNC, etc.

What's run in the thread pool is typically minimal (a single system call) but in 
the case of the libtpms backend, may be a full library call to the libtpms 
driver.  The key point is that we're not mixing re-entrant code (the code that 
interfaces with libtpms) with non re-entrant code (QEMU).

Submitting to the thread pool and processing the response is all done via the 
thread pool.

Regards,

Anthony Liguori

>
> Stefan
>
>
>
>>
>>>> I would go further and claim that forcing a threaded build for a
>>>> periferal just looks wrong. What's going on, select+unblocking access
>>>> does not work for tpm?
>>> Yep.
>> Then maybe it's better to make tpm *depend* on threads,
>> not force them.
>
> And how would that change the code ?
>
>
> Stefan
>
>

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

end of thread, other threads:[~2012-03-21 23:27 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-14 13:43 [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 1/7] Support for TPM command line options Stefan Berger
2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
2012-02-20  8:51   ` Michael S. Tsirkin
2012-02-20 15:48     ` Stefan Berger
2012-02-20 19:37       ` Michael S. Tsirkin
2012-02-20 19:58         ` Stefan Berger
2012-02-23 20:47       ` Stefan Berger
2012-02-20 22:02   ` Michael S. Tsirkin
2012-02-21  0:43     ` Stefan Berger
2012-02-21  3:18       ` Michael S. Tsirkin
2012-02-21 11:19         ` Stefan Berger
2012-02-21 12:18           ` Michael S. Tsirkin
2012-02-21 15:05             ` Stefan Berger
2012-02-21 19:58               ` Michael S. Tsirkin
2012-02-21 22:30                 ` Stefan Berger
2012-02-21 23:08                   ` Michael S. Tsirkin
2012-02-22  0:21                     ` Stefan Berger
2012-02-22  4:34                       ` Michael S. Tsirkin
2012-02-22 15:03                         ` Stefan Berger
2012-02-22 17:55                     ` Stefan Berger
2012-03-02 12:02                     ` Stefan Berger
2012-03-04 22:59                       ` Michael S. Tsirkin
2012-03-05 15:44                         ` Stefan Berger
2012-03-05 15:46                         ` Stefan Berger
2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 3/7] Add a debug register Stefan Berger
2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 4/7] Build the TPM frontend code Stefan Berger
2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
2012-02-20 19:51   ` Michael S. Tsirkin
2012-02-20 20:25     ` Stefan Berger
2012-02-20 21:15       ` Michael S. Tsirkin
2012-02-21  1:03         ` Stefan Berger
2012-03-21 23:27           ` Anthony Liguori
2012-02-20 20:01   ` Michael S. Tsirkin
2012-02-20 21:12     ` Stefan Berger
2012-02-20 21:30   ` Michael S. Tsirkin
2012-02-21  0:30     ` Stefan Berger
2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
2011-12-14 13:43 ` [Qemu-devel] [PATCH V14 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
2012-01-12 16:59 ` [Qemu-devel] [PATCH V14 0/7] Qemu Trusted Platform Module (TPM) integration Paul Moore
2012-01-16 19:21   ` Paul Moore

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.