Index: root/xen-unstable.hg/tools/ioemu/hw/tpm.c =================================================================== --- /dev/null +++ root/xen-unstable.hg/tools/ioemu/hw/tpm.c @@ -0,0 +1,503 @@ +/* + * 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_xenstore(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 + fprintf(logfile,"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 + fprintf(logfile,"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 + fprintf(logfile,"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 + fprintf(logfile,"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 + fprintf(logfile,"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 + fprintf(logfile,"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 + fprintf(logfile,"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_xenstore(); + 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 + fprintf(logfile,"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; + + vtpm_instance = vtpm_instance_from_xenstore(); + + 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); + 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 +}; + +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 + 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 = 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) { + fprintf(logfile,"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) + 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; +} + + +/****************************************************************************/ +/* Get the virtual TPM instance information from the xenstore */ +/****************************************************************************/ +int has_tpm_device(void) +{ + return xenstore_domain_has_devtype("vtpm"); +} + +static uint32_t vtpm_instance_from_xenstore(void) +{ + int repeats = 15; /* try for seconds */ + unsigned int num; + uint32_t number = (uint32_t)-1; + char **e = xenstore_domain_get_devices("vtpm",&num); + + if (e) { + while (repeats > 0) { + char *status = xenstore_read_hotplug_status("vtpm",e[0]); + if (status) { + if (!strcmp(status,"connected")) { + char *inst = xenstore_backend_read_variable("vtpm", + e[0], + "instance"); + if (1 != (sscanf(inst,"%d",&number))) + number = (uint32_t)-1; + free(inst); + } + free(status); + break; + } + free(status); + sleep(1); + repeats--; + } + free(e); + } + fprintf(logfile,"vtpm instance:%d\n",number); + return number; +} 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 @@ -868,6 +868,8 @@ static void pc_init1(uint64_t ram_size, if (pci_enabled) { pci_piix3_ide_init(pci_bus, bs_table, piix3_devfn + 1); + if (has_tpm_device()) + 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,10 @@ void pci_rtl8139_init(PCIBus *bus, NICIn void pci_pcnet_init(PCIBus *bus, NICInfo *nd); +/* tpm.c */ +int has_tpm_device(void); +void pci_tpm_init(PCIBus *bus); + /* pckbd.c */ void kbd_init(void); @@ -1208,6 +1212,11 @@ int xenstore_fd(void); void xenstore_process_event(void *opaque); void xenstore_check_new_media_present(int timeout); void xenstore_write_vncport(int vnc_display); +int xenstore_domain_has_devtype(const char *devtype); +char **xenstore_domain_get_devices(const char *devtype, unsigned int *num); +char *xenstore_read_hotplug_status(const char *devtype, const char *inst); +char *xenstore_backend_read_variable(const char *devtype, const char *inst, + const char *var); /* xen_platform.c */ void pci_xen_platform_init(PCIBus *bus); 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 @@ -360,6 +360,7 @@ VL_OBJS+= usb-uhci.o VL_OBJS+= piix4acpi.o VL_OBJS+= xenstore.o VL_OBJS+= xen_platform.o +VL_OBJS+= tpm.o DEFINES += -DHAS_AUDIO endif ifeq ($(TARGET_BASE_ARCH), ppc) Index: root/xen-unstable.hg/tools/ioemu/xenstore.c =================================================================== --- root.orig/xen-unstable.hg/tools/ioemu/xenstore.c +++ root/xen-unstable.hg/tools/ioemu/xenstore.c @@ -213,3 +213,67 @@ void xenstore_write_vncport(int display) free(portstr); free(buf); } + + +char **xenstore_domain_get_devices(const char *devtype, unsigned int *num) +{ + char *path = NULL, *buf = NULL; + char **e = NULL; + + if (xsh == NULL) + goto out; + + path = xs_get_domain_path(xsh, domid); + if (path == NULL) + goto out; + + if (pasprintf(&buf, "%s/device/%s", path,devtype) == -1) + goto out; + + e = xs_directory(xsh, XBT_NULL, buf, num); + + out: + free(path); + free(buf); + return e; +} + +int xenstore_domain_has_devtype(const char *devtype) +{ + int rc = 0; + unsigned int num; + char **e = xenstore_domain_get_devices(devtype, &num); + if (e) + rc = 1; + free(e); + return rc; +} + +char *xenstore_backend_read_variable(const char *devtype, const char *inst, + const char *var) +{ + char *value = NULL; + char *buf = NULL; + unsigned int len; + + if (xsh == NULL) + goto out; + + if (pasprintf(&buf, "/local/domain/0/backend/%s/%d/%s/%s", + devtype, + domid, + inst, + var) == -1) + goto out; + + value = xs_read(xsh, XBT_NULL, buf, &len); + + out: + free(buf); + return value; +} + +char *xenstore_read_hotplug_status(const char *devtype, const char *inst) +{ + return xenstore_backend_read_variable(devtype, inst, "hotplug-status"); +}