All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration
@ 2011-09-28 13:22 Stefan Berger
  2011-09-28 13:22 ` [Qemu-devel] [PATCH V11 1/5] Support for TPM command line options Stefan Berger
                   ` (8 more replies)
  0 siblings, 9 replies; 13+ messages in thread
From: Stefan Berger @ 2011-09-28 13:22 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: anbang.ruan, mst, andreas.niederl, serge

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.


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

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

* [Qemu-devel] [PATCH V11 1/5] Support for TPM command line options
  2011-09-28 13:22 [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Stefan Berger
@ 2011-09-28 13:22 ` Stefan Berger
  2011-09-28 13:22 ` [Qemu-devel] [PATCH V11 2/5] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Stefan Berger @ 2011-09-28 13:22 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: anbang.ruan, mst, andreas.niederl, serge

[-- Attachment #1: qemu_tpm.diff --]
[-- Type: text/plain, Size: 15270 bytes --]

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

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

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
 hmp-commands.hx |    2 
 hw/tpm_tis.h    |   89 +++++++++++++++++++++++++++++
 monitor.c       |   10 +++
 qemu-config.c   |   20 ++++++
 qemu-options.hx |   34 +++++++++++
 tpm.c           |  167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tpm.h           |   90 ++++++++++++++++++++++++++++++
 vl.c            |   15 +++++
 8 files changed, 427 insertions(+)

Index: qemu-git.pt/qemu-options.hx
===================================================================
--- qemu-git.pt.orig/qemu-options.hx
+++ qemu-git.pt/qemu-options.hx
@@ -1760,6 +1760,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
 
Index: qemu-git.pt/vl.c
===================================================================
--- qemu-git.pt.orig/vl.c
+++ qemu-git.pt/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"
@@ -2675,6 +2676,11 @@ int main(int argc, char **argv, char **e
                 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;
@@ -3327,6 +3333,12 @@ int main(int argc, char **argv, char **e
         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);
@@ -3575,6 +3587,9 @@ int main(int argc, char **argv, char **e
     quit_timers();
     net_cleanup();
     res_free();
+#ifdef CONFIG_TPM
+    tpm_cleanup();
+#endif
 
     return 0;
 }
Index: qemu-git.pt/qemu-config.c
===================================================================
--- qemu-git.pt.orig/qemu-config.c
+++ qemu-git.pt/qemu-config.c
@@ -508,6 +508,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,
@@ -522,6 +541,7 @@ static QemuOptsList *vm_config_groups[32
     &qemu_option_rom_opts,
     &qemu_machine_opts,
     &qemu_boot_opts,
+    &qemu_tpmdev_opts,
     NULL,
 };
 
Index: qemu-git.pt/hw/tpm_tis.h
===================================================================
--- /dev/null
+++ qemu-git.pt/hw/tpm_tis.h
@@ -0,0 +1,89 @@
+/*
+ * 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;
+    bool     g_malloced;
+} 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 uint16_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+    return (sb->buffer[4] << 8) + sb->buffer[5];
+}
+
+#endif /* _HW_TPM_TIS_H */
Index: qemu-git.pt/tpm.c
===================================================================
--- /dev/null
+++ qemu-git.pt/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);
+        }
+    }
+}
Index: qemu-git.pt/tpm.h
===================================================================
--- /dev/null
+++ qemu-git.pt/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 */
Index: qemu-git.pt/monitor.c
===================================================================
--- qemu-git.pt.orig/monitor.c
+++ qemu-git.pt/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"
@@ -3163,6 +3164,15 @@ static const 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,
     },
Index: qemu-git.pt/hmp-commands.hx
===================================================================
--- qemu-git.pt.orig/hmp-commands.hx
+++ qemu-git.pt/hmp-commands.hx
@@ -1351,6 +1351,8 @@ show device tree
 show qdev device model list
 @item info roms
 show roms
+@item info tpm
+show the TPM device
 @end table
 ETEXI
 

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

* [Qemu-devel] [PATCH V11 2/5] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-09-28 13:22 [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Stefan Berger
  2011-09-28 13:22 ` [Qemu-devel] [PATCH V11 1/5] Support for TPM command line options Stefan Berger
@ 2011-09-28 13:22 ` Stefan Berger
  2011-09-28 13:22 ` [Qemu-devel] [PATCH V11 3/5] Add a debug register Stefan Berger
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Stefan Berger @ 2011-09-28 13:22 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: anbang.ruan, mst, andreas.niederl, serge

[-- Attachment #1: qemu_tpm_tis.diff --]
[-- Type: text/plain, Size: 30209 bytes --]

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.

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

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
 hw/tpm_tis.c |  820 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 820 insertions(+)

Index: qemu-git.pt/hw/tpm_tis.c
===================================================================
--- /dev/null
+++ qemu-git.pt/hw/tpm_tis.c
@@ -0,0 +1,820 @@
+/*
+ * 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);
+
+    s->be_driver->ops->destroy(s->be_driver);
+
+    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] 13+ messages in thread

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

[-- Attachment #1: qemu_tpm_tis_debugreg.diff --]
[-- Type: text/plain, Size: 3887 bytes --]

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

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

v3:
 - all output goes to stderr

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
 hw/tpm_tis.c |   73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)

Index: qemu-git.pt/hw/tpm_tis.c
===================================================================
--- qemu-git.pt.orig/hw/tpm_tis.c
+++ qemu-git.pt/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(TPMSta
     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 *
     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);

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

* [Qemu-devel] [PATCH V11 4/5] Build the TPM frontend code
  2011-09-28 13:22 [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (2 preceding siblings ...)
  2011-09-28 13:22 ` [Qemu-devel] [PATCH V11 3/5] Add a debug register Stefan Berger
@ 2011-09-28 13:22 ` Stefan Berger
  2011-09-28 13:23 ` [Qemu-devel] [PATCH V11 5/5] Add a TPM Passthrough backend driver implementation Stefan Berger
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Stefan Berger @ 2011-09-28 13:22 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: anbang.ruan, mst, andreas.niederl, serge

[-- Attachment #1: buildit.diff --]
[-- Type: text/plain, Size: 2223 bytes --]

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(+)

Index: qemu-git.pt/Makefile.target
===================================================================
--- qemu-git.pt.orig/Makefile.target
+++ qemu-git.pt/Makefile.target
@@ -194,6 +194,7 @@ obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virt
 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)
Index: qemu-git.pt/configure
===================================================================
--- qemu-git.pt.orig/configure
+++ qemu-git.pt/configure
@@ -182,6 +182,7 @@ usb_redir=""
 opengl=""
 zlib="yes"
 guest_agent="yes"
+tpm="no"
 
 # parse CC options first
 for opt do
@@ -766,6 +767,8 @@ for opt do
   ;;
   --disable-guest-agent) guest_agent="no"
   ;;
+  --enable-tpm) tpm="yes"
+  ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
@@ -1048,6 +1051,7 @@ echo "  --disable-usb-redir      disable
 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
@@ -2714,6 +2718,7 @@ echo "nss used          $smartcard_nss"
 echo "usb net redir     $usb_redir"
 echo "OpenGL support    $opengl"
 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"
@@ -3558,6 +3563,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
+
 linker_script="-Wl,-T../config-host.ld -Wl,-T,\$(SRC_PATH)/\$(ARCH).ld"
 if test "$target_linux_user" = "yes" -o "$target_bsd_user" = "yes" ; then
   case "$ARCH" in

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

* [Qemu-devel] [PATCH V11 5/5] Add a TPM Passthrough backend driver implementation
  2011-09-28 13:22 [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (3 preceding siblings ...)
  2011-09-28 13:22 ` [Qemu-devel] [PATCH V11 4/5] Build the TPM frontend code Stefan Berger
@ 2011-09-28 13:23 ` Stefan Berger
  2011-10-02 11:30   ` Michael S. Tsirkin
  2011-10-02 11:38 ` [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Michael S. Tsirkin
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 13+ messages in thread
From: Stefan Berger @ 2011-09-28 13:23 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: anbang.ruan, mst, andreas.niederl, serge

[-- Attachment #1: qemu_tpm_passthrough.diff --]
[-- Type: text/plain, Size: 20998 bytes --]

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

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

Regards,
Andreas Niederl, Stefan Berger

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

Signed-off-by: Andreas Niederl <andreas.niederl@iaik.tugraz.at>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
 Makefile.target      |    1 
 configure            |    3 
 hw/tpm_passthrough.c |  471 +++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-options.hx      |   33 +++
 tpm.c                |   23 ++
 tpm.h                |   34 +++
 6 files changed, 565 insertions(+)
 create mode 100644 hw/tpm_passthrough.c

Index: qemu-git.pt/Makefile.target
===================================================================
--- qemu-git.pt.orig/Makefile.target
+++ qemu-git.pt/Makefile.target
@@ -195,6 +195,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)
Index: qemu-git.pt/configure
===================================================================
--- qemu-git.pt.orig/configure
+++ qemu-git.pt/configure
@@ -3565,6 +3565,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
Index: qemu-git.pt/hw/tpm_passthrough.c
===================================================================
--- /dev/null
+++ qemu-git.pt/hw/tpm_passthrough.c
@@ -0,0 +1,471 @@
+/*
+ *  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);
+            }
+
+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,
+};
Index: qemu-git.pt/qemu-options.hx
===================================================================
--- qemu-git.pt.orig/qemu-options.hx
+++ qemu-git.pt/qemu-options.hx
@@ -1777,6 +1777,7 @@ The general form of a TPM device option 
 @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.
@@ -1788,6 +1789,38 @@ Use ? to print all available TPM backend
 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
Index: qemu-git.pt/tpm.c
===================================================================
--- qemu-git.pt.orig/tpm.c
+++ qemu-git.pt/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;
Index: qemu-git.pt/tpm.h
===================================================================
--- qemu-git.pt.orig/tpm.h
+++ qemu-git.pt/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 
 
 #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 */

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

* Re: [Qemu-devel] [PATCH V11 5/5] Add a TPM Passthrough backend driver implementation
  2011-09-28 13:23 ` [Qemu-devel] [PATCH V11 5/5] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2011-10-02 11:30   ` Michael S. Tsirkin
  0 siblings, 0 replies; 13+ messages in thread
From: Michael S. Tsirkin @ 2011-10-02 11:30 UTC (permalink / raw)
  To: Stefan Berger; +Cc: serge, qemu-devel, anbang.ruan, andreas.niederl

On Wed, Sep 28, 2011 at 09:23:00AM -0400, Stefan Berger wrote:

...

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

...

> Index: qemu-git.pt/configure
> ===================================================================
> --- qemu-git.pt.orig/configure
> +++ qemu-git.pt/configure
> @@ -3565,6 +3565,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

I think we might want to make this a configure option
separate from tpm. The number of ways this can fail
might make some vendors want to disable this mode.

>      echo "CONFIG_TPM=y" >> $config_host_mak
>    fi
>  fi

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

* Re: [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration
  2011-09-28 13:22 [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (4 preceding siblings ...)
  2011-09-28 13:23 ` [Qemu-devel] [PATCH V11 5/5] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2011-10-02 11:38 ` Michael S. Tsirkin
  2011-10-02 20:12   ` Stefan Berger
  2011-10-04 18:44 ` [Qemu-devel] [PATCH 6/5] Introduce --enable-tpm-passthrough configure option Stefan Berger
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 13+ messages in thread
From: Michael S. Tsirkin @ 2011-10-02 11:38 UTC (permalink / raw)
  To: Stefan Berger; +Cc: serge, qemu-devel, anbang.ruan, andreas.niederl

On Wed, Sep 28, 2011 at 09:22:55AM -0400, Stefan Berger wrote:
> The following series of patches adds TPM (Trusted Platform Module) support
> to Qemu. An emulator for the TIS (TPM Interface Spec) interface is
> added that provides the basis for accessing a 'backend' implementing the actual
> TPM functionality. The TIS emulator serves as a 'frontend' enabling for
> example Linux's TPM TIS (tpm_tis) driver.
> 
> 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.

Looks pretty clean, ACK to patches 1-4.

The passthrough mode is quite easy to misuse, though most
of the problem is in the hardware, not on our side.

I'm still trying to think of a good way to warn users
about the pitfalls with that. Disabling by default in configure, unless
explictly required, is certainly one way.
And/or, let's rename it 'assigned' mode to resemble the name of
another fragile qemu feature :) Only half joking ...

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

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

* Re: [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration
  2011-10-02 11:38 ` [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Michael S. Tsirkin
@ 2011-10-02 20:12   ` Stefan Berger
  2011-10-02 21:05     ` Michael S. Tsirkin
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Berger @ 2011-10-02 20:12 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: anbang.ruan, andreas.niederl, qemu-devel, serge

On 10/02/2011 07:38 AM, Michael S. Tsirkin wrote:
> On Wed, Sep 28, 2011 at 09:22:55AM -0400, Stefan Berger wrote:
>> The following series of patches adds TPM (Trusted Platform Module) support
>> to Qemu. An emulator for the TIS (TPM Interface Spec) interface is
>> added that provides the basis for accessing a 'backend' implementing the actual
>> TPM functionality. The TIS emulator serves as a 'frontend' enabling for
>> example Linux's TPM TIS (tpm_tis) driver.
>>
>> 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.
> Looks pretty clean, ACK to patches 1-4.
Thanks.
> The passthrough mode is quite easy to misuse, though most
> of the problem is in the hardware, not on our side.
>
> I'm still trying to think of a good way to warn users
> about the pitfalls with that. Disabling by default in configure, unless
The documentation isn't enough? If it's really needed could I add 
another patch on top of the existing V11?
> explictly required, is certainly one way.
> And/or, let's rename it 'assigned' mode to resemble the name of
> another fragile qemu feature :) Only half joking ...
>
Well, not sure what exactly you mean, but some things seem late versus 
closing time...

    Stefan

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

* Re: [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration
  2011-10-02 20:12   ` Stefan Berger
@ 2011-10-02 21:05     ` Michael S. Tsirkin
  0 siblings, 0 replies; 13+ messages in thread
From: Michael S. Tsirkin @ 2011-10-02 21:05 UTC (permalink / raw)
  To: Stefan Berger; +Cc: anbang.ruan, andreas.niederl, qemu-devel, serge

On Sun, Oct 02, 2011 at 04:12:14PM -0400, Stefan Berger wrote:
> On 10/02/2011 07:38 AM, Michael S. Tsirkin wrote:
> >On Wed, Sep 28, 2011 at 09:22:55AM -0400, Stefan Berger wrote:
> >>The following series of patches adds TPM (Trusted Platform Module) support
> >>to Qemu. An emulator for the TIS (TPM Interface Spec) interface is
> >>added that provides the basis for accessing a 'backend' implementing the actual
> >>TPM functionality. The TIS emulator serves as a 'frontend' enabling for
> >>example Linux's TPM TIS (tpm_tis) driver.
> >>
> >>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.
> >Looks pretty clean, ACK to patches 1-4.
> Thanks.
> >The passthrough mode is quite easy to misuse, though most
> >of the problem is in the hardware, not on our side.
> >
> >I'm still trying to think of a good way to warn users
> >about the pitfalls with that. Disabling by default in configure, unless
> The documentation isn't enough?

Let's be frank with ourselves, no one reads documentation.

> If it's really needed could I add
> another patch on top of the existing V11?

Sure.

> >explictly required, is certainly one way.
> >And/or, let's rename it 'assigned' mode to resemble the name of
> >another fragile qemu feature :) Only half joking ...
> >
> Well, not sure what exactly you mean, but some things seem late
> versus closing time...
> 
>    Stefan

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

* [Qemu-devel] [PATCH 6/5] Introduce --enable-tpm-passthrough configure option
  2011-09-28 13:22 [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (5 preceding siblings ...)
  2011-10-02 11:38 ` [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Michael S. Tsirkin
@ 2011-10-04 18:44 ` Stefan Berger
  2011-10-04 18:44 ` [Qemu-devel] [PATCH 7/5] Move parsing of filedescriptor into common function Stefan Berger
  2011-10-04 18:45 ` [Qemu-devel] [PATCH 8/5] Add fd parameter for TPM passthrough driver Stefan Berger
  8 siblings, 0 replies; 13+ messages in thread
From: Stefan Berger @ 2011-10-04 18:44 UTC (permalink / raw)
  To: qemu-devel; +Cc: anbang.ruan, mst, Stefan Berger, andreas.niederl, serge

Introduce --enable-tpm-passthrough configure option.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
  configure |   16 +++++++++++++++-
  1 file changed, 15 insertions(+), 1 deletion(-)

Index: qemu-git.pt/configure
===================================================================
--- qemu-git.pt.orig/configure
+++ qemu-git.pt/configure
@@ -183,6 +183,7 @@ opengl=""
  zlib="yes"
  guest_agent="yes"
  tpm="no"
+tpm_passthrough="no"

  # parse CC options first
  for opt do
@@ -769,11 +770,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)
@@ -1052,6 +1062,7 @@ echo "  --enable-usb-redir       enable
  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
@@ -2719,6 +2730,7 @@ echo "usb net redir     $usb_redir"
  echo "OpenGL support    $opengl"
  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"
@@ -3566,7 +3578,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] 13+ messages in thread

* [Qemu-devel] [PATCH 7/5] Move parsing of filedescriptor into common function
  2011-09-28 13:22 [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (6 preceding siblings ...)
  2011-10-04 18:44 ` [Qemu-devel] [PATCH 6/5] Introduce --enable-tpm-passthrough configure option Stefan Berger
@ 2011-10-04 18:44 ` Stefan Berger
  2011-10-04 18:45 ` [Qemu-devel] [PATCH 8/5] Add fd parameter for TPM passthrough driver Stefan Berger
  8 siblings, 0 replies; 13+ messages in thread
From: Stefan Berger @ 2011-10-04 18:44 UTC (permalink / raw)
  To: qemu-devel; +Cc: anbang.ruan, mst, Stefan Berger, andreas.niederl, serge

Move the parsing of a filedescriptor into a common function qemu_parse_fd().
Have the code in net.c call this function.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
  cutils.c      |   12 ++++++++++++
  net.c         |    7 +------
  qemu-common.h |    1 +
  3 files changed, 14 insertions(+), 6 deletions(-)

Index: qemu-git.pt/net.c
===================================================================
--- qemu-git.pt.orig/net.c
+++ qemu-git.pt/net.c
@@ -733,12 +733,7 @@ int net_handle_fd_param(Monitor *mon, co
              return -1;
          }
      } else {
-        char *endptr = NULL;
-
-        fd = strtol(param, &endptr, 10);
-        if (*endptr || (fd == 0 && param == endptr)) {
-            return -1;
-        }
+        fd = qemu_parse_fd(param);
      }

      return fd;
Index: qemu-git.pt/qemu-common.h
===================================================================
--- qemu-git.pt.orig/qemu-common.h
+++ qemu-git.pt/qemu-common.h
@@ -143,6 +143,7 @@ time_t mktimegm(struct tm *tm);
  int qemu_fls(int i);
  int qemu_fdatasync(int fd);
  int fcntl_setfl(int fd, int flag);
+int qemu_parse_fd(const char *param);

  /*
   * strtosz() suffixes used to specify the default treatment of an
Index: qemu-git.pt/cutils.c
===================================================================
--- qemu-git.pt.orig/cutils.c
+++ qemu-git.pt/cutils.c
@@ -415,3 +415,15 @@ int64_t strtosz(const char *nptr, char *
  {
      return strtosz_suffix(nptr, end, STRTOSZ_DEFSUFFIX_MB);
  }
+
+int qemu_parse_fd(const char *param)
+{
+    int fd;
+    char *endptr = NULL;
+
+    fd = strtol(param, &endptr, 10);
+    if (*endptr || (fd == 0 && param == endptr)) {
+        return -1;
+    }
+    return fd;
+}

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

* [Qemu-devel] [PATCH 8/5] Add fd parameter for TPM passthrough driver
  2011-09-28 13:22 [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (7 preceding siblings ...)
  2011-10-04 18:44 ` [Qemu-devel] [PATCH 7/5] Move parsing of filedescriptor into common function Stefan Berger
@ 2011-10-04 18:45 ` Stefan Berger
  8 siblings, 0 replies; 13+ messages in thread
From: Stefan Berger @ 2011-10-04 18:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: anbang.ruan, mst, Stefan Berger, andreas.niederl, serge

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

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
  hw/tpm_passthrough.c |   74 
+++++++++++++++++++++++++++++++++------------------
  qemu-config.c        |    5 +++
  2 files changed, 53 insertions(+), 26 deletions(-)

Index: qemu-git.pt/qemu-config.c
===================================================================
--- qemu-git.pt.orig/qemu-config.c
+++ qemu-git.pt/qemu-config.c
@@ -523,6 +523,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 */ }
      },
  };
Index: qemu-git.pt/hw/tpm_passthrough.c
===================================================================
--- qemu-git.pt.orig/hw/tpm_passthrough.c
+++ qemu-git.pt/hw/tpm_passthrough.c
@@ -356,32 +356,54 @@ static int tpm_passthrough_handle_device
      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;
+    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;
+        }
+
+        snprintf(buf, sizeof(buf), "fd=%d", tb->s.tpm_pt->tpm_fd);
+
+        tb->parameters = g_strdup(buf);
+
+        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;
+        }
+
+        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)) {

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

end of thread, other threads:[~2011-10-04 18:45 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-09-28 13:22 [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2011-09-28 13:22 ` [Qemu-devel] [PATCH V11 1/5] Support for TPM command line options Stefan Berger
2011-09-28 13:22 ` [Qemu-devel] [PATCH V11 2/5] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
2011-09-28 13:22 ` [Qemu-devel] [PATCH V11 3/5] Add a debug register Stefan Berger
2011-09-28 13:22 ` [Qemu-devel] [PATCH V11 4/5] Build the TPM frontend code Stefan Berger
2011-09-28 13:23 ` [Qemu-devel] [PATCH V11 5/5] Add a TPM Passthrough backend driver implementation Stefan Berger
2011-10-02 11:30   ` Michael S. Tsirkin
2011-10-02 11:38 ` [Qemu-devel] [PATCH V11 0/5] Qemu Trusted Platform Module (TPM) integration Michael S. Tsirkin
2011-10-02 20:12   ` Stefan Berger
2011-10-02 21:05     ` Michael S. Tsirkin
2011-10-04 18:44 ` [Qemu-devel] [PATCH 6/5] Introduce --enable-tpm-passthrough configure option Stefan Berger
2011-10-04 18:44 ` [Qemu-devel] [PATCH 7/5] Move parsing of filedescriptor into common function Stefan Berger
2011-10-04 18:45 ` [Qemu-devel] [PATCH 8/5] Add fd parameter for TPM passthrough driver Stefan Berger

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