Threadlets are used for asynchronous I/O to the host TPM device because the Linux TPM driver does not allow for non-blocking I/O.
This patch is based on the Threadlets patch series v12 posted on this list. Signed-off-by: Andreas Niederl <andreas.nied...@iaik.tugraz.at> --- hw/tpm_backend.c | 1 + hw/tpm_host_backend.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/tpm_int.h | 7 ++ hw/tpm_tis.c | 3 - 4 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 hw/tpm_host_backend.c diff --git a/hw/tpm_backend.c b/hw/tpm_backend.c index a0bec7c..2d3b550 100644 --- a/hw/tpm_backend.c +++ b/hw/tpm_backend.c @@ -26,6 +26,7 @@ typedef struct { } TPMDriverTable; static const TPMDriverTable driver_table[] = { + { .name = "host", .open = qemu_tpm_host_open }, }; int qemu_tpm_add(QemuOpts *opts) { diff --git a/hw/tpm_host_backend.c b/hw/tpm_host_backend.c new file mode 100644 index 0000000..238b030 --- /dev/null +++ b/hw/tpm_host_backend.c @@ -0,0 +1,219 @@ + +#include <errno.h> +#include <signal.h> + +#include "qemu-common.h" +#include "qemu-threadlets.h" + +#include "hw/tpm_int.h" + + +#define STATUS_DONE (1 << 1) +#define STATUS_IN_PROGRESS (1 << 0) +#define STATUS_IDLE 0 + +typedef struct { + TPMDriver common; + + ThreadletWork work; + + uint8_t send_status; + uint8_t recv_status; + + int32_t send_len; + int32_t recv_len; + + int fd; +} TPMHostDriver; + +static int tpm_host_send(TPMDriver *drv, uint8_t locty, uint32_t len) +{ + TPMHostDriver *hdrv = DO_UPCAST(TPMHostDriver, common, drv); + int n = 0; + + drv->locty = locty; + + switch (hdrv->send_status) { + case STATUS_IN_PROGRESS: + break; + case STATUS_IDLE: + hdrv->send_len = len; + hdrv->recv_len = TPM_MAX_PKT; + /* asynchronous send */ + n = 1; + submit_work(&hdrv->work); + break; + case STATUS_DONE: + break; + default: + n = -1; + fprintf(stderr, + "tpm host backend: internal error on send status %d\n", + hdrv->send_status); + break; + } + + return n; +} + +static int tpm_host_recv(TPMDriver *drv, uint8_t locty, uint32_t len) +{ + TPMHostDriver *hdrv = DO_UPCAST(TPMHostDriver, common, drv); + int n = 0; + + drv->locty = locty; + + switch (hdrv->recv_status) { + case STATUS_IN_PROGRESS: + break; + case STATUS_IDLE: + break; + case STATUS_DONE: + hdrv->recv_status = STATUS_IDLE; + n = hdrv->recv_len; + break; + default: + n = -1; + fprintf(stderr, + "tpm host backend: internal error on recv status %d\n", + hdrv->recv_status); + break; + } + + return n; +} + + +/* borrowed from qemu-char.c */ +static int unix_write(int fd, const uint8_t *buf, uint32_t len) +{ + int ret, len1; + + len1 = len; + while (len1 > 0) { + ret = write(fd, buf, len1); + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) + return -1; + } else if (ret == 0) { + break; + } else { + buf += ret; + len1 -= ret; + } + } + return len - len1; +} + +static int unix_read(int fd, uint8_t *buf, uint32_t len) +{ + int ret, len1; + uint8_t *buf1; + + len1 = len; + buf1 = buf; + while ((len1 > 0) && (ret = read(fd, buf1, len1)) != 0) { + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) + return -1; + } else { + buf1 += ret; + len1 -= ret; + } + } + return len - len1; +} + + +static void tpm_host_send_receive(ThreadletWork *work) +{ + TPMHostDriver *drv = container_of(work, TPMHostDriver, work); + TPMDriver *s = &drv->common; + uint32_t tpm_ret; + int ret; + + drv->send_status = STATUS_IN_PROGRESS; + + DSHOW_BUFF(s->buf, "To TPM"); + + ret = unix_write(drv->fd, s->buf, drv->send_len); + + drv->send_len = ret; + drv->send_status = STATUS_DONE; + + if (ret < 0) { + fprintf(stderr, "Error: while transmitting data to host tpm" + ": %s (%i)\n", + strerror(errno), errno); + return; + } + + drv->recv_status = STATUS_IN_PROGRESS; + + ret = unix_read(drv->fd, s->buf, drv->recv_len); + + drv->recv_len = ret; + drv->recv_status = STATUS_DONE; + drv->send_status = STATUS_IDLE; + + if (ret < 0) { + fprintf(stderr, "Error: while reading data from host tpm" + ": %s (%i)\n", + strerror(errno), errno); + return; + } + + DSHOW_BUFF(s->buf, "From TPM"); + + tpm_ret = (s->buf[8])*256 + s->buf[9]; + if (tpm_ret) { + DPRINTF("tpm command failed with error %d\n", tpm_ret); + } else { + DPRINTF("tpm command succeeded\n"); + } +} + + +TPMDriver *qemu_tpm_host_open(QemuOpts *opts) +{ + TPMDriver *drv = NULL; + TPMHostDriver *hdrv = NULL; + char *path = NULL; + int fd = -1; + + hdrv = qemu_mallocz(sizeof(TPMHostDriver)); + memset(hdrv, 0, sizeof(TPMHostDriver)); + drv = &hdrv->common; + + /* methods */ + drv->send = tpm_host_send; + drv->recv = tpm_host_recv; + + /* file open */ + if (qemu_opt_get(opts, "path") == NULL) { + fprintf(stderr, "tpm: No path specified.\n"); + goto fail; + } + + path = qemu_strdup(qemu_opt_get(opts, "path")); + fd = open(path, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Cannot open %s: %s (%i)\n", + path, strerror(errno), errno); + goto fail; + } + hdrv->fd = fd; + + hdrv->work.func = tpm_host_send_receive; + + return drv; + +fail: + if (fd >= 0) { + close(fd); + } + qemu_free(hdrv); + return NULL; +} + + diff --git a/hw/tpm_int.h b/hw/tpm_int.h index d52d7e2..4f9fbf3 100644 --- a/hw/tpm_int.h +++ b/hw/tpm_int.h @@ -22,6 +22,13 @@ struct TPMDriver { TPMDriver *tpm_get_driver(const char *id); +TPMDriver *qemu_tpm_host_open(QemuOpts *opts); + + +#define TPM_MAX_PKT 4096 +#define TPM_MAX_PATH 4096 + + #define DEBUG_TPM #ifdef DEBUG_TPM void show_buff(unsigned char *buff, const char *string); diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c index 0cee917..ff469ec 100644 --- a/hw/tpm_tis.c +++ b/hw/tpm_tis.c @@ -38,9 +38,6 @@ #include "hw/tpm_int.h" -#define TPM_MAX_PKT 4096 -#define TPM_MAX_PATH 4096 - #define TIS_ADDR_BASE 0xFED40000 /* tis registers */ -- 1.7.3.3