qemu-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]