diff options
Diffstat (limited to 'tools/ioemu/hw')
-rw-r--r-- | tools/ioemu/hw/fdc.c | 2 | ||||
-rw-r--r-- | tools/ioemu/hw/ide.c | 4 | ||||
-rw-r--r-- | tools/ioemu/hw/ne2000.c | 35 | ||||
-rw-r--r-- | tools/ioemu/hw/pc.c | 3 | ||||
-rw-r--r-- | tools/ioemu/hw/pci.c | 24 | ||||
-rw-r--r-- | tools/ioemu/hw/piix4acpi.c | 12 | ||||
-rw-r--r-- | tools/ioemu/hw/piix_pci.c | 18 | ||||
-rw-r--r-- | tools/ioemu/hw/rtl8139.c | 8 | ||||
-rw-r--r-- | tools/ioemu/hw/serial.c | 126 | ||||
-rw-r--r-- | tools/ioemu/hw/tpm_tis.c | 1114 | ||||
-rw-r--r-- | tools/ioemu/hw/vga.c | 13 | ||||
-rw-r--r-- | tools/ioemu/hw/xen_platform.c | 14 |
12 files changed, 1326 insertions, 47 deletions
diff --git a/tools/ioemu/hw/fdc.c b/tools/ioemu/hw/fdc.c index 3890ace120..1d16cd6518 100644 --- a/tools/ioemu/hw/fdc.c +++ b/tools/ioemu/hw/fdc.c @@ -898,7 +898,7 @@ static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction) fdctrl->data_len = fdctrl->fifo[8]; } else { int tmp; - fdctrl->data_len = 128 << fdctrl->fifo[5]; + fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]); tmp = (cur_drv->last_sect - ks + 1); if (fdctrl->fifo[0] & 0x80) tmp += cur_drv->last_sect; diff --git a/tools/ioemu/hw/ide.c b/tools/ioemu/hw/ide.c index 8b070cc0cc..94d5beaad5 100644 --- a/tools/ioemu/hw/ide.c +++ b/tools/ioemu/hw/ide.c @@ -557,9 +557,9 @@ static void ide_atapi_identify(IDEState *s) padstr((uint8_t *)(p + 23), QEMU_VERSION, 8); /* firmware version */ padstr((uint8_t *)(p + 27), "QEMU CD-ROM", 40); /* model */ put_le16(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */ - put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */ + put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA supported */ put_le16(p + 53, 3); /* words 64-70, 54-58 valid */ - put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */ + put_le16(p + 63, 0x07); /* mdma0-2 supported */ put_le16(p + 64, 1); /* PIO modes */ put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */ put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */ diff --git a/tools/ioemu/hw/ne2000.c b/tools/ioemu/hw/ne2000.c index e23c6df069..5fe383fb2a 100644 --- a/tools/ioemu/hw/ne2000.c +++ b/tools/ioemu/hw/ne2000.c @@ -137,6 +137,7 @@ typedef struct NE2000State { uint8_t curpag; uint8_t mult[8]; /* multicast mask array */ int irq; + int tainted; PCIDevice *pci_dev; VLANClientState *vc; uint8_t macaddr[6]; @@ -226,6 +227,27 @@ static int ne2000_can_receive(void *opaque) #define MIN_BUF_SIZE 60 +static inline int ne2000_valid_ring_addr(NE2000State *s, unsigned int addr) +{ + addr <<= 8; + return addr < s->stop && addr >= s->start; +} + +static inline int ne2000_check_state(NE2000State *s) +{ + if (!s->tainted) + return 0; + + if (s->start >= s->stop || s->stop > NE2000_MEM_SIZE) + return -EINVAL; + + if (!ne2000_valid_ring_addr(s, s->curpag)) + return -EINVAL; + + s->tainted = 0; + return 0; +} + static void ne2000_receive(void *opaque, const uint8_t *buf, int size) { NE2000State *s = opaque; @@ -239,6 +261,12 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size) printf("NE2000: received len=%d\n", size); #endif + if (ne2000_check_state(s)) + return; + + if (!ne2000_valid_ring_addr(s, s->boundary)) + return; + if (s->cmd & E8390_STOP || ne2000_buffer_full(s)) return; @@ -359,9 +387,11 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) switch(offset) { case EN0_STARTPG: s->start = val << 8; + s->tainted = 1; break; case EN0_STOPPG: s->stop = val << 8; + s->tainted = 1; break; case EN0_BOUNDARY: s->boundary = val; @@ -406,6 +436,7 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) break; case EN1_CURPAG: s->curpag = val; + s->tainted = 1; break; case EN1_MULT ... EN1_MULT + 7: s->mult[offset - EN1_MULT] = val; @@ -509,7 +540,7 @@ static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, { addr &= ~1; /* XXX: check exact behaviour if not even */ if (addr < 32 || - (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE - 2)) { cpu_to_le32wu((uint32_t *)(s->mem + addr), val); } } @@ -539,7 +570,7 @@ static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) { addr &= ~1; /* XXX: check exact behaviour if not even */ if (addr < 32 || - (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE - 2)) { return le32_to_cpupu((uint32_t *)(s->mem + addr)); } else { return 0xffffffff; diff --git a/tools/ioemu/hw/pc.c b/tools/ioemu/hw/pc.c index 5b5fd42abf..234c4722bb 100644 --- a/tools/ioemu/hw/pc.c +++ b/tools/ioemu/hw/pc.c @@ -875,6 +875,9 @@ static void pc_init1(uint64_t ram_size, int vga_ram_size, char *boot_device, } } + if (has_tpm_device()) + tpm_tis_init(&pic_set_irq_new, isa_pic, 11); + kbd_init(); DMA_init(0); #ifdef HAS_AUDIO diff --git a/tools/ioemu/hw/pci.c b/tools/ioemu/hw/pci.c index 77737c8615..cea81f7b7f 100644 --- a/tools/ioemu/hw/pci.c +++ b/tools/ioemu/hw/pci.c @@ -221,16 +221,23 @@ uint32_t pci_default_read_config(PCIDevice *d, uint32_t address, int len) { uint32_t val; + switch(len) { - case 1: - val = d->config[address]; - break; - case 2: - val = le16_to_cpu(*(uint16_t *)(d->config + address)); - break; default: case 4: - val = le32_to_cpu(*(uint32_t *)(d->config + address)); + if (address <= 0xfc) { + val = le32_to_cpu(*(uint32_t *)(d->config + address)); + break; + } + /* fall through */ + case 2: + if (address <= 0xfe) { + val = le16_to_cpu(*(uint16_t *)(d->config + address)); + break; + } + /* fall through */ + case 1: + val = d->config[address]; break; } return val; @@ -333,7 +340,8 @@ void pci_default_write_config(PCIDevice *d, d->config[addr] = val; } - addr++; + if (++addr > 0xff) + break; val >>= 8; } diff --git a/tools/ioemu/hw/piix4acpi.c b/tools/ioemu/hw/piix4acpi.c index 7b75d01c64..aabd5ca7e9 100644 --- a/tools/ioemu/hw/piix4acpi.c +++ b/tools/ioemu/hw/piix4acpi.c @@ -398,8 +398,16 @@ void pci_piix4_acpi_init(PCIBus *bus, int devfn) pci_conf[0x0e] = 0x00; pci_conf[0x3d] = 0x01; /* Hardwired to PIRQA is used */ - pci_register_io_region((PCIDevice *)d, 4, 0x10, - PCI_ADDRESS_SPACE_IO, acpi_map); + /* PMBA POWER MANAGEMENT BASE ADDRESS, hardcoded to 0x1f40 + * to make shutdown work for IPF, due to IPF Guest Firmware + * will enumerate pci devices. + * + * TODO: if Guest Firmware or Guest OS will change this PMBA, + * More logic will be added. + */ + pci_conf[0x40] = 0x41; /* Special device-specific BAR at 0x40 */ + pci_conf[0x41] = 0x1f; + acpi_map(d, 0, 0x1f40, 0x10, PCI_ADDRESS_SPACE_IO); acpi_reset(d); } diff --git a/tools/ioemu/hw/piix_pci.c b/tools/ioemu/hw/piix_pci.c index 051e082263..497d66898c 100644 --- a/tools/ioemu/hw/piix_pci.c +++ b/tools/ioemu/hw/piix_pci.c @@ -338,10 +338,14 @@ static void pci_bios_init_device(PCIDevice *d) break; case 0x0680: if (vendor_id == 0x8086 && device_id == 0x7113) { - /* PIIX4 ACPI PM */ - pci_config_writew(d, 0x20, 0x0000); /* NO smb bus IO enable in PIIX4 */ + /* + * PIIX4 ACPI PM. + * Special device with special PCI config space. No ordinary BARs. + */ + pci_config_writew(d, 0x20, 0x0000); // No smb bus IO enable pci_config_writew(d, 0x22, 0x0000); - goto default_map; + pci_config_writew(d, 0x3c, 0x0009); // Hardcoded IRQ9 + pci_config_writew(d, 0x3d, 0x0001); } break; case 0x0300: @@ -394,14 +398,6 @@ static void pci_bios_init_device(PCIDevice *d) pic_irq = pci_irqs[pin]; pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq); } - - if (class== 0x0680&& vendor_id == 0x8086 && device_id == 0x7113) { - // PIIX4 ACPI PM - pci_config_writew(d, 0x20, 0x0000); // NO smb bus IO enable in PIIX4 - pci_config_writew(d, 0x22, 0x0000); - pci_config_writew(d, 0x3c, 0x0009); // Hardcodeed IRQ9 - pci_config_writew(d, 0x3d, 0x0001); - } } /* diff --git a/tools/ioemu/hw/rtl8139.c b/tools/ioemu/hw/rtl8139.c index c704ab8360..77e3c6d9fb 100644 --- a/tools/ioemu/hw/rtl8139.c +++ b/tools/ioemu/hw/rtl8139.c @@ -1999,12 +1999,12 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer allocated space %d\n", s->cplus_txbuffer_len)); } - while (s->cplus_txbuffer && s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len) + if (s->cplus_txbuffer && s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len) { - s->cplus_txbuffer_len += CP_TX_BUFFER_SIZE; - s->cplus_txbuffer = realloc(s->cplus_txbuffer, s->cplus_txbuffer_len); + free(s->cplus_txbuffer); + s->cplus_txbuffer = NULL; - DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer space changed to %d\n", s->cplus_txbuffer_len)); + DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer space exceeded: %d\n", s->cplus_txbuffer_offset + txsize)); } if (!s->cplus_txbuffer) diff --git a/tools/ioemu/hw/serial.c b/tools/ioemu/hw/serial.c index f36beb209f..b99e774eae 100644 --- a/tools/ioemu/hw/serial.c +++ b/tools/ioemu/hw/serial.c @@ -22,6 +22,9 @@ * THE SOFTWARE. */ #include "vl.h" +#include <sys/time.h> +#include <time.h> +#include <assert.h> //#define DEBUG_SERIAL @@ -70,6 +73,11 @@ #define UART_LSR_OE 0x02 /* Overrun error indicator */ #define UART_LSR_DR 0x01 /* Receiver data ready */ +/* Maximum retries for a single byte transmit. */ +#define WRITE_MAX_SINGLE_RETRIES 3 +/* Maximum retries for a sequence of back-to-back unsuccessful transmits. */ +#define WRITE_MAX_TOTAL_RETRIES 10 + struct SerialState { uint8_t divider; uint8_t rbr; /* receive register */ @@ -90,6 +98,19 @@ struct SerialState { int last_break_enable; target_ulong base; int it_shift; + + /* + * If a character transmitted via UART cannot be written to its + * destination immediately we remember it here and retry a few times via + * a polling timer. + * - write_single_retries: Number of write retries for current byte. + * - write_total_retries: Number of write retries for back-to-back + * unsuccessful transmits. + */ + int write_single_retries; + int write_total_retries; + char write_chr; + QEMUTimer *write_retry_timer; }; static void serial_update_irq(SerialState *s) @@ -140,10 +161,98 @@ static void serial_update_parameters(SerialState *s) #endif } +/* Rate limit serial requests so that e.g. grub on a serial console + doesn't kill dom0. Simple token bucket. If we get some actual + data from the user, instantly refil the bucket. */ + +/* How long it takes to generate a token, in microseconds. */ +#define TOKEN_PERIOD 1000 +/* Maximum and initial size of token bucket */ +#define TOKENS_MAX 100000 + +static int tokens_avail; + +static void serial_get_token(void) +{ + static struct timeval last_refil_time; + static int started; + + assert(tokens_avail >= 0); + if (!tokens_avail) { + struct timeval delta, now; + int generated; + + if (!started) { + gettimeofday(&last_refil_time, NULL); + tokens_avail = TOKENS_MAX; + started = 1; + return; + } + retry: + gettimeofday(&now, NULL); + delta.tv_sec = now.tv_sec - last_refil_time.tv_sec; + delta.tv_usec = now.tv_usec - last_refil_time.tv_usec; + if (delta.tv_usec < 0) { + delta.tv_usec += 1000000; + delta.tv_sec--; + } + assert(delta.tv_usec >= 0 && delta.tv_sec >= 0); + if (delta.tv_usec < TOKEN_PERIOD) { + struct timespec ts; + /* Wait until at least one token is available. */ + ts.tv_sec = TOKEN_PERIOD / 1000000; + ts.tv_nsec = (TOKEN_PERIOD % 1000000) * 1000; + while (nanosleep(&ts, &ts) < 0 && errno == EINTR) + ; + goto retry; + } + generated = (delta.tv_sec * 1000000) / TOKEN_PERIOD; + generated += + ((delta.tv_sec * 1000000) % TOKEN_PERIOD + delta.tv_usec) / TOKEN_PERIOD; + assert(generated > 0); + + last_refil_time.tv_usec += (generated * TOKEN_PERIOD) % 1000000; + last_refil_time.tv_sec += last_refil_time.tv_usec / 1000000; + last_refil_time.tv_usec %= 1000000; + last_refil_time.tv_sec += (generated * TOKEN_PERIOD) / 1000000; + if (generated > TOKENS_MAX) + generated = TOKENS_MAX; + tokens_avail = generated; + } + tokens_avail--; +} + +static void serial_chr_write(void *opaque) +{ + SerialState *s = opaque; + + /* Cancel any outstanding retry if this is a new byte. */ + qemu_del_timer(s->write_retry_timer); + + /* Retry every 100ms for 300ms total. */ + if (qemu_chr_write(s->chr, &s->write_chr, 1) == -1) { + s->write_total_retries++; + if (s->write_single_retries++ >= WRITE_MAX_SINGLE_RETRIES) + fprintf(stderr, "serial: write error\n"); + else if (s->write_total_retries <= WRITE_MAX_TOTAL_RETRIES) { + qemu_mod_timer(s->write_retry_timer, + qemu_get_clock(vm_clock) + ticks_per_sec / 10); + return; + } + } else { + s->write_total_retries = 0; /* if successful then reset counter */ + } + + /* Success: Notify guest that THR is empty. */ + s->thr_ipending = 1; + s->lsr |= UART_LSR_THRE; + s->lsr |= UART_LSR_TEMT; + serial_update_irq(s); +} + static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) { SerialState *s = opaque; - unsigned char ch; addr &= 7; #ifdef DEBUG_SERIAL @@ -159,12 +268,9 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) s->thr_ipending = 0; s->lsr &= ~UART_LSR_THRE; serial_update_irq(s); - ch = val; - qemu_chr_write(s->chr, &ch, 1); - s->thr_ipending = 1; - s->lsr |= UART_LSR_THRE; - s->lsr |= UART_LSR_TEMT; - serial_update_irq(s); + s->write_chr = val; + s->write_single_retries = 0; + serial_chr_write(s); } break; case 1: @@ -245,9 +351,11 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr) ret = s->mcr; break; case 5: + serial_get_token(); ret = s->lsr; break; case 6: + serial_get_token(); if (s->mcr & UART_MCR_LOOP) { /* in loopback, the modem output pins are connected to the inputs */ @@ -296,12 +404,14 @@ static int serial_can_receive1(void *opaque) static void serial_receive1(void *opaque, const uint8_t *buf, int size) { SerialState *s = opaque; + tokens_avail = TOKENS_MAX; serial_receive_byte(s, buf[0]); } static void serial_event(void *opaque, int event) { SerialState *s = opaque; + tokens_avail = TOKENS_MAX; if (event == CHR_EVENT_BREAK) serial_receive_break(s); } @@ -356,6 +466,7 @@ SerialState *serial_init(SetIRQFunc *set_irq, void *opaque, s->lsr = UART_LSR_TEMT | UART_LSR_THRE; s->iir = UART_IIR_NO_INT; s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; + s->write_retry_timer = qemu_new_timer(vm_clock, serial_chr_write, s); register_savevm("serial", base, 1, serial_save, serial_load, s); @@ -443,6 +554,7 @@ SerialState *serial_mm_init (SetIRQFunc *set_irq, void *opaque, s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; s->base = base; s->it_shift = it_shift; + s->write_retry_timer = qemu_new_timer(vm_clock, serial_chr_write, s); register_savevm("serial", base, 1, serial_save, serial_load, s); diff --git a/tools/ioemu/hw/tpm_tis.c b/tools/ioemu/hw/tpm_tis.c new file mode 100644 index 0000000000..a4d07bc67f --- /dev/null +++ b/tools/ioemu/hw/tpm_tis.c @@ -0,0 +1,1114 @@ +/* + * 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> + * + * 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 "vl.h" + +//#define DEBUG_TPM + +#define TPM_MAX_PKT 4096 + +#define VTPM_BAD_INSTANCE (uint32_t)0xffffffff + +#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 + +/* 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 + +/* local data structures */ + +typedef struct TPMTx { + int fd[2]; +} tpmTx; + +typedef struct TPMBuffer { + uint8_t instance[4]; /* instance number in network byte order */ + uint8_t buf[TPM_MAX_PKT]; +} __attribute__((packed)) tpmBuffer; + +/* 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 { + 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; + uint32_t vtpm_instance; /* vtpm inst. number; determined from xenstore*/ + int Transmitlayer; + tpmTx tpmTx; +} tpmState; + + +/* local prototypes */ +static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg); +static int TPM_Receive(tpmState *s, tpmBuffer *buffer); +static uint32_t vtpm_instance_from_xenstore(void); +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 close_vtpm_channel(tpmState *s, int force); +static void open_vtpm_channel(tpmState *s); +static void tis_attempt_receive(tpmState *s, uint8_t locty); + +/* transport layer functions: local sockets */ +static int create_local_socket(tpmState *s, uint32_t vtpm_instance); +static int write_local_socket(tpmState *s, const tpmBuffer *); +static int read_local_socket(tpmState *s, tpmBuffer *); +static int close_local_socket(tpmState *s, int force); +static int has_channel_local_socket(tpmState *s); +#define LOCAL_SOCKET_PATH "/var/vtpm/vtpm_all.socket" + + +#define NUM_TRANSPORTS 1 + +struct vTPM_transmit { + int (*open) (tpmState *s, uint32_t vtpm_instance); + int (*write) (tpmState *s, const tpmBuffer *); + int (*read) (tpmState *s, tpmBuffer *); + int (*close) (tpmState *s, int); + int (*has_channel) (tpmState *s); +} vTPMTransmit[NUM_TRANSPORTS] = { + { .open = create_local_socket, + .write = write_local_socket, + .read = read_local_socket, + .close = close_local_socket, + .has_channel = has_channel_local_socket, + } +}; + + +#define IS_COMM_WITH_VTPM(s) \ + ((s)->Transmitlayer >= 0 && \ + vTPMTransmit[(s)->Transmitlayer].has_channel(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 void tpm_initialize_instance(tpmState *s, uint32_t instance) +{ + s->buffer.instance[0] = (instance >> 24) & 0xff; + s->buffer.instance[1] = (instance >> 16) & 0xff; + s->buffer.instance[2] = (instance >> 8) & 0xff; + s->buffer.instance[3] = (instance >> 0) & 0xff; +} + +/* + * open communication channel with a vTPM + */ +static void open_vtpm_channel(tpmState *s) +{ + int idx; + /* search a usable transmit layer */ + for (idx = 0; idx < NUM_TRANSPORTS; idx++) { + if (1 == vTPMTransmit[idx].open(s, s->vtpm_instance)) { + /* found one */ + s->Transmitlayer = idx; + break; + } + } +} + +/* + * close the communication channel with the vTPM + */ +static inline void close_vtpm_channel(tpmState *s, int force) +{ + if (1 == vTPMTransmit[s->Transmitlayer].close(s, force)) { + s->Transmitlayer = -1; + } +} + +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 + * only one is needed for reading and writing + */ +static int create_local_socket(tpmState *s, uint32_t vtpm_instance) +{ + int success = 1; + if (s->tpmTx.fd[0] < 0) { + s->tpmTx.fd[0] = 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[0], + (struct sockaddr *)&addr, + sizeof(addr)) != 0) { + close_local_socket(s, 1); + success = 0; + } else { + /* put filedescriptor in non-blocking mode for polling */ + int flags = fcntl(s->tpmTx.fd[0], F_GETFL); + fcntl(s->tpmTx.fd[0], F_SETFL, flags | O_NONBLOCK); + } +#ifdef DEBUG_TPM + if (success) + fprintf(logfile,"Successfully connected using local socket " + LOCAL_SOCKET_PATH ".\n"); + else + fprintf(logfile,"Could not connect to local socket " + LOCAL_SOCKET_PATH ".\n"); +#endif + } else { + success = 0; + } + } + return success; +} + +/* + * the 'write' method for sending requests to the vTPM + * four bytes with the vTPM instance number are prepended to each request + */ +static int write_local_socket(tpmState *s, const tpmBuffer *buffer) +{ + uint32_t size = tpm_get_size_from_buffer(buffer->buf); + int len; + + len = write(s->tpmTx.fd[0], + buffer->instance, + sizeof(buffer->instance) + size); + if (len == sizeof(buffer->instance) + size) { + return len; + } else { + return -1; + } +} + +/* + * the 'read' method for receiving of responses from the TPM + * this function expects that four bytes with the instance number + * are received from the vTPM + */ +static int read_local_socket(tpmState *s, tpmBuffer *buffer) +{ + int off; +#ifdef DEBUG_TPM + fprintf(logfile, "Reading from fd %d\n", s->tpmTx.fd[0]); +#endif + off = read(s->tpmTx.fd[0], + buffer->instance, + sizeof(buffer->instance)+TPM_MAX_PKT); +#ifdef DEBUG_TPM + fprintf(logfile, "Read %d bytes\n", off); +#endif + return off; +} + +/* + * 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 int close_local_socket(tpmState *s, int force) +{ + if (force) { + close(s->tpmTx.fd[0]); +#ifdef DEBUG_TPM + fprintf(logfile,"Closed connection with fd %d\n",s->tpmTx.fd[0]); +#endif + s->tpmTx.fd[0] = -1; + return 1; /* socket was closed */ + } +#ifdef DEBUG_TPM + fprintf(logfile,"Keeping connection with fd %d\n",s->tpmTx.fd[0]); +#endif + return 0; +} + +/* + * 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] > 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].sts = STS_VALID ; + s->offset = 0; + } +#ifdef DEBUG_TPM + fprintf(logfile,"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 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; + 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 */ + + 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 && + IS_COMM_WITH_VTPM(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 + * - no data are already available (data have already been read) + * - there's a communication path to the vTPM established + */ + if (!IS_VALID_LOC(s->aborting_locty)) { + if (s->loc[locty].state == STATE_EXECUTION) { + if (0 == (s->loc[locty].sts & STS_DATA_AVAILABLE)){ + if (IS_COMM_WITH_VTPM(s)) { + int n = TPM_Receive(s, &s->buffer); + if (n > 0) { + s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE; + s->loc[locty].state = STATE_COMPLETION; + close_vtpm_channel(s, FORCE_CLOSE); + tis_raise_irq(s, locty, INT_DATA_AVAILABLE); + } + } + } + } + } +} + +/* + * 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 (s->active_loc == locty) { + s->loc[locty].access |= (1 << 5); + } else { + s->loc[locty].access &= ~(1 << 5); + } + val = s->loc[locty].access; + } 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) { + tis_attempt_receive(s, locty); + val = s->loc[locty].ints; + } else + if (offset == TPM_REG_INTF_CAPABILITY) { + val = CAPABILITIES_SUPPORTED; + } else + if (offset == TPM_REG_STS) { /* status register */ + tis_attempt_receive(s, locty); + val = (sizeof(s->buffer.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) + val >>= shift; + +#ifdef DEBUG_TPM + fprintf(logfile," read(%08x) = %08x\n", + 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); + int n, c; + uint32_t len; + +#ifdef DEBUG_TPM + fprintf(logfile,"write(%08x) = %08x\n", + 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 ? */ + 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); + } + } + 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) { + n = TPM_Send(s, &s->buffer,"tpm_data_write"); + if (n > 0) { + /* sending of data was successful */ + s->offset = 0; + 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); + } + } + } + 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 + fprintf(logfile,"Byte to send to TPM: %02x\n", val); +#endif + s->loc[locty].state = 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].sts = STS_EXPECT | STS_VALID; + } else { + s->loc[locty].sts = STS_VALID; + } + } + } + } +} + +/* + * 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 */ + + /* + 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=(tpmState*)opaque; + uint8_t locty = s->active_loc; + + if (!IS_VALID_LOC(locty) || + (!(s->loc[locty].inte & INT_ENABLED) && + (s->aborting_locty != NO_LOCALITY)) || + !IS_COMM_WITH_VTPM(s)) { + /* no more interrupts requested, so no more polling needed */ + qemu_del_timer(s->poll_timer); + } + + if (!IS_COMM_WITH_VTPM(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_vtpm_channel(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)) { + if (s->loc[locty].state == STATE_EXECUTION) { + /* poll for result */ + int n = TPM_Receive(s, &s->buffer); + if (n > 0) { + s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE; + s->loc[locty].state = STATE_COMPLETION; + close_vtpm_channel(s, FORCE_CLOSE); + tis_raise_irq(s, locty, INT_DATA_AVAILABLE); + } else { + /* nothing received */ + 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 (IS_COMM_WITH_VTPM(s)) { + int repeats = 30; /* 30 seconds; really should be infty */ + while (repeats > 0 && + !(s->loc[s->active_loc].sts & STS_DATA_AVAILABLE)) { + int n = TPM_Receive(s, &s->buffer); + if (n > 0) { + if (IS_VALID_LOC(s->active_loc)) { + s->loc[s->active_loc].sts = STS_VALID | STS_DATA_AVAILABLE; + } + /* close the connection with the vTPM for good */ + close_vtpm_channel(s, 1); + break; + } + sleep(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_8s(f, &s->loc[c].sts); + 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_8s(f, &s->loc[c].sts); + qemu_get_be32s(f, &s->loc[c].inte); + qemu_get_be32s(f, &s->loc[c].ints); + } + + /* need to be able to get the instance number from the xenstore */ + s->vtpm_instance = vtpm_instance_from_xenstore(); + if (s->vtpm_instance == VTPM_BAD_INSTANCE) + return -EINVAL; + tpm_initialize_instance(s, s->vtpm_instance); + + return 0; +} + + +typedef struct LPCtpmState { + tpmState tpm; + int mem; +} LPCtpmState; + + +/* + * initialize TIS interface + */ +void tpm_tis_init(SetIRQFunc *set_irq, void *opaque, int irq) +{ + LPCtpmState *d; + tpmState *s; + int c = 0; + uint32_t vtpm_in; + + vtpm_in = vtpm_instance_from_xenstore(); + /* no valid vtpm instance -> no device */ + if (vtpm_in == VTPM_BAD_INSTANCE) + return; + + d = qemu_mallocz(sizeof(LPCtpmState)); + d->mem = cpu_register_io_memory(0, tis_readfn, tis_writefn, d); + + if (d->mem == -1) { + return; + } + + cpu_register_physical_memory(TIS_ADDR_BASE, + 0x1000 * NUM_LOCALITIES, d->mem); + + /* initialize tpmState */ + s = &d->tpm; + + s->offset = 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->poll_timer = qemu_new_timer(vm_clock, tis_poll_timer, s); + s->set_irq = set_irq; + s->irq_opaque = opaque; + s->irq = irq; + s->vtpm_instance = vtpm_in; + s->Transmitlayer = -1; + s->tpmTx.fd[0] = -1; + s->tpmTx.fd[1] = -1; + + tpm_initialize_instance(s, s->vtpm_instance); + memset(s->buffer.buf,0,sizeof(s->buffer.buf)); + + register_savevm("tpm-tis", 0, 1, tpm_save, tpm_load, s); +} + +/****************************************************************************/ +/* optional verbose logging of data to/from vtpm */ +/****************************************************************************/ +#ifdef DEBUG_TPM +static void showBuff(unsigned char *buff, char *string) +{ + uint32_t i, len; + + len = tpm_get_size_from_buffer(buff); + fprintf(logfile,"%s length = %d\n", string, len); + for (i = 0; i < len; i++) { + if (i && !(i % 16)) { + fprintf(logfile,"\n"); + } + fprintf(logfile,"%.2X ", buff[i]); + } + fprintf(logfile,"\n"); +} +#endif + +/****************************************************************************/ +/* 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 int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg) +{ + int len; + uint32_t size = tpm_get_size_from_buffer(buffer->buf); + + /* try to establish a connection to the vTPM */ + if ( !IS_COMM_WITH_VTPM(s)) { + open_vtpm_channel(s); + } + + if ( !IS_COMM_WITH_VTPM(s)) { + unsigned char tag = buffer->buf[1]; + + /* there's a failure response from the TPM */ + memcpy(buffer->buf, tpm_failure, sizeof(tpm_failure)); + buffer->buf[1] = tag + 3; + if (IS_VALID_LOC(s->active_loc)) { + s->loc[s->active_loc].sts = STS_DATA_AVAILABLE | STS_VALID; + } +#ifdef DEBUG_TPM + fprintf(logfile,"No TPM running!\n"); +#endif + /* the request went out ok. */ + return sizeof(buffer->instance) + size; + } + +#ifdef DEBUG_TPM + showBuff(buffer->buf, "To TPM"); +#endif + + len = vTPMTransmit[s->Transmitlayer].write(s, buffer); + if (len < 0) { + s->Transmitlayer = -1; + } + return len; +} + +/* + * 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; + + off = vTPMTransmit[s->Transmitlayer].read(s, buffer); + + if (off < 0) { + /* EAGAIN is set in errno due to non-blocking mode */ + return -1; + } + + if (off == 0) { +#ifdef DEBUG_TPM + fprintf(logfile,"TPM GONE? errno=%d\n",errno); +#endif + close_vtpm_channel(s, 1); + /* pretend that data are available */ + if (IS_VALID_LOC(s->active_loc)) { + s->loc[s->active_loc].sts = STS_VALID | STS_DATA_AVAILABLE; + s->loc[s->active_loc].state = STATE_COMPLETION; + tis_raise_irq(s, s->active_loc, INT_DATA_AVAILABLE); + } + return -1; + } + +#ifdef DEBUG_TPM + if (off > sizeof(buffer->instance ) + 6) { + uint32_t size = tpm_get_size_from_buffer(buffer->buf); + if (size + sizeof(buffer->instance) != off) { + fprintf(logfile,"TPM: Packet size is bad! %d != %d\n", + size + sizeof(buffer->instance), + off); + } else { + uint32_t ret; + showBuff(buffer->buf, "From TPM"); + ret = (buffer->buf[8])*256 + buffer->buf[9]; + if (ret) + fprintf(logfile,"Receive failed with error %d\n", ret); + else + fprintf(logfile,"Receive succeeded. Got response of length %d (=%d)\n", + size, off); + } + } +#endif + + /* assuming reading in one chunk for now */ + return off; +} + + +/**************************************************************************** + Helper functions for reading data from the xenstore such as + reading virtual TPM instance information + ****************************************************************************/ +int has_tpm_device(void) +{ + int ret = 0; + struct xs_handle *handle = xs_daemon_open(); + if (handle) { + ret = xenstore_domain_has_devtype(handle, "vtpm"); + xs_daemon_close(handle); + } + return ret; +} + + +/* + * Wait until hotplug scripts have finished then read the vTPM instance + * number from the xenstore. + */ +static uint32_t vtpm_instance_from_xenstore(void) +{ + unsigned int num; + uint32_t number = VTPM_BAD_INSTANCE; + int end = 0; + char *token = "tok"; + int subscribed = 0; + int ctr = 0; + fd_set readfds; + + struct xs_handle *handle = xs_daemon_open(); + + FD_ZERO(&readfds); + + if (handle) { + char **e = xenstore_domain_get_devices(handle, "vtpm", &num); + int fd = xs_fileno(handle); + FD_SET(fd, &readfds); + if (e) { + do { + struct timeval tv = { + .tv_sec = 30, + .tv_usec = 0, + }; + /* need to make sure that the hotplug scripts have finished */ + char *status = xenstore_read_hotplug_status(handle, + "vtpm", + e[0]); + if (status) { + if (!strcmp(status, "connected")) { + char *inst = xenstore_backend_read_variable(handle, + "vtpm", + e[0], + "instance"); + if (1 != (sscanf(inst,"%d",&number))) + number = VTPM_BAD_INSTANCE; + free(inst); + } else { + fprintf(logfile, + "bad status '%s' from vtpm hotplug\n", + status); + } + free(status); + end = 1; + } else { + /* no status, yet */ + int rc; + unsigned int nr; + char **f; + + if (!subscribed) { + rc = xenstore_subscribe_to_hotplug_status(handle, + "vtpm", + e[0], + token); + if (rc != 0) + break; + subscribed = 1; + } + rc = select(fd+1, &readfds, NULL, NULL, &tv); + /* get what's available -- drain the fd */ + f = xs_read_watch(handle, &nr); + ctr++; + free(f); + if (ctr > 2) + end = 1; + } + } while (end == 0); + free(e); + } + if (subscribed) { + /* clean up */ + xenstore_unsubscribe_from_hotplug_status(handle, + "vtpm", + e[0], + token); + } + xs_daemon_close(handle); + } + if (number == VTPM_BAD_INSTANCE) + fprintf(logfile, "no valid vtpm instance"); + else + fprintf(logfile,"vtpm instance:%d\n",number); + return number; +} diff --git a/tools/ioemu/hw/vga.c b/tools/ioemu/hw/vga.c index bbe3e582a5..6b9317f048 100644 --- a/tools/ioemu/hw/vga.c +++ b/tools/ioemu/hw/vga.c @@ -1463,14 +1463,15 @@ void check_sse2(void) */ static void vga_draw_graphic(VGAState *s, int full_update) { - int y1, y, update, page_min, page_max, linesize, y_start, double_scan, mask; + int y1, y, update, linesize, y_start, double_scan, mask; int width, height, shift_control, line_offset, bwidth; ram_addr_t page0, page1; int disp_width, multi_scan, multi_run; uint8_t *d; uint32_t v, addr1, addr; vga_draw_line_func *vga_draw_line; - + ram_addr_t page_min, page_max; + full_update |= update_basic_params(s); s->get_resolution(s, &width, &height); @@ -1561,8 +1562,8 @@ static void vga_draw_graphic(VGAState *s, int full_update) addr1 = (s->start_addr * 4); bwidth = width * 4; y_start = -1; - page_min = 0x7fffffff; - page_max = -1; + page_min = 0; + page_max = 0; d = s->ds->data; linesize = s->ds->linesize; y1 = 0; @@ -1592,9 +1593,9 @@ static void vga_draw_graphic(VGAState *s, int full_update) if (update) { if (y_start < 0) y_start = y; - if (page0 < page_min) + if (page_min == 0 || page0 < page_min) page_min = page0; - if (page1 > page_max) + if (page_max == 0 || page1 > page_max) page_max = page1; vga_draw_line(s, d, s->vram_ptr + addr, width); if (s->cursor_draw_line) diff --git a/tools/ioemu/hw/xen_platform.c b/tools/ioemu/hw/xen_platform.c index 8b319858cb..a0e9f1e397 100644 --- a/tools/ioemu/hw/xen_platform.c +++ b/tools/ioemu/hw/xen_platform.c @@ -97,7 +97,8 @@ struct pci_config_header { uint8_t bist; /* Built in self test */ uint32_t base_address_regs[6]; uint32_t reserved1; - uint32_t reserved2; + uint16_t subsystem_vendor_id; + uint16_t subsystem_id; uint32_t rom_addr; uint32_t reserved3; uint32_t reserved4; @@ -116,16 +117,21 @@ void pci_xen_platform_init(PCIBus *bus) d = pci_register_device(bus, "xen-platform", sizeof(PCIDevice), -1, NULL, NULL); pch = (struct pci_config_header *)d->config; - pch->vendor_id = 0xfffd; - pch->device_id = 0x0101; + pch->vendor_id = 0x5853; + pch->device_id = 0x0001; pch->command = 3; /* IO and memory access */ - pch->revision = 0; + pch->revision = 1; pch->api = 0; pch->subclass = 0x80; /* Other */ pch->class = 0xff; /* Unclassified device class */ pch->header_type = 0; pch->interrupt_pin = 1; + /* Microsoft WHQL requires non-zero subsystem IDs. */ + /* http://www.pcisig.com/reflector/msg02205.html. */ + pch->subsystem_vendor_id = pch->vendor_id; /* Duplicate vendor id. */ + pch->subsystem_id = 0x0001; /* Hardcode sub-id as 1. */ + pci_register_io_region(d, 0, 0x100, PCI_ADDRESS_SPACE_IO, platform_ioport_map); |