Patchwork [V7,13/13] Add a TPM backend null driver implementation

login
register
mail settings
Submitter Stefan Berger
Date Aug. 10, 2011, 7:29 p.m.
Message ID <20110810193018.619841098@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/109463/
State New
Headers show

Comments

Stefan Berger - Aug. 10, 2011, 7:29 p.m.
This patch adds a TPM null driver implementation acting as a backend for
the TIS hardware emulation. The NULL driver responds to all commands with
a TPM fault response.

To use this null driver, use either

-tpm null

or

-tpmdev null,id=tpm0 -device tpm-tis,tpmdev=tpm0

as parameters on the command line.

If TPM support is chosen via './configure --enable-tpm ...' TPM support is now
always compiled into Qemu and at least the null driver will be available on
emulators for x86_64 and i386.

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

---
 Makefile.target |    2 
 configure       |    8 -
 hw/tpm_null.c   |  326 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-options.hx |   13 +-
 tpm.c           |    1 
 tpm.h           |    1 
 6 files changed, 340 insertions(+), 11 deletions(-)

Patch

Index: qemu-git/hw/tpm_null.c
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_null.c
@@ -0,0 +1,326 @@ 
+/*
+ *  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 "tpm.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 bool thread_terminate;
+static bool thread_running;
+
+static ThreadParams tpm_thread_params;
+
+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_null_main_loop(void *d)
+{
+    ThreadParams *thr_parms = d;
+    uint32_t in_len;
+    uint8_t *in, *out;
+    uint8_t locty;
+
+#ifdef DEBUG_TPM
+    fprintf(stderr, "tpm: THREAD IS STARTING\n");
+#endif
+
+    /* 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;
+            }
+
+            locty = thr_parms->tpm_state->command_locty;
+
+            in_len = thr_parms->tpm_state->loc[locty].w_offset;
+
+            out = thr_parms->tpm_state->loc[locty].r_buffer.buffer;
+
+            memcpy(out, tpm_std_fatal_error_response,
+                   sizeof(tpm_std_fatal_error_response));
+
+            out[1] = (in_len > 2 && in[1] >= 0xc1 && in[1] <= 0xc3)
+                   ? in[1] + 3
+                   : 0xc4;
+#ifdef DEBUG_TPM
+            fprintf(stderr, "tpm_null: sending fault response to VM\n");
+#endif
+            thr_parms->recv_data_callback(thr_parms->tpm_state, locty);
+        } while (in_len > 0);
+    }
+
+#ifdef DEBUG_TPM
+    fprintf(stderr, "tpm: THREAD IS ENDING\n");
+#endif
+
+    thread_running = false;
+
+    return NULL;
+}
+
+
+static void tpm_null_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);
+
+        memset(&thread, 0, sizeof(thread));
+    }
+}
+
+
+static void tpm_null_tpm_atexit(void)
+{
+    tpm_null_terminate_tpm_thread();
+}
+
+
+/**
+ * Start the TPM (thread). If it had been started before, then terminate
+ * and start it again.
+ */
+static int tpm_null_startup_tpm(void)
+{
+    /* terminate a running TPM */
+    tpm_null_terminate_tpm_thread();
+
+    /* reset the flag so the thread keeps on running */
+    thread_terminate = false;
+
+    qemu_thread_create(&thread, tpm_null_main_loop, &tpm_thread_params);
+
+    thread_running = true;
+
+    return 0;
+}
+
+
+static int tpm_null_do_startup_tpm(void)
+{
+    return tpm_null_startup_tpm();
+}
+
+
+static int tpm_null_early_startup_tpm(void)
+{
+    return tpm_null_do_startup_tpm();
+}
+
+
+static int tpm_null_late_startup_tpm(void)
+{
+    return tpm_null_do_startup_tpm();
+}
+
+
+static void tpm_null_reset(void)
+{
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    fprintf(stderr, "tpm: CALL TO TPM_RESET!\n");
+#endif
+
+    tpm_null_terminate_tpm_thread();
+}
+
+
+/*
+ * Since the null driver does not have much persistent storage
+ * there is not much to do here...
+ */
+static int tpm_null_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_null_init(TPMState *s, TPMRecvDataCB *recv_data_cb)
+{
+    tpm_thread_params.tpm_state = s;
+    tpm_thread_params.recv_data_callback = recv_data_cb;
+
+    atexit(tpm_null_tpm_atexit);
+
+    return 0;
+}
+
+
+static bool tpm_null_get_tpm_established_flag(void)
+{
+    return false;
+}
+
+
+static bool tpm_null_get_startup_error(void)
+{
+    return false;
+}
+
+
+/**
+ * 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_null_save_volatile_data(void)
+{
+    return 0;
+}
+
+
+static size_t tpm_null_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_null_create_desc(void)
+{
+    static int done;
+
+    if (!done) {
+        snprintf(dev_description, sizeof(dev_description),
+                 "Null TPM backend driver");
+        done = 1;
+    }
+
+    return dev_description;
+}
+
+
+static TPMBackend *tpm_null_create(QemuOpts *opts, const char *id,
+                                      const char *model)
+{
+    TPMBackend *driver;
+
+    driver = qemu_malloc(sizeof(TPMBackend));
+    if (!driver) {
+        fprintf(stderr, "Could not allocate memory.\n");
+        return NULL;
+    }
+    driver->id = qemu_strdup(id);
+    if (model) {
+        driver->model = qemu_strdup(model);
+    }
+    driver->ops = &tpm_null_driver;
+
+    return driver;
+}
+
+
+static void tpm_null_destroy(TPMBackend *driver)
+{
+    qemu_free(driver->id);
+    qemu_free(driver->model);
+    qemu_free(driver);
+}
+
+
+TPMDriverOps tpm_null_driver = {
+    .id                       = "null",
+    .desc                     = tpm_null_create_desc,
+    .job_for_main_thread      = NULL,
+    .create                   = tpm_null_create,
+    .destroy                  = tpm_null_destroy,
+    .init                     = tpm_null_init,
+    .early_startup_tpm        = tpm_null_early_startup_tpm,
+    .late_startup_tpm         = tpm_null_late_startup_tpm,
+    .realloc_buffer           = tpm_null_realloc_buffer,
+    .reset                    = tpm_null_reset,
+    .had_startup_error        = tpm_null_get_startup_error,
+    .save_volatile_data       = tpm_null_save_volatile_data,
+    .load_volatile_data       = tpm_null_instantiate_with_volatile_data,
+    .get_tpm_established_flag = tpm_null_get_tpm_established_flag,
+};
Index: qemu-git/Makefile.target
===================================================================
--- qemu-git.orig/Makefile.target
+++ qemu-git/Makefile.target
@@ -233,7 +233,7 @@  obj-i386-y += debugcon.o multiboot.o
 obj-i386-y += pc_piix.o
 obj-i386-$(CONFIG_KVM) += kvmclock.o
 obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
-obj-i386-$(CONFIG_TPM) += tpm_tis.o sha1.o
+obj-i386-$(CONFIG_TPM) += tpm_tis.o sha1.o tpm_null.o
 obj-i386-$(CONFIG_TPM_BUILTIN) += tpm_builtin.o
 
 ifdef CONFIG_TPM_BUILTIN
Index: qemu-git/tpm.c
===================================================================
--- qemu-git.orig/tpm.c
+++ qemu-git/tpm.c
@@ -24,6 +24,7 @@ 
 #if defined(TARGET_I386) || defined(TARGET_X86_64)
 
 static const TPMDriverOps *bes[] = {
+    &tpm_null_driver,
 #ifdef CONFIG_TPM_BUILTIN
     &tpm_builtin,
 #endif
Index: qemu-git/tpm.h
===================================================================
--- qemu-git.orig/tpm.h
+++ qemu-git/tpm.h
@@ -141,6 +141,7 @@  void tpm_measure_buffer(const void *buff
                         TPMMeasureType type, uint8_t pcrindex,
                         const void *data, uint32_t data_len);
 
+extern TPMDriverOps tpm_null_driver;
 extern TPMDriverOps tpm_builtin;
 
 #endif /* _HW_TPM_CONFIG_H */
Index: qemu-git/qemu-options.hx
===================================================================
--- qemu-git.orig/qemu-options.hx
+++ qemu-git/qemu-options.hx
@@ -1765,6 +1765,8 @@  DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \
     "-tpm builtin,path=<path>[,model=<model>][,key=<aes key>]\n" \
     "                enable a builtin TPM with state in file in path\n" \
     "                and encrypt the TPM's state with the given AES key\n" \
+    "-tpm null       enable a TPM null driver that responds with a fault\n" \
+    "                message to every TPM request\n" \
     "-tpm model=?    to list available TPM device models\n" \
     "-tpm ?          to list available TPM backend types\n",
     QEMU_ARCH_I386)
@@ -1780,8 +1782,9 @@  The general form of a TPM device option 
 
 @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
 @findex -tpmdev
-Backend type must be:
-@option{builtin}.
+Backend type must be one of:
+@option{builtin},
+@option{null}.
 
 The specific backend type will determine the applicable options.
 The @code{-tpmdev} options requires a @code{-device} option.
@@ -1822,6 +1825,12 @@  using AES-CBC encryption scheme supply t
 @example
 -tpmdev builtin,id=tpm0,path=<path_to_qcow2>,key=aes-cbc:0x1234567890abcdef01234567890abcdef -device tpm-tis,tpmdev=tpm0
 @end example
+
+@item -tpmdev null
+
+Creates an instance of a TPM null driver that responds to every command
+with a fault message.
+
 @end table
 
 The short form of a TPM device option is:
Index: qemu-git/configure
===================================================================
--- qemu-git.orig/configure
+++ qemu-git/configure
@@ -2608,8 +2608,6 @@  EOF
   libtpms=no
   if compile_prog "" "-ltpms" ; then
     libtpms=yes
-  else
-    tpm_need_pkgs="libtpms development package"
   fi
 fi
 
@@ -3612,12 +3610,6 @@  if test "$tpm" = "yes"; then
   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