Index: qemu-git/hw/tpm_builtin.c
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_builtin.c
@@ -0,0 +1,1253 @@
+/*
+ *  built-in TPM support using libtpms
+ *
+ *  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 <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 "blockdev.h"
+#include "block_int.h"
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "hw/tpm_tis.h"
+#include "hw/pc.h"
+
+/* following define will be removed once SeaBIOS has TPM support */
+
+//#define DEBUG_TPM
+//#define DEBUG_TPM_SR /* suspend - resume */
+
+
+#define SAVESTATE_TYPE 'S'
+#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 {
+    TPMState *tpm_state;
+
+    TPMRecvDataCB *recv_data_callback;
+} ThreadParams;
+
+
+enum BSEntryType {
+    BS_ENTRY_PERMSTATE,
+    BS_ENTRY_SAVESTATE,
+    BS_ENTRY_VOLASTATE,
+};
+
+
+typedef struct BSEntry {
+    enum BSEntryType type;
+    int64_t  offset;
+    uint32_t space;
+    uint32_t blobsize;
+} __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;
+    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;
+
+static QemuMutex state_mutex; /* protects *_state below */
+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 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;
+
+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 int tpmlib_get_prop(enum TPMLIB_TPMProperty prop)
+{
+    int result;
+
+    TPM_RESULT res = TPMLIB_GetTPMProperty(prop, &result);
+
+    assert(res == TPM_SUCCESS);
+
+    return result;
+}
+
+
+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 find_bs_entry_idx(BSDir *dir, enum BSEntryType type)
+{
+    int c;
+
+    for (c = 0; c < dir->num_entries; c++)
+        if (dir->entries[c].type == type)
+            return c;
+
+    return -ENOENT;
+}
+
+
+static unsigned int sizeof_bsdir(BSDir *dir)
+{
+    return offsetof(BSDir, entries) +
+           dir->num_entries * sizeof(BSEntry);
+}
+
+
+static uint32_t calc_dir_checksum(BSDir *dir)
+{
+    uint16_t checksum, orig;
+
+    orig = dir->checksum;
+    dir->checksum = 0;
+
+    checksum = memsum((unsigned char *)dir, sizeof_bsdir(dir));
+
+    dir->checksum = orig;
+
+    return checksum;
+}
+
+
+static bool 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 == calc_dir_checksum(dir));
+}
+
+
+static int 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 = calc_dir_checksum(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 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;
+}
+
+
+/*
+ * 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)
+{
+    uint8_t buf[BDRV_SECTOR_SIZE];
+    BSDir *dir;
+
+    if (bdrv_read(bs, 0, buf, 1) < 0)
+        return -EIO;
+
+    dir = (BSDir *)buf;
+
+    if (!is_valid_bsdir(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 (memsum(buf, sizeof(buf)) != 0) {
+                fprintf(stderr,
+                        "vTPM block storage directory is not valid. "
+                        "Assuming the key is wrong.\n");
+                had_startup_error = true;
+                return 1;
+            }
+        }
+#ifdef DEBUG_TPM
+        fprintf(stderr, "*** tpm: Blanking the storage directory.\n");
+#endif
+        return create_blank_dir(bs);
+    }
+
+    return 0;
+}
+
+
+static int 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++;
+    uint32_t space;
+
+    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;
+
+    switch (type) {
+    case BS_ENTRY_PERMSTATE:
+        space = PERMSTATE_DISK_SPACE;
+        break;
+    case BS_ENTRY_SAVESTATE:
+        space = SAVESTATE_DISK_SPACE;
+        break;
+    case BS_ENTRY_VOLASTATE:
+        space = VOLASTATE_DISK_SPACE;
+        break;
+    default:
+        assert(false);
+    }
+
+    dir->entries[idx].space = space;
+    dir->entries[idx].blobsize = blobsize;
+
+    dir->checksum = calc_dir_checksum(dir);
+
+    assert(space >= blobsize);
+
+    memset(buf, 0x0, sizeof(buf));
+    memcpy(buf, dir, sizeof_bsdir(dir));
+
+    if (bdrv_write(bs, 0, buf, 1) < 0)
+        return -EIO;
+
+    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;
+
+    assert(is_valid_bsdir(dir));
+
+    if ((idx = find_bs_entry_idx(dir, type)) < 0) {
+        if ((idx = 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(BlockDriverState *bs,
+                             enum BSEntryType type,
+                             BSEntry *entry,
+                             uint32_t blobsize)
+{
+    uint8_t buf[BDRV_SECTOR_SIZE];
+    BSDir *dir;
+    int idx;
+
+    if (bdrv_read(bs, 0, buf, 1) < 0)
+        return -EIO;
+
+    dir = (BSDir *)buf;
+
+    assert(is_valid_bsdir(dir));
+
+    if ((idx = find_bs_entry_idx(dir, type)) < 0) {
+        if ((idx = create_bs_entry(bs, dir, type, 0)) < 0)
+            return -EIO;
+    }
+
+    assert(blobsize <= dir->entries[idx].space);
+    dir->entries[idx].blobsize = blobsize;
+
+    dir->checksum = calc_dir_checksum(dir);
+
+    if (bdrv_write(bs, 0, buf, 1) < 0)
+        return -EIO;
+
+    memcpy(entry, &dir->entries[idx], sizeof(*entry));
+
+    return 0;
+}
+
+
+static int load_sized_data_from_bs(BlockDriverState *bs,
+                                   enum BSEntryType be,
+                                   TPMSizedBuffer *tsb)
+{
+    BSEntry entry;
+    int n;
+
+    if ((n = get_bs_entry(bs, be, &entry)) < 0)
+        return n;
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    fprintf(stderr,"load: be-type: %d, offset: %ld, size: %d\n",
+            be, entry.offset, entry.blobsize);
+#endif
+
+    if (entry.blobsize == 0)
+        return 0;
+
+    tsb->buffer = qemu_malloc(entry.blobsize);
+    if (!tsb->buffer)
+        return -ENOMEM;
+
+    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");
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+static int load_tpm_permanent_state_from_bs(BlockDriverState *bs,
+                                            TPMSizedBuffer *tsb)
+{
+    return load_sized_data_from_bs(bs, BS_ENTRY_PERMSTATE, tsb);
+}
+
+
+static int load_tpm_savestate_from_bs(BlockDriverState *bs,
+                                      TPMSizedBuffer *tsb)
+{
+    return load_sized_data_from_bs(bs, BS_ENTRY_SAVESTATE, tsb);
+}
+
+
+static int load_tpm_volatile_state_from_bs(BlockDriverState *bs,
+                                           TPMSizedBuffer *tsb)
+{
+    return load_sized_data_from_bs(bs, BS_ENTRY_VOLASTATE, tsb);
+}
+
+
+static int save_sized_data_to_bs(BlockDriverState *bs,
+                                 enum BSEntryType be,
+                                 uint8_t *data, uint32_t data_len)
+{
+    BSEntry entry;
+    int n;
+
+    if ((n = set_bs_entry_size(bs, be, &entry, data_len)) < 0)
+        return n;
+
+    if (data_len > 0) {
+        if (bdrv_pwrite(bs, entry.offset, data, data_len) != data_len)
+            return -EIO;
+    }
+
+    return 0;
+}
+
+
+/* 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 = 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 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 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 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;
+    }
+
+    if (tpm_initialized) {
+        qemu_cond_wait(&bs_write_result_cond, &state_mutex);
+    } else {
+        /* during initialization: defer the write */
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+        fprintf(stderr,"tpm: deferred write/sync since not in thread\n");
+#endif
+    }
+
+    return had_fatal_error;
+}
+
+
+static void load_tpm_state_from_bs(BlockDriverState *bs)
+{
+    load_tpm_permanent_state_from_bs(bs, &permanent_state);
+    load_tpm_savestate_from_bs(bs, &save_state);
+
+    if (need_read_volatile) {
+        clear_sized_buffer(&volatile_state);
+        load_tpm_volatile_state_from_bs(bs, &volatile_state);
+        need_read_volatile = false;
+    }
+}
+
+
+/**
+ * Start the TPM. If it had been started before, then terminate and start
+ * it again.
+ */
+static int startup_tpm(void)
+{
+    TPM_RESULT res;
+
+    if (tpm_initialized) {
+        TPMLIB_Terminate();
+        tpm_initialized = false;
+    }
+
+    res = TPMLIB_MainInit();
+    if (res != TPM_SUCCESS) {
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+        fprintf(stderr,"tpm: Error: Call to TPMLIB_MainInit() failed (rc=%d)\n",
+                       res);
+#endif
+        return 1;
+    }
+
+    tpm_initialized = true;
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    fprintf(stderr,"tpm: *** tpm startup was successful! ***\n");
+#endif
+
+    return 0;
+}
+
+
+/*
+ * 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 late_startup_tpm(void)
+{
+    int rc;
+
+    if (startup_bs(bs)) {
+        had_fatal_error = 1;
+        return 1;
+    }
+
+    load_tpm_state_from_bs(bs);
+
+    rc  = startup_tpm();
+    if (rc) {
+        had_fatal_error = 1;
+        return 1;
+    }
+
+    return 0;
+}
+
+
+static void terminate_tpm_thread(void)
+{
+    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);
+
+        qemu_thread_join(&thread, NULL);
+        memset(&thread, 0, sizeof(thread));
+
+        if (tpm_initialized) {
+            TPMLIB_Terminate();
+            tpm_initialized = false;
+        }
+    }
+}
+
+
+static void tpm_atexit(void)
+{
+    terminate_tpm_thread();
+
+    close(pipefd[0]);
+    pipefd[0] = -1;
+
+    close(pipefd[1]);
+    pipefd[1] = -1;
+}
+
+
+static void *mainLoop(void *d)
+{
+    ThreadParams *tParams = (ThreadParams *)d;
+    uint32_t in_len, out_len;
+    uint8_t *in, *out;
+    TPM_RESULT res;
+    uint32_t resp_size; /* total length of response */
+
+    /* start command processing */
+    while (!thread_terminate) {
+        /* receive and handle commands */
+        in_len = 0;
+        do {
+#ifdef DEBUG_TPM
+            fprintf(stderr,"waiting for commands...\n");
+#endif
+
+            if (thread_terminate)
+                break;
+
+            qemu_mutex_lock(&tParams->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 (!tParams->tpm_state->to_tpm_execute)
+                qemu_cond_wait(&tParams->tpm_state->to_tpm_cond,
+                               &tParams->tpm_state->state_lock);
+
+            tParams->tpm_state->to_tpm_execute = false;
+
+            qemu_mutex_unlock(&tParams->tpm_state->state_lock);
+
+            if (thread_terminate)
+                break;
+
+            g_locty = tParams->tpm_state->command_locty;
+
+            in = tParams->tpm_state->loc[g_locty].w_buffer.buffer;
+            in_len = tParams->tpm_state->loc[g_locty].w_offset;
+
+            if (!had_fatal_error) {
+
+                out_len = tParams->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(stdout, in, in_len);
+#endif
+
+                resp_size = 0;
+
+                /* TPMLIB_Process may realloc the response buffer */
+                res = TPMLIB_Process(
+                    &tParams->tpm_state->loc[g_locty].r_buffer.buffer,
+                    &resp_size, &out_len,
+                    in, in_len);
+
+                /* TPMLIB_Process must not grow the buffer */
+                assert(out_len ==
+                       tParams->tpm_state->loc[g_locty].r_buffer.size);
+
+                if (res != TPM_SUCCESS) {
+#ifdef DEBUG_TPM
+                    fprintf(stderr,"TPMLIB_Process() failed\n");
+#endif
+                    had_fatal_error = 1;
+                }
+            }
+
+            if (had_fatal_error) {
+                out = tParams->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,"sending %d bytes to VM\n", resp_size);
+            dumpBuffer(stdout, out, resp_size);
+#endif
+            tParams->recv_data_callback(tParams->tpm_state, g_locty);
+        } while (in_len > 0);
+    }
+
+    return NULL;
+}
+
+
+/*****************************************************************
+ * call back functions for the libtpms TPM library
+ ****************************************************************/
+static TPM_RESULT tpm_nvram_init(void)
+{
+    return TPM_SUCCESS;
+}
+
+
+static TPM_RESULT tpm_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 [sum=%08x]; rc = %d\n",
+            *length, memsum(*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_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 (!strcmp(name, TPM_PERMANENT_ALL_NAME)) {
+        tsb = &permanent_state;
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+        fprintf(stderr,"tpm: STORING %d BYTES OF PERMANENT ALL\n",
+                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 %d BYTES OF SAVESTATE\n",
+                length);
+#endif
+        what = SAVESTATE_TYPE;
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    } else if (!strcmp(name, TPM_VOLATILESTATE_NAME)) {
+        fprintf(stderr,"tpm: STORING %d BYTES OF VOLATILE STATE\n",
+                length);
+#endif
+    }
+
+    if (tsb) {
+        /* we may get called here during TPMLIB_MainInit() rather than
+           while running as a thread. Skip writing the state until we
+           process the first command. */
+        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_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_io_init(void)
+{
+    return TPM_SUCCESS;
+}
+
+
+static TPM_RESULT tpm_io_getlocality(TPM_MODIFIER_INDICATOR *localityModifier)
+{
+    *localityModifier = (TPM_MODIFIER_INDICATOR)g_locty;
+
+    return TPM_SUCCESS;
+}
+
+
+static TPM_RESULT tpm_io_getphysicalpresence(TPM_BOOL *physicalPresence)
+{
+    *physicalPresence = FALSE;
+
+    return TPM_SUCCESS;
+}
+
+
+/*****************************************************************/
+
+
+static void reset(void)
+{
+    static bool thread_running;
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+    fprintf(stderr,"tpm: CALL TO TPM_RESET!\n");
+#endif
+
+    if (thread_running) {
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+        fprintf(stderr,"tpm: TERMINATING RUNNING TPM THREAD\n");
+#endif
+        terminate_tpm_thread();
+    }
+
+    clear_sized_buffer(&permanent_state);
+    clear_sized_buffer(&save_state);
+    clear_sized_buffer(&volatile_state);
+    had_fatal_error = false;
+    thread_terminate = false;
+    need_read_volatile = false;
+    had_startup_error = false;
+
+    qemu_thread_create(&thread, mainLoop, &tpm_thread_params);
+    thread_running = true;
+}
+
+
+/*
+ * 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 tpm_initialized variable is 'true'.
+ *
+ */
+static int instantiate_with_volatile_data(TPMState *s)
+{
+    if (tpm_initialized) {
+#ifdef DEBUG_TPM_SR
+        fprintf(stderr,"tpm: This is resume of a SNAPSHOT?!\n");
+#endif
+        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_nvram_init,
+    .tpm_nvram_loaddata         = tpm_nvram_loaddata,
+    .tpm_nvram_storedata        = tpm_nvram_storedata,
+    .tpm_nvram_deletename       = tpm_nvram_deletename,
+    .tpm_io_init                = tpm_io_init,
+    .tpm_io_getlocality         = tpm_io_getlocality,
+    .tpm_io_getphysicalpresence = tpm_io_getphysicalpresence,
+};
+
+
+static int 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_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], fulfill_sync_to_bs_request, NULL, NULL);
+
+    atexit(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 get_tpm_established_flag(void)
+{
+    TPM_BOOL tpmEstablished = false;
+
+    if (tpm_initialized)
+        TPM_IO_TpmEstablished_Get(&tpmEstablished);
+
+    return (bool)tpmEstablished;
+}
+
+
+static bool 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. Since we
+ * store the volatile state into the block storage device we leave
+ * the provided buffer untouched.
+ */
+static int 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.
+         */
+        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);
+#ifdef DEBUG_TPM_SR
+    fprintf(stderr,"tpm: got %d bytes of volatilestate\n", buflen);
+#endif
+
+    if (res != TPM_SUCCESS) {
+#ifdef DEBUG_TPM_SR
+        fprintf(stderr,"tpm: Error: Could not store TPM state\n");
+#endif
+        return 1;
+    }
+
+    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 */
+    fulfill_sync_to_bs_request(NULL);
+
+    return 0;
+}
+
+
+static size_t realloc_buffer(TPMSizedBuffer *sb)
+{
+    TPM_RESULT res;
+    size_t wanted_size = tpmlib_get_prop(TPMPROP_TPM_BUFFER_MAX);
+
+    if (sb->size != wanted_size) {
+        res = TPM_Realloc(&sb->buffer, wanted_size);
+        if (res == TPM_SUCCESS)
+            sb->size = wanted_size;
+        else
+            sb->size = 0;
+    }
+    return sb->size;
+}
+
+
+static const char *create_desc(void)
+{
+    static int done;
+
+    if (!done) {
+        snprintf(dev_description, sizeof(dev_description),
+                 "Qemu's built-in TPM; requires %ukb of block storage",
+                 MINIMUM_BS_SIZE_KB);
+        done = 1;
+    }
+
+    return dev_description;
+}
+
+
+#define TPM_OPTS "id=vtpm-nvram"
+
+static bool handle_options(QemuOpts *opts)
+{
+    const char *value;
+
+    value = qemu_opt_get(opts, "path");
+    if (value) {
+        drive_add(IF_NONE, -1, value, TPM_OPTS);
+    } else {
+        fprintf(stderr,"-tpm is missing path= parameter\n");
+        return false;
+    }
+    return true;
+}
+
+
+struct backend_tpm_driver builtin = {
+    .id                             = "builtin",
+    .desc                           = create_desc,
+    .handle_options                 = handle_options,
+    .init                           = init,
+    .late_startup_tpm               = late_startup_tpm,
+    .realloc_buffer                 = realloc_buffer,
+    .reset                          = reset,
+    .had_startup_error              = get_startup_error,
+    .save_volatile_data             = save_volatile_data,
+    .load_volatile_data             = instantiate_with_volatile_data,
+    .get_tpm_established_flag       = get_tpm_established_flag,
+};
Index: qemu-git/Makefile.target
===================================================================
--- qemu-git.orig/Makefile.target
+++ qemu-git/Makefile.target
@@ -294,6 +294,11 @@ obj-sparc-y += grlib_gptimer.o grlib_irq
 ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),i386 x86_64))
 
 obj-i386-$(CONFIG_TPM) += tpm_tis.o qemu-thread.o
+obj-i386-$(CONFIG_TPM_BUILTIN) += tpm_builtin.o
+
+ifdef CONFIG_TPM_BUILTIN
+LIBS+=-ltpms
+endif
 
 endif
 
Index: qemu-git/configure
===================================================================
--- qemu-git.orig/configure
+++ qemu-git/configure
@@ -3319,6 +3319,9 @@ if test "$tpm" = "yes"; then
   fi
 
   if test "$has_tpm" = "1"; then
+      if test -r /usr/include/libtpms/tpm_library.h ; then
+          echo "CONFIG_TPM_BUILTIN=y" >> $config_target_mak
+      fi
       echo "CONFIG_TPM=y" >> $config_host_mak
   fi
 fi
Index: qemu-git/hw/tpm_tis.c
===================================================================
--- qemu-git.orig/hw/tpm_tis.c
+++ qemu-git/hw/tpm_tis.c
@@ -94,6 +94,9 @@ static uint32_t tis_mem_readl(void *opaq
 
 
 static const struct backend_tpm_driver *bes[] = {
+#ifdef CONFIG_TPM_BUILTIN
+    &builtin,
+#endif
     NULL,
 };
 
