From: Stefan Berger <stefanb@linux.vnet.ibm.com>
To: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH] Add a TPM Passthrough backend driver implementation
Date: Mon, 15 Aug 2011 13:34:51 -0400 [thread overview]
Message-ID: <4E4958BB.1000603@linux.vnet.ibm.com> (raw)
In-Reply-To: <1313163072-15152-1-git-send-email-andreas.niederl@iaik.tugraz.at>
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 */
prev parent reply other threads:[~2011-08-15 17:35 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-08-12 15:31 [Qemu-devel] [PATCH] Add a TPM Passthrough backend driver implementation Andreas Niederl
2011-08-15 17:34 ` Stefan Berger [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4E4958BB.1000603@linux.vnet.ibm.com \
--to=stefanb@linux.vnet.ibm.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.