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,17 +18,36 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>
  */
 
+#include "tpm_builtin.h"
+#include "blockdev.h"
+#include "block_int.h"
 #include "qemu-common.h"
-#include "tpm.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'
+
+#define VTPM_DRIVE  "drive-vtpm0-nvram"
+#define TPM_OPTS "id=" VTPM_DRIVE
+
 /* data structures */
 
 typedef struct ThreadParams {
@@ -44,12 +63,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;
+static TPMSizedBuffer volatile_state;
+static TPMSizedBuffer save_state;
+static int pipefd[2] = {-1, -1};
 
 static bool thread_terminate;
 static bool tpm_initialized;
 static bool had_fatal_error;
 static bool had_startup_error;
 static bool thread_running;
+static bool need_read_volatile;
 
 static ThreadParams tpm_thread_params;
 
@@ -63,11 +88,23 @@ 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;
+    uint32_t in_len, out_len;
     uint8_t *in, *out;
     uint32_t resp_size; /* total length of response */
 
@@ -75,9 +112,11 @@ 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
     } else {
@@ -94,6 +133,8 @@ static void *tpm_builtin_main_loop(void 
         in_len = 0;
         do {
 #ifdef DEBUG_TPM
+            /* TPMLIB output goes to stdout */
+            fflush(stdout);
             fprintf(stderr, "tpm: waiting for commands...\n");
 #endif
 
@@ -125,6 +166,8 @@ static void *tpm_builtin_main_loop(void 
 
             if (tpm_initialized) {
 
+                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",
@@ -135,17 +178,26 @@ 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);
+
+#ifdef DEBUG_TPM
+                fflush(stdout);
+#endif
 
-                if (res != 0) {
+                if (res != TPM_SUCCESS) {
 #ifdef DEBUG_TPM
-                    fprintf(stderr,
-                            "tpm: Sending/receiving TPM request/response "
-                            "failed.\n");
+                    fprintf(stderr, "tpm: TPMLIB_Process() failed\n");
 #endif
-                    had_fatal_error = true;
+                    /* we didn't get a response from the TPM; so
+                       construct one ourselves */
+                    goto send_err_response;
                 }
             } else {
+send_err_response:
                 out = thr_parms->tpm_state->loc[g_locty].r_buffer.buffer;
 
                 resp_size = sizeof(tpm_std_fatal_error_response);
@@ -168,11 +220,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
 
@@ -203,7 +257,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);
         }
 
@@ -215,6 +269,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;
 }
 
 
@@ -238,9 +298,23 @@ 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)
 {
-    return tpm_builtin_startup_tpm();
+    int rc;
+
+    rc = tpm_builtin_startup_bs(bs, fail_on_encrypted_drive);
+    if (rc) {
+        had_startup_error = true;
+        return rc;
+    }
+
+    tpm_builtin_load_tpm_state_from_bs(bs);
+
+    rc = tpm_builtin_startup_tpm();
+    if (rc) {
+        had_startup_error = true;
+    }
+    return rc;
 }
 
 
@@ -250,7 +324,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);
 }
 
 
@@ -266,7 +340,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_startup_error = true;
     }
 
@@ -274,6 +348,222 @@ 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 (had_fatal_error) {
+        return TPM_FAIL;
+    }
+
+#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 a copy of the last permanent state */
+            rc = TPM_Malloc(data, *length);
+            if (rc == 0) {
+                memcpy(*data, permanent_state.buffer, *length);
+            }
+        }
+    } 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 %5d 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 (had_fatal_error) {
+        return TPM_FAIL;
+    }
+
+    if (!strcmp(name, TPM_PERMANENT_ALL_NAME)) {
+        tsb = &permanent_state;
+        /* keep a copy of the last permanent state */
+        if (copy_sized_buffer(tsb, (unsigned char *)data, length) !=
+            TPM_SUCCESS) {
+                had_fatal_error = TRUE;
+                goto err_exit;
+        }
+
+#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;
+
+        set_sized_buffer(tsb, (unsigned char *)data, length);
+
+#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);
+
+        if (tpm_builtin_request_sync_to_bs(what)) {
+            rc = TPM_FAIL;
+        }
+
+        if (what == SAVESTATE_TYPE) {
+            /* TPM library will free */
+            tsb->buffer = NULL;
+            tsb->size = 0;
+        }
+
+        qemu_mutex_unlock(&state_mutex);
+    }
+
+err_exit:
+    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;
+
+    if (had_fatal_error) {
+        return TPM_FAIL;
+    }
+
+    /* only handle the savestate here */
+    if (!strcmp(name, TPM_SAVESTATE_NAME)) {
+        qemu_mutex_lock(&state_mutex);
+
+        clear_sized_buffer(&save_state);
+
+        if (tpm_builtin_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 (tpm_builtin_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
@@ -282,7 +572,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;
 }
 
@@ -313,27 +607,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_DRIVE);
+    if (bs == NULL) {
+        fprintf(stderr, "The " VTPM_DRIVE " driver was not found.\n");
+        goto err_exit;
+    }
+
+    if (TPMLIB_RegisterCallbacks(&callbacks) != TPM_SUCCESS) {
+        goto err_exit;
+    }
+
+    if (tpm_builtin_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;
 }
 
 
@@ -349,6 +711,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.
@@ -356,17 +722,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 (tpm_builtin_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;
@@ -382,7 +777,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;
     }
 
@@ -409,7 +805,13 @@ static TPMBackend *tpm_builtin_create(Qe
 
     value = qemu_opt_get(opts, "path");
     if (value) {
-        /* !!! handle file path */
+        if (access(value, R_OK|W_OK)) {
+            fprintf(stderr,
+                    "Cannot access file '%s' from tpm's path option.\n",
+                    value);
+            goto err_exit;
+        }
+        drive_add(IF_NONE, -1, value, TPM_OPTS);
     } else {
         fprintf(stderr, "-tpm is missing path= parameter\n");
         goto err_exit;
@@ -436,7 +838,7 @@ static void tpm_builtin_destroy(TPMBacke
 TPMDriverOps 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,
     .create                   = tpm_builtin_create,
     .destroy                  = tpm_builtin_destroy,
     .init                     = tpm_builtin_init,
Index: qemu-git/configure
===================================================================
--- qemu-git.orig/configure
+++ qemu-git/configure
@@ -3571,7 +3571,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
Index: qemu-git/hw/tpm_builtin.h
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_builtin.h
@@ -0,0 +1,56 @@
+/*
+ * tpm_builtin.h - include file for tpm_builtin.h
+ *
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Author: Stefan Berger <stefanb@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_BUILTIN_H
+#define _HW_TPM_BUILTIN_H
+
+#include "tpm.h"
+#include "tpm_tis.h"
+
+#include <libtpms/tpm_types.h>
+#include <libtpms/tpm_memory.h>
+#include <libtpms/tpm_error.h>
+
+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;
+}
+
+static inline TPM_RESULT copy_sized_buffer(TPMSizedBuffer *tpmsb,
+                                           uint8_t *buffer, uint32_t size)
+{
+    TPM_RESULT rc;
+
+    rc = TPM_Realloc(&tpmsb->buffer, size);
+
+    if (rc == TPM_SUCCESS) {
+        tpmsb->size = size;
+        memcpy(tpmsb->buffer, buffer, size);
+    }
+
+    return rc;
+}
+
+
+#endif /* _HW_TPM_BUILTIN_H */
