All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefan Berger <stefanb@linux.vnet.ibm.com>
To: stefanb@linux.vnet.ibm.com, qemu-devel@nongnu.org
Cc: andreas.niederl@iaik.tugraz.at, serge@hallyn.com
Subject: [Qemu-devel] [PATCH V4 06/10] Add a TPM backend skeleton implementation
Date: Fri, 06 May 2011 13:32:30 -0400	[thread overview]
Message-ID: <20110506173246.617414609@linux.vnet.ibm.com> (raw)
In-Reply-To: 20110506173224.278066589@linux.vnet.ibm.com

[-- 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);
 

  parent reply	other threads:[~2011-05-06 17:33 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 ` Stefan Berger [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20110506173246.617414609@linux.vnet.ibm.com \
    --to=stefanb@linux.vnet.ibm.com \
    --cc=andreas.niederl@iaik.tugraz.at \
    --cc=qemu-devel@nongnu.org \
    --cc=serge@hallyn.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.