new file mode 100644
@@ -0,0 +1,6 @@
+#ifndef TPM_H
+#define TPM_H
+
+int qemu_tpm_add(QemuOpts *opts);
+
+#endif /* TPM_H */
new file mode 100644
@@ -0,0 +1,63 @@
+
+#include "qemu-option.h"
+
+#include "hw/tpm.h"
+#include "hw/tpm_int.h"
+
+
+static QLIST_HEAD(, TPMDriver) tpm_drivers =
+ QLIST_HEAD_INITIALIZER(tpm_drivers);
+
+TPMDriver *tpm_get_driver(const char *id)
+{
+ TPMDriver *drv;
+ QLIST_FOREACH(drv, &tpm_drivers, list) {
+ if (!strcmp(drv->id, id)) {
+ return drv;
+ }
+ }
+ return NULL;
+}
+
+
+typedef struct {
+ const char *name;
+ TPMDriver *(*open)(QemuOpts *opts);
+} TPMDriverTable;
+
+static const TPMDriverTable driver_table[] = {
+};
+
+int qemu_tpm_add(QemuOpts *opts) {
+ TPMDriver *drv = NULL;
+ int i;
+
+ if (qemu_opts_id(opts) == NULL) {
+ fprintf(stderr, "tpm: no id specified\n");
+ return -1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(driver_table); i++) {
+ if (strcmp(driver_table[i].name, qemu_opt_get(opts, "type")) == 0) {
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(driver_table)) {
+ fprintf(stderr, "tpm: backend type %s not found\n",
+ qemu_opt_get(opts, "type"));
+ return -1;
+ }
+
+ drv = driver_table[i].open(opts);
+
+ if (drv == NULL) {
+ return -1;
+ }
+
+ drv->id = qemu_strdup(qemu_opts_id(opts));
+
+ QLIST_INSERT_HEAD(&tpm_drivers, drv, list);
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,36 @@
+#ifndef TPM_INT_H
+#define TPM_INT_H
+
+
+#include <inttypes.h>
+#include "qemu-queue.h"
+#include "qemu-option.h"
+
+
+typedef struct TPMDriver TPMDriver;
+struct TPMDriver {
+ char *id;
+
+ uint8_t locty;
+ uint8_t *buf;
+
+ int (*send)(TPMDriver *drv, uint8_t locty, uint32_t len);
+ int (*recv)(TPMDriver *drv, uint8_t locty, uint32_t len);
+
+ QLIST_ENTRY(TPMDriver) list;
+};
+
+TPMDriver *tpm_get_driver(const char *id);
+
+#define DEBUG_TPM
+#ifdef DEBUG_TPM
+void show_buff(unsigned char *buff, const char *string);
+#define DPRINTF(fmt, ...) \
+ fprintf(stderr, "tpm_tis: %s: " fmt, __FUNCTION__, ##__VA_ARGS__)
+#define DSHOW_BUFF(buf, info) show_buff(buf, info)
+#else
+#define DPRINTF(fmt, ...)
+#define DSHOW_BUFF(buf, info)
+#endif
+
+#endif /* TPM_INT_H */
new file mode 100644
@@ -0,0 +1,711 @@
+/*
+ * 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 <stefanb@us.ibm.com>
+ * David Safford <safford@us.ibm.com>
+ *
+ * Author: Andreas Niederl <andreas.niederl@iaik.tugraz.at>
+ * 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/
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.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-timer.h"
+
+#include "hw/tpm_int.h"
+
+
+#define TPM_MAX_PKT 4096
+#define TPM_MAX_PATH 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
+
+/* locality data */
+typedef struct {
+ uint32_t state;
+ uint32_t inte;
+ uint32_t ints;
+ uint8_t access;
+ uint8_t sts;
+} TPMLocality;
+
+/* TPM device state */
+typedef struct {
+ ISADevice dev;
+ qemu_irq pic;
+ uint32_t irq;
+ uint8_t irq_pending;
+
+ QEMUTimer *poll_timer;
+ int poll_attempts;
+
+ uint8_t aborting; /* boolean value */
+ uint32_t offset;
+ uint8_t buf[TPM_MAX_PKT];
+
+ uint8_t active_locty;
+ uint8_t aborting_locty;
+ uint8_t next_locty;
+
+ TPMLocality loc[NUM_LOCALITIES];
+
+ char *backend;
+
+ TPMDriver *drv;
+} TPMState;
+
+
+/**********************************************************************
+ 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);
+}
+
+
+#ifdef DEBUG_TPM
+/****************************************************************************/
+/* */
+/* optional verbose logging of data to/from tpm chip */
+/* */
+/****************************************************************************/
+void show_buff(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
+
+
+/* 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) {
+ DPRINTF("Raising IRQ for flag %08x\n",irqmask);
+ qemu_set_irq(s->pic, 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_locty = s->next_locty;
+
+ /*
+ * 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 */
+}
+
+/**********************************************************************/
+
+/*
+ * Prepare the next interrupt for example after a command has
+ * been sent out for the purpose of receiving the response.
+ * Depending on how many interrupts (used for polling on the fd) have
+ * already been schedule, this function determines the delta in time
+ * to the next interrupt. This accomodates for commands that finish
+ * quickly.
+ */
+static void tis_prep_next_interrupt(TPMState *s)
+{
+ int64_t expiration;
+ int rate = 5; /* 5 times per second */
+
+ /*
+ * poll often at the beginning for quickly finished commands,
+ * then back off
+ */
+ if (s->poll_attempts < 5) {
+ rate = 20;
+ } else if (s->poll_attempts < 10) {
+ rate = 10;
+ }
+
+ expiration = qemu_get_clock(vm_clock) + (get_ticks_per_sec() / rate);
+ qemu_mod_timer(s->poll_timer, expiration);
+ s->poll_attempts++;
+}
+
+
+/*
+ * The polling routine called when the 'timer interrupt' fires.
+ * Tries to receive a command from the vTPM.
+ */
+static void tis_poll_timer(void *opaque)
+{
+ TPMState *s = opaque;
+ uint8_t locty = s->active_locty;
+
+ if (!IS_VALID_LOC(locty) ||
+ (!(s->loc[locty].inte & INT_ENABLED) &&
+ (s->aborting_locty != NO_LOCALITY))) {
+ /* no more interrupts requested, so no more polling needed */
+ qemu_del_timer(s->poll_timer);
+ }
+
+ if (s->aborting_locty != NO_LOCALITY) {
+ int n = s->drv->recv(s->drv, locty, TPM_MAX_PKT);
+ DPRINTF("Receiving for abort.\n");
+ if (n > 0) {
+ tis_abort(s);
+ DPRINTF("Abort is complete.\n");
+ } else {
+ tis_prep_next_interrupt(s);
+ }
+ } else if (IS_VALID_LOC(locty)) {
+ if (s->loc[locty].state == STATE_EXECUTION) {
+ int n = s->drv->recv(s->drv, locty, TPM_MAX_PKT);
+
+ /* poll for result */
+ if (n > 0) {
+ s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+ s->loc[locty].state = STATE_COMPLETION;
+ tis_raise_irq(s, locty, INT_DATA_AVAILABLE);
+ } else {
+ /* nothing received */
+ tis_prep_next_interrupt(s);
+ }
+ }
+ }
+}
+
+
+/*
+ * Try to receive a response from the backend TPM
+ */
+static void tis_attempt_receive(TPMState *s, uint8_t locty)
+{
+ /*
+ * Attempt to read from the backend TPM here if
+ * - not aborting a command
+ * - command has been sent and state is 'EXECUTION' now
+ * - no data are already available (data have already been read)
+ */
+ if (!IS_VALID_LOC(s->aborting_locty)) {
+ if (s->loc[locty].state == STATE_EXECUTION) {
+ if (0 == (s->loc[locty].sts & STS_DATA_AVAILABLE)){
+ int n = s->drv->recv(s->drv, locty, TPM_MAX_PKT);
+ if (n > 0) {
+ s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+ s->loc[locty].state = STATE_COMPLETION;
+ tis_raise_irq(s, locty, INT_DATA_AVAILABLE);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * read a byte of response data
+ */
+static uint32_t tpm_data_read(TPMState *s, uint8_t locty)
+{
+ uint32_t ret, len;
+
+ /* try to receive data, if none are there it is ok */
+ tis_attempt_receive(s, locty);
+
+ if (s->loc[locty].state != STATE_COMPLETION) {
+ DPRINTF("tpm_data_read with no data available!\n");
+ 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;
+ }
+ DPRINTF("tpm_data_read byte x%02x [%d]\n", ret, s->offset - 1);
+ return ret;
+}
+
+
+/* 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) {
+ /* start timer and inside the timer wait for the result */
+ s->poll_attempts = 0;
+ tis_prep_next_interrupt(s);
+ } 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 = opaque;
+ uint16_t offset = addr & 0xffc;
+ uint8_t shift = (addr & 0x3) * 8;
+ uint32_t val = 0;
+ uint8_t locty = locality_from_addr(addr);
+
+ switch (offset) {
+ case TPM_REG_ACCESS:
+ if (s->active_locty == locty) {
+ s->loc[locty].access |= (1 << 5);
+ } else {
+ s->loc[locty].access &= ~(1 << 5);
+ }
+ val = s->loc[locty].access;
+ break;
+ case TPM_REG_INT_ENABLE:
+ val = s->loc[locty].inte;
+ break;
+ case TPM_REG_INT_VECTOR:
+ val = s->irq;
+ break;
+ case TPM_REG_INT_STATUS:
+ tis_attempt_receive(s, locty);
+ val = s->loc[locty].ints;
+ break;
+ case TPM_REG_INTF_CAPABILITY:
+ val = CAPABILITIES_SUPPORTED;
+ break;
+ case TPM_REG_STS: /* status register */
+ tis_attempt_receive(s, locty);
+ val = (sizeof(s->buf) - s->offset) << 8 | s->loc[locty].sts;
+ break;
+ case TPM_REG_DATA_FIFO:
+ val = tpm_data_read(s, locty);
+ break;
+ case TPM_REG_DID_VID:
+ val = (TPM_DID << 16) | TPM_VID;
+ break;
+ case TPM_REG_RID:
+ val = TPM_RID;
+ break;
+ default:
+ DPRINTF("Bad register offset %#08x\n", offset);
+ break;
+ }
+
+ if (shift && locty == 0) {
+ val >>= shift;
+ }
+
+ DPRINTF("read(0x" TARGET_FMT_plx ") = %#08x\n", addr, val);
+
+ 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 = opaque;
+ uint16_t offset = addr & 0xfff;
+ uint8_t locty = locality_from_addr(addr);
+ uint32_t len;
+
+ DPRINTF("write(0x" TARGET_FMT_plx ") = %#08x\n", addr, val);
+
+ switch (offset) {
+ case TPM_REG_ACCESS:
+ if (val & ACCESS_ACTIVE_LOCALITY) {
+ /* give up locality if currently owned */
+ if (s->active_locty == 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_locty && IS_VALID_LOC(s->active_locty)) {
+ s->loc[s->active_locty].access |= ACCESS_BEEN_SEIZED;
+ s->loc[locty].access = ACCESS_TPM_REG_VALID_STS;
+ tis_prep_abort(s, s->active_locty, locty);
+ }
+ }
+ if (val & ACCESS_REQUEST_USE) {
+ if (IS_VALID_LOC(s->active_locty)) {
+ /* locality election */
+ s->loc[s->active_locty].access |= ACCESS_PENDING_REQUEST;
+ } else {
+ /* no locality active -> make this one active now */
+ s->loc[locty].access |= ACCESS_ACTIVE_LOCALITY;
+ s->active_locty = locty;
+ tis_raise_irq(s, locty, INT_LOCALITY_CHANGED);
+ }
+ }
+ break;
+ case TPM_REG_INT_ENABLE:
+ s->loc[locty].inte = (val & (INT_ENABLED | (0x3 << 3) |
+ INTERRUPTS_SUPPORTED));
+ break;
+ case TPM_REG_INT_VECTOR:
+ /*
+ * isa_init_irq would exit qemu
+ * if the requested IRQ is already assigned
+ */
+ /*isa_init_irq(&s->dev, &s->pic, val);*/
+ DPRINTF("writes to TPM_INT_VECTOR not supported!\n");
+ break;
+ case TPM_REG_INT_STATUS:
+ /* clearing of interrupt flags */
+ if ((val & INTERRUPTS_SUPPORTED) &&
+ (s->loc[locty].ints & INTERRUPTS_SUPPORTED)) {
+ qemu_set_irq(s->pic, 0);
+ s->irq_pending = 0;
+ }
+ s->loc[locty].ints &= ~(val & INTERRUPTS_SUPPORTED);
+ break;
+ case 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) {
+ if (s->loc[locty].state == STATE_RECEPTION) {
+ int n = s->drv->send(s->drv, locty,
+ tpm_get_size_from_buffer(s->buf));
+ if (n > 0) {
+ /* sending of data was successful */
+ s->offset = 0;
+ s->loc[locty].state = STATE_EXECUTION;
+ if (s->loc[locty].inte &
+ (INT_ENABLED | INT_DATA_AVAILABLE)) {
+ s->poll_attempts = 0;
+ tis_prep_next_interrupt(s);
+ }
+ }
+ }
+ }
+ if (val & STS_RESPONSE_RETRY) {
+ s->offset = 0;
+ }
+ break;
+ case 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 {
+ DPRINTF("Byte to send to TPM: %02x at offset: %03d\n", val,
+ s->offset);
+ s->loc[locty].state = STATE_RECEPTION;
+
+ 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 (len > s->offset) {
+ s->loc[locty].sts = STS_EXPECT | STS_VALID;
+ } else {
+ s->loc[locty].sts = STS_VALID;
+ }
+ }
+ }
+ break;
+ default:
+ DPRINTF("Bad register offset %#08x\n", offset);
+ break;
+ }
+}
+
+
+static CPUReadMemoryFunc * const tis_readfn[3] = {
+ tis_mem_readl,
+ tis_mem_readl,
+ tis_mem_readl
+};
+
+static CPUWriteMemoryFunc * const tis_writefn[3] = {
+ tis_mem_writel,
+ tis_mem_writel,
+ tis_mem_writel
+};
+
+/*
+ * Need to get any outstanding responses from the TPM back, so
+ * this might delay the suspend for a while.
+ */
+static void tpm_pre_save(void *opaque)
+{
+ TPMState *s = opaque;
+ uint8_t locty = s->active_locty;
+
+ /* 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_locty].sts & STS_DATA_AVAILABLE)) {
+ sleep(1);
+ }
+ }
+}
+
+
+static void tpm_reset(TPMState *s)
+{
+ int c;
+
+ s->offset = 0;
+ s->aborting = 0;
+ s->active_locty = NO_LOCALITY;
+
+ for (c = 0; c < NUM_LOCALITIES; c++) {
+ 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;
+ }
+ s->aborting_locty = NO_LOCALITY;
+
+ memset(s->buf, 0, sizeof(s->buf));
+}
+
+static const VMStateDescription vmstate_loc = {
+ .name = "loc",
+ .version_id = 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 = {
+ .name = "tpm",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = tpm_pre_save,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(irq, 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, 0,
+ vmstate_loc, TPMLocality),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/*
+ * initialize TIS interface
+ */
+static int tpm_init(ISADevice *dev)
+{
+ TPMState *s = DO_UPCAST(TPMState, dev, dev);
+ int isairq = 11;
+ TPMDriver *drv = NULL;
+ int iomem;
+ int i;
+
+ if (!s->backend) {
+ fprintf(stderr, "tpm: no backend selected!\n");
+ return -1;
+ }
+ drv = tpm_get_driver(s->backend);
+ if (drv == NULL) {
+ fprintf(stderr, "tpm: backend id %s not found\n", s->backend);
+ return -1;
+ }
+
+ tpm_reset(s);
+
+ drv->buf = s->buf;
+ s->drv = drv;
+
+ vmstate_register(&dev->qdev, 1, &vmstate_tpm, s);
+ for(i = 0; i < NUM_LOCALITIES; i++) {
+ vmstate_register(&dev->qdev, 1, &vmstate_loc, &s->loc[i]);
+ }
+
+ isa_init_irq(dev, &s->pic, isairq);
+ s->irq = isairq;
+
+ iomem = cpu_register_io_memory(tis_readfn, tis_writefn, s,
+ DEVICE_LITTLE_ENDIAN);
+ cpu_register_physical_memory(TIS_ADDR_BASE,
+ 0x1000 * NUM_LOCALITIES, iomem);
+
+ s->poll_timer = qemu_new_timer(vm_clock, tis_poll_timer, s);
+
+ return 0;
+}
+
+static void qdev_tpm_reset(DeviceState *dev)
+{
+ TPMState *s = DO_UPCAST(TPMState, dev.qdev, dev);
+ tpm_reset(s);
+}
+
+static ISADeviceInfo tpm_info = {
+ .init = tpm_init,
+ .qdev.name = "tpm",
+ .qdev.desc = "TPM TIS Interface",
+ .qdev.size = sizeof (TPMState),
+ .qdev.vmsd = &vmstate_tpm,
+ .qdev.reset = qdev_tpm_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_STRING("backend", TPMState, backend),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void tpm_register (void)
+{
+ isa_qdev_register(&tpm_info);
+}
+device_init (tpm_register)
This implementation is based on the TPM 1.2 interface for virtualized TPM devices from the Xen-4.0.0 ioemu-qemu-xen fork. A backend driver infrastructure is provided to be able to use different device backends. Signed-off-by: Andreas Niederl <andreas.niederl@iaik.tugraz.at> --- hw/tpm.h | 6 + hw/tpm_backend.c | 63 +++++ hw/tpm_int.h | 36 +++ hw/tpm_tis.c | 711 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 816 insertions(+), 0 deletions(-) create mode 100644 hw/tpm.h create mode 100644 hw/tpm_backend.c create mode 100644 hw/tpm_int.h create mode 100644 hw/tpm_tis.c