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; +}