From: Bernhard Kauer <kauer@os.inf.tu-dresden.de>
To: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH] SVM support
Date: Thu, 30 Aug 2007 17:26:07 +0200 [thread overview]
Message-ID: <20070830152607.GB4197@chrom.inf.tu-dresden.de> (raw)
In-Reply-To: <46D6DA17.7050304@suse.de>
[-- Attachment #1: Type: text/plain, Size: 1026 bytes --]
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
[-- Attachment #2: qemu_tpm_tis.diff --]
[-- Type: text/x-diff, Size: 26778 bytes --]
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 <stefanb@us.ibm.com>
+ * David Safford <safford@us.ibm.com>
+ * Bernhard Kauer <kauer@tudos.org>
+ *
+ * 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;
+}
next prev parent reply other threads:[~2007-08-30 15:26 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-08-30 14:54 [Qemu-devel] [PATCH] SVM support Alexander Graf
2007-08-30 15:26 ` Bernhard Kauer [this message]
2007-08-30 16:01 ` Alexander Graf
2007-09-02 14:48 ` Alexander Graf
2007-09-13 2:49 ` Thiemo Seufer
2007-09-13 15:27 ` Alexander Graf
2007-09-17 8:08 ` J. Mayer
2007-09-17 10:02 ` Alexander Graf
2007-09-17 14:16 ` Jocelyn Mayer
2007-09-18 1:02 ` Alexander Graf
2007-09-18 1:24 ` Alexander Graf
2007-09-18 10:09 ` J. Mayer
2007-09-18 16:14 ` Blue Swirl
2007-09-17 14:07 ` Thiemo Seufer
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=20070830152607.GB4197@chrom.inf.tu-dresden.de \
--to=kauer@os.inf.tu-dresden.de \
--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).