Patchwork [V8,07/14] Implementation of the libtpms-based backend

login
register
mail settings
Submitter Stefan Berger
Date Aug. 31, 2011, 2:35 p.m.
Message ID <20110831143621.194691516@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/112559/
State New
Headers show

Comments

Stefan Berger - Aug. 31, 2011, 2:35 p.m.
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 connects 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 is registered with
libtpms. Those callback functions are invoked by libtpms for example to
store the TPM's state.

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.

v6:
  - cache a copy of the last permanent state blob
  - move some functions into tpm_builtin.h
  - reworked parts of the error path handling where the TPM is
    now used to process commands under error conditions and the callbacks
    make the TPM aware of the error conditions. Only as the last resort
    fault messages are sent by the backend driver circumventing the TPM.
  - add out_len variable used in the thread

v5:
  - check access() to TPM's state file and report error if file is not
    accessible

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 |  450 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 hw/tpm_builtin.h |   56 ++++++
 3 files changed, 482 insertions(+), 25 deletions(-)
Michael S. Tsirkin - Sept. 1, 2011, 5:27 p.m.
On Wed, Aug 31, 2011 at 10:35:58AM -0400, Stefan Berger wrote:
> 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 connects 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 is registered with
> libtpms. Those callback functions are invoked by libtpms for example to
> store the TPM's state.
> 
> 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.
> 
> v6:
>   - cache a copy of the last permanent state blob
>   - move some functions into tpm_builtin.h
>   - reworked parts of the error path handling where the TPM is
>     now used to process commands under error conditions and the callbacks
>     make the TPM aware of the error conditions. Only as the last resort
>     fault messages are sent by the backend driver circumventing the TPM.
>   - add out_len variable used in the thread
> 
> v5:
>   - check access() to TPM's state file and report error if file is not
>     accessible
> 
> 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 |  450 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
>  hw/tpm_builtin.h |   56 ++++++
>  3 files changed, 482 insertions(+), 25 deletions(-)
> 
> 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

Just wondering - might a stub driver be useful for
basic testing on systems without TPM hardware?

The namespace comment applies to this and all other patches.
Stefan Berger - Sept. 2, 2011, 1:24 a.m.
On 09/01/2011 01:27 PM, Michael S. Tsirkin wrote:
> On Wed, Aug 31, 2011 at 10:35:58AM -0400, Stefan Berger wrote:
>> 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 connects 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 is registered with
>> libtpms. Those callback functions are invoked by libtpms for example to
>> store the TPM's state.
>>
>> 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.
>>
>> v6:
>>    - cache a copy of the last permanent state blob
>>    - move some functions into tpm_builtin.h
>>    - reworked parts of the error path handling where the TPM is
>>      now used to process commands under error conditions and the callbacks
>>      make the TPM aware of the error conditions. Only as the last resort
>>      fault messages are sent by the backend driver circumventing the TPM.
>>    - add out_len variable used in the thread
>>
>> v5:
>>    - check access() to TPM's state file and report error if file is not
>>      accessible
>>
>> 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 |  450 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
>>   hw/tpm_builtin.h |   56 ++++++
>>   3 files changed, 482 insertions(+), 25 deletions(-)
>>
>> 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
> Just wondering - might a stub driver be useful for
> basic testing on systems without TPM hardware?
'systems without TPM hardware' -- we're not relying on underlying TPM 
provided by the host. But I assume that's not what you meant.

A 'null' driver, which responds to every command with an error response, 
is added in patch 13.

> The namespace comment applies to this and all other patches.
>
In patch 6 I am adding a skeleton backend driver that I am transforming 
into the libtpms-based backend in patch 7. I didn't name the file 
tpm_skeleton.c but already tpm_builtin.c and all functions already start 
with the prefix tpm_builtin. This presumably makes it easier to review 
since the 'meat' is added in part 7 and unnecessary function name 
changes are avoided. The null driver later on and the passthrough driver 
(posted by Andreas Niederl) all can be derived rather easily from that 
initial skeleton driver. In those we do adhere to the namespace requirement.

   Stefan
Michael S. Tsirkin - Sept. 4, 2011, 4:27 p.m.
On Thu, Sep 01, 2011 at 09:24:26PM -0400, Stefan Berger wrote:
> In patch 6 I am adding a skeleton backend driver that I am
> transforming into the libtpms-based backend in patch 7. I didn't
> name the file tpm_skeleton.c but already tpm_builtin.c and all
> functions already start with the prefix tpm_builtin. This presumably
> makes it easier to review since the 'meat' is added in part 7 and
> unnecessary function name changes are avoided.

It is a good idea to split your code to patches.
But you don't need your code to actually work
in an intermediate step - or even compile
if it is not added to the Makefile.

That will help reviewers by sending small patches
and not waste reviewer's time on reading code
removed in a later patch.

Patch

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 = g_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;
+        g_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 */