Index: qemu-git/hw/tpm_tis.c
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_tis.c
@@ -0,0 +1,1011 @@
+/*
+ * tpm_tis.c - QEMU emulator for a 1.2 TPM with TIS interface
+ *
+ * Copyright (C) 2006,2010 IBM Corporation
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ *         David Safford <safford@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.
+ *
+ *
+ * Implementation of the TIS interface according to specs at
+ * https://www.trustedcomputinggroup.org/groups/pc_client/TCG_PCClientTPMSpecification_1-20_1-00_FINAL.pdf
+ *
+ */
+
+#include <stdio.h>
+
+#include "block.h"
+#include "hw/hw.h"
+#include "hw/pc.h"
+#include "hw/tpm_tis.h"
+
+//#define DEBUG_TIS
+//#define DEBUG_TIS_SR
+
+/* whether the STS interrupt is supported */
+//#define RAISE_STS_IRQ
+
+/* tis registers */
+#define TIS_REG_ACCESS                0x00
+#define TIS_REG_INT_ENABLE            0x08
+#define TIS_REG_INT_VECTOR            0x0c
+#define TIS_REG_INT_STATUS            0x10
+#define TIS_REG_INTF_CAPABILITY       0x14
+#define TIS_REG_STS                   0x18
+#define TIS_REG_DATA_FIFO             0x24
+#define TIS_REG_DID_VID               0xf00
+#define TIS_REG_RID                   0xf04
+
+/* vendor-specific registers */
+#define TIS_REG_DEBUG                 0xf90
+
+#define STS_VALID                    (1 << 7)
+#define STS_COMMAND_READY            (1 << 6)
+#define STS_TPM_GO                   (1 << 5)
+#define STS_DATA_AVAILABLE           (1 << 4)
+#define STS_EXPECT                   (1 << 3)
+#define STS_RESPONSE_RETRY           (1 << 1)
+
+#define ACCESS_TPM_REG_VALID_STS     (1 << 7)
+#define ACCESS_ACTIVE_LOCALITY       (1 << 5)
+#define ACCESS_BEEN_SEIZED           (1 << 4)
+#define ACCESS_SEIZE                 (1 << 3)
+#define ACCESS_PENDING_REQUEST       (1 << 2)
+#define ACCESS_REQUEST_USE           (1 << 1)
+#define ACCESS_TPM_ESTABLISHMENT     (1 << 0)
+
+#define INT_ENABLED                  (1 << 31)
+#define INT_DATA_AVAILABLE           (1 << 0)
+#define INT_STS_VALID                (1 << 1)
+#define INT_LOCALITY_CHANGED         (1 << 2)
+#define INT_COMMAND_READY            (1 << 7)
+
+#ifndef RAISE_STS_IRQ
+
+# define INTERRUPTS_SUPPORTED         (INT_LOCALITY_CHANGED | \
+                                       INT_DATA_AVAILABLE   | \
+                                       INT_COMMAND_READY)
+
+#else
+
+# define INTERRUPTS_SUPPORTED         (INT_LOCALITY_CHANGED | \
+                                       INT_DATA_AVAILABLE   | \
+                                       INT_STS_VALID | \
+                                       INT_COMMAND_READY)
+
+#endif
+
+#define CAPABILITIES_SUPPORTED       ((1 << 4) |            \
+                                      INTERRUPTS_SUPPORTED)
+
+#define TPM_DID          0x0001
+#define TPM_VID          0x0001
+#define TPM_RID          0x0001
+
+#define TPM_NO_DATA_BYTE 0xff
+
+/* prototypes */
+static uint32_t tis_mem_readl(void *opaque, target_phys_addr_t addr);
+
+
+static const struct backend_tpm_driver *bes[] = {
+    NULL,
+};
+
+/* active backend driver */
+static const struct backend_tpm_driver *active_be;
+
+const struct backend_tpm_driver *tis_get_active_backend(void)
+{
+    return active_be;
+}
+
+const struct backend_tpm_driver *tis_set_backend_driver(const char *id)
+{
+    int i;
+
+    for (i = 0; bes[i] != NULL; i++)
+        if (!strcmp(bes[i]->id, id))
+            break;
+
+    active_be = bes[i];
+
+    return active_be;
+}
+
+void tis_display_backend_drivers(FILE *out)
+{
+    int i;
+
+    fprintf(out, "Supported TPM types (choose only one):\n");
+
+    for (i = 0; bes[i] != NULL; i++) {
+        fprintf(out, "%7s   %s",
+                bes[i]->id, bes[i]->desc());
+        fprintf(out, "\n");
+    }
+    fprintf(out, "\n");
+}
+
+
+#ifdef DEBUG_TIS
+static void showBuff(const TPMSizedBuffer *sb, const char *string)
+{
+    uint16_t len;
+
+    len = tpm_get_size_from_buffer(sb);
+    fprintf(stderr,"tpm_tis: %s length = %d\n", string, len);
+    dumpBuffer(stderr, sb->buffer, len);
+}
+#endif
+
+
+static inline uint8_t locality_from_addr(target_phys_addr_t addr)
+{
+    return (uint8_t)((addr >> 12) & 0x7);
+}
+
+
+/*
+ * Send a TPM request.
+ * Call this with the state_lock held so we can sync with the receive
+ * callback.
+ */
+static void tis_tpm_send(TPMState *s, uint8_t locty)
+{
+#ifdef DEBUG_TIS
+    showBuff(&s->loc[locty].w_buffer, "tpm_tis: To TPM");
+#endif
+    s->command_locty = locty;
+
+    /* w_offset serves as length indicator for length of data;
+       it's reset when the response comes back */
+    s->loc[locty].state = STATE_EXECUTION;
+    s->loc[locty].sts &= ~STS_EXPECT;
+
+    s->to_tpm_execute = true;
+    qemu_cond_signal(&s->to_tpm_cond);
+}
+
+
+/* raise an interrupt if allowed */
+static void tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
+{
+    if (!IS_VALID_LOCTY(locty))
+        return;
+
+    if ((s->loc[locty].inte & INT_ENABLED) &&
+        (s->loc[locty].inte & irqmask)) {
+#ifdef DEBUG_TIS
+        fprintf(stderr,"tpm_tis: Raising IRQ for flag %08x\n",irqmask);
+#endif
+        qemu_irq_raise(s->irq);
+        s->loc[locty].ints |= irqmask;
+    }
+}
+
+
+static uint32_t tis_check_request_use_except(TPMState *s, uint8_t locty)
+{
+    uint8_t l;
+
+    for (l = 0; l < NUM_LOCALITIES; l++) {
+        if (l == locty)
+            continue;
+        if ((s->loc[l].access & ACCESS_REQUEST_USE))
+            return 1;
+    }
+
+    return 0;
+}
+
+
+static void tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
+{
+    int change = (s->active_locty != new_active_locty);
+
+    if (change && IS_VALID_LOCTY(s->active_locty)) {
+        /* reset flags on the old active locality */
+        s->loc[s->active_locty].access &= ~(ACCESS_ACTIVE_LOCALITY|
+                                            ACCESS_REQUEST_USE);
+        if (IS_VALID_LOCTY(new_active_locty) &&
+            s->loc[new_active_locty].access & ACCESS_SEIZE)
+            s->loc[s->active_locty].access |= ACCESS_BEEN_SEIZED;
+    }
+
+    s->active_locty = new_active_locty;
+#ifdef DEBUG_TIS
+    fprintf(stderr,"tpm_tis: Active locality is now %d\n", s->active_locty);
+#endif
+
+    if (IS_VALID_LOCTY(new_active_locty)) {
+        /* set flags on the new active locality */
+        s->loc[new_active_locty].access |= ACCESS_ACTIVE_LOCALITY;
+        s->loc[new_active_locty].access &= ~(ACCESS_REQUEST_USE |
+                                             ACCESS_SEIZE);
+    }
+
+    if (change)
+        tis_raise_irq(s, s->active_locty, INT_LOCALITY_CHANGED);
+}
+
+
+/* abort -- this function switches the locality */
+static void tis_abort(TPMState *s, uint8_t locty)
+{
+    s->loc[locty].r_offset = 0;
+    s->loc[locty].w_offset = 0;
+
+#ifdef DEBUG_TIS
+    fprintf(stderr,"tpm_tis: tis_abort: new active locality is %d\n",
+            s->next_locty);
+#endif
+
+    /*
+     * Need to react differently depending on who's aborting now and
+     * which locality will become active afterwards.
+     */
+    if (s->aborting_locty == s->next_locty) {
+        s->loc[s->aborting_locty].state = STATE_READY;
+        s->loc[s->aborting_locty].sts   = STS_COMMAND_READY;
+        tis_raise_irq(s, s->aborting_locty, INT_COMMAND_READY );
+    }
+
+    /* locality after abort is another one than the current one */
+    tis_new_active_locality(s, s->next_locty);
+
+    s->next_locty = NO_LOCALITY;
+    s->aborting_locty = NO_LOCALITY; /* nobody's aborting a command anymore */
+}
+
+
+/* prepare aborting current command */
+static void tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
+{
+    uint8_t busy_locty;
+
+    s->aborting_locty = locty;
+    s->next_locty = newlocty;  /* locality after successful abort */
+
+    /*
+     * only abort a command using an interrupt if currently executing
+     * a command AND if there's a valid connection to the vTPM.
+     */
+    for (busy_locty = 0; busy_locty < NUM_LOCALITIES; busy_locty++) {
+        if (s->loc[busy_locty].state == STATE_EXECUTION) {
+            /* there is currently no way to interrupt the TPM's operations
+               while it's executing a command; once the TPM is done and
+               returns the buffer, it will switch to the next_locty; */
+#ifdef DEBUG_TIS
+            fprintf(stderr,"tpm_tis: Locality %d is busy - "
+                           "deferring abort\n", busy_locty);
+#endif
+            return;
+        }
+    }
+
+    tis_abort(s, locty);
+}
+
+
+/*
+ * Callback from the TPM to indicate that the response was received.
+ */
+static void tis_tpm_receive_cb(TPMState *s, uint8_t locty)
+{
+    qemu_mutex_lock(&s->state_lock);
+
+    s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+    s->loc[locty].state = STATE_COMPLETION;
+    s->loc[locty].r_offset = 0;
+    s->loc[locty].w_offset = 0;
+
+    if (IS_VALID_LOCTY(s->next_locty))
+        tis_abort(s, locty);
+
+    qemu_cond_signal(&s->from_tpm_cond);
+
+    qemu_mutex_unlock(&s->state_lock);
+
+#ifndef RAISE_STS_IRQ
+    tis_raise_irq(s, locty, INT_DATA_AVAILABLE);
+#else
+    tis_raise_irq(s, locty, INT_DATA_AVAILABLE | INT_STS_VALID);
+#endif
+}
+
+
+/*
+ * read a byte of response data
+ */
+static uint32_t tpm_data_read(TPMState *s, uint8_t locty)
+{
+    uint32_t ret = TPM_NO_DATA_BYTE;
+    uint16_t len;
+
+    qemu_mutex_lock(&s->state_lock);
+
+    if ((s->loc[locty].sts & STS_DATA_AVAILABLE)) {
+        len = tpm_get_size_from_buffer(&s->loc[locty].r_buffer);
+
+        ret = s->loc[locty].r_buffer.buffer[s->loc[locty].r_offset++];
+        if (s->loc[locty].r_offset >= len) {
+            /* got last byte */
+            s->loc[locty].sts = STS_VALID;
+#ifdef RAISE_STS_IRQ
+            tis_raise_irq(s, locty, INT_STS_VALID);
+#endif
+        }
+#ifdef DEBUG_TIS
+        fprintf(stderr,"tpm_tis: tpm_data_read byte 0x%02x   [%d]\n",
+                ret,s->loc[locty].r_offset-1);
+#endif
+    }
+
+    qemu_mutex_unlock(&s->state_lock);
+
+    return ret;
+}
+
+
+static void tis_dump_state(void *opaque, target_phys_addr_t addr)
+{
+    static const unsigned regs[] = {
+        TIS_REG_ACCESS,
+        TIS_REG_INT_ENABLE,
+        TIS_REG_INT_VECTOR,
+        TIS_REG_INT_STATUS,
+        TIS_REG_INTF_CAPABILITY,
+        TIS_REG_STS,
+        TIS_REG_DID_VID,
+        TIS_REG_RID,
+        0xfff};
+    int idx;
+    uint8_t locty = locality_from_addr(addr);
+    target_phys_addr_t base = addr & ~0xfff;
+    TPMState *s = (TPMState *)opaque;
+
+    fprintf(stdout,
+            "tpm_tis: active locality      : %d\n"
+            "tpm_tis: state of locality %d : %d\n"
+            "tpm_tis: register dump:\n",
+            s->active_locty,
+            locty, s->loc[locty].state);
+
+    for (idx = 0; regs[idx] != 0xfff; idx++)
+        fprintf(stdout, "tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
+                        tis_mem_readl(opaque, base + regs[idx]));
+
+    fprintf(stdout,
+            "tpm_tis: read offset   : %d\n"
+            "tpm_tis: result buffer : ",
+            s->loc[locty].r_offset);
+    for (idx = 0;
+         idx < tpm_get_size_from_buffer(&s->loc[locty].r_buffer);
+         idx++)
+        fprintf(stdout, "%c%02x%s",
+                s->loc[locty].r_offset == idx ? '>' : ' ',
+                s->loc[locty].r_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    fprintf(stdout,
+            "\n"
+            "tpm_tis: write offset  : %d\n"
+            "tpm_tis: request buffer: ",
+            s->loc[locty].w_offset);
+    for (idx = 0;
+         idx < tpm_get_size_from_buffer(&s->loc[locty].w_buffer);
+         idx++)
+        fprintf(stdout, "%c%02x%s",
+                s->loc[locty].w_offset == idx ? '>' : ' ',
+                s->loc[locty].w_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    fprintf(stdout,"\n");
+}
+
+
+/*
+ * Read a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static uint32_t tis_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    TPMState *s = (TPMState *)opaque;
+    uint16_t offset = addr & 0xffc;
+    uint8_t shift = (addr & 0x3) * 8;
+    uint32_t val = 0xff;
+    uint8_t locty = locality_from_addr(addr);
+
+    if (!s->tpm_initialized) {
+        active_be->late_startup_tpm();
+        s->tpm_initialized = true;
+    }
+
+    if (active_be->had_startup_error())
+        return 0xFFFFFFFF;
+
+    switch (offset) {
+    case TIS_REG_ACCESS:
+        /* never show the SEIZE flag even though we use it internally */
+        val = s->loc[locty].access & ~ACCESS_SEIZE;
+        /* the pending flag is alawys calculated */
+        if (tis_check_request_use_except(s, locty))
+            val |= ACCESS_PENDING_REQUEST;
+        val |= !active_be->get_tpm_established_flag();
+        break;
+    case TIS_REG_INT_ENABLE:
+        val = s->loc[locty].inte;
+        break;
+    case TIS_REG_INT_VECTOR:
+        val = s->irq_num;
+        break;
+    case TIS_REG_INT_STATUS:
+        val = s->loc[locty].ints;
+        break;
+    case TIS_REG_INTF_CAPABILITY:
+        val = CAPABILITIES_SUPPORTED;
+        break;
+    case TIS_REG_STS:
+        if (s->active_locty == locty) {
+            if ((s->loc[locty].sts & STS_DATA_AVAILABLE))
+                val =  (tpm_get_size_from_buffer(&s->loc[locty].r_buffer) -
+                        s->loc[locty].r_offset ) << 8 | s->loc[locty].sts;
+            else
+                val = (s->loc[locty].w_buffer.size -
+                       s->loc[locty].w_offset) << 8 | s->loc[locty].sts;
+        }
+        break;
+    case TIS_REG_DATA_FIFO:
+        if (s->active_locty == locty) {
+            switch (s->loc[locty].state) {
+                case STATE_COMPLETION:
+                    val = tpm_data_read(s, locty);
+                    break;
+                default:
+                    val = TPM_NO_DATA_BYTE;
+                    break;
+            }
+        }
+        break;
+    case TIS_REG_DID_VID:
+        val = (TPM_DID << 16) | TPM_VID;
+        break;
+    case TIS_REG_RID:
+        val = TPM_RID;
+        break;
+    case TIS_REG_DEBUG:
+        tis_dump_state(opaque, addr);
+        break;
+    }
+
+    if (shift)
+        val >>= shift;
+
+#ifdef DEBUG_TIS
+    fprintf(stderr,"tpm_tis:  read(%08x) = %08x\n", (int)addr, val);
+#endif
+
+    return val;
+}
+
+
+/*
+ * Write a value to a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static void tis_mem_writel_intern(void *opaque, target_phys_addr_t addr,
+                                  uint32_t val, bool hw_access)
+{
+    TPMState *s= (TPMState *)opaque;
+    uint16_t off = addr & 0xfff;
+    uint8_t locty = locality_from_addr(addr);
+    uint8_t active_locty, l;
+    int c, set_new_locty = 1;
+    uint16_t len;
+
+#ifdef DEBUG_TIS
+    fprintf(stderr,"tpm_tis: write(%08x) = %08x\n", (int)addr, val);
+#endif
+
+    if (!s->tpm_initialized) {
+        active_be->late_startup_tpm();
+        s->tpm_initialized = true;
+    }
+
+    if (active_be->had_startup_error())
+        return;
+
+    qemu_mutex_lock(&s->state_lock);
+
+    switch (off) {
+    case TIS_REG_ACCESS:
+
+        if ((val & ACCESS_SEIZE))
+            val &= ~(ACCESS_REQUEST_USE | ACCESS_ACTIVE_LOCALITY);
+
+        active_locty = s->active_locty;
+
+        if ((val & ACCESS_ACTIVE_LOCALITY)) {
+            /* give up locality if currently owned */
+            if (s->active_locty == locty) {
+#ifdef DEBUG_TIS
+                fprintf(stderr,"tpm_tis: Releasing locality %d\n", locty);
+#endif
+                uint8_t newlocty = NO_LOCALITY;
+                /* anybody wants the locality ? */
+                for (c = NUM_LOCALITIES - 1; c >= 0; c--) {
+                    if ((s->loc[c].access & ACCESS_REQUEST_USE)) {
+#ifdef DEBUG_TIS
+                        fprintf(stderr,"tpm_tis: Locality %d requests use.\n",
+                                c);
+#endif
+                        newlocty = c;
+                        break;
+                    }
+                }
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: ACCESS_ACTIVE_LOCALITY: "
+                                "Next active locality: %d\n",
+                                newlocty);
+#endif
+                if (IS_VALID_LOCTY(newlocty)) {
+                    set_new_locty = 0;
+                    tis_prep_abort(s, locty, newlocty);
+                } else
+                    active_locty = NO_LOCALITY;
+            } else {
+                /* not currently the owner; clear a pending request */
+                s->loc[locty].access &= ~ACCESS_REQUEST_USE;
+            }
+        }
+
+        if ((val & ACCESS_BEEN_SEIZED))
+            s->loc[locty].access &= ~ACCESS_BEEN_SEIZED;
+
+        if ((val & ACCESS_SEIZE)) {
+            /* allow seize if a locality is active and the requesting
+               locality is higher than the one that's active
+               OR
+               allow seize for requesting locality if no locality is
+               active */
+            while ( (IS_VALID_LOCTY(s->active_locty) &&
+                     locty > s->active_locty) ||
+                   (!IS_VALID_LOCTY(s->active_locty))) {
+
+                /* already a pending SEIZE ? */
+                if ((s->loc[locty].access & ACCESS_SEIZE))
+                    break;
+
+                /* check for ongoing seize by a higher locality */
+                for (l = locty + 1; l < NUM_LOCALITIES; l++)
+                    if ((s->loc[l].access & ACCESS_SEIZE))
+                        break;
+
+                /* cancel any seize by a lower locality */
+                for (l = 0; l < locty - 1; l++)
+                    s->loc[l].access &= ~ACCESS_SEIZE;
+
+                s->loc[locty].access |= ACCESS_SEIZE;
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: ACCESS_SEIZE: "
+                                "Locality %d seized from locality %d\n",
+                                locty, s->active_locty);
+                fprintf(stderr, "tpm_tis: ACCESS_SEIZE: Initiating abort.\n");
+#endif
+                set_new_locty = 0;
+                tis_prep_abort(s, s->active_locty, locty);
+                break;
+            }
+        }
+
+        if ((val & ACCESS_REQUEST_USE)) {
+            if (s->active_locty != locty) {
+                if (IS_VALID_LOCTY(s->active_locty)) {
+                     s->loc[locty].access |= ACCESS_REQUEST_USE;
+                } else {
+                    /* no locality active -> make this one active now */
+                    active_locty = locty;
+                }
+            }
+        }
+
+        if (set_new_locty)
+            tis_new_active_locality(s, active_locty);
+
+        break;
+    case TIS_REG_INT_ENABLE:
+        if (s->active_locty != locty)
+            break;
+
+        s->loc[locty].inte = (val & (INT_ENABLED | (0x3 << 3) |
+                                     INTERRUPTS_SUPPORTED));
+        break;
+    case TIS_REG_INT_VECTOR:
+        /* hard wired -- ignore */
+        break;
+    case TIS_REG_INT_STATUS:
+        if (s->active_locty != locty)
+            break;
+
+        /* clearing of interrupt flags */
+        if (((val & INTERRUPTS_SUPPORTED)) &&
+            (s->loc[locty].ints & INTERRUPTS_SUPPORTED)) {
+            s->loc[locty].ints &= ~val;
+            if (s->loc[locty].ints == 0) {
+                qemu_irq_lower(s->irq);
+#ifdef DEBUG_TIS
+                fprintf(stderr,"tpm_tis: Lowering IRQ\n");
+#endif
+            }
+        }
+        s->loc[locty].ints &= ~(val & INTERRUPTS_SUPPORTED);
+        break;
+    case TIS_REG_STS:
+        if (s->active_locty != locty)
+            break;
+
+        val &= (STS_COMMAND_READY | STS_TPM_GO | STS_RESPONSE_RETRY);
+
+        if (val == STS_COMMAND_READY) {
+            switch (s->loc[locty].state) {
+
+            case STATE_READY:
+                s->loc[locty].w_offset = 0;
+                s->loc[locty].r_offset = 0;
+            break;
+
+            case STATE_IDLE:
+                s->loc[locty].sts   = STS_COMMAND_READY;
+                s->loc[locty].state = STATE_READY;
+                tis_raise_irq(s, locty, INT_COMMAND_READY);
+            break;
+
+            case STATE_EXECUTION:
+            case STATE_RECEPTION:
+                /* abort currently running command */
+#ifdef DEBUG_TIS
+                fprintf(stderr, "tpm_tis: %s: Initiating abort.\n",
+                        __FUNCTION__);
+#endif
+                tis_prep_abort(s, locty, locty);
+            break;
+
+            case STATE_COMPLETION:
+                s->loc[locty].w_offset = 0;
+                s->loc[locty].r_offset = 0;
+                /* shortcut to ready state with C/R set */
+                s->loc[locty].state = STATE_READY;
+                if (!(s->loc[locty].sts & STS_COMMAND_READY)) {
+                    s->loc[locty].sts   = STS_COMMAND_READY;
+                    tis_raise_irq(s, locty, INT_COMMAND_READY);
+                }
+            break;
+
+            }
+        } else if (val == STS_TPM_GO) {
+            switch (s->loc[locty].state) {
+            case STATE_RECEPTION:
+                tis_tpm_send(s, locty);
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        } else if (val == STS_RESPONSE_RETRY) {
+            switch (s->loc[locty].state) {
+            case STATE_COMPLETION:
+                s->loc[locty].r_offset = 0;
+                s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        }
+        break;
+    case TIS_REG_DATA_FIFO:
+        /* data fifo */
+        if (s->active_locty != locty)
+            break;
+
+        if (s->loc[locty].state == STATE_IDLE ||
+            s->loc[locty].state == STATE_EXECUTION ||
+            s->loc[locty].state == STATE_COMPLETION) {
+            /* drop the byte */
+        } else {
+#ifdef DEBUG_TIS
+            fprintf(stderr,"tpm_tis: Byte to send to TPM: %02x\n", val);
+#endif
+            if (s->loc[locty].state == STATE_READY) {
+                s->loc[locty].state = STATE_RECEPTION;
+                s->loc[locty].sts = STS_EXPECT | STS_VALID;
+            }
+
+            if ((s->loc[locty].sts & STS_EXPECT)) {
+                if (s->loc[locty].w_offset < s->loc[locty].w_buffer.size)
+                    s->loc[locty].w_buffer.buffer[s->loc[locty].w_offset++] =
+                        (uint8_t)val;
+                else
+                    s->loc[locty].sts = STS_VALID;
+            }
+
+            /* check for complete packet */
+            if ( s->loc[locty].w_offset > 5 &&
+                (s->loc[locty].sts & STS_EXPECT)) {
+                /* we have a packet length - see if we have all of it */
+#ifdef RAISE_STS_IRQ
+                bool needIrq = !(s->loc[locty].sts & STS_VALID);
+#endif
+                len = tpm_get_size_from_buffer(&s->loc[locty].w_buffer);
+                if (len > s->loc[locty].w_offset) {
+                    s->loc[locty].sts = STS_EXPECT | STS_VALID;
+                } else {
+                    /* packet complete */
+                    s->loc[locty].sts = STS_VALID;
+                }
+#ifdef RAISE_STS_IRQ
+                if (needIrq)
+                    tis_raise_irq(s, locty, INT_STS_VALID);
+#endif
+            }
+        }
+        break;
+    }
+
+    qemu_mutex_unlock(&s->state_lock);
+}
+
+
+static void tis_mem_writel(void *opaque, target_phys_addr_t addr,
+                           uint32_t val)
+{
+    return tis_mem_writel_intern(opaque, addr, val, false);
+}
+
+
+static CPUReadMemoryFunc *tis_readfn[3] = {
+    tis_mem_readl,
+    tis_mem_readl,
+    tis_mem_readl
+};
+
+static CPUWriteMemoryFunc *tis_writefn[3] = {
+    tis_mem_writel,
+    tis_mem_writel,
+    tis_mem_writel
+};
+
+
+/*
+ * This function gets called when resuming a snapshot. In that
+ * case we received the TIS state from persistent storage and
+ * just need to reset.
+ */
+void tis_reset_for_snapshot_resume(TPMState *s)
+{
+    s->tpm_initialized = false;
+    active_be->reset();
+}
+
+
+/*
+ * This function is called when the machine starts, resets or due to
+ * S3 resume.
+ * In case of S3 resume we don't reset the TIS but maintain its state.
+ */
+static void tis_reset(TPMState *s)
+{
+    int c;
+
+    s->tpm_initialized = false;
+
+    active_be->reset();
+
+    s->active_locty = NO_LOCALITY;
+    s->next_locty = NO_LOCALITY;
+    s->aborting_locty = NO_LOCALITY;
+
+    for (c = 0; c < NUM_LOCALITIES; c++) {
+        s->loc[c].access = ACCESS_TPM_REG_VALID_STS;
+        s->loc[c].sts = 0;
+        s->loc[c].inte = (1 << 3);
+        s->loc[c].ints = 0;
+        s->loc[c].state = STATE_IDLE;
+
+        s->loc[c].w_offset = 0;
+        active_be->realloc_buffer(&s->loc[c].w_buffer);
+        s->loc[c].r_offset = 0;
+        active_be->realloc_buffer(&s->loc[c].r_buffer);
+    }
+}
+
+
+static void tpm_tis_reset(DeviceState *d)
+{
+    TPMState *s = container_of(d, TPMState, busdev.qdev);
+    tis_reset(s);
+}
+
+
+static int tpm_tis_init(ISADevice *dev)
+{
+    TPMState *s = DO_UPCAST(TPMState, busdev, dev);
+    int iomemtype;
+
+    qemu_mutex_init(&s->state_lock);
+    qemu_cond_init(&s->from_tpm_cond);
+    qemu_cond_init(&s->to_tpm_cond);
+
+    if (active_be->init(s, tis_tpm_receive_cb))
+        goto err_exit;
+
+    isa_init_irq(dev, &s->irq, s->irq_num);
+
+    iomemtype = cpu_register_io_memory(tis_readfn, tis_writefn, s,
+                                       DEVICE_LITTLE_ENDIAN);
+    cpu_register_physical_memory(TIS_ADDR_BASE, 0x1000 * NUM_LOCALITIES,
+                                 iomemtype);
+
+    return 0;
+
+ err_exit:
+    exit(1);
+}
+
+/* persistent state handling */
+
+static void tpm_tis_pre_save(void *opaque)
+{
+    TPMState *s= (TPMState *)opaque;
+    uint8_t locty = s->active_locty;
+
+    qemu_mutex_lock(&s->state_lock);
+
+    /* wait for outstanding requests to complete */
+    if (IS_VALID_LOCTY(locty) && s->loc[locty].state == STATE_EXECUTION)
+        qemu_cond_wait(&s->from_tpm_cond, &s->state_lock);
+
+
+#ifdef DEBUG_TIS_SR
+    fprintf(stderr,"tpm_tis: suspend: locty 0 : r_offset = %d, w_offset = %d\n",
+        s->loc[0].r_offset,
+        s->loc[0].w_offset);
+    if (s->loc[0].r_offset)
+        tis_dump_state(opaque, 0);
+#endif
+
+    qemu_mutex_unlock(&s->state_lock);
+
+    /* copy current active read or write buffer into the buffer
+       written to disk */
+    if (IS_VALID_LOCTY(locty)) {
+        switch (s->loc[locty].state) {
+        case STATE_RECEPTION:
+            memcpy(s->buf,
+                   s->loc[locty].w_buffer.buffer,
+                   MIN(sizeof(s->buf),
+                       s->loc[locty].w_buffer.size));
+            s->offset = s->loc[locty].w_offset;
+        break;
+        case STATE_COMPLETION:
+            memcpy(s->buf,
+                   s->loc[locty].r_buffer.buffer,
+                   MIN(sizeof(s->buf),
+                       s->loc[locty].r_buffer.size));
+            s->offset = s->loc[locty].r_offset;
+        break;
+        default:
+            /* leak nothing */
+            memset(s->buf, 0x0, sizeof(s->buf));
+        break;
+        }
+    }
+
+    tis_get_active_backend()->save_volatile_data();
+}
+
+
+static int tpm_tis_post_load(void *opaque, int version_id)
+{
+    TPMState *s = (TPMState *)opaque;
+    (void)version_id;
+
+    uint8_t locty = s->active_locty;
+
+    if (IS_VALID_LOCTY(locty)) {
+        switch (s->loc[locty].state) {
+        case STATE_RECEPTION:
+            memcpy(s->loc[locty].w_buffer.buffer,
+                   s->buf,
+                   MIN(sizeof(s->buf),
+                       s->loc[locty].w_buffer.size));
+            s->loc[locty].w_offset = s->offset;
+        break;
+        case STATE_COMPLETION:
+            memcpy(s->loc[locty].r_buffer.buffer,
+                   s->buf,
+                   MIN(sizeof(s->buf),
+                       s->loc[locty].r_buffer.size));
+            s->loc[locty].r_offset = s->offset;
+        break;
+        default:
+        break;
+        }
+    }
+
+#ifdef DEBUG_TIS_SR
+    fprintf(stderr,"tpm_tis: resume : locty 0 : r_offset = %d, w_offset = %d\n",
+            s->loc[0].r_offset,
+            s->loc[0].w_offset);
+#endif
+
+    return tis_get_active_backend()->load_volatile_data(s);
+}
+
+
+static const VMStateDescription vmstate_locty = {
+    .name = "loc",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(state   , TPMLocality),
+        VMSTATE_UINT32(inte    , TPMLocality),
+        VMSTATE_UINT32(ints    , TPMLocality),
+        VMSTATE_UINT8 (access  , TPMLocality),
+        VMSTATE_UINT8 (sts     , TPMLocality),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+
+static const VMStateDescription vmstate_tpm_tis = {
+    .name = "tpm",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .pre_save  = tpm_tis_pre_save,
+    .post_load = tpm_tis_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32(irq_num       , TPMState),
+        VMSTATE_UINT32(offset        , TPMState),
+        VMSTATE_BUFFER(buf           , TPMState),
+        VMSTATE_UINT8 (  active_locty, TPMState),
+        VMSTATE_UINT8 (aborting_locty, TPMState),
+        VMSTATE_UINT8 (    next_locty, TPMState),
+
+        VMSTATE_STRUCT_ARRAY(loc, TPMState, NUM_LOCALITIES, 1,
+                             vmstate_locty, TPMLocality),
+
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+static ISADeviceInfo tpm_tis_device_info = {
+    .init         = tpm_tis_init,
+    .qdev.name    = "tpm-tis",
+    .qdev.size    = sizeof(TPMState),
+    .qdev.no_user = 1,
+    .qdev.vmsd    = &vmstate_tpm_tis,
+    .qdev.reset   = tpm_tis_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT8("active_locality", TPMState,
+                          active_locty, NO_LOCALITY),
+        DEFINE_PROP_UINT32("irq", TPMState,
+                           irq_num, TPM_TIS_IRQ),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+
+static void tpm_tis_register_device(void)
+{
+    isa_qdev_register(&tpm_tis_device_info);
+}
+
+device_init(tpm_tis_register_device)
Index: qemu-git/hw/tpm_tis.h
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_tis.h
@@ -0,0 +1,158 @@
+/*
+ * tpm_tis.h - include file for tpm_tis.c
+ *
+ * Copyright (C) 2006,2010,2011 IBM Corporation
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ *         David Safford <safford@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_TIS_H
+#define _HW_TPM_TIS_H
+
+#include <stdint.h>
+
+#include "isa.h"
+#include "block_int.h"
+#include "qemu-thread.h"
+
+#define TIS_ADDR_BASE       0xFED40000
+
+#define NUM_LOCALITIES      5     /* per spec */
+#define NO_LOCALITY         0xff
+
+#define IS_VALID_LOCTY(x)   ((x) < NUM_LOCALITIES)
+
+
+#define TPM_TIS_IRQ         11
+
+#define TIS_TPM_BUFFER_MAX  4096
+
+
+typedef struct TPMSizedBuffer
+{
+    uint32_t size;
+    uint8_t  *buffer;
+} TPMSizedBuffer;
+
+
+enum tis_state {
+    STATE_IDLE = 0,
+    STATE_READY,
+    STATE_COMPLETION,
+    STATE_EXECUTION,
+    STATE_RECEPTION,
+};
+
+
+/* locality data  -- all fields are persisted */
+typedef struct TPMLocality {
+    enum tis_state state;
+    uint8_t access;
+    uint8_t sts;
+    uint32_t inte;
+    uint32_t ints;
+
+    uint16_t w_offset;
+    uint16_t r_offset;
+    TPMSizedBuffer w_buffer;
+    TPMSizedBuffer r_buffer;
+} TPMLocality;
+
+
+/* overall state of the TPM interface; 's' marks a persisted field */
+typedef struct TPMState {
+    ISADevice busdev;
+
+    uint32_t offset;
+    uint8_t buf[TIS_TPM_BUFFER_MAX];
+
+    uint8_t active_locty;
+    uint8_t aborting_locty;
+    uint8_t next_locty;
+
+    uint8_t command_locty;
+    TPMLocality loc[NUM_LOCALITIES];
+
+    qemu_irq irq;
+    uint32_t irq_num;
+
+    QemuMutex state_lock;
+    QemuCond  from_tpm_cond;
+    QemuCond  to_tpm_cond;
+    bool      to_tpm_execute;
+
+    bool      tpm_initialized;
+} TPMState;
+
+
+void tis_reset_for_snapshot_resume(TPMState *s);
+const struct backend_tpm_driver *tis_get_active_backend(void);
+
+typedef void (TPMRecvDataCB)(TPMState *s, uint8_t locty);
+
+struct backend_tpm_driver {
+    const char *id;
+    const char *(*desc)(void);
+
+    bool (*handle_options)(QemuOpts *);
+
+    int (*init)(TPMState *s, TPMRecvDataCB *datacb);
+    int (*late_startup_tpm)(void);
+    bool (*had_startup_error)(void);
+
+    size_t (*realloc_buffer)(TPMSizedBuffer *sb);
+
+    void (*reset)(void);
+
+    int (*save_volatile_data)(void);
+    int (*load_volatile_data)(TPMState *s);
+
+    bool (*get_tpm_established_flag)(void);
+};
+
+extern struct backend_tpm_driver builtin;
+
+
+/* utility functions */
+
+static inline uint16_t tpm_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+    return (sb->buffer[4] << 8) + sb->buffer[5];
+}
+
+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 void dumpBuffer(FILE *stream,
+                              unsigned char *buffer, unsigned int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++) {
+        if (i && !(i % 16))
+            fprintf(stream, "\n");
+         fprintf(stream, "%.2X ", buffer[i]);
+    }
+    fprintf(stream, "\n");
+}
+
+#endif /* _HW_TPM_TIS_H */
Index: qemu-git/hw/pc.c
===================================================================
--- qemu-git.orig/hw/pc.c
+++ qemu-git/hw/pc.c
@@ -1153,6 +1153,9 @@ void pc_basic_device_init(qemu_irq *isa_
         fd[i] = drive_get(IF_FLOPPY, 0, i);
     }
     fdctrl_init_isa(fd);
+
+    if (has_tpm)
+        isa_create_simple("tpm-tis");
 }
 
 void pc_pci_device_init(PCIBus *pci_bus)
