Patchwork [2/7] Add TPM host passthrough device backend.

login
register
mail settings
Submitter Andreas Niederl
Date Dec. 13, 2010, 6:04 p.m.
Message ID <1292263491-15429-2-git-send-email-andreas.niederl@iaik.tugraz.at>
Download mbox | patch
Permalink /patch/75389/
State New
Headers show

Comments

Andreas Niederl - Dec. 13, 2010, 6:04 p.m.
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.niederl@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

Patch

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 */