qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration
@ 2011-12-12 19:12 Stefan Berger
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options Stefan Berger
                   ` (7 more replies)
  0 siblings, 8 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 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.

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      |    2 +
 configure            |   28 ++
 hmp-commands.hx      |    2 +
 hw/tpm_passthrough.c |  512 +++++++++++++++++++++++++++++
 hw/tpm_tis.c         |  891 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/tpm_tis.h         |   91 +++++
 monitor.c            |   10 +
 qemu-config.c        |   25 ++
 qemu-options.hx      |   71 ++++
 tpm.c                |  190 +++++++++++
 tpm.h                |  124 +++++++
 vl.c                 |   15 +
 12 files changed, 1961 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] 29+ messages in thread

* [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options
  2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
  2011-12-12 23:16   ` Anthony Liguori
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 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>

---

v12:
 - use all 4 bytes of the message length indicator

v10:
 - tpm_display_backend_drivers always prints to stderr

v9:
 - prefixing all functions with tpm_tis_ and all constants with TPM_TIS_
 - splitting off of -tpm command line support into its own patch
 - only support TPM passthrough for now

v8:
 - adjusting formatting of backend drivers output to accomodate better
   formatting of 'passthrough' backend output

v6:
 - use #idef CONFIG_TPM to surround TPM calls
 - use QLIST_FOREACH_SAFE rather than QLIST_FOREACH in tpm_cleanup
 - commented backend ops in tpm.h
 - moving to IRQ 5 (11 collided with network cards)

v5:
 - fixing typo reported by Serge Hallyn
 - Adapting code to split command line parameters supporting
   -tpmdev ... -device tpm-tis,tpmdev=...
 - moved code out of arch_init.c|h into tpm.c|h
 - increasing reserved memory for ACPI tables to 128kb (from 64kb)
 - the backend interface has a create() function for interpreting the command
   line parameters and returning a TPMDevice structure; previoulsy
   this function was called handle_options()
 - the backend interface has a destroy() function for cleaning up after
   the create() function was called
 - added support for 'info tpm' in monitor

v4:
 - coding style fixes

v3:
 - added hw/tpm_tis.h to this patch so Qemu compiles at this stage
---
 hmp-commands.hx |    2 +
 hw/tpm_tis.h    |   91 ++++++++++++++++++++++++++++++
 monitor.c       |   10 +++
 qemu-config.c   |   20 +++++++
 qemu-options.hx |   34 +++++++++++
 tpm.c           |  167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tpm.h           |   90 +++++++++++++++++++++++++++++
 vl.c            |   15 +++++
 8 files changed, 429 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/hw/tpm_tis.h b/hw/tpm_tis.h
new file mode 100644
index 0000000..e6a9a4b
--- /dev/null
+++ b/hw/tpm_tis.h
@@ -0,0 +1,91 @@
+/*
+ * 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 <stdint.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;
+
+enum tpm_tis_state {
+    TPM_TIS_STATE_IDLE = 0,
+    TPM_TIS_STATE_READY,
+    TPM_TIS_STATE_COMPLETION,
+    TPM_TIS_STATE_EXECUTION,
+    TPM_TIS_STATE_RECEPTION,
+};
+
+/* locality data  -- all fields are persisted */
+typedef struct TPMLocality {
+    enum tpm_tis_state state;
+    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;
+
+/* utility functions */
+
+static inline uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+    return (sb->buffer[2] << 24) |
+           (sb->buffer[3] << 16) |
+           (sb->buffer[4] <<  8) |
+            sb->buffer[5];
+}
+
+#endif /* _HW_TPM_TIS_H */
diff --git a/monitor.c b/monitor.c
index 7334401..28873c3 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"
@@ -2735,6 +2736,15 @@ static mon_cmd_t info_cmds[] = {
         .help       = "show available trace-events & their state",
         .mhandler.info = do_trace_print_events,
     },
+#if defined(CONFIG_TPM)
+    {
+        .name       = "tpm",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show the TPM device",
+        .mhandler.info = do_info_tpm,
+    },
+#endif
     {
         .name       = NULL,
     },
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..58a5dfa 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1903,6 +1903,40 @@ ETEXI
 
 DEFHEADING()
 
+DEFHEADING(TPM device options:)
+
+#ifndef _WIN32
+# ifdef CONFIG_TPM
+DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
+    "-tpmdev [<type>],id=str[,option][,option][,...]\n",
+    QEMU_ARCH_ALL)
+# endif
+#endif
+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..fcab023
--- /dev/null
+++ b/tpm.c
@@ -0,0 +1,167 @@
+/*
+ * 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.  See
+ * the COPYING file in the top-level directory.
+ *
+ * Based on net.c
+ */
+#include "config.h"
+
+#include "tpm.h"
+#include "monitor.h"
+#include "qerror.h"
+
+static const TPMDriverOps *bes[] = {
+    NULL,
+};
+
+static QLIST_HEAD(, TPMBackend) tpm_backends =
+    QLIST_HEAD_INITIALIZER(tpm_backends);
+
+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];
+}
+
+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");
+}
+
+TPMBackend *qemu_find_tpm(const char *id)
+{
+    TPMBackend *drv;
+
+    QLIST_FOREACH(drv, &tpm_backends, list) {
+        if (!strcmp(drv->id, id)) {
+            return drv;
+        }
+    }
+
+    return NULL;
+}
+
+void do_info_tpm(Monitor *mon)
+{
+    TPMBackend *drv;
+    unsigned int c = 0;
+
+    monitor_printf(mon, "TPM device:\n");
+
+    QLIST_FOREACH(drv, &tpm_backends, list) {
+        monitor_printf(mon, " tpm%d: model=%s\n",
+                       c, drv->fe_model);
+        monitor_printf(mon, "  \\ %s: type=%s%s%s\n",
+                       drv->id, drv->ops->id,
+                       drv->parameters ? "," : "",
+                       drv->parameters ? drv->parameters : "");
+        c++;
+    }
+}
+
+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);
+}
+
+void tpm_cleanup(void)
+{
+    TPMBackend *drv, *next;
+
+    QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
+        QLIST_REMOVE(drv, list);
+        drv->ops->destroy(drv);
+    }
+}
+
+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;
+}
+
+void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
+{
+    QemuOpts *opts;
+
+    if (strcmp("none", optarg) != 0) {
+        if (*optarg == '?') {
+            tpm_display_backend_drivers();
+            exit(0);
+        }
+        opts = qemu_opts_parse(opts_list, optarg, 1);
+        if (!opts) {
+            exit(1);
+        }
+    }
+}
diff --git a/tpm.h b/tpm.h
new file mode 100644
index 0000000..85c2a35
--- /dev/null
+++ b/tpm.h
@@ -0,0 +1,90 @@
+#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);
+};
+
+static inline void tpm_dump_buffer(FILE *stream,
+                                   unsigned char *buffer, unsigned int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++) {
+        if (i && !(i % 16)) {
+            fprintf(stream, "\n");
+        }
+        fprintf(stream, "%.2X ", buffer[i]);
+    }
+    fprintf(stream, "\n");
+}
+
+#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
+
+void 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 do_info_tpm(Monitor *mon);
+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..12915d0 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,11 @@ int main(int argc, char **argv, char **envp)
                 ram_size = value;
                 break;
             }
+#ifdef CONFIG_TPM
+            case QEMU_OPTION_tpmdev:
+                tpm_config_parse(qemu_find_opts("tpmdev"), optarg);
+                break;
+#endif
             case QEMU_OPTION_mempath:
                 mem_path = optarg;
                 break;
@@ -3243,6 +3249,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 +3499,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] 29+ messages in thread

* [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
  2011-12-12 23:23   ` Anthony Liguori
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 3/7] Add a debug register Stefan Berger
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 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>

---

v13:
  - don't call destroy callback in error path in tpm_tis_init

v11:
  - initialize val with 0xffffffff as default value to return from MMIO
    read to an undefined location
  - display size of read or write operations in debugging output

v10:
  - Use error_report where necessary
  - Convert to use memory_region_init_io; use DEVICE_NATIVE_ENDIAN which
    works for an x86 VM on x86 or ppc host
  - Implement cleanup in tpm_tis_init
  - put frontend model name into TPMBackend driver structure for later display
    with the monitor

v9:
  - prefixing all function with tpm_tis_ and all constants with TPM_TIS_
  - adding minimum VMStateDescription and marking device as non-migratable

v5:
  - adding comment to tis_data_read
  - refactoring following support for split command line options
    -tpmdev and -device
  - code handling the configuration of the TPM device was moved to tpm.c
  - removed empty line at end of file

v3:
  - prefixing functions with tis_
  - added a function to the backend interface 'early_startup_tpm' that
    allows to detect the presence of the block storage and gracefully fails
    Qemu if it's not available. This works with migration using shared
    storage but doesn't support migration with block storage migration.
    For encyrypted QCoW2 and in case of a snapshot resue the late_startup_tpm
    interface function is called
---
 hw/tpm_tis.c |  818 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 818 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..34d0043
--- /dev/null
+++ b/hw/tpm_tis.c
@@ -0,0 +1,818 @@
+/*
+ * 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 <stdio.h>
+
+/*#define DEBUG_TIS */
+
+/* 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
+
+#ifdef DEBUG_TIS
+static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
+{
+    uint16_t len;
+
+    len = tpm_tis_get_size_from_buffer(sb);
+    fprintf(stderr, "tpm_tis: %s length = %d\n", string, len);
+    tpm_dump_buffer(stderr, sb->buffer, len);
+}
+#endif
+
+static inline uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
+{
+    return (uint8_t)((addr >> 12) & 0x7);
+}
+
+/*
+ * 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;
+#ifdef DEBUG_TIS
+    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
+#endif
+    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].state = TPM_TIS_STATE_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)) {
+#ifdef DEBUG_TIS
+        fprintf(stderr, "tpm_tis: Raising IRQ for flag %08x\n", irqmask);
+#endif
+        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;
+#ifdef DEBUG_TIS
+    fprintf(stderr, "tpm_tis: Active locality is now %d\n",
+            s->s.tis.active_locty);
+#endif
+
+    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;
+
+#ifdef DEBUG_TIS
+    fprintf(stderr, "tpm_tis: tis_abort: new active locality is %d\n",
+            tis->next_locty);
+#endif
+
+    /*
+     * 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].state = TPM_TIS_STATE_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].state == TPM_TIS_STATE_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; */
+#ifdef DEBUG_TIS
+            fprintf(stderr, "tpm_tis: Locality %d is busy - "
+                            "deferring abort\n", busy_locty);
+#endif
+            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].state = TPM_TIS_STATE_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
+        }
+#ifdef DEBUG_TIS
+        fprintf(stderr, "tpm_tis: tpm_tis_data_read byte 0x%02x   [%d]\n",
+                ret, tis->loc[locty].r_offset-1);
+#endif
+    }
+
+    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].state) {
+            case TPM_TIS_STATE_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;
+    }
+
+#ifdef DEBUG_TIS
+    fprintf(stderr, "tpm_tis:  read.%u(%08x) = %08x\n",
+            size, (int)addr, (uint32_t)val);
+#endif
+
+    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;
+
+#ifdef DEBUG_TIS
+    fprintf(stderr, "tpm_tis: write.%u(%08x) = %08x\n",
+            size, (int)addr, (uint32_t)val);
+#endif
+
+    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) {
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: Releasing locality %d\n", locty);
+#endif
+                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)) {
+#ifdef DEBUG_TIS
+                        fprintf(stderr, "tpm_tis: Locality %d requests use.\n",
+                                c);
+#endif
+                        newlocty = c;
+                        break;
+                    }
+                }
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
+                                "Next active locality: %d\n",
+                                newlocty);
+#endif
+                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;
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: TPM_TIS_ACCESS_SEIZE: "
+                                "Locality %d seized from locality %d\n",
+                                locty, tis->active_locty);
+                fprintf(stderr,
+                        "tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
+#endif
+                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);
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: Lowering IRQ\n");
+#endif
+            }
+        }
+        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].state) {
+
+            case TPM_TIS_STATE_READY:
+                tis->loc[locty].w_offset = 0;
+                tis->loc[locty].r_offset = 0;
+            break;
+
+            case TPM_TIS_STATE_IDLE:
+                tis->loc[locty].sts   = TPM_TIS_STS_COMMAND_READY;
+                tis->loc[locty].state = TPM_TIS_STATE_READY;
+                tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
+            break;
+
+            case TPM_TIS_STATE_EXECUTION:
+            case TPM_TIS_STATE_RECEPTION:
+                /* abort currently running command */
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: %s: Initiating abort.\n",
+                        __func__);
+#endif
+                tpm_tis_prep_abort(s, locty, locty);
+            break;
+
+            case TPM_TIS_STATE_COMPLETION:
+                tis->loc[locty].w_offset = 0;
+                tis->loc[locty].r_offset = 0;
+                /* shortcut to ready state with C/R set */
+                tis->loc[locty].state = TPM_TIS_STATE_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].state) {
+            case TPM_TIS_STATE_RECEPTION:
+                tpm_tis_tpm_send(s, locty);
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
+            switch (tis->loc[locty].state) {
+            case TPM_TIS_STATE_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].state == TPM_TIS_STATE_IDLE ||
+            tis->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
+            tis->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
+            /* drop the byte */
+        } else {
+#ifdef DEBUG_TIS
+            fprintf(stderr, "tpm_tis: Byte to send to TPM: %02x\n",
+                    (uint8_t)val);
+#endif
+            if (tis->loc[locty].state == TPM_TIS_STATE_READY) {
+                tis->loc[locty].state = TPM_TIS_STATE_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].state = TPM_TIS_STATE_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] 29+ messages in thread

* [Qemu-devel] [PATCH V13 3/7] Add a debug register
  2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options Stefan Berger
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code Stefan Berger
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 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>

---

v9:
 - prefixing all function with tpm_tis_ and all constants with TPM_TIS_

v3:
 - all output goes to stderr
---
 hw/tpm_tis.c |   73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 73 insertions(+), 0 deletions(-)

diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
index 34d0043..1fd105e 100644
--- a/hw/tpm_tis.c
+++ b/hw/tpm_tis.c
@@ -47,6 +47,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)
@@ -92,6 +95,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);
+
+
 #ifdef DEBUG_TIS
 static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
 {
@@ -319,6 +327,66 @@ 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;
+
+    fprintf(stderr,
+            "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].state);
+
+    for (idx = 0; regs[idx] != 0xfff; idx++) {
+        fprintf(stderr, "tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
+                (uint32_t)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
+    }
+
+    fprintf(stderr,
+            "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++) {
+        fprintf(stderr, "%c%02x%s",
+                tis->loc[locty].r_offset == idx ? '>' : ' ',
+                tis->loc[locty].r_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    }
+    fprintf(stderr,
+            "\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++) {
+        fprintf(stderr, "%c%02x%s",
+                tis->loc[locty].w_offset == idx ? '>' : ' ',
+                tis->loc[locty].w_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    }
+    fprintf(stderr, "\n");
+}
+#endif
+
 /*
  * Read a register of the TIS interface
  * See specs pages 33-63 for description of the registers
@@ -391,6 +459,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] 29+ messages in thread

* [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code
  2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (2 preceding siblings ...)
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 3/7] Add a debug register Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
  2011-12-12 23:24   ` Anthony Liguori
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 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 |    1 +
 configure       |   11 +++++++++++
 2 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/Makefile.target b/Makefile.target
index 39b2e5a..37d5d10 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -205,6 +205,7 @@ 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-$(CONFIG_TPM) += tpm.o 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] 29+ messages in thread

* [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation
  2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (3 preceding siblings ...)
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
  2011-12-12 23:27   ` Anthony Liguori
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 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>

---

Changes for v12:
 - check size indicator in response from TPM to match that of the received
   packet

Changes for v10:
 - clarified documentation
 - using /dev/tpm0 as default device if path option is not given
 - refactored code handling 'device' option into its own function
 - fixed name of structure to TPMPassthruThreadParams
 - only add tpm_passthrough_driver to collection of backends if
   it is compiled on the host

Changes for v9:
 - prefixing of all functions and variables with tpm_passthrough_
 - cleanup of all variables into a structure that is now accessed
   using TPMBackend (tb->s.tpm_pt)
 - build it on Linux machines
 - added function to test whether given device is a TPM and refuse
   startup if it is not
---
 Makefile.target      |    1 +
 configure            |    3 +
 hw/tpm_passthrough.c |  477 ++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-options.hx      |   33 ++++
 tpm.c                |   23 +++
 tpm.h                |   34 ++++
 6 files changed, 571 insertions(+), 0 deletions(-)
 create mode 100644 hw/tpm_passthrough.c

diff --git a/Makefile.target b/Makefile.target
index 37d5d10..4fc96e6 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -206,6 +206,7 @@ obj-$(CONFIG_KVM) += kvm.o kvm-all.o
 obj-$(CONFIG_NO_KVM) += kvm-stub.o
 obj-y += memory.o
 obj-$(CONFIG_TPM) += tpm.o 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..f9cfe3d
--- /dev/null
+++ b/hw/tpm_passthrough.c
@@ -0,0 +1,477 @@
+/*
+ *  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 */
+
+/* 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 void *tpm_passthrough_main_loop(void *d)
+{
+    TPMPassthruThreadParams *thr_parms = d;
+    TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
+    uint32_t in_len, out_len;
+    uint8_t *in, *out;
+    uint8_t locty;
+    TPMLocality *cmd_locty;
+    int ret;
+
+#ifdef DEBUG_TPM
+    fprintf(stderr, "tpm_passthrough: THREAD IS STARTING\n");
+#endif
+
+    /* start command processing */
+    while (!tpm_pt->thread_terminate) {
+        /* receive and handle commands */
+        in_len = 0;
+        do {
+#ifdef DEBUG_TPM
+            fprintf(stderr, "tpm_passthrough: waiting for commands...\n");
+#endif
+
+            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;
+            }
+
+            locty = thr_parms->tpm_state->command_locty;
+
+            cmd_locty = thr_parms->tpm_state->cmd_locty;
+
+            in      = cmd_locty->w_buffer.buffer;
+            in_len  = cmd_locty->w_offset;
+            out     = cmd_locty->r_buffer.buffer;
+            out_len = cmd_locty->r_buffer.size;
+
+            ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
+            if (ret < 0) {
+                error_report("tpm_passthrough: error while transmitting data "
+                             "to host tpm: %s (%i)\n",
+                             strerror(errno), errno);
+                tpm_write_std_fatal_error_response(out, out_len, in, in_len);
+                goto send_resp;
+            }
+
+            ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
+            if (ret < 0) {
+                error_report("tpm_passthrough: error while reading data from "
+                             "host tpm : %s (%i)\n",
+                             strerror(errno), errno);
+                tpm_write_std_fatal_error_response(out, out_len, in, in_len);
+            } else if (ret < sizeof(struct tpm_resp_hdr) ||
+                       tpm_tis_get_size_from_buffer(&cmd_locty->r_buffer) !=
+                       ret) {
+                error_report("tpm_passthrough: received invalid response "
+                             "packet from TPM\n");
+                tpm_write_std_fatal_error_response(out, out_len, in, in_len);
+            }
+
+send_resp:
+            thr_parms->recv_data_callback(thr_parms->tpm_state, locty);
+        } while (in_len > 0);
+    }
+
+#ifdef DEBUG_TPM
+    fprintf(stderr, "tpm_passthrough: THREAD IS ENDING\n");
+#endif
+
+    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;
+    }
+
+#if defined DEBUG_TPM
+    fprintf(stderr, "tpm_passthrough: TERMINATING RUNNING TPM THREAD\n");
+#endif
+
+    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);
+
+        while (tpm_pt->thread_running) {
+            usleep(100000);
+        }
+        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_main_loop,
+                       &tpm_pt->tpm_thread_params);
+
+    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;
+
+#if defined DEBUG_TPM
+    fprintf(stderr, "tpm_passthrough: CALL TO TPM_RESET!\n");
+#endif
+
+    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 58a5dfa..4916baf 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1920,6 +1920,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.
@@ -1931,6 +1932,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 fcab023..6470e59 100644
--- a/tpm.c
+++ b/tpm.c
@@ -18,12 +18,35 @@
 #include "qerror.h"
 
 static const TPMDriverOps *bes[] = {
+#ifdef CONFIG_TPM_PASSTHROUGH
+    &tpm_passthrough_driver,
+#endif
     NULL,
 };
 
 static QLIST_HEAD(, TPMBackend) tpm_backends =
     QLIST_HEAD_INITIALIZER(tpm_backends);
 
+void tpm_write_std_fatal_error_response(uint8_t *out, uint32_t out_len,
+                                        uint8_t *in, uint32_t in_len)
+{
+    if (out_len >= sizeof(struct tpm_resp_hdr)) {
+        struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
+        struct tpm_req_hdr *req  = (struct tpm_req_hdr *)in;
+        uint16_t tag = be16_to_cpu(req->tag);
+
+        resp->tag = cpu_to_be16(
+                     (in_len > 2 &&
+                      tag >= TPM_TAG_RQU_COMMAND &&
+                      tag <= TPM_TAG_RQU_AUTH2_COMMAND)
+                     ? tag + 3
+                     : 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 85c2a35..505a535 100644
--- a/tpm.h
+++ b/tpm.h
@@ -7,12 +7,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;
 
@@ -79,6 +85,30 @@ static inline void tpm_dump_buffer(FILE *stream,
 
 #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
+
 void tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
 int tpm_init(void);
 void tpm_cleanup(void);
@@ -86,5 +116,9 @@ TPMBackend *qemu_find_tpm(const char *id);
 void do_info_tpm(Monitor *mon);
 void tpm_display_backend_drivers(void);
 const TPMDriverOps *tpm_get_backend_driver(const char *id);
+void tpm_write_std_fatal_error_response(uint8_t *out, uint32_t out_len,
+                                        uint8_t *in, uint32_t in_len);
+
+extern const TPMDriverOps tpm_passthrough_driver;
 
 #endif /* _QEMU_TPM_H */
-- 
1.7.6.4

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

* [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (4 preceding siblings ...)
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
  2011-12-12 23:27   ` Anthony Liguori
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
  2011-12-13  5:45 ` [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
  7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 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] 29+ messages in thread

* [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver
  2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (5 preceding siblings ...)
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
  2011-12-12 23:30   ` Anthony Liguori
  2011-12-13  5:45 ` [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
  7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 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>

---

v13:
 - Only accepting a character device's file descriptor

v12:
 - added documentation part
---
 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 f9cfe3d..57bb77a 100644
--- a/hw/tpm_passthrough.c
+++ b/hw/tpm_passthrough.c
@@ -361,33 +361,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 4916baf..7eca701 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1932,7 +1932,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.
@@ -1941,6 +1941,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] 29+ messages in thread

* Re: [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options Stefan Berger
@ 2011-12-12 23:16   ` Anthony Liguori
  2011-12-13  2:16     ` Stefan Berger
  0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:16 UTC (permalink / raw)
  To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl

On 12/12/2011 01:12 PM, Stefan Berger wrote:
> 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>
>
> ---
>
> v12:
>   - use all 4 bytes of the message length indicator
>
> v10:
>   - tpm_display_backend_drivers always prints to stderr
>
> v9:
>   - prefixing all functions with tpm_tis_ and all constants with TPM_TIS_
>   - splitting off of -tpm command line support into its own patch
>   - only support TPM passthrough for now
>
> v8:
>   - adjusting formatting of backend drivers output to accomodate better
>     formatting of 'passthrough' backend output
>
> v6:
>   - use #idef CONFIG_TPM to surround TPM calls
>   - use QLIST_FOREACH_SAFE rather than QLIST_FOREACH in tpm_cleanup
>   - commented backend ops in tpm.h
>   - moving to IRQ 5 (11 collided with network cards)
>
> v5:
>   - fixing typo reported by Serge Hallyn
>   - Adapting code to split command line parameters supporting
>     -tpmdev ... -device tpm-tis,tpmdev=...
>   - moved code out of arch_init.c|h into tpm.c|h
>   - increasing reserved memory for ACPI tables to 128kb (from 64kb)
>   - the backend interface has a create() function for interpreting the command
>     line parameters and returning a TPMDevice structure; previoulsy
>     this function was called handle_options()
>   - the backend interface has a destroy() function for cleaning up after
>     the create() function was called
>   - added support for 'info tpm' in monitor
>
> v4:
>   - coding style fixes
>
> v3:
>   - added hw/tpm_tis.h to this patch so Qemu compiles at this stage
> ---
>   hmp-commands.hx |    2 +
>   hw/tpm_tis.h    |   91 ++++++++++++++++++++++++++++++
>   monitor.c       |   10 +++
>   qemu-config.c   |   20 +++++++
>   qemu-options.hx |   34 +++++++++++
>   tpm.c           |  167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   tpm.h           |   90 +++++++++++++++++++++++++++++
>   vl.c            |   15 +++++
>   8 files changed, 429 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/hw/tpm_tis.h b/hw/tpm_tis.h
> new file mode 100644
> index 0000000..e6a9a4b
> --- /dev/null
> +++ b/hw/tpm_tis.h
> @@ -0,0 +1,91 @@
> +/*
> + * 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

Macros shouldn't start with '_' as that namespace is reserved in C.

> +#include "isa.h"
> +#include "qemu-thread.h"
> +
> +#include<stdint.h>

Just 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;
> +
> +enum tpm_tis_state {
> +    TPM_TIS_STATE_IDLE = 0,
> +    TPM_TIS_STATE_READY,
> +    TPM_TIS_STATE_COMPLETION,
> +    TPM_TIS_STATE_EXECUTION,
> +    TPM_TIS_STATE_RECEPTION,
> +};

Typed enums still need to be CamelCase and typedefed.

> +/* locality data  -- all fields are persisted */
> +typedef struct TPMLocality {
> +    enum tpm_tis_state state;
> +    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;
> +
> +/* utility functions */
> +
> +static inline uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
> +{
> +    return (sb->buffer[2]<<  24) |
> +           (sb->buffer[3]<<  16) |
> +           (sb->buffer[4]<<   8) |
> +            sb->buffer[5];
> +}

I would strongly suggest not making this static inline.

> +
> +#endif /* _HW_TPM_TIS_H */
> diff --git a/monitor.c b/monitor.c
> index 7334401..28873c3 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"
> @@ -2735,6 +2736,15 @@ static mon_cmd_t info_cmds[] = {
>           .help       = "show available trace-events&  their state",
>           .mhandler.info = do_trace_print_events,
>       },
> +#if defined(CONFIG_TPM)
> +    {
> +        .name       = "tpm",
> +        .args_type  = "",
> +        .params     = "",
> +        .help       = "show the TPM device",
> +        .mhandler.info = do_info_tpm,
> +    },
> +#endif

Please don't make monitor commands conditional.  Make it fail in a predictable 
fashion if tpm isn't configured.

>       {
>           .name       = NULL,
>       },
> 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,

I assume this is my mailer or is the whitespace munged here?

>       NULL,
>   };
>
> diff --git a/qemu-options.hx b/qemu-options.hx
> index b3db10c..58a5dfa 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1903,6 +1903,40 @@ ETEXI
>
>   DEFHEADING()
>
> +DEFHEADING(TPM device options:)
> +
> +#ifndef _WIN32
> +# ifdef CONFIG_TPM
> +DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
> +    "-tpmdev [<type>],id=str[,option][,option][,...]\n",
> +    QEMU_ARCH_ALL)
> +# endif
> +#endif
> +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..fcab023
> --- /dev/null
> +++ b/tpm.c
> @@ -0,0 +1,167 @@
> +/*
> + * 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.  See
> + * the COPYING file in the top-level directory.
>

v2 or later please.

> + * Based on net.c
> + */
> +#include "config.h"
> +
> +#include "tpm.h"
> +#include "monitor.h"
> +#include "qerror.h"
> +
> +static const TPMDriverOps *bes[] = {
> +    NULL,
> +};
> +
> +static QLIST_HEAD(, TPMBackend) tpm_backends =
> +    QLIST_HEAD_INITIALIZER(tpm_backends);
> +
> +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];
> +}
> +
> +void tpm_display_backend_drivers(void)
> +{
> +    int i;
> +
> +    fprintf(stderr, "Supported TPM types (choose only one):\n");

Having fprintfs to stderr is a sign something is wrong.

In this case, we should have a programatic way to query the backends (like via 
qmp_query_tpm) and then in the option handling code, we should use that function 
and print to the screen from there.

> +    for (i = 0; bes[i] != NULL; i++) {
> +        fprintf(stderr, "%12s   %s\n", bes[i]->id, bes[i]->desc());
> +    }
> +    fprintf(stderr, "\n");
> +}
> +
> +TPMBackend *qemu_find_tpm(const char *id)
> +{
> +    TPMBackend *drv;
> +
> +    QLIST_FOREACH(drv,&tpm_backends, list) {
> +        if (!strcmp(drv->id, id)) {
> +            return drv;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +void do_info_tpm(Monitor *mon)
> +{
> +    TPMBackend *drv;
> +    unsigned int c = 0;
> +
> +    monitor_printf(mon, "TPM device:\n");
> +
> +    QLIST_FOREACH(drv,&tpm_backends, list) {
> +        monitor_printf(mon, " tpm%d: model=%s\n",
> +                       c, drv->fe_model);
> +        monitor_printf(mon, "  \\ %s: type=%s%s%s\n",
> +                       drv->id, drv->ops->id,
> +                       drv->parameters ? "," : "",
> +                       drv->parameters ? drv->parameters : "");
> +        c++;
> +    }
> +}

We should do this through sure QAPI now that it's in the the tree with a proper 
schema entry and an implementation in hmp.c.

> +
> +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);
> +}
> +
> +void tpm_cleanup(void)
> +{
> +    TPMBackend *drv, *next;
> +
> +    QLIST_FOREACH_SAFE(drv,&tpm_backends, list, next) {
> +        QLIST_REMOVE(drv, list);
> +        drv->ops->destroy(drv);
> +    }
> +}
> +
> +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;
> +}
> +
> +void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
> +{
> +    QemuOpts *opts;
> +
> +    if (strcmp("none", optarg) != 0) {
> +        if (*optarg == '?') {
> +            tpm_display_backend_drivers();
> +            exit(0);

Don't exit from something other than vl.c.  Return an error code and let vl.c exit.

> +        }
> +        opts = qemu_opts_parse(opts_list, optarg, 1);
> +        if (!opts) {
> +            exit(1);
> +        }
> +    }
> +}
> diff --git a/tpm.h b/tpm.h
> new file mode 100644
> index 0000000..85c2a35
> --- /dev/null
> +++ b/tpm.h
> @@ -0,0 +1,90 @@

Needs a copyright.

> +#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);
> +};
> +
> +static inline void tpm_dump_buffer(FILE *stream,
> +                                   unsigned char *buffer, unsigned int len)
> +{
> +    int i;
> +
> +    for (i = 0; i<  len; i++) {
> +        if (i&&  !(i % 16)) {
> +            fprintf(stream, "\n");
> +        }
> +        fprintf(stream, "%.2X ", buffer[i]);
> +    }
> +    fprintf(stream, "\n");
> +}

This definitely shouldn't be static inline and it's questionable whether it 
should exist in the first place.

> +#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
> +
> +void 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 do_info_tpm(Monitor *mon);
> +void tpm_display_backend_drivers(void);
> +const TPMDriverOps *tpm_get_backend_driver(const char *id);

Please document these functions.

> +#endif /* _QEMU_TPM_H */
> diff --git a/vl.c b/vl.c
> index 5372a96..12915d0 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,11 @@ int main(int argc, char **argv, char **envp)
>                   ram_size = value;
>                   break;
>               }
> +#ifdef CONFIG_TPM
> +            case QEMU_OPTION_tpmdev:
> +                tpm_config_parse(qemu_find_opts("tpmdev"), optarg);
> +                break;
> +#endif

Don't make options conditional.

Regards,

Anthony Liguori

>               case QEMU_OPTION_mempath:
>                   mem_path = optarg;
>                   break;
> @@ -3243,6 +3249,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 +3499,9 @@ int main(int argc, char **argv, char **envp)
>       pause_all_vcpus();
>       net_cleanup();
>       res_free();
> +#ifdef CONFIG_TPM
> +    tpm_cleanup();
> +#endif
>
>       return 0;
>   }

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

* Re: [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2011-12-12 23:23   ` Anthony Liguori
  2011-12-12 23:54     ` Stefan Berger
  0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:23 UTC (permalink / raw)
  To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl

On 12/12/2011 01:12 PM, 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>
>
> ---
>
> v13:
>    - don't call destroy callback in error path in tpm_tis_init
>
> v11:
>    - initialize val with 0xffffffff as default value to return from MMIO
>      read to an undefined location
>    - display size of read or write operations in debugging output
>
> v10:
>    - Use error_report where necessary
>    - Convert to use memory_region_init_io; use DEVICE_NATIVE_ENDIAN which
>      works for an x86 VM on x86 or ppc host
>    - Implement cleanup in tpm_tis_init
>    - put frontend model name into TPMBackend driver structure for later display
>      with the monitor
>
> v9:
>    - prefixing all function with tpm_tis_ and all constants with TPM_TIS_
>    - adding minimum VMStateDescription and marking device as non-migratable
>
> v5:
>    - adding comment to tis_data_read
>    - refactoring following support for split command line options
>      -tpmdev and -device
>    - code handling the configuration of the TPM device was moved to tpm.c
>    - removed empty line at end of file
>
> v3:
>    - prefixing functions with tis_
>    - added a function to the backend interface 'early_startup_tpm' that
>      allows to detect the presence of the block storage and gracefully fails
>      Qemu if it's not available. This works with migration using shared
>      storage but doesn't support migration with block storage migration.
>      For encyrypted QCoW2 and in case of a snapshot resue the late_startup_tpm
>      interface function is called
> ---
>   hw/tpm_tis.c |  818 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   1 files changed, 818 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..34d0043
> --- /dev/null
> +++ b/hw/tpm_tis.c
> @@ -0,0 +1,818 @@
> +/*
> + * 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<stdio.h>


#include "qemu-common.h"

> +
> +/*#define DEBUG_TIS */
> +
> +/* 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
> +
> +#ifdef DEBUG_TIS
> +static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
> +{
> +    uint16_t len;
> +
> +    len = tpm_tis_get_size_from_buffer(sb);
> +    fprintf(stderr, "tpm_tis: %s length = %d\n", string, len);
> +    tpm_dump_buffer(stderr, sb->buffer, len);
> +}
> +#endif

In the very least, move the guards into the function so that there aren't so 
many #ifdefs in the code.

> +
> +static inline uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
> +{
> +    return (uint8_t)((addr>>  12)&  0x7);
> +}

The compiler ignores inline for the most part.  It's just placating you by 
letting you think that you're asking it to do something :-)

> +
> +/*
> + * 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;
> +#ifdef DEBUG_TIS
> +    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
> +#endif
> +    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].state = TPM_TIS_STATE_EXECUTION;
> +    tis->loc[locty].sts&= ~TPM_TIS_STS_EXPECT;
> +
> +    s->to_tpm_execute = true;
> +    qemu_cond_signal(&s->to_tpm_cond);

The locking seems to presume that the device model is re-entrant which it's not 
today.  Am I missing something here?

> +}
> +
> +/* 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)) {
> +#ifdef DEBUG_TIS
> +        fprintf(stderr, "tpm_tis: Raising IRQ for flag %08x\n", irqmask);
> +#endif

I'd much prefer a dprintf.

> +        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;
> +#ifdef DEBUG_TIS
> +    fprintf(stderr, "tpm_tis: Active locality is now %d\n",
> +            s->s.tis.active_locty);
> +#endif
> +
> +    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;
> +
> +#ifdef DEBUG_TIS
> +    fprintf(stderr, "tpm_tis: tis_abort: new active locality is %d\n",
> +            tis->next_locty);
> +#endif
> +
> +    /*
> +     * 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].state = TPM_TIS_STATE_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].state == TPM_TIS_STATE_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; */
> +#ifdef DEBUG_TIS
> +            fprintf(stderr, "tpm_tis: Locality %d is busy - "
> +                            "deferring abort\n", busy_locty);
> +#endif
> +            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].state = TPM_TIS_STATE_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
> +        }
> +#ifdef DEBUG_TIS
> +        fprintf(stderr, "tpm_tis: tpm_tis_data_read byte 0x%02x   [%d]\n",
> +                ret, tis->loc[locty].r_offset-1);
> +#endif
> +    }
> +
> +    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].state) {
> +            case TPM_TIS_STATE_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;
> +    }
> +
> +#ifdef DEBUG_TIS
> +    fprintf(stderr, "tpm_tis:  read.%u(%08x) = %08x\n",
> +            size, (int)addr, (uint32_t)val);
> +#endif
> +
> +    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;
> +
> +#ifdef DEBUG_TIS
> +    fprintf(stderr, "tpm_tis: write.%u(%08x) = %08x\n",
> +            size, (int)addr, (uint32_t)val);
> +#endif
> +
> +    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) {
> +#ifdef DEBUG_TIS
> +                fprintf(stderr, "tpm_tis: Releasing locality %d\n", locty);
> +#endif
> +                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)) {
> +#ifdef DEBUG_TIS
> +                        fprintf(stderr, "tpm_tis: Locality %d requests use.\n",
> +                                c);
> +#endif
> +                        newlocty = c;
> +                        break;
> +                    }
> +                }
> +#ifdef DEBUG_TIS
> +                fprintf(stderr, "tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
> +                                "Next active locality: %d\n",
> +                                newlocty);
> +#endif
> +                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;
> +#ifdef DEBUG_TIS
> +                fprintf(stderr, "tpm_tis: TPM_TIS_ACCESS_SEIZE: "
> +                                "Locality %d seized from locality %d\n",
> +                                locty, tis->active_locty);
> +                fprintf(stderr,
> +                        "tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
> +#endif
> +                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);
> +#ifdef DEBUG_TIS
> +                fprintf(stderr, "tpm_tis: Lowering IRQ\n");
> +#endif
> +            }
> +        }
> +        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].state) {
> +
> +            case TPM_TIS_STATE_READY:
> +                tis->loc[locty].w_offset = 0;
> +                tis->loc[locty].r_offset = 0;
> +            break;
> +
> +            case TPM_TIS_STATE_IDLE:
> +                tis->loc[locty].sts   = TPM_TIS_STS_COMMAND_READY;
> +                tis->loc[locty].state = TPM_TIS_STATE_READY;
> +                tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
> +            break;
> +
> +            case TPM_TIS_STATE_EXECUTION:
> +            case TPM_TIS_STATE_RECEPTION:
> +                /* abort currently running command */
> +#ifdef DEBUG_TIS
> +                fprintf(stderr, "tpm_tis: %s: Initiating abort.\n",
> +                        __func__);
> +#endif
> +                tpm_tis_prep_abort(s, locty, locty);
> +            break;
> +
> +            case TPM_TIS_STATE_COMPLETION:
> +                tis->loc[locty].w_offset = 0;
> +                tis->loc[locty].r_offset = 0;
> +                /* shortcut to ready state with C/R set */
> +                tis->loc[locty].state = TPM_TIS_STATE_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].state) {
> +            case TPM_TIS_STATE_RECEPTION:
> +                tpm_tis_tpm_send(s, locty);
> +                break;
> +            default:
> +                /* ignore */
> +                break;
> +            }
> +        } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
> +            switch (tis->loc[locty].state) {
> +            case TPM_TIS_STATE_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].state == TPM_TIS_STATE_IDLE ||
> +            tis->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
> +            tis->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
> +            /* drop the byte */
> +        } else {
> +#ifdef DEBUG_TIS
> +            fprintf(stderr, "tpm_tis: Byte to send to TPM: %02x\n",
> +                    (uint8_t)val);
> +#endif
> +            if (tis->loc[locty].state == TPM_TIS_STATE_READY) {
> +                tis->loc[locty].state = TPM_TIS_STATE_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].state = TPM_TIS_STATE_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)

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

* Re: [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code Stefan Berger
@ 2011-12-12 23:24   ` Anthony Liguori
  2011-12-12 23:56     ` Stefan Berger
  0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:24 UTC (permalink / raw)
  To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl

On 12/12/2011 01:12 PM, Stefan Berger wrote:
> Build the TPM frontend code that has been added so far.
>
> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
> ---
>   Makefile.target |    1 +
>   configure       |   11 +++++++++++
>   2 files changed, 12 insertions(+), 0 deletions(-)
>
> diff --git a/Makefile.target b/Makefile.target
> index 39b2e5a..37d5d10 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -205,6 +205,7 @@ 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-$(CONFIG_TPM) += tpm.o 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"

Please probe instead of requiring it to be explicitly enabled.

Regards,

Anthony Liguori

>
>   # 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

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

* Re: [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2011-12-12 23:27   ` Anthony Liguori
  2011-12-12 23:59     ` Stefan Berger
  0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:27 UTC (permalink / raw)
  To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl

On 12/12/2011 01:12 PM, 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>
>
> ---
>
> Changes for v12:
>   - check size indicator in response from TPM to match that of the received
>     packet
>
> Changes for v10:
>   - clarified documentation
>   - using /dev/tpm0 as default device if path option is not given
>   - refactored code handling 'device' option into its own function
>   - fixed name of structure to TPMPassthruThreadParams
>   - only add tpm_passthrough_driver to collection of backends if
>     it is compiled on the host
>
> Changes for v9:
>   - prefixing of all functions and variables with tpm_passthrough_
>   - cleanup of all variables into a structure that is now accessed
>     using TPMBackend (tb->s.tpm_pt)
>   - build it on Linux machines
>   - added function to test whether given device is a TPM and refuse
>     startup if it is not
> ---
>   Makefile.target      |    1 +
>   configure            |    3 +
>   hw/tpm_passthrough.c |  477 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   qemu-options.hx      |   33 ++++
>   tpm.c                |   23 +++
>   tpm.h                |   34 ++++
>   6 files changed, 571 insertions(+), 0 deletions(-)
>   create mode 100644 hw/tpm_passthrough.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 37d5d10..4fc96e6 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -206,6 +206,7 @@ obj-$(CONFIG_KVM) += kvm.o kvm-all.o
>   obj-$(CONFIG_NO_KVM) += kvm-stub.o
>   obj-y += memory.o
>   obj-$(CONFIG_TPM) += tpm.o 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..f9cfe3d
> --- /dev/null
> +++ b/hw/tpm_passthrough.c
> @@ -0,0 +1,477 @@
> +/*
> + *  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 */
> +
> +/* 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 void *tpm_passthrough_main_loop(void *d)
> +{
> +    TPMPassthruThreadParams *thr_parms = d;
> +    TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
> +    uint32_t in_len, out_len;
> +    uint8_t *in, *out;
> +    uint8_t locty;
> +    TPMLocality *cmd_locty;
> +    int ret;

This is rather scary.  I'd rather see us make use of a GThreadPool in order to 
submit read/write requests asynchronously to the /dev/tpm device.   I don't 
think the code should be structured expecting synchronous command execution.

Regards,

Anthony Liguori

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

* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
@ 2011-12-12 23:27   ` Anthony Liguori
  2011-12-13  0:12     ` Stefan Berger
  0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:27 UTC (permalink / raw)
  To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl

On 12/12/2011 01:12 PM, Stefan Berger wrote:
> 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"

Same as before, please probe for existence.

Regards,

Anthony Liguori

>
>   # 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

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

* Re: [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
@ 2011-12-12 23:30   ` Anthony Liguori
  2011-12-13  0:17     ` Stefan Berger
  0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:30 UTC (permalink / raw)
  To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl

On 12/12/2011 01:12 PM, Stefan Berger wrote:
> 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>
>
> ---
>
> v13:
>   - Only accepting a character device's file descriptor
>
> v12:
>   - added documentation part
> ---
>   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 f9cfe3d..57bb77a 100644
> --- a/hw/tpm_passthrough.c
> +++ b/hw/tpm_passthrough.c
> @@ -361,33 +361,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;
>       }

I think you're being overzealous here.  The backend only uses read/write to 
interact with the passthrough device.  You could use this as a mechanism to tie 
in an emulated VTPM by using a socket.  I'm not suggesting we do that for 
libvtpm, but I think we don't gain anything from being overly restrictive here.

I don't think a user passing the wrong type of fd is the common case to optimize 
for wrt usability.

Regards,

Anthony Liguori

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

* Re: [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-12-12 23:23   ` Anthony Liguori
@ 2011-12-12 23:54     ` Stefan Berger
  0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 23:54 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: mst, qemu-devel, andreas.niederl

On 12/12/2011 06:23 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, 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>
[...]

>> +
>> +/*
>> + * 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;
>> +#ifdef DEBUG_TIS
>> +    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
>> +#endif
>> +    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].state = TPM_TIS_STATE_EXECUTION;
>> +    tis->loc[locty].sts&= ~TPM_TIS_STS_EXPECT;
>> +
>> +    s->to_tpm_execute = true;
>> +    qemu_cond_signal(&s->to_tpm_cond);
>
> The locking seems to presume that the device model is re-entrant which 
> it's not today.  Am I missing something here?
>

The TPM TIS frontend communicates with the TPM backend via a condition 
notifying it when a complete buffer with a TPM request has been 
received. The TPM backend is running as a thread, created via 
qemu_thread_create(). This is the design that was driven by the 
libtpms-based implementation.

    Stefan

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

* Re: [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code
  2011-12-12 23:24   ` Anthony Liguori
@ 2011-12-12 23:56     ` Stefan Berger
  0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 23:56 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, mst

On 12/12/2011 06:24 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, Stefan Berger wrote:
>> Build the TPM frontend code that has been added so far.
>>
>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>> ---
>>   Makefile.target |    1 +
>>   configure       |   11 +++++++++++
>>   2 files changed, 12 insertions(+), 0 deletions(-)
>>
>> diff --git a/Makefile.target b/Makefile.target
>> index 39b2e5a..37d5d10 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -205,6 +205,7 @@ 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-$(CONFIG_TPM) += tpm.o 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"
>
> Please probe instead of requiring it to be explicitly enabled.

At this point there is not much to probe. We're only building the 
front-end with no backend, i.e., passthrough, being built in.

    Stefan

>
> Regards,
>
> Anthony Liguori
>
>>
>>   # 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
>

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

* Re: [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation
  2011-12-12 23:27   ` Anthony Liguori
@ 2011-12-12 23:59     ` Stefan Berger
  0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 23:59 UTC (permalink / raw)
  To: qemu-devel

On 12/12/2011 06:27 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, 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].
[...]
>>
>> +static void *tpm_passthrough_main_loop(void *d)
>> +{
>> +    TPMPassthruThreadParams *thr_parms = d;
>> +    TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
>> +    uint32_t in_len, out_len;
>> +    uint8_t *in, *out;
>> +    uint8_t locty;
>> +    TPMLocality *cmd_locty;
>> +    int ret;
>
> This is rather scary.  I'd rather see us make use of a GThreadPool in 
> order to submit read/write requests asynchronously to the /dev/tpm 
> device.   I don't think the code should be structured expecting 
> synchronous command execution.

This part here is running as a thread, create via qemu_thread_create(). 
Relative to the main thread this is of course running asynchronously. 
The same design will re-appear when the libtpms based TPM backend 
appears. Here we will need a thread for concurrent execution of more 
time consuming crypto functions.

Regards,
    Stefan

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

* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-12 23:27   ` Anthony Liguori
@ 2011-12-13  0:12     ` Stefan Berger
  2011-12-13  4:51       ` Paul Brook
  0 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-13  0:12 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: mst, qemu-devel, andreas.niederl

On 12/12/2011 06:27 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, Stefan Berger wrote:
>> 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"
>
> Same as before, please probe for existence.


We would be probing for /dev/tpm0. Is that really what we want that this 
driver only gets compiled if /dev/tpm0 is (currently) available?

  Regards,
      Stefan

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

* Re: [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver
  2011-12-12 23:30   ` Anthony Liguori
@ 2011-12-13  0:17     ` Stefan Berger
  0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-13  0:17 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: mst, qemu-devel, andreas.niederl

On 12/12/2011 06:30 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, Stefan Berger wrote:
>> 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>
>>
[...]
>> -    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;
>>       }
>
> I think you're being overzealous here.  The backend only uses 
> read/write to interact with the passthrough device.  You could use 
> this as a mechanism to tie in an emulated VTPM by using a socket.  I'm 
> not suggesting we do that for libvtpm, but I think we don't gain 
> anything from being overly restrictive here.

We prevent files, pipes, sockets and block devices using this check. 
Sockets may make sense in the future, but would like to enable that 
separately.

>
> I don't think a user passing the wrong type of fd is the common case 
> to optimize for wrt usability.

I don't think it makes sense to have the TPM passthrough driver write() 
into a block device or file, so therefore I prevented that. The above 
check is just a single line...

    Stefan

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

* Re: [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options
  2011-12-12 23:16   ` Anthony Liguori
@ 2011-12-13  2:16     ` Stefan Berger
  0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-13  2:16 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: andreas.niederl, qemu-devel, mst

On 12/12/2011 06:16 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, Stefan Berger wrote:
>> @@ -2735,6 +2736,15 @@ static mon_cmd_t info_cmds[] = {
>>           .help       = "show available trace-events&  their state",
>>           .mhandler.info = do_trace_print_events,
>>       },
>> +#if defined(CONFIG_TPM)
>> +    {
>> +        .name       = "tpm",
>> +        .args_type  = "",
>> +        .params     = "",
>> +        .help       = "show the TPM device",
>> +        .mhandler.info = do_info_tpm,
>> +    },
>> +#endif
>
> Please don't make monitor commands conditional.  Make it fail in a 
> predictable fashion if tpm isn't configured.
>

This will then require tpm.c to always be compiled. You'll find the 
CONFIG_TPM there then.
@@ -563,6 +582,7 @@ static QemuOptsList *vm_config_groups[32] = {
>> &qemu_option_rom_opts,
>> &qemu_machine_opts,
>> &qemu_boot_opts,
>> +&qemu_tpmdev_opts,
>
> I assume this is my mailer or is the whitespace munged here?
>

Must be your mailer.@@ -0,0 +1,167 @@
>> +/*
>> + * 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.  
>> See
>> + * the COPYING file in the top-level directory.
>>
>
> v2 or later please.

ok.

>> +
>> +void tpm_display_backend_drivers(void)
>> +{
>> +    int i;
>> +
>> +    fprintf(stderr, "Supported TPM types (choose only one):\n");
>
> Having fprintfs to stderr is a sign something is wrong.
>
> In this case, we should have a programatic way to query the backends 
> (like via qmp_query_tpm) and then in the option handling code, we 
> should use that function and print to the screen from there.
>

Will try to convert but keep this function ?

>> +
>> +void do_info_tpm(Monitor *mon)
>> +{
>> +    TPMBackend *drv;
>> +    unsigned int c = 0;
>> +
>> +    monitor_printf(mon, "TPM device:\n");
>> +
>> +    QLIST_FOREACH(drv,&tpm_backends, list) {
>> +        monitor_printf(mon, " tpm%d: model=%s\n",
>> +                       c, drv->fe_model);
>> +        monitor_printf(mon, "  \\ %s: type=%s%s%s\n",
>> +                       drv->id, drv->ops->id,
>> +                       drv->parameters ? "," : "",
>> +                       drv->parameters ? drv->parameters : "");
>> +        c++;
>> +    }
>> +}
>
> We should do this through sure QAPI now that it's in the the tree with 
> a proper schema entry and an implementation in hmp.c.

True...

>> +void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
>> +{
>> +    QemuOpts *opts;
>> +
>> +    if (strcmp("none", optarg) != 0) {
>> +        if (*optarg == '?') {
>> +            tpm_display_backend_drivers();
>> +            exit(0);
>
> Don't exit from something other than vl.c.  Return an error code and 
> let vl.c exit.

I implemented this following along the lines of

qemu-system-x86_64 -soundhw ?

which then also shows and error code 0. What error code should it return 
to the shell ?


>
>> +        }
>> +        opts = qemu_opts_parse(opts_list, optarg, 1);
>> +        if (!opts) {
>> +            exit(1);
>> +        }
>> +    }
>> +}
>> diff --git a/tpm.h b/tpm.h
>> new file mode 100644
>> index 0000000..85c2a35
>> --- /dev/null
>> +++ b/tpm.h
>> @@ -0,0 +1,90 @@
>
> Needs a copyright.

Ok.

>> +static inline void tpm_dump_buffer(FILE *stream,
>> +                                   unsigned char *buffer, unsigned 
>> int len)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i<  len; i++) {
>> +        if (i&&  !(i % 16)) {
>> +            fprintf(stream, "\n");
>> +        }
>> +        fprintf(stream, "%.2X ", buffer[i]);
>> +    }
>> +    fprintf(stream, "\n");
>> +}
>
> This definitely shouldn't be static inline and it's questionable 
> whether it should exist in the first place.

Do you have an alternative for this function? Assuming it's useful for 
debugging, should I just move it into tpm.c ?


>
>> +#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
>> +
>> +void 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 do_info_tpm(Monitor *mon);
>> +void tpm_display_backend_drivers(void);
>> +const TPMDriverOps *tpm_get_backend_driver(const char *id);
>
> Please document these functions.

Will document them in tpm.c where their implementation is.


@@ -2550,6 +2551,11 @@ int main(int argc, char **argv, char **envp)
>>                   ram_size = value;
>>                   break;
>>               }
>> +#ifdef CONFIG_TPM
>> +            case QEMU_OPTION_tpmdev:
>> +                tpm_config_parse(qemu_find_opts("tpmdev"), optarg);
>> +                break;
>> +#endif
>
> Don't make options conditional.

Can I have an #ifdef-#else-#endif construct there along the lines of 
CONFIG_SDL with an exit(1) in the #else branch?


    Stefan

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

* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-13  0:12     ` Stefan Berger
@ 2011-12-13  4:51       ` Paul Brook
  2011-12-13 12:51         ` Stefan Berger
  0 siblings, 1 reply; 29+ messages in thread
From: Paul Brook @ 2011-12-13  4:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: mst, andreas.niederl, Stefan Berger

> >> +tpm_passthrough="no"
> > 
> > Same as before, please probe for existence.
> 
> We would be probing for /dev/tpm0. Is that really what we want that this
> driver only gets compiled if /dev/tpm0 is (currently) available?

If what you say is true then this code should always be enabled.

Paul

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

* Re: [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration
  2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (6 preceding siblings ...)
  2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
@ 2011-12-13  5:45 ` Stefan Weil
  2011-12-13 12:43   ` Stefan Berger
  7 siblings, 1 reply; 29+ messages in thread
From: Stefan Weil @ 2011-12-13  5:45 UTC (permalink / raw)
  To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl

Am 12.12.2011 20:12, schrieb Stefan Berger:
> 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.
>

Hi Stefan,

please use "GPL V2 or later" (not GPL V2 only) for new files.
"Qemu" should be replaced by "QEMU".

Regards,
Stefan Weil

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

* Re: [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration
  2011-12-13  5:45 ` [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
@ 2011-12-13 12:43   ` Stefan Berger
  0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-13 12:43 UTC (permalink / raw)
  To: Stefan Weil; +Cc: andreas.niederl, qemu-devel, mst

On 12/13/2011 12:45 AM, Stefan Weil wrote:
> Am 12.12.2011 20:12, schrieb Stefan Berger:
>> 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.
>>
>
> Hi Stefan,
>
> please use "GPL V2 or later" (not GPL V2 only) for new files.
> "Qemu" should be replaced by "QEMU".

Not a problem. I fixed this last night.


    Stefan

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

* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-13  4:51       ` Paul Brook
@ 2011-12-13 12:51         ` Stefan Berger
  2011-12-13 13:51           ` Michael S. Tsirkin
  2011-12-13 17:25           ` Paul Brook
  0 siblings, 2 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-13 12:51 UTC (permalink / raw)
  To: Paul Brook; +Cc: andreas.niederl, qemu-devel, mst

On 12/12/2011 11:51 PM, Paul Brook wrote:
>>>> +tpm_passthrough="no"
>>> Same as before, please probe for existence.
>> We would be probing for /dev/tpm0. Is that really what we want that this
>> driver only gets compiled if /dev/tpm0 is (currently) available?
> If what you say is true then this code should always be enabled.
>
Michael Tsirkin previously requested that there be an option for the TPM 
passthrough driver to be selectively enabled since at least using 
/dev/tpm0 may not be what everybody wants. The passthrough driver at 
some point will also be able to use sockets to communicate with a TPM 
when a file descriptor is passed to Qemu, so maybe that changes then?


    Stefan

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

* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-13 12:51         ` Stefan Berger
@ 2011-12-13 13:51           ` Michael S. Tsirkin
  2011-12-13 17:41             ` Anthony Liguori
  2011-12-13 17:25           ` Paul Brook
  1 sibling, 1 reply; 29+ messages in thread
From: Michael S. Tsirkin @ 2011-12-13 13:51 UTC (permalink / raw)
  To: Stefan Berger; +Cc: andreas.niederl, Paul Brook, qemu-devel

On Tue, Dec 13, 2011 at 07:51:17AM -0500, Stefan Berger wrote:
> On 12/12/2011 11:51 PM, Paul Brook wrote:
> >>>>+tpm_passthrough="no"
> >>>Same as before, please probe for existence.
> >>We would be probing for /dev/tpm0. Is that really what we want that this
> >>driver only gets compiled if /dev/tpm0 is (currently) available?
> >If what you say is true then this code should always be enabled.
> >
> Michael Tsirkin previously requested that there be an option for the
> TPM passthrough driver to be selectively enabled since at least
> using /dev/tpm0 may not be what everybody wants. The passthrough
> driver at some point will also be able to use sockets to communicate
> with a TPM when a file descriptor is passed to Qemu, so maybe that
> changes then?
> 
> 
>    Stefan

The passthrough as it is, is pretty easy to misuse.
This is a hardware problem, not software, and
I don't think it's fixable.

So I do not think all downstreams will want to support this
mode, making it easy to disable this is IMO important.

-- 
MST

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

* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-13 12:51         ` Stefan Berger
  2011-12-13 13:51           ` Michael S. Tsirkin
@ 2011-12-13 17:25           ` Paul Brook
  1 sibling, 0 replies; 29+ messages in thread
From: Paul Brook @ 2011-12-13 17:25 UTC (permalink / raw)
  To: Stefan Berger; +Cc: andreas.niederl, qemu-devel, mst

> On 12/12/2011 11:51 PM, Paul Brook wrote:
> >>>> +tpm_passthrough="no"
> >>> 
> >>> Same as before, please probe for existence.
> >> 
> >> We would be probing for /dev/tpm0. Is that really what we want that this
> >> driver only gets compiled if /dev/tpm0 is (currently) available?
> > 
> > If what you say is true then this code should always be enabled.
> 
> Michael Tsirkin previously requested that there be an option for the TPM
> passthrough driver to be selectively enabled since at least using
> /dev/tpm0 may not be what everybody wants. The passthrough driver at
> some point will also be able to use sockets to communicate with a TPM
> when a file descriptor is passed to Qemu, so maybe that changes then?

Surely that's a runtime decision made by the qemu user, not a compile time 
decision made by the distribution vendor.  Testing functionality of the build 
machine is fundamentally wrong.  At best it completely breaks cross compiling, 
at worst it subtly breaks building on a dev machine and running on a 
production server.

Configure time probing only makes sense for code that will fail to build if 
external compile time dependencies are not present.

If you really think building qemu without this functionality is useful, then 
maybe add a configure option to disable it.  With the possible exception of 
developer debugging aids, code that is not enabled by default is clearly not 
worth having and should be removed altogether.

Paul

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

* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-13 13:51           ` Michael S. Tsirkin
@ 2011-12-13 17:41             ` Anthony Liguori
  2011-12-13 17:48               ` Stefan Berger
  0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-13 17:41 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, Paul Brook, andreas.niederl, Stefan Berger

On 12/13/2011 07:51 AM, Michael S. Tsirkin wrote:
> On Tue, Dec 13, 2011 at 07:51:17AM -0500, Stefan Berger wrote:
>> On 12/12/2011 11:51 PM, Paul Brook wrote:
>>>>>> +tpm_passthrough="no"
>>>>> Same as before, please probe for existence.
>>>> We would be probing for /dev/tpm0. Is that really what we want that this
>>>> driver only gets compiled if /dev/tpm0 is (currently) available?
>>> If what you say is true then this code should always be enabled.
>>>
>> Michael Tsirkin previously requested that there be an option for the
>> TPM passthrough driver to be selectively enabled since at least
>> using /dev/tpm0 may not be what everybody wants. The passthrough
>> driver at some point will also be able to use sockets to communicate
>> with a TPM when a file descriptor is passed to Qemu, so maybe that
>> changes then?
>>
>>
>>     Stefan
>
> The passthrough as it is, is pretty easy to misuse.
> This is a hardware problem, not software, and
> I don't think it's fixable.

Can you elaborate?  And can this be documented such that users are aware of this.

Regards,

Anthony Liguori

>
> So I do not think all downstreams will want to support this
> mode, making it easy to disable this is IMO important.
>

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

* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-13 17:41             ` Anthony Liguori
@ 2011-12-13 17:48               ` Stefan Berger
  2011-12-13 20:33                 ` Paul Brook
  0 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-13 17:48 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Paul Brook, andreas.niederl, qemu-devel, Michael S. Tsirkin

On 12/13/2011 12:41 PM, Anthony Liguori wrote:
> On 12/13/2011 07:51 AM, Michael S. Tsirkin wrote:
>> On Tue, Dec 13, 2011 at 07:51:17AM -0500, Stefan Berger wrote:
>>> On 12/12/2011 11:51 PM, Paul Brook wrote:
>>>>>>> +tpm_passthrough="no"
>>>>>> Same as before, please probe for existence.
>>>>> We would be probing for /dev/tpm0. Is that really what we want 
>>>>> that this
>>>>> driver only gets compiled if /dev/tpm0 is (currently) available?
>>>> If what you say is true then this code should always be enabled.
>>>>
>>> Michael Tsirkin previously requested that there be an option for the
>>> TPM passthrough driver to be selectively enabled since at least
>>> using /dev/tpm0 may not be what everybody wants. The passthrough
>>> driver at some point will also be able to use sockets to communicate
>>> with a TPM when a file descriptor is passed to Qemu, so maybe that
>>> changes then?
>>>
>>>
>>>     Stefan
>>
>> The passthrough as it is, is pretty easy to misuse.
>> This is a hardware problem, not software, and
>> I don't think it's fixable.
>
> Can you elaborate?  And can this be documented such that users are 
> aware of this.
>

 From qemu-doc.html:

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.
> Regards,
>
> Anthony Liguori
>

Stefan

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

* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
  2011-12-13 17:48               ` Stefan Berger
@ 2011-12-13 20:33                 ` Paul Brook
  0 siblings, 0 replies; 29+ messages in thread
From: Paul Brook @ 2011-12-13 20:33 UTC (permalink / raw)
  To: Stefan Berger; +Cc: andreas.niederl, qemu-devel, Michael S. Tsirkin

> 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.

Presumably the same is true of any other application that has access to 
/dev/tpm0?

This doesn't sound any different to any other kind of device passthrough (e.g. 
USB).  If the host is also talking to the device then it's likely to get 
confused.  If the guest tells the device to do something suicidal then it'll 
most likely die.

Complicated of cource by the fact that the whole point of the TPM is you don't 
really trust yourself, so the usual failure mode is a complete nervous 
breakdown.  But that comes with the territory.

Paul

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

end of thread, other threads:[~2011-12-13 20:33 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options Stefan Berger
2011-12-12 23:16   ` Anthony Liguori
2011-12-13  2:16     ` Stefan Berger
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
2011-12-12 23:23   ` Anthony Liguori
2011-12-12 23:54     ` Stefan Berger
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 3/7] Add a debug register Stefan Berger
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code Stefan Berger
2011-12-12 23:24   ` Anthony Liguori
2011-12-12 23:56     ` Stefan Berger
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
2011-12-12 23:27   ` Anthony Liguori
2011-12-12 23:59     ` Stefan Berger
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
2011-12-12 23:27   ` Anthony Liguori
2011-12-13  0:12     ` Stefan Berger
2011-12-13  4:51       ` Paul Brook
2011-12-13 12:51         ` Stefan Berger
2011-12-13 13:51           ` Michael S. Tsirkin
2011-12-13 17:41             ` Anthony Liguori
2011-12-13 17:48               ` Stefan Berger
2011-12-13 20:33                 ` Paul Brook
2011-12-13 17:25           ` Paul Brook
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
2011-12-12 23:30   ` Anthony Liguori
2011-12-13  0:17     ` Stefan Berger
2011-12-13  5:45 ` [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
2011-12-13 12:43   ` Stefan Berger

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