From: Anthony Liguori <anthony@codemonkey.ws>
To: Andreas Niederl <andreas.niederl@iaik.tugraz.at>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH 1/3] Add TPM 1.2 host device passthrough interface
Date: Wed, 25 Aug 2010 11:46:19 -0500 [thread overview]
Message-ID: <4C7548DB.7040209@codemonkey.ws> (raw)
In-Reply-To: <1282753982-1761-1-git-send-email-andreas.niederl@iaik.tugraz.at>
On 08/25/2010 11:33 AM, Andreas Niederl wrote:
> 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 separate thread is used for I/O to the host TPM device because the Linux TPM
> driver does not allow for non-blocking I/O.
>
> Signed-off-by: Andreas Niederl<andreas.niederl@iaik.tugraz.at>
>
Interesting. A few high level comments.
You need to make the device qdev-aware and VMState aware.
It would be better to use the generic thread pool series floating around
on the list than to introduce a device specific thread.
The command line interface should separate our the host passthrough from
a future vTPM interface. IOW, something like a -tpm
host,path=/dev/tpm0,id=vtpm0 -device tpm,driver=vtpm0
Regards,
Anthony Liguori
> ---
> 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<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/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
> +
> +
>
next prev parent reply other threads:[~2010-08-25 16:46 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-08-25 16:33 [Qemu-devel] [PATCH 1/3] Add TPM 1.2 host device passthrough interface Andreas Niederl
2010-08-25 16:33 ` [Qemu-devel] [PATCH 2/3] Add command line options for TPM interface Andreas Niederl
2010-08-25 19:15 ` Blue Swirl
2010-08-25 16:33 ` [Qemu-devel] [PATCH 3/3] seabios: Add DSDT entry for an emulated TPM 1.2 device Andreas Niederl
2010-08-25 16:46 ` Anthony Liguori [this message]
2010-08-25 19:46 ` [Qemu-devel] [PATCH 1/3] Add TPM 1.2 host device passthrough interface Blue Swirl
2010-08-25 20:46 ` malc
2010-08-26 17:22 ` Blue Swirl
2010-08-26 21:44 ` malc
2010-08-26 21:57 ` Anthony Liguori
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4C7548DB.7040209@codemonkey.ws \
--to=anthony@codemonkey.ws \
--cc=andreas.niederl@iaik.tugraz.at \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).