---
Makefile.target | 3 +
configure | 9 +
hw/pc_piix.c | 8 +
hw/tpm_tis.c | 831 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 851 insertions(+), 0 deletions(-)
create mode 100644 hw/tpm_tis.c
diff --git a/Makefile.target b/Makefile.target
index c8281e9..2226c75 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -193,6 +193,9 @@ obj-y += e1000.o
# Inter-VM PCI shared memory
obj-$(CONFIG_KVM) += ivshmem.o
+# TPM passthrough device
+obj-$(CONFIG_TPM) += tpm_tis.o qemu-thread.o
+
# Hardware support
obj-i386-y += vga.o
obj-i386-y += mc146818rtc.o i8259.o pc.o
diff --git a/configure b/configure
index a20371c..e1b55a8 100755
--- a/configure
+++ b/configure
@@ -315,6 +315,7 @@ pkgversion=""
check_utests="no"
user_pie="no"
zero_malloc=""
+tpm="no"
# OS specific
if check_define __linux__ ; then
@@ -448,6 +449,7 @@ AIX)
usb="linux"
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
audio_possible_drivers="$audio_possible_drivers fmod"
+ tpm="yes"
fi
;;
esac
@@ -707,6 +709,8 @@ for opt do
;;
--enable-vhost-net) vhost_net="yes"
;;
+ --disable-tpm) tpm="no"
+ ;;
--*dir)
;;
*) echo "ERROR: unknown option $opt"; show_help="yes"
@@ -895,6 +899,7 @@ echo " --enable-docs enable documentation build"
echo " --disable-docs disable documentation build"
echo " --disable-vhost-net disable vhost-net acceleration support"
echo " --enable-vhost-net enable vhost-net acceleration support"
+echo " --disable-tpm disable tpm passthrough device emulation"
echo ""
echo "NOTE: The object files are built at the place where configure is
launched"
exit 1
@@ -2187,6 +2192,7 @@ echo "preadv support $preadv"
echo "fdatasync $fdatasync"
echo "uuid support $uuid"
echo "vhost-net support $vhost_net"
+echo "tpm support $tpm"
if test $sdl_too_old = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -2423,6 +2429,9 @@ fi
if test "$fdatasync" = "yes" ; then
echo "CONFIG_FDATASYNC=y">> $config_host_mak
fi
+if test "$tpm" = "yes" ; then
+ echo "CONFIG_TPM=y">> $config_host_mak
+fi
# XXX: suppress that
if [ "$bsd" = "yes" ] ; then
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 32a1057..ebc3478 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -38,6 +38,10 @@
#define MAX_IDE_BUS 2
+#ifdef CONFIG_TPM
+void tpm_tis_init(qemu_irq irq);
+#endif
+
static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 };
static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 };
static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
@@ -147,6 +151,10 @@ static void pc_init1(ram_addr_t ram_size,
}
}
+#ifdef CONFIG_TPM
+ tpm_tis_init(isa_reserve_irq(11));
+#endif
+
pc_audio_init(pci_enabled ? pci_bus : NULL, isa_irq);
pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device,
diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
new file mode 100644
index 0000000..e109e1a
--- /dev/null
+++ b/hw/tpm_tis.c
@@ -0,0 +1,831 @@
+/*
+ * tpm_tis.c - QEMU emulator for a 1.2 TPM with TIS interface
+ *
+ * Copyright (C) 2006 IBM Corporation
+ * Copyright (C) 2010 IAIK, Graz University of Technology
+ *
+ * Author: Stefan Berger<address@hidden>
+ * David Safford<address@hidden>
+ *
+ * Author: Andreas Niederl<address@hidden>
+ * Pass through a TPM device rather than using the emulator
+ * Modified to use a separate thread for IO to/from TPM as the Linux TPM driver
+ * framework does not allow non-blocking IO
+ *
+ * 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<sys/types.h>
+#include<sys/stat.h>
+#include<errno.h>
+#include<string.h>
+
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "hw/hw.h"
+#include "hw/pc.h"
+#include "hw/pci.h"
+#include "hw/pci_ids.h"
+#include "qemu-thread.h"
+
+//#define TPM_DEBUG
+
+#define TPM_MAX_PKT 4096
+
+#define TIS_ADDR_BASE 0xFED40000
+
+/* tis registers */
+#define TPM_REG_ACCESS 0x00
+#define TPM_REG_INT_ENABLE 0x08
+#define TPM_REG_INT_VECTOR 0x0c
+#define TPM_REG_INT_STATUS 0x10
+#define TPM_REG_INTF_CAPABILITY 0x14
+#define TPM_REG_STS 0x18
+#define TPM_REG_DATA_FIFO 0x24
+#define TPM_REG_DID_VID 0xf00
+#define TPM_REG_RID 0xf04
+
+#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_LOCALITY_CHANGED (1<< 2)
+#define INT_COMMAND_READY (1<< 7)
+
+#define INTERRUPTS_SUPPORTED (INT_LOCALITY_CHANGED | \
+ INT_DATA_AVAILABLE | \
+ INT_COMMAND_READY)
+#define CAPABILITIES_SUPPORTED ((1<< 4) | \
+ INTERRUPTS_SUPPORTED)
+
+enum {
+ STATE_IDLE = 0,
+ STATE_READY,
+ STATE_COMPLETION,
+ STATE_EXECUTION,
+ STATE_RECEPTION
+};
+
+#define NUM_LOCALITIES 5
+#define NO_LOCALITY 0xff
+
+#define IS_VALID_LOC(x) ((x)< NUM_LOCALITIES)
+
+#define TPM_DID 0x0001
+#define TPM_VID 0x0001
+#define TPM_RID 0x0001
+
+
+const char *tpm_device = NULL;
+
+/* locality data */
+typedef struct TPMLocal {
+ uint32_t state;
+ uint8_t access;
+ uint8_t sts;
+ uint32_t inte;
+ uint32_t ints;
+} tpmLoc;
+
+/* overall state of the TPM interface; 's' marks as save upon suspension */
+typedef struct TPMState {
+ uint8_t transmit; /* boolean value */
+ /* set when tpm transmit in progress */
+ uint8_t aborting; /* boolean value */
+ uint32_t offset; /* s */
+ uint8_t buf[TPM_MAX_PKT]; /* s */
+ int tpmfd; /* s */
+
+ /* path to host tpm device */
+ const char *path; /* s */
+ uint32_t path_len; /* s */
+
+ QemuMutex tpm_thread_lock;
+ QemuMutex tpm_thread_abort_lock;
+ QemuCond tpm_thread_cond_data_avail;
+ QemuThread tpm_thread_id;
+
+ uint8_t active_loc; /* s */
+ uint8_t aborting_locty;
+ uint8_t next_locty;
+ uint8_t irq_pending; /* s */
+ tpmLoc loc[NUM_LOCALITIES]; /* s */
+ qemu_irq_handler set_irq;
+ void *irq_opaque;
+ int irq;
+} tpmState;
+
+
+/* local prototypes */
+static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask);
+static void tis_abort(tpmState *s);
+
+#ifdef TPM_DEBUG
+static void showBuff(unsigned char *buff, const char *string);
+#endif
+
+
+/**********************************************************************
+ helper functions
+ *********************************************************************/
+
+static inline uint32_t tpm_get_size_from_buffer(const uint8_t *buffer)
+{
+ uint32_t len = (buffer[4]<< 8) + buffer[5];
+ return len;
+}
+
+static inline uint8_t locality_from_addr(target_phys_addr_t addr)
+{
+ return (uint8_t)((addr>> 12)& 0x7);
+}
+
+
+static void die2(int err, const char *what)
+{
+ fprintf(stderr, "%s failed: %s\n", what, strerror(err));
+ abort();
+}
+
+static void die(const char *what)
+{
+ die2(errno, what);
+}
+
+
+/* borrowed from qemu-char.c */
+static int unix_write(int fd, const uint8_t *buf, uint32_t len1)
+{
+ int ret, len;
+
+ len = len1;
+ while (len> 0) {
+ ret = write(fd, buf, len);
+ if (ret< 0) {
+ if (errno != EINTR&& errno != EAGAIN)
+ return -1;
+ } else if (ret == 0) {
+ break;
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+ return len1 - len;
+}
+
+static int unix_tpm_read(int fd, uint8_t *buf)
+{
+ int ret, len, len1;
+ uint8_t *buf1;
+
+ buf1 = buf;
+ len = sizeof(buf) + TPM_MAX_PKT;
+ while ((buf1 - buf)< 6) {
+ ret = read(fd, buf1, len);
+ if (ret< 0) {
+ if (errno != EINTR&& errno != EAGAIN)
+ return -1;
+ } else {
+ buf1 += ret;
+ }
+ }
+
+ len1 = tpm_get_size_from_buffer(buf);
+ if (len1> TPM_MAX_PKT) {
+ fprintf(stderr, "Error: received invalid tpm response size: %d\n",
+ len1);
+ return -1;
+ }
+
+ /* response size minus already read data */
+ len = len1 - (buf1 - buf);
+ while (len> 0) {
+ ret = read(fd, buf, len);
+ if (ret< 0) {
+ if (errno != EINTR&& errno != EAGAIN)
+ return -1;
+ } else if (ret == 0) {
+ break;
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+ return len1 - len;
+}
+
+/****************************************************************************/
+/* Transmit request to TPM and read Response */
+/****************************************************************************/
+
+static void *tpm_thread(void *opaque)
+{
+ tpmState *s = opaque;
+ sigset_t set;
+ uint32_t size = 0;
+ /* hardcode locality 0 */
+ uint8_t locty = 0;
+#ifdef TPM_DEBUG
+ uint32_t ret;
+#endif
+
+ /* block all signals */
+ if (sigfillset(&set)) die("sigfillset");
+ if (sigprocmask(SIG_BLOCK,&set, NULL)) die("sigprocmask");
+
+ qemu_mutex_lock(&s->tpm_thread_lock);
+ while(1) {
+ if(!s->transmit)
+ qemu_cond_wait(&s->tpm_thread_cond_data_avail,&s->tpm_thread_lock);
+ qemu_mutex_unlock(&s->tpm_thread_lock);
+
+#ifdef TPM_DEBUG
+ showBuff(s->buf, "To TPM");
+#endif
+ size = tpm_get_size_from_buffer(s->buf);
+
+ if(unix_write(s->tpmfd, s->buf, size)< 0) {
+ fprintf(stderr, "Error: while transmitting data to host tpm"
+ ": %s (%i)\n",
+ strerror(errno), errno);
+ tis_abort(s);
+ qemu_mutex_lock(&s->tpm_thread_lock);
+ continue;
+ }
+
+ if(unix_tpm_read(s->tpmfd, s->buf)< 0) {
+ fprintf(stderr, "Error: while reading data from host tpm"
+ ": %s (%i)\n",
+ strerror(errno), errno);
+ tis_abort(s);
+ qemu_mutex_lock(&s->tpm_thread_lock);
+ continue;
+ }
+
+ qemu_mutex_lock(&s->tpm_thread_abort_lock);
+ if(!s->aborting) {
+#ifdef TPM_DEBUG
+ showBuff(s->buf, "From TPM");
+
+ ret = (s->buf[8])*256 + s->buf[9];
+ if (ret)
+ printf("tpm command failed with error %d\n", ret);
+ else
+ printf("tpm command succeeded\n");
+#endif
+
+ qemu_mutex_lock(&s->tpm_thread_lock);
+ s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+ s->loc[locty].state = STATE_COMPLETION;
+ s->transmit = 0;
+ tis_raise_irq(s, locty, INT_DATA_AVAILABLE);
+ } else {
+ qemu_mutex_lock(&s->tpm_thread_lock);
+ tis_abort(s);
+#ifdef TPM_DEBUG
+ printf("Abort is complete.\n");
+#endif
+ s->transmit = 0;
+ }
+ qemu_mutex_unlock(&s->tpm_thread_abort_lock);
+ }
+
+ return NULL;
+}
+
+
+/**********************************************************************/
+
+/*
+ * read a byte of response data
+ */
+static uint32_t tpm_data_read(tpmState *s, uint8_t locty)
+{
+ uint32_t ret, len;
+
+ if (s->loc[locty].state != STATE_COMPLETION) {
+#ifdef TPM_DEBUG
+ printf("tpm_data_read with no data available!\n");
+#endif
+ return 0xff;
+ }
+
+ len = tpm_get_size_from_buffer(s->buf);
+ ret = s->buf[s->offset++];
+ if (s->offset>= len) {
+ s->loc[locty].sts = STS_VALID ;
+ s->offset = 0;
+ }
+#ifdef TPM_DEBUG
+ printf("tpm_data_read byte x%02x [%d]\n",ret,s->offset-1);
+#endif
+ return ret;
+}
+
+/* raise an interrupt if allowed */
+static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask)
+{
+ if (!s->irq_pending&&
+ (s->loc[locty].inte& INT_ENABLED)&&
+ (s->loc[locty].inte& irqmask)) {
+ if ((irqmask& s->loc[locty].ints) == 0) {
+#ifdef TPM_DEBUG
+ printf("Raising IRQ for flag %08x\n",irqmask);
+#endif
+ s->set_irq(s->irq_opaque, s->irq, 1);
+ s->irq_pending = 1;
+ s->loc[locty].ints |= irqmask;
+ }
+ }
+}
+
+/* abort execution of command */
+static void tis_abort(tpmState *s)
+{
+ s->offset = 0;
+ s->active_loc = s->next_locty;
+ qemu_mutex_lock(&s->tpm_thread_lock);
+ s->transmit = 0;
+ qemu_mutex_unlock(&s->tpm_thread_lock);
+
+ /*
+ * 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 */
+ if (s->aborting_locty != s->next_locty&& s->next_locty != NO_LOCALITY) {
+ s->loc[s->aborting_locty].access&= ~ACCESS_ACTIVE_LOCALITY;
+ s->loc[s->next_locty].access |= ACCESS_ACTIVE_LOCALITY;
+ tis_raise_irq(s, s->next_locty, INT_LOCALITY_CHANGED);
+ }
+
+ s->aborting_locty = NO_LOCALITY; /* nobody's aborting a command anymore */
+}
+
+/* abort current command */
+static void tis_prep_abort(tpmState *s, uint8_t locty, uint8_t newlocty)
+{
+ s->aborting_locty = locty; /* current locality */
+ s->next_locty = newlocty; /* locality after successful abort */
+
+ s->next_locty = 0; /* only locality 0 available */
+
+ /*
+ * only abort a command using an interrupt if currently executing
+ * a command AND if there's a valid connection to the vTPM.
+ */
+ if (s->loc[locty].state == STATE_EXECUTION) {
+ qemu_mutex_lock(&s->tpm_thread_abort_lock);
+ s->aborting = 1;
+ qemu_mutex_unlock(&s->tpm_thread_abort_lock);
+ } else {
+ tis_abort(s);
+ }
+}
+
+
+/*
+ * 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 = 0;
+ uint8_t locty = locality_from_addr(addr);
+
+ if (offset == TPM_REG_ACCESS) {
+ if (locty> 0) {
+ /* no access to localities other than 0 possible */
+ val = 0;
+ } else {
+ if (s->active_loc == locty) {
+ s->loc[locty].access |= (1<< 5);
+ } else {
+ s->loc[locty].access&= ~(1<< 5);
+ }
+ val = s->loc[locty].access;
+ }
+ } else
+ if (locty> 0) {
+ /* higher localities deactivated */
+ val = 0xffff;
+ } else
+ if (offset == TPM_REG_INT_ENABLE) {
+ val = s->loc[locty].inte;
+ } else
+ if (offset == TPM_REG_INT_VECTOR) {
+ val = s->irq;
+ } else
+ if (offset == TPM_REG_INT_STATUS) {
+ val = s->loc[locty].ints;
+ } else
+ if (offset == TPM_REG_INTF_CAPABILITY) {
+ val = CAPABILITIES_SUPPORTED;
+ } else
+ if (offset == TPM_REG_STS) { /* status register */
+ /* ??? - appears to be just some value as we aren't a hardware TPM */
+ val = (sizeof(s->buf) - s->offset)<< 8 | s->loc[locty].sts;
+ } else
+ if (offset == TPM_REG_DATA_FIFO) {
+ val = tpm_data_read(s, locty);
+ } else
+ if (offset == TPM_REG_DID_VID) {
+ val = (TPM_DID<< 16) | TPM_VID;
+ } else
+ if (offset == TPM_REG_RID) {
+ val = TPM_RID;
+ }
+
+ if (shift&& locty == 0) {
+ val>>= shift;
+ }
+
+#ifdef TPM_DEBUG
+ printf(" 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(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ tpmState* s=(tpmState*)opaque;
+ uint16_t off = addr& 0xfff;
+ uint8_t locty = locality_from_addr(addr);
+ uint32_t len;
+
+#ifdef TPM_DEBUG
+ printf("write(%08x) = %08x\n",
+ (int)addr,
+ val);
+#endif
+
+ /* linux tpm_tis driver only uses locality 0 */
+ if (locty> 0) {
+ fprintf(stderr, "Warning: Write to unavailable locality: %d \n",
locty);
+ return;
+ }
+#ifdef TPM_DEBUG
+ if(s->transmit) {
+ fprintf(stderr, "Warning: tpm received data while transmit"
+ " in progress.\n");
+ printf("WARN: write(%08x) = %08x\n",
+ (int)addr,
+ val);
+ }
+#endif
+
+ if (off == TPM_REG_ACCESS) {
+ if (val& ACCESS_ACTIVE_LOCALITY) {
+ /* give up locality if currently owned */
+ if (s->active_loc == locty) {
+ /*uint8_t newlocty = NO_LOCALITY;*/
+ s->loc[locty].access&= ~(ACCESS_PENDING_REQUEST);
+ /* anybody wants the locality ? */
+ if (s->loc[locty].access& ACCESS_REQUEST_USE) {
+ s->loc[locty].access |= ACCESS_TPM_REG_VALID_STS;
+ s->loc[locty].access&= ~ACCESS_REQUEST_USE;
+ }
+ tis_prep_abort(s, locty, locty);
+ }
+ }
+ if (val& ACCESS_BEEN_SEIZED) {
+ /* clear the flag */
+ s->loc[locty].access&= ~ACCESS_BEEN_SEIZED;
+ }
+ if (val& ACCESS_SEIZE) {
+ if (locty> s->active_loc&& IS_VALID_LOC(s->active_loc)) {
+ s->loc[s->active_loc].access |= ACCESS_BEEN_SEIZED;
+ s->loc[locty].access = ACCESS_TPM_REG_VALID_STS;
+ tis_prep_abort(s, s->active_loc, locty);
+ }
+ }
+ if (val& ACCESS_REQUEST_USE) {
+ if (IS_VALID_LOC(s->active_loc)) {
+ /* locality election */
+ s->loc[s->active_loc].access |= ACCESS_PENDING_REQUEST;
+ } else {
+ /* no locality active -> make this one active now */
+ s->loc[locty].access |= ACCESS_ACTIVE_LOCALITY;
+ s->active_loc = locty;
+ tis_raise_irq(s, locty, INT_LOCALITY_CHANGED);
+ }
+ }
+ } else
+ if (off == TPM_REG_INT_ENABLE) {
+ s->loc[locty].inte = (val& (INT_ENABLED | (0x3<< 3) |
+ INTERRUPTS_SUPPORTED));
+ } else
+ if (off == TPM_REG_INT_STATUS) {
+ /* clearing of interrupt flags */
+ if ((val& INTERRUPTS_SUPPORTED)&&
+ (s->loc[locty].ints& INTERRUPTS_SUPPORTED)) {
+ s->set_irq(s->irq_opaque, s->irq, 0);
+ s->irq_pending = 0;
+ }
+ s->loc[locty].ints&= ~(val& INTERRUPTS_SUPPORTED);
+ } else
+ if (off == TPM_REG_STS) {
+ if (val& STS_COMMAND_READY) {
+ if (s->loc[locty].state == STATE_IDLE) {
+ s->loc[locty].sts = STS_COMMAND_READY;
+ s->loc[locty].state = STATE_READY;
+ tis_raise_irq(s, locty, INT_COMMAND_READY);
+ } else if (s->loc[locty].state == STATE_COMPLETION ||
+ s->loc[locty].state == STATE_EXECUTION ||
+ s->loc[locty].state == STATE_RECEPTION) {
+ /* abort currently running command */
+ tis_prep_abort(s, locty, locty);
+ }
+ }
+ if (val& STS_TPM_GO) {
+ s->offset = 0;
+
+ if (s->transmit != 1) {
+ qemu_mutex_lock(&s->tpm_thread_lock);
+ s->transmit = 1;
+ s->loc[locty].state = STATE_EXECUTION;
+ qemu_mutex_unlock(&s->tpm_thread_lock);
+ qemu_cond_signal(&s->tpm_thread_cond_data_avail);
+ }
+ }
+ if (val& STS_RESPONSE_RETRY) {
+ s->offset = 0;
+ }
+ } else if (off == TPM_REG_DATA_FIFO) {
+ /* data fifo */
+ if (s->loc[locty].state == STATE_IDLE ||
+ s->loc[locty].state == STATE_EXECUTION ||
+ s->loc[locty].state == STATE_COMPLETION) {
+ /* drop the byte */
+ } else {
+#ifdef TPM_DEBUG
+ printf("Byte to send to TPM: %02x at offset: %03d\n", val, s->offset);
+#endif
+ s->loc[locty].state = STATE_RECEPTION;
+ s->loc[locty].sts = STS_EXPECT | STS_VALID;
+
+ if (s->offset< TPM_MAX_PKT)
+ s->buf[s->offset++] = (uint8_t)val;
+
+ if (s->offset> 5) {
+ /* we have a packet length - see if we have all of it */
+ len = tpm_get_size_from_buffer(s->buf);
+ if (s->offset>= len) {
+#ifdef TPM_DEBUG
+ printf("We have a complete packet of %u bytes\n", len);
+#endif
+ s->offset = 0;
+
+ qemu_mutex_lock(&s->tpm_thread_lock);
+ s->transmit = 1;
+ qemu_mutex_unlock(&s->tpm_thread_lock);
+ qemu_cond_signal(&s->tpm_thread_cond_data_avail);
+ s->loc[locty].sts = STS_VALID;
+ }
+ }
+ }
+ }
+}
+
+
+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
+};
+
+/*
+ * Save the internal state of this interface for later resumption.
+ * Need to get any outstanding responses from the vTPM back, so
+ * this might delay the suspend for a while.
+ */
+static void tpm_save(QEMUFile* f,void* opaque)
+{
+ tpmState* s=(tpmState*)opaque;
+ uint8_t locty = s->active_loc;
+ int c;
+
+ /* need to wait for outstanding requests to complete */
+ if (s->loc[locty].state == STATE_EXECUTION) {
+ int repeats = 30; /* 30 seconds; really should be infty */
+ while (repeats> 0&&
+ !(s->loc[s->active_loc].sts& STS_DATA_AVAILABLE)) {
+ sleep(1);
+ }
+ }
+
+ close(s->tpmfd);
+
+ qemu_put_be32s(f,&s->offset);
+ qemu_put_buffer(f, s->buf, TPM_MAX_PKT);
+ qemu_put_8s(f,&s->active_loc);
+ qemu_put_8s(f,&s->irq_pending);
+ for (c = 0; c< NUM_LOCALITIES; c++) {
+ qemu_put_be32s(f,&s->loc[c].state);
+ qemu_put_8s(f,&s->loc[c].access);
+ qemu_put_8s(f,&s->loc[c].sts);
+ qemu_put_be32s(f,&s->loc[c].inte);
+ qemu_put_be32s(f,&s->loc[c].ints);
+ }
+ qemu_put_buffer(f, (uint8_t*) s->path, s->path_len);
+ qemu_put_be32s(f,&s->path_len);
+}
+
+
+/*
+ * load TIS interface state
+ */
+static int tpm_load(QEMUFile* f,void* opaque,int version_id)
+{
+ tpmState* s=(tpmState*)opaque;
+ int c;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f,&s->offset);
+ qemu_get_buffer(f, s->buf, TPM_MAX_PKT);
+ qemu_get_8s(f,&s->active_loc);
+ qemu_get_8s(f,&s->irq_pending);
+ for (c = 0; c< NUM_LOCALITIES; c++) {
+ qemu_get_be32s(f,&s->loc[c].state);
+ qemu_get_8s(f,&s->loc[c].access);
+ qemu_get_8s(f,&s->loc[c].sts);
+ qemu_get_be32s(f,&s->loc[c].inte);
+ qemu_get_be32s(f,&s->loc[c].ints);
+ }
+ qemu_get_be32s(f,&s->path_len);
+ qemu_get_buffer(f, (uint8_t*) s->path, s->path_len);
+
+ s->tpmfd = open(s->path, O_RDWR);
+
+ if(s->tpmfd< 0)
+ hw_error("Cannot open %s: %s (%i)\n", s->path, strerror(errno), errno);
+
+ qemu_mutex_init(&s->tpm_thread_lock);
+ qemu_cond_init(&s->tpm_thread_cond_data_avail);
+ qemu_mutex_init(&s->tpm_thread_abort_lock);
+
+ qemu_thread_create(&s->tpm_thread_id,&tpm_thread, s);
+
+ return 0;
+}
+
+
+typedef struct LPCtpmState {
+ tpmState tpm;
+ int mem;
+} LPCtpmState;
+
+
+/*
+ * initialize TIS interface
+ */
+
+struct IRQState {
+ qemu_irq_handler handler;
+ void *opaque;
+ int n;
+};
+
+void tpm_tis_init(qemu_irq irq);
+
+void tpm_tis_init(qemu_irq irq)
+{
+ LPCtpmState *d;
+ tpmState *s;
+ int c = 0;
+ int tpmfd = -1;
+
+ if(tpm_device == NULL) {
+ return;
+ }
+
+ tpmfd = open(tpm_device, O_RDWR);
+ if(tpmfd< 0) {
+ fprintf(stderr, "Cannot open %s: %s (%i)\n", tpm_device,
+ strerror(errno), errno);
+ return;
+ }
+
+ d = qemu_mallocz(sizeof(LPCtpmState));
+ d->mem = cpu_register_io_memory(tis_readfn, tis_writefn, d);
+
+ if (d->mem == -1) {
+ fprintf(stderr, "Failed to register IO memory TPM TIS\n");
+ return;
+ }
+
+ cpu_register_physical_memory(TIS_ADDR_BASE,
+ 0x1000 * NUM_LOCALITIES, d->mem);
+
+ /* initialize tpmState */
+ s =&d->tpm;
+
+ s->offset = 0;
+ s->transmit = 0;
+ s->aborting = 0;
+ s->active_loc = NO_LOCALITY;
+
+ while (c< NUM_LOCALITIES) {
+ s->loc[c].access = (1<< 7);
+ s->loc[c].sts = 0;
+ s->loc[c].inte = (1<< 3);
+ s->loc[c].ints = 0;
+ s->loc[c].state = STATE_IDLE;
+ c++;
+ }
+ s->set_irq = irq->handler;
+ s->irq_opaque = irq->opaque;
+ s->irq = irq->n;
+ s->aborting_locty = NO_LOCALITY;
+ s->tpmfd = tpmfd;
+ s->path_len = strlen(tpm_device);
+ s->path = tpm_device;
+
+ memset(s->buf,0,sizeof(s->buf));
+
+ qemu_mutex_init(&s->tpm_thread_lock);
+ qemu_cond_init(&s->tpm_thread_cond_data_avail);
+ qemu_mutex_init(&s->tpm_thread_abort_lock);
+
+ qemu_thread_create(&s->tpm_thread_id,&tpm_thread, s);
+
+ register_savevm(NULL, "tpm-tis", 0, 1, tpm_save, tpm_load, s);
+}
+
+
+int tpm_init(QemuOpts *opts) {
+ if(tpm_device == NULL) {
+ tpm_device = qemu_opt_get(opts, "dev");
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/* */
+/* optional verbose logging of data to/from tpm chip */
+/* */
+/****************************************************************************/
+#ifdef TPM_DEBUG
+static void showBuff(unsigned char *buff, const char *string)
+{
+ uint32_t i, len;
+
+ len = tpm_get_size_from_buffer(buff);
+ printf("%s length=%d\n", string, len);
+ for (i = 0; i< len; i++) {
+ if (i&& !(i % 16)) {
+ printf("\n");
+ }
+ printf("%.2X ", buff[i]);
+ }
+ printf("\n");
+}
+#endif
+
+