@@ -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) {
new file mode 100644
@@ -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;
+}
+
+
@@ -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);
@@ -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 */
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