Index: root/xen-unstable.hg/tools/ioemu/hw/tpm.c =================================================================== --- /dev/null +++ root/xen-unstable.hg/tools/ioemu/hw/tpm.c @@ -0,0 +1,538 @@ +/* + * tpm.c - QEMU emulator for an AMD LPC bridge and ATMEL TPM chip + * + * Copyright (C) 2004-2006 IBM Corporation + * + * Author: David Safford + * Stefan Berger + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "vl.h" + +//#define DEBUG_TPM + +#define ATML_STATUS_BUSY 1 +#define ATML_STATUS_DATA_AVAIL 2 +#define ATML_STATUS_READY 8 +#define ATML_STATUS_ABORT 1 +#define TPM_MAX_PKT 4096 + +#define SOCKET_PATH "/tmp/vtpm_all.socket" + +uint32_t vtpm_instance = (uint32_t)-1; +int has_tpm = 0; +extern char domain_name[]; +int tpm_fd = -1; /* UnixIO fd.; open after sending request */ + + +typedef struct TPMBuffer { + uint8_t instance[4]; /* instance number in network byte order */ + uint8_t buf[TPM_MAX_PKT]; +} __attribute__((packed)) tpmBuffer; + +typedef struct TPMState { + PCIDevice *pci_dev; + uint32_t addr; + uint8_t index; + uint8_t status; + uint32_t offset; + tpmBuffer buffer; +} tpmState; + +static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg); +static int TPM_Receive(tpmBuffer *buffer); +static uint32_t vtpm_instance_from_db(void); +static int create_unixio_socket(const char *path); +static int get_unixio_socket(void); + +static inline uint32_t tpm_get_size_from_buffer(const uint8_t *buffer) +{ + uint32_t len = (buffer[4] << 8) + buffer[5]; + return len; +} + +/* process writes to register index port - 0x4e */ +static void tpm_index_write(void *opaque, uint32_t addr, uint32_t val) +{ + tpmState *s = opaque; + s->index = (uint8_t) val; +#ifdef DEBUG_TPM + printf("tpm_index_write byte %0x\n",val); +#endif +} + +/* read register from 0x4f */ +static uint32_t tpm_index_read(void *opaque, uint32_t addr) +{ + static const uint8_t data[] = {1,1,0,6,'A','T','M','L'}; + tpmState *s = opaque; + uint32_t ret; + int fd = get_unixio_socket(); + if (fd < 0) { + /* no socket -> no TPM */ + return 0xff; + } + close(fd); + + if (s->index < sizeof(data)) + ret = data[s->index]; + else if (s->index < sizeof(data)+2){ + if (s->index == sizeof(data)+1) + ret = (s->addr) >> 8; /* pci address hi */ + else + ret = s->addr & 0xff; /* pci address lo */ + } else { + return(0xff); + } +#ifdef DEBUG_TPM + printf("tpm_index_read index %d byte %0d\n",s->index,ret); +#endif + return ret; +} + +static void tpm_data_write(void *opaque, uint32_t addr, uint32_t val) +{ + tpmState *s = opaque; + uint32_t len; + + /* not accepting new data until data have been read or aborted */ + if ((s->status & (0x80 | ATML_STATUS_DATA_AVAIL)) || (tpm_fd > 0)) { + /* 0x80 is an intermediate (?) state of the ATMEL TPM */ + s->status = 0x80 | ATML_STATUS_BUSY | ATML_STATUS_READY; + return; + } + + s->status = 0x40; /* intermediate state */ +#ifdef DEBUG_TPM + printf("tpm_data_write got byte %0x [%d]\n",val,s->offset); +#endif + 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 ( s->offset >= len) { +#ifdef DEBUG_TPM + printf("Complete packet with %d bytes\n",len); +#endif + if (len > 6) { + int n = TPM_Send(s, &s->buffer,"tpm_data_write"); + if (n > 0) { + /* sending of data was successful */ + s->offset = 0; + s->status |= ATML_STATUS_BUSY; + } + } + } + } +} + +static uint32_t tpm_data_read(void *opaque, uint32_t addr) +{ + tpmState *s = opaque; + uint32_t ret,len; + + if (0 == (s->status & ATML_STATUS_DATA_AVAIL)){ + if (tpm_fd > 0) { + int n = TPM_Receive(&s->buffer); + if (n > 0) { + s->status = ATML_STATUS_DATA_AVAIL; + close(tpm_fd); + tpm_fd = -1; + } + } + } + + if (0 == (s->status & ATML_STATUS_DATA_AVAIL)){ + /* still nothing available */ + return(0xff); + } + + len = tpm_get_size_from_buffer(s->buffer.buf); + ret = s->buffer.buf[s->offset++]; + if(s->offset >= len){ + s->status = ATML_STATUS_READY; + s->offset = 0; + } +#ifdef DEBUG_TPM + printf("tpm_data_read byte x%02x [%d]\n",ret,s->offset-1); +#endif + return ret; +} + +static void tpm_status_write(void *opaque, uint32_t addr, uint32_t val) +{ + tpmState *s = opaque; + if ((val & ATML_STATUS_ABORT)) { + if (tpm_fd > 0) { +#ifdef DEBUG_TPM + printf("Closing due to abort!\n"); +#endif + close(tpm_fd); + tpm_fd = -1; + } + s->status = ATML_STATUS_READY; + s->offset = 0; + } +} + +static uint32_t tpm_status_read(void *opaque, uint32_t addr) +{ + tpmState *s = opaque; + uint32_t ret; + + if (0 == (s->status & ATML_STATUS_DATA_AVAIL)) { + if (tpm_fd > 0) { + int n = TPM_Receive(&s->buffer); + if (n > 0) { + s->status = 0xc0 | ATML_STATUS_DATA_AVAIL; + close(tpm_fd); + tpm_fd = -1; + } + } + } + + ret = s->status; + +#ifdef DEBUG_TPM + printf("tpm_status_read byte %0x\n",ret); +#endif + return ret; +} + +static void tpm_save(QEMUFile* f,void* opaque) +{ + tpmState* s=(tpmState*)opaque; + + /* need to wait for outstanding requests */ + if (tpm_fd > 0) { + int repeats = 15; + while (repeats > 0) { + int n = TPM_Receive(&s->buffer); + if (n > 0) { + s->status = ATML_STATUS_DATA_AVAIL; + close(tpm_fd); + tpm_fd = -1; + break; + } + sleep(1); + } + } + + qemu_put_be32s(f, &s->addr); + qemu_put_8s(f, &s->index); + qemu_put_8s(f, &s->status); + qemu_put_be32s(f,&s->offset); + qemu_put_buffer(f, s->buffer.buf, TPM_MAX_PKT); +} + +static int tpm_load(QEMUFile* f,void* opaque,int version_id) +{ + tpmState* s=(tpmState*)opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, &s->addr); + qemu_get_8s(f, &s->index); + qemu_get_8s(f, &s->status); + qemu_get_be32s(f,&s->offset); + qemu_get_buffer(f, s->buffer.buf, TPM_MAX_PKT); + + /* need to read the instance number from the db */ + vtpm_instance = vtpm_instance_from_db(); + if (vtpm_instance == (uint32_t)-1) + return -EINVAL; + + s->buffer.instance[0] = (vtpm_instance >> 24) & 0xff; + s->buffer.instance[1] = (vtpm_instance >> 16) & 0xff; + s->buffer.instance[2] = (vtpm_instance >> 8) & 0xff; + s->buffer.instance[3] = (vtpm_instance >> 0) & 0xff; + + return 0; +} + +typedef struct PCItpmState { + PCIDevice dev; + tpmState tpm; +} PCItpmState; + +/* pci emulator has mapped our io space, so set up ports */ +static void tpm_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + PCItpmState *d = (PCItpmState *)pci_dev; + tpmState *s = &d->tpm; + +#ifdef DEBUG_TPM + printf("tpm_map: address %x\n",addr); +#endif + s->addr = addr; + register_ioport_write(0x4e,1,1,tpm_index_write,s); + register_ioport_read(0x4f,1,1,tpm_index_read,s); + register_ioport_write(addr,1,1,tpm_data_write,s); + register_ioport_read(addr,1,1,tpm_data_read,s); + register_ioport_write(addr+1,1,1,tpm_status_write,s); + register_ioport_read(addr+1,1,1,tpm_status_read,s); +} + +/* setup the LPC interface, and map in the ATMEL chip */ +void pci_tpm_init(PCIBus *bus) +{ + PCItpmState *d; + tpmState *s; + uint8_t *pci_conf; + + d = (PCItpmState *)pci_register_device(bus, "tpm", + sizeof(PCItpmState), -1, NULL, NULL); + pci_conf = d->dev.config; + pci_conf[0x00] = 0x22; // AMD + pci_conf[0x01] = 0x10; + pci_conf[0x02] = 0x68; // LPC + pci_conf[0x03] = 0x74; + pci_conf[0x0a] = 0x01; // ISA bridge + pci_conf[0x0b] = 0x06; + pci_conf[0x0e] = 0x00; // header_type + pci_conf[0x3d] = 0; // no interrupt pin + + pci_register_io_region(&d->dev, 0, 0x100, + PCI_ADDRESS_SPACE_IO, tpm_map); + + /* initialize tpmState */ + s = &d->tpm; + s->pci_dev = (PCIDevice *)d; + s->status = ATML_STATUS_READY; + s->offset = 0; + s->index = 0; + + if ((int)vtpm_instance < 0) { + vtpm_instance = vtpm_instance_from_db(); + } + + s->buffer.instance[0] = (vtpm_instance >> 24) & 0xff; + s->buffer.instance[1] = (vtpm_instance >> 16) & 0xff; + s->buffer.instance[2] = (vtpm_instance >> 8) & 0xff; + s->buffer.instance[3] = (vtpm_instance >> 0) & 0xff; + memset(s->buffer.buf,0,sizeof(s->buffer.buf)); + + register_savevm("tpm", 0, 1, tpm_save, tpm_load, s); + register_savevm("tpm_pci", 0, 1, generic_pci_save, generic_pci_load, + &d->dev); +} + +/****************************************************************************/ +/* optional verbose logging of data to/from tpm chip */ +/****************************************************************************/ +#ifdef DEBUG_TPM +static void showBuff(unsigned char *buff, char *string) +{ + uint32_t i, len; + + len = tpm_get_size_from_buffer(buff); + printf("%s length = %d\n", string, len); + for (i = 0; i < len; i++) { + if (i && !(i % 16)) { + printf("\n"); + } + printf("%.2X ", buff[i]); + } + printf("\n"); +} +#endif + +/****************************************************************************/ +/* Transmit request to TPM and read Response */ +/****************************************************************************/ + +const static unsigned char tpm_failure[] = { + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0x09 +}; + +static int create_unixio_socket(const char *path) +{ + int fd = socket(PF_UNIX, SOCK_STREAM, 0); + + if (fd > 0) { + struct sockaddr_un addr; + memset(&addr, 0x0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, path); + if (connect(fd, + (struct sockaddr *)&addr, + sizeof(addr)) != 0) { + close(fd); + fd = -1; + } + } + return fd; +} + +/* try to create a unixio socket by trying different paths */ +static int get_unixio_socket() +{ + int fd = create_unixio_socket(SOCKET_PATH); + return fd; +} + +static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg) +{ + uint32_t size; + int len; + + size = tpm_get_size_from_buffer(buffer->buf); + + tpm_fd = get_unixio_socket(); + + if (tpm_fd < 0) { + 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; + s->status = ATML_STATUS_DATA_AVAIL; +#ifdef DEBUG_TPM + printf("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 = write(tpm_fd, + buffer->instance, + sizeof(buffer->instance) + size); + if (len == sizeof(buffer->instance) + size) { + /* put filedescriptor in non-blocking mode */ + int flags = fcntl(tpm_fd, F_GETFL); + fcntl(tpm_fd, F_SETFL, flags | O_NONBLOCK); + return len; + } else { + close(tpm_fd); + tpm_fd = -1; + return -1; + } +} + +/* + Try to receive data from the file descriptor. Since it is in + non-blocking mode it is possible that no data are actually received. +*/ +static int TPM_Receive(tpmBuffer *buffer) +{ + int off; + + off = read(tpm_fd, + buffer->instance, + sizeof(buffer->instance)+TPM_MAX_PKT); + +#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) { + printf("TPM: Packet size is bad!\n"); + } else { + uint32_t ret; + showBuff(buffer->buf, "From TPM"); + ret = (buffer->buf[8])*256 + buffer->buf[9]; + if (ret) + printf("Receive failed with error %d\n", ret); + else + printf("Receive succeeded. Got response of length %d (=%d)\n", + size, off); + } + } +#endif + + /* assuming reading in one chunk for now */ + return off; +} + + +/****************************************************************************/ +/* Get the virtual TPM instance number from the vtpm-to-vm association file */ +/****************************************************************************/ + +#define SEARCHLINE "INSTANCE = " + +static uint32_t vtpm_instance_from_db(void) +{ + uint32_t instance = (uint32_t)-1; + int apipe[2]; + int found = 0; + int repeats = 15; /* try for seconds */ + + do { + repeats --; + pipe(apipe); + if (!fork()) { + char buffer[1024]; + close(apipe[0]); + dup2(apipe[1], STDOUT_FILENO); + snprintf(buffer, + sizeof(buffer), + "cd /etc/xen/scripts ; " + "source vtpm-common.sh ;" + "echo \"" + SEARCHLINE + "$(vtpmdb_find_instance %s) \"", + &domain_name[7]); + system(buffer); + close(apipe[1]); + _exit(0); + } else { + int len; + char buffer[1024]; + close(apipe[1]); + dup2(apipe[0], STDIN_FILENO); + while ((len = read(apipe[0], + buffer, + sizeof(buffer)-1)) > 0) { + char *start; + buffer[len] = 0; + start = strstr(buffer,SEARCHLINE); + if (NULL != start) { + if (1 == sscanf(&start[sizeof(SEARCHLINE)-1], + "%d", + &instance)) { + if (instance > 0) + found = 1; + break; + } + } + } + close(apipe[0]); + } + if ((found == 0) && (repeats > 0)) { + sleep(1); + } else { +#ifdef DEBUG_TPM + printf("instance from db: %d\n",instance); +#endif + break; + } + } while (1); + + if (found == 0) { +#ifdef DEBUG_TPM + printf("instance not found in db! (%d)\n",instance); +#endif + instance = (uint32_t)-1; + } + return instance; +} Index: root/xen-unstable.hg/tools/ioemu/hw/pc.c =================================================================== --- root.orig/xen-unstable.hg/tools/ioemu/hw/pc.c +++ root/xen-unstable.hg/tools/ioemu/hw/pc.c @@ -867,7 +867,10 @@ static void pc_init1(uint64_t ram_size, } if (pci_enabled) { + extern int has_tpm; pci_piix3_ide_init(pci_bus, bs_table, piix3_devfn + 1); + if (has_tpm) + pci_tpm_init(pci_bus); } else { for(i = 0; i < 2; i++) { isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], Index: root/xen-unstable.hg/tools/ioemu/vl.h =================================================================== --- root.orig/xen-unstable.hg/tools/ioemu/vl.h +++ root/xen-unstable.hg/tools/ioemu/vl.h @@ -854,6 +854,9 @@ void pci_rtl8139_init(PCIBus *bus, NICIn void pci_pcnet_init(PCIBus *bus, NICInfo *nd); +/* tpm.c */ +void pci_tpm_init(PCIBus *bus); + /* pckbd.c */ void kbd_init(void); Index: root/xen-unstable.hg/tools/ioemu/vl.c =================================================================== --- root.orig/xen-unstable.hg/tools/ioemu/vl.c +++ root/xen-unstable.hg/tools/ioemu/vl.c @@ -169,6 +169,8 @@ time_t timeoffset = 0; char domain_name[1024] = { 'H','V', 'M', 'X', 'E', 'N', '-'}; extern int domid; +extern uint32_t vtpm_instance; +extern int has_tpm; /***********************************************************/ /* x86 ISA bus support */ @@ -5327,6 +5329,7 @@ void help(void) "-l item1,... output log to %s (use -d ? for a list of log items)\n" "-d domain domain that we're serving\n" "-domain-name domain name that we're serving\n" + "-vtpm_instance the vTPM instance we're talking to\n" "-hdachs c,h,s[,t] force hard disk 0 physical geometry and the optional BIOS\n" " translation (t=none or lba) (usually qemu can guess them)\n" "-L path set the directory for the BIOS and VGA BIOS\n" @@ -5418,6 +5421,7 @@ enum { QEMU_OPTION_std_vga, QEMU_OPTION_monitor, QEMU_OPTION_domainname, + QEMU_OPTION_vtpm_instance, QEMU_OPTION_serial, QEMU_OPTION_parallel, QEMU_OPTION_loadvm, @@ -5505,6 +5509,7 @@ const QEMUOption qemu_options[] = { { "std-vga", 0, QEMU_OPTION_std_vga }, { "monitor", 1, QEMU_OPTION_monitor }, { "domain-name", 1, QEMU_OPTION_domainname }, + { "vtpm_instance", 1, QEMU_OPTION_vtpm_instance }, { "serial", 1, QEMU_OPTION_serial }, { "parallel", 1, QEMU_OPTION_parallel }, { "loadvm", HAS_ARG, QEMU_OPTION_loadvm }, @@ -6284,6 +6289,11 @@ int main(int argc, char **argv) case QEMU_OPTION_domainname: strncat(domain_name, optarg, sizeof(domain_name) - 20); break; + case QEMU_OPTION_vtpm_instance: + vtpm_instance = atoi(optarg); + has_tpm = 1; + fprintf(logfile, "vtpm instance: %d\n", vtpm_instance); + break; case QEMU_OPTION_d: domid = atoi(optarg); fprintf(logfile, "domid: %d\n", domid); Index: root/xen-unstable.hg/tools/ioemu/Makefile.target =================================================================== --- root.orig/xen-unstable.hg/tools/ioemu/Makefile.target +++ root/xen-unstable.hg/tools/ioemu/Makefile.target @@ -354,7 +354,7 @@ VL_OBJS+= ne2000.o rtl8139.o pcnet.o ifeq ($(TARGET_BASE_ARCH), i386) # Hardware support VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) -VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o +VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o tpm.o VL_OBJS+= cirrus_vga.o mixeng.o parallel.o acpi.o piix_pci.o VL_OBJS+= usb-uhci.o VL_OBJS+= piix4acpi.o