On Thu, Aug 30, 2007 at 04:54:15PM +0200, Alexander Graf wrote:
> Hi,
> 
> after a lot of struggling I finally got everything working smoothly
> (special thanks to Fabrice and Avi), so I believe this patch is ready to
> be taken upstream.
> CC_DST was never a problem, as everything I did in the eflags area
> already worked. I managed to clobber the segment attributes though, so
> that was the real problem here.
> Nevertheless there is still a lot of functionality missing, whereas none
> of that is used in kvm by now, so that works already.
> 
> So there are still missing parts that I will list here:
> 
> - NPT support
> - Everything related to device virtualisation
> - The "Secure" part of the extension (would need TPM emulation for that)

I backported Xen's TPM emulation to Qemu for exactly that purpose.
I also started to implement skinit, but did not had the time to
finish that work, yet. Please note that this patch requires
a two line patch to the tpm-emulator to understand the localities.


        Bernhard Kauer
Index: Makefile.target
===================================================================
RCS file: /sources/qemu/qemu/Makefile.target,v
retrieving revision 1.191
diff -u -r1.191 Makefile.target
--- Makefile.target	31 Jul 2007 23:44:21 -0000	1.191
+++ Makefile.target	7 Aug 2007 03:43:28 -0000
@@ -429,6 +429,7 @@
 VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
 VL_OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
 VL_OBJS+= usb-uhci.o smbus_eeprom.o vmmouse.o vmware_vga.o
+VL_OBJS+= tpm_tis.o
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)
Index: hw/pc.c
===================================================================
RCS file: /sources/qemu/qemu/hw/pc.c,v
retrieving revision 1.81
diff -u -r1.81 pc.c
--- hw/pc.c	6 Jun 2007 16:26:13 -0000	1.81
+++ hw/pc.c	7 Aug 2007 03:43:37 -0000
@@ -914,6 +917,9 @@
     if (i440fx_state) {
         i440fx_init_memory_mappings(i440fx_state);
     }
+    
+    tpm_tis_init();
+    
 #if 0
     /* ??? Need to figure out some way for the user to
        specify SCSI devices.  */
--- /dev/null	2007-08-06 15:49:45.580307540 +0200
+++ hw/tpm_tis.c	2007-08-07 05:39:41.000000000 +0200
@@ -0,0 +1,890 @@
+/*
+ * tpm_tis.c - QEMU emulator for a 1.2 TPM with TIS interface
+ *
+ * Copyright (C) 2006 IBM Corporation
+ *
+ * Author: Stefan Berger <[EMAIL PROTECTED]>
+ *         David Safford <[EMAIL PROTECTED]>
+ *         Bernhard Kauer <[EMAIL PROTECTED]>
+ *
+ * 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 <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+#include "vl.h"
+
+
+#define DEBUG_TPM
+#define logfile stderr
+#define LOCAL_SOCKET_PATH      "tpmd"
+/* if the connection to the vTPM should be closed after a successfully
+   received response; set to '0' to allow keeping the connection */
+#define FORCE_CLOSE      0
+
+
+
+#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_HASH_END              0x20
+#define TPM_REG_DATA_FIFO             0x24
+#define TPM_REG_HASH_START            0x28
+#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_RECEPTION,
+  STATE_RECEPTION_DONE,
+  STATE_EXECUTION,
+  STATE_COMPLETION,
+  STATE_COMPLETION_NODATA,
+  STATE_HASHING,
+};
+
+#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
+
+
+
+/* local data structures */
+
+typedef struct TPMTx {
+  int fd;
+} tpmTx;
+
+typedef struct TPMBuffer {
+    uint8_t buf[TPM_MAX_PKT];
+} __attribute__((packed)) tpmBuffer;
+
+/* locality data */
+typedef struct TPMLocal {
+    uint32_t state;
+    uint8_t access;
+    uint32_t inte;
+    uint32_t ints;
+} tpmLoc;
+
+/* overall state of the TPM interface; 's' marks as save upon suspension */
+typedef struct TPMState {
+    uint32_t offset;            /* s */
+    tpmBuffer buffer;           /* s */
+    uint8_t active_loc;         /* s */
+    uint8_t aborting_locty;
+    uint8_t next_locty;
+    uint8_t irq_pending;        /* s */
+    tpmLoc loc[NUM_LOCALITIES]; /* s */
+    QEMUTimer *poll_timer;
+    SetIRQFunc *set_irq;
+    void *irq_opaque;
+    int irq;
+    int poll_attempts;
+    tpmTx tpmTx;
+} tpmState;
+
+
+/* local prototypes */
+static void TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty);
+static int TPM_Receive(tpmState *s, tpmBuffer *buffer);
+static void TPM_Transfer(tpmState *s, void *buffer);
+static void tis_poll_timer(void *opaque);
+static void tis_prep_next_interrupt(tpmState *s);
+static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask);
+static void tis_attempt_receive(tpmState *s, uint8_t locty);
+
+/* transport layer functions: local sockets */
+static int create_local_socket(tpmState *s);
+static int write_local_socket(tpmState *s, const tpmBuffer *);
+static int read_local_socket(tpmState *s, tpmBuffer *);
+static void close_local_socket(tpmState *s, int force);
+static int has_channel_local_socket(tpmState *s);
+
+
+/**********************************************************************
+ 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);
+}
+
+
+/**********************************************************************
+    low-level transmission layer methods
+ *********************************************************************/
+
+/*
+ * the 'open' method that creates the filedescriptor for communicating
+ */
+static
+int
+create_local_socket(tpmState *s)
+{
+    if (!has_channel_local_socket(s)) {
+        s->tpmTx.fd = socket(PF_LOCAL, SOCK_STREAM, 0);
+
+        if (has_channel_local_socket(s)) {
+            struct sockaddr_un addr;
+            memset(&addr, 0x0, sizeof(addr));
+            addr.sun_family = AF_LOCAL;
+            strcpy(addr.sun_path, LOCAL_SOCKET_PATH);
+            if (connect(s->tpmTx.fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) 
+                close_local_socket(s, 1);
+	    else 
+	      {
+                /* put filedescriptor in non-blocking mode for polling */
+                int flags = fcntl(s->tpmTx.fd, F_GETFL);
+                fcntl(s->tpmTx.fd, F_SETFL, flags | O_NONBLOCK);
+		return 1;
+	      }
+        }
+	return 0;
+    }
+    return 1;
+}
+
+/*
+ * the 'write' method for sending requests to the vTPM
+ */
+static int write_local_socket(tpmState *s, const tpmBuffer *buffer)
+{
+    return write(s->tpmTx.fd,  buffer->buf, tpm_get_size_from_buffer(buffer->buf));
+}
+
+/*
+ * the 'read' method for receiving of responses from the TPM
+ */
+static int read_local_socket(tpmState *s, tpmBuffer *buffer)
+{
+    return read(s->tpmTx.fd, buffer->buf, TPM_MAX_PKT);
+}
+
+/*
+ * the 'close' method
+ * shut down communication with the vTPM
+ * 'force' = 1 indicates that the socket *must* be closed
+ * 'force' = 0 indicates that a connection may be maintained
+ */
+static
+void
+close_local_socket(tpmState *s, int force)
+{
+  if (force && (s->tpmTx.fd >=0)) 
+    {
+      close(s->tpmTx.fd);
+      s->tpmTx.fd = -1;
+    }
+}
+
+/*
+ * the 'has_channel' method that checks whether there's a communication
+ * channel with the vTPM
+ */
+static int has_channel_local_socket(tpmState *s)
+{
+    return (s->tpmTx.fd > 0);
+}
+
+/**********************************************************************/
+
+/*
+ * 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)
+        return 0xff;
+
+    len = tpm_get_size_from_buffer(s->buffer.buf);
+    ret = s->buffer.buf[s->offset++];
+    if (s->offset >= len)
+        s->loc[locty].state = STATE_COMPLETION_NODATA;
+    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 DEBUG_TPM
+            fprintf(logfile,"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;
+
+    /*
+     * 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;
+        tis_raise_irq(s, s->aborting_locty, INT_COMMAND_READY);
+    }
+    else
+      {
+	s->loc[s->aborting_locty].access &= ~ACCESS_ACTIVE_LOCALITY;
+	s->loc[s->aborting_locty].state = STATE_IDLE;
+      }
+
+    /* 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->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 */
+
+    qemu_del_timer(s->poll_timer);
+}
+
+/*
+ * 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 */
+
+    /*
+     * 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 &&
+        has_channel_local_socket(s)) {
+        /* start timer and inside the timer wait for the result */
+        s->poll_attempts = 0;
+        tis_prep_next_interrupt(s);
+    } else {
+        tis_abort(s);
+    }
+}
+
+
+/*
+ * Try to receive a response from the vTPM
+ */
+static void tis_attempt_receive(tpmState *s, uint8_t locty)
+{
+  /*
+   * Attempt to read from the vTPM here if
+   * - not aborting a command
+   * - command has been sent and state is 'EXECUTION' now
+   */
+  if (!IS_VALID_LOC(s->aborting_locty)) 
+    TPM_Receive(s, &s->buffer);
+}
+
+/*
+ * 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 = 0xffffff;
+    uint8_t locty = locality_from_addr(addr);
+
+    switch (offset)
+      {
+      case TPM_REG_ACCESS:
+	assert((s->active_loc != locty) || (s->loc[locty].access & ACCESS_ACTIVE_LOCALITY));
+        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:
+        tis_attempt_receive(s, locty);
+        val = (sizeof(s->buffer.buf) - s->offset) << 8 | STS_VALID;
+	switch (s->loc[locty].state)
+	  {
+	  case STATE_READY:
+	    val |= STS_COMMAND_READY;
+	      break;
+	  case STATE_RECEPTION:
+	    val |= STS_EXPECT;
+	    break;
+	  case STATE_COMPLETION:
+	    val |= STS_DATA_AVAILABLE;
+	    break;
+	  }
+	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;
+      }
+
+    if (shift)
+        val >>= shift;
+
+
+#ifdef DEBUG_TPM
+    fprintf(logfile," read(%08llx) = %08x state %x\n",
+            addr,
+            val,
+	    s->loc[locty].state);
+#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;
+    uint8_t locty = locality_from_addr(addr);
+    int c;
+    uint32_t len;
+
+
+#ifdef DEBUG_TPM
+    fprintf(logfile,"write(%08llx) = %08x\n",
+            addr,
+            val);
+#endif
+    
+    switch (addr & 0xfff)
+      {
+      case TPM_REG_ACCESS:      
+	switch (val)
+	  {
+	  case ACCESS_ACTIVE_LOCALITY:
+	    /* give up locality if currently owned */
+	    if (s->active_loc == locty) {
+	      uint8_t newlocty = NO_LOCALITY;
+	      s->loc[locty].access &= ~ACCESS_REQUEST_USE;
+	      /* anybody wants the locality ? */
+	      for (c = NUM_LOCALITIES - 1; c >= 0; c--) {
+		if (s->loc[c].access & ACCESS_REQUEST_USE) {
+		  s->loc[c].access |= ACCESS_TPM_REG_VALID_STS;
+		  s->loc[c].access &= ~ACCESS_REQUEST_USE;
+		  newlocty = c;
+		  break;
+		}
+	      }
+	      tis_prep_abort(s, locty, newlocty);
+	    }
+	    break;
+	  case ACCESS_BEEN_SEIZED:
+	    s->loc[locty].access &= ~ACCESS_BEEN_SEIZED;
+	    break;
+	  case 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_REQUEST_USE;
+	      tis_prep_abort(s, s->active_loc, locty);
+	    }
+	    break;
+	  case 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);
+	      }
+	    break;
+	  default:
+	    /* more than one or reserved bit set -> ignore request */
+	    break;
+	  }
+	break;
+      case TPM_REG_INT_ENABLE:
+	s->loc[locty].inte = (val & (INT_ENABLED | (0x3 << 3) | INTERRUPTS_SUPPORTED));
+	break;
+      case 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);
+	break;
+      case TPM_REG_STS:
+        if (val & STS_COMMAND_READY) {
+	  if (s->loc[locty].state == STATE_IDLE) {
+	      s->loc[locty].state = STATE_READY;
+	      tis_raise_irq(s, locty, INT_COMMAND_READY);
+            } else if (s->loc[locty].state == STATE_RECEPTION ||
+		       s->loc[locty].state == STATE_RECEPTION_DONE ||
+                       s->loc[locty].state == STATE_EXECUTION  ||
+		       s->loc[locty].state == STATE_COMPLETION ||
+		       s->loc[locty].state == STATE_COMPLETION_NODATA
+                       ) {
+                /* abort currently running command */
+                tis_prep_abort(s, locty, locty);
+            }
+        }
+        if (val & STS_TPM_GO)
+	  TPM_Send(s, &s->buffer, locty);
+        if (val & STS_RESPONSE_RETRY) {
+            s->offset = 0;
+        }
+	break;
+      case TPM_REG_HASH_END:
+	{
+	  char resetpcrs_cmd[] = {0x00, 0xC1, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x03, 0x00, 0x00, 0x1e};
+	  char sha1completeextend_cmd[] = {0x00, 0xC1, 0x00, 0x00, 0x00, 18+s->offset, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, s->offset};
+	  // call TPM_PCRReset(17)
+	  memcpy(s->buffer.buf, resetpcrs_cmd, sizeof(resetpcrs_cmd));
+	  TPM_Transfer(s, s->buffer.buf);
+
+	  // call SHA1_FINISH_Extend(17)
+	  memmove(s->buffer.buf+sizeof(sha1completeextend_cmd), s->buffer.buf, s->offset);
+	  memcpy(s->buffer.buf, sha1completeextend_cmd, sizeof(sha1completeextend_cmd));
+	  TPM_Transfer(s, s->buffer.buf);
+	  s->offset = 0;
+	  s->active_loc = -1;
+	  s->loc[4].state = STATE_IDLE;
+	}
+	break;
+      case TPM_REG_DATA_FIFO:
+	switch (s->loc[locty].state)
+	  {
+	  case STATE_READY:
+	    s->loc[locty].state = STATE_RECEPTION;
+	  case STATE_RECEPTION:
+	    if (s->offset < sizeof(s->buffer.buf))
+	      s->buffer.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->buffer.buf);
+	      if (len <= s->offset)
+		s->loc[locty].state = STATE_RECEPTION_DONE;
+	    }
+	    break;
+	  case STATE_HASHING:
+	    s->buffer.buf[s->offset++] = (uint8_t) val;
+	    if (65 == s->offset)
+	      {
+		fprintf(logfile, "got byte: %x\n", val);
+		char sha1update_cmd[] = {0x00, 0xC1, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x50};
+		memmove(s->buffer.buf+sizeof(sha1update_cmd), s->buffer.buf, s->offset);
+		memcpy(s->buffer.buf, sha1update_cmd, sizeof(sha1update_cmd));
+		TPM_Transfer(s, s->buffer.buf);
+		s->offset = 0;
+	      }
+	    break;
+	  default:
+	    /* drop the byte */
+	    break;
+	  }
+	break;
+      case TPM_REG_HASH_START:
+	if ((locty != 4) || (s->active_loc!=0xff))
+	  break;
+	else
+	  {
+	    char sha1start_cmd[] = {0x00, 0xC1, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xA0};
+	    s->active_loc = 4;
+	    s->loc[s->active_loc].state = STATE_HASHING;
+	    s->offset = 0;
+	    memcpy(s->buffer.buf, sha1start_cmd, sizeof(sha1start_cmd));
+	    assert(ntohl((long *)(s->buffer.buf+10)) >= 64);
+	    TPM_Transfer(s, s->buffer.buf);
+	    break;
+	  }	
+      default:
+	/* ignore write request */
+	break;
+      }
+}
+
+/*
+ * 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 */
+
+    fprintf(logfile,"tis_prep_next_interrupt()\n");
+    /*
+       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) + (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_loc;
+
+    fprintf(logfile,"tis_poll_timer()\n");
+
+    if (!IS_VALID_LOC(locty) ||
+        (!(s->loc[locty].inte & INT_ENABLED) &&
+          (s->aborting_locty != NO_LOCALITY)) ||
+        !has_channel_local_socket(s)) {
+        /* no more interrupts requested, so no more polling needed */
+        qemu_del_timer(s->poll_timer);
+    }
+
+    if (!has_channel_local_socket(s)) {
+      if (s->aborting_locty != NO_LOCALITY)
+	tis_abort(s);
+      return;
+    }
+
+    if (s->aborting_locty != NO_LOCALITY) {
+        int n = TPM_Receive(s, &s->buffer);
+#ifdef DEBUG_TPM
+        fprintf(logfile,"Receiving for abort.\n");
+#endif
+        if (n > 0) {
+            close_local_socket(s, FORCE_CLOSE);
+            tis_abort(s);
+#ifdef DEBUG_TPM
+            fprintf(logfile,"Abort is complete.\n");
+#endif
+        } else {
+            tis_prep_next_interrupt(s);
+        }
+    } else if (IS_VALID_LOC(locty)) {
+      /* poll for result */
+      fprintf(logfile,"poll for result.\n");
+      if (0 > TPM_Receive(s, &s->buffer))
+	tis_prep_next_interrupt(s);    
+    }
+}
+
+
+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;
+    int c;
+
+    /* need to wait for outstanding requests to complete */
+    if (has_channel_local_socket(s)) {
+        int repeats = 30; /* 30 seconds; really should be infty */
+        while (repeats > 0 &&
+               (s->loc[s->active_loc].state == STATE_EXECUTION)) {
+	  if (TPM_Receive(s, &s->buffer) > 0) 
+	    break;
+	  sleep(1);
+        }
+    }
+
+    close_local_socket(s, 1);
+    qemu_put_be32s(f,&s->offset);
+    qemu_put_buffer(f, s->buffer.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_be32s(f, &s->loc[c].inte);
+        qemu_put_be32s(f, &s->loc[c].ints);
+    }
+}
+
+/*
+ * 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->buffer.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_be32s(f, &s->loc[c].inte);
+        qemu_get_be32s(f, &s->loc[c].ints);
+    }
+
+    return 0;
+}
+
+
+
+/*
+ * initialize TIS interface
+ */
+void tpm_tis_init(SetIRQFunc *set_irq, void *opaque, int irq)
+{
+  int mem;
+  tpmState *s;
+
+  if (!((s = qemu_mallocz(sizeof(*s)))))
+    return;
+  if (-1 == ((mem = cpu_register_io_memory(0, tis_readfn, tis_writefn, s))))
+    return;
+
+  cpu_register_physical_memory(TIS_ADDR_BASE, 0x1000 * NUM_LOCALITIES, mem);
+
+
+  /* initialize tpmState */
+  s->offset = 0;
+  s->active_loc = NO_LOCALITY;
+  s->aborting_locty = NO_LOCALITY;
+
+  {
+    int i;
+    for (i=0; i < NUM_LOCALITIES; i++)
+      {
+	s->loc[i].access = (1 << 7);
+	s->loc[i].inte = (1 << 3);
+	s->loc[i].ints = 0;
+	s->loc[i].state = STATE_IDLE;
+      }
+  }
+  s->poll_timer = qemu_new_timer(vm_clock, tis_poll_timer, s);
+  s->set_irq = set_irq;
+  s->irq_opaque = opaque;
+  s->irq = irq;
+  s->tpmTx.fd = -1;  
+
+  register_savevm("tpm-tis", 0, 1, tpm_save, tpm_load, s);
+}
+
+
+/****************************************************************************/
+/* Transmit request to TPM and read Response                                */
+/****************************************************************************/
+
+const static unsigned char tpm_failure[] = {
+    0x00, 0x00,
+    0x00, 0x00, 0x00, 0x0a,
+    0x00, 0x00, 0x00, 0x09
+};
+
+
+/*
+ * Send a TPM request.
+ */
+static void TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty)
+{
+    uint32_t size = tpm_get_size_from_buffer(buffer->buf);
+
+    if (s->loc[locty].state != STATE_RECEPTION_DONE)
+      /*ignore all toGo requests*/
+      return;
+
+    /* transmit the locality in the highest 3 bits */
+    buffer->buf[0] &= 0x1f;
+    buffer->buf[0] |= (locty << 5);
+    s->offset = 0;
+
+    /* check or try to establish a connection to the vTPM and send buffer */
+    if (create_local_socket(s) && (write_local_socket(s, buffer) == size))
+      {
+	/* sending of data was successful */
+	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);
+	}
+      }
+    else
+      {
+	unsigned char tag = buffer->buf[1];
+
+	fprintf(logfile,"TPM_Send() failure\n");
+        /* produce a failure response from the TPM */
+        memcpy(buffer->buf, tpm_failure, sizeof(tpm_failure));
+        buffer->buf[1] = tag + 3;
+	s->loc[locty].state = STATE_COMPLETION;
+      }
+}
+
+
+/*
+ * Try to receive data from the file descriptor. Since it is in
+ * non-blocking mode it is possible that no data are actually received -
+ * whatever calls this function needs to try again later.
+ */
+static int TPM_Receive(tpmState *s, tpmBuffer *buffer)
+{
+    int off = 0;
+
+    if (s->loc[s->active_loc].state != STATE_EXECUTION)
+      return 0;
+
+    if (has_channel_local_socket(s))
+      off = read_local_socket(s, buffer);
+
+    if (off < 0)
+      /* EAGAIN is set in errno due to non-blocking mode */
+      return -1;
+
+    if (IS_VALID_LOC(s->active_loc)) 
+      {
+	s->loc[s->active_loc].state = STATE_COMPLETION;
+	tis_raise_irq(s, s->active_loc, INT_DATA_AVAILABLE);
+      }
+    close_local_socket(s, off == 0 ? 1 : FORCE_CLOSE);
+
+    /* assuming reading in one chunk for now */
+    return off;
+}
+
+
+static
+void
+TPM_Transfer(tpmState *s, void *buffer)
+{
+  fprintf(logfile, "TPM_Transfer() %x\n", s->offset);
+  assert(s->loc[s->active_loc].state == STATE_HASHING);
+  s->loc[s->active_loc].state = STATE_RECEPTION_DONE;
+  TPM_Send(s, buffer, s->active_loc);
+  while (s->loc[s->active_loc].state == STATE_EXECUTION) {
+    if (TPM_Receive(s, buffer) >= 0) 
+      break;
+    sleep(1);
+  }
+  s->loc[s->active_loc].state = STATE_HASHING;
+}

Reply via email to