All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration
@ 2011-05-06 17:32 Stefan Berger
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options Stefan Berger
                   ` (11 more replies)
  0 siblings, 12 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

The following series of patches adds a TPM (Trusted Platform Module)
TIS (TPM Interface Spec) interface to Qemu and with that provides
means to access a backend implementing the actual TPM functionality.
This frontend enables for example Linux's TPM TIS (tpm_tis) driver.

I am also posting the implementation of a backend implementation that is based
on a library (libtpms) providing TPM functionality. This library is currently
undergoing further testing but is now available via Fedora Rawhide:

http://download.fedora.redhat.com/pub/fedora/linux/development/rawhide/x86_64/os/Packages/libtpms-0.5.1-5.x86_64.rpm
http://download.fedora.redhat.com/pub/fedora/linux/development/rawhide/x86_64/os/Packages/libtpms-devel-0.5.1-5.x86_64.rpm

All testing was done with the libtpms-based backend. It provides support for
VM suspend/resume, migration and snapshotting. It uses QCoW2 as the file
format for storing its persistent state onto, which is necessary for support
of snapshotting. Using Linux as the OS along with some recently posted patches
for the Linux TPM TIS driver, suspend/resume works fine (using 'virsh
save/restore') along with hibernation and OS suspend (ACPI S3).

Proper support for the TPM requires support in the BIOS since the BIOS
needs to initialize the TPM upon machine start or issue commands to the TPM
when it resumes from suspend (ACPI S3). It also builds and connects the
necessary ACPI tables (SSDT for TPM device, TCPA table for logging) to the
ones that are built by a BIOS. To support this I have fairly extensive
set of extensions for SeaBIOS that have already been posted to the SeaBIOS
mailing list and been ACK'ed by Kevin (thank you! :-)).

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

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

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

Regards,
    Stefan

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

* [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
@ 2011-05-06 17:32 ` Stefan Berger
  2011-05-06 20:23   ` Serge E. Hallyn
  2011-05-17 20:58   ` Serge E. Hallyn
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 02/10] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
                   ` (10 subsequent siblings)
  11 siblings, 2 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

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

This patch adds support for TPM command line options.
The command line supported here (considering the libtpms based
backend) are

./qemu-... -tpm type=<type>,path=<path to blockstorage file>,

and

./qemu-... -tpm ?

where the latter works similar to -soundhw ? and shows a list of
available TPM backends (i.e., libtpms-based, Xen).

Only the 'type' is interpreted in arch_init.c. Using this parameter,
the backend is chosen, i.e., 'builtin' for the libtpms-based
builtin TPM. 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
'handle_options' and return true if the VM can be started or 'false'
if not enough or bad parameters were provided.

v4:
 - coding style fixes

v3:
 - added hw/tpm_tis.h to this patch so Qemu compiles at this stage

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

---
 arch_init.c     |   80 +++++++++++++++++++++++++++++++
 arch_init.h     |    2 
 hw/pc.c         |    4 +
 hw/pc.h         |    7 ++
 hw/tpm_tis.h    |  143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-config.c   |   20 +++++++
 qemu-options.hx |   11 ++++
 vl.c            |   12 ++++
 8 files changed, 279 insertions(+)

Index: qemu-git/hw/pc.h
===================================================================
--- qemu-git.orig/hw/pc.h
+++ qemu-git/hw/pc.h
@@ -6,6 +6,7 @@
 #include "isa.h"
 #include "fdc.h"
 #include "net.h"
+#include "tpm_tis.h"
 
 /* PC-style peripherals (also used by other machines).  */
 
@@ -128,6 +129,12 @@ void pc_register_ferr_irq(qemu_irq irq);
 void pc_cmos_set_s3_resume(void *opaque, int irq, int level);
 void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
 
+/* tpm_tis.c */
+extern bool has_tpm;
+const BackendTPMDriver *tis_set_backend_driver(const char *tpm_type);
+void tis_display_backend_drivers(FILE *);
+
+
 void pc_cpus_init(const char *cpu_model);
 void pc_memory_init(ram_addr_t ram_size,
                     const char *kernel_filename,
Index: qemu-git/qemu-options.hx
===================================================================
--- qemu-git.orig/qemu-options.hx
+++ qemu-git/qemu-options.hx
@@ -1041,6 +1041,17 @@ Specify SMBIOS type 0 fields
 Specify SMBIOS type 1 fields
 ETEXI
 
+#ifndef _WIN32
+# ifdef CONFIG_TPM
+DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \
+    ""
+    "-tpm type=<type>,path=<path>\n" \
+    "                enable a TPM with state from file in given path\n"
+    "                use -tpm ? to get a list of supported TPM types\n",
+    QEMU_ARCH_I386)
+# endif
+#endif
+
 DEFHEADING()
 STEXI
 @end table
Index: qemu-git/vl.c
===================================================================
--- qemu-git.orig/vl.c
+++ qemu-git/vl.c
@@ -244,6 +244,8 @@ int nb_numa_nodes;
 uint64_t node_mem[MAX_NODES];
 uint64_t node_cpumask[MAX_NODES];
 
+bool has_tpm = false;
+
 static QEMUTimer *nographic_timer;
 
 uint8_t qemu_uuid[16];
@@ -2333,6 +2335,16 @@ int main(int argc, char **argv, char **e
                 ram_size = value;
                 break;
             }
+#ifdef CONFIG_TPM
+            case QEMU_OPTION_tpm:
+                if (!(tpm_available())) {
+                    printf("Option %s not supported for this target\n",
+                           popt->name);
+                    exit(1);
+                }
+                select_tpm(optarg);
+                break;
+#endif
             case QEMU_OPTION_mempath:
                 mem_path = optarg;
                 break;
Index: qemu-git/qemu-config.c
===================================================================
--- qemu-git.orig/qemu-config.c
+++ qemu-git/qemu-config.c
@@ -450,6 +450,25 @@ QemuOptsList qemu_option_rom_opts = {
     },
 };
 
+static QemuOptsList qemu_tpm_opts = {
+    .name = "tpm",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
+    .desc = {
+        {
+            .name = "type",
+            .type = QEMU_OPT_STRING,
+            .help = "Type of TPM backend",
+        },
+        {
+            .name = "path",
+            .type = QEMU_OPT_STRING,
+            .help = "Persitent storage for TPM state",
+        },
+        { /* end of list */ }
+    },
+};
+
+
 static QemuOptsList *vm_config_groups[32] = {
     &qemu_drive_opts,
     &qemu_chardev_opts,
@@ -464,6 +483,7 @@ static QemuOptsList *vm_config_groups[32
     &qemu_trace_opts,
 #endif
     &qemu_option_rom_opts,
+    &qemu_tpm_opts,
     NULL,
 };
 
Index: qemu-git/arch_init.c
===================================================================
--- qemu-git.orig/arch_init.c
+++ qemu-git/arch_init.c
@@ -41,6 +41,8 @@
 #include "net.h"
 #include "gdbstub.h"
 #include "hw/smbios.h"
+#include "blockdev.h"
+#include "hw/tpm_tis.h"
 
 #ifdef TARGET_SPARC
 int graphic_width = 1024;
@@ -726,3 +728,81 @@ int xen_available(void)
     return 0;
 #endif
 }
+
+int tpm_available(void) {
+#ifdef CONFIG_TPM
+    return 1;
+#else
+    return 0;
+#endif
+}
+
+#ifdef CONFIG_TPM
+
+#if defined (TARGET_I386) || defined (TARGET_X86_64)
+
+
+static int configure_tpm(QemuOpts *opts)
+{
+    const char *value;
+    const BackendTPMDriver *be;
+
+    if (has_tpm) {
+        fprintf(stderr,"Only one TPM is allowed\n");
+        return 1;
+    }
+
+    value = qemu_opt_get(opts, "type");
+    if (!value) {
+        fprintf(stderr,
+                "Missing TPM backend type.");
+        tis_display_backend_drivers(stderr);
+        return 1;
+    }
+
+    be = tis_set_backend_driver(value);
+    if (be == NULL) {
+        fprintf(stderr,
+                "A TPM backend driver of type %s is not supported.\n",
+                value);
+        tis_display_backend_drivers(stderr);
+        return 1;
+    }
+
+    has_tpm = be->handle_options(opts);
+    if (!has_tpm) {
+        return 1;
+    }
+
+    return 0;
+}
+
+
+void select_tpm(const char *optarg)
+{
+    QemuOpts *opts;
+
+    if (strcmp("none", optarg) != 0) {
+        if (*optarg == '?') {
+            tis_display_backend_drivers(stdout);
+            exit(0);
+        }
+        opts = qemu_opts_parse(qemu_find_opts("tpm"), optarg, 0);
+        if (!opts) {
+            exit(1);
+        }
+        if (configure_tpm(opts)) {
+            exit(1);
+        }
+    }
+}
+
+# else /* CONFIG_TPM */
+
+void select_tpm(const char *optarg)
+{
+    (void)optarg;
+}
+
+# endif
+#endif /* CONFIG_TPM */
Index: qemu-git/arch_init.h
===================================================================
--- qemu-git.orig/arch_init.h
+++ qemu-git/arch_init.h
@@ -29,5 +29,7 @@ int audio_available(void);
 void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus);
 int kvm_available(void);
 int xen_available(void);
+int tpm_available(void);
+void select_tpm(const char *optarg);
 
 #endif
Index: qemu-git/hw/tpm_tis.h
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_tis.h
@@ -0,0 +1,143 @@
+/*
+ * tpm_tis.h - include file for tpm_tis.c
+ *
+ * Copyright (C) 2006,2010,2011 IBM Corporation
+ *
+ * Author: 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.
+ */
+#ifndef _HW_TPM_TIS_H
+#define _HW_TPM_TIS_H
+
+#include "isa.h"
+#include "block_int.h"
+#include "qemu-thread.h"
+
+#include <stdint.h>
+
+#define TIS_ADDR_BASE       0xFED40000
+
+#define NUM_LOCALITIES      5     /* per spec */
+#define NO_LOCALITY         0xff
+
+#define IS_VALID_LOCTY(x)   ((x) < NUM_LOCALITIES)
+
+
+#define TPM_TIS_IRQ         11
+
+#define TIS_TPM_BUFFER_MAX  4096
+
+
+typedef struct TPMSizedBuffer
+{
+    uint32_t size;
+    uint8_t  *buffer;
+} TPMSizedBuffer;
+
+
+enum tis_state {
+    STATE_IDLE = 0,
+    STATE_READY,
+    STATE_COMPLETION,
+    STATE_EXECUTION,
+    STATE_RECEPTION,
+};
+
+
+/* locality data  -- all fields are persisted */
+typedef struct TPMLocality {
+    enum tis_state state;
+    uint8_t access;
+    uint8_t sts;
+    uint32_t inte;
+    uint32_t ints;
+
+    uint16_t w_offset;
+    uint16_t r_offset;
+    TPMSizedBuffer w_buffer;
+    TPMSizedBuffer r_buffer;
+} TPMLocality;
+
+
+/* overall state of the TPM interface; 's' marks a persisted field */
+typedef struct TPMState {
+    ISADevice busdev;
+
+    uint32_t offset;
+    uint8_t buf[TIS_TPM_BUFFER_MAX];
+
+    uint8_t active_locty;
+    uint8_t aborting_locty;
+    uint8_t next_locty;
+
+    uint8_t command_locty;
+    TPMLocality loc[NUM_LOCALITIES];
+
+    qemu_irq irq;
+    uint32_t irq_num;
+
+    QemuMutex state_lock;
+    QemuCond  from_tpm_cond;
+    QemuCond  to_tpm_cond;
+    bool      to_tpm_execute;
+
+    bool      tpm_initialized;
+} TPMState;
+
+
+typedef void (TPMRecvDataCB)(TPMState *s, uint8_t locty);
+
+typedef struct BackendTPMDriver {
+    const char *id;
+    const char *(*desc)(void);
+
+    void (*job_for_main_thread)(void *);
+
+    bool (*handle_options)(QemuOpts *);
+
+    int (*init)(TPMState *s, TPMRecvDataCB *datacb);
+    int (*early_startup_tpm)(void);
+    int (*late_startup_tpm)(void);
+    bool (*had_startup_error)(void);
+
+    size_t (*realloc_buffer)(TPMSizedBuffer *sb);
+
+    void (*reset)(void);
+
+    int (*save_volatile_data)(void);
+    int (*load_volatile_data)(TPMState *s);
+
+    bool (*get_tpm_established_flag)(void);
+} BackendTPMDriver;
+
+
+void tis_reset_for_snapshot_resume(TPMState *s);
+const BackendTPMDriver *tis_get_active_backend(void);
+
+/* utility functions */
+
+static inline uint16_t tis_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+    return (sb->buffer[4] << 8) + sb->buffer[5];
+}
+
+static inline void dumpBuffer(FILE *stream,
+                              unsigned char *buffer, unsigned int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++) {
+        if (i && !(i % 16)) {
+            fprintf(stream, "\n");
+        }
+        fprintf(stream, "%.2X ", buffer[i]);
+    }
+    fprintf(stream, "\n");
+}
+
+#endif /* _HW_TPM_TIS_H */
Index: qemu-git/hw/pc.c
===================================================================
--- qemu-git.orig/hw/pc.c
+++ qemu-git/hw/pc.c
@@ -1154,6 +1154,10 @@ void pc_basic_device_init(qemu_irq *isa_
         fd[i] = drive_get(IF_FLOPPY, 0, i);
     }
     fdctrl_init_isa(fd);
+
+    if (has_tpm) {
+        isa_create_simple("tpm-tis");
+    }
 }
 
 void pc_pci_device_init(PCIBus *pci_bus)

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

* [Qemu-devel] [PATCH V4 02/10] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options Stefan Berger
@ 2011-05-06 17:32 ` Stefan Berger
  2011-05-07  1:54   ` Serge E. Hallyn
  2011-05-18  7:23   ` Markus Armbruster
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 03/10] Add persistent state handling to TPM TIS frontend driver Stefan Berger
                   ` (9 subsequent siblings)
  11 siblings, 2 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

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

This patch adds the main code of the TPM frontend driver, the TPM TIS
interface, to Qemu. The code is largely based on my 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.

v3:
  - prefixing functions with tis_
  - added a function to the backend interface 'early_startup_tpm' that
    allows to detect the presence of the block storage and gracefully fails
    Qemu if it's not available. This works with migration using shared
    storage but doesn't support migration with block storage migration.
    For encyrypted QCoW2 and in case of a snapshot resue the late_startup_tpm
    interface function is called

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

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

Index: qemu-git/hw/tpm_tis.c
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_tis.c
@@ -0,0 +1,871 @@
+/*
+ * tpm_tis.c - QEMU emulator for a 1.2 TPM with TIS interface
+ *
+ * Copyright (C) 2006,2010 IBM Corporation
+ *
+ * Author: 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 at
+ * https://www.trustedcomputinggroup.org/groups/pc_client/TCG_PCClientTPMSpecification_1-20_1-00_FINAL.pdf
+ *
+ */
+
+#include "block.h"
+#include "hw/hw.h"
+#include "hw/pc.h"
+#include "hw/tpm_tis.h"
+
+#include <stdio.h>
+
+//#define DEBUG_TIS
+
+/* whether the STS interrupt is supported */
+//#define RAISE_STS_IRQ
+
+/* tis registers */
+#define TIS_REG_ACCESS                0x00
+#define TIS_REG_INT_ENABLE            0x08
+#define TIS_REG_INT_VECTOR            0x0c
+#define TIS_REG_INT_STATUS            0x10
+#define TIS_REG_INTF_CAPABILITY       0x14
+#define TIS_REG_STS                   0x18
+#define TIS_REG_DATA_FIFO             0x24
+#define TIS_REG_DID_VID               0xf00
+#define TIS_REG_RID                   0xf04
+
+
+#define STS_VALID                    (1 << 7)
+#define STS_COMMAND_READY            (1 << 6)
+#define STS_TPM_GO                   (1 << 5)
+#define STS_DATA_AVAILABLE           (1 << 4)
+#define STS_EXPECT                   (1 << 3)
+#define STS_RESPONSE_RETRY           (1 << 1)
+
+#define ACCESS_TPM_REG_VALID_STS     (1 << 7)
+#define ACCESS_ACTIVE_LOCALITY       (1 << 5)
+#define ACCESS_BEEN_SEIZED           (1 << 4)
+#define ACCESS_SEIZE                 (1 << 3)
+#define ACCESS_PENDING_REQUEST       (1 << 2)
+#define ACCESS_REQUEST_USE           (1 << 1)
+#define ACCESS_TPM_ESTABLISHMENT     (1 << 0)
+
+#define INT_ENABLED                  (1 << 31)
+#define INT_DATA_AVAILABLE           (1 << 0)
+#define INT_STS_VALID                (1 << 1)
+#define INT_LOCALITY_CHANGED         (1 << 2)
+#define INT_COMMAND_READY            (1 << 7)
+
+#ifndef RAISE_STS_IRQ
+
+# define INTERRUPTS_SUPPORTED         (INT_LOCALITY_CHANGED | \
+                                       INT_DATA_AVAILABLE   | \
+                                       INT_COMMAND_READY)
+
+#else
+
+# define INTERRUPTS_SUPPORTED         (INT_LOCALITY_CHANGED | \
+                                       INT_DATA_AVAILABLE   | \
+                                       INT_STS_VALID | \
+                                       INT_COMMAND_READY)
+
+#endif
+
+#define CAPABILITIES_SUPPORTED       ((1 << 4) |            \
+                                      INTERRUPTS_SUPPORTED)
+
+#define TPM_DID          0x0001
+#define TPM_VID          0x0001
+#define TPM_RID          0x0001
+
+#define TPM_NO_DATA_BYTE 0xff
+
+/* prototypes */
+static uint32_t tis_mem_readl(void *opaque, target_phys_addr_t addr);
+
+
+static const BackendTPMDriver *bes[] = {
+    NULL,
+};
+
+/* active backend driver */
+static const BackendTPMDriver *active_be;
+
+const BackendTPMDriver *tis_get_active_backend(void)
+{
+    return active_be;
+}
+
+const BackendTPMDriver *tis_set_backend_driver(const char *id)
+{
+    int i;
+
+    for (i = 0; bes[i] != NULL; i++) {
+        if (!strcmp(bes[i]->id, id)) {
+            break;
+        }
+    }
+
+    active_be = bes[i];
+
+    return active_be;
+}
+
+void tis_display_backend_drivers(FILE *out)
+{
+    int i;
+
+    fprintf(out, "Supported TPM types (choose only one):\n");
+
+    for (i = 0; bes[i] != NULL; i++) {
+        fprintf(out, "%7s   %s",
+                bes[i]->id, bes[i]->desc());
+        fprintf(out, "\n");
+    }
+    fprintf(out, "\n");
+}
+
+
+#ifdef DEBUG_TIS
+static void tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
+{
+    uint16_t len;
+
+    len = tis_get_size_from_buffer(sb);
+    fprintf(stderr,"tpm_tis: %s length = %d\n", string, len);
+    dumpBuffer(stderr, sb->buffer, len);
+}
+#endif
+
+
+static inline uint8_t tis_locality_from_addr(target_phys_addr_t addr)
+{
+    return (uint8_t)((addr >> 12) & 0x7);
+}
+
+
+/*
+ * Send a TPM request.
+ * Call this with the state_lock held so we can sync with the receive
+ * callback.
+ */
+static void tis_tpm_send(TPMState *s, uint8_t locty)
+{
+#ifdef DEBUG_TIS
+    tis_show_buffer(&s->loc[locty].w_buffer, "tpm_tis: To TPM");
+#endif
+    s->command_locty = locty;
+
+    /* w_offset serves as length indicator for length of data;
+       it's reset when the response comes back */
+    s->loc[locty].state = STATE_EXECUTION;
+    s->loc[locty].sts &= ~STS_EXPECT;
+
+    s->to_tpm_execute = true;
+    qemu_cond_signal(&s->to_tpm_cond);
+}
+
+
+/* raise an interrupt if allowed */
+static void tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
+{
+    if (!IS_VALID_LOCTY(locty)) {
+        return;
+    }
+
+    if ((s->loc[locty].inte & INT_ENABLED) &&
+        (s->loc[locty].inte & irqmask)) {
+#ifdef DEBUG_TIS
+        fprintf(stderr,"tpm_tis: Raising IRQ for flag %08x\n",irqmask);
+#endif
+        qemu_irq_raise(s->irq);
+        s->loc[locty].ints |= irqmask;
+    }
+}
+
+
+static uint32_t tis_check_request_use_except(TPMState *s, uint8_t locty)
+{
+    uint8_t l;
+
+    for (l = 0; l < NUM_LOCALITIES; l++) {
+        if (l == locty) {
+            continue;
+        }
+        if ((s->loc[l].access & ACCESS_REQUEST_USE)) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+
+static void tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
+{
+    int change = (s->active_locty != new_active_locty);
+
+    if (change && IS_VALID_LOCTY(s->active_locty)) {
+        /* reset flags on the old active locality */
+        s->loc[s->active_locty].access &= ~(ACCESS_ACTIVE_LOCALITY|
+                                            ACCESS_REQUEST_USE);
+        if (IS_VALID_LOCTY(new_active_locty) &&
+            s->loc[new_active_locty].access & ACCESS_SEIZE) {
+            s->loc[s->active_locty].access |= ACCESS_BEEN_SEIZED;
+        }
+    }
+
+    s->active_locty = new_active_locty;
+#ifdef DEBUG_TIS
+    fprintf(stderr,"tpm_tis: Active locality is now %d\n", s->active_locty);
+#endif
+
+    if (IS_VALID_LOCTY(new_active_locty)) {
+        /* set flags on the new active locality */
+        s->loc[new_active_locty].access |= ACCESS_ACTIVE_LOCALITY;
+        s->loc[new_active_locty].access &= ~(ACCESS_REQUEST_USE |
+                                             ACCESS_SEIZE);
+    }
+
+    if (change) {
+        tis_raise_irq(s, s->active_locty, INT_LOCALITY_CHANGED);
+    }
+}
+
+
+/* abort -- this function switches the locality */
+static void tis_abort(TPMState *s, uint8_t locty)
+{
+    s->loc[locty].r_offset = 0;
+    s->loc[locty].w_offset = 0;
+
+#ifdef DEBUG_TIS
+    fprintf(stderr,"tpm_tis: tis_abort: new active locality is %d\n",
+            s->next_locty);
+#endif
+
+    /*
+     * Need to react differently depending on who's aborting now and
+     * which locality will become active afterwards.
+     */
+    if (s->aborting_locty == s->next_locty) {
+        s->loc[s->aborting_locty].state = STATE_READY;
+        s->loc[s->aborting_locty].sts   = STS_COMMAND_READY;
+        tis_raise_irq(s, s->aborting_locty, INT_COMMAND_READY );
+    }
+
+    /* locality after abort is another one than the current one */
+    tis_new_active_locality(s, s->next_locty);
+
+    s->next_locty = NO_LOCALITY;
+    s->aborting_locty = NO_LOCALITY; /* nobody's aborting a command anymore */
+}
+
+
+/* prepare aborting current command */
+static void tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
+{
+    uint8_t busy_locty;
+
+    s->aborting_locty = locty;
+    s->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 < NUM_LOCALITIES; busy_locty++) {
+        if (s->loc[busy_locty].state == STATE_EXECUTION) {
+            /* there is currently no way to interrupt the TPM's operations
+               while it's executing a command; once the TPM is done and
+               returns the buffer, it will switch to the next_locty; */
+#ifdef DEBUG_TIS
+            fprintf(stderr,"tpm_tis: Locality %d is busy - "
+                           "deferring abort\n", busy_locty);
+#endif
+            return;
+        }
+    }
+
+    tis_abort(s, locty);
+}
+
+
+/*
+ * Callback from the TPM to indicate that the response was received.
+ */
+static void tis_tpm_receive_cb(TPMState *s, uint8_t locty)
+{
+    qemu_mutex_lock(&s->state_lock);
+
+    s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+    s->loc[locty].state = STATE_COMPLETION;
+    s->loc[locty].r_offset = 0;
+    s->loc[locty].w_offset = 0;
+
+    if (IS_VALID_LOCTY(s->next_locty)) {
+        tis_abort(s, locty);
+    }
+
+    qemu_cond_signal(&s->from_tpm_cond);
+
+    qemu_mutex_unlock(&s->state_lock);
+
+#ifndef RAISE_STS_IRQ
+    tis_raise_irq(s, locty, INT_DATA_AVAILABLE);
+#else
+    tis_raise_irq(s, locty, INT_DATA_AVAILABLE | INT_STS_VALID);
+#endif
+}
+
+
+/*
+ * read a byte of response data
+ */
+static uint32_t tis_data_read(TPMState *s, uint8_t locty)
+{
+    uint32_t ret = TPM_NO_DATA_BYTE;
+    uint16_t len;
+
+    if ((s->loc[locty].sts & STS_DATA_AVAILABLE)) {
+        len = tis_get_size_from_buffer(&s->loc[locty].r_buffer);
+
+        ret = s->loc[locty].r_buffer.buffer[s->loc[locty].r_offset++];
+        if (s->loc[locty].r_offset >= len) {
+            /* got last byte */
+            s->loc[locty].sts = STS_VALID;
+#ifdef RAISE_STS_IRQ
+            tis_raise_irq(s, locty, INT_STS_VALID);
+#endif
+        }
+#ifdef DEBUG_TIS
+        fprintf(stderr,"tpm_tis: tis_data_read byte 0x%02x   [%d]\n",
+                ret,s->loc[locty].r_offset-1);
+#endif
+    }
+
+    return ret;
+}
+
+
+/*
+ * Read a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static uint32_t tis_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    TPMState *s = opaque;
+    uint16_t offset = addr & 0xffc;
+    uint8_t shift = (addr & 0x3) * 8;
+    uint32_t val = 0xff;
+    uint8_t locty = tis_locality_from_addr(addr);
+
+    qemu_mutex_lock(&s->state_lock);
+
+    if (!s->tpm_initialized) {
+        active_be->late_startup_tpm();
+        s->tpm_initialized = true;
+    }
+
+    if (active_be->had_startup_error()) {
+        qemu_mutex_unlock(&s->state_lock);
+        return 0xFFFFFFFF;
+    }
+
+    switch (offset) {
+    case TIS_REG_ACCESS:
+        /* never show the SEIZE flag even though we use it internally */
+        val = s->loc[locty].access & ~ACCESS_SEIZE;
+        /* the pending flag is alawys calculated */
+        if (tis_check_request_use_except(s, locty)) {
+            val |= ACCESS_PENDING_REQUEST;
+        }
+        val |= !active_be->get_tpm_established_flag();
+        break;
+    case TIS_REG_INT_ENABLE:
+        val = s->loc[locty].inte;
+        break;
+    case TIS_REG_INT_VECTOR:
+        val = s->irq_num;
+        break;
+    case TIS_REG_INT_STATUS:
+        val = s->loc[locty].ints;
+        break;
+    case TIS_REG_INTF_CAPABILITY:
+        val = CAPABILITIES_SUPPORTED;
+        break;
+    case TIS_REG_STS:
+        if (s->active_locty == locty) {
+            if ((s->loc[locty].sts & STS_DATA_AVAILABLE)) {
+                val =  (tis_get_size_from_buffer(&s->loc[locty].r_buffer) -
+                        s->loc[locty].r_offset ) << 8 | s->loc[locty].sts;
+            } else {
+                val = (s->loc[locty].w_buffer.size -
+                       s->loc[locty].w_offset) << 8 | s->loc[locty].sts;
+            }
+        }
+        break;
+    case TIS_REG_DATA_FIFO:
+        if (s->active_locty == locty) {
+            switch (s->loc[locty].state) {
+                case STATE_COMPLETION:
+                    val = tis_data_read(s, locty);
+                    break;
+                default:
+                    val = TPM_NO_DATA_BYTE;
+                    break;
+            }
+        }
+        break;
+    case TIS_REG_DID_VID:
+        val = (TPM_DID << 16) | TPM_VID;
+        break;
+    case TIS_REG_RID:
+        val = TPM_RID;
+        break;
+    }
+
+    qemu_mutex_unlock(&s->state_lock);
+
+    if (shift) {
+        val >>= shift;
+    }
+
+#ifdef DEBUG_TIS
+    fprintf(stderr,"tpm_tis:  read(%08x) = %08x\n", (int)addr, val);
+#endif
+
+    return val;
+}
+
+
+/*
+ * Write a value to a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static void tis_mem_writel_intern(void *opaque, target_phys_addr_t addr,
+                                  uint32_t val, bool hw_access)
+{
+    TPMState *s = opaque;
+    uint16_t off = addr & 0xfff;
+    uint8_t locty = tis_locality_from_addr(addr);
+    uint8_t active_locty, l;
+    int c, set_new_locty = 1;
+    uint16_t len;
+
+#ifdef DEBUG_TIS
+    fprintf(stderr,"tpm_tis: write(%08x) = %08x\n", (int)addr, val);
+#endif
+
+    qemu_mutex_lock(&s->state_lock);
+
+    if (!s->tpm_initialized) {
+        active_be->late_startup_tpm();
+        s->tpm_initialized = true;
+    }
+
+    if (active_be->had_startup_error()) {
+        qemu_mutex_unlock(&s->state_lock);
+        return;
+    }
+
+    switch (off) {
+    case TIS_REG_ACCESS:
+
+        if ((val & ACCESS_SEIZE)) {
+            val &= ~(ACCESS_REQUEST_USE | ACCESS_ACTIVE_LOCALITY);
+        }
+
+        active_locty = s->active_locty;
+
+        if ((val & ACCESS_ACTIVE_LOCALITY)) {
+            /* give up locality if currently owned */
+            if (s->active_locty == locty) {
+#ifdef DEBUG_TIS
+                fprintf(stderr,"tpm_tis: Releasing locality %d\n", locty);
+#endif
+                uint8_t newlocty = NO_LOCALITY;
+                /* anybody wants the locality ? */
+                for (c = NUM_LOCALITIES - 1; c >= 0; c--) {
+                    if ((s->loc[c].access & ACCESS_REQUEST_USE)) {
+#ifdef DEBUG_TIS
+                        fprintf(stderr,"tpm_tis: Locality %d requests use.\n",
+                                c);
+#endif
+                        newlocty = c;
+                        break;
+                    }
+                }
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: ACCESS_ACTIVE_LOCALITY: "
+                                "Next active locality: %d\n",
+                                newlocty);
+#endif
+                if (IS_VALID_LOCTY(newlocty)) {
+                    set_new_locty = 0;
+                    tis_prep_abort(s, locty, newlocty);
+                } else
+                    active_locty = NO_LOCALITY;
+            } else {
+                /* not currently the owner; clear a pending request */
+                s->loc[locty].access &= ~ACCESS_REQUEST_USE;
+            }
+        }
+
+        if ((val & ACCESS_BEEN_SEIZED)) {
+            s->loc[locty].access &= ~ACCESS_BEEN_SEIZED;
+        }
+
+        if ((val & 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 ( (IS_VALID_LOCTY(s->active_locty) &&
+                     locty > s->active_locty) ||
+                   (!IS_VALID_LOCTY(s->active_locty))) {
+
+                /* already a pending SEIZE ? */
+                if ((s->loc[locty].access & ACCESS_SEIZE)) {
+                    break;
+                }
+
+                /* check for ongoing seize by a higher locality */
+                for (l = locty + 1; l < NUM_LOCALITIES; l++) {
+                    if ((s->loc[l].access & ACCESS_SEIZE)) {
+                        break;
+                    }
+                }
+
+                /* cancel any seize by a lower locality */
+                for (l = 0; l < locty - 1; l++) {
+                    s->loc[l].access &= ~ACCESS_SEIZE;
+                }
+
+                s->loc[locty].access |= ACCESS_SEIZE;
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: ACCESS_SEIZE: "
+                                "Locality %d seized from locality %d\n",
+                                locty, s->active_locty);
+                fprintf(stderr, "tpm_tis: ACCESS_SEIZE: Initiating abort.\n");
+#endif
+                set_new_locty = 0;
+                tis_prep_abort(s, s->active_locty, locty);
+                break;
+            }
+        }
+
+        if ((val & ACCESS_REQUEST_USE)) {
+            if (s->active_locty != locty) {
+                if (IS_VALID_LOCTY(s->active_locty)) {
+                     s->loc[locty].access |= ACCESS_REQUEST_USE;
+                } else {
+                    /* no locality active -> make this one active now */
+                    active_locty = locty;
+                }
+            }
+        }
+
+        if (set_new_locty) {
+            tis_new_active_locality(s, active_locty);
+        }
+
+        break;
+    case TIS_REG_INT_ENABLE:
+        if (s->active_locty != locty) {
+            break;
+        }
+
+        s->loc[locty].inte = (val & (INT_ENABLED | (0x3 << 3) |
+                                     INTERRUPTS_SUPPORTED));
+        break;
+    case TIS_REG_INT_VECTOR:
+        /* hard wired -- ignore */
+        break;
+    case TIS_REG_INT_STATUS:
+        if (s->active_locty != locty) {
+            break;
+        }
+
+        /* clearing of interrupt flags */
+        if (((val & INTERRUPTS_SUPPORTED)) &&
+            (s->loc[locty].ints & INTERRUPTS_SUPPORTED)) {
+            s->loc[locty].ints &= ~val;
+            if (s->loc[locty].ints == 0) {
+                qemu_irq_lower(s->irq);
+#ifdef DEBUG_TIS
+                fprintf(stderr,"tpm_tis: Lowering IRQ\n");
+#endif
+            }
+        }
+        s->loc[locty].ints &= ~(val & INTERRUPTS_SUPPORTED);
+        break;
+    case TIS_REG_STS:
+        if (s->active_locty != locty) {
+            break;
+        }
+
+        val &= (STS_COMMAND_READY | STS_TPM_GO | STS_RESPONSE_RETRY);
+
+        if (val == STS_COMMAND_READY) {
+            switch (s->loc[locty].state) {
+
+            case STATE_READY:
+                s->loc[locty].w_offset = 0;
+                s->loc[locty].r_offset = 0;
+            break;
+
+            case STATE_IDLE:
+                s->loc[locty].sts   = STS_COMMAND_READY;
+                s->loc[locty].state = STATE_READY;
+                tis_raise_irq(s, locty, INT_COMMAND_READY);
+            break;
+
+            case STATE_EXECUTION:
+            case STATE_RECEPTION:
+                /* abort currently running command */
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: %s: Initiating abort.\n",
+                        __FUNCTION__);
+#endif
+                tis_prep_abort(s, locty, locty);
+            break;
+
+            case STATE_COMPLETION:
+                s->loc[locty].w_offset = 0;
+                s->loc[locty].r_offset = 0;
+                /* shortcut to ready state with C/R set */
+                s->loc[locty].state = STATE_READY;
+                if (!(s->loc[locty].sts & STS_COMMAND_READY)) {
+                    s->loc[locty].sts   = STS_COMMAND_READY;
+                    tis_raise_irq(s, locty, INT_COMMAND_READY);
+                }
+            break;
+
+            }
+        } else if (val == STS_TPM_GO) {
+            switch (s->loc[locty].state) {
+            case STATE_RECEPTION:
+                tis_tpm_send(s, locty);
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        } else if (val == STS_RESPONSE_RETRY) {
+            switch (s->loc[locty].state) {
+            case STATE_COMPLETION:
+                s->loc[locty].r_offset = 0;
+                s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        }
+        break;
+    case TIS_REG_DATA_FIFO:
+        /* data fifo */
+        if (s->active_locty != locty) {
+            break;
+        }
+
+        if (s->loc[locty].state == STATE_IDLE ||
+            s->loc[locty].state == STATE_EXECUTION ||
+            s->loc[locty].state == STATE_COMPLETION) {
+            /* drop the byte */
+        } else {
+#ifdef DEBUG_TIS
+            fprintf(stderr,"tpm_tis: Byte to send to TPM: %02x\n", val);
+#endif
+            if (s->loc[locty].state == STATE_READY) {
+                s->loc[locty].state = STATE_RECEPTION;
+                s->loc[locty].sts = STS_EXPECT | STS_VALID;
+            }
+
+            if ((s->loc[locty].sts & STS_EXPECT)) {
+                if (s->loc[locty].w_offset < s->loc[locty].w_buffer.size) {
+                    s->loc[locty].w_buffer.buffer[s->loc[locty].w_offset++] =
+                        (uint8_t)val;
+                } else {
+                    s->loc[locty].sts = STS_VALID;
+                }
+            }
+
+            /* check for complete packet */
+            if ( s->loc[locty].w_offset > 5 &&
+                (s->loc[locty].sts & STS_EXPECT)) {
+                /* we have a packet length - see if we have all of it */
+#ifdef RAISE_STS_IRQ
+                bool needIrq = !(s->loc[locty].sts & STS_VALID);
+#endif
+                len = tis_get_size_from_buffer(&s->loc[locty].w_buffer);
+                if (len > s->loc[locty].w_offset) {
+                    s->loc[locty].sts = STS_EXPECT | STS_VALID;
+                } else {
+                    /* packet complete */
+                    s->loc[locty].sts = STS_VALID;
+                }
+#ifdef RAISE_STS_IRQ
+                if (needIrq) {
+                    tis_raise_irq(s, locty, INT_STS_VALID);
+                }
+#endif
+            }
+        }
+        break;
+    }
+
+    qemu_mutex_unlock(&s->state_lock);
+}
+
+
+static void tis_mem_writel(void *opaque, target_phys_addr_t addr,
+                           uint32_t val)
+{
+    return tis_mem_writel_intern(opaque, addr, val, false);
+}
+
+
+static CPUReadMemoryFunc *tis_readfn[3] = {
+    tis_mem_readl,
+    tis_mem_readl,
+    tis_mem_readl
+};
+
+static CPUWriteMemoryFunc *tis_writefn[3] = {
+    tis_mem_writel,
+    tis_mem_writel,
+    tis_mem_writel
+};
+
+
+/*
+ * This function gets called when resuming a snapshot. In that
+ * case we received the TIS state from persistent storage and
+ * just need to reset.
+ */
+void tis_reset_for_snapshot_resume(TPMState *s)
+{
+    s->tpm_initialized = false;
+    active_be->reset();
+    /* early startup not possible here */
+}
+
+
+static int tis_do_early_startup_tpm(TPMState *s)
+{
+    int rc;
+    /*
+     * Attempt an early startup of the backend; this only works
+     * if the block storage is not encrypted since the key is not
+     * available yet.
+     */
+    rc = active_be->early_startup_tpm();
+
+    switch (rc) {
+    case 0:
+#ifdef DEBUG_TIS
+        fprintf(stderr,"tpm_tis: Early startup worked -- "
+                "TPM backend is initialized\n");
+#endif
+        s->tpm_initialized = true;
+        break;
+
+    case -ENOKEY:
+#ifdef DEBUG_TIS
+        fprintf(stderr,"tpm_tis: Early startup failed -- "
+                "no key for encrypted drive\n");
+#endif
+        break;
+
+    default:
+        break;
+    }
+
+    return rc;
+}
+
+
+/*
+ * This function is called when the machine starts, resets or due to
+ * S3 resume.
+ */
+static void tis_s_reset(TPMState *s)
+{
+    int c;
+
+    s->tpm_initialized = false;
+
+    active_be->reset();
+
+    s->active_locty = NO_LOCALITY;
+    s->next_locty = NO_LOCALITY;
+    s->aborting_locty = NO_LOCALITY;
+
+    for (c = 0; c < NUM_LOCALITIES; c++) {
+        s->loc[c].access = ACCESS_TPM_REG_VALID_STS;
+        s->loc[c].sts = 0;
+        s->loc[c].inte = (1 << 3);
+        s->loc[c].ints = 0;
+        s->loc[c].state = STATE_IDLE;
+
+        s->loc[c].w_offset = 0;
+        active_be->realloc_buffer(&s->loc[c].w_buffer);
+        s->loc[c].r_offset = 0;
+        active_be->realloc_buffer(&s->loc[c].r_buffer);
+    }
+
+    tis_do_early_startup_tpm(s);
+}
+
+
+static void tis_reset(DeviceState *d)
+{
+    TPMState *s = container_of(d, TPMState, busdev.qdev);
+    tis_s_reset(s);
+}
+
+
+static int tis_init(ISADevice *dev)
+{
+    TPMState *s = DO_UPCAST(TPMState, busdev, dev);
+    int iomemtype, rc;
+
+    qemu_mutex_init(&s->state_lock);
+    qemu_cond_init(&s->from_tpm_cond);
+    qemu_cond_init(&s->to_tpm_cond);
+
+    if (active_be->init(s, tis_tpm_receive_cb)) {
+        goto err_exit;
+    }
+
+    isa_init_irq(dev, &s->irq, s->irq_num);
+
+    iomemtype = cpu_register_io_memory(tis_readfn, tis_writefn, s,
+                                       DEVICE_LITTLE_ENDIAN);
+    cpu_register_physical_memory(TIS_ADDR_BASE, 0x1000 * NUM_LOCALITIES,
+                                 iomemtype);
+
+    /*
+     * startup the TPM backend early to detect problems early
+     */
+    rc = tis_do_early_startup_tpm(s);
+    if (rc != 0 && rc != -ENOKEY) {
+        fprintf(stderr,"tpm_tis: Fatal error accessing TPM's block storage.\n");
+        goto err_exit;
+    }
+
+    return 0;
+
+ err_exit:
+    return -1;
+}
+

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

* [Qemu-devel] [PATCH V4 03/10] Add persistent state handling to TPM TIS frontend driver
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options Stefan Berger
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 02/10] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2011-05-06 17:32 ` Stefan Berger
  2011-05-18  7:25   ` Markus Armbruster
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 04/10] Add tpm_tis driver to build process Stefan Berger
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

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

This patch adds support for handling of persistent state to the TPM TIS
frontend.

The currently used buffer is determined (can only be in currently active
locality and either be a read or a write buffer) and only that buffer's content
is stored. The reverse is done when the state is restored from disk
where the buffer's content are copied into the currently used buffer.

To keep compatibility with existing Xen the VMStateDescription was adapted
to be compatible with existing state. For that I am adding Andreas
Niederl as an author to the file.

v4:
 - main thread releases the 'state' lock while periodically calling the
   backends function that may request it to write data into block storage.

v3:
 - all functions prefixed with tis_
 - while the main thread is waiting for an outstanding TPM command to finish,
   it periodically does some work (writes data to the block storage)

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

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

Index: qemu-git/hw/tpm_tis.c
===================================================================
--- qemu-git.orig/hw/tpm_tis.c
+++ qemu-git/hw/tpm_tis.c
@@ -6,6 +6,8 @@
  * Author: 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
@@ -869,3 +871,168 @@ static int tis_init(ISADevice *dev)
     return -1;
 }
 
+/* persistent state handling */
+
+static void tis_pre_save(void *opaque)
+{
+    TPMState *s = opaque;
+    uint8_t locty = s->active_locty;
+
+    qemu_mutex_lock(&s->state_lock);
+
+    /* wait for outstanding requests to complete */
+    if (IS_VALID_LOCTY(locty) && s->loc[locty].state == STATE_EXECUTION) {
+        if (!active_be->job_for_main_thread) {
+            qemu_cond_wait(&s->from_tpm_cond, &s->state_lock);
+        } else {
+            while (s->loc[locty].state == STATE_EXECUTION) {
+                qemu_mutex_unlock(&s->state_lock);
+
+                active_be->job_for_main_thread(NULL);
+                usleep(10000);
+
+                qemu_mutex_lock(&s->state_lock);
+            }
+        }
+    }
+
+#ifdef DEBUG_TIS_SR
+    fprintf(stderr,"tpm_tis: suspend: locty 0 : r_offset = %d, w_offset = %d\n",
+        s->loc[0].r_offset,
+        s->loc[0].w_offset);
+    if (s->loc[0].r_offset) {
+        tis_dump_state(opaque, 0);
+    }
+#endif
+
+    qemu_mutex_unlock(&s->state_lock);
+
+    /* copy current active read or write buffer into the buffer
+       written to disk */
+    if (IS_VALID_LOCTY(locty)) {
+        switch (s->loc[locty].state) {
+        case STATE_RECEPTION:
+            memcpy(s->buf,
+                   s->loc[locty].w_buffer.buffer,
+                   MIN(sizeof(s->buf),
+                       s->loc[locty].w_buffer.size));
+            s->offset = s->loc[locty].w_offset;
+        break;
+        case STATE_COMPLETION:
+            memcpy(s->buf,
+                   s->loc[locty].r_buffer.buffer,
+                   MIN(sizeof(s->buf),
+                       s->loc[locty].r_buffer.size));
+            s->offset = s->loc[locty].r_offset;
+        break;
+        default:
+            /* leak nothing */
+            memset(s->buf, 0x0, sizeof(s->buf));
+        break;
+        }
+    }
+
+    tis_get_active_backend()->save_volatile_data();
+}
+
+
+static int tis_post_load(void *opaque,
+                         int version_id __attribute__((unused)))
+{
+    TPMState *s = opaque;
+
+    uint8_t locty = s->active_locty;
+
+    if (IS_VALID_LOCTY(locty)) {
+        switch (s->loc[locty].state) {
+        case STATE_RECEPTION:
+            memcpy(s->loc[locty].w_buffer.buffer,
+                   s->buf,
+                   MIN(sizeof(s->buf),
+                       s->loc[locty].w_buffer.size));
+            s->loc[locty].w_offset = s->offset;
+        break;
+        case STATE_COMPLETION:
+            memcpy(s->loc[locty].r_buffer.buffer,
+                   s->buf,
+                   MIN(sizeof(s->buf),
+                       s->loc[locty].r_buffer.size));
+            s->loc[locty].r_offset = s->offset;
+        break;
+        default:
+        break;
+        }
+    }
+
+#ifdef DEBUG_TIS_SR
+    fprintf(stderr,"tpm_tis: resume : locty 0 : r_offset = %d, w_offset = %d\n",
+            s->loc[0].r_offset,
+            s->loc[0].w_offset);
+#endif
+
+    return tis_get_active_backend()->load_volatile_data(s);
+}
+
+
+static const VMStateDescription vmstate_locty = {
+    .name = "loc",
+    .version_id = 1,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(state   , TPMLocality),
+        VMSTATE_UINT32(inte    , TPMLocality),
+        VMSTATE_UINT32(ints    , TPMLocality),
+        VMSTATE_UINT8 (access  , TPMLocality),
+        VMSTATE_UINT8 (sts     , TPMLocality),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+
+static const VMStateDescription vmstate_tis = {
+    .name = "tpm",
+    .version_id = 1,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .pre_save  = tis_pre_save,
+    .post_load = tis_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32(irq_num       , TPMState),
+        VMSTATE_UINT32(offset        , TPMState),
+        VMSTATE_BUFFER(buf           , TPMState),
+        VMSTATE_UINT8 (  active_locty, TPMState),
+        VMSTATE_UINT8 (aborting_locty, TPMState),
+        VMSTATE_UINT8 (    next_locty, TPMState),
+
+        VMSTATE_STRUCT_ARRAY(loc, TPMState, NUM_LOCALITIES, 1,
+                             vmstate_locty, TPMLocality),
+
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+static ISADeviceInfo tis_device_info = {
+    .init         = tis_init,
+    .qdev.name    = "tpm-tis",
+    .qdev.size    = sizeof(TPMState),
+    .qdev.no_user = 1,
+    .qdev.vmsd    = &vmstate_tis,
+    .qdev.reset   = tis_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT8("active_locality", TPMState,
+                          active_locty, NO_LOCALITY),
+        DEFINE_PROP_UINT32("irq", TPMState,
+                           irq_num, TPM_TIS_IRQ),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+
+static void tis_register_device(void)
+{
+    isa_qdev_register(&tis_device_info);
+}
+
+device_init(tis_register_device)

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

* [Qemu-devel] [PATCH V4 04/10] Add tpm_tis driver to build process
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (2 preceding siblings ...)
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 03/10] Add persistent state handling to TPM TIS frontend driver Stefan Berger
@ 2011-05-06 17:32 ` Stefan Berger
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 05/10] Add a debug register Stefan Berger
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

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

The TPM interface (tpm_tis) needs to be explicitly enabled via 
./configure --enable-tpm. This patch also restricts the building of the
TPM support to i386 and x86_64 targets since both backends I know
of, the Xen backend and the libtpms-based backend, will likely only
be available for these targets, at least initially. The list can be
easily extend. I am trying to prevent that one will end up with
support for a frontend but no available backend.

v3:
 - fixed and moved hunks in Makefile.target into right place

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

Index:qemu/Makefile.target
===================================================================
---
 Makefile.target |    1 +
 configure       |   20 ++++++++++++++++++++
 2 files changed, 21 insertions(+)

Index: qemu-git/Makefile.target
===================================================================
--- qemu-git.orig/Makefile.target
+++ qemu-git/Makefile.target
@@ -226,6 +226,7 @@ obj-i386-y += device-hotplug.o pci-hotpl
 obj-i386-y += debugcon.o multiboot.o
 obj-i386-y += pc_piix.o kvmclock.o
 obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
+obj-i386-$(CONFIG_TPM) += tpm_tis.o
 
 # shared objects
 obj-ppc-y = ppc.o
Index: qemu-git/configure
===================================================================
--- qemu-git.orig/configure
+++ qemu-git/configure
@@ -178,6 +178,7 @@ rbd=""
 smartcard=""
 smartcard_nss=""
 opengl=""
+tpm="no"
 
 # parse CC options first
 for opt do
@@ -713,6 +714,8 @@ for opt do
   ;;
   --kerneldir=*) kerneldir="$optarg"
   ;;
+  --enable-tpm) tpm="yes"
+  ;;
   --with-pkgversion=*) pkgversion=" ($optarg)"
   ;;
   --disable-docs) docs="no"
@@ -945,6 +948,7 @@ echo "  --disable-smartcard      disable
 echo "  --enable-smartcard       enable smartcard support"
 echo "  --disable-smartcard-nss  disable smartcard nss support"
 echo "  --enable-smartcard-nss   enable smartcard nss support"
+echo "  --enable-tpm             enables an emulated TPM"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -2623,6 +2627,7 @@ echo "rbd support       $rbd"
 echo "xfsctl support    $xfs"
 echo "nss used          $smartcard_nss"
 echo "OpenGL support    $opengl"
+echo "TPM support       $tpm"
 
 if test $sdl_too_old = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3433,6 +3438,21 @@ if test "$gprof" = "yes" ; then
   fi
 fi
 
+if test "$tpm" = "yes"; then
+  has_tpm=0
+  if test "$target_softmmu" = "yes" ; then
+    case "$TARGET_BASE_ARCH" in
+    i386)
+      has_tpm=1
+    ;;
+    esac
+  fi
+
+  if test "$has_tpm" = "1"; then
+      echo "CONFIG_TPM=y" >> $config_host_mak
+  fi
+fi
+
 linker_script="-Wl,-T../config-host.ld -Wl,-T,\$(SRC_PATH)/\$(ARCH).ld"
 if test "$target_linux_user" = "yes" -o "$target_bsd_user" = "yes" ; then
   case "$ARCH" in

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

* [Qemu-devel] [PATCH V4 05/10] Add a debug register
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (3 preceding siblings ...)
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 04/10] Add tpm_tis driver to build process Stefan Berger
@ 2011-05-06 17:32 ` Stefan Berger
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 06/10] Add a TPM backend skeleton implementation Stefan Berger
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

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

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

v3:
 - all output goes to stderr

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

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

Index: qemu-git/hw/tpm_tis.c
===================================================================
--- qemu-git.orig/hw/tpm_tis.c
+++ qemu-git/hw/tpm_tis.c
@@ -42,6 +42,8 @@
 #define TIS_REG_DID_VID               0xf00
 #define TIS_REG_RID                   0xf04
 
+/* vendor-specific registers */
+#define TIS_REG_DEBUG                 0xf90
 
 #define STS_VALID                    (1 << 7)
 #define STS_COMMAND_READY            (1 << 6)
@@ -356,6 +358,66 @@ static uint32_t tis_data_read(TPMState *
 }
 
 
+#ifdef DEBUG_TIS
+static void tis_dump_state(void *opaque, target_phys_addr_t addr)
+{
+    static const unsigned regs[] = {
+        TIS_REG_ACCESS,
+        TIS_REG_INT_ENABLE,
+        TIS_REG_INT_VECTOR,
+        TIS_REG_INT_STATUS,
+        TIS_REG_INTF_CAPABILITY,
+        TIS_REG_STS,
+        TIS_REG_DID_VID,
+        TIS_REG_RID,
+        0xfff};
+    int idx;
+    uint8_t locty = tis_locality_from_addr(addr);
+    target_phys_addr_t base = addr & ~0xfff;
+    TPMState *s = opaque;
+
+    fprintf(stderr,
+            "tpm_tis: active locality      : %d\n"
+            "tpm_tis: state of locality %d : %d\n"
+            "tpm_tis: register dump:\n",
+            s->active_locty,
+            locty, s->loc[locty].state);
+
+    for (idx = 0; regs[idx] != 0xfff; idx++) {
+        fprintf(stderr, "tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
+                        tis_mem_readl(opaque, base + regs[idx]));
+    }
+
+    fprintf(stderr,
+            "tpm_tis: read offset   : %d\n"
+            "tpm_tis: result buffer : ",
+            s->loc[locty].r_offset);
+    for (idx = 0;
+         idx < tis_get_size_from_buffer(&s->loc[locty].r_buffer);
+         idx++) {
+        fprintf(stderr, "%c%02x%s",
+                s->loc[locty].r_offset == idx ? '>' : ' ',
+                s->loc[locty].r_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    }
+    fprintf(stderr,
+            "\n"
+            "tpm_tis: write offset  : %d\n"
+            "tpm_tis: request buffer: ",
+            s->loc[locty].w_offset);
+    for (idx = 0;
+         idx < tis_get_size_from_buffer(&s->loc[locty].w_buffer);
+         idx++) {
+        fprintf(stderr, "%c%02x%s",
+                s->loc[locty].w_offset == idx ? '>' : ' ',
+                s->loc[locty].w_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    }
+    fprintf(stderr,"\n");
+}
+#endif
+
+
 /*
  * Read a register of the TIS interface
  * See specs pages 33-63 for description of the registers
@@ -431,6 +493,11 @@ static uint32_t tis_mem_readl(void *opaq
     case TIS_REG_RID:
         val = TPM_RID;
         break;
+#ifdef DEBUG_TIS
+    case TIS_REG_DEBUG:
+        tis_dump_state(opaque, addr);
+        break;
+#endif
     }
 
     qemu_mutex_unlock(&s->state_lock);

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

* [Qemu-devel] [PATCH V4 06/10] Add a TPM backend skeleton implementation
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (4 preceding siblings ...)
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 05/10] Add a debug register Stefan Berger
@ 2011-05-06 17:32 ` Stefan Berger
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 07/10] Implementation of the libtpms-based backend Stefan Berger
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

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

This patch provides a TPM backend skeleton implementation. It doesn't do
anything useful (except for returning error response for every TPM command)
but it compiles.

v3:
  - in tpm_builtin.c all functions prefixed with tpm_builtin_
  - build the builtin TPM driver available at this point; it returns
    a failure response message for every command
  - do not try to join the TPM thread but poll for its termination;
    the libtpms-based driver will require Qemu's main thread to write
    data to the block storage device while trying to join

V2:
  - only terminating thread in tpm_atexit if it's running

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

---
 Makefile.target  |    5 
 configure        |    1 
 hw/tpm_builtin.c |  425 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/tpm_tis.c     |    3 
 hw/tpm_tis.h     |    2 
 5 files changed, 436 insertions(+)

Index: qemu-git/hw/tpm_builtin.c
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_builtin.c
@@ -0,0 +1,425 @@
+/*
+ *  builtin 'null' TPM driver
+ *
+ *  Copyright (c) 2010, 2011 IBM Corporation
+ *  Copyright (c) 2010, 2011 Stefan Berger
+ *
+ * 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 "hw/hw.h"
+#include "hw/tpm_tis.h"
+#include "hw/pc.h"
+
+
+//#define DEBUG_TPM
+//#define DEBUG_TPM_SR /* suspend - resume */
+
+
+/* data structures */
+
+typedef struct ThreadParams {
+    TPMState *tpm_state;
+
+    TPMRecvDataCB *recv_data_callback;
+} ThreadParams;
+
+
+/* local variables */
+
+static QemuThread thread;
+
+static QemuMutex state_mutex; /* protects *_state below */
+static QemuMutex tpm_initialized_mutex; /* protect tpm_initialized */
+
+static bool thread_terminate = false;
+static bool tpm_initialized = false;
+static bool had_fatal_error = false;
+static bool had_startup_error = false;
+static bool thread_running = false;
+
+static ThreadParams tpm_thread_params;
+
+/* locality of the command being executed by libtpms */
+static uint8_t g_locty;
+
+static const unsigned char tpm_std_fatal_error_response[10] = {
+    0x00, 0xc4, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09 /* TPM_FAIL */
+};
+
+static char dev_description[80];
+
+
+static void *tpm_builtin_main_loop(void *d)
+{
+    int res = 1;
+    ThreadParams *thr_parms = d;
+    uint32_t in_len, out_len;
+    uint8_t *in, *out;
+    uint32_t resp_size; /* total length of response */
+
+#ifdef DEBUG_TPM
+    fprintf(stderr, "tpm: THREAD IS STARTING\n");
+#endif
+
+    if (res != 0) {
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+        fprintf(stderr, "tpm: Error: TPM initialization failed (rc=%d)\n",
+                res);
+#endif
+	had_fatal_error = true;
+    } else {
+        qemu_mutex_lock(&tpm_initialized_mutex);
+
+        tpm_initialized = true;
+
+        qemu_mutex_unlock(&tpm_initialized_mutex);
+    }
+
+    /* start command processing */
+    while (!thread_terminate) {
+        /* receive and handle commands */
+        in_len = 0;
+        do {
+#ifdef DEBUG_TPM
+            fprintf(stderr, "tpm: waiting for commands...\n");
+#endif
+
+            if (thread_terminate) {
+                break;
+            }
+
+            qemu_mutex_lock(&thr_parms->tpm_state->state_lock);
+
+            /* in case we were to slow and missed the signal, the
+               to_tpm_execute boolean tells us about a pending command */
+            if (!thr_parms->tpm_state->to_tpm_execute) {
+                qemu_cond_wait(&thr_parms->tpm_state->to_tpm_cond,
+                               &thr_parms->tpm_state->state_lock);
+            }
+
+            thr_parms->tpm_state->to_tpm_execute = false;
+
+            qemu_mutex_unlock(&thr_parms->tpm_state->state_lock);
+
+            if (thread_terminate) {
+                break;
+            }
+
+            g_locty = thr_parms->tpm_state->command_locty;
+
+            in = thr_parms->tpm_state->loc[g_locty].w_buffer.buffer;
+            in_len = thr_parms->tpm_state->loc[g_locty].w_offset;
+
+            if (!had_fatal_error) {
+
+                out_len = thr_parms->tpm_state->loc[g_locty].r_buffer.size;
+
+#ifdef DEBUG_TPM
+                fprintf(stderr,
+                        "tpm: received %d bytes from VM in locality %d\n",
+                        in_len,
+                        g_locty);
+                dumpBuffer(stderr, in, in_len);
+#endif
+
+                resp_size = 0;
+
+                /* !!! Send command to TPM & wait for response */
+
+                if (res != 0) {
+#ifdef DEBUG_TPM
+                    fprintf(stderr,
+                            "tpm: Sending/receiving TPM request/response "
+                            "failed.\n");
+#endif
+                    had_fatal_error = 1;
+                }
+            }
+
+            if (had_fatal_error) {
+                out = thr_parms->tpm_state->loc[g_locty].r_buffer.buffer;
+                resp_size = sizeof(tpm_std_fatal_error_response);
+                memcpy(out, tpm_std_fatal_error_response, resp_size);
+                out[1] = (in_len > 2 && in[1] >= 0xc1 && in[1] <= 0xc3)
+                       ? in[1] + 3
+                       : 0xc4;
+            }
+#ifdef DEBUG_TPM
+            fprintf(stderr, "tpm: sending %d bytes to VM\n", resp_size);
+            dumpBuffer(stderr,
+                       thr_parms->tpm_state->loc[g_locty].r_buffer.buffer,
+                       resp_size);
+#endif
+            thr_parms->recv_data_callback(thr_parms->tpm_state, g_locty);
+        } while (in_len > 0);
+    }
+
+    qemu_mutex_lock(&tpm_initialized_mutex);
+
+    if (tpm_initialized) {
+        tpm_initialized = false;
+    }
+
+    qemu_mutex_unlock(&tpm_initialized_mutex);
+
+#ifdef DEBUG_TPM
+    fprintf(stderr, "tpm: THREAD IS ENDING\n");
+#endif
+
+    thread_running = false;
+
+    return NULL;
+}
+
+
+static void tpm_builtin_terminate_tpm_thread(void)
+{
+    if (!thread_running) {
+        return;
+    }
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    fprintf(stderr, "tpm: TERMINATING RUNNING TPM THREAD\n");
+#endif
+
+    if (!thread_terminate) {
+        thread_terminate = true;
+
+        qemu_mutex_lock  (&tpm_thread_params.tpm_state->state_lock);
+        qemu_cond_signal (&tpm_thread_params.tpm_state->to_tpm_cond);
+        qemu_mutex_unlock(&tpm_thread_params.tpm_state->state_lock);
+
+        /* The thread will set thread_running = false; it may
+         * still ask us to write data to the disk, though.
+         */
+        while (thread_running) {
+            /* !!! write data to disk if necessary */
+            usleep(100000);
+        }
+
+        memset(&thread, 0, sizeof(thread));
+    }
+}
+
+
+static void tpm_builtin_tpm_atexit(void)
+{
+    tpm_builtin_terminate_tpm_thread();
+}
+
+
+/**
+ * Start the TPM (thread). If it had been started before, then terminate
+ * and start it again.
+ */
+static int tpm_builtin_startup_tpm(void)
+{
+    /* terminate a running TPM */
+    tpm_builtin_terminate_tpm_thread();
+
+    /* reset the flag so the thread keeps on running */
+    thread_terminate = false;
+
+    qemu_thread_create(&thread, tpm_builtin_main_loop, &tpm_thread_params);
+
+    thread_running = true;
+
+    return 0;
+}
+
+
+static int tpm_builtin_do_startup_tpm(void)
+{
+    return tpm_builtin_startup_tpm();
+}
+
+
+/*
+ * Startup the TPM early. This only works for non-encrytped
+ * BlockStorage, since we would not have the key yet.
+ */
+static int tpm_builtin_early_startup_tpm(void)
+{
+    return tpm_builtin_do_startup_tpm();
+}
+
+
+/*
+ * Start up the TPM before it sees the first command.
+ * We need to do this late since only now we will have the
+ * block storage encryption key and can read the previous
+ * TPM state. During 'reset' the key would not be available.
+ */
+static int tpm_builtin_late_startup_tpm(void)
+{
+    /* give it a new try */
+    had_fatal_error = false;
+    had_startup_error = false;
+
+    if (tpm_builtin_do_startup_tpm()) {
+        had_fatal_error = true;
+    }
+
+    return had_fatal_error;
+}
+
+
+static void tpm_builtin_reset(void)
+{
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    fprintf(stderr, "tpm: CALL TO TPM_RESET!\n");
+#endif
+
+    tpm_builtin_terminate_tpm_thread();
+
+    had_fatal_error = false;
+    had_startup_error = false;
+}
+
+
+/*
+ * restore TPM volatile state from given data
+ *
+ * The data are ignore by this driver, instead we read the volatile state
+ * from the TPM block store.
+ *
+ * This function gets called by Qemu when
+ * (1) resuming after a suspend
+ * (2) resuming a snapshot
+ *
+ * (1) works fine since we get call to the reset function as well
+ * (2) requires us to call the reset function ourselves; we do this
+ *     indirectly by calling the tis_reset_for_snapshot_resume();
+ *     a sure indicator of whether this function is called due to a resume
+ *     of a snapshot is that the tread_running variable is 'true'.
+ *
+ */
+static int tpm_builtin_instantiate_with_volatile_data(TPMState *s)
+{
+    if (thread_running) {
+#ifdef DEBUG_TPM_SR
+        fprintf(stderr, "tpm: This is resume of a SNAPSHOT\n");
+#endif
+        tis_reset_for_snapshot_resume(s);
+    }
+
+    return 0;
+}
+
+
+static int tpm_builtin_init(TPMState *s, TPMRecvDataCB *recv_data_cb)
+{
+    tpm_thread_params.tpm_state = s;
+    tpm_thread_params.recv_data_callback = recv_data_cb;
+
+    qemu_mutex_init(&state_mutex);
+    qemu_mutex_init(&tpm_initialized_mutex);
+
+    atexit(tpm_builtin_tpm_atexit);
+
+    return 0;
+}
+
+
+static bool tpm_builtin_get_tpm_established_flag(void)
+{
+    return false;
+}
+
+
+static bool tpm_builtin_get_startup_error(void)
+{
+    return had_startup_error;
+}
+
+
+/**
+ * This function is called by tpm_tis.c once the TPM has processed
+ * the last command and returned the response to the TIS.
+ */
+static int tpm_builtin_save_volatile_data(void)
+{
+    if (!tpm_initialized) {
+        /* TPM was never initialized
+           volatile_state.buffer may be NULL if TPM was never used.
+         */
+        return 0;
+    }
+
+    return 0;
+}
+
+
+static size_t tpm_builtin_realloc_buffer(TPMSizedBuffer *sb)
+{
+    size_t wanted_size = 4096;
+
+    if (sb->size != wanted_size) {
+        sb->buffer = qemu_realloc(sb->buffer, wanted_size);
+        if (sb->buffer != NULL) {
+            sb->size = wanted_size;
+        } else {
+            sb->size = 0;
+        }
+    }
+    return sb->size;
+}
+
+
+static const char *tpm_builtin_create_desc(void)
+{
+    static int done;
+
+    if (!done) {
+        snprintf(dev_description, sizeof(dev_description),
+                 "Skeleton TPM backend");
+        done = 1;
+    }
+
+    return dev_description;
+}
+
+
+static bool tpm_builtin_handle_options(QemuOpts *opts)
+{
+    const char *value;
+
+    value = qemu_opt_get(opts, "path");
+    if (value) {
+        /* !!! handle file path */
+    } else {
+        fprintf(stderr, "-tpm is missing path= parameter\n");
+        return false;
+    }
+    return true;
+}
+
+
+BackendTPMDriver tpm_builtin = {
+    .id                       = "builtin",
+    .desc                     = tpm_builtin_create_desc,
+    .job_for_main_thread      = NULL,
+    .handle_options           = tpm_builtin_handle_options,
+    .init                     = tpm_builtin_init,
+    .early_startup_tpm        = tpm_builtin_early_startup_tpm,
+    .late_startup_tpm         = tpm_builtin_late_startup_tpm,
+    .realloc_buffer           = tpm_builtin_realloc_buffer,
+    .reset                    = tpm_builtin_reset,
+    .had_startup_error        = tpm_builtin_get_startup_error,
+    .save_volatile_data       = tpm_builtin_save_volatile_data,
+    .load_volatile_data       = tpm_builtin_instantiate_with_volatile_data,
+    .get_tpm_established_flag = tpm_builtin_get_tpm_established_flag,
+};
Index: qemu-git/Makefile.target
===================================================================
--- qemu-git.orig/Makefile.target
+++ qemu-git/Makefile.target
@@ -227,6 +227,11 @@ obj-i386-y += debugcon.o multiboot.o
 obj-i386-y += pc_piix.o kvmclock.o
 obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
 obj-i386-$(CONFIG_TPM) += tpm_tis.o
+obj-i386-$(CONFIG_TPM_BUILTIN) += tpm_builtin.o
+
+ifdef CONFIG_TPM_BUILTIN
+LIBS+=-ltpms
+endif
 
 # shared objects
 obj-ppc-y = ppc.o
Index: qemu-git/hw/tpm_tis.c
===================================================================
--- qemu-git.orig/hw/tpm_tis.c
+++ qemu-git/hw/tpm_tis.c
@@ -95,6 +95,9 @@ static uint32_t tis_mem_readl(void *opaq
 
 
 static const BackendTPMDriver *bes[] = {
+#ifdef CONFIG_TPM_BUILTIN
+    &tpm_builtin,
+#endif
     NULL,
 };
 
Index: qemu-git/configure
===================================================================
--- qemu-git.orig/configure
+++ qemu-git/configure
@@ -3449,6 +3449,7 @@ if test "$tpm" = "yes"; then
   fi
 
   if test "$has_tpm" = "1"; then
+      echo "CONFIG_TPM_BUILTIN=y" >> $config_target_mak
       echo "CONFIG_TPM=y" >> $config_host_mak
   fi
 fi
Index: qemu-git/hw/tpm_tis.h
===================================================================
--- qemu-git.orig/hw/tpm_tis.h
+++ qemu-git/hw/tpm_tis.h
@@ -116,6 +116,8 @@ typedef struct BackendTPMDriver {
 } BackendTPMDriver;
 
 
+extern BackendTPMDriver tpm_builtin;
+
 void tis_reset_for_snapshot_resume(TPMState *s);
 const BackendTPMDriver *tis_get_active_backend(void);
 

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

* [Qemu-devel] [PATCH V4 07/10] Implementation of the libtpms-based backend
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (5 preceding siblings ...)
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 06/10] Add a TPM backend skeleton implementation Stefan Berger
@ 2011-05-06 17:32 ` Stefan Berger
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 08/10] Introduce file lock for the block layer Stefan Berger
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

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

This patch provides the glue for the TPM TIS interface (frontend) to
the libtpms that provides the actual TPM functionality.

Some details:

This part of the patch provides support for the spawning of a thread
that will interact with the libtpms-based TPM. It expects a signal
from the frontend to wake and pick up the TPM command that is supposed
to be processed and delivers the response packet using a callback
function provided by the frontend.

The backend connectects itself to the frontend by filling out an interface
structure with pointers to the function implementing support for various
operations.

In this part a structure with callback functions with is registered with
libtpms. Those callback functions mostly deal with persistent storage.

The libtpms-based backend implements functionality to write into a 
Qemu block storage device rather than to plain files. With that we
can support VM snapshotting and we also get the possibility to use
encrypted QCoW2 for free. Thanks to Anthony for pointing this out.
The storage part of the driver has been split off into its own patch.

v3:
  - temporarily deactivate the building of the tpm_builtin.c until
    subsequent patch completely converts it to the libtpms based driver

v2:
  - fixes to adhere to the qemu coding style


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

---
 configure        |    1 
 hw/tpm_builtin.c |  415 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 hw/tpm_tis.h     |   17 ++
 3 files changed, 412 insertions(+), 21 deletions(-)

Index: qemu-git/hw/tpm_tis.h
===================================================================
--- qemu-git.orig/hw/tpm_tis.h
+++ qemu-git/hw/tpm_tis.h
@@ -142,4 +142,21 @@ static inline void dumpBuffer(FILE *stre
     fprintf(stream, "\n");
 }
 
+static inline void clear_sized_buffer(TPMSizedBuffer *tpmsb)
+{
+    if (tpmsb->buffer) {
+       tpmsb->size = 0;
+       qemu_free(tpmsb->buffer);
+       tpmsb->buffer = NULL;
+    }
+}
+
+static inline void set_sized_buffer(TPMSizedBuffer *tpmsb,
+                                    uint8_t *buffer, uint32_t size)
+{
+    clear_sized_buffer(tpmsb);
+    tpmsb->size = size;
+    tpmsb->buffer = buffer;
+}
+
 #endif /* _HW_TPM_TIS_H */
Index: qemu-git/hw/tpm_builtin.c
===================================================================
--- qemu-git.orig/hw/tpm_builtin.c
+++ qemu-git/hw/tpm_builtin.c
@@ -1,5 +1,5 @@
 /*
- *  builtin 'null' TPM driver
+ *  builtin TPM driver based on libtpms
  *
  *  Copyright (c) 2010, 2011 IBM Corporation
  *  Copyright (c) 2010, 2011 Stefan Berger
@@ -18,16 +18,32 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>
  */
 
+#include "blockdev.h"
+#include "block_int.h"
 #include "qemu-common.h"
 #include "hw/hw.h"
 #include "hw/tpm_tis.h"
 #include "hw/pc.h"
+#include "migration.h"
+#include "sysemu.h"
+
+#include <libtpms/tpm_library.h>
+#include <libtpms/tpm_error.h>
+#include <libtpms/tpm_memory.h>
+#include <libtpms/tpm_nvfilename.h>
+#include <libtpms/tpm_tis.h>
+
+#include <zlib.h>
 
 
 //#define DEBUG_TPM
 //#define DEBUG_TPM_SR /* suspend - resume */
 
 
+#define SAVESTATE_TYPE 'S'
+#define PERMSTATE_TYPE 'P'
+#define VOLASTATE_TYPE 'V'
+
 /* data structures */
 
 typedef struct ThreadParams {
@@ -43,12 +59,18 @@ static QemuThread thread;
 
 static QemuMutex state_mutex; /* protects *_state below */
 static QemuMutex tpm_initialized_mutex; /* protect tpm_initialized */
+static QemuCond bs_write_result_cond;
+static TPMSizedBuffer permanent_state = { .size = 0, .buffer = NULL, };
+static TPMSizedBuffer volatile_state  = { .size = 0, .buffer = NULL, };
+static TPMSizedBuffer save_state      = { .size = 0, .buffer = NULL, };
+static int pipefd[2] =  {-1, -1};
 
 static bool thread_terminate = false;
 static bool tpm_initialized = false;
 static bool had_fatal_error = false;
 static bool had_startup_error = false;
 static bool thread_running = false;
+static bool need_read_volatile = false;
 
 static ThreadParams tpm_thread_params;
 
@@ -62,9 +84,21 @@ static const unsigned char tpm_std_fatal
 static char dev_description[80];
 
 
+static int tpmlib_get_prop(enum TPMLIB_TPMProperty prop)
+{
+    int result;
+
+    TPM_RESULT res = TPMLIB_GetTPMProperty(prop, &result);
+
+    assert(res == TPM_SUCCESS);
+
+    return result;
+}
+
+
 static void *tpm_builtin_main_loop(void *d)
 {
-    int res = 1;
+    TPM_RESULT res;
     ThreadParams *thr_parms = d;
     uint32_t in_len, out_len;
     uint8_t *in, *out;
@@ -74,9 +108,10 @@ static void *tpm_builtin_main_loop(void 
     fprintf(stderr, "tpm: THREAD IS STARTING\n");
 #endif
 
-    if (res != 0) {
+    res = TPMLIB_MainInit();
+    if (res != TPM_SUCCESS) {
 #if defined DEBUG_TPM || defined DEBUG_TPM_SR
-        fprintf(stderr, "tpm: Error: TPM initialization failed (rc=%d)\n",
+        fprintf(stderr," tpm: Error: Call to TPMLIB_MainInit() failed (rc=%d)\n",
                 res);
 #endif
 	had_fatal_error = true;
@@ -94,6 +129,8 @@ static void *tpm_builtin_main_loop(void 
         in_len = 0;
         do {
 #ifdef DEBUG_TPM
+            /* TPMLIB output goes to stderr */
+            fflush(stdout);
             fprintf(stderr, "tpm: waiting for commands...\n");
 #endif
 
@@ -137,13 +174,19 @@ static void *tpm_builtin_main_loop(void 
 
                 resp_size = 0;
 
-                /* !!! Send command to TPM & wait for response */
+                /* TPMLIB_Process may realloc the response buffer */
+                res = TPMLIB_Process(
+                    &thr_parms->tpm_state->loc[g_locty].r_buffer.buffer,
+                    &resp_size, &out_len,
+                    in, in_len);
 
-                if (res != 0) {
 #ifdef DEBUG_TPM
-                    fprintf(stderr,
-                            "tpm: Sending/receiving TPM request/response "
-                            "failed.\n");
+                fflush(stdout);
+#endif
+
+                if (res != TPM_SUCCESS) {
+#ifdef DEBUG_TPM
+                    fprintf(stderr, "tpm: TPMLIB_Process() failed\n");
 #endif
                     had_fatal_error = 1;
                 }
@@ -171,11 +214,13 @@ static void *tpm_builtin_main_loop(void 
 
     if (tpm_initialized) {
         tpm_initialized = false;
+        TPMLIB_Terminate();
     }
 
     qemu_mutex_unlock(&tpm_initialized_mutex);
 
 #ifdef DEBUG_TPM
+    fflush(stdout);
     fprintf(stderr, "tpm: THREAD IS ENDING\n");
 #endif
 
@@ -206,7 +251,7 @@ static void tpm_builtin_terminate_tpm_th
          * still ask us to write data to the disk, though.
          */
         while (thread_running) {
-            /* !!! write data to disk if necessary */
+            tpm_builtin_fulfill_sync_to_bs_request(NULL);
             usleep(100000);
         }
 
@@ -218,6 +263,12 @@ static void tpm_builtin_terminate_tpm_th
 static void tpm_builtin_tpm_atexit(void)
 {
     tpm_builtin_terminate_tpm_thread();
+
+    close(pipefd[0]);
+    pipefd[0] = -1;
+
+    close(pipefd[1]);
+    pipefd[1] = -1;
 }
 
 
@@ -241,8 +292,17 @@ static int tpm_builtin_startup_tpm(void)
 }
 
 
-static int tpm_builtin_do_startup_tpm(void)
+static int tpm_builtin_do_startup_tpm(bool fail_on_encrypted_drive)
 {
+    int rc;
+
+    rc = startup_bs(bs, fail_on_encrypted_drive);
+    if (rc) {
+        return rc;
+    }
+
+    load_tpm_state_from_bs(bs);
+
     return tpm_builtin_startup_tpm();
 }
 
@@ -253,7 +313,7 @@ static int tpm_builtin_do_startup_tpm(vo
  */
 static int tpm_builtin_early_startup_tpm(void)
 {
-    return tpm_builtin_do_startup_tpm();
+    return tpm_builtin_do_startup_tpm(true);
 }
 
 
@@ -269,7 +329,7 @@ static int tpm_builtin_late_startup_tpm(
     had_fatal_error = false;
     had_startup_error = false;
 
-    if (tpm_builtin_do_startup_tpm()) {
+    if (tpm_builtin_do_startup_tpm(false)) {
         had_fatal_error = true;
     }
 
@@ -277,6 +337,213 @@ static int tpm_builtin_late_startup_tpm(
 }
 
 
+/*****************************************************************
+ * call back functions for the libtpms TPM library
+ ****************************************************************/
+static TPM_RESULT tpm_builtin_nvram_init(void)
+{
+    return TPM_SUCCESS;
+}
+
+
+static TPM_RESULT tpm_builtin_nvram_loaddata(unsigned char **data,
+                                   uint32_t *length,
+                                   size_t tpm_number __attribute__((unused)),
+                                   const char *name)
+{
+    TPM_RESULT rc = TPM_SUCCESS;
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    fprintf(stderr, "tpm: TPM_NVRAM_LoadData: tpm_number = %d, name = %s\n",
+            (int)tpm_number, name);
+#endif
+    *length = 0;
+
+    if (!strcmp(name, TPM_PERMANENT_ALL_NAME)) {
+        *length = permanent_state.size;
+
+        if (*length == 0) {
+            rc = TPM_RETRY;
+        } else {
+            /*
+             * keep the permanent state for
+             * as long as possible. We may
+             * be in a resume operation and only
+             * get the volatile state later on when
+             * Qemu provides the state.
+             * Once the volatile state is there,
+             * we can discard the permanent state,
+             * otherwise the perment state will be
+             * discarded in other places
+             */
+            if (volatile_state.size == 0) {
+                rc = TPM_Malloc(data, *length);
+                if (rc == 0)
+                    memcpy(*data, permanent_state.buffer, *length);
+            } else {
+                *data = permanent_state.buffer;
+
+                permanent_state.size = 0;
+                permanent_state.buffer = NULL;
+            }
+        }
+    } else if (!strcmp(name, TPM_VOLATILESTATE_NAME)) {
+        *length = volatile_state.size;
+        if (*length == 0) {
+            rc = TPM_RETRY;
+        } else {
+            *data = volatile_state.buffer;
+
+            volatile_state.size = 0;
+            volatile_state.buffer = NULL;
+        }
+    } else if (!strcmp(name, TPM_SAVESTATE_NAME)) {
+        *length = save_state.size;
+        if (*length == 0) {
+            rc = TPM_RETRY;
+        } else {
+            *data = save_state.buffer;
+            save_state.size = 0;
+            save_state.buffer = NULL;
+        }
+    }
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    fprintf(stderr, "tpm: Read %d bytes of state [crc=%08x]; rc = %d\n",
+            *length, (int)crc32(0, *data, *length), rc);
+#endif
+
+    return rc;
+}
+
+
+/*
+ * Called by the TPM when permanent data, savestate or volatile state
+ * is updated or needs to be saved.
+ * Primarily we care about savestate and permanent data here.
+ */
+static TPM_RESULT tpm_builtin_nvram_storedata(const unsigned char *data,
+                                   uint32_t length,
+                                   size_t tpm_number __attribute__((unused)),
+                                   const char *name)
+{
+    TPM_RESULT rc = TPM_SUCCESS;
+    char what;
+    TPMSizedBuffer *tsb = NULL;
+
+    if (incoming_expected) {
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+        fprintf(stderr, "tpm: Not storing data due to incoming migration\n");
+#endif
+        return TPM_SUCCESS;
+    }
+
+    if (!strcmp(name, TPM_PERMANENT_ALL_NAME)) {
+        tsb = &permanent_state;
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+        fprintf(stderr, "tpm: STORING %5d BYTES OF PERMANENT ALL  [crc=%08x]\n",
+                length, (int)crc32(0, data, length));
+#endif
+        what = PERMSTATE_TYPE;
+    } else if (!strcmp(name, TPM_SAVESTATE_NAME)) {
+        tsb = &save_state;
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+        fprintf(stderr, "tpm: STORING %5d BYTES OF SAVESTATE      [crc=%08x]\n",
+                length, (int)crc32(0, data, length));
+#endif
+        what = SAVESTATE_TYPE;
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    } else if (!strcmp(name, TPM_VOLATILESTATE_NAME)) {
+        fprintf(stderr, "tpm: GOT     %5d BYTES OF VOLATILE STATE [crc=%08x]\n",
+                length, (int)crc32(0, data, length));
+#endif
+    }
+
+    if (tsb) {
+        qemu_mutex_lock(&state_mutex);
+
+        set_sized_buffer(tsb, (unsigned char *)data, length);
+
+        if (request_sync_to_bs(what)) {
+            rc = TPM_FAIL;
+        }
+
+        /* TPM library will free */
+        tsb->size = 0;
+        tsb->buffer = NULL;
+
+        qemu_mutex_unlock(&state_mutex);
+    }
+
+    if (had_fatal_error) {
+        rc = TPM_FAIL;
+    }
+
+    return rc;
+}
+
+
+static TPM_RESULT tpm_builtin_nvram_deletename(
+                                  size_t tpm_number __attribute__((unused)),
+                                  const char *name,
+                                  TPM_BOOL mustExist)
+{
+    TPM_RESULT rc = TPM_SUCCESS;
+
+    /* only handle the savestate here */
+    if (!strcmp(name, TPM_SAVESTATE_NAME)) {
+        qemu_mutex_lock(&state_mutex);
+
+        clear_sized_buffer(&save_state);
+
+        if (request_sync_to_bs(SAVESTATE_TYPE)) {
+            rc = TPM_FAIL;
+        }
+
+        qemu_mutex_unlock(&state_mutex);
+    } else if (!strcmp(name, TPM_VOLATILESTATE_NAME)) {
+        qemu_mutex_lock(&state_mutex);
+
+        clear_sized_buffer(&volatile_state);
+
+        if (request_sync_to_bs(VOLASTATE_TYPE)) {
+            rc = TPM_FAIL;
+        }
+
+        qemu_mutex_unlock(&state_mutex);
+    }
+
+    return rc;
+}
+
+
+static TPM_RESULT tpm_builtin_io_init(void)
+{
+    return TPM_SUCCESS;
+}
+
+
+static TPM_RESULT tpm_builtin_io_getlocality(
+                                 TPM_MODIFIER_INDICATOR *localityModifier)
+{
+    *localityModifier = (TPM_MODIFIER_INDICATOR)g_locty;
+
+    return TPM_SUCCESS;
+}
+
+
+static TPM_RESULT tpm_builtin_io_getphysicalpresence(
+                                 TPM_BOOL *physicalPresence)
+{
+    *physicalPresence = FALSE;
+
+    return TPM_SUCCESS;
+}
+
+
+/*****************************************************************/
+
+
 static void tpm_builtin_reset(void)
 {
 #if defined DEBUG_TPM || defined DEBUG_TPM_SR
@@ -285,7 +552,11 @@ static void tpm_builtin_reset(void)
 
     tpm_builtin_terminate_tpm_thread();
 
+    clear_sized_buffer(&permanent_state);
+    clear_sized_buffer(&save_state);
+    clear_sized_buffer(&volatile_state);
     had_fatal_error = false;
+    need_read_volatile = false;
     had_startup_error = false;
 }
 
@@ -316,27 +587,95 @@ static int tpm_builtin_instantiate_with_
         tis_reset_for_snapshot_resume(s);
     }
 
+    /* we need to defer the read since we will not have the encryption key
+       in case storage is encrypted at this point */
+    need_read_volatile = true;
+
     return 0;
 }
 
 
+struct libtpms_callbacks callbacks = {
+    .sizeOfStruct               = sizeof(struct libtpms_callbacks),
+    .tpm_nvram_init             = tpm_builtin_nvram_init,
+    .tpm_nvram_loaddata         = tpm_builtin_nvram_loaddata,
+    .tpm_nvram_storedata        = tpm_builtin_nvram_storedata,
+    .tpm_nvram_deletename       = tpm_builtin_nvram_deletename,
+    .tpm_io_init                = tpm_builtin_io_init,
+    .tpm_io_getlocality         = tpm_builtin_io_getlocality,
+    .tpm_io_getphysicalpresence = tpm_builtin_io_getphysicalpresence,
+};
+
+
 static int tpm_builtin_init(TPMState *s, TPMRecvDataCB *recv_data_cb)
 {
+    int flags;
+
+    bs = bdrv_find("vtpm-nvram");
+    if (bs == NULL) {
+        fprintf(stderr, "The vtpm-nvram driver was not found.\n");
+        goto err_exit;
+    }
+
+    if (TPMLIB_RegisterCallbacks(&callbacks) != TPM_SUCCESS) {
+        goto err_exit;
+    }
+
+    if (check_bs(bs)) {
+        goto err_exit;
+    }
+
     tpm_thread_params.tpm_state = s;
     tpm_thread_params.recv_data_callback = recv_data_cb;
 
     qemu_mutex_init(&state_mutex);
     qemu_mutex_init(&tpm_initialized_mutex);
+    qemu_cond_init(&bs_write_result_cond);
+
+    if (pipe(pipefd)) {
+        goto err_exit;
+    }
+
+    flags = fcntl(pipefd[0], F_GETFL);
+    if (flags < 0) {
+        goto err_exit_close_pipe;
+    }
+
+    if (fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK ) < 0) {
+        goto err_exit_close_pipe;
+    }
+
+    qemu_set_fd_handler(pipefd[0], tpm_builtin_fulfill_sync_to_bs_request,
+                        NULL, NULL);
 
     atexit(tpm_builtin_tpm_atexit);
 
     return 0;
+
+err_exit_close_pipe:
+    close(pipefd[0]);
+    pipefd[0] = -1;
+    close(pipefd[1]);
+    pipefd[1] = -1;
+
+err_exit:
+    return 1;
 }
 
 
 static bool tpm_builtin_get_tpm_established_flag(void)
 {
-    return false;
+    TPM_BOOL tpmEstablished = false;
+
+    qemu_mutex_lock(&tpm_initialized_mutex);
+
+    if (tpm_initialized) {
+        TPM_IO_TpmEstablished_Get(&tpmEstablished);
+    }
+
+    qemu_mutex_unlock(&tpm_initialized_mutex);
+
+    return (bool)tpmEstablished;
 }
 
 
@@ -352,6 +691,10 @@ static bool tpm_builtin_get_startup_erro
  */
 static int tpm_builtin_save_volatile_data(void)
 {
+    TPM_RESULT res;
+    unsigned char *buffer;
+    uint32_t buflen;
+
     if (!tpm_initialized) {
         /* TPM was never initialized
            volatile_state.buffer may be NULL if TPM was never used.
@@ -359,17 +702,46 @@ static int tpm_builtin_save_volatile_dat
         return 0;
     }
 
+    /* have the serialized state written to a buffer only */
+#ifdef DEBUG_TPM_SR
+    fprintf(stderr, "tpm: Calling TPMLIB_VolatileAll_Store()\n");
+#endif
+    res = TPMLIB_VolatileAll_Store(&buffer, &buflen);
+
+    if (res != TPM_SUCCESS) {
+#ifdef DEBUG_TPM_SR
+        fprintf(stderr, "tpm: Error: Could not store TPM volatile state\n");
+#endif
+        return 1;
+    }
+
+#ifdef DEBUG_TPM_SR
+    fprintf(stderr, "tpm: got %d bytes of volatilestate [crc=%08x]\n",
+            buflen, (int)crc32(0, buffer, buflen));
+#endif
+
+    set_sized_buffer(&volatile_state, buffer, buflen);
+    if (write_state_to_bs(VOLASTATE_TYPE)) {
+        return 1;
+    }
+    volatile_state.size = 0;
+    volatile_state.buffer = NULL;
+
+    /* make sure that everything has been written to disk */
+    tpm_builtin_fulfill_sync_to_bs_request(NULL);
+
     return 0;
 }
 
 
 static size_t tpm_builtin_realloc_buffer(TPMSizedBuffer *sb)
 {
-    size_t wanted_size = 4096;
+    TPM_RESULT res;
+    size_t wanted_size = tpmlib_get_prop(TPMPROP_TPM_BUFFER_MAX);
 
     if (sb->size != wanted_size) {
-        sb->buffer = qemu_realloc(sb->buffer, wanted_size);
-        if (sb->buffer != NULL) {
+        res = TPM_Realloc(&sb->buffer, wanted_size);
+        if (res == TPM_SUCCESS) {
             sb->size = wanted_size;
         } else {
             sb->size = 0;
@@ -385,7 +757,8 @@ static const char *tpm_builtin_create_de
 
     if (!done) {
         snprintf(dev_description, sizeof(dev_description),
-                 "Skeleton TPM backend");
+                 "Qemu's built-in TPM; requires %ukb of block storage",
+                 MINIMUM_BS_SIZE_KB);
         done = 1;
     }
 
@@ -393,13 +766,15 @@ static const char *tpm_builtin_create_de
 }
 
 
+#define TPM_OPTS "id=vtpm-nvram"
+
 static bool tpm_builtin_handle_options(QemuOpts *opts)
 {
     const char *value;
 
     value = qemu_opt_get(opts, "path");
     if (value) {
-        /* !!! handle file path */
+        drive_add(IF_NONE, -1, value, TPM_OPTS);
     } else {
         fprintf(stderr, "-tpm is missing path= parameter\n");
         return false;
@@ -411,7 +786,7 @@ static bool tpm_builtin_handle_options(Q
 BackendTPMDriver tpm_builtin = {
     .id                       = "builtin",
     .desc                     = tpm_builtin_create_desc,
-    .job_for_main_thread      = NULL,
+    .job_for_main_thread      = tpm_builtin_fulfill_sync_to_bs_request,
     .handle_options           = tpm_builtin_handle_options,
     .init                     = tpm_builtin_init,
     .early_startup_tpm        = tpm_builtin_early_startup_tpm,
Index: qemu-git/configure
===================================================================
--- qemu-git.orig/configure
+++ qemu-git/configure
@@ -3449,7 +3449,6 @@ if test "$tpm" = "yes"; then
   fi
 
   if test "$has_tpm" = "1"; then
-      echo "CONFIG_TPM_BUILTIN=y" >> $config_target_mak
       echo "CONFIG_TPM=y" >> $config_host_mak
   fi
 fi

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

* [Qemu-devel] [PATCH V4 08/10] Introduce file lock for the block layer
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (6 preceding siblings ...)
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 07/10] Implementation of the libtpms-based backend Stefan Berger
@ 2011-05-06 17:32 ` Stefan Berger
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 09/10] Add block storage support for libtpms based TPM backend Stefan Berger
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

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

This patch introduces file locking via fcntl() for the block layer so that
concurrent access to files shared by 2 Qemu instances, for example via NFS,
can be serialized. This feature is useful primarily during initial phases of
VM migration where the target machine's TIS driver validates the block
storage (and in a later patch checks for missing AES keys).

Support for win32 is based on win32 API and has been lightly tested with a
standalone test program locking shared storage from two different machines.

To enable locking a file multiple times, a counter is used. Actual locking
happens the very first time and unlocking happens when the counter is zero.

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

---

---
 block.c           |   40 ++++++++++++++++++++++++++++++++++
 block.h           |    8 ++++++
 block/raw-posix.c |   62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 block/raw-win32.c |   51 ++++++++++++++++++++++++++++++++++++++++++++
 block_int.h       |    4 +++
 5 files changed, 165 insertions(+)

Index: qemu-git/block.c
===================================================================
--- qemu-git.orig/block.c
+++ qemu-git/block.c
@@ -475,6 +475,8 @@ static int bdrv_open_common(BlockDriverS
         goto free_and_fail;
     }
 
+    drv->num_locks = 0;
+
     bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR);
 
     ret = refresh_total_sectors(bs, bs->total_sectors);
@@ -1181,6 +1183,44 @@ void bdrv_get_geometry(BlockDriverState 
     *nb_sectors_ptr = length;
 }
 
+/* file locking */
+static int bdrv_lock_common(BlockDriverState *bs, BDRVLockType lock_type)
+{
+    BlockDriver *drv = bs->drv;
+
+    if (!drv)
+        return -ENOMEDIUM;
+
+    if (bs->file) {
+        drv = bs->file->drv;
+        if (drv->bdrv_lock) {
+            return drv->bdrv_lock(bs->file, lock_type);
+        }
+    }
+
+    if (drv->bdrv_lock) {
+        return drv->bdrv_lock(bs, lock_type);
+    }
+
+    return -ENOTSUP;
+}
+
+
+int bdrv_lock(BlockDriverState *bs)
+{
+    if (bdrv_is_read_only(bs)) {
+        return bdrv_lock_common(bs, BDRV_F_RDLCK);
+    }
+
+    return bdrv_lock_common(bs, BDRV_F_WRLCK);
+}
+
+void bdrv_unlock(BlockDriverState *bs)
+{
+    bdrv_lock_common(bs, BDRV_F_UNLCK);
+}
+
+
 struct partition {
         uint8_t boot_ind;           /* 0x80 - active */
         uint8_t head;               /* starting head */
Index: qemu-git/block.h
===================================================================
--- qemu-git.orig/block.h
+++ qemu-git/block.h
@@ -42,6 +42,12 @@ typedef struct QEMUSnapshotInfo {
 #define BDRV_SECTOR_MASK   ~(BDRV_SECTOR_SIZE - 1)
 
 typedef enum {
+    BDRV_F_UNLCK,
+    BDRV_F_RDLCK,
+    BDRV_F_WRLCK,
+} BDRVLockType;
+
+typedef enum {
     BLOCK_ERR_REPORT, BLOCK_ERR_IGNORE, BLOCK_ERR_STOP_ENOSPC,
     BLOCK_ERR_STOP_ANY
 } BlockErrorAction;
@@ -95,6 +101,8 @@ int bdrv_commit(BlockDriverState *bs);
 void bdrv_commit_all(void);
 int bdrv_change_backing_file(BlockDriverState *bs,
     const char *backing_file, const char *backing_fmt);
+int bdrv_lock(BlockDriverState *bs);
+void bdrv_unlock(BlockDriverState *bs);
 void bdrv_register(BlockDriver *bdrv);
 
 
Index: qemu-git/block/raw-posix.c
===================================================================
--- qemu-git.orig/block/raw-posix.c
+++ qemu-git/block/raw-posix.c
@@ -718,6 +718,66 @@ static int64_t raw_getlength(BlockDriver
 }
 #endif
 
+static int raw_lock(BlockDriverState *bs, BDRVLockType lock_type)
+{
+    BlockDriver *drv = bs->drv;
+    BDRVRawState *s = bs->opaque;
+    struct flock flock = {
+        .l_whence = SEEK_SET,
+        .l_start = 0,
+        .l_len = 0,
+    };
+    int n;
+
+    switch (lock_type) {
+    case BDRV_F_RDLCK:
+    case BDRV_F_WRLCK:
+        if (drv->num_locks) {
+            drv->num_locks++;
+            return 0;
+        }
+        flock.l_type = (lock_type == BDRV_F_RDLCK) ? F_RDLCK : F_WRLCK;
+        break;
+
+    case BDRV_F_UNLCK:
+        if (--drv->num_locks > 0) {
+            return 0;
+        }
+
+        assert(drv->num_locks == 0);
+
+        flock.l_type = F_UNLCK;
+        break;
+
+    default:
+        return -EINVAL;
+    }
+
+    while (1) {
+        n = fcntl(s->fd, F_SETLKW, &flock);
+        if (n < 0) {
+            if (errno == EINTR) {
+                continue;
+            }
+            if (errno == EAGAIN) {
+                usleep(10000);
+                continue;
+            }
+        }
+        break;
+    }
+
+    if (n == 0 &&
+        ((lock_type == BDRV_F_RDLCK) || (lock_type == BDRV_F_WRLCK))) {
+        drv->num_locks = 1;
+    }
+
+    if (n)
+        return -errno;
+
+    return 0;
+}
+
 static int raw_create(const char *filename, QEMUOptionParameter *options)
 {
     int fd;
@@ -814,6 +874,8 @@ static BlockDriver bdrv_file = {
     .bdrv_truncate = raw_truncate,
     .bdrv_getlength = raw_getlength,
 
+    .bdrv_lock = raw_lock,
+
     .create_options = raw_create_options,
 };
 
Index: qemu-git/block_int.h
===================================================================
--- qemu-git.orig/block_int.h
+++ qemu-git/block_int.h
@@ -137,6 +137,10 @@ struct BlockDriver {
      */
     int (*bdrv_has_zero_init)(BlockDriverState *bs);
 
+    /* File locking */
+    int num_locks;
+    int (*bdrv_lock)(BlockDriverState *bs, BDRVLockType lock_type);
+
     QLIST_ENTRY(BlockDriver) list;
 };
 
Index: qemu-git/block/raw-win32.c
===================================================================
--- qemu-git.orig/block/raw-win32.c
+++ qemu-git/block/raw-win32.c
@@ -213,6 +213,56 @@ static int64_t raw_getlength(BlockDriver
     return l.QuadPart;
 }
 
+static int raw_lock(BlockDriverState *bs, int lock_type)
+{
+    BlockDriver *drv = bs->drv;
+    BDRVRawState *s = bs->opaque;
+    OVERLAPPED ov;
+    BOOL res;
+    DWORD num_bytes;
+
+    switch (lock_type) {
+    case BDRV_F_RDLCK:
+    case BDRV_F_WRLCK:
+        if (drv->num_locks) {
+            drv->num_locks++;
+            return 0;
+        }
+
+        memset(&ov, 0, sizeof(ov));
+
+        res = LockFileEx(s->hfile, LOCKFILE_EXCLUSIVE_LOCK, 0, ~0, ~0, &ov);
+
+        if (res == FALSE) {
+            res = GetOverlappedResult(s->hfile, &ov, &num_bytes, TRUE);
+        }
+
+        if (res == TRUE) {
+            drv->num_locks = 1;
+        }
+
+        break;
+
+    case BDRV_F_UNLCK:
+        if (--drv->num_locks > 0) {
+            return 0;
+        }
+
+        assert(drv->num_locks >= 0);
+
+        res = UnlockFile(hfile, 0, 0, ~0, ~0);
+        break;
+
+    default:
+        return -EINVAL;
+    }
+
+    if (res == FALSE)
+        return -EIO;
+
+    return 0;
+}
+
 static int raw_create(const char *filename, QEMUOptionParameter *options)
 {
     int fd;
@@ -257,6 +307,7 @@ static BlockDriver bdrv_file = {
     .bdrv_write		= raw_write,
     .bdrv_truncate	= raw_truncate,
     .bdrv_getlength	= raw_getlength,
+    .bdrv_lock          = raw_lock,
 
     .create_options = raw_create_options,
 };

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

* [Qemu-devel] [PATCH V4 09/10] Add block storage support for libtpms based TPM backend
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (7 preceding siblings ...)
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 08/10] Introduce file lock for the block layer Stefan Berger
@ 2011-05-06 17:32 ` Stefan Berger
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 10/10] Encrypt state blobs using AES CBC encryption Stefan Berger
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

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

This patch supports the storage of TPM persisten state.

The TPM creates state of varying size, depending for example how many
keys are loaded into it a a certain time. The worst-case sizes of
the different blobs the TPM can write have been pre-calculated and this
value is used to determine the minimum size of the Qcow2 image. It needs to
be 63kb. 'qemu-... -tpm ?' shows this number when this backend driver is
available.


The layout of the TPM's persistent data in the block storage is as follows:

The first sector (512 bytes) holds a primitive directory for the different
types of blobs that the TPM can write. This directory holds a revision
number, a checksum over its content, the number of entries, and the entries
themselves. The entries are described through their absolute offsets, their
maximum sizes, the number of currently valid bytes (the blobs infalte
and deflate) and what type of blob it is (see below for the types).

typedef struct BSDir {
    uint16_t  rev;
    uint32_t  checksum; 
    uint32_t  num_entries;
    uint32_t  reserved[10];
    BSEntry   entries[BS_DIR_MAX_NUM_ENTRIES];
} __attribute__((packed)) BSDir;


Their worst case sizes have been calculated and according to these sizes
the blobs are written at certain offsets into the blockstorage. Their offsets
are all aligned to sectors (512 byte boundaries).

The TPM provides three different blobs that are written into the storage:

- volatile state
- permanent state
- save state

The 'save state' is written when the VM suspends (ACPI S3) and read when it
resumes. This is done in concert with the BIOS where the BIOS needs to send
a command to the TPM upon resume (TPM_Startup(ST_STATE)), while the OS
issues the command TPM_SaveState().

The 'perment state' is written when the TPM receives a command that alters
its permenent state, i.e., when the a key is loaded into the TPM that
is expected to be there upon reboot of the machine / VM.

Volatile state is written when the frontend triggers it to do so, i.e.,
when the VM's state is written out during taking of a snapshot, migration
or suspension to disk (as in 'virsh save'). This state serves to resume
at the point where the TPM previously stopped but there is no need for it
after a machine reboot for example.

Tricky parts here are related to encrypted storage where certain operations
need to be deferred since the key for the storage only becomes available
much later than the time that the backend is instantiated.

The backend also tries to check for the validity of the block storage for
example. If the Qcow2 is not encrypted and the checksum is found to be
bad, the block storage directory will be initialized. 
In case the Qcow2 is encrypted, initialization will only be done if
the directory is found to be all 0s. In case the directory cannot be
checksummed correctly, but is not all 0s, it is assumed that the user
provided a wrong key. In this case I am not exiting qemu, but black-out
the TPM interface (returns 0xff in all memory location) due to a presumed
fatal error and let the VM run (without TPM functionality).

v4:
  - functions prefixed with tpm_builtin
  - added 10 uint32_t to BSDir as being reserved for future use
  - never move data in the block storage while migration is going on
  - use brdv_lock/bdrv_unlock to serialize access to the TPM's state
    file which is primarily necessary during migration and the startup
    of qemu on the target host where the content of the drive is being
    read and validated

v3:
  - added reserved int's for future extensions to the entries in the
    directory structure
  - added crc32 to every entry in the directory structure and calculating
    it when writing and checking it when reading
  - fixed an endianess issue related to crc calculation
  - surrounding debugging output function in adjust_data_layout
    with #if defined DEBUG_TPM
  - probing for installed libtpms development package by test-compiling

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

---
 configure        |   25 +
 hw/tpm_builtin.c |  812 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 834 insertions(+), 3 deletions(-)

Index: qemu-git/hw/tpm_builtin.c
===================================================================
--- qemu-git.orig/hw/tpm_builtin.c
+++ qemu-git/hw/tpm_builtin.c
@@ -1,4 +1,4 @@
-/*
+;/*
  *  builtin TPM driver based on libtpms
  *
  *  Copyright (c) 2010, 2011 IBM Corporation
@@ -44,6 +44,33 @@
 #define PERMSTATE_TYPE 'P'
 #define VOLASTATE_TYPE 'V'
 
+#define ALIGN(VAL, SIZE) \
+  ( ( (VAL) + (SIZE) - 1 ) & ~( (SIZE) - 1 ) )
+
+
+#define DIRECTORY_SIZE        BDRV_SECTOR_SIZE
+
+#define PERMSTATE_DISK_OFFSET ALIGN(DIRECTORY_SIZE, BDRV_SECTOR_SIZE)
+#define PERMSTATE_DISK_SPACE \
+              ALIGN(tpmlib_get_prop(TPMPROP_TPM_MAX_NV_SPACE),\
+                    BDRV_SECTOR_SIZE)
+#define SAVESTATE_DISK_OFFSET (PERMSTATE_DISK_OFFSET + PERMSTATE_DISK_SPACE)
+#define SAVESTATE_DISK_SPACE \
+              ALIGN(tpmlib_get_prop(TPMPROP_TPM_MAX_SAVESTATE_SPACE),\
+                    BDRV_SECTOR_SIZE)
+#define VOLASTATE_DISK_OFFSET (SAVESTATE_DISK_OFFSET + SAVESTATE_DISK_SPACE)
+#define VOLASTATE_DISK_SPACE \
+              ALIGN(tpmlib_get_prop(TPMPROP_TPM_MAX_VOLATILESTATE_SPACE),\
+                    BDRV_SECTOR_SIZE)
+
+# define MINIMUM_BS_SIZE       ALIGN(ALIGN(VOLASTATE_DISK_OFFSET +\
+                                           VOLASTATE_DISK_SPACE,  \
+                                           BDRV_SECTOR_SIZE),     \
+                                     1024)
+
+#define MINIMUM_BS_SIZE_KB    (int)(MINIMUM_BS_SIZE / 1024)
+
+
 /* data structures */
 
 typedef struct ThreadParams {
@@ -53,6 +80,40 @@ typedef struct ThreadParams {
 } ThreadParams;
 
 
+enum BSEntryType {
+    BS_ENTRY_PERMSTATE,
+    BS_ENTRY_SAVESTATE,
+    BS_ENTRY_VOLASTATE,
+
+    BS_ENTRY_LAST,
+};
+
+
+typedef struct BSEntry {
+    enum BSEntryType type;
+    uint64_t offset;
+    uint32_t space;
+    uint32_t blobsize;
+    uint32_t blobcrc32;
+    uint32_t reserved[9];
+} __attribute__((packed)) BSEntry;
+
+
+#define BS_DIR_MAX_NUM_ENTRIES    3  /* permanent, volatile savestate */
+
+typedef struct BSDir {
+    uint16_t  rev;
+    uint32_t  checksum;
+    uint32_t  num_entries;
+    uint32_t  reserved[10];
+    BSEntry   entries[BS_DIR_MAX_NUM_ENTRIES];
+} __attribute__((packed)) BSDir;
+
+
+#define BS_DIR_REV1         1
+
+#define BS_DIR_REV_CURRENT  BS_DIR_REV1
+
 /* local variables */
 
 static QemuThread thread;
@@ -73,6 +134,7 @@ static bool thread_running = false;
 static bool need_read_volatile = false;
 
 static ThreadParams tpm_thread_params;
+static BlockDriverState *bs;
 
 /* locality of the command being executed by libtpms */
 static uint8_t g_locty;
@@ -84,6 +146,13 @@ static const unsigned char tpm_std_fatal
 static char dev_description[80];
 
 
+static void tpm_builtin_adjust_data_layout(BlockDriverState *bs, BSDir *dir);
+static int tpm_builtin_load_sized_data_from_bs(BlockDriverState *bs,
+                                               enum BSEntryType be,
+                                               TPMSizedBuffer *tsb);
+
+
+
 static int tpmlib_get_prop(enum TPMLIB_TPMProperty prop)
 {
     int result;
@@ -96,6 +165,743 @@ static int tpmlib_get_prop(enum TPMLIB_T
 }
 
 
+static unsigned int memsum(const unsigned char *buf, int len)
+{
+     int res = 0, i;
+
+     for (i = 0; i < len; i++) {
+          res += buf[i];
+     }
+
+     return res;
+}
+
+
+/************************************************
+    Block Storage interaction
+ ***********************************************/
+static int tpm_builtin_find_bs_entry_idx(BSDir *dir, enum BSEntryType type)
+{
+    unsigned int c;
+
+    for (c = 0; c < dir->num_entries; c++) {
+        if (dir->entries[c].type == type) {
+            return c;
+        }
+    }
+
+    return -ENOENT;
+}
+
+
+static void tpm_builtin_dir_be_to_cpu(BSDir *dir)
+{
+    unsigned int c;
+
+    be16_to_cpus(&dir->rev);
+    be32_to_cpus(&dir->checksum);
+    be32_to_cpus(&dir->num_entries);
+
+    for (c = 0; c < dir->num_entries && c < BS_DIR_MAX_NUM_ENTRIES; c++) {
+        be32_to_cpus(&dir->entries[c].type);
+        be64_to_cpus(&dir->entries[c].offset);
+        be32_to_cpus(&dir->entries[c].space);
+        be32_to_cpus(&dir->entries[c].blobsize);
+        be32_to_cpus(&dir->entries[c].blobcrc32);
+    }
+}
+
+
+static void tpm_builtin_dir_cpu_to_be(BSDir *dir)
+{
+    unsigned int c;
+
+    for (c = 0; c < dir->num_entries && c < BS_DIR_MAX_NUM_ENTRIES; c++) {
+        dir->entries[c].type      = cpu_to_be32(dir->entries[c].type);
+        dir->entries[c].offset    = cpu_to_be64(dir->entries[c].offset);
+        dir->entries[c].space     = cpu_to_be32(dir->entries[c].space);
+        dir->entries[c].blobsize  = cpu_to_be32(dir->entries[c].blobsize);
+        dir->entries[c].blobcrc32 = cpu_to_be32(dir->entries[c].blobcrc32);
+    }
+
+    dir->rev         = cpu_to_be16(dir->rev);
+    dir->checksum    = cpu_to_be32(dir->checksum);
+    dir->num_entries = cpu_to_be32(dir->num_entries);
+}
+
+
+static unsigned int tpm_builtin_sizeof_bsdir(BSDir *dir)
+{
+    return offsetof(BSDir, entries) +
+           dir->num_entries * sizeof(BSEntry);
+}
+
+
+static uint32_t tpm_builtin_calc_dir_checksum(BSDir *dir)
+{
+    uint16_t checksum, orig;
+    unsigned int bsdir_size = tpm_builtin_sizeof_bsdir(dir);
+
+    orig = dir->checksum;
+    dir->checksum = 0;
+
+    /* normalize to big endian */
+    tpm_builtin_dir_cpu_to_be(dir);
+
+    checksum = crc32(0, (unsigned char *)dir, bsdir_size);
+
+    tpm_builtin_dir_be_to_cpu(dir);
+
+    dir->checksum = orig;
+
+    return checksum;
+}
+
+
+static bool tpm_builtin_is_valid_bsdir(BSDir *dir)
+{
+    if (dir->rev != BS_DIR_REV_CURRENT ||
+        dir->num_entries > BS_DIR_MAX_NUM_ENTRIES) {
+        return false;
+    }
+    return (dir->checksum == tpm_builtin_calc_dir_checksum(dir));
+}
+
+
+static bool tpm_builtin_has_valid_content(BSDir *dir)
+{
+    bool rc = true;
+    uint32_t c;
+    TPMSizedBuffer tsb = {
+        .buffer = NULL,
+        .size = 0
+    };
+
+    for (c = 0; c < dir->num_entries; c++) {
+        if (tpm_builtin_load_sized_data_from_bs(bs,
+                                                dir->entries[c].type,
+                                                &tsb) != 0) {
+            rc = false;
+            break;
+        }
+
+        clear_sized_buffer(&tsb);
+    }
+
+    return rc;
+}
+
+
+static int tpm_builtin_create_blank_dir(BlockDriverState *bs)
+{
+    uint8_t buf[BDRV_SECTOR_SIZE];
+    BSDir *dir;
+
+    memset(buf, 0x0, sizeof(buf));
+
+    dir = (BSDir *)buf;
+    dir->rev = BS_DIR_REV_CURRENT;
+    dir->num_entries = 0;
+
+    dir->checksum = tpm_builtin_calc_dir_checksum(dir);
+
+    tpm_builtin_dir_cpu_to_be(dir);
+
+    if (bdrv_write(bs, 0, buf, 1) < 0) {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+/**
+ * Validate the block storage doing some basic tests. That's
+ * all that can be done at this point since we don't have the
+ * key yet in case it is encrypted.
+ */
+static int tpm_builtin_check_bs(BlockDriverState *bs)
+{
+    int64_t len;
+    char buf[20];
+
+    if (!bs) {
+        fprintf(stderr, "Need a block driver for this vTPM type.\n");
+        goto err_exit;
+    }
+
+    len = bdrv_getlength(bs);
+    if (len < MINIMUM_BS_SIZE) {
+        fprintf(stderr, "Required size for vTPM backing store is %dkb\n",
+                        MINIMUM_BS_SIZE_KB);
+        goto err_exit;
+    }
+
+    bdrv_get_format(bs, buf, sizeof(buf));
+    if (strcmp(buf, "qcow2")) {
+        fprintf(stderr, "vTPM backing store must be of type qcow2\n");
+        goto err_exit;
+    }
+
+    return 0;
+
+ err_exit:
+    fprintf(stderr,
+            "Create the drive using 'qemu-img create -f qcow2 "
+            "<filename> %dk'\n", MINIMUM_BS_SIZE_KB);
+    return -EFAULT;
+}
+
+
+static uint32_t tpm_builtin_get_bs_entry_type_space(enum BSEntryType type)
+{
+    switch (type) {
+    case BS_ENTRY_PERMSTATE:
+        return PERMSTATE_DISK_SPACE;
+    case BS_ENTRY_SAVESTATE:
+        return SAVESTATE_DISK_SPACE;
+    case BS_ENTRY_VOLASTATE:
+        return VOLASTATE_DISK_SPACE;
+    default:
+        assert(false);
+    }
+}
+
+
+/*
+ * Startup the block storage: read the directory and check whether its
+ * checksum is valid. If the checksum is not valid then
+ *
+ * - if the block storage is not encrypted initialize it assuming it's
+ *   been freshly created or corrupted
+ *
+ * - if the block storage is encrypted
+ *     - check whether it's been freshly created (expecting a 0 sum of the
+ *       directory; seems to work with any key) and initialize it in that case
+ *     - otherwise, if there are some unreadable data, assume that
+ *       the wrong key was given and mark it as a starup error. We log it
+ *       but won't exit() here.
+ */
+static int startup_bs(BlockDriverState *bs, bool fail_on_encrypted_drive)
+{
+    uint8_t buf[BDRV_SECTOR_SIZE];
+    BSDir *dir;
+    int rc = 0;
+
+    if (bdrv_lock(bs)) {
+        return -EIO;
+    }
+
+    if (bdrv_read(bs, 0, buf, 1) < 0) {
+        had_startup_error = true;
+        rc = -EIO;
+        goto err_exit;
+    }
+
+    dir = (BSDir *)buf;
+
+    tpm_builtin_dir_be_to_cpu(dir);
+
+    if (!tpm_builtin_is_valid_bsdir(dir) ||
+        !tpm_builtin_has_valid_content(dir)) {
+        /* if it's encrypted and has something else than null-content,
+           we assume to have the wrong key */
+        if (bdrv_is_encrypted(bs)) {
+            if (fail_on_encrypted_drive) {
+                rc = -ENOKEY;
+                goto err_exit;
+            }
+            if (memsum(buf, sizeof(buf)) != 0) {
+                fprintf(stderr,
+                        "vTPM block storage directory is not valid. "
+                        "Assuming the key is wrong.\n");
+                had_startup_error = true;
+                rc = -EKEYREJECTED;
+                goto err_exit;
+            }
+        }
+
+        if (incoming_expected) {
+            /* don't modify the dir in case of incoming migration */
+            rc = 0;
+            goto err_exit;
+        }
+#ifdef DEBUG_TPM
+        fprintf(stderr, "*** tpm: Blanking the storage directory.\n");
+#endif
+        rc = tpm_builtin_create_blank_dir(bs);
+        if (rc != 0) {
+            had_startup_error =  true;
+        }
+        goto err_exit;
+    }
+
+    /* not that we can read the dir, make sure the data are layed out
+     * correctly.
+     */
+    tpm_builtin_adjust_data_layout(bs, dir);
+
+err_exit:
+    bdrv_unlock(bs);
+
+    return rc;
+}
+
+
+static int tpm_builtin_create_bs_entry(BlockDriverState *bs,
+                                       BSDir *dir,
+                                       enum BSEntryType type,
+                                       uint32_t blobsize)
+{
+    uint8_t buf[BDRV_SECTOR_SIZE];
+    uint32_t idx = dir->num_entries++;
+    unsigned int bsdir_size;
+
+    dir->entries[idx].offset = (idx == 0)
+        ? ALIGN(DIRECTORY_SIZE, BDRV_SECTOR_SIZE)
+        : dir->entries[idx-1].offset + ALIGN(dir->entries[idx-1].space,
+                                             BDRV_SECTOR_SIZE);
+
+    dir->entries[idx].type = type;
+
+    dir->entries[idx].space = tpm_builtin_get_bs_entry_type_space(type);
+    dir->entries[idx].blobsize = blobsize;
+
+    dir->checksum = tpm_builtin_calc_dir_checksum(dir);
+
+    bsdir_size = tpm_builtin_sizeof_bsdir(dir);
+
+    tpm_builtin_dir_cpu_to_be(dir);
+
+    assert(dir->entries[idx].space >= blobsize);
+
+    memset(buf, 0x0, sizeof(buf));
+    memcpy(buf, dir, bsdir_size);
+
+    if (bdrv_write(bs, 0, buf, 1) < 0) {
+        idx = -EIO;
+    }
+
+    tpm_builtin_dir_be_to_cpu(dir);
+
+    return idx;
+}
+
+
+static int get_bs_entry(BlockDriverState *bs,
+                        enum BSEntryType type,
+                        BSEntry *entry)
+{
+    uint8_t buf[BDRV_SECTOR_SIZE];
+    BSDir *dir;
+    int idx;
+
+    if (bdrv_read(bs, 0, buf, 1) < 0) {
+        return -EIO;
+    }
+
+    dir = (BSDir *)buf;
+
+    tpm_builtin_dir_be_to_cpu(dir);
+
+    assert(tpm_builtin_is_valid_bsdir(dir));
+
+    if ((idx = tpm_builtin_find_bs_entry_idx(dir, type)) < 0) {
+        if ((idx = tpm_builtin_create_bs_entry(bs, dir, type, 0)) < 0) {
+            return -EIO;
+        }
+    }
+
+    memcpy(entry, &dir->entries[idx], sizeof(*entry));
+
+    return 0;
+}
+
+
+static int set_bs_entry_size_crc(BlockDriverState *bs,
+                                 enum BSEntryType type,
+                                 BSEntry *entry,
+                                 uint32_t blobsize,
+                                 uint32_t blobcrc32)
+{
+    uint8_t buf[BDRV_SECTOR_SIZE];
+    BSDir *dir;
+    int idx;
+
+    if (bdrv_read(bs, 0, buf, 1) < 0) {
+        return -EIO;
+    }
+
+    dir = (BSDir *)buf;
+
+    tpm_builtin_dir_be_to_cpu(dir);
+
+    assert(tpm_builtin_is_valid_bsdir(dir) &&
+           tpm_builtin_has_valid_content(dir));
+
+    if ((idx = tpm_builtin_find_bs_entry_idx(dir, type)) < 0) {
+        if ((idx = tpm_builtin_create_bs_entry(bs, dir, type, 0)) < 0) {
+            return -EIO;
+        }
+    }
+
+    assert(blobsize <= dir->entries[idx].space);
+
+    dir->entries[idx].blobsize  = blobsize;
+    dir->entries[idx].blobcrc32 = blobcrc32;
+
+    dir->checksum = tpm_builtin_calc_dir_checksum(dir);
+
+    tpm_builtin_dir_cpu_to_be(dir);
+
+    if (bdrv_write(bs, 0, buf, 1) < 0) {
+        return -EIO;
+    }
+
+    tpm_builtin_dir_be_to_cpu(dir);
+
+    memcpy(entry, &dir->entries[idx], sizeof(*entry));
+
+    return 0;
+}
+
+
+static int tpm_builtin_load_sized_data_from_bs(BlockDriverState *bs,
+                                               enum BSEntryType be,
+                                               TPMSizedBuffer *tsb)
+{
+    BSEntry entry;
+    int rc;
+
+    if (bdrv_lock(bs)) {
+        return -EIO;
+    }
+
+    if ((rc = get_bs_entry(bs, be, &entry)) < 0) {
+        goto err_exit;
+    }
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    fprintf(stderr,"load: be-type: %d, offset: %6ld, size: %5d\n",
+            be, (long int)entry.offset, entry.blobsize);
+#endif
+
+    if (entry.blobsize == 0) {
+        goto err_exit;
+    }
+
+    tsb->buffer = qemu_malloc(entry.blobsize);
+    if (!tsb->buffer) {
+        rc = -ENOMEM;
+        goto err_exit;
+    }
+
+    tsb->size = entry.blobsize;
+
+    if (bdrv_pread(bs, entry.offset, tsb->buffer, tsb->size) != tsb->size) {
+        clear_sized_buffer(tsb);
+        fprintf(stderr,"tpm: Error while reading sized data!\n");
+        rc = -EIO;
+        goto err_exit;
+    }
+
+    if (entry.blobcrc32 != crc32(0, tsb->buffer, tsb->size)) {
+        fprintf(stderr,"tpm: CRC of sized data is wrong! : %x vs. %x\n",
+                entry.blobcrc32,
+                (int)crc32(0, tsb->buffer, tsb->size));
+        clear_sized_buffer(tsb);
+        rc = -EBADMSG;
+    }
+
+err_exit:
+    bdrv_unlock(bs);
+
+    return rc;
+}
+
+
+static int tpm_builtin_load_tpm_permanent_state_from_bs(BlockDriverState *bs,
+                                            TPMSizedBuffer *tsb)
+{
+    return tpm_builtin_load_sized_data_from_bs(bs, BS_ENTRY_PERMSTATE, tsb);
+}
+
+
+static int tpm_builtin_load_tpm_savestate_from_bs(BlockDriverState *bs,
+                                                  TPMSizedBuffer *tsb)
+{
+    return tpm_builtin_load_sized_data_from_bs(bs, BS_ENTRY_SAVESTATE, tsb);
+}
+
+
+static int tpm_builtin_load_tpm_volatile_state_from_bs(BlockDriverState *bs,
+                                                       TPMSizedBuffer *tsb)
+{
+    return tpm_builtin_load_sized_data_from_bs(bs, BS_ENTRY_VOLASTATE, tsb);
+}
+
+
+static int tpm_builtin_save_sized_data_to_bs(BlockDriverState *bs,
+                                             enum BSEntryType be,
+                                             uint8_t *data, uint32_t data_len)
+{
+    BSEntry entry;
+    int rc;
+    uint32_t crc = 0;
+
+    if (data_len > 0) {
+        crc = crc32(0, (unsigned char *)data, data_len);
+    }
+
+    if (bdrv_lock(bs)) {
+        return -EIO;
+    }
+
+    if ((rc = set_bs_entry_size_crc(bs, be, &entry, data_len, crc)) < 0) {
+        goto err_exit;
+    }
+
+    if (data_len > 0) {
+        if (bdrv_pwrite(bs, entry.offset, data, data_len) != data_len) {
+            rc = -EIO;
+        }
+    }
+
+err_exit:
+    bdrv_unlock(bs);
+
+    return rc;
+}
+
+
+/* Write the TPM's state to block storage */
+static int sync_permanent_state_to_disk(BlockDriverState *bs)
+{
+    int rc = 0;
+
+    if (permanent_state.size) {
+        rc = tpm_builtin_save_sized_data_to_bs(bs, BS_ENTRY_PERMSTATE,
+                                               permanent_state.buffer,
+                                               permanent_state.size);
+    }
+
+    return rc;
+}
+
+
+static int sync_savestate_to_disk(BlockDriverState *bs)
+{
+    return tpm_builtin_save_sized_data_to_bs(bs, BS_ENTRY_SAVESTATE,
+                                 save_state.buffer, save_state.size);
+}
+
+
+static int sync_volatile_state_to_disk(BlockDriverState *bs)
+{
+    return tpm_builtin_save_sized_data_to_bs(bs, BS_ENTRY_VOLASTATE,
+                                 volatile_state.buffer, volatile_state.size);
+}
+
+
+/*
+ * Write a given type of state, identified by the char, to block
+ * storage. If anything goes wrong, set the had_fatal_error variable
+ */
+static int write_state_to_bs(char what)
+{
+    int rc = 0;
+
+    qemu_mutex_lock(&state_mutex);
+
+    switch (what) {
+    case PERMSTATE_TYPE:
+        rc = sync_permanent_state_to_disk(bs);
+        break;
+    case SAVESTATE_TYPE:
+        rc = sync_savestate_to_disk(bs);
+        break;
+    case VOLASTATE_TYPE:
+        rc = sync_volatile_state_to_disk(bs);
+        break;
+    default:
+        assert(false);
+    }
+
+    if (rc) {
+        fprintf(stderr,"tpm: Error while writing TPM state to bs. "
+                       "Setting fatal error.");
+        had_fatal_error = true;
+    }
+
+    qemu_mutex_unlock(&state_mutex);
+
+    return rc;
+}
+
+
+/*
+ * Write the 'savestate' or 'permanent state' in the
+ * global buffer to disk. The requester tells us what
+ * to writy by a single byte in the pipe. If anything
+ * goes wrong, we'll set the had_fatal_error flag.
+ * We sync with the requester using signals on a
+ * condition.
+ */
+static void tpm_builtin_fulfill_sync_to_bs_request(void *opaque)
+{
+    char buf[10];
+    int c, n;
+
+    while ((n = read(pipefd[0], buf, sizeof(buf))) > 0) {
+        for (c = 0; c < n; c++) {
+            write_state_to_bs(buf[c]);
+        }
+    }
+
+    qemu_cond_signal(&bs_write_result_cond);
+}
+
+
+/*
+ * Request that either savestate or permanent state be written
+ * to the disk. Call this function with the state_mutex held.
+ * It will synchronize with the sync_to_bs function that does
+ * the work. In case a previous fatal error occurred, nothing
+ * will be done.
+ */
+static bool request_sync_to_bs(char what)
+{
+    char cmd[1] = { what };
+
+    if (had_fatal_error) {
+        return had_fatal_error;
+    }
+
+    if (write(pipefd[1], cmd, 1) != 1) {
+        had_fatal_error = true;
+        return true;
+    }
+
+    qemu_cond_wait(&bs_write_result_cond, &state_mutex);
+
+    return had_fatal_error;
+}
+
+
+static void tpm_builtin_load_tpm_state_from_bs(BlockDriverState *bs)
+{
+    tpm_builtin_load_tpm_permanent_state_from_bs(bs, &permanent_state);
+    tpm_builtin_load_tpm_savestate_from_bs(bs, &save_state);
+
+    if (need_read_volatile) {
+        clear_sized_buffer(&volatile_state);
+        tpm_builtin_load_tpm_volatile_state_from_bs(bs, &volatile_state);
+        need_read_volatile = false;
+    }
+}
+
+
+/*
+ * Adjust the layout of the data. This may be necessary if
+ * we migrated to a TPM implementation with different blob
+ * sizes that would altogether fit onto the disk but write into
+ * the next blob due to their current layout.
+ *
+ * At this point we must already have passed the size check of
+ * the drive and know it is big enough. For encrypted storage
+ * we must have the (right!) key at this point.
+ */
+static void tpm_builtin_adjust_data_layout(BlockDriverState *bs, BSDir *dir)
+{
+    unsigned int c;
+    int rc;
+    uint64_t exp_offset = ALIGN(DIRECTORY_SIZE, BDRV_SECTOR_SIZE);
+    bool need_move = false;
+    TPMSizedBuffer tsb[BS_ENTRY_LAST];
+    enum BSEntryType type;
+
+    if (incoming_expected) {
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+        fprintf(stderr, "tpm: In migration - would not move data.\n");
+#endif
+        return;
+    }
+
+    /* check all entries' space and offsets */
+    for (c = 0; c < dir->num_entries; c++) {
+        if (dir->entries[c].offset != exp_offset) {
+#if defined DEBUG_TPM
+            fprintf(stderr,
+                    "entry[%d].type = %d, offset = %ld, expected = %ld\n",
+                    c,
+                    dir->entries[c].type,
+                    (long int)dir->entries[c].offset,
+                    (long int)exp_offset);
+#endif
+            need_move = true;
+            break;
+        }
+        if (dir->entries[c].space !=
+            tpm_builtin_get_bs_entry_type_space(dir->entries[c].type)) {
+#if defined DEBUG_TPM
+            fprintf(stderr,
+                    "entry[%d].type = %d, space = %d, expected = %d\n",
+                    c,
+                    dir->entries[c].type,
+                    dir->entries[c].space,
+                    get_bs_entry_type_space(dir->entries[c].type));
+#endif
+            need_move = true;
+            break;
+        }
+        exp_offset = ALIGN(exp_offset + dir->entries[c].space,
+                           BDRV_SECTOR_SIZE);
+    }
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    fprintf(stderr,"tpm: Data move necessary: %s\n",
+            (need_move) ? "true" : "false");
+#endif
+
+    if (need_move) {
+        /*
+         * read data
+         * create blank dir
+         * write all data back
+         */
+
+        for (type = 0; type < BS_ENTRY_LAST; type++) {
+            tsb[type].size = 0;
+            tsb[type].buffer = NULL;
+
+            if (tpm_builtin_load_sized_data_from_bs(bs,
+                                                    type, &tsb[type]) != 0) {
+                had_fatal_error = true;
+                goto err_exit;
+            }
+        }
+
+        tpm_builtin_create_blank_dir(bs);
+        dir = NULL;
+
+        for (type = 0; type < BS_ENTRY_LAST; type++) {
+            rc = tpm_builtin_save_sized_data_to_bs(bs, type,
+                                                   tsb[type].buffer,
+                                                   tsb[type].size);
+            if (rc) {
+                had_fatal_error = true;
+                break;
+            }
+        }
+
+err_exit:
+        for (type = 0; type < BS_ENTRY_LAST; type++) {
+            clear_sized_buffer(&tsb[type]);
+        }
+    }
+}
+
+
 static void *tpm_builtin_main_loop(void *d)
 {
     TPM_RESULT res;
@@ -301,7 +1107,7 @@ static int tpm_builtin_do_startup_tpm(bo
         return rc;
     }
 
-    load_tpm_state_from_bs(bs);
+    tpm_builtin_load_tpm_state_from_bs(bs);
 
     return tpm_builtin_startup_tpm();
 }
@@ -621,7 +1427,7 @@ static int tpm_builtin_init(TPMState *s,
         goto err_exit;
     }
 
-    if (check_bs(bs)) {
+    if (tpm_builtin_check_bs(bs)) {
         goto err_exit;
     }
 
Index: qemu-git/configure
===================================================================
--- qemu-git.orig/configure
+++ qemu-git/configure
@@ -2474,6 +2474,22 @@ if test "$trace_backend" = "dtrace"; the
 fi
 
 ##########################################
+# libtpms probe
+
+if test "$tpm" = "yes" ; then
+  cat > $TMPC <<EOF
+#include <libtpms/tpm_library.h>
+int main(void) { return (int)TPMLIB_GetVersion(); }
+EOF
+  libtpms=no
+  if compile_prog "" "-ltpms" ; then
+    libtpms=yes
+  else
+    tpm_need_pkgs="libtpms development package"
+  fi
+fi
+
+##########################################
 # End of CC checks
 # After here, no more $cc or $ld runs
 
@@ -3449,6 +3465,15 @@ if test "$tpm" = "yes"; then
   fi
 
   if test "$has_tpm" = "1"; then
+      if test "$libtpms" = "yes" ; then
+          echo "CONFIG_TPM_BUILTIN=y" >> $config_target_mak
+      else
+          echo
+          echo "TPM support cannot be added since no TPM backend can be compiled."
+          echo "Please install the $tpm_need_pkgs."
+          echo
+          exit 1
+      fi
       echo "CONFIG_TPM=y" >> $config_host_mak
   fi
 fi

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

* [Qemu-devel] [PATCH V4 10/10] Encrypt state blobs using AES CBC encryption
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (8 preceding siblings ...)
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 09/10] Add block storage support for libtpms based TPM backend Stefan Berger
@ 2011-05-06 17:32 ` Stefan Berger
  2011-05-09 14:21 ` [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Serge E. Hallyn
  2011-05-10  4:07 ` Serge E. Hallyn
  11 siblings, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 17:32 UTC (permalink / raw)
  To: stefanb, qemu-devel; +Cc: andreas.niederl, serge

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

This patch adds encryption of the individual state blobs that are written
into the block storage. The 'directory' at the beginnig of the block
storage is not encrypted.

Keys can be passed either as a string of hexadecimal digits forming a 256,
192 or 128 bit AES key. Those keys can optionally start with '0x'. If the
parse does not recognize it as such, the string itself is taken as the AES
key.

The key is passed via command line argument. It's wiped from the command
line after parsing. If key=0x1234... was passed before it will then be
changed to key=------... so that 'ps' does not show the key anymore. Obviously
it cannot be completely prevented that the key is visible during a very
short period of time until qemu is done parsing the command line parameters.

A flag is introduced in the directory structure indicating whether the blobs
are encrypted.

An additional 'layer' for reading and writing the blobs to the underlying
block storage is added. This layer encrypts the blobs for writing if a key is
available. Similarly it decrypts the blobs after reading.

Checks are added the test whether a key has been provided although all
data are stored in clear-text or whether a key is missing. In either one of
the cases the backend returns an error and Qemu terminates.

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

---
 arch_init.c      |   10 ++
 hw/tpm_builtin.c |  214 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 qemu-config.c    |    5 +
 qemu-options.hx  |    2 
 4 files changed, 226 insertions(+), 5 deletions(-)

Index: qemu-git/hw/tpm_builtin.c
===================================================================
--- qemu-git.orig/hw/tpm_builtin.c
+++ qemu-git/hw/tpm_builtin.c
@@ -26,6 +26,7 @@
 #include "hw/pc.h"
 #include "migration.h"
 #include "sysemu.h"
+#include "aes.h"
 
 #include <libtpms/tpm_library.h>
 #include <libtpms/tpm_error.h>
@@ -105,7 +106,8 @@ typedef struct BSDir {
     uint16_t  rev;
     uint32_t  checksum;
     uint32_t  num_entries;
-    uint32_t  reserved[10];
+    uint32_t  flags;
+    uint32_t  reserved[9];
     BSEntry   entries[BS_DIR_MAX_NUM_ENTRIES];
 } __attribute__((packed)) BSDir;
 
@@ -114,6 +116,8 @@ typedef struct BSDir {
 
 #define BS_DIR_REV_CURRENT  BS_DIR_REV1
 
+#define BS_DIR_FLAG_ENC_BLOBS   (1 << 0)
+
 /* local variables */
 
 static QemuThread thread;
@@ -145,6 +149,8 @@ static const unsigned char tpm_std_fatal
 
 static char dev_description[80];
 
+static bool has_key;
+static AES_KEY tpm_enc_key, tpm_dec_key;
 
 static void tpm_builtin_adjust_data_layout(BlockDriverState *bs, BSDir *dir);
 static int tpm_builtin_load_sized_data_from_bs(BlockDriverState *bs,
@@ -201,6 +207,7 @@ static void tpm_builtin_dir_be_to_cpu(BS
     be16_to_cpus(&dir->rev);
     be32_to_cpus(&dir->checksum);
     be32_to_cpus(&dir->num_entries);
+    be32_to_cpus(&dir->flags);
 
     for (c = 0; c < dir->num_entries && c < BS_DIR_MAX_NUM_ENTRIES; c++) {
         be32_to_cpus(&dir->entries[c].type);
@@ -227,6 +234,7 @@ static void tpm_builtin_dir_cpu_to_be(BS
     dir->rev         = cpu_to_be16(dir->rev);
     dir->checksum    = cpu_to_be32(dir->checksum);
     dir->num_entries = cpu_to_be32(dir->num_entries);
+    dir->flags       = cpu_to_be32(dir->flags);
 }
 
 
@@ -292,6 +300,36 @@ static bool tpm_builtin_has_valid_conten
 }
 
 
+static uint32_t tpm_builtin_get_dir_flags(void)
+{
+    if (has_key) {
+        return BS_DIR_FLAG_ENC_BLOBS;
+    }
+
+    return 0;
+}
+
+
+static bool tpm_builtin_has_missing_key(const BSDir *dir)
+{
+    if ((dir->flags & BS_DIR_FLAG_ENC_BLOBS) && !has_key) {
+        return true;
+    }
+
+    return false;
+}
+
+
+static bool tpm_builtin_has_unnecessary_key(const BSDir *dir)
+{
+    if (!(dir->flags & BS_DIR_FLAG_ENC_BLOBS) && has_key) {
+        return true;
+    }
+
+    return false;
+}
+
+
 static int tpm_builtin_create_blank_dir(BlockDriverState *bs)
 {
     uint8_t buf[BDRV_SECTOR_SIZE];
@@ -302,6 +340,7 @@ static int tpm_builtin_create_blank_dir(
     dir = (BSDir *)buf;
     dir->rev = BS_DIR_REV_CURRENT;
     dir->num_entries = 0;
+    dir->flags = tpm_builtin_get_dir_flags();
 
     dir->checksum = tpm_builtin_calc_dir_checksum(dir);
 
@@ -402,6 +441,28 @@ static int startup_bs(BlockDriverState *
 
     tpm_builtin_dir_be_to_cpu(dir);
 
+    if (tpm_builtin_is_valid_bsdir(dir)) {
+        if (tpm_builtin_has_missing_key(dir)) {
+            fprintf(stderr,
+                    "tpm: the data are encrypted but I am missing the key.\n");
+            rc = -EIO;
+            goto err_exit;
+        }
+        if (tpm_builtin_has_unnecessary_key(dir)) {
+            fprintf(stderr,
+                    "tpm: I have a key but the data are not encrypted.\n");
+            rc = -EIO;
+            goto err_exit;
+        }
+        if ((dir->flags & BS_DIR_FLAG_ENC_BLOBS) &&
+            !tpm_builtin_has_valid_content(dir)) {
+            fprintf(stderr, "tpm: cannot read the data - "
+                    "is this the wrong key?\n");
+            rc = -EIO;
+            goto err_exit;
+        }
+    }
+
     if (!tpm_builtin_is_valid_bsdir(dir) ||
         !tpm_builtin_has_valid_content(dir)) {
         /* if it's encrypted and has something else than null-content,
@@ -566,6 +627,81 @@ static int set_bs_entry_size_crc(BlockDr
 }
 
 
+static int tpm_builtin_bdrv_pread(BlockDriverState *bs, int64_t offset,
+                                  void *buf, int count,
+                                  enum BSEntryType type)
+{
+    int ret;
+    union {
+        uint64_t ll[2];
+        uint8_t b[16];
+    } ivec;
+    int toread = count;
+
+    if (has_key) {
+        toread = ALIGN(count, AES_BLOCK_SIZE);
+    }
+
+    ret = bdrv_pread(bs, offset, buf, toread);
+
+    if (ret != toread) {
+        return ret;
+    }
+
+    if (has_key) {
+        ivec.ll[0] = cpu_to_be64(type);
+        ivec.ll[1] = 0;
+
+        AES_cbc_encrypt(buf, buf, toread, &tpm_dec_key, ivec.b, 0);
+    }
+
+    return count;
+}
+
+
+static int tpm_builtin_bdrv_pwrite(BlockDriverState *bs, int64_t offset,
+                                   void *buf, int count,
+                                   enum BSEntryType type)
+{
+    int ret;
+    union {
+        uint64_t ll[2];
+        uint8_t b[16];
+    } ivec;
+    int towrite = count;
+    void *out_buf = buf;
+
+    if (has_key) {
+        ivec.ll[0] = cpu_to_be64(type);
+        ivec.ll[1] = 0;
+
+        towrite = ALIGN(count, AES_BLOCK_SIZE);
+
+        if (towrite != count) {
+            out_buf = qemu_malloc(towrite);
+
+            if (out_buf == NULL) {
+                return -ENOMEM;
+            }
+        }
+
+        AES_cbc_encrypt(buf, out_buf, towrite, &tpm_enc_key, ivec.b, 1);
+    }
+
+    ret = bdrv_pwrite(bs, offset, out_buf, towrite);
+
+    if (out_buf != buf) {
+        qemu_free(out_buf);
+    }
+
+    if (ret == towrite) {
+        return count;
+    }
+
+    return ret;
+}
+
+
 static int tpm_builtin_load_sized_data_from_bs(BlockDriverState *bs,
                                                enum BSEntryType be,
                                                TPMSizedBuffer *tsb)
@@ -590,7 +726,7 @@ static int tpm_builtin_load_sized_data_f
         goto err_exit;
     }
 
-    tsb->buffer = qemu_malloc(entry.blobsize);
+    tsb->buffer = qemu_malloc(ALIGN(entry.blobsize, AES_BLOCK_SIZE));
     if (!tsb->buffer) {
         rc = -ENOMEM;
         goto err_exit;
@@ -598,7 +734,8 @@ static int tpm_builtin_load_sized_data_f
 
     tsb->size = entry.blobsize;
 
-    if (bdrv_pread(bs, entry.offset, tsb->buffer, tsb->size) != tsb->size) {
+    if (tpm_builtin_bdrv_pread(bs, entry.offset, tsb->buffer, tsb->size, be) !=
+        tsb->size) {
         clear_sized_buffer(tsb);
         fprintf(stderr,"tpm: Error while reading sized data!\n");
         rc = -EIO;
@@ -662,7 +799,8 @@ static int tpm_builtin_save_sized_data_t
     }
 
     if (data_len > 0) {
-        if (bdrv_pwrite(bs, entry.offset, data, data_len) != data_len) {
+        if (tpm_builtin_bdrv_pwrite(bs, entry.offset, data, data_len, be) !=
+            data_len) {
             rc = -EIO;
         }
     }
@@ -1572,11 +1710,61 @@ static const char *tpm_builtin_create_de
 }
 
 
+static bool tpm_builtin_parse_as_hexkey(const char *rawkey,
+                                        unsigned char keyvalue[32],
+                                        int *keysize)
+{
+    unsigned int c;
+    unsigned char nib = 0;
+
+    /* skip over leading '0x' */
+    if (!strncmp(rawkey, "0x", 2)) {
+        rawkey += 2;
+    }
+
+    while (c < 64) {
+        if (rawkey[c] == 0) {
+            break;
+        }
+
+        if (rawkey[c] >= '0' && rawkey[c] <= '9') {
+            nib |= rawkey[c] - '0';
+        } else if (rawkey[c] >= 'A' && rawkey[c] <= 'F') {
+            nib |= rawkey[c] - 'A' + 10;
+        } else if (rawkey[c] >= 'a' && rawkey[c] <= 'f') {
+            nib |= rawkey[c] - 'a' + 10;
+        } else {
+            break;
+        }
+
+        keyvalue[c/2] = nib;
+
+        nib <<= 4;
+
+        c++;
+    }
+
+    if (c == 256/4) {
+        *keysize = 256;
+    } else if (c >= 192/4) {
+        *keysize = 192;
+    } else if (c >= 128/4) {
+        *keysize = 128;
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+
 #define TPM_OPTS "id=vtpm-nvram"
 
 static bool tpm_builtin_handle_options(QemuOpts *opts)
 {
     const char *value;
+    int keysize;
+    unsigned char keyvalue[256/8];
 
     value = qemu_opt_get(opts, "path");
     if (value) {
@@ -1585,6 +1773,24 @@ static bool tpm_builtin_handle_options(Q
         fprintf(stderr, "-tpm is missing path= parameter\n");
         return false;
     }
+
+    value = qemu_opt_get(opts, "key");
+    if (value) {
+        has_key = true;
+        memset(keyvalue, 0x0, sizeof(keyvalue));
+
+        if (!tpm_builtin_parse_as_hexkey(value, keyvalue, &keysize)) {
+            keysize = 128;
+            strncpy((char *)keyvalue, value, 128/8);
+        }
+
+        if (AES_set_encrypt_key(keyvalue, keysize, &tpm_enc_key) != 0 ||
+            AES_set_decrypt_key(keyvalue, keysize, &tpm_dec_key) != 0 ) {
+            fprintf(stderr, "tpm: Error setting the key.\n");
+            return false;
+        }
+    }
+
     return true;
 }
 
Index: qemu-git/qemu-config.c
===================================================================
--- qemu-git.orig/qemu-config.c
+++ qemu-git/qemu-config.c
@@ -464,6 +464,11 @@ static QemuOptsList qemu_tpm_opts = {
             .type = QEMU_OPT_STRING,
             .help = "Persitent storage for TPM state",
         },
+        {
+            .name = "key",
+            .type = QEMU_OPT_STRING,
+            .help = "Data encryption key",
+        },
         { /* end of list */ }
     },
 };
Index: qemu-git/qemu-options.hx
===================================================================
--- qemu-git.orig/qemu-options.hx
+++ qemu-git/qemu-options.hx
@@ -1045,7 +1045,7 @@ ETEXI
 # ifdef CONFIG_TPM
 DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \
     ""
-    "-tpm type=<type>,path=<path>\n" \
+    "-tpm type=<type>,path=<path>[,key=<aes key>]\n" \
     "                enable a TPM with state from file in given path\n"
     "                use -tpm ? to get a list of supported TPM types\n",
     QEMU_ARCH_I386)
Index: qemu-git/arch_init.c
===================================================================
--- qemu-git.orig/arch_init.c
+++ qemu-git/arch_init.c
@@ -781,6 +781,7 @@ static int configure_tpm(QemuOpts *opts)
 void select_tpm(const char *optarg)
 {
     QemuOpts *opts;
+    char *key;
 
     if (strcmp("none", optarg) != 0) {
         if (*optarg == '?') {
@@ -794,6 +795,15 @@ void select_tpm(const char *optarg)
         if (configure_tpm(opts)) {
             exit(1);
         }
+
+        /* if a key is provided, wipe it out so no one can see it with 'ps' */
+        key = strstr(optarg, "key=");
+        if (key) {
+            key += 4;
+            while (key[0] && key[0] != ',') {
+                *key++ = '-';
+            }
+        }
     }
 }
 

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

* Re: [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options Stefan Berger
@ 2011-05-06 20:23   ` Serge E. Hallyn
  2011-05-06 20:32     ` Stefan Berger
  2011-05-17 20:58   ` Serge E. Hallyn
  1 sibling, 1 reply; 31+ messages in thread
From: Serge E. Hallyn @ 2011-05-06 20:23 UTC (permalink / raw)
  To: Stefan Berger; +Cc: serge, qemu-devel, andreas.niederl

Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> This patch adds support for TPM command line options.
> The command line supported here (considering the libtpms based
> backend) are
> 
> ./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
> 
> and
> 
> ./qemu-... -tpm ?
>
> where the latter works similar to -soundhw ? and shows a list of
> available TPM backends (i.e., libtpms-based, Xen).
> 
> Only the 'type' is interpreted in arch_init.c. Using this parameter,
> the backend is chosen, i.e., 'builtin' for the libtpms-based
> builtin TPM. 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
> 'handle_options' and return true if the VM can be started or 'false'
> if not enough or bad parameters were provided.
> 
> v4:
>  - coding style fixes
> 
> v3:
>  - added hw/tpm_tis.h to this patch so Qemu compiles at this stage
> 
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

Thanks, Stefan.  Two nits:

> +static QemuOptsList qemu_tpm_opts = {
> +    .name = "tpm",
> +    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
> +    .desc = {
> +        {
> +            .name = "type",
> +            .type = QEMU_OPT_STRING,
> +            .help = "Type of TPM backend",
> +        },
> +        {
> +            .name = "path",
> +            .type = QEMU_OPT_STRING,
> +            .help = "Persitent storage for TPM state",

Persistent.

...

> +# else /* CONFIG_TPM */
> +
> +void select_tpm(const char *optarg)
> +{
> +    (void)optarg;
> +}

I realize this should never get called if !CONFIG_TPM, but that still
doesn't seem like cause to go directly calling a potentially NULL
string.

Otherwise, fwiw

Acked-by: Serge Hallyn <serge.hallyn@ubuntu.com>

thanks,
-serge

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

* Re: [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options
  2011-05-06 20:23   ` Serge E. Hallyn
@ 2011-05-06 20:32     ` Stefan Berger
  2011-05-06 20:33       ` Serge E. Hallyn
  0 siblings, 1 reply; 31+ messages in thread
From: Stefan Berger @ 2011-05-06 20:32 UTC (permalink / raw)
  To: Serge E. Hallyn; +Cc: qemu-devel, andreas.niederl

On 05/06/2011 04:23 PM, Serge E. Hallyn wrote:
> Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
>> This patch adds support for TPM command line options.
>> The command line supported here (considering the libtpms based
>> backend) are
>>
>> ./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
>>
>> and
>>
>> ./qemu-... -tpm ?
>>
>> where the latter works similar to -soundhw ? and shows a list of
>> available TPM backends (i.e., libtpms-based, Xen).
>>
>> Only the 'type' is interpreted in arch_init.c. Using this parameter,
>> the backend is chosen, i.e., 'builtin' for the libtpms-based
>> builtin TPM. 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
>> 'handle_options' and return true if the VM can be started or 'false'
>> if not enough or bad parameters were provided.
>>
>> v4:
>>   - coding style fixes
>>
>> v3:
>>   - added hw/tpm_tis.h to this patch so Qemu compiles at this stage
>>
>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
> Thanks, Stefan.  Two nits:
>
>> +static QemuOptsList qemu_tpm_opts = {
>> +    .name = "tpm",
>> +    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
>> +    .desc = {
>> +        {
>> +            .name = "type",
>> +            .type = QEMU_OPT_STRING,
>> +            .help = "Type of TPM backend",
>> +        },
>> +        {
>> +            .name = "path",
>> +            .type = QEMU_OPT_STRING,
>> +            .help = "Persitent storage for TPM state",
> Persistent.
Oh, typo. Will fix it.
> ...
>
>> +# else /* CONFIG_TPM */
>> +
>> +void select_tpm(const char *optarg)
>> +{
>> +    (void)optarg;
>> +}
> I realize this should never get called if !CONFIG_TPM, but that still
> doesn't seem like cause to go directly calling a potentially NULL
> string.
(void)optarg is just there to make the compiler not put out a warning 
about an unused parameter. I could have used __attribute__((unused)) 
instead but chose this one here. It's not calling anything.

Thanks!

    Stefan
> Otherwise, fwiw
>
> Acked-by: Serge Hallyn<serge.hallyn@ubuntu.com>
>
> thanks,
> -serge

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

* Re: [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options
  2011-05-06 20:32     ` Stefan Berger
@ 2011-05-06 20:33       ` Serge E. Hallyn
  0 siblings, 0 replies; 31+ messages in thread
From: Serge E. Hallyn @ 2011-05-06 20:33 UTC (permalink / raw)
  To: Stefan Berger; +Cc: andreas.niederl, qemu-devel, Serge E. Hallyn

Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> On 05/06/2011 04:23 PM, Serge E. Hallyn wrote:
> >Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> >>This patch adds support for TPM command line options.
> >>The command line supported here (considering the libtpms based
> >>backend) are
> >>
> >>./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
> >>
> >>and
> >>
> >>./qemu-... -tpm ?
> >>
> >>where the latter works similar to -soundhw ? and shows a list of
> >>available TPM backends (i.e., libtpms-based, Xen).
> >>
> >>Only the 'type' is interpreted in arch_init.c. Using this parameter,
> >>the backend is chosen, i.e., 'builtin' for the libtpms-based
> >>builtin TPM. 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
> >>'handle_options' and return true if the VM can be started or 'false'
> >>if not enough or bad parameters were provided.
> >>
> >>v4:
> >>  - coding style fixes
> >>
> >>v3:
> >>  - added hw/tpm_tis.h to this patch so Qemu compiles at this stage
> >>
> >>Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
> >Thanks, Stefan.  Two nits:
> >
> >>+static QemuOptsList qemu_tpm_opts = {
> >>+    .name = "tpm",
> >>+    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
> >>+    .desc = {
> >>+        {
> >>+            .name = "type",
> >>+            .type = QEMU_OPT_STRING,
> >>+            .help = "Type of TPM backend",
> >>+        },
> >>+        {
> >>+            .name = "path",
> >>+            .type = QEMU_OPT_STRING,
> >>+            .help = "Persitent storage for TPM state",
> >Persistent.
> Oh, typo. Will fix it.
> >...
> >
> >>+# else /* CONFIG_TPM */
> >>+
> >>+void select_tpm(const char *optarg)
> >>+{
> >>+    (void)optarg;
> >>+}
> >I realize this should never get called if !CONFIG_TPM, but that still
> >doesn't seem like cause to go directly calling a potentially NULL
> >string.
> (void)optarg is just there to make the compiler not put out a
> warning about an unused parameter. I could have used
> __attribute__((unused)) instead but chose this one here. It's not
> calling anything.

Haha, yes, I must have been seeing things :)

-serge

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

* Re: [Qemu-devel] [PATCH V4 02/10] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 02/10] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2011-05-07  1:54   ` Serge E. Hallyn
  2011-05-18  7:23   ` Markus Armbruster
  1 sibling, 0 replies; 31+ messages in thread
From: Serge E. Hallyn @ 2011-05-07  1:54 UTC (permalink / raw)
  To: Stefan Berger; +Cc: serge, qemu-devel, andreas.niederl

Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> This patch adds the main code of the TPM frontend driver, the TPM TIS
> interface, to Qemu. The code is largely based on my 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.
> 
> v3:
>   - prefixing functions with tis_
>   - added a function to the backend interface 'early_startup_tpm' that
>     allows to detect the presence of the block storage and gracefully fails
>     Qemu if it's not available. This works with migration using shared
>     storage but doesn't support migration with block storage migration.
>     For encyrypted QCoW2 and in case of a snapshot resue the late_startup_tpm
>     interface function is called
> 
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

Most of this is pretty foreign to me so this doesn't mean much, but

Acked-by: Serge Hallyn <serge.hallyn@ubuntu.com>

> +/*

Worth pointing out here that this is called with mutex held.

> + * read a byte of response data
> + */
> +static uint32_t tis_data_read(TPMState *s, uint8_t locty)
> +{
> +    uint32_t ret = TPM_NO_DATA_BYTE;
> +    uint16_t len;
> +
> +    if ((s->loc[locty].sts & STS_DATA_AVAILABLE)) {
> +        len = tis_get_size_from_buffer(&s->loc[locty].r_buffer);
> +
> +        ret = s->loc[locty].r_buffer.buffer[s->loc[locty].r_offset++];
> +        if (s->loc[locty].r_offset >= len) {
> +            /* got last byte */
> +            s->loc[locty].sts = STS_VALID;

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

* Re: [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (9 preceding siblings ...)
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 10/10] Encrypt state blobs using AES CBC encryption Stefan Berger
@ 2011-05-09 14:21 ` Serge E. Hallyn
  2011-05-09 17:37   ` Stefan Berger
  2011-05-10  4:07 ` Serge E. Hallyn
  11 siblings, 1 reply; 31+ messages in thread
From: Serge E. Hallyn @ 2011-05-09 14:21 UTC (permalink / raw)
  To: Stefan Berger; +Cc: serge, qemu-devel, andreas.niederl

Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> The following series of patches adds a TPM (Trusted Platform Module)
> TIS (TPM Interface Spec) interface to Qemu and with that provides
> means to access a backend implementing the actual TPM functionality.
> This frontend enables for example Linux's TPM TIS (tpm_tis) driver.
> 
> I am also posting the implementation of a backend implementation that is based
> on a library (libtpms) providing TPM functionality. This library is currently
> undergoing further testing but is now available via Fedora Rawhide:
> 
> http://download.fedora.redhat.com/pub/fedora/linux/development/rawhide/x86_64/os/Packages/libtpms-0.5.1-5.x86_64.rpm
> http://download.fedora.redhat.com/pub/fedora/linux/development/rawhide/x86_64/os/Packages/libtpms-devel-0.5.1-5.x86_64.rpm

Hi,

where is the source for these?

thanks,
-serge

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

* Re: [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration
  2011-05-09 14:21 ` [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Serge E. Hallyn
@ 2011-05-09 17:37   ` Stefan Berger
  0 siblings, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-09 17:37 UTC (permalink / raw)
  To: Serge E. Hallyn; +Cc: qemu-devel, andreas.niederl

On 05/09/2011 10:21 AM, Serge E. Hallyn wrote:
> Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
>> The following series of patches adds a TPM (Trusted Platform Module)
>> TIS (TPM Interface Spec) interface to Qemu and with that provides
>> means to access a backend implementing the actual TPM functionality.
>> This frontend enables for example Linux's TPM TIS (tpm_tis) driver.
>>
>> I am also posting the implementation of a backend implementation that is based
>> on a library (libtpms) providing TPM functionality. This library is currently
>> undergoing further testing but is now available via Fedora Rawhide:
>>
>> http://download.fedora.redhat.com/pub/fedora/linux/development/rawhide/x86_64/os/Packages/libtpms-0.5.1-5.x86_64.rpm
>> http://download.fedora.redhat.com/pub/fedora/linux/development/rawhide/x86_64/os/Packages/libtpms-devel-0.5.1-5.x86_64.rpm
> Hi,
>
> where is the source for these?

http://download.fedora.redhat.com/pub/fedora/linux/development/rawhide/source/SRPMS/libtpms-0.5.1-5.src.rpm

    Stefan

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

* Re: [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration
  2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
                   ` (10 preceding siblings ...)
  2011-05-09 14:21 ` [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Serge E. Hallyn
@ 2011-05-10  4:07 ` Serge E. Hallyn
  2011-05-10 10:46   ` Stefan Berger
  11 siblings, 1 reply; 31+ messages in thread
From: Serge E. Hallyn @ 2011-05-10  4:07 UTC (permalink / raw)
  To: Stefan Berger; +Cc: serge, qemu-devel, andreas.niederl

To get this to compile on top of qemu-kvm, I needed the following
patch to force CONFIG_THREAD on so as to define things like
qemu_mutex_lock:

Index: qemu-kvm-tpm/configure
===================================================================
--- qemu-kvm-tpm.orig/configure	2011-05-09 21:19:10.920002303 -0500
+++ qemu-kvm-tpm/configure	2011-05-09 21:19:22.150002305 -0500
@@ -3420,6 +3420,7 @@
           exit 1
       fi
       echo "CONFIG_TPM=y" >> $config_host_mak
+      echo "CONFIG_THREAD=y" >> $config_host_mak
   fi
 fi
 

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

* Re: [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration
  2011-05-10  4:07 ` Serge E. Hallyn
@ 2011-05-10 10:46   ` Stefan Berger
  2011-05-10 11:59     ` Serge E. Hallyn
  0 siblings, 1 reply; 31+ messages in thread
From: Stefan Berger @ 2011-05-10 10:46 UTC (permalink / raw)
  To: Serge E. Hallyn; +Cc: qemu-devel, andreas.niederl

On 05/10/2011 12:07 AM, Serge E. Hallyn wrote:
> To get this to compile on top of qemu-kvm, I needed the following
> patch to force CONFIG_THREAD on so as to define things like
> qemu_mutex_lock:
>
> Index: qemu-kvm-tpm/configure
> ===================================================================
> --- qemu-kvm-tpm.orig/configure	2011-05-09 21:19:10.920002303 -0500
> +++ qemu-kvm-tpm/configure	2011-05-09 21:19:22.150002305 -0500
> @@ -3420,6 +3420,7 @@
>             exit 1
>         fi
>         echo "CONFIG_TPM=y">>  $config_host_mak
> +      echo "CONFIG_THREAD=y">>  $config_host_mak
>     fi
>   fi
>
That seems to be qemu-kvm specific.

   Stefan

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

* Re: [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration
  2011-05-10 10:46   ` Stefan Berger
@ 2011-05-10 11:59     ` Serge E. Hallyn
  2011-05-10 12:43       ` Stefan Berger
  0 siblings, 1 reply; 31+ messages in thread
From: Serge E. Hallyn @ 2011-05-10 11:59 UTC (permalink / raw)
  To: Stefan Berger; +Cc: andreas.niederl, qemu-devel, Serge E. Hallyn

Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> On 05/10/2011 12:07 AM, Serge E. Hallyn wrote:
> >To get this to compile on top of qemu-kvm, I needed the following
> >patch to force CONFIG_THREAD on so as to define things like
> >qemu_mutex_lock:
> >
> >Index: qemu-kvm-tpm/configure
> >===================================================================
> >--- qemu-kvm-tpm.orig/configure	2011-05-09 21:19:10.920002303 -0500
> >+++ qemu-kvm-tpm/configure	2011-05-09 21:19:22.150002305 -0500
> >@@ -3420,6 +3420,7 @@
> >            exit 1
> >        fi
> >        echo "CONFIG_TPM=y">>  $config_host_mak
> >+      echo "CONFIG_THREAD=y">>  $config_host_mak
> >    fi
> >  fi
> >
> That seems to be qemu-kvm specific.

Hm, yeah, I guess it is.  Wonder what the odds are of that actually
working then.  Well, I've just about got this and libtpms packaged,
will hopefully finish up this afternoon and see.

I saw nothing problematic in the patches, but just didn't feel
qualified to send acks based on simple review, so figured I'd be
better off actually testing.

What is your plan regarding libtpms?  Will you be making actual
releases at sf.net at some point?

thanks,
-serge

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

* Re: [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration
  2011-05-10 11:59     ` Serge E. Hallyn
@ 2011-05-10 12:43       ` Stefan Berger
  2011-05-10 14:20         ` Serge E. Hallyn
  0 siblings, 1 reply; 31+ messages in thread
From: Stefan Berger @ 2011-05-10 12:43 UTC (permalink / raw)
  To: Serge E. Hallyn; +Cc: Anthony Liguori, qemu-devel, andreas.niederl

On 05/10/2011 07:59 AM, Serge E. Hallyn wrote:
> Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
>> On 05/10/2011 12:07 AM, Serge E. Hallyn wrote:
>>> To get this to compile on top of qemu-kvm, I needed the following
>>> patch to force CONFIG_THREAD on so as to define things like
>>> qemu_mutex_lock:
>>>
>>> Index: qemu-kvm-tpm/configure
>>> ===================================================================
>>> --- qemu-kvm-tpm.orig/configure	2011-05-09 21:19:10.920002303 -0500
>>> +++ qemu-kvm-tpm/configure	2011-05-09 21:19:22.150002305 -0500
>>> @@ -3420,6 +3420,7 @@
>>>             exit 1
>>>         fi
>>>         echo "CONFIG_TPM=y">>   $config_host_mak
>>> +      echo "CONFIG_THREAD=y">>   $config_host_mak
>>>     fi
>>>   fi
>>>
>> That seems to be qemu-kvm specific.
> Hm, yeah, I guess it is.  Wonder what the odds are of that actually
> working then.  Well, I've just about got this and libtpms packaged,
> will hopefully finish up this afternoon and see.
>
> I saw nothing problematic in the patches, but just didn't feel
> qualified to send acks based on simple review, so figured I'd be
> better off actually testing.
>
I am currently making modifications to the patches to support command 
lines like this one to be in line with other devices:

-tpm type=builtin,path=<path>,id=xyz   -device tpm_tis,id=xyz

Typically with command lines like this Qemu also supports multiple 
devices of the same type. With the TPM this is a bit problematic since 
it would need to support multiple TPMs also in the firmware (aka BIOS). 
So I may support this command line but only allow one TPM.

> What is your plan regarding libtpms?  Will you be making actual
> releases at sf.net at some point?
I was going to wait for a review of all the patches here on the ml and 
see the code checked in -- until that hasn't happened anything could 
change. So for now I am keeping libtpms in Fedora Rawhide and then was 
going to produce a libtpms-0.5.2 and make it commonly available via 
Fedora, maybe putting a copy of the library on sf.net. I would hold off 
on packaging and distributing it.

    Stefan

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

* Re: [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration
  2011-05-10 12:43       ` Stefan Berger
@ 2011-05-10 14:20         ` Serge E. Hallyn
  0 siblings, 0 replies; 31+ messages in thread
From: Serge E. Hallyn @ 2011-05-10 14:20 UTC (permalink / raw)
  To: Stefan Berger
  Cc: Anthony Liguori, andreas.niederl, qemu-devel, Serge E. Hallyn

Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> >What is your plan regarding libtpms?  Will you be making actual
> >releases at sf.net at some point?
> I was going to wait for a review of all the patches here on the ml
> and see the code checked in -- until that hasn't happened anything
> could change. So for now I am keeping libtpms in Fedora Rawhide and
> then was going to produce a libtpms-0.5.2 and make it commonly
> available via Fedora, maybe putting a copy of the library on sf.net.
> I would hold off on packaging and distributing it.

Ok, thanks, so I certainly won't push it into the archives yet, but
will keep it in a ppa for easy testing.

thanks,
-serge

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

* Re: [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options Stefan Berger
  2011-05-06 20:23   ` Serge E. Hallyn
@ 2011-05-17 20:58   ` Serge E. Hallyn
  2011-05-17 23:15     ` Stefan Berger
  2011-05-17 23:16     ` Stefan Berger
  1 sibling, 2 replies; 31+ messages in thread
From: Serge E. Hallyn @ 2011-05-17 20:58 UTC (permalink / raw)
  To: Stefan Berger; +Cc: serge, qemu-devel, andreas.niederl

Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> This patch adds support for TPM command line options.
> The command line supported here (considering the libtpms based
> backend) are
> 
> ./qemu-... -tpm type=<type>,path=<path to blockstorage file>,

Hm, I did

kvm -tpm type=builtin,path=tpm.img -m 1G disk.img -vnc :1

with disk.img being a newly installed VM.  I installed trousers
and tpm-tools, tried loading the tpm, tpm_tis, tpm_infineon, and
some other modules, /dev/tpm was never created, and

  tpm_takeownership

continued to give me:

Tspi_Context_Connect failed: 0x00003011 - layer=tsp, code=0011 (17), Communication failure

Which kernel module should work with builtin?

thanks,
-serge

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

* Re: [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options
  2011-05-17 20:58   ` Serge E. Hallyn
@ 2011-05-17 23:15     ` Stefan Berger
  2011-05-18  1:52       ` Serge E. Hallyn
  2011-05-17 23:16     ` Stefan Berger
  1 sibling, 1 reply; 31+ messages in thread
From: Stefan Berger @ 2011-05-17 23:15 UTC (permalink / raw)
  To: qemu-devel

On 05/17/2011 04:58 PM, Serge E. Hallyn wrote:
> Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
>> This patch adds support for TPM command line options.
>> The command line supported here (considering the libtpms based
>> backend) are
>>
>> ./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
> Hm, I did
>
> kvm -tpm type=builtin,path=tpm.img -m 1G disk.img -vnc :1
>
> with disk.img being a newly installed VM.  I installed trousers
> and tpm-tools, tried loading the tpm, tpm_tis, tpm_infineon, and
> some other modules, /dev/tpm was never created, and
>
>    tpm_takeownership
>
> continued to give me:
>
> Tspi_Context_Connect failed: 0x00003011 - layer=tsp, code=0011 (17), Communication failure
>
> Which kernel module should work with builtin?
The device model is a tpm-tis. So modprobe tpm_tis should create a 
/dev/tpm0. If nothing else helps enable the DEBUG_TIS in hw/tpm_tis and 
recompile.

Did you start it with SeaBIOS and the TPM-related patches applied to it? 
If not, you'll have to initialize the TPM that otherwise the BIOS would 
do. Otherwise the debugging output from the tpm-tis should begin very 
early once the BIOS sends commands to the TIS/TPM.

Regards,
   Stefan
> thanks,
> -serge
>

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

* Re: [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options
  2011-05-17 20:58   ` Serge E. Hallyn
  2011-05-17 23:15     ` Stefan Berger
@ 2011-05-17 23:16     ` Stefan Berger
  1 sibling, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-17 23:16 UTC (permalink / raw)
  To: Serge E. Hallyn; +Cc: qemu-devel, andreas.niederl

On 05/17/2011 04:58 PM, Serge E. Hallyn wrote:
> Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
>> This patch adds support for TPM command line options.
>> The command line supported here (considering the libtpms based
>> backend) are
>>
>> ./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
> Hm, I did
>
> kvm -tpm type=builtin,path=tpm.img -m 1G disk.img -vnc :1
>
> with disk.img being a newly installed VM.  I installed trousers
> and tpm-tools, tried loading the tpm, tpm_tis, tpm_infineon, and
> some other modules, /dev/tpm was never created, and
>
>    tpm_takeownership
>
> continued to give me:
>
> Tspi_Context_Connect failed: 0x00003011 - layer=tsp, code=0011 (17), Communication failure
>
> Which kernel module should work with builtin?
The device model is a tpm-tis. So modprobe tpm_tis should create a 
/dev/tpm0. If nothing else helps enable the DEBUG_TIS in hw/tpm_tis and 
recompile.

Did you start it with SeaBIOS and the TPM-related patches applied to it? 
If not, you'll have to initialize the TPM that otherwise the BIOS would 
do. Otherwise the debugging output from the tpm-tis should begin very 
early once the BIOS sends commands to the TIS/TPM.

Regards,
   Stefan
> thanks,
> -serge

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

* Re: [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options
  2011-05-17 23:15     ` Stefan Berger
@ 2011-05-18  1:52       ` Serge E. Hallyn
  0 siblings, 0 replies; 31+ messages in thread
From: Serge E. Hallyn @ 2011-05-18  1:52 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel

Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> On 05/17/2011 04:58 PM, Serge E. Hallyn wrote:
> >Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> >>This patch adds support for TPM command line options.
> >>The command line supported here (considering the libtpms based
> >>backend) are
> >>
> >>./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
> >Hm, I did
> >
> >kvm -tpm type=builtin,path=tpm.img -m 1G disk.img -vnc :1
> >
> >with disk.img being a newly installed VM.  I installed trousers
> >and tpm-tools, tried loading the tpm, tpm_tis, tpm_infineon, and
> >some other modules, /dev/tpm was never created, and
> >
> >   tpm_takeownership
> >
> >continued to give me:
> >
> >Tspi_Context_Connect failed: 0x00003011 - layer=tsp, code=0011 (17), Communication failure
> >
> >Which kernel module should work with builtin?
> The device model is a tpm-tis. So modprobe tpm_tis should create a
> /dev/tpm0. If nothing else helps enable the DEBUG_TIS in hw/tpm_tis
> and recompile.
> 
> Did you start it with SeaBIOS and the TPM-related patches applied to

No, feh!  I'll go grab those, thanks.

Sorry, I had apparently moved 0/10 (bc it screws up git am), and
then thought 1/10 was also the intro bc it looks like it has an
intro.  (Now I see 0/10 at
http://lists.gnu.org/archive/html/qemu-devel/2011-05/msg00541.html)

thanks,
-serge

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

* Re: [Qemu-devel] [PATCH V4 02/10] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 02/10] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
  2011-05-07  1:54   ` Serge E. Hallyn
@ 2011-05-18  7:23   ` Markus Armbruster
  2011-05-18 10:53     ` Stefan Berger
  1 sibling, 1 reply; 31+ messages in thread
From: Markus Armbruster @ 2011-05-18  7:23 UTC (permalink / raw)
  To: Stefan Berger; +Cc: serge, qemu-devel, andreas.niederl

Stefan Berger <stefanb@linux.vnet.ibm.com> writes:

> This patch adds the main code of the TPM frontend driver, the TPM TIS
> interface, to Qemu. The code is largely based on my 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.
>
> v3:
>   - prefixing functions with tis_
>   - added a function to the backend interface 'early_startup_tpm' that
>     allows to detect the presence of the block storage and gracefully fails
>     Qemu if it's not available. This works with migration using shared
>     storage but doesn't support migration with block storage migration.
>     For encyrypted QCoW2 and in case of a snapshot resue the late_startup_tpm
>     interface function is called
>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
>
> ---
>  hw/tpm_tis.c |  871 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 871 insertions(+)
>
> Index: qemu-git/hw/tpm_tis.c
> ===================================================================
> --- /dev/null
> +++ qemu-git/hw/tpm_tis.c
[...]
> +static void tis_reset(DeviceState *d)
> +{
> +    TPMState *s = container_of(d, TPMState, busdev.qdev);
> +    tis_s_reset(s);
> +}
> +
> +
> +static int tis_init(ISADevice *dev)

Function not used, sure this compiles with -Werror?  If not, it needs
fixing.

As far as I can see, the use is added in 03/10, where you add the
missing qdev parts.  Makes it hard to review for qdev sanity, so I did
*not* do that.

> +{
> +    TPMState *s = DO_UPCAST(TPMState, busdev, dev);
> +    int iomemtype, rc;
> +
> +    qemu_mutex_init(&s->state_lock);
> +    qemu_cond_init(&s->from_tpm_cond);
> +    qemu_cond_init(&s->to_tpm_cond);
> +
> +    if (active_be->init(s, tis_tpm_receive_cb)) {
> +        goto err_exit;
> +    }
> +
> +    isa_init_irq(dev, &s->irq, s->irq_num);
> +
> +    iomemtype = cpu_register_io_memory(tis_readfn, tis_writefn, s,
> +                                       DEVICE_LITTLE_ENDIAN);
> +    cpu_register_physical_memory(TIS_ADDR_BASE, 0x1000 * NUM_LOCALITIES,
> +                                 iomemtype);
> +
> +    /*
> +     * startup the TPM backend early to detect problems early
> +     */
> +    rc = tis_do_early_startup_tpm(s);
> +    if (rc != 0 && rc != -ENOKEY) {
> +        fprintf(stderr,"tpm_tis: Fatal error accessing TPM's block storage.\n");
> +        goto err_exit;
> +    }
> +
> +    return 0;
> +
> + err_exit:
> +    return -1;
> +}
> +

Please fix "new blank line at EOF."

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

* Re: [Qemu-devel] [PATCH V4 03/10] Add persistent state handling to TPM TIS frontend driver
  2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 03/10] Add persistent state handling to TPM TIS frontend driver Stefan Berger
@ 2011-05-18  7:25   ` Markus Armbruster
  2011-05-18 10:51     ` Stefan Berger
  2011-05-25 14:49     ` Stefan Berger
  0 siblings, 2 replies; 31+ messages in thread
From: Markus Armbruster @ 2011-05-18  7:25 UTC (permalink / raw)
  To: Stefan Berger; +Cc: serge, qemu-devel, andreas.niederl

Stefan Berger <stefanb@linux.vnet.ibm.com> writes:

> This patch adds support for handling of persistent state to the TPM TIS
> frontend.
>
> The currently used buffer is determined (can only be in currently active
> locality and either be a read or a write buffer) and only that buffer's content
> is stored. The reverse is done when the state is restored from disk
> where the buffer's content are copied into the currently used buffer.
>
> To keep compatibility with existing Xen the VMStateDescription was adapted
> to be compatible with existing state. For that I am adding Andreas
> Niederl as an author to the file.
>
> v4:
>  - main thread releases the 'state' lock while periodically calling the
>    backends function that may request it to write data into block storage.
>
> v3:
>  - all functions prefixed with tis_
>  - while the main thread is waiting for an outstanding TPM command to finish,
>    it periodically does some work (writes data to the block storage)
>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
>
> ---
>  hw/tpm_tis.c |  167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 167 insertions(+)
>
> Index: qemu-git/hw/tpm_tis.c
> ===================================================================
> --- qemu-git.orig/hw/tpm_tis.c
> +++ qemu-git/hw/tpm_tis.c
[...]
> +static ISADeviceInfo tis_device_info = {
> +    .init         = tis_init,
> +    .qdev.name    = "tpm-tis",
> +    .qdev.size    = sizeof(TPMState),
> +    .qdev.no_user = 1,
> +    .qdev.vmsd    = &vmstate_tis,
> +    .qdev.reset   = tis_reset,
> +    .qdev.props = (Property[]) {
> +        DEFINE_PROP_UINT8("active_locality", TPMState,
> +                          active_locty, NO_LOCALITY),
> +        DEFINE_PROP_UINT32("irq", TPMState,
> +                           irq_num, TPM_TIS_IRQ),
> +        DEFINE_PROP_END_OF_LIST(),
> +    },
> +};
> +
> +
> +static void tis_register_device(void)
> +{
> +    isa_qdev_register(&tis_device_info);
> +}
> +
> +device_init(tis_register_device)

Why is this device no_user?

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

* Re: [Qemu-devel] [PATCH V4 03/10] Add persistent state handling to TPM TIS frontend driver
  2011-05-18  7:25   ` Markus Armbruster
@ 2011-05-18 10:51     ` Stefan Berger
  2011-05-25 14:49     ` Stefan Berger
  1 sibling, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-18 10:51 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: serge, qemu-devel, andreas.niederl

On 05/18/2011 03:25 AM, Markus Armbruster wrote:
> Stefan Berger<stefanb@linux.vnet.ibm.com>  writes:
>
> Why is this device no_user?
Because I instantiated it still with isa_create_simple(). This is going 
to change in v5.

Thanks.
    Stefan

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

* Re: [Qemu-devel] [PATCH V4 02/10] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
  2011-05-18  7:23   ` Markus Armbruster
@ 2011-05-18 10:53     ` Stefan Berger
  0 siblings, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-18 10:53 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: serge, qemu-devel, andreas.niederl

On 05/18/2011 03:23 AM, Markus Armbruster wrote:
> Stefan Berger<stefanb@linux.vnet.ibm.com>  writes:
>
>> This patch adds the main code of the TPM frontend driver, the TPM TIS
>> interface, to Qemu. The code is largely based on my 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.
>>
>> v3:
>>    - prefixing functions with tis_
>>    - added a function to the backend interface 'early_startup_tpm' that
>>      allows to detect the presence of the block storage and gracefully fails
>>      Qemu if it's not available. This works with migration using shared
>>      storage but doesn't support migration with block storage migration.
>>      For encyrypted QCoW2 and in case of a snapshot resue the late_startup_tpm
>>      interface function is called
>>
>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>>
>> ---
>>   hw/tpm_tis.c |  871 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 871 insertions(+)
>>
>> Index: qemu-git/hw/tpm_tis.c
>> ===================================================================
>> --- /dev/null
>> +++ qemu-git/hw/tpm_tis.c
> [...]
>> +static void tis_reset(DeviceState *d)
>> +{
>> +    TPMState *s = container_of(d, TPMState, busdev.qdev);
>> +    tis_s_reset(s);
>> +}
>> +
>> +
>> +static int tis_init(ISADevice *dev)
> Function not used, sure this compiles with -Werror?  If not, it needs
> fixing.
>
> As far as I can see, the use is added in 03/10, where you add the
> missing qdev parts.  Makes it hard to review for qdev sanity, so I did
> *not* do that.
>
I tried to split up the patches in smaller parts and had to cut 
'somewhere'.
Without additional compile option it does not report any errors.

>> +{
>> +    TPMState *s = DO_UPCAST(TPMState, busdev, dev);
>> +    int iomemtype, rc;
>> +
>> +    qemu_mutex_init(&s->state_lock);
>> +    qemu_cond_init(&s->from_tpm_cond);
>> +    qemu_cond_init(&s->to_tpm_cond);
>> +
>> +    if (active_be->init(s, tis_tpm_receive_cb)) {
>> +        goto err_exit;
>> +    }
>> +
>> +    isa_init_irq(dev,&s->irq, s->irq_num);
>> +
>> +    iomemtype = cpu_register_io_memory(tis_readfn, tis_writefn, s,
>> +                                       DEVICE_LITTLE_ENDIAN);
>> +    cpu_register_physical_memory(TIS_ADDR_BASE, 0x1000 * NUM_LOCALITIES,
>> +                                 iomemtype);
>> +
>> +    /*
>> +     * startup the TPM backend early to detect problems early
>> +     */
>> +    rc = tis_do_early_startup_tpm(s);
>> +    if (rc != 0&&  rc != -ENOKEY) {
>> +        fprintf(stderr,"tpm_tis: Fatal error accessing TPM's block storage.\n");
>> +        goto err_exit;
>> +    }
>> +
>> +    return 0;
>> +
>> + err_exit:
>> +    return -1;
>> +}
>> +
> Please fix "new blank line at EOF."
Will do.

Thanks.
    Stefan

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

* Re: [Qemu-devel] [PATCH V4 03/10] Add persistent state handling to TPM TIS frontend driver
  2011-05-18  7:25   ` Markus Armbruster
  2011-05-18 10:51     ` Stefan Berger
@ 2011-05-25 14:49     ` Stefan Berger
  1 sibling, 0 replies; 31+ messages in thread
From: Stefan Berger @ 2011-05-25 14:49 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: serge, qemu-devel, andreas.niederl

On 05/18/2011 03:25 AM, Markus Armbruster wrote:
> Stefan Berger<stefanb@linux.vnet.ibm.com>  writes:
>
>> +static void tis_register_device(void)
>> +{
>> +    isa_qdev_register(&tis_device_info);
>> +}
>> +
>> +device_init(tis_register_device)
> Why is this device no_user?
In case you want to review V5, I posted it:

http://lists.nongnu.org/archive/html/qemu-devel/2011-05/msg02091.html

Regards,
    Stefan

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

end of thread, other threads:[~2011-05-25 14:50 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-06 17:32 [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 01/10] Support for TPM command line options Stefan Berger
2011-05-06 20:23   ` Serge E. Hallyn
2011-05-06 20:32     ` Stefan Berger
2011-05-06 20:33       ` Serge E. Hallyn
2011-05-17 20:58   ` Serge E. Hallyn
2011-05-17 23:15     ` Stefan Berger
2011-05-18  1:52       ` Serge E. Hallyn
2011-05-17 23:16     ` Stefan Berger
2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 02/10] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
2011-05-07  1:54   ` Serge E. Hallyn
2011-05-18  7:23   ` Markus Armbruster
2011-05-18 10:53     ` Stefan Berger
2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 03/10] Add persistent state handling to TPM TIS frontend driver Stefan Berger
2011-05-18  7:25   ` Markus Armbruster
2011-05-18 10:51     ` Stefan Berger
2011-05-25 14:49     ` Stefan Berger
2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 04/10] Add tpm_tis driver to build process Stefan Berger
2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 05/10] Add a debug register Stefan Berger
2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 06/10] Add a TPM backend skeleton implementation Stefan Berger
2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 07/10] Implementation of the libtpms-based backend Stefan Berger
2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 08/10] Introduce file lock for the block layer Stefan Berger
2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 09/10] Add block storage support for libtpms based TPM backend Stefan Berger
2011-05-06 17:32 ` [Qemu-devel] [PATCH V4 10/10] Encrypt state blobs using AES CBC encryption Stefan Berger
2011-05-09 14:21 ` [Qemu-devel] [PATCH V4 00/10] Qemu Trusted Platform Module (TPM) integration Serge E. Hallyn
2011-05-09 17:37   ` Stefan Berger
2011-05-10  4:07 ` Serge E. Hallyn
2011-05-10 10:46   ` Stefan Berger
2011-05-10 11:59     ` Serge E. Hallyn
2011-05-10 12:43       ` Stefan Berger
2011-05-10 14:20         ` Serge E. Hallyn

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.