Message ID | 1313163072-15152-1-git-send-email-andreas.niederl@iaik.tugraz.at |
---|---|
State | New |
Headers | show |
On 08/12/2011 11:31 AM, Andreas Niederl wrote: > This patch is based of off version 7 of Stefan Berger's patch series > "Qemu Trusted Platform Module (TPM) integration" > and adds a new backend driver for it. > > This patch adds a passthrough backend driver for passing commands sent to the > emulated TPM device directly to a TPM device opened on the host machine. > > Thus it is possible to use a hardware TPM device in a system running on QEMU, > providing the ability to access a TPM in a special state (e.g. after a Trusted > Boot). > > This functionality is being used in the acTvSM Trusted Virtualization Platform > which is available on [1]. > > Usage example: > qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \ > -device tpm-tis,tpmdev=tpm0 \ > -cdrom test.iso -boot d > The code looks good to me. Though hopefully nothing on the host is using the TPM in this case so that all PCRs are cleared once the VM starts -- this may otherwise create problems with attestation. Also, this device may need to be marked as unmigratable -- I am not sure how to do this though. There is an API in savevm.c to register a device as unmigratable, but I don't think we can call it (register_device_unmigratable()). It may either require some rearranging of code in tpm_tis where the VMStateDescription structure also has a field to mark a device as unmigratable (used by usb emulators for example) or the introduction of a new API call that allows a device to be marked as unmigratable after it has been registered. Regards, Stefan > Regards, > Andreas Niederl > > [1] http://trustedjava.sourceforge.net/ > > Signed-off-by: Andreas Niederl<andreas.niederl@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 */
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 */
This patch is based of off version 7 of Stefan Berger's patch series "Qemu Trusted Platform Module (TPM) integration" and adds a new backend driver for it. This patch adds a passthrough backend driver for passing commands sent to the emulated TPM device directly to a TPM device opened on the host machine. Thus it is possible to use a hardware TPM device in a system running on QEMU, providing the ability to access a TPM in a special state (e.g. after a Trusted Boot). This functionality is being used in the acTvSM Trusted Virtualization Platform which is available on [1]. Usage example: qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \ -device tpm-tis,tpmdev=tpm0 \ -cdrom test.iso -boot d Regards, Andreas Niederl [1] http://trustedjava.sourceforge.net/ Signed-off-by: Andreas Niederl <andreas.niederl@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