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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.