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

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

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

v15:
 - applies to checkout of 8a22565 (Mar 27)
 - replacing g_malloc's with g_new; no more checks for NULL after allocs
 - introducing usage of bottom half in TIS frontend to deliver responses
 - get rid of locks since global lock is held by all threads entering TIS
   code
 - cleanups

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

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

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

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

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

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

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

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

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

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

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

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

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

Regards,
    Stefan


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

 Makefile.target      |    3 +
 configure            |   28 ++
 hmp-commands.hx      |    2 +
 hmp.c                |   28 ++
 hmp.h                |    1 +
 hw/tpm_backend.c     |   58 ++++
 hw/tpm_backend.h     |   43 +++
 hw/tpm_passthrough.c |  452 +++++++++++++++++++++++++
 hw/tpm_tis.c         |  895 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/tpm_tis.h         |   82 +++++
 monitor.c            |    8 +
 qapi-schema.json     |   29 ++
 qemu-config.c        |   25 ++
 qemu-options.hx      |   70 ++++
 tpm.c                |  230 +++++++++++++
 tpm.h                |  114 +++++++
 vl.c                 |   20 ++
 17 files changed, 2088 insertions(+), 0 deletions(-)
 create mode 100644 hw/tpm_backend.c
 create mode 100644 hw/tpm_backend.h
 create mode 100644 hw/tpm_passthrough.c
 create mode 100644 hw/tpm_tis.c
 create mode 100644 hw/tpm_tis.h
 create mode 100644 tpm.c
 create mode 100644 tpm.h

-- 
1.7.4.4

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

* [Qemu-devel] [PATCH V15 1/7] Support for TPM command line options
  2012-03-27 20:24 [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
@ 2012-03-27 20:24 ` Stefan Berger
  2012-03-27 21:35   ` Anthony Liguori
  2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 14+ messages in thread
From: Stefan Berger @ 2012-03-27 20:24 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

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

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

and

./qemu-... -tpmdev ?

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

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

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

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

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

diff --git a/hmp-commands.hx b/hmp-commands.hx
index bd35a3e..7bbb5b8 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1402,6 +1402,8 @@ show device tree
 show qdev device model list
 @item info roms
 show roms
+@item info tpm
+show the TPM device
 @end table
 ETEXI
 
diff --git a/hmp.c b/hmp.c
index 9cf2d13..bac6b5a 100644
--- a/hmp.c
+++ b/hmp.c
@@ -546,6 +546,34 @@ void hmp_info_block_jobs(Monitor *mon)
     }
 }
 
+void hmp_info_tpm(Monitor *mon)
+{
+    TPMInfoList *info_list, *info;
+    Error *err = NULL;
+    unsigned int c = 0;
+
+    info_list = qmp_query_tpm(&err);
+    if (err) {
+        monitor_printf(mon, "TPM device not supported\n");
+        error_free(err);
+        return;
+    }
+
+    monitor_printf(mon, "TPM device:\n");
+
+    for (info = info_list; info; info = info->next) {
+        TPMInfo *ti = info->value;
+        monitor_printf(mon, " tpm%d: model=%s\n",
+                       c, ti->model);
+        monitor_printf(mon, "  \\ %s: type=%s%s%s\n",
+                       ti->id, ti->type,
+                       ti->parameters ? "," : "",
+                       ti->parameters ? ti->parameters : "");
+        c++;
+    }
+    qapi_free_TPMInfoList(info_list);
+}
+
 void hmp_quit(Monitor *mon, const QDict *qdict)
 {
     monitor_suspend(mon);
diff --git a/hmp.h b/hmp.h
index 8807853..4884739 100644
--- a/hmp.h
+++ b/hmp.h
@@ -33,6 +33,7 @@ void hmp_info_spice(Monitor *mon);
 void hmp_info_balloon(Monitor *mon);
 void hmp_info_pci(Monitor *mon);
 void hmp_info_block_jobs(Monitor *mon);
+void hmp_info_tpm(Monitor *mon);
 void hmp_quit(Monitor *mon, const QDict *qdict);
 void hmp_stop(Monitor *mon, const QDict *qdict);
 void hmp_system_reset(Monitor *mon, const QDict *qdict);
diff --git a/hw/tpm_tis.h b/hw/tpm_tis.h
new file mode 100644
index 0000000..f906390
--- /dev/null
+++ b/hw/tpm_tis.h
@@ -0,0 +1,81 @@
+/*
+ * tpm_tis.c - QEMU's TPM TIS interface emulator
+ *
+ * Copyright (C) 2006,2010,2011 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger <stefanb@us.ibm.com>
+ *  David Safford <safford@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ *
+ * Implementation of the TIS interface according to specs found at
+ * http://www.trustedcomputiggroup.org
+ *
+ */
+#ifndef HW_TPM_TIS_H
+#define HW_TPM_TIS_H
+
+#include "isa.h"
+#include "qemu-thread.h"
+#include "qemu-common.h"
+
+#define TPM_TIS_ADDR_BASE           0xFED40000
+
+#define TPM_TIS_NUM_LOCALITIES      5     /* per spec */
+#define TPM_TIS_LOCALITY_SHIFT      12
+#define TPM_TIS_NO_LOCALITY         0xff
+
+#define TPM_TIS_IS_VALID_LOCTY(x)   ((x) < TPM_TIS_NUM_LOCALITIES)
+
+#define TPM_TIS_IRQ                 5
+
+#define TPM_TIS_BUFFER_MAX          4096
+
+
+typedef struct TPMSizedBuffer {
+    uint32_t size;
+    uint8_t  *buffer;
+} TPMSizedBuffer;
+
+typedef enum {
+    TPM_TIS_STATUS_IDLE = 0,
+    TPM_TIS_STATUS_READY,
+    TPM_TIS_STATUS_COMPLETION,
+    TPM_TIS_STATUS_EXECUTION,
+    TPM_TIS_STATUS_RECEPTION,
+} TPMTISStatus;
+
+/* locality data  -- all fields are persisted */
+typedef struct TPMLocality {
+    TPMTISStatus status;
+    uint8_t access;
+    uint8_t sts;
+    uint32_t inte;
+    uint32_t ints;
+
+    uint16_t w_offset;
+    uint16_t r_offset;
+    TPMSizedBuffer w_buffer;
+    TPMSizedBuffer r_buffer;
+} TPMLocality;
+
+typedef struct TPMTISState {
+    uint32_t offset;
+    uint8_t buf[TPM_TIS_BUFFER_MAX];
+
+    uint8_t active_locty;
+    uint8_t aborting_locty;
+    uint8_t next_locty;
+
+    TPMLocality loc[TPM_TIS_NUM_LOCALITIES];
+
+    qemu_irq irq;
+    uint32_t irq_num;
+} TPMTISState;
+
+#endif /* HW_TPM_TIS_H */
diff --git a/monitor.c b/monitor.c
index 2ff1e0b..427992b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -47,6 +47,7 @@
 #include "migration.h"
 #include "kvm.h"
 #include "acl.h"
+#include "tpm.h"
 #include "qint.h"
 #include "qfloat.h"
 #include "qlist.h"
@@ -2602,6 +2603,13 @@ static mon_cmd_t info_cmds[] = {
         .mhandler.info = do_trace_print_events,
     },
     {
+        .name       = "tpm",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show the TPM device",
+        .mhandler.info = hmp_info_tpm,
+    },
+    {
         .name       = NULL,
     },
 };
diff --git a/qapi-schema.json b/qapi-schema.json
index 0d11d6e..4ad6d29 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1701,3 +1701,32 @@
 # Since: 1.1
 ##
 { 'command': 'xen-save-devices-state', 'data': {'filename': 'str'} }
+
+##
+# @TPMInfo:
+#
+# Information about the TPM
+#
+# @model: The TPM frontend model, i.e., tpm-tis
+#
+# @id: The ID of the TPM
+#
+# @type: The type of TPM backend, i.e., passthrough
+#
+# @parameters: Additional parameters of the TPM backend device
+#
+# Since: 1.1
+##
+{ 'type': 'TPMInfo',
+  'data': {'model': 'str', 'id': 'str', 'type': 'str', 'parameters': 'str' } }
+
+##
+# @query-tpm
+#
+# Return information about the TPM device.
+#
+# Returns: @TPMInfo on success
+#
+# Since: 1.1
+##
+{ 'command': 'query-tpm', 'returns': ['TPMInfo'] }
diff --git a/qemu-config.c b/qemu-config.c
index be84a03..edc8d5d 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -613,6 +613,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,
@@ -628,6 +647,7 @@ static QemuOptsList *vm_config_groups[32] = {
     &qemu_machine_opts,
     &qemu_boot_opts,
     &qemu_iscsi_opts,
+    &qemu_tpmdev_opts,
     NULL,
 };
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 662f571..355a9c1 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2003,6 +2003,36 @@ ETEXI
 
 DEFHEADING()
 
+DEFHEADING(TPM device options:)
+
+DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
+    "-tpmdev [<type>],id=str[,option][,option][,...]\n",
+    QEMU_ARCH_ALL)
+STEXI
+
+The general form of a TPM device option is:
+@table @option
+
+@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
+@findex -tpmdev
+Backend type must be:
+
+The specific backend type will determine the applicable options.
+The @code{-tpmdev} options requires a @code{-device} option.
+
+Options to each backend are described below.
+
+Use ? to print all available TPM backend types.
+@example
+qemu -tpmdev ?
+@end example
+
+@end table
+
+ETEXI
+
+DEFHEADING()
+
 DEFHEADING(Linux/Multiboot boot specific:)
 STEXI
 
diff --git a/tpm.c b/tpm.c
new file mode 100644
index 0000000..6324c1e
--- /dev/null
+++ b/tpm.c
@@ -0,0 +1,213 @@
+/*
+ * TPM configuration
+ *
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger    <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * Based on net.c
+ */
+#include "config.h"
+
+#include "monitor.h"
+#include "qerror.h"
+#include "tpm.h"
+#include "qmp-commands.h"
+
+static QLIST_HEAD(, TPMBackend) tpm_backends =
+    QLIST_HEAD_INITIALIZER(tpm_backends);
+
+#ifdef CONFIG_TPM
+
+static const TPMDriverOps *bes[] = {
+    NULL,
+};
+
+const TPMDriverOps *tpm_get_backend_driver(const char *id)
+{
+    int i;
+
+    for (i = 0; bes[i] != NULL; i++) {
+        if (!strcmp(bes[i]->id, id)) {
+            break;
+        }
+    }
+
+    return bes[i];
+}
+
+/*
+ * Walk the list of available TPM backend drivers and display them on the
+ * screen.
+ */
+void tpm_display_backend_drivers(void)
+{
+    int i;
+
+    fprintf(stderr, "Supported TPM types (choose only one):\n");
+
+    for (i = 0; bes[i] != NULL; i++) {
+        fprintf(stderr, "%12s   %s\n", bes[i]->id, bes[i]->desc());
+    }
+    fprintf(stderr, "\n");
+}
+
+/*
+ * Find the TPM with the given Id
+ */
+TPMBackend *qemu_find_tpm(const char *id)
+{
+    TPMBackend *drv;
+
+    QLIST_FOREACH(drv, &tpm_backends, list) {
+        if (!strcmp(drv->id, id)) {
+            return drv;
+        }
+    }
+
+    return NULL;
+}
+
+static int configure_tpm(QemuOpts *opts)
+{
+    const char *value;
+    const char *id;
+    const TPMDriverOps *be;
+    TPMBackend *drv;
+
+    if (!QLIST_EMPTY(&tpm_backends)) {
+        error_report("Only one TPM is allowed.\n");
+        return 1;
+    }
+
+    id = qemu_opts_id(opts);
+    if (id == NULL) {
+        qerror_report(QERR_MISSING_PARAMETER, "id");
+        return 1;
+    }
+
+    value = qemu_opt_get(opts, "type");
+    if (!value) {
+        qerror_report(QERR_MISSING_PARAMETER, "type");
+        tpm_display_backend_drivers();
+        return 1;
+    }
+
+    be = tpm_get_backend_driver(value);
+    if (be == NULL) {
+        qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
+                      "a TPM backend type");
+        tpm_display_backend_drivers();
+        return 1;
+    }
+
+    drv = be->create(opts, id);
+    if (!drv) {
+        return 1;
+    }
+
+    QLIST_INSERT_HEAD(&tpm_backends, drv, list);
+
+    return 0;
+}
+
+static int tpm_init_tpmdev(QemuOpts *opts, void *dummy)
+{
+    return configure_tpm(opts);
+}
+
+/*
+ * Walk the list of TPM backend drivers that are in use and call their
+ * destroy function to have them cleaned up.
+ */
+void tpm_cleanup(void)
+{
+    TPMBackend *drv, *next;
+
+    QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
+        QLIST_REMOVE(drv, list);
+        drv->ops->destroy(drv);
+    }
+}
+
+/*
+ * Initialize the TPM. Process the tpmdev command line options describing the
+ * TPM backend.
+ */
+int tpm_init(void)
+{
+    if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
+                          tpm_init_tpmdev, NULL, 1) != 0) {
+        return -1;
+    }
+
+    atexit(tpm_cleanup);
+
+    return 0;
+}
+
+/*
+ * Parse the TPM configuration options.
+ * It is possible to pass an option '-tpmdev none' to not activate any TPM.
+ * To display all available TPM backends the user may use '-tpmdev ?'
+ */
+int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
+{
+    QemuOpts *opts;
+
+    if (strcmp("none", optarg) != 0) {
+        if (*optarg == '?') {
+            tpm_display_backend_drivers();
+            return -1;
+        }
+        opts = qemu_opts_parse(opts_list, optarg, 1);
+        if (!opts) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+#endif /* CONFIG_TPM */
+
+static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
+{
+    TPMInfo *res = g_new0(TPMInfo, 1);
+
+    res->model = g_strdup(drv->fe_model);
+    res->id = g_strdup(drv->id);
+    if (drv->parameters) {
+        res->parameters = g_strdup(drv->parameters);
+    }
+    res->type = g_strdup(drv->ops->id);
+
+    return res;
+}
+
+/*
+ * Walk the list of active TPM backends and collect information about them
+ * following the schema description in qapi-schema.json.
+ */
+TPMInfoList *qmp_query_tpm(Error **errp)
+{
+    TPMBackend *drv;
+    TPMInfoList *info, *head = NULL, *cur_item = NULL;
+
+    QLIST_FOREACH(drv, &tpm_backends, list) {
+        info = g_new0(TPMInfoList, 1);
+        info->value = qmp_query_tpm_inst(drv);
+
+        if (!cur_item) {
+            head = cur_item = info;
+        } else {
+            cur_item->next = info;
+            cur_item = info;
+        }
+    }
+
+    return head;
+}
diff --git a/tpm.h b/tpm.h
new file mode 100644
index 0000000..4e871d9
--- /dev/null
+++ b/tpm.h
@@ -0,0 +1,81 @@
+/*
+ * TPM configuration
+ *
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger    <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef QEMU_TPM_H
+#define QEMU_TPM_H
+
+#include "memory.h"
+#include "hw/tpm_tis.h"
+
+struct TPMDriverOps;
+typedef struct TPMDriverOps TPMDriverOps;
+
+typedef struct TPMBackend {
+    char *id;
+    const char *fe_model;
+    char *parameters;
+    const TPMDriverOps *ops;
+
+    QLIST_ENTRY(TPMBackend) list;
+} TPMBackend;
+
+/* overall state of the TPM interface */
+typedef struct TPMState {
+    ISADevice busdev;
+    MemoryRegion mmio;
+
+    union {
+        TPMTISState tis;
+    } s;
+
+    uint8_t     command_locty;
+    TPMLocality *cmd_locty;
+
+    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 (*deliver_request)(TPMBackend *t);
+
+    void (*reset)(TPMBackend *t);
+
+    bool (*get_tpm_established_flag)(TPMBackend *t);
+};
+
+#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
+
+int tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
+int tpm_init(void);
+void tpm_cleanup(void);
+TPMBackend *qemu_find_tpm(const char *id);
+void tpm_display_backend_drivers(void);
+const TPMDriverOps *tpm_get_backend_driver(const char *id);
+
+#endif /* QEMU_TPM_H */
diff --git a/vl.c b/vl.c
index 0fccf50..dcda728 100644
--- a/vl.c
+++ b/vl.c
@@ -139,6 +139,7 @@ int main(int argc, char **argv)
 #include "block.h"
 #include "blockdev.h"
 #include "block-migration.h"
+#include "tpm.h"
 #include "dma.h"
 #include "audio/audio.h"
 #include "migration.h"
@@ -2661,6 +2662,16 @@ int main(int argc, char **argv, char **envp)
                 ram_size = value;
                 break;
             }
+            case QEMU_OPTION_tpmdev:
+#ifdef CONFIG_TPM
+                if (tpm_config_parse(qemu_find_opts("tpmdev"), optarg) < 0) {
+                    exit(1);
+                }
+#else
+                fprintf(stderr, "TPM support is disabled\n");
+                exit(1);
+#endif
+                break;
             case QEMU_OPTION_mempath:
                 mem_path = optarg;
                 break;
@@ -3385,6 +3396,12 @@ int main(int argc, char **argv, char **envp)
         exit(1);
     }
 
+#ifdef CONFIG_TPM
+    if (tpm_init() < 0) {
+        exit(1);
+    }
+#endif
+
     /* init the bluetooth world */
     if (foreach_device_config(DEV_BT, bt_parse))
         exit(1);
@@ -3635,6 +3652,9 @@ int main(int argc, char **argv, char **envp)
     pause_all_vcpus();
     net_cleanup();
     res_free();
+#ifdef CONFIG_TPM
+    tpm_cleanup();
+#endif
 
     return 0;
 }
-- 
1.7.4.4

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

* [Qemu-devel] [PATCH V15 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2012-03-27 20:24 [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
  2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 1/7] Support for TPM command line options Stefan Berger
@ 2012-03-27 20:24 ` Stefan Berger
  2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 3/7] Add a debug register Stefan Berger
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Stefan Berger @ 2012-03-27 20:24 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

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

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

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

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

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

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


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

diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
new file mode 100644
index 0000000..c9bb899
--- /dev/null
+++ b/hw/tpm_tis.c
@@ -0,0 +1,825 @@
+/*
+ * 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/pci_ids.h"
+#include "hw/tpm_tis.h"
+#include "qemu-error.h"
+#include "qemu-common.h"
+
+/*#define DEBUG_TIS */
+
+#ifdef DEBUG_TIS
+#define dprintf(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+    do { } while (0)
+#endif
+
+/* whether the STS interrupt is supported */
+/*#define RAISE_STS_IRQ */
+
+/* tis registers */
+#define TPM_TIS_REG_ACCESS                0x00
+#define TPM_TIS_REG_INT_ENABLE            0x08
+#define TPM_TIS_REG_INT_VECTOR            0x0c
+#define TPM_TIS_REG_INT_STATUS            0x10
+#define TPM_TIS_REG_INTF_CAPABILITY       0x14
+#define TPM_TIS_REG_STS                   0x18
+#define TPM_TIS_REG_DATA_FIFO             0x24
+#define TPM_TIS_REG_DID_VID               0xf00
+#define TPM_TIS_REG_RID                   0xf04
+
+#define TPM_TIS_STS_VALID                 (1 << 7)
+#define TPM_TIS_STS_COMMAND_READY         (1 << 6)
+#define TPM_TIS_STS_TPM_GO                (1 << 5)
+#define TPM_TIS_STS_DATA_AVAILABLE        (1 << 4)
+#define TPM_TIS_STS_EXPECT                (1 << 3)
+#define TPM_TIS_STS_RESPONSE_RETRY        (1 << 1)
+
+#define TPM_TIS_ACCESS_TPM_REG_VALID_STS  (1 << 7)
+#define TPM_TIS_ACCESS_ACTIVE_LOCALITY    (1 << 5)
+#define TPM_TIS_ACCESS_BEEN_SEIZED        (1 << 4)
+#define TPM_TIS_ACCESS_SEIZE              (1 << 3)
+#define TPM_TIS_ACCESS_PENDING_REQUEST    (1 << 2)
+#define TPM_TIS_ACCESS_REQUEST_USE        (1 << 1)
+#define TPM_TIS_ACCESS_TPM_ESTABLISHMENT  (1 << 0)
+
+#define TPM_TIS_INT_ENABLED               (1 << 31)
+#define TPM_TIS_INT_DATA_AVAILABLE        (1 << 0)
+#define TPM_TIS_INT_STS_VALID             (1 << 1)
+#define TPM_TIS_INT_LOCALITY_CHANGED      (1 << 2)
+#define TPM_TIS_INT_COMMAND_READY         (1 << 7)
+
+#ifndef RAISE_STS_IRQ
+
+#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
+                                      TPM_TIS_INT_DATA_AVAILABLE   | \
+                                      TPM_TIS_INT_COMMAND_READY)
+
+#else
+
+#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
+                                      TPM_TIS_INT_DATA_AVAILABLE   | \
+                                      TPM_TIS_INT_STS_VALID | \
+                                      TPM_TIS_INT_COMMAND_READY)
+
+#endif
+
+#define TPM_TIS_CAPABILITIES_SUPPORTED   ((1 << 4) | \
+                                          TPM_TIS_INTERRUPTS_SUPPORTED)
+
+#define TPM_TIS_TPM_DID       0x0001
+#define TPM_TIS_TPM_VID       PCI_VENDOR_ID_IBM
+#define TPM_TIS_TPM_RID       0x0001
+
+#define TPM_TIS_NO_DATA_BYTE  0xff
+
+/* utility functions */
+
+static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
+{
+    return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
+}
+
+static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+    return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
+}
+
+static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
+{
+#ifdef DEBUG_TIS
+    uint32_t len, i;
+
+    len = tpm_tis_get_size_from_buffer(sb);
+    dprintf("tpm_tis: %s length = %d\n", string, len);
+    for (i = 0; i < len; i++) {
+        if (i && !(i % 16)) {
+            dprintf("\n");
+        }
+        dprintf("%.2X ", sb->buffer[i]);
+    }
+    dprintf("\n");
+#endif
+}
+
+/*
+ * Send a request to the TPM.
+ */
+static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
+{
+    TPMTISState *tis = &s->s.tis;
+
+    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
+
+    s->command_locty = locty;
+    s->cmd_locty = &tis->loc[locty];
+
+    /*
+     * w_offset serves as length indicator for length of data;
+     * it's reset when the response comes back
+     */
+    tis->loc[locty].status = TPM_TIS_STATUS_EXECUTION;
+    tis->loc[locty].sts &= ~TPM_TIS_STS_EXPECT;
+
+    s->be_driver->ops->deliver_request(s->be_driver);
+}
+
+/* raise an interrupt if allowed */
+static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
+{
+    TPMTISState *tis = &s->s.tis;
+
+    if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
+        return;
+    }
+
+    if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
+        (tis->loc[locty].inte & irqmask)) {
+        dprintf("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
+        qemu_irq_raise(s->s.tis.irq);
+        tis->loc[locty].ints |= irqmask;
+    }
+}
+
+static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
+{
+    uint8_t l;
+
+    for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
+        if (l == locty) {
+            continue;
+        }
+        if ((s->s.tis.loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
+{
+    TPMTISState *tis = &s->s.tis;
+    int change = (s->s.tis.active_locty != new_active_locty);
+
+    if (change && TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) {
+        /* reset flags on the old active locality */
+        tis->loc[s->s.tis.active_locty].access &=
+                 ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|TPM_TIS_ACCESS_REQUEST_USE);
+        if (TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
+            tis->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE) {
+            tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
+        }
+    }
+
+    tis->active_locty = new_active_locty;
+
+    dprintf("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty);
+
+    if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
+        /* set flags on the new active locality */
+        tis->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
+        tis->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
+                                               TPM_TIS_ACCESS_SEIZE);
+    }
+
+    if (change) {
+        tpm_tis_raise_irq(s, tis->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
+    }
+}
+
+/* abort -- this function switches the locality */
+static void tpm_tis_abort(TPMState *s, uint8_t locty)
+{
+    TPMTISState *tis = &s->s.tis;
+
+    tis->loc[locty].r_offset = 0;
+    tis->loc[locty].w_offset = 0;
+
+    dprintf("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty);
+
+    /*
+     * Need to react differently depending on who's aborting now and
+     * which locality will become active afterwards.
+     */
+    if (tis->aborting_locty == tis->next_locty) {
+        tis->loc[tis->aborting_locty].status = TPM_TIS_STATUS_READY;
+        tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_READY;
+        tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY);
+    }
+
+    /* locality after abort is another one than the current one */
+    tpm_tis_new_active_locality(s, tis->next_locty);
+
+    tis->next_locty = TPM_TIS_NO_LOCALITY;
+    /* nobody's aborting a command anymore */
+    tis->aborting_locty = TPM_TIS_NO_LOCALITY;
+}
+
+/* prepare aborting current command */
+static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
+{
+    TPMTISState *tis = &s->s.tis;
+    uint8_t busy_locty;
+
+    tis->aborting_locty = locty;
+    tis->next_locty = newlocty;  /* locality after successful abort */
+
+    /*
+     * only abort a command using an interrupt if currently executing
+     * a command AND if there's a valid connection to the vTPM.
+     */
+    for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
+        if (tis->loc[busy_locty].status == TPM_TIS_STATUS_EXECUTION) {
+            /*
+             * there is currently no way to interrupt the TPM's operations
+             * while it's executing a command; once the TPM is done and
+             * returns the buffer, it will switch to the next_locty;
+             */
+            dprintf("tpm_tis: Locality %d is busy - deferring abort\n",
+                    busy_locty);
+            return;
+        }
+    }
+
+    tpm_tis_abort(s, locty);
+}
+
+static void tpm_tis_receive_bh(void *opaque)
+{
+    TPMState *s = opaque;
+    TPMTISState *tis = &s->s.tis;
+    uint8_t locty = s->command_locty;
+
+    tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE;
+    tis->loc[locty].status = TPM_TIS_STATUS_COMPLETION;
+    tis->loc[locty].r_offset = 0;
+    tis->loc[locty].w_offset = 0;
+
+    if (TPM_TIS_IS_VALID_LOCTY(tis->next_locty)) {
+        tpm_tis_abort(s, locty);
+    }
+
+#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
+}
+
+/*
+ * 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;
+
+    assert(s->command_locty == locty);
+
+    qemu_bh_schedule(tis->bh);
+}
+
+/*
+ * Read a byte of response data
+ */
+static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
+{
+    TPMTISState *tis = &s->s.tis;
+    uint32_t ret = TPM_TIS_NO_DATA_BYTE;
+    uint16_t len;
+
+    if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
+        len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
+
+        ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++];
+        if (tis->loc[locty].r_offset >= len) {
+            /* got last byte */
+            tis->loc[locty].sts = TPM_TIS_STS_VALID;
+#ifdef RAISE_STS_IRQ
+            tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
+#endif
+        }
+        dprintf("tpm_tis: tpm_tis_data_read byte 0x%02x   [%d]\n",
+                ret, tis->loc[locty].r_offset-1);
+    }
+
+    return ret;
+}
+
+/*
+ * Read a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
+                                  unsigned size)
+{
+    TPMState *s = opaque;
+    TPMTISState *tis = &s->s.tis;
+    uint16_t offset = addr & 0xffc;
+    uint8_t shift = (addr & 0x3) * 8;
+    uint32_t val = 0xffffffff;
+    uint8_t locty = tpm_tis_locality_from_addr(addr);
+
+    if (s->be_driver->ops->had_startup_error(s->be_driver)) {
+        return val;
+    }
+
+    switch (offset) {
+    case TPM_TIS_REG_ACCESS:
+        /* never show the SEIZE flag even though we use it internally */
+        val = tis->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
+        /* the pending flag is alawys calculated */
+        if (tpm_tis_check_request_use_except(s, locty)) {
+            val |= TPM_TIS_ACCESS_PENDING_REQUEST;
+        }
+        val |= !s->be_driver->ops->get_tpm_established_flag(s->be_driver);
+        break;
+    case TPM_TIS_REG_INT_ENABLE:
+        val = tis->loc[locty].inte;
+        break;
+    case TPM_TIS_REG_INT_VECTOR:
+        val = tis->irq_num;
+        break;
+    case TPM_TIS_REG_INT_STATUS:
+        val = tis->loc[locty].ints;
+        break;
+    case TPM_TIS_REG_INTF_CAPABILITY:
+        val = TPM_TIS_CAPABILITIES_SUPPORTED;
+        break;
+    case TPM_TIS_REG_STS:
+        if (tis->active_locty == locty) {
+            if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
+                val =  (tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer)
+                        - tis->loc[locty].r_offset) << 8 | tis->loc[locty].sts;
+            } else {
+                val = (tis->loc[locty].w_buffer.size -
+                       tis->loc[locty].w_offset) << 8 | tis->loc[locty].sts;
+            }
+        }
+        break;
+    case TPM_TIS_REG_DATA_FIFO:
+        if (tis->active_locty == locty) {
+            switch (tis->loc[locty].status) {
+            case TPM_TIS_STATUS_COMPLETION:
+                val = tpm_tis_data_read(s, locty);
+                break;
+            default:
+                val = TPM_TIS_NO_DATA_BYTE;
+                break;
+            }
+        }
+        break;
+    case TPM_TIS_REG_DID_VID:
+        val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
+        break;
+    case TPM_TIS_REG_RID:
+        val = TPM_TIS_TPM_RID;
+        break;
+    }
+
+    if (shift) {
+        val >>= shift;
+    }
+
+    dprintf("tpm_tis:  read.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
+
+    return val;
+}
+
+/*
+ * Write a value to a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static void tpm_tis_mmio_write_intern(void *opaque, target_phys_addr_t addr,
+                                      uint64_t val, unsigned size,
+                                      bool hw_access)
+{
+    TPMState *s = opaque;
+    TPMTISState *tis = &s->s.tis;
+    uint16_t off = addr & 0xfff;
+    uint8_t locty = tpm_tis_locality_from_addr(addr);
+    uint8_t active_locty, l;
+    int c, set_new_locty = 1;
+    uint16_t len;
+
+    dprintf("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
+
+    if (locty == 4 && !hw_access) {
+        dprintf("tpm_tis: Access to locality 4 only allowed from hardware\n");
+        return;
+    }
+
+    if (s->be_driver->ops->had_startup_error(s->be_driver)) {
+        return;
+    }
+
+    switch (off) {
+    case TPM_TIS_REG_ACCESS:
+
+        if ((val & TPM_TIS_ACCESS_SEIZE)) {
+            val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
+                     TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+        }
+
+        active_locty = tis->active_locty;
+
+        if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
+            /* give up locality if currently owned */
+            if (tis->active_locty == locty) {
+                dprintf("tpm_tis: Releasing locality %d\n", locty);
+
+                uint8_t newlocty = TPM_TIS_NO_LOCALITY;
+                /* anybody wants the locality ? */
+                for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
+                    if ((tis->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
+                        dprintf("tpm_tis: Locality %d requests use.\n", c);
+                        newlocty = c;
+                        break;
+                    }
+                }
+                dprintf("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
+                        "Next active locality: %d\n",
+                        newlocty);
+
+                if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
+                    set_new_locty = 0;
+                    tpm_tis_prep_abort(s, locty, newlocty);
+                } else {
+                    active_locty = TPM_TIS_NO_LOCALITY;
+                }
+            } else {
+                /* not currently the owner; clear a pending request */
+                tis->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
+            }
+        }
+
+        if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
+            tis->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
+        }
+
+        if ((val & TPM_TIS_ACCESS_SEIZE)) {
+            /*
+             * allow seize if a locality is active and the requesting
+             * locality is higher than the one that's active
+             * OR
+             * allow seize for requesting locality if no locality is
+             * active
+             */
+            while ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty) &&
+                    locty > tis->active_locty) ||
+                    !TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) {
+
+                /* already a pending SEIZE ? */
+                if ((tis->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
+                    break;
+                }
+
+                /* check for ongoing seize by a higher locality */
+                for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
+                    if ((tis->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
+                        break;
+                    }
+                }
+
+                /* cancel any seize by a lower locality */
+                for (l = 0; l < locty - 1; l++) {
+                    tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
+                }
+
+                tis->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
+                dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
+                        "Locality %d seized from locality %d\n",
+                        locty, tis->active_locty);
+                dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
+                set_new_locty = 0;
+                tpm_tis_prep_abort(s, tis->active_locty, locty);
+                break;
+            }
+        }
+
+        if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
+            if (tis->active_locty != locty) {
+                if (TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) {
+                    tis->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
+                } else {
+                    /* no locality active -> make this one active now */
+                    active_locty = locty;
+                }
+            }
+        }
+
+        if (set_new_locty) {
+            tpm_tis_new_active_locality(s, active_locty);
+        }
+
+        break;
+    case TPM_TIS_REG_INT_ENABLE:
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        tis->loc[locty].inte = (val & (TPM_TIS_INT_ENABLED | (0x3 << 3) |
+                                     TPM_TIS_INTERRUPTS_SUPPORTED));
+        break;
+    case TPM_TIS_REG_INT_VECTOR:
+        /* hard wired -- ignore */
+        break;
+    case TPM_TIS_REG_INT_STATUS:
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        /* clearing of interrupt flags */
+        if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
+            (tis->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
+            tis->loc[locty].ints &= ~val;
+            if (tis->loc[locty].ints == 0) {
+                qemu_irq_lower(tis->irq);
+                dprintf("tpm_tis: Lowering IRQ\n");
+            }
+        }
+        tis->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
+        break;
+    case TPM_TIS_REG_STS:
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
+                TPM_TIS_STS_RESPONSE_RETRY);
+
+        if (val == TPM_TIS_STS_COMMAND_READY) {
+            switch (tis->loc[locty].status) {
+
+            case TPM_TIS_STATUS_READY:
+                tis->loc[locty].w_offset = 0;
+                tis->loc[locty].r_offset = 0;
+            break;
+
+            case TPM_TIS_STATUS_IDLE:
+                tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
+                tis->loc[locty].status = TPM_TIS_STATUS_READY;
+                tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
+            break;
+
+            case TPM_TIS_STATUS_EXECUTION:
+            case TPM_TIS_STATUS_RECEPTION:
+                /* abort currently running command */
+                dprintf("tpm_tis: %s: Initiating abort.\n",
+                        __func__);
+                tpm_tis_prep_abort(s, locty, locty);
+            break;
+
+            case TPM_TIS_STATUS_COMPLETION:
+                tis->loc[locty].w_offset = 0;
+                tis->loc[locty].r_offset = 0;
+                /* shortcut to ready state with C/R set */
+                tis->loc[locty].status = TPM_TIS_STATUS_READY;
+                if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
+                    tis->loc[locty].sts   = TPM_TIS_STS_COMMAND_READY;
+                    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
+                }
+            break;
+
+            }
+        } else if (val == TPM_TIS_STS_TPM_GO) {
+            switch (tis->loc[locty].status) {
+            case TPM_TIS_STATUS_RECEPTION:
+                tpm_tis_tpm_send(s, locty);
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
+            switch (tis->loc[locty].status) {
+            case TPM_TIS_STATUS_COMPLETION:
+                tis->loc[locty].r_offset = 0;
+                tis->loc[locty].sts = TPM_TIS_STS_VALID |
+                                      TPM_TIS_STS_DATA_AVAILABLE;
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        }
+        break;
+    case TPM_TIS_REG_DATA_FIFO:
+        /* data fifo */
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        if (tis->loc[locty].status == TPM_TIS_STATUS_IDLE ||
+            tis->loc[locty].status == TPM_TIS_STATUS_EXECUTION ||
+            tis->loc[locty].status == TPM_TIS_STATUS_COMPLETION) {
+            /* drop the byte */
+        } else {
+            dprintf("tpm_tis: Byte to send to TPM: %02x\n", (uint8_t)val);
+            if (tis->loc[locty].status == TPM_TIS_STATUS_READY) {
+                tis->loc[locty].status = TPM_TIS_STATUS_RECEPTION;
+                tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID;
+            }
+
+            if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
+                if (tis->loc[locty].w_offset < tis->loc[locty].w_buffer.size) {
+                    tis->loc[locty].w_buffer.
+                        buffer[tis->loc[locty].w_offset++] = (uint8_t)val;
+                } else {
+                    tis->loc[locty].sts = TPM_TIS_STS_VALID;
+                }
+            }
+
+            /* check for complete packet */
+            if (tis->loc[locty].w_offset > 5 &&
+                (tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
+                /* we have a packet length - see if we have all of it */
+#ifdef RAISE_STS_IRQ
+                bool needIrq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID);
+#endif
+                len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
+                if (len > tis->loc[locty].w_offset) {
+                    tis->loc[locty].sts = TPM_TIS_STS_EXPECT |
+                                          TPM_TIS_STS_VALID;
+                } else {
+                    /* packet complete */
+                    tis->loc[locty].sts = TPM_TIS_STS_VALID;
+                }
+#ifdef RAISE_STS_IRQ
+                if (needIrq) {
+                    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
+                }
+#endif
+            }
+        }
+        break;
+    }
+}
+
+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_LITTLE_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 = DO_UPCAST(TPMState, busdev.qdev, d);
+    TPMTISState *tis = &s->s.tis;
+    int c;
+
+    s->be_driver->ops->reset(s->be_driver);
+
+    tis->active_locty = TPM_TIS_NO_LOCALITY;
+    tis->next_locty = TPM_TIS_NO_LOCALITY;
+    tis->aborting_locty = TPM_TIS_NO_LOCALITY;
+
+    for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
+        tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
+        tis->loc[c].sts = 0;
+        tis->loc[c].inte = (1 << 3);
+        tis->loc[c].ints = 0;
+        tis->loc[c].status = TPM_TIS_STATUS_IDLE;
+
+        tis->loc[c].w_offset = 0;
+        s->be_driver->ops->realloc_buffer(&tis->loc[c].w_buffer);
+        tis->loc[c].r_offset = 0;
+        s->be_driver->ops->realloc_buffer(&tis->loc[c].r_buffer);
+    }
+
+    tpm_tis_do_startup_tpm(s);
+}
+
+static int tpm_tis_init(ISADevice *dev)
+{
+    TPMState *s = DO_UPCAST(TPMState, busdev, dev);
+    TPMTISState *tis = &s->s.tis;
+    int rc;
+
+    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);
+        goto err_exit;
+    }
+
+    s->be_driver->fe_model = "tpm-tis";
+
+    if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) {
+        goto err_exit;
+    }
+
+    tis->bh = qemu_bh_new(tpm_tis_receive_bh, s);
+
+    if (tis->irq_num > 15) {
+        error_report("IRQ %d for TPM TIS is outside valid range of 0 to 15.\n",
+                     tis->irq_num);
+        goto err_exit;
+    }
+
+    isa_init_irq(dev, &tis->irq, tis->irq_num);
+
+    memory_region_init_io(&s->mmio, &tpm_tis_memory_ops, s, "tpm-tis-mmio",
+                          TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT);
+    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_del_subregion(get_system_memory(), &s->mmio);
+    memory_region_destroy(&s->mmio);
+
+ err_exit:
+    return -1;
+}
+
+static const VMStateDescription vmstate_tpm_tis = {
+    .name = "tpm",
+    .unmigratable = 1,
+};
+
+static Property tpm_tis_properties[] = {
+    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_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+
+    ic->init = tpm_tis_init;
+
+    dc->props = tpm_tis_properties;
+    dc->reset = tpm_tis_reset;
+    dc->vmsd  = &vmstate_tpm_tis;
+}
+
+static TypeInfo tpm_tis_info = {
+    .name        = "tpm-tis",
+    .parent      = TYPE_ISA_DEVICE,
+    .class_init  = tpm_tis_class_initfn,
+    .instance_size = sizeof(TPMState),
+};
+
+static void tpm_tis_register(void)
+{
+    type_register_static(&tpm_tis_info);
+}
+
+type_init(tpm_tis_register)
diff --git a/hw/tpm_tis.h b/hw/tpm_tis.h
index f906390..273e421 100644
--- a/hw/tpm_tis.h
+++ b/hw/tpm_tis.h
@@ -65,6 +65,7 @@ typedef struct TPMLocality {
 } TPMLocality;
 
 typedef struct TPMTISState {
+    QEMUBH *bh;
     uint32_t offset;
     uint8_t buf[TPM_TIS_BUFFER_MAX];
 
-- 
1.7.4.4

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

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

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

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

diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
index c9bb899..0abd8f3 100644
--- a/hw/tpm_tis.c
+++ b/hw/tpm_tis.c
@@ -55,6 +55,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)
@@ -100,6 +103,11 @@
 
 #define TPM_TIS_NO_DATA_BYTE  0xff
 
+/* local prototypes */
+
+static uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
+                                  unsigned size);
+
 /* utility functions */
 
 static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
@@ -334,6 +342,63 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
     return ret;
 }
 
+#ifdef DEBUG_TIS
+static void tpm_tis_dump_state(void *opaque, target_phys_addr_t addr)
+{
+    static const unsigned regs[] = {
+        TPM_TIS_REG_ACCESS,
+        TPM_TIS_REG_INT_ENABLE,
+        TPM_TIS_REG_INT_VECTOR,
+        TPM_TIS_REG_INT_STATUS,
+        TPM_TIS_REG_INTF_CAPABILITY,
+        TPM_TIS_REG_STS,
+        TPM_TIS_REG_DID_VID,
+        TPM_TIS_REG_RID,
+        0xfff};
+    int idx;
+    uint8_t locty = tpm_tis_locality_from_addr(addr);
+    target_phys_addr_t base = addr & ~0xfff;
+    TPMState *s = opaque;
+    TPMTISState *tis = &s->s.tis;
+
+    dprintf("tpm_tis: active locality      : %d\n"
+            "tpm_tis: state of locality %d : %d\n"
+            "tpm_tis: register dump:\n",
+            tis->active_locty,
+            locty, tis->loc[locty].status);
+
+    for (idx = 0; regs[idx] != 0xfff; idx++) {
+        dprintf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
+                (uint32_t)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
+    }
+
+    dprintf("tpm_tis: read offset   : %d\n"
+            "tpm_tis: result buffer : ",
+            tis->loc[locty].r_offset);
+    for (idx = 0;
+         idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
+         idx++) {
+        dprintf("%c%02x%s",
+                tis->loc[locty].r_offset == idx ? '>' : ' ',
+                tis->loc[locty].r_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    }
+    dprintf("\n"
+            "tpm_tis: write offset  : %d\n"
+            "tpm_tis: request buffer: ",
+            tis->loc[locty].w_offset);
+    for (idx = 0;
+         idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
+         idx++) {
+        dprintf("%c%02x%s",
+                tis->loc[locty].w_offset == idx ? '>' : ' ',
+                tis->loc[locty].w_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    }
+    dprintf("\n");
+}
+#endif
+
 /*
  * Read a register of the TIS interface
  * See specs pages 33-63 for description of the registers
@@ -403,6 +468,11 @@ static uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
     case TPM_TIS_REG_RID:
         val = TPM_TIS_TPM_RID;
         break;
+#ifdef DEBUG_TIS
+    case TPM_TIS_REG_DEBUG:
+        tpm_tis_dump_state(opaque, addr);
+        break;
+#endif
     }
 
     if (shift) {
-- 
1.7.4.4

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

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

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

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

diff --git a/Makefile.target b/Makefile.target
index 44b2e83..7cb4588 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -220,6 +220,8 @@ obj-$(CONFIG_KVM) += kvm.o kvm-all.o
 obj-$(CONFIG_NO_KVM) += kvm-stub.o
 obj-$(CONFIG_VGA) += vga.o
 obj-y += memory.o savevm.o
+obj-y += tpm.o
+obj-$(CONFIG_TPM) += tpm_tis.o
 LIBS+=-lz
 
 obj-i386-$(CONFIG_KVM) += hyperv.o
diff --git a/configure b/configure
index 14ef738..c728b89 100755
--- a/configure
+++ b/configure
@@ -195,6 +195,7 @@ zlib="yes"
 guest_agent="yes"
 libiscsi=""
 coroutine=""
+tpm="no"
 
 # parse CC options first
 for opt do
@@ -825,6 +826,8 @@ for opt do
   ;;
   --disable-guest-agent) guest_agent="no"
   ;;
+  --enable-tpm) tpm="yes"
+  ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
@@ -1115,6 +1118,7 @@ echo "  --disable-guest-agent    disable building of the QEMU Guest Agent"
 echo "  --enable-guest-agent     enable building of the QEMU Guest Agent"
 echo "  --with-coroutine=BACKEND coroutine backend. Supported options:"
 echo "                           gthread, ucontext, sigaltstack, windows"
+echo "  --enable-tpm             enable TPM support"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -2954,6 +2958,7 @@ echo "OpenGL support    $opengl"
 echo "libiscsi support  $libiscsi"
 echo "build guest agent $guest_agent"
 echo "coroutine backend $coroutine_backend"
+echo "TPM support       $tpm"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3840,6 +3845,12 @@ if test "$gprof" = "yes" ; then
   fi
 fi
 
+if test "$tpm" = "yes"; then
+  if test "$target_softmmu" = "yes" ; then
+    echo "CONFIG_TPM=y" >> $config_host_mak
+  fi
+fi
+
 if test "$ARCH" = "tci"; then
   linker_script=""
 else
-- 
1.7.4.4

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

* [Qemu-devel] [PATCH V15 5/7] Add a TPM Passthrough backend driver implementation
  2012-03-27 20:24 [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (3 preceding siblings ...)
  2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 4/7] Build the TPM frontend code Stefan Berger
@ 2012-03-27 20:24 ` Stefan Berger
  2012-03-27 21:41   ` Anthony Liguori
  2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 14+ messages in thread
From: Stefan Berger @ 2012-03-27 20:24 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

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

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

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

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

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

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

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

Regards,
Andreas Niederl, Stefan Berger

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

Signed-off-by: Andreas Niederl <andreas.niederl@iaik.tugraz.at>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 Makefile.target      |    3 +-
 configure            |    3 +
 hw/tpm_backend.c     |   58 +++++++
 hw/tpm_backend.h     |   43 +++++
 hw/tpm_passthrough.c |  419 ++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-options.hx      |   37 +++++-
 tpm.c                |   17 ++
 tpm.h                |   33 ++++
 8 files changed, 611 insertions(+), 2 deletions(-)
 create mode 100644 hw/tpm_backend.c
 create mode 100644 hw/tpm_backend.h
 create mode 100644 hw/tpm_passthrough.c

diff --git a/Makefile.target b/Makefile.target
index 7cb4588..7af3593 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -221,7 +221,8 @@ obj-$(CONFIG_NO_KVM) += kvm-stub.o
 obj-$(CONFIG_VGA) += vga.o
 obj-y += memory.o savevm.o
 obj-y += tpm.o
-obj-$(CONFIG_TPM) += tpm_tis.o
+obj-$(CONFIG_TPM) += tpm_tis.o tpm_backend.o
+obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
 LIBS+=-lz
 
 obj-i386-$(CONFIG_KVM) += hyperv.o
diff --git a/configure b/configure
index c728b89..6395f06 100755
--- a/configure
+++ b/configure
@@ -3847,6 +3847,9 @@ fi
 
 if test "$tpm" = "yes"; then
   if test "$target_softmmu" = "yes" ; then
+    if test "$linux" = "yes" ; then
+      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
+    fi
     echo "CONFIG_TPM=y" >> $config_host_mak
   fi
 fi
diff --git a/hw/tpm_backend.c b/hw/tpm_backend.c
new file mode 100644
index 0000000..4cf0809
--- /dev/null
+++ b/hw/tpm_backend.c
@@ -0,0 +1,58 @@
+/*
+ *  common TPM backend driver functions
+ *
+ *  Copyright (c) 2012 IBM Corporation
+ *  Authors:
+ *    Stefan Berger <stefanb@us.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "tpm.h"
+#include "qemu-thread.h"
+#include "hw/tpm_backend.h"
+
+void tpm_backend_thread_deliver_request(TPMBackendThread *tbt)
+{
+   g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL);
+}
+
+void tpm_backend_thread_create(TPMBackendThread *tbt,
+                               GFunc func, gpointer user_data)
+{
+    if (!tbt->pool) {
+        tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL);
+        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
+    }
+}
+
+void tpm_backend_thread_end(TPMBackendThread *tbt)
+{
+    if (tbt->pool) {
+        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
+        g_thread_pool_free(tbt->pool, FALSE, TRUE);
+        tbt->pool = NULL;
+    }
+}
+
+void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt,
+                                  GFunc func, gpointer user_data)
+{
+    if (!tbt->pool) {
+        tpm_backend_thread_create(tbt, func, user_data);
+    } else {
+        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_TPM_RESET,
+                           NULL);
+    }
+}
diff --git a/hw/tpm_backend.h b/hw/tpm_backend.h
new file mode 100644
index 0000000..f5fe198
--- /dev/null
+++ b/hw/tpm_backend.h
@@ -0,0 +1,43 @@
+/*
+ *  common TPM backend driver functions
+ *
+ *  Copyright (c) 2012 IBM Corporation
+ *  Authors:
+ *    Stefan Berger <stefanb@us.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef HW_TPM_BACKEND_H
+#define HW_TPM_BACKEND_H
+
+typedef struct TPMBackendThread {
+    GThreadPool *pool;
+} TPMBackendThread;
+
+void tpm_backend_thread_deliver_request(TPMBackendThread *tbt);
+void tpm_backend_thread_create(TPMBackendThread *tbt,
+                               GFunc func, gpointer user_data);
+void tpm_backend_thread_end(TPMBackendThread *tbt);
+void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt,
+                                  GFunc func, gpointer user_data);
+
+typedef enum TPMBackendCmd {
+    TPM_BACKEND_CMD_INIT = 1,
+    TPM_BACKEND_CMD_PROCESS_CMD,
+    TPM_BACKEND_CMD_END,
+    TPM_BACKEND_CMD_TPM_RESET,
+} TPMBackendCmd;
+
+#endif /* HW_TPM_BACKEND_H */
diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
new file mode 100644
index 0000000..dee7418
--- /dev/null
+++ b/hw/tpm_passthrough.c
@@ -0,0 +1,419 @@
+/*
+ *  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/tpm_backend.h"
+#include "hw/pc.h"
+
+/* #define DEBUG_TPM */
+
+#ifdef DEBUG_TPM
+#define dprintf(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+    do { } while (0)
+#endif
+
+/* data structures */
+
+typedef struct TPMPassthruThreadParams {
+    TPMState *tpm_state;
+
+    TPMRecvDataCB *recv_data_callback;
+    TPMBackend *tb;
+} TPMPassthruThreadParams;
+
+struct TPMPassthruState {
+    QemuThread thread;
+    TPMBackendThread tbt;
+
+    TPMPassthruThreadParams tpm_thread_params;
+
+    char *tpm_dev;
+    int tpm_fd;
+    bool had_startup_error;
+};
+
+#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
+
+/* borrowed from qemu-char.c */
+static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
+{
+    int ret, len1;
+
+    len1 = len;
+    while (len1 > 0) {
+        ret = write(fd, buf, len1);
+        if (ret < 0) {
+            if (errno != EINTR && errno != EAGAIN) {
+                return -1;
+            }
+        } else if (ret == 0) {
+            break;
+        } else {
+            buf  += ret;
+            len1 -= ret;
+        }
+    }
+    return len - len1;
+}
+
+static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
+{
+    int ret, len1;
+    uint8_t *buf1;
+
+    len1 = len;
+    buf1 = buf;
+    while ((len1 > 0) && (ret = read(fd, buf1, len1)) != 0) {
+        if (ret < 0) {
+            if (errno != EINTR && errno != EAGAIN) {
+                return -1;
+            }
+        } else {
+            buf1 += ret;
+            len1 -= ret;
+        }
+    }
+    return len - len1;
+}
+
+static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
+{
+    return be32_to_cpu(*(uint32_t *)&buf[2]);
+}
+
+static int tpm_passthrough_unix_tx_bufs(int tpm_fd,
+                                        const uint8_t *in, uint32_t in_len,
+                                        uint8_t *out, uint32_t out_len)
+{
+    int ret;
+
+    ret = tpm_passthrough_unix_write(tpm_fd, in, in_len);
+    if (ret < 0) {
+        error_report("tpm_passthrough: error while transmitting data "
+                     "to TPM: %s (%i)\n",
+                     strerror(errno), errno);
+        goto err_exit;
+    }
+
+    ret = tpm_passthrough_unix_read(tpm_fd, out, out_len);
+    if (ret < 0) {
+        error_report("tpm_passthrough: error while reading data from "
+                     "TPM: %s (%i)\n",
+                     strerror(errno), errno);
+    } else if (ret < sizeof(struct tpm_resp_hdr) ||
+               tpm_passthrough_get_size_from_buffer(out) != ret) {
+        ret = -1;
+        error_report("tpm_passthrough: received invalid response "
+                     "packet from TPM\n");
+    }
+
+err_exit:
+    if (ret < 0) {
+        tpm_write_fatal_error_response(out, out_len);
+    }
+
+    return ret;
+}
+
+static int tpm_passthrough_unix_transfer(int tpm_fd,
+                                         const TPMLocality *cmd_locty)
+{
+    return tpm_passthrough_unix_tx_bufs(tpm_fd,
+                                        cmd_locty->w_buffer.buffer,
+                                        cmd_locty->w_offset,
+                                        cmd_locty->r_buffer.buffer,
+                                        cmd_locty->r_buffer.size);
+}
+
+static void tpm_passthrough_worker_thread(gpointer data,
+                                          gpointer user_data)
+{
+    TPMPassthruThreadParams *thr_parms = user_data;
+    TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
+    TPMBackendCmd cmd = (TPMBackendCmd)data;
+
+    dprintf("tpm_passthrough: processing command type %ld\n", cmd);
+
+    switch (cmd) {
+    case TPM_BACKEND_CMD_PROCESS_CMD:
+        tpm_passthrough_unix_transfer(tpm_pt->tpm_fd,
+                                      thr_parms->tpm_state->cmd_locty);
+
+        thr_parms->recv_data_callback(thr_parms->tpm_state,
+                                      thr_parms->tpm_state->command_locty);
+        break;
+    case TPM_BACKEND_CMD_INIT:
+    case TPM_BACKEND_CMD_END:
+    case TPM_BACKEND_CMD_TPM_RESET:
+        /* nothing to do */
+        break;
+    }
+}
+
+/*
+ * 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_backend_thread_end(&tpm_pt->tbt);
+
+    tpm_backend_thread_create(&tpm_pt->tbt,
+                              tpm_passthrough_worker_thread,
+                              &tb->s.tpm_pt->tpm_thread_params);
+
+    return 0;
+}
+
+static int tpm_passthrough_startup_tpm(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+    int rc;
+
+    rc = tpm_passthrough_do_startup_tpm(tb);
+    if (rc) {
+        tpm_pt->had_startup_error = true;
+    }
+    return rc;
+}
+
+static void tpm_passthrough_reset(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    dprintf("tpm_passthrough: CALL TO TPM_RESET!\n");
+
+    tpm_backend_thread_end(&tpm_pt->tbt);
+
+    tpm_pt->had_startup_error = false;
+}
+
+static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
+                                TPMRecvDataCB *recv_data_cb)
+{
+    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;
+
+    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);
+        sb->size = wanted_size;
+    }
+    return sb->size;
+}
+
+static void tpm_passthrough_deliver_request(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    tpm_backend_thread_deliver_request(&tpm_pt->tbt);
+}
+
+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;
+    size_t bufsize;
+
+    value = qemu_opt_get(opts, "path");
+    if (!value) {
+        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+    }
+
+    tb->s.tpm_pt->tpm_dev = g_strdup(value);
+
+    bufsize = sizeof("path=") + strlen(tb->s.tpm_pt->tpm_dev) + 1;
+
+    tb->parameters = g_malloc0(bufsize);
+
+    snprintf(tb->parameters, bufsize, "path=%s", tb->s.tpm_pt->tpm_dev);
+
+    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_free_parameters;
+    }
+
+    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_free_parameters:
+    g_free(tb->parameters);
+    tb->parameters = NULL;
+
+    g_free(tb->s.tpm_pt->tpm_dev);
+    tb->s.tpm_pt->tpm_dev = NULL;
+
+    return 1;
+}
+
+static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
+{
+    TPMBackend *tb;
+
+    tb = g_new0(TPMBackend, 1);
+    tb->s.tpm_pt = g_new0(TPMPassthruState, 1);
+    tb->id = g_strdup(id);
+
+    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_backend_thread_end(&tpm_pt->tbt);
+
+    close(tpm_pt->tpm_fd);
+
+    g_free(tb->id);
+    g_free(tb->parameters);
+    g_free(tb->s.tpm_pt->tpm_dev);
+    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,
+    .deliver_request          = tpm_passthrough_deliver_request,
+    .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
+};
diff --git a/qemu-options.hx b/qemu-options.hx
index 355a9c1..c8fbe07 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2006,7 +2006,9 @@ DEFHEADING()
 DEFHEADING(TPM device options:)
 
 DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
-    "-tpmdev [<type>],id=str[,option][,option][,...]\n",
+    "-tpmdev [passthrough],id=id[,option][,option][,...]\n"
+    "-tpmdev passthrough,id=id[,path=path]\n"
+    "                use path to provide path to a character device; default is /dev/tpm0\n",
     QEMU_ARCH_ALL)
 STEXI
 
@@ -2016,6 +2018,7 @@ The general form of a TPM device option is:
 @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
 @findex -tpmdev
 Backend type must be:
+@option{passthrough}.
 
 The specific backend type will determine the applicable options.
 The @code{-tpmdev} options requires a @code{-device} option.
@@ -2027,6 +2030,38 @@ Use ? to print all available TPM backend types.
 qemu -tpmdev ?
 @end example
 
+@item -tpmdev passthrough, id=@var{id}, path=@var{path}
+
+(Linux-host only) Enable access to the host's TPM using the passthrough
+driver.
+
+@option{path} specifies the path to the host's TPM device, i.e., on
+a Linux host this would be @code{/dev/tpm0}.
+@option{path} is optional and by default @code{/dev/tpm0} is used.
+
+Some notes about using the host's TPM with the passthrough driver:
+
+The TPM device accessed by the passthrough driver must not be
+used by any other application on the host.
+
+Since the host's firmware (BIOS/UEFI) has already initialized the TPM,
+the VM's firmware (BIOS/UEFI) will not be able to initialize the
+TPM again and may therefore not show a TPM-specific menu that would
+otherwise allow the user to configure the TPM, e.g., allow the user to
+enable/disable or activate/deactivate the TPM.
+Further, if TPM ownership is released from within a VM then the host's TPM
+will get disabled and deactivated. To enable and activate the
+TPM again afterwards, the host has to be rebooted and the user is
+required to enter the firmware's menu to enable and activate the TPM.
+If the TPM is left disabled and/or deactivated most TPM commands will fail.
+
+To create a passthrough TPM use the following two options:
+@example
+-tpmdev passthrough,id=tpm0 -device tpm-tis,tpmdev=tpm0
+@end example
+Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
+@code{tpmdev=tpm0} in the device option.
+
 @end table
 
 ETEXI
diff --git a/tpm.c b/tpm.c
index 6324c1e..e285eaa 100644
--- a/tpm.c
+++ b/tpm.c
@@ -24,9 +24,26 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =
 #ifdef CONFIG_TPM
 
 static const TPMDriverOps *bes[] = {
+#ifdef CONFIG_TPM_PASSTHROUGH
+    &tpm_passthrough_driver,
+#endif
     NULL,
 };
 
+/*
+ * Write an error message in the given output buffer.
+ */
+void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
+{
+    if (out_len >= sizeof(struct tpm_resp_hdr)) {
+        struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
+
+        resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
+        resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
+        resp->errcode = cpu_to_be32(TPM_FAIL);
+    }
+}
+
 const TPMDriverOps *tpm_get_backend_driver(const char *id)
 {
     int i;
diff --git a/tpm.h b/tpm.h
index 4e871d9..c7de642 100644
--- a/tpm.h
+++ b/tpm.h
@@ -18,12 +18,18 @@
 struct TPMDriverOps;
 typedef struct TPMDriverOps TPMDriverOps;
 
+typedef struct TPMPassthruState TPMPassthruState;
+
 typedef struct TPMBackend {
     char *id;
     const char *fe_model;
     char *parameters;
     const TPMDriverOps *ops;
 
+    union {
+        TPMPassthruState *tpm_pt;
+    } s;
+
     QLIST_ENTRY(TPMBackend) list;
 } TPMBackend;
 
@@ -71,11 +77,38 @@ struct TPMDriverOps {
 
 #define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
 
+struct tpm_req_hdr {
+    uint16_t tag;
+    uint32_t len;
+    uint32_t ordinal;
+} __attribute__((packed));
+
+struct tpm_resp_hdr {
+    uint16_t tag;
+    uint32_t len;
+    uint32_t errcode;
+} __attribute__((packed));
+
+#define TPM_TAG_RQU_COMMAND       0xc1
+#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2
+#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3
+
+#define TPM_TAG_RSP_COMMAND       0xc4
+#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5
+#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6
+
+#define TPM_FAIL                  9
+
+#define TPM_ORD_GetTicks          0xf1
+
 int tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
 int tpm_init(void);
 void tpm_cleanup(void);
 TPMBackend *qemu_find_tpm(const char *id);
 void tpm_display_backend_drivers(void);
 const TPMDriverOps *tpm_get_backend_driver(const char *id);
+void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len);
+
+extern const TPMDriverOps tpm_passthrough_driver;
 
 #endif /* QEMU_TPM_H */
-- 
1.7.4.4

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

* [Qemu-devel] [PATCH V15 6/7] Introduce --enable-tpm-passthrough configure option
  2012-03-27 20:24 [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (4 preceding siblings ...)
  2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2012-03-27 20:24 ` Stefan Berger
  2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
  2012-03-27 21:43 ` [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Anthony Liguori
  7 siblings, 0 replies; 14+ messages in thread
From: Stefan Berger @ 2012-03-27 20:24 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

Introduce --enable-tpm-passthrough configure option.

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

diff --git a/configure b/configure
index 6395f06..e6d198a 100755
--- a/configure
+++ b/configure
@@ -196,6 +196,7 @@ guest_agent="yes"
 libiscsi=""
 coroutine=""
 tpm="no"
+tpm_passthrough="no"
 
 # parse CC options first
 for opt do
@@ -828,11 +829,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)
@@ -1119,6 +1129,7 @@ echo "  --enable-guest-agent     enable building of the QEMU Guest Agent"
 echo "  --with-coroutine=BACKEND coroutine backend. Supported options:"
 echo "                           gthread, ucontext, sigaltstack, windows"
 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
@@ -2959,6 +2970,7 @@ echo "libiscsi support  $libiscsi"
 echo "build guest agent $guest_agent"
 echo "coroutine backend $coroutine_backend"
 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"
@@ -3848,7 +3860,9 @@ fi
 if test "$tpm" = "yes"; then
   if test "$target_softmmu" = "yes" ; then
     if test "$linux" = "yes" ; then
-      echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
+      if test "$tpm_passthrough" = "yes" ; then
+        echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
+      fi
     fi
     echo "CONFIG_TPM=y" >> $config_host_mak
   fi
-- 
1.7.4.4

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

* [Qemu-devel] [PATCH V15 7/7] Add fd parameter for TPM passthrough driver
  2012-03-27 20:24 [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (5 preceding siblings ...)
  2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
@ 2012-03-27 20:24 ` Stefan Berger
  2012-03-27 21:43 ` [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Anthony Liguori
  7 siblings, 0 replies; 14+ messages in thread
From: Stefan Berger @ 2012-03-27 20:24 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst

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

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

diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
index dee7418..fa3c99b 100644
--- a/hw/tpm_passthrough.c
+++ b/hw/tpm_passthrough.c
@@ -322,30 +322,62 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
 {
     const char *value;
     size_t bufsize;
+    struct stat statbuf;
 
-    value = qemu_opt_get(opts, "path");
-    if (!value) {
-        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
-    }
+    value = qemu_opt_get(opts, "fd");
+    if (value) {
+        if (qemu_opt_get(opts, "path")) {
+            error_report("fd= is invalid with path=");
+            goto err_exit;
+        }
+
+        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");
+            goto err_exit;
+        }
+
+        bufsize = sizeof("fd=") + sizeof(stringify(INT_MAX)) + 1;
+
+        tb->parameters = g_malloc0(bufsize);
+
+        snprintf(tb->parameters, bufsize, "fd=%d", tb->s.tpm_pt->tpm_fd);
+    } else {
+        value = qemu_opt_get(opts, "path");
+        if (!value) {
+            value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+        }
+
+        tb->s.tpm_pt->tpm_dev = g_strdup(value);
 
-    tb->s.tpm_pt->tpm_dev = g_strdup(value);
+        bufsize = sizeof("path=") + strlen(tb->s.tpm_pt->tpm_dev) + 1;
 
-    bufsize = sizeof("path=") + strlen(tb->s.tpm_pt->tpm_dev) + 1;
+        tb->parameters = g_malloc0(bufsize);
 
-    tb->parameters = g_malloc0(bufsize);
+        snprintf(tb->parameters, bufsize, "path=%s", tb->s.tpm_pt->tpm_dev);
 
-    snprintf(tb->parameters, bufsize, "path=%s", tb->s.tpm_pt->tpm_dev);
+        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_free_parameters;
+        }
+    }
+
+    if (fstat(tb->s.tpm_pt->tpm_fd, &statbuf) != 0) {
+        error_report("Cannot determine file descriptor type for TPM "
+                     "device: %s", strerror(errno));
+        goto err_close_tpmdev;
+    }
 
-    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);
+    /* only allow character devices for now */
+    if (!S_ISCHR(statbuf.st_mode)) {
+        error_report("TPM file descriptor is not a character device");
         goto err_free_parameters;
     }
 
     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);
+        error_report("Device is not a TPM.\n");
         goto err_close_tpmdev;
     }
 
@@ -362,6 +394,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
     g_free(tb->s.tpm_pt->tpm_dev);
     tb->s.tpm_pt->tpm_dev = NULL;
 
+ err_exit:
     return 1;
 }
 
diff --git a/qemu-config.c b/qemu-config.c
index edc8d5d..a5e2677 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -628,6 +628,11 @@ static QemuOptsList qemu_tpmdev_opts = {
             .type = QEMU_OPT_STRING,
             .help = "Persistent storage for TPM state",
         },
+        {
+            .name = "fd",
+            .type = QEMU_OPT_STRING,
+            .help = "Filedescriptor for accessing the TPM",
+        },
         { /* end of list */ }
     },
 };
diff --git a/qemu-options.hx b/qemu-options.hx
index c8fbe07..4b262fb 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2007,8 +2007,9 @@ DEFHEADING(TPM device options:)
 
 DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
     "-tpmdev [passthrough],id=id[,option][,option][,...]\n"
-    "-tpmdev passthrough,id=id[,path=path]\n"
-    "                use path to provide path to a character device; default is /dev/tpm0\n",
+    "-tpmdev passthrough,id=id[,path=path][,fd=h]\n"
+    "                use path to provide path to a character device; default is /dev/tpm0\n"
+    "                use fd to provide a file descriptor to a character device\n",
     QEMU_ARCH_ALL)
 STEXI
 
@@ -2030,7 +2031,7 @@ Use ? to print all available TPM backend types.
 qemu -tpmdev ?
 @end example
 
-@item -tpmdev passthrough, id=@var{id}, path=@var{path}
+@item -tpmdev passthrough, id=@var{id}, path=@var{path}, fd=@var{h}
 
 (Linux-host only) Enable access to the host's TPM using the passthrough
 driver.
@@ -2039,6 +2040,10 @@ driver.
 a Linux host this would be @code{/dev/tpm0}.
 @option{path} is optional and by default @code{/dev/tpm0} is used.
 
+@option{fd} specifies the file descriptor of the host's TPM device.
+@option{fd} and @option{path} are mutually exclusive.
+@option{fd} is optional.
+
 Some notes about using the host's TPM with the passthrough driver:
 
 The TPM device accessed by the passthrough driver must not be
-- 
1.7.4.4

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

* Re: [Qemu-devel] [PATCH V15 1/7] Support for TPM command line options
  2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 1/7] Support for TPM command line options Stefan Berger
@ 2012-03-27 21:35   ` Anthony Liguori
  2012-03-28 15:24     ` Stefan Berger
  0 siblings, 1 reply; 14+ messages in thread
From: Anthony Liguori @ 2012-03-27 21:35 UTC (permalink / raw)
  To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl

On 03/27/2012 03:24 PM, Stefan Berger wrote:
> This patch adds support for TPM command line options.
> The command line options supported here are
>
> ./qemu-... -tpmdev passthrough,path=<path to TPM device>,id=<id>
>             -device tpm-tis,tpmdev=<id>
>
> and
>
> ./qemu-... -tpmdev ?
>
> where the latter works similar to -soundhw ? and shows a list of
> available TPM backends (for example 'passthrough').
>
> Using the type parameter, the backend is chosen, i.e., 'passthrough' for the
> passthrough driver. The interpretation of the other parameters along
> with determining whether enough parameters were provided is pushed into
> the backend driver, which needs to implement the interface function
> 'create' and return a TPMDriver structure if the VM can be started or 'NULL'
> if not enough or bad parameters were provided.
>
> Monitor support for 'info tpm' has been added. It for example prints the
> following:
>
> (qemu) info tpm
> TPM devices:
>   tpm0: model=tpm-tis
>    \ tpm0: type=passthrough,path=/dev/tpm0
>
> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
> ---
>   hmp-commands.hx  |    2 +
>   hmp.c            |   28 +++++++
>   hmp.h            |    1 +
>   hw/tpm_tis.h     |   81 +++++++++++++++++++++
>   monitor.c        |    8 ++
>   qapi-schema.json |   29 ++++++++
>   qemu-config.c    |   20 +++++
>   qemu-options.hx  |   30 ++++++++
>   tpm.c            |  213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   tpm.h            |   81 +++++++++++++++++++++
>   vl.c             |   20 +++++
>   11 files changed, 513 insertions(+), 0 deletions(-)
>   create mode 100644 hw/tpm_tis.h
>   create mode 100644 tpm.c
>   create mode 100644 tpm.h
>
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index bd35a3e..7bbb5b8 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -1402,6 +1402,8 @@ show device tree
>   show qdev device model list
>   @item info roms
>   show roms
> +@item info tpm
> +show the TPM device
>   @end table
>   ETEXI
>
> diff --git a/hmp.c b/hmp.c
> index 9cf2d13..bac6b5a 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -546,6 +546,34 @@ void hmp_info_block_jobs(Monitor *mon)
>       }
>   }
>
> +void hmp_info_tpm(Monitor *mon)
> +{
> +    TPMInfoList *info_list, *info;
> +    Error *err = NULL;
> +    unsigned int c = 0;
> +
> +    info_list = qmp_query_tpm(&err);
> +    if (err) {
> +        monitor_printf(mon, "TPM device not supported\n");
> +        error_free(err);
> +        return;
> +    }
> +
> +    monitor_printf(mon, "TPM device:\n");
> +
> +    for (info = info_list; info; info = info->next) {
> +        TPMInfo *ti = info->value;
> +        monitor_printf(mon, " tpm%d: model=%s\n",
> +                       c, ti->model);
> +        monitor_printf(mon, "  \\ %s: type=%s%s%s\n",
> +                       ti->id, ti->type,
> +                       ti->parameters ? "," : "",
> +                       ti->parameters ? ti->parameters : "");
> +        c++;
> +    }
> +    qapi_free_TPMInfoList(info_list);
> +}
> +
>   void hmp_quit(Monitor *mon, const QDict *qdict)
>   {
>       monitor_suspend(mon);
> diff --git a/hmp.h b/hmp.h
> index 8807853..4884739 100644
> --- a/hmp.h
> +++ b/hmp.h
> @@ -33,6 +33,7 @@ void hmp_info_spice(Monitor *mon);
>   void hmp_info_balloon(Monitor *mon);
>   void hmp_info_pci(Monitor *mon);
>   void hmp_info_block_jobs(Monitor *mon);
> +void hmp_info_tpm(Monitor *mon);
>   void hmp_quit(Monitor *mon, const QDict *qdict);
>   void hmp_stop(Monitor *mon, const QDict *qdict);
>   void hmp_system_reset(Monitor *mon, const QDict *qdict);
> diff --git a/hw/tpm_tis.h b/hw/tpm_tis.h
> new file mode 100644
> index 0000000..f906390
> --- /dev/null
> +++ b/hw/tpm_tis.h
> @@ -0,0 +1,81 @@
> +/*
> + * 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.

Or later please.  We're sticking with GPLv2 as our effective license but asking 
that all new code is GPLv2 or later.

> + *
> + *
> + * 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"

This shouldn't be needed anymore.

> +#include "qemu-common.h"
> +
> +#define TPM_TIS_ADDR_BASE           0xFED40000
> +
> +#define TPM_TIS_NUM_LOCALITIES      5     /* per spec */
> +#define TPM_TIS_LOCALITY_SHIFT      12
> +#define TPM_TIS_NO_LOCALITY         0xff
> +
> +#define TPM_TIS_IS_VALID_LOCTY(x)   ((x)<  TPM_TIS_NUM_LOCALITIES)
> +
> +#define TPM_TIS_IRQ                 5
> +
> +#define TPM_TIS_BUFFER_MAX          4096
> +
> +
> +typedef struct TPMSizedBuffer {
> +    uint32_t size;
> +    uint8_t  *buffer;
> +} TPMSizedBuffer;
> +
> +typedef enum {
> +    TPM_TIS_STATUS_IDLE = 0,
> +    TPM_TIS_STATUS_READY,
> +    TPM_TIS_STATUS_COMPLETION,
> +    TPM_TIS_STATUS_EXECUTION,
> +    TPM_TIS_STATUS_RECEPTION,
> +} TPMTISStatus;
> +
> +/* locality data  -- all fields are persisted */
> +typedef struct TPMLocality {
> +    TPMTISStatus status;
> +    uint8_t access;
> +    uint8_t sts;
> +    uint32_t inte;
> +    uint32_t ints;
> +
> +    uint16_t w_offset;
> +    uint16_t r_offset;
> +    TPMSizedBuffer w_buffer;
> +    TPMSizedBuffer r_buffer;
> +} TPMLocality;
> +
> +typedef struct TPMTISState {
> +    uint32_t offset;
> +    uint8_t buf[TPM_TIS_BUFFER_MAX];
> +
> +    uint8_t active_locty;
> +    uint8_t aborting_locty;
> +    uint8_t next_locty;
> +
> +    TPMLocality loc[TPM_TIS_NUM_LOCALITIES];
> +
> +    qemu_irq irq;
> +    uint32_t irq_num;

I'm a bit confused here.  If TPMTISState has an irq, shouldn't it be a DeviceState?

> +} TPMTISState;
> +
> +#endif /* HW_TPM_TIS_H */
> diff --git a/monitor.c b/monitor.c
> index 2ff1e0b..427992b 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -47,6 +47,7 @@
>   #include "migration.h"
>   #include "kvm.h"
>   #include "acl.h"
> +#include "tpm.h"
>   #include "qint.h"
>   #include "qfloat.h"
>   #include "qlist.h"
> @@ -2602,6 +2603,13 @@ static mon_cmd_t info_cmds[] = {
>           .mhandler.info = do_trace_print_events,
>       },
>       {
> +        .name       = "tpm",
> +        .args_type  = "",
> +        .params     = "",
> +        .help       = "show the TPM device",
> +        .mhandler.info = hmp_info_tpm,
> +    },
> +    {
>           .name       = NULL,
>       },
>   };
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 0d11d6e..4ad6d29 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1701,3 +1701,32 @@
>   # Since: 1.1
>   ##
>   { 'command': 'xen-save-devices-state', 'data': {'filename': 'str'} }
> +
> +##
> +# @TPMInfo:
> +#
> +# Information about the TPM
> +#
> +# @model: The TPM frontend model, i.e., tpm-tis
> +#
> +# @id: The ID of the TPM
> +#
> +# @type: The type of TPM backend, i.e., passthrough
> +#
> +# @parameters: Additional parameters of the TPM backend device

This is in some sort of key=value format?  Why not specify those parameters 
properly in the schema as optional items?

> +# Since: 1.1
> +##
> +{ 'type': 'TPMInfo',
> +  'data': {'model': 'str', 'id': 'str', 'type': 'str', 'parameters': 'str' } }
> +
> +##
> +# @query-tpm
> +#
> +# Return information about the TPM device.
> +#
> +# Returns: @TPMInfo on success
> +#
> +# Since: 1.1
> +##
> +{ 'command': 'query-tpm', 'returns': ['TPMInfo'] }
> diff --git a/qemu-config.c b/qemu-config.c
> index be84a03..edc8d5d 100644
> --- a/qemu-config.c
> +++ b/qemu-config.c
> @@ -613,6 +613,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,
> @@ -628,6 +647,7 @@ static QemuOptsList *vm_config_groups[32] = {
>       &qemu_machine_opts,
>       &qemu_boot_opts,
>       &qemu_iscsi_opts,
> +&qemu_tpmdev_opts,
>       NULL,
>   };
>
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 662f571..355a9c1 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -2003,6 +2003,36 @@ ETEXI
>
>   DEFHEADING()
>
> +DEFHEADING(TPM device options:)
> +
> +DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
> +    "-tpmdev [<type>],id=str[,option][,option][,...]\n",
> +    QEMU_ARCH_ALL)
> +STEXI
> +
> +The general form of a TPM device option is:
> +@table @option
> +
> +@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
> +@findex -tpmdev
> +Backend type must be:
> +
> +The specific backend type will determine the applicable options.
> +The @code{-tpmdev} options requires a @code{-device} option.
> +
> +Options to each backend are described below.
> +
> +Use ? to print all available TPM backend types.
> +@example
> +qemu -tpmdev ?
> +@end example
> +
> +@end table
> +
> +ETEXI
> +
> +DEFHEADING()
> +
>   DEFHEADING(Linux/Multiboot boot specific:)
>   STEXI
>
> diff --git a/tpm.c b/tpm.c
> new file mode 100644
> index 0000000..6324c1e
> --- /dev/null
> +++ b/tpm.c
> @@ -0,0 +1,213 @@
> +/*
> + * TPM configuration
> + *
> + * Copyright (C) 2011 IBM Corporation
> + *
> + * Authors:
> + *  Stefan Berger<stefanb@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + * Based on net.c
> + */
> +#include "config.h"
> +
> +#include "monitor.h"
> +#include "qerror.h"
> +#include "tpm.h"
> +#include "qmp-commands.h"
> +
> +static QLIST_HEAD(, TPMBackend) tpm_backends =
> +    QLIST_HEAD_INITIALIZER(tpm_backends);
> +
> +#ifdef CONFIG_TPM
> +
> +static const TPMDriverOps *bes[] = {
> +    NULL,
> +};
> +
> +const TPMDriverOps *tpm_get_backend_driver(const char *id)
> +{
> +    int i;
> +
> +    for (i = 0; bes[i] != NULL; i++) {
> +        if (!strcmp(bes[i]->id, id)) {
> +            break;
> +        }
> +    }
> +
> +    return bes[i];
> +}
> +
> +/*
> + * Walk the list of available TPM backend drivers and display them on the
> + * screen.
> + */
> +void tpm_display_backend_drivers(void)
> +{
> +    int i;
> +
> +    fprintf(stderr, "Supported TPM types (choose only one):\n");
> +
> +    for (i = 0; bes[i] != NULL; i++) {
> +        fprintf(stderr, "%12s   %s\n", bes[i]->id, bes[i]->desc());
> +    }
> +    fprintf(stderr, "\n");
> +}
> +
> +/*
> + * Find the TPM with the given Id
> + */
> +TPMBackend *qemu_find_tpm(const char *id)
> +{
> +    TPMBackend *drv;
> +
> +    QLIST_FOREACH(drv,&tpm_backends, list) {
> +        if (!strcmp(drv->id, id)) {
> +            return drv;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +static int configure_tpm(QemuOpts *opts)
> +{
> +    const char *value;
> +    const char *id;
> +    const TPMDriverOps *be;
> +    TPMBackend *drv;
> +
> +    if (!QLIST_EMPTY(&tpm_backends)) {
> +        error_report("Only one TPM is allowed.\n");
> +        return 1;
> +    }
> +
> +    id = qemu_opts_id(opts);
> +    if (id == NULL) {
> +        qerror_report(QERR_MISSING_PARAMETER, "id");
> +        return 1;
> +    }
> +
> +    value = qemu_opt_get(opts, "type");
> +    if (!value) {
> +        qerror_report(QERR_MISSING_PARAMETER, "type");
> +        tpm_display_backend_drivers();
> +        return 1;
> +    }
> +
> +    be = tpm_get_backend_driver(value);
> +    if (be == NULL) {
> +        qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
> +                      "a TPM backend type");
> +        tpm_display_backend_drivers();
> +        return 1;
> +    }
> +
> +    drv = be->create(opts, id);
> +    if (!drv) {
> +        return 1;
> +    }
> +
> +    QLIST_INSERT_HEAD(&tpm_backends, drv, list);
> +
> +    return 0;
> +}
> +
> +static int tpm_init_tpmdev(QemuOpts *opts, void *dummy)
> +{
> +    return configure_tpm(opts);
> +}
> +
> +/*
> + * Walk the list of TPM backend drivers that are in use and call their
> + * destroy function to have them cleaned up.
> + */
> +void tpm_cleanup(void)
> +{
> +    TPMBackend *drv, *next;
> +
> +    QLIST_FOREACH_SAFE(drv,&tpm_backends, list, next) {
> +        QLIST_REMOVE(drv, list);
> +        drv->ops->destroy(drv);
> +    }
> +}
> +
> +/*
> + * Initialize the TPM. Process the tpmdev command line options describing the
> + * TPM backend.
> + */
> +int tpm_init(void)
> +{
> +    if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
> +                          tpm_init_tpmdev, NULL, 1) != 0) {
> +        return -1;
> +    }
> +
> +    atexit(tpm_cleanup);
> +
> +    return 0;
> +}
> +
> +/*
> + * Parse the TPM configuration options.
> + * It is possible to pass an option '-tpmdev none' to not activate any TPM.
> + * To display all available TPM backends the user may use '-tpmdev ?'
> + */
> +int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
> +{
> +    QemuOpts *opts;
> +
> +    if (strcmp("none", optarg) != 0) {
> +        if (*optarg == '?') {
> +            tpm_display_backend_drivers();
> +            return -1;
> +        }
> +        opts = qemu_opts_parse(opts_list, optarg, 1);
> +        if (!opts) {
> +            return -1;
> +        }
> +    }
> +    return 0;
> +}
> +
> +#endif /* CONFIG_TPM */
> +
> +static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
> +{
> +    TPMInfo *res = g_new0(TPMInfo, 1);
> +
> +    res->model = g_strdup(drv->fe_model);
> +    res->id = g_strdup(drv->id);
> +    if (drv->parameters) {
> +        res->parameters = g_strdup(drv->parameters);
> +    }
> +    res->type = g_strdup(drv->ops->id);
> +
> +    return res;
> +}
> +
> +/*
> + * Walk the list of active TPM backends and collect information about them
> + * following the schema description in qapi-schema.json.
> + */
> +TPMInfoList *qmp_query_tpm(Error **errp)
> +{
> +    TPMBackend *drv;
> +    TPMInfoList *info, *head = NULL, *cur_item = NULL;
> +
> +    QLIST_FOREACH(drv,&tpm_backends, list) {
> +        info = g_new0(TPMInfoList, 1);
> +        info->value = qmp_query_tpm_inst(drv);
> +
> +        if (!cur_item) {
> +            head = cur_item = info;
> +        } else {
> +            cur_item->next = info;
> +            cur_item = info;
> +        }
> +    }
> +
> +    return head;
> +}
> diff --git a/tpm.h b/tpm.h
> new file mode 100644
> index 0000000..4e871d9
> --- /dev/null
> +++ b/tpm.h
> @@ -0,0 +1,81 @@
> +/*
> + * TPM configuration
> + *
> + * Copyright (C) 2011 IBM Corporation
> + *
> + * Authors:
> + *  Stefan Berger<stefanb@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +#ifndef QEMU_TPM_H
> +#define QEMU_TPM_H
> +
> +#include "memory.h"
> +#include "hw/tpm_tis.h"
> +
> +struct TPMDriverOps;
> +typedef struct TPMDriverOps TPMDriverOps;
> +
> +typedef struct TPMBackend {
> +    char *id;
> +    const char *fe_model;
> +    char *parameters;
> +    const TPMDriverOps *ops;
> +
> +    QLIST_ENTRY(TPMBackend) list;
> +} TPMBackend;
> +
> +/* overall state of the TPM interface */
> +typedef struct TPMState {
> +    ISADevice busdev;
> +    MemoryRegion mmio;
> +
> +    union {
> +        TPMTISState tis;
> +    } s;
> +
> +    uint8_t     command_locty;
> +    TPMLocality *cmd_locty;
> +
> +    char *backend;
> +    TPMBackend *be_driver;
> +} TPMState;

I'm a bit confused at what the relationship between TPMTISState and TPMSTate.

Regards,

Anthony Liguori

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

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

On 03/27/2012 03:24 PM, Stefan Berger wrote:
>  From Andreas Niederl's original posting with adaptations where necessary:
>
> This patch is based of off version 9 of Stefan Berger's patch series
>    "Qemu Trusted Platform Module (TPM) integration"
> and adds a new backend driver for it.
>
> This patch adds a passthrough backend driver for passing commands sent to the
> emulated TPM device directly to a TPM device opened on the host machine.
>
> Thus it is possible to use a hardware TPM device in a system running on QEMU,
> providing the ability to access a TPM in a special state (e.g. after a Trusted
> Boot).
>
> This functionality is being used in the acTvSM Trusted Virtualization Platform
> which is available on [1].
>
> Usage example:
>    qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
>                       -device tpm-tis,tpmdev=tpm0 \
>                       -cdrom test.iso -boot d
>
> Some notes about the host TPM:
> The TPM needs to be enabled and activated. If that's not the case one
> has to go through the BIOS/UEFI and enable and activate that TPM for TPM
> commands to work as expected.
> It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
> command line or 'modprobe tpm_tis force=1' in case of using it as a module.
>
> Regards,
> Andreas Niederl, Stefan Berger
>
> [1] http://trustedjava.sourceforge.net/
>
> Signed-off-by: Andreas Niederl<andreas.niederl@iaik.tugraz.at>
> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
> ---
>   Makefile.target      |    3 +-
>   configure            |    3 +
>   hw/tpm_backend.c     |   58 +++++++
>   hw/tpm_backend.h     |   43 +++++
>   hw/tpm_passthrough.c |  419 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   qemu-options.hx      |   37 +++++-
>   tpm.c                |   17 ++
>   tpm.h                |   33 ++++
>   8 files changed, 611 insertions(+), 2 deletions(-)
>   create mode 100644 hw/tpm_backend.c
>   create mode 100644 hw/tpm_backend.h
>   create mode 100644 hw/tpm_passthrough.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 7cb4588..7af3593 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -221,7 +221,8 @@ obj-$(CONFIG_NO_KVM) += kvm-stub.o
>   obj-$(CONFIG_VGA) += vga.o
>   obj-y += memory.o savevm.o
>   obj-y += tpm.o
> -obj-$(CONFIG_TPM) += tpm_tis.o
> +obj-$(CONFIG_TPM) += tpm_tis.o tpm_backend.o
> +obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
>   LIBS+=-lz
>
>   obj-i386-$(CONFIG_KVM) += hyperv.o
> diff --git a/configure b/configure
> index c728b89..6395f06 100755
> --- a/configure
> +++ b/configure
> @@ -3847,6 +3847,9 @@ fi
>
>   if test "$tpm" = "yes"; then
>     if test "$target_softmmu" = "yes" ; then
> +    if test "$linux" = "yes" ; then
> +      echo "CONFIG_TPM_PASSTHROUGH=y">>  $config_target_mak
> +    fi
>       echo "CONFIG_TPM=y">>  $config_host_mak
>     fi
>   fi
> diff --git a/hw/tpm_backend.c b/hw/tpm_backend.c
> new file mode 100644
> index 0000000..4cf0809
> --- /dev/null
> +++ b/hw/tpm_backend.c
> @@ -0,0 +1,58 @@
> +/*
> + *  common TPM backend driver functions
> + *
> + *  Copyright (c) 2012 IBM Corporation
> + *  Authors:
> + *    Stefan Berger<stefanb@us.ibm.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see<http://www.gnu.org/licenses/>
> + */
> +
> +#include "tpm.h"
> +#include "qemu-thread.h"
> +#include "hw/tpm_backend.h"
> +
> +void tpm_backend_thread_deliver_request(TPMBackendThread *tbt)
> +{
> +   g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL);
> +}
> +
> +void tpm_backend_thread_create(TPMBackendThread *tbt,
> +                               GFunc func, gpointer user_data)
> +{
> +    if (!tbt->pool) {
> +        tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL);
> +        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
> +    }
> +}
> +
> +void tpm_backend_thread_end(TPMBackendThread *tbt)
> +{
> +    if (tbt->pool) {
> +        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
> +        g_thread_pool_free(tbt->pool, FALSE, TRUE);
> +        tbt->pool = NULL;
> +    }
> +}
> +
> +void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt,
> +                                  GFunc func, gpointer user_data)
> +{
> +    if (!tbt->pool) {
> +        tpm_backend_thread_create(tbt, func, user_data);
> +    } else {
> +        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_TPM_RESET,
> +                           NULL);
> +    }
> +}
> diff --git a/hw/tpm_backend.h b/hw/tpm_backend.h
> new file mode 100644
> index 0000000..f5fe198
> --- /dev/null
> +++ b/hw/tpm_backend.h
> @@ -0,0 +1,43 @@
> +/*
> + *  common TPM backend driver functions
> + *
> + *  Copyright (c) 2012 IBM Corporation
> + *  Authors:
> + *    Stefan Berger<stefanb@us.ibm.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see<http://www.gnu.org/licenses/>
> + */
> +
> +#ifndef HW_TPM_BACKEND_H
> +#define HW_TPM_BACKEND_H
> +
> +typedef struct TPMBackendThread {
> +    GThreadPool *pool;
> +} TPMBackendThread;
> +
> +void tpm_backend_thread_deliver_request(TPMBackendThread *tbt);
> +void tpm_backend_thread_create(TPMBackendThread *tbt,
> +                               GFunc func, gpointer user_data);
> +void tpm_backend_thread_end(TPMBackendThread *tbt);
> +void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt,
> +                                  GFunc func, gpointer user_data);
> +
> +typedef enum TPMBackendCmd {
> +    TPM_BACKEND_CMD_INIT = 1,
> +    TPM_BACKEND_CMD_PROCESS_CMD,
> +    TPM_BACKEND_CMD_END,
> +    TPM_BACKEND_CMD_TPM_RESET,
> +} TPMBackendCmd;
> +
> +#endif /* HW_TPM_BACKEND_H */
> diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
> new file mode 100644
> index 0000000..dee7418
> --- /dev/null
> +++ b/hw/tpm_passthrough.c
> @@ -0,0 +1,419 @@
> +/*
> + *  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/tpm_backend.h"
> +#include "hw/pc.h"
> +
> +/* #define DEBUG_TPM */
> +
> +#ifdef DEBUG_TPM
> +#define dprintf(fmt, ...) \
> +    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define dprintf(fmt, ...) \
> +    do { } while (0)
> +#endif
> +
> +/* data structures */
> +
> +typedef struct TPMPassthruThreadParams {
> +    TPMState *tpm_state;
> +
> +    TPMRecvDataCB *recv_data_callback;
> +    TPMBackend *tb;
> +} TPMPassthruThreadParams;
> +
> +struct TPMPassthruState {
> +    QemuThread thread;
> +    TPMBackendThread tbt;
> +
> +    TPMPassthruThreadParams tpm_thread_params;
> +
> +    char *tpm_dev;
> +    int tpm_fd;
> +    bool had_startup_error;
> +};
> +
> +#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
> +
> +/* borrowed from qemu-char.c */

Sharing code is better than borrowing code :-)

Regards,

Anthony Liguori

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

* Re: [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration
  2012-03-27 20:24 [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (6 preceding siblings ...)
  2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
@ 2012-03-27 21:43 ` Anthony Liguori
  2012-03-28 17:46   ` Stefan Berger
  7 siblings, 1 reply; 14+ messages in thread
From: Anthony Liguori @ 2012-03-27 21:43 UTC (permalink / raw)
  To: Stefan Berger; +Cc: Michael S. Tsirkin, qemu-devel, andreas.niederl

On 03/27/2012 03:24 PM, 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.

I haven't done a thorough review, but overall, I'm pretty happy with how this 
looks now.

I think this is more or less ready to merge once it's been thoroughly reviewed.

Thanks for doing all of this work, I'm very happy with the end result!

Regards,

Anthony Liguori

>
> v15:
>   - applies to checkout of 8a22565 (Mar 27)
>   - replacing g_malloc's with g_new; no more checks for NULL after allocs
>   - introducing usage of bottom half in TIS frontend to deliver responses
>   - get rid of locks since global lock is held by all threads entering TIS
>     code
>   - cleanups
>
> v14:
>   - applies to checkout of da5361c (Dec 12)
>   - implemented Anthony Liguori's suggestions
>   - dropping the version log on individual patches
>
> v13:
>   - applies to checkout of 61a5872 (Dec 12)
>   - only allowing character devices as fd parameter
>   - fixing error path in tpm_tis_init
>
> v12:
>   - applies to checkout of ebffe2a (Oct 11)
>   - added documentation for fd parameter
>   - nits
>
> v11:
>   - applies to checkout of 46f3069 (Sep 28)
>   - some filing on the documentation
>   - small nits fixed
>
> v10:
>   - applies to checkout of 1ce9ce6 (Sep 27)
>   - addressed Michael Tsirkin's comments on v9
>
> v9:
>   - addressed Michael Tsirkin's and other reviewers' comments
>   - only posting Andreas Niederl's passthrough driver as the backend driver
>
> v8:
>   - applies to checkout of f0fb8b7 (Aug 30)
>   - fixing compilation error pointed out by Andreas Niederl
>   - adding patch that allows to feed an initial state into the libtpms TPM
>   - following memory API changes (glib) where necessary
>
> v7:
>   - applies to checkout of b9c6cbf (Aug 9)
>   - measuring the modules if multiboot is used
>   - coding style fixes
>
> v6:
>   - applies to checkout of 75ef849 (July 2nd)
>   - some fixes and improvements to existing patches; see individual patches
>   - added a patch with a null driver responding to all TPM requests with
>     a response indicating failure; this backend has no dependencies and
>     can alwayy be built;
>   - added a patch to support the hashing of kernel, ramfs and command line
>     if those were passed to Qemu using -kernel, -initrd and -append
>     respectively. Measurements are taken, logged, and passed to SeaBIOS using
>     the firmware interface.
>   - libtpms revision 7 now requires 83kb of block storage due to having more
>     NVRAM space
>
> v5:
>   - applies to checkout of 1fddfba1
>   - adding support for split command line using the -tpmdev ... -device ...
>     options while keeping the -tpm option
>   - support for querying the device models using -tpm model=?
>   - support for monitor 'info tpm'
>   - adding documentation of command line options for man page and web page
>   - increasing room for ACPI tables that qemu reserves to 128kb (from 64kb)
>   - adding (experimental) support for block migration
>   - adding (experimental) support for taking measurements when kernel,
>     initrd and kernel command line are directly passed to Qemu
>
> v4:
>   - applies to checkout of d2d979c6
>   - more coding style fixes
>   - adding patch for supporting blob encryption (in addition to the existing
>     QCoW2-level encryption)
>     - this allows for graceful termination of a migration if the target
>       is detected to have a wrong key
>     - tested with big and little endian hosts
>   - main thread releases mutex while checking for work to do on behalf of
>     backend
>   - introducing file locking (fcntl) on the block layer for serializing access
>     to shared (QCoW2) files (used during migration)
>
> v3:
>   - Building a null driver at patch 5/8 that responds to all requests
>     with an error response; subsequently this driver is transformed to the
>     libtpms-based driver for real TPM functionality
>   - Reworked the threading; dropped the patch for qemu_thread_join; the
>     main thread synchronizing with the TPM thread termination may need
>     to write data to the block storage while waiting for the thread to
>     terminate; did not previously show a problem but is safer
>   - A lot of testing based on recent git checkout 4b4a72e5 (4/10):
>     - migration of i686 VM from x86_64 host to i686 host to ppc64 host while
>       running tests inside the VM
>     - tests with S3 suspend/resume
>     - tests with snapshots
>     - multiple-hour tests with VM suspend/resume (using virsh save/restore)
>       while running a TPM test suite inside the VM
>     All tests passed; [not all of them were done on the ppc64 host]
>
> v2:
>   - splitting some of the patches into smaller ones for easier review
>   - fixes in individual patches
>
> Regards,
>      Stefan
>
>
> Stefan Berger (7):
>    Support for TPM command line options
>    Add TPM (frontend) hardware interface (TPM TIS) to Qemu
>    Add a debug register
>    Build the TPM frontend code
>    Add a TPM Passthrough backend driver implementation
>    Introduce --enable-tpm-passthrough configure option
>    Add fd parameter for TPM passthrough driver
>
>   Makefile.target      |    3 +
>   configure            |   28 ++
>   hmp-commands.hx      |    2 +
>   hmp.c                |   28 ++
>   hmp.h                |    1 +
>   hw/tpm_backend.c     |   58 ++++
>   hw/tpm_backend.h     |   43 +++
>   hw/tpm_passthrough.c |  452 +++++++++++++++++++++++++
>   hw/tpm_tis.c         |  895 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   hw/tpm_tis.h         |   82 +++++
>   monitor.c            |    8 +
>   qapi-schema.json     |   29 ++
>   qemu-config.c        |   25 ++
>   qemu-options.hx      |   70 ++++
>   tpm.c                |  230 +++++++++++++
>   tpm.h                |  114 +++++++
>   vl.c                 |   20 ++
>   17 files changed, 2088 insertions(+), 0 deletions(-)
>   create mode 100644 hw/tpm_backend.c
>   create mode 100644 hw/tpm_backend.h
>   create mode 100644 hw/tpm_passthrough.c
>   create mode 100644 hw/tpm_tis.c
>   create mode 100644 hw/tpm_tis.h
>   create mode 100644 tpm.c
>   create mode 100644 tpm.h
>

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

* Re: [Qemu-devel] [PATCH V15 1/7] Support for TPM command line options
  2012-03-27 21:35   ` Anthony Liguori
@ 2012-03-28 15:24     ` Stefan Berger
  0 siblings, 0 replies; 14+ messages in thread
From: Stefan Berger @ 2012-03-28 15:24 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: mst, qemu-devel, andreas.niederl

On 03/27/2012 05:35 PM, Anthony Liguori wrote:
> On 03/27/2012 03:24 PM, Stefan Berger wrote:
>> This patch adds support for TPM command line options.
>> The command line options supported here are
>>
[...]
>> Monitor support for 'info tpm' has been added. It for example prints the
>> following:
>>
>> (qemu) info tpm
>> TPM devices:
>>   tpm0: model=tpm-tis
>>    \ tpm0: type=passthrough,path=/dev/tpm0
>>
>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>> ---
[...]
>>
>> --- /dev/null
>> +++ b/hw/tpm_tis.h
>> @@ -0,0 +1,81 @@
>> +/*
>> + * 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.
>
> Or later please.  We're sticking with GPLv2 as our effective license 
> but asking that all new code is GPLv2 or later.

Forgot that one. Fixed.
>
>> + *
>> + *
>> + * 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"
>
> This shouldn't be needed anymore.

Indeed. Removed.+
>> +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;
>
> I'm a bit confused here.  If TPMTISState has an irq, shouldn't it be a 
> DeviceState?
>

So I guess you would expect the irq to be part of TPMState?diff --git 
a/qapi-schema.json b/qapi-schema.json

>> index 0d11d6e..4ad6d29 100644
>> --- a/qapi-schema.json
>> +++ b/qapi-schema.json
>> @@ -1701,3 +1701,32 @@
>>   # Since: 1.1
>>   ##
>>   { 'command': 'xen-save-devices-state', 'data': {'filename': 'str'} }
>> +
>> +##
>> +# @TPMInfo:
>> +#
>> +# Information about the TPM
>> +#
>> +# @model: The TPM frontend model, i.e., tpm-tis
>> +#
>> +# @id: The ID of the TPM
>> +#
>> +# @type: The type of TPM backend, i.e., passthrough
>> +#
>> +# @parameters: Additional parameters of the TPM backend device
>
> This is in some sort of key=value format?  Why not specify those 
> parameters properly in the schema as optional items?
>

Yes, it's in key=value format and it is optional. So, I fixed this now 
with #optional and '*parameters'. Should it make
a difference in the code, except for the auto-generated code?
+
>> +/* overall state of the TPM interface */
>> +typedef struct TPMState {
>> +    ISADevice busdev;
>> +    MemoryRegion mmio;
>> +
>> +    union {
>> +        TPMTISState tis;
>> +    } s;
>> +
>> +    uint8_t     command_locty;
>> +    TPMLocality *cmd_locty;
>> +
>> +    char *backend;
>> +    TPMBackend *be_driver;
>> +} TPMState;
>
> I'm a bit confused at what the relationship between TPMTISState and 
> TPMSTate.

The rational is that TPMState could accomodate different types of 
front-ends in the union with the part in the union being private to the 
front-end. Right now there is only a TIS emulator, there could be a 
virtio (with the restriction that it would have to support TPMLocality). 
Obviously there is a backend driver necessary for this split between 
front- and backend to work, so this is what the 'char *backend' and 
TPMBackend *be_driver are good for. cmd_locty is shared between front 
and backend and is set by the frontend and read by the backend.

Regards,
    Stefan

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

* Re: [Qemu-devel] [PATCH V15 5/7] Add a TPM Passthrough backend driver implementation
  2012-03-27 21:41   ` Anthony Liguori
@ 2012-03-28 15:38     ` Stefan Berger
  0 siblings, 0 replies; 14+ messages in thread
From: Stefan Berger @ 2012-03-28 15:38 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: andreas.niederl, qemu-devel, mst

On 03/27/2012 05:41 PM, Anthony Liguori wrote:
> On 03/27/2012 03:24 PM, Stefan Berger wrote:
>>  From Andreas Niederl's original posting with adaptations where 
>> necessary:
>>
>> This patch is based of off version 9 of Stefan Berger's patch series
>>    "Qemu Trusted Platform Module (TPM) integration"
>> and adds a new backend driver for it.
>>
[...]

>> +/* borrowed from qemu-char.c */
>
> Sharing code is better than borrowing code :-)
>

True. Fixed.


    Stefan

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

* Re: [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration
  2012-03-27 21:43 ` [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Anthony Liguori
@ 2012-03-28 17:46   ` Stefan Berger
  0 siblings, 0 replies; 14+ messages in thread
From: Stefan Berger @ 2012-03-28 17:46 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: andreas.niederl, qemu-devel, Michael S. Tsirkin

On 03/27/2012 05:43 PM, Anthony Liguori wrote:
> On 03/27/2012 03:24 PM, 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.
>
> I haven't done a thorough review, but overall, I'm pretty happy with 
> how this looks now.

Great. So then I'd rather wait for your further review than posting V16.
>
> I think this is more or less ready to merge once it's been thoroughly 
> reviewed.
>

... I am keeping my fingers crossed...

> Thanks for doing all of this work, I'm very happy with the end result!

Regards,
     Stefan

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

end of thread, other threads:[~2012-03-28 17:47 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-03-27 20:24 [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 1/7] Support for TPM command line options Stefan Berger
2012-03-27 21:35   ` Anthony Liguori
2012-03-28 15:24     ` Stefan Berger
2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 3/7] Add a debug register Stefan Berger
2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 4/7] Build the TPM frontend code Stefan Berger
2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
2012-03-27 21:41   ` Anthony Liguori
2012-03-28 15:38     ` Stefan Berger
2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
2012-03-27 20:24 ` [Qemu-devel] [PATCH V15 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
2012-03-27 21:43 ` [Qemu-devel] [PATCH V15 0/7] Qemu Trusted Platform Module (TPM) integration Anthony Liguori
2012-03-28 17:46   ` 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.