Regards,
Andreas Niederl
[1] http://trustedjava.sourceforge.net/
Signed-off-by: Andreas Niederl<andreas.nied...@iaik.tugraz.at>
---
Makefile.target | 2 +-
hw/tpm_passthrough.c | 450 ++++++++++++++++++++++++++++++++++++++++++++++++++
tpm.c | 1 +
tpm.h | 1 +
4 files changed, 453 insertions(+), 1 deletions(-)
create mode 100644 hw/tpm_passthrough.c
diff --git a/Makefile.target b/Makefile.target
index f4d42d4..7f19f28 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -233,7 +233,7 @@ obj-i386-y += debugcon.o multiboot.o
obj-i386-y += pc_piix.o
obj-i386-$(CONFIG_KVM) += kvmclock.o
obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
-obj-i386-$(CONFIG_TPM) += tpm_tis.o sha1.o tpm_null.o
+obj-i386-$(CONFIG_TPM) += tpm_tis.o sha1.o tpm_null.o tpm_passthrough.o
obj-i386-$(CONFIG_TPM_BUILTIN) += tpm_builtin.o
ifdef CONFIG_TPM_BUILTIN
diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
new file mode 100644
index 0000000..aabfea2
--- /dev/null
+++ b/hw/tpm_passthrough.c
@@ -0,0 +1,450 @@
+/*
+ * passthrough TPM driver
+ *
+ * Copyright (c) 2010, 2011 IBM Corporation
+ * Copyright (c) 2010, 2011 Stefan Berger
+ *
+ * Copyright (C) 2011 IAIK, Graz University of Technology
+ * Author: Andreas Niederl
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see<http://www.gnu.org/licenses/>
+ */
+
+#include "qemu-common.h"
+#include "tpm.h"
+#include "hw/hw.h"
+#include "hw/tpm_tis.h"
+#include "hw/pc.h"
+
+
+//#define DEBUG_TPM
+//#define DEBUG_TPM_SR /* suspend - resume */
+
+
+/* data structures */
+
+typedef struct ThreadParams {
+ TPMState *tpm_state;
+
+ TPMRecvDataCB *recv_data_callback;
+
+ int fd;
+} ThreadParams;
+
+
+/* local variables */
+
+static QemuThread thread;
+
+static bool thread_terminate = false;
+static bool thread_running = false;
+
+static ThreadParams tpm_thread_params;
+
+static const unsigned char tpm_std_fatal_error_response[10] = {
+ 0x00, 0xc4, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09 /* TPM_FAIL */
+};
+
+static char dev_description[80];
+
+static char tpm_dev[255];
+static int tpmfd = -1;
+
+static bool had_startup_error = false;
+
+
+/* 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_write_std_fatal_error_response(uint8_t *out,
+ uint8_t *in, uint32_t in_len)
+{
+ memcpy(out, tpm_std_fatal_error_response,
+ sizeof(tpm_std_fatal_error_response));
+ out[1] = (in_len> 2&& in[1]>= 0xc1&& in[1]<= 0xc3)
+ ? in[1] + 3
+ : 0xc4;
+}
+
+static void *tpm_passthrough_main_loop(void *d)
+{
+ ThreadParams *thr_parms = d;
+ uint32_t in_len, out_len;
+ uint8_t *in, *out;
+ uint8_t locty;
+ int ret;
+
+#ifdef DEBUG_TPM
+ fprintf(stderr, "tpm: THREAD IS STARTING\n");
+#endif
+
+ /* start command processing */
+ while (!thread_terminate) {
+ /* receive and handle commands */
+ in_len = 0;
+ do {
+#ifdef DEBUG_TPM
+ fprintf(stderr, "tpm: waiting for commands...\n");
+#endif
+
+ if (thread_terminate) {
+ break;
+ }
+
+ qemu_mutex_lock(&thr_parms->tpm_state->state_lock);
+
+ /* in case we were to slow and missed the signal, the
+ to_tpm_execute boolean tells us about a pending command */
+ if (!thr_parms->tpm_state->to_tpm_execute) {
+ qemu_cond_wait(&thr_parms->tpm_state->to_tpm_cond,
+&thr_parms->tpm_state->state_lock);
+ }
+
+ thr_parms->tpm_state->to_tpm_execute = false;
+
+ qemu_mutex_unlock(&thr_parms->tpm_state->state_lock);
+
+ if (thread_terminate) {
+ break;
+ }
+
+ locty = thr_parms->tpm_state->command_locty;
+
+ in = thr_parms->tpm_state->loc[locty].w_buffer.buffer;
+ in_len = thr_parms->tpm_state->loc[locty].w_offset;
+ out = thr_parms->tpm_state->loc[locty].r_buffer.buffer;
+ out_len = thr_parms->tpm_state->loc[locty].r_buffer.size;
+
+ ret = unix_write(tpmfd, in, in_len);
+ if (ret< 0) {
+ fprintf(stderr, "tpm: error while transmitting data to host
tpm"
+ ": %s (%i)\n",
+ strerror(errno), errno);
+ tpm_write_std_fatal_error_response(out, in, in_len);
+ continue;
+ }
+
+ ret = unix_read(tpmfd, out, out_len);
+ if (ret< 0) {
+ fprintf(stderr, "tpm: error while reading data from host tpm"
+ ": %s (%i)\n",
+ strerror(errno), errno);
+ tpm_write_std_fatal_error_response(out, in, in_len);
+ continue;
+ }
+
+ thr_parms->recv_data_callback(thr_parms->tpm_state, locty);
+ } while (in_len> 0);
+ }
+
+#ifdef DEBUG_TPM
+ fprintf(stderr, "tpm: THREAD IS ENDING\n");
+#endif
+
+ thread_running = false;
+
+ return NULL;
+}
+
+
+static void tpm_passthrough_terminate_tpm_thread(void)
+{
+ if (!thread_running) {
+ return;
+ }
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ fprintf(stderr, "tpm: TERMINATING RUNNING TPM THREAD\n");
+#endif
+
+ if (!thread_terminate) {
+ thread_terminate = true;
+
+ qemu_mutex_lock (&tpm_thread_params.tpm_state->state_lock);
+ qemu_cond_signal (&tpm_thread_params.tpm_state->to_tpm_cond);
+ qemu_mutex_unlock(&tpm_thread_params.tpm_state->state_lock);
+
+ while (thread_running) {
+ usleep(100000);
+ }
+ memset(&thread, 0, sizeof(thread));
+ }
+}
+
+
+static void tpm_passthrough_tpm_atexit(void)
+{
+ tpm_passthrough_terminate_tpm_thread();
+
+ close(tpmfd);
+ tpmfd = -1;
+}
+
+
+/**
+ * Start the TPM (thread). If it had been started before, then terminate
+ * and start it again.
+ */
+static int tpm_passthrough_startup_tpm(void)
+{
+ /* terminate a running TPM */
+ tpm_passthrough_terminate_tpm_thread();
+
+ /* reset the flag so the thread keeps on running */
+ thread_terminate = false;
+
+ qemu_thread_create(&thread, tpm_passthrough_main_loop,&tpm_thread_params);
+
+ thread_running = true;
+
+ return 0;
+}
+
+
+static int tpm_passthrough_do_startup_tpm(void)
+{
+ int rc;
+
+ rc = tpm_passthrough_startup_tpm();
+ if (rc) {
+ had_startup_error = true;
+ }
+ return rc;
+}
+
+
+static int tpm_passthrough_early_startup_tpm(void)
+{
+ return tpm_passthrough_do_startup_tpm();
+}
+
+
+static int tpm_passthrough_late_startup_tpm(void)
+{
+ return tpm_passthrough_do_startup_tpm();
+}
+
+
+static void tpm_passthrough_reset(void)
+{
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ fprintf(stderr, "tpm: CALL TO TPM_RESET!\n");
+#endif
+
+ tpm_passthrough_terminate_tpm_thread();
+
+ had_startup_error = false;
+}
+
+
+/*
+ * Since the null driver does not have much persistent storage
+ * there is not much to do here...
+ */
+static int tpm_passthrough_instantiate_with_volatile_data(TPMState *s)
+{
+ if (thread_running) {
+#ifdef DEBUG_TPM_SR
+ fprintf(stderr, "tpm: This is resume of a SNAPSHOT\n");
+#endif
+ tis_reset_for_snapshot_resume(s);
+ }
+
+ return 0;
+}
+
+
+static int tpm_passthrough_init(TPMState *s, TPMRecvDataCB *recv_data_cb)
+{
+ tpm_thread_params.tpm_state = s;
+ tpm_thread_params.recv_data_callback = recv_data_cb;
+
+ tpmfd = open(tpm_dev, O_RDWR);
+ if (tpmfd< 0) {
+ fprintf(stderr,
+ "Cannot open device '%s' from tpm's path option.\n",
+ tpm_dev);
+ goto err_exit;
+ }
+
+ atexit(tpm_passthrough_tpm_atexit);
+
+ return 0;
+
+err_exit:
+ if (tpmfd>= 0) {
+ close(tpmfd);
+ tpmfd = -1;
+ }
+ return 1;
+}
+
+
+static bool tpm_passthrough_get_tpm_established_flag(void)
+{
+ return false;
+}
+
+
+static bool tpm_passthrough_get_startup_error(void)
+{
+ return had_startup_error;
+}
+
+
+/**
+ * This function is called by tpm_tis.c once the TPM has processed
+ * the last command and returned the response to the TIS.
+ */
+static int tpm_passthrough_save_volatile_data(void)
+{
+ return 0;
+}
+
+
+static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
+{
+ size_t wanted_size = 4096;
+
+ if (sb->size != wanted_size) {
+ sb->buffer = qemu_realloc(sb->buffer, wanted_size);
+ if (sb->buffer != NULL) {
+ sb->size = wanted_size;
+ } else {
+ sb->size = 0;
+ }
+ }
+ return sb->size;
+}
+
+
+static const char *tpm_passthrough_create_desc(void)
+{
+ static int done;
+
+ if (!done) {
+ snprintf(dev_description, sizeof(dev_description),
+ "Passthrough TPM backend driver");
+ done = 1;
+ }
+
+ return dev_description;
+}
+
+
+static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id,
+ const char *model)
+{
+ TPMBackend *driver;
+ const char *value;
+
+ driver = qemu_malloc(sizeof(TPMBackend));
+ if (!driver) {
+ fprintf(stderr, "Could not allocate memory.\n");
+ return NULL;
+ }
+ driver->id = qemu_strdup(id);
+ driver->model = NULL;
+ if (model)
+ driver->model = qemu_strdup(model);
+ driver->ops =&tpm_passthrough_driver;
+
+ value = qemu_opt_get(opts, "path");
+ if (value) {
+ if (access(value, R_OK|W_OK)) {
+ fprintf(stderr,
+ "Cannot access device '%s' from tpm's path option.\n",
+ value);
+ goto err_exit;
+ }
+ strncpy(tpm_dev, value, sizeof(tpm_dev));
+ } else {
+ fprintf(stderr, "-tpm is missing path= parameter\n");
+ goto err_exit;
+ }
+
+ return driver;
+
+err_exit:
+ qemu_free(driver->id);
+ if (driver->model)
+ qemu_free(driver->model);
+ qemu_free(driver);
+ return NULL;
+}
+
+
+static void tpm_passthrough_destroy(TPMBackend *driver)
+{
+ qemu_free(driver->id);
+ if (driver->model)
+ qemu_free(driver->model);
+ qemu_free(driver);
+}
+
+
+TPMDriverOps tpm_passthrough_driver = {
+ .id = "passthrough",
+ .desc = tpm_passthrough_create_desc,
+ .job_for_main_thread = NULL,
+ .create = tpm_passthrough_create,
+ .destroy = tpm_passthrough_destroy,
+ .init = tpm_passthrough_init,
+ .early_startup_tpm = tpm_passthrough_early_startup_tpm,
+ .late_startup_tpm = tpm_passthrough_late_startup_tpm,
+ .realloc_buffer = tpm_passthrough_realloc_buffer,
+ .reset = tpm_passthrough_reset,
+ .had_startup_error = tpm_passthrough_get_startup_error,
+ .save_volatile_data = tpm_passthrough_save_volatile_data,
+ .load_volatile_data = tpm_passthrough_instantiate_with_volatile_data,
+ .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
+};
diff --git a/tpm.c b/tpm.c
index b75fe5c..edcbfe7 100644
--- a/tpm.c
+++ b/tpm.c
@@ -28,6 +28,7 @@ static const TPMDriverOps *bes[] = {
#ifdef CONFIG_TPM_BUILTIN
&tpm_builtin,
#endif
+&tpm_passthrough_driver,
NULL,
};
diff --git a/tpm.h b/tpm.h
index d00e599..01cae9a 100644
--- a/tpm.h
+++ b/tpm.h
@@ -143,5 +143,6 @@ void tpm_measure_buffer(const void *buffer, long length,
extern TPMDriverOps tpm_null_driver;
extern TPMDriverOps tpm_builtin;
+extern TPMDriverOps tpm_passthrough_driver;
#endif /* _HW_TPM_CONFIG_H */