[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 3/5] Add TPM host passthrough device backend.
From: |
Andreas Niederl |
Subject: |
[Qemu-devel] [PATCH 3/5] Add TPM host passthrough device backend. |
Date: |
Fri, 18 Feb 2011 16:33:33 +0100 |
A separate thread is used for I/O to the host TPM device because the Linux TPM
driver does not allow non-blocking I/O.
Signed-off-by: Andreas Niederl <address@hidden>
---
Makefile.objs | 5 +-
hw/tpm_backend.c | 1 +
hw/tpm_host_backend.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++++
hw/tpm_int.h | 3 +
4 files changed, 290 insertions(+), 1 deletions(-)
create mode 100644 hw/tpm_host_backend.c
diff --git a/Makefile.objs b/Makefile.objs
index 6c78453..55fd6b5 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -284,7 +284,10 @@ hw-obj-$(CONFIG_VIRTFS) += virtio-9p-local.o
virtio-9p-xattr.o
hw-obj-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
# TPM passthrough device
-hw-obj-$(CONFIG_TPM) += tpm_tis.o tpm_backend.o
+hw-obj-$(CONFIG_TPM) += tpm_tis.o tpm_backend.o tpm_host_backend.o
+ifndef CONFIG_THREAD
+common-obj-$(CONFIG_TPM) += qemu-thread.o
+endif
######################################################################
# libdis
diff --git a/hw/tpm_backend.c b/hw/tpm_backend.c
index b87c089..41dbfd4 100644
--- a/hw/tpm_backend.c
+++ b/hw/tpm_backend.c
@@ -40,6 +40,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..4ae9deb
--- /dev/null
+++ b/hw/tpm_host_backend.c
@@ -0,0 +1,282 @@
+/*
+ * tpm_host_backend.c - TPM host passthrough backend driver
+ *
+ * Copyright (C) 2011 IAIK, Graz University of Technology
+ *
+ * Author: Andreas Niederl <address@hidden>
+ *
+ * A custom thread is used for asynchronous I/O to the host TPM device
+ * because the Linux TPM driver does not allow non-blocking I/O.
+ *
+ * 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 <errno.h>
+#include <signal.h>
+
+#include "qemu-common.h"
+#include "qemu-thread.h"
+
+#include "hw/tpm_int.h"
+
+
+typedef struct {
+ QemuThread id;
+ QemuMutex lock;
+ QemuCond send_command;
+} TPMThread;
+
+#define STATUS_DONE (1 << 1)
+#define STATUS_IN_PROGRESS (1 << 0)
+#define STATUS_IDLE 0
+
+typedef struct {
+ TPMDriver common;
+
+ TPMThread thread;
+
+ 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;
+
+ qemu_mutex_lock(&hdrv->thread.lock);
+ 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;
+ qemu_cond_signal( &hdrv->thread.send_command);
+ break;
+ case STATUS_DONE:
+ n = hdrv->send_len;
+ hdrv->send_status = STATUS_IDLE;
+ break;
+ default:
+ n = -1;
+ fprintf(stderr,
+ "tpm host backend: internal error on send status %d\n",
+ hdrv->send_status);
+ break;
+ }
+ qemu_mutex_unlock(&hdrv->thread.lock);
+
+ 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;
+
+ qemu_mutex_lock(&hdrv->thread.lock);
+ 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;
+ }
+ qemu_mutex_unlock(&hdrv->thread.lock);
+
+ 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 die2(int err, const char *what)
+{
+ fprintf(stderr, "%s failed: %s\n", what, strerror(err));
+ abort();
+}
+
+static void die(const char *what)
+{
+ die2(errno, what);
+}
+
+static void *tpm_host_thread(void *opaque)
+{
+ TPMHostDriver *drv = opaque;
+ TPMDriver *s = &drv->common;
+ sigset_t set;
+ uint32_t tpm_ret;
+ int ret;
+
+ /* block all signals */
+ if (sigfillset(&set)) {
+ die("sigfillset");
+ }
+ if (sigprocmask(SIG_BLOCK, &set, NULL)) {
+ die("sigprocmask");
+ }
+
+ qemu_mutex_lock(&drv->thread.lock);
+ while (1) {
+ qemu_cond_wait(&drv->thread.send_command, &drv->thread.lock);
+ drv->send_status = STATUS_IN_PROGRESS;
+ qemu_mutex_unlock(&drv->thread.lock);
+
+ DSHOW_BUFF(s->buf, "To TPM");
+
+ ret = unix_write(drv->fd, s->buf, drv->send_len);
+
+ qemu_mutex_lock(&drv->thread.lock);
+ 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);
+ continue;
+ }
+
+ drv->recv_status = STATUS_IN_PROGRESS;
+ qemu_mutex_unlock(&drv->thread.lock);
+
+ ret = unix_read(drv->fd, s->buf, drv->recv_len);
+
+ qemu_mutex_lock(&drv->thread.lock);
+ 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);
+ continue;
+ }
+
+ 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");
+ }
+ }
+
+ return NULL;
+}
+
+
+TPMDriver *qemu_tpm_host_open(QemuOpts *opts)
+{
+ TPMDriver *drv = NULL;
+ TPMHostDriver *hdrv = NULL;
+ TPMThread *thread = 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;
+
+ thread = &hdrv->thread;
+ qemu_mutex_init(&thread->lock);
+ qemu_cond_init( &thread->send_command);
+
+ qemu_thread_create(&thread->id, &tpm_host_thread, hdrv);
+
+ 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 7869a81..71a71f4 100644
--- a/hw/tpm_int.h
+++ b/hw/tpm_int.h
@@ -36,6 +36,9 @@ 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
--
1.7.4.1
Re: [Qemu-devel] [PATCH 1/5] Add TPM 1.2 device interface, Stefan Berger, 2011/02/18