From: Stefan Berger <stefanb@linux.vnet.ibm.com>
To: Corey Bryant <coreyb@linux.vnet.ibm.com>
Cc: qemu-devel@nongnu.org, aliguori@amazon.com
Subject: Re: [Qemu-devel] [PATCH 4/4] tpm: Provide libtpms software TPM backend
Date: Tue, 19 Nov 2013 14:37:45 -0500 [thread overview]
Message-ID: <528BBE09.4070406@linux.vnet.ibm.com> (raw)
In-Reply-To: <1383748746-8909-1-git-send-email-coreyb@linux.vnet.ibm.com>
On 11/06/2013 09:39 AM, Corey Bryant wrote:
> This patch provides a software TPM backend implementation. The
> core software TPM functionality is provided by the libtpms
> library. With this patch, multiple guests can run with their
> own emulated TPMs.
>
> The libtpms repository can be found at:
> https://github.com/coreycb/libtpms
>
> Signed-off-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
> ---
> configure | 24 ++
> hw/tpm/Makefile.objs | 2 +
> hw/tpm/tpm_libtpms.c | 885 ++++++++++++++++++++++++++++++++++++++++++++++++++
> qemu-options.hx | 31 ++-
> tpm.c | 2 +-
> 5 files changed, 941 insertions(+), 3 deletions(-)
> create mode 100644 hw/tpm/tpm_libtpms.c
>
> diff --git a/configure b/configure
> index 9addff1..085142f 100755
> --- a/configure
> +++ b/configure
> @@ -2475,6 +2475,26 @@ else
> fi
>
> ##########################################
> +# TPM libtpms probe
> +
> +tpm_libtpms=no
> +if test "$tpm" != "no" ; then
> +cat > $TMPC <<EOF
> +#include <libtpms/tpm_library.h>
> +#include <libtpms/tpm_error.h>
> +#include <libtpms/tpm_memory.h>
> +#include <libtpms/tpm_nvfilename.h>
> +#include <libtpms/tpm_tis.h>
> +int main(int argc, char **argv) { TPMLIB_GetVersion(); return 0; }
> +EOF
> + libtpms_libs=`$pkg_config libtpms --libs 2> /dev/null`
> + if compile_prog "$libtpms_libs" ; then
> + tpm_libtpms=$tpm
> + libs_softmmu="$libs_softmmu $libtpms_libs"
> + fi
> +fi
> +
> +##########################################
> # adjust virtio-blk-data-plane based on linux-aio
>
> if test "$virtio_blk_data_plane" = "yes" -a \
> @@ -3746,6 +3766,7 @@ echo "gcov enabled $gcov"
> echo "TPM support $tpm"
> echo "libssh2 support $libssh2"
> echo "TPM passthrough $tpm_passthrough"
> +echo "TPM libtpms $tpm_libtpms"
> echo "QOM debugging $qom_cast_debug"
>
> if test "$sdl_too_old" = "yes"; then
> @@ -4154,6 +4175,9 @@ if test "$tpm" = "yes"; then
> if test "$tpm_passthrough" = "yes"; then
> echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
> fi
> + if test "$tpm_libtpms" = "yes"; then
> + echo "CONFIG_TPM_LIBTPMS=y" >> $config_host_mak
> + fi
> fi
>
> # use default implementation for tracing backend-specific routines
> diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
> index 99f5983..77e9065 100644
> --- a/hw/tpm/Makefile.objs
> +++ b/hw/tpm/Makefile.objs
> @@ -1,2 +1,4 @@
> common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
> common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
> +common-obj-$(CONFIG_TPM_LIBTPMS) += tpm_libtpms.o
> +common-obj-$(CONFIG_TPM_LIBTPMS) += tpm_nvram.o
> diff --git a/hw/tpm/tpm_libtpms.c b/hw/tpm/tpm_libtpms.c
> new file mode 100644
> index 0000000..f9c1f80
> --- /dev/null
> +++ b/hw/tpm/tpm_libtpms.c
> @@ -0,0 +1,885 @@
> +/*
> + * libtpms TPM driver
> + *
> + * Copyright (C) 2013 IBM Corporation
> + *
> + * Authors:
> + * Stefan Berger <stefanb@us.ibm.com>
> + * Corey Bryant <coreyb@linux.vnet.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "sysemu/tpm_backend.h"
> +#include "tpm_int.h"
> +#include "tpm_nvram.h"
> +#include "qapi/qmp/qerror.h"
> +#include "migration/migration.h"
> +#include "sysemu/tpm_backend_int.h"
> +
> +#include <libtpms/tpm_library.h>
> +#include <libtpms/tpm_error.h>
> +#include <libtpms/tpm_memory.h>
> +#include <libtpms/tpm_nvfilename.h>
> +#include <libtpms/tpm_tis.h>
> +
> +/* #define DEBUG_TPM */
> +
> +#ifdef DEBUG_TPM
> +#define DPRINTF(fmt, ...) \
> + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#define DPRINTF_BUFFER(buffer, len) \
> + do { tpm_ltpms_dump_buffer(stderr, buffer, len); } while (0)
> +#else
> +#define DPRINTF(fmt, ...) \
> + do { } while (0)
> +#define DPRINTF_BUFFER(buffer, len) \
> + do { } while (0)
> +#endif
> +
> +#define NVRAM_BLOB_OFFSET_FROM_ENTRY(entry_offset) \
> + (entry_offset + sizeof(uint32_t))
> +
> +#define TYPE_TPM_LIBTPMS "tpm-libtpms"
> +#define TPM_LIBTPMS(obj) \
> + OBJECT_CHECK(TPMLTPMsState, (obj), TYPE_TPM_LIBTPMS)
> +
> +static const TPMDriverOps tpm_ltpms_driver;
> +
> +/* data structures */
> +typedef struct TPMLTPMsThreadParams {
> + TPMState *tpm_state;
> +
> + TPMRecvDataCB *recv_data_callback;
> + TPMBackend *tb;
> +} TPMLTPMsThreadParams;
> +
> +struct NVRAMEntry {
> + uint32_t cur_size;
> + uint8_t *buffer;
> +};
> +
> +typedef struct NVRAMEntry NVRAMEntry;
> +
> +struct TPMLTPMsState {
> + TPMBackend parent;
> +
> + TPMBackendThread tbt;
> +
> + TPMLTPMsThreadParams tpm_thread_params;
> +
> + bool tpm_initialized;
> + bool had_fatal_error;
> +
> + BlockDriverState *bdrv;
> +
> + NVRAMEntry *perm_state_entry;
> + NVRAMEntry *save_state_entry;
> + NVRAMEntry *vola_state_entry;
> +
> + uint32_t perm_state_entry_offset;
> + uint32_t save_state_entry_offset;
> + uint32_t vola_state_entry_offset;
> +
> + uint32_t perm_state_max_size;
> + uint32_t save_state_max_size;
> + uint32_t vola_state_max_size;
> +
> + QemuMutex tpm_initialized_mutex;
> +
> + uint8_t locty; /* locality of command being executed by libtpms */
> +};
> +
> +typedef struct TPMLTPMsState TPMLTPMsState;
> +
> +static TPMBackend *tpm_backend;
> +
> +/* functions */
> +
> +#ifdef DEBUG_TPM
> +static inline void tpm_ltpms_dump_buffer(FILE *stream, unsigned char *buffer,
> + unsigned int len)
> +{
> + int i;
> +
> + for (i = 0; i < len; i++) {
> + if (i && !(i % 16)) {
> + fprintf(stream, "\n");
> + }
> + fprintf(stream, "%.2X ", buffer[i]);
> + }
> + fprintf(stream, "\n");
> +}
> +#endif
> +
> +static inline void tpm_ltpms_free_nvram_entry(NVRAMEntry **entry)
> +{
> + if (*entry) {
> + TPM_Free((unsigned char *)*entry);
> + *entry = NULL;
> + }
> +}
> +
> +static inline void tpm_ltpms_free_nvram_buffer(NVRAMEntry *entry)
> +{
> + if (entry && entry->buffer) {
> + TPM_Free(entry->buffer);
> + entry->buffer = NULL;
> + entry->cur_size = 0;
> + }
> +}
> +
> +static inline void tpm_ltpms_free_nvram_all(TPMLTPMsState *tpm_ltpms)
> +{
> + tpm_ltpms_free_nvram_buffer(tpm_ltpms->perm_state_entry);
> + tpm_ltpms_free_nvram_buffer(tpm_ltpms->save_state_entry);
> + tpm_ltpms_free_nvram_buffer(tpm_ltpms->vola_state_entry);
> +
> + tpm_ltpms_free_nvram_entry(&tpm_ltpms->perm_state_entry);
> + tpm_ltpms_free_nvram_entry(&tpm_ltpms->save_state_entry);
> + tpm_ltpms_free_nvram_entry(&tpm_ltpms->vola_state_entry);
> +}
> +
> +/*
> + * Calls into libtpms to get a runtime property of the TPM
> + */
> +static int tpmlib_get_prop(enum TPMLIB_TPMProperty prop)
> +{
> + int result;
> +
> + TPM_RESULT res = TPMLIB_GetTPMProperty(prop, &result);
> + assert(res == TPM_SUCCESS);
> +
> + return result;
> +}
> +
> +/*
> + * Generates the drive offsets where NVRAM blobs are stored. Each offset
> + * allows for enough room to store the current blob size plus a blob of
> + * the maximum size.
> + */
> +static void tpm_ltpms_get_nvram_offsets(TPMLTPMsState *tpm_ltpms)
> +{
> + tpm_ltpms->perm_state_entry_offset = 0;
> + tpm_ltpms->perm_state_max_size = tpmlib_get_prop(TPMPROP_TPM_MAX_NV_SPACE);
> +
> + tpm_ltpms->save_state_entry_offset =
> + ROUND_UP(tpm_ltpms->perm_state_entry_offset + sizeof(uint32_t) +
> + tpm_ltpms->perm_state_max_size + 1, 1024);
> + tpm_ltpms->save_state_max_size =
> + tpmlib_get_prop(TPMPROP_TPM_MAX_SAVESTATE_SPACE);
> +
> + tpm_ltpms->vola_state_entry_offset =
> + ROUND_UP(tpm_ltpms->save_state_entry_offset + sizeof(uint32_t) +
> + tpm_ltpms->save_state_max_size + 1, 1024);
> + tpm_ltpms->vola_state_max_size =
> + tpmlib_get_prop(TPMPROP_TPM_MAX_VOLATILESTATE_SPACE);
> +}
> +
> +/*
> + * Writes an NVRAM entry and it's blob to the specified drive offset
> + */
> +static int tpm_ltpms_write_to_nvram(TPMLTPMsState *tpm_ltpms, uint32_t offset,
> + NVRAMEntry *entry, uint32_t max_size)
> +{
> + int rc;
> + uint8_t *buffer = entry->buffer;
> + uint32_t size = entry->cur_size;
> + BlockDriverState *bdrv = tpm_ltpms->bdrv;
> +
> + DPRINTF("tpm_libtpms: Writing NVRAM entry to offset %"PRIu32"\n", offset);
> +
> + if (tpm_ltpms->had_fatal_error) {
> + return TPM_FAIL;
> + }
> +
> + if (size > max_size) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR, "TPM NVRAM blob size too big");
> + return TPM_FAIL;
> + }
> +
> + DPRINTF("tpm_libtpms: current blob size = %"PRIu32"\n", size);
> +
> + /* Write the blob */
> + if (size > 0) {
> + DPRINTF_BUFFER(buffer, size);
> +
> + rc = tpm_nvram_bdrv_write(bdrv, NVRAM_BLOB_OFFSET_FROM_ENTRY(offset),
> + buffer, size);
> + if (rc != size) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR, "TPM NVRAM write failed");
> + return rc;
> + }
> + }
> +
> + /* Blob size is stored on disk in big-endian */
> + size = cpu_to_be32(size);
> +
> + /* Write the blob size */
> + rc = tpm_nvram_bdrv_write(bdrv, offset, (uint8_t *)&size, sizeof(size));
> + if (rc != sizeof(size)) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR, "TPM NVRAM write failed");
> + return rc;
> + }
> +
> + return TPM_SUCCESS;
> +}
> +
> +/*
> + * Reads an NVRAM entry and it's blob from the specified drive offset
> + */
> +static int tpm_ltpms_read_from_nvram(TPMLTPMsState *tpm_ltpms, uint32_t offset,
> + NVRAMEntry **entry, uint32_t max_size)
> +{
> + int rc;
> + uint8_t *buffer = NULL;
> + uint32_t *size = NULL;
> + BlockDriverState *bdrv = tpm_ltpms->bdrv;
> +
> + DPRINTF("tpm_libtpms: Reading NVRAM entry from offset %"PRIu32"\n", offset);
> +
> + if (tpm_ltpms->had_fatal_error) {
> + return TPM_FAIL;
> + }
> +
> + /* Allocate the in-memory blob entry */
> + rc = TPM_Malloc((unsigned char **)entry, sizeof(**entry));
> + if (rc != TPM_SUCCESS) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR,
> + "TPM memory allocation failed");
> + abort();
> + }
> +
> + /* Read the blob size */
> + rc = tpm_nvram_bdrv_read(bdrv, offset, (uint8_t **)&size, sizeof(*size));
> + if (rc != sizeof(*size)) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR, "TPM NVRAM read failed");
> + goto err_exit;
> + }
> +
> + /* Blob size is stored on disk in big-endian */
> + *size = be32_to_cpu(*size);
> +
> + if (*size > max_size) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR, "TPM NVRAM blob size too big");
> + rc = TPM_FAIL;
> + goto err_exit;
> + }
> +
> + DPRINTF("tpm_libtpms: current blob size = %"PRIu32"\n", *size);
> +
> + (*entry)->cur_size = *size;
> + (*entry)->buffer = NULL;
> +
> + /* Read the blob */
> + if (*size > 0) {
> + rc = tpm_nvram_bdrv_read(bdrv, NVRAM_BLOB_OFFSET_FROM_ENTRY(offset),
> + &buffer, *size);
> + if (rc != *size) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR, "TPM NVRAM read failed");
> + goto err_exit;
> + }
> +
> + (*entry)->buffer = buffer;
> +
> + DPRINTF_BUFFER(buffer, *size);
> + }
> +
> + rc = TPM_SUCCESS;
> +
> +err_exit:
> + if (size) {
The check is not necessary.
> + TPM_Free((uint8_t *)size);
> + }
> +
> + return rc;
> +}
> +
> +/*
> + * Loads the TPM's NVRAM state from NVRAM drive into memory
> + */
> +static int tpm_ltpms_load_tpm_state_from_nvram(TPMLTPMsState *tpm_ltpms)
> +{
> + int rc;
> +
> + rc = tpm_ltpms_read_from_nvram(tpm_ltpms,
> + tpm_ltpms->perm_state_entry_offset,
> + &tpm_ltpms->perm_state_entry,
> + tpm_ltpms->perm_state_max_size);
> + if (rc) {
> + goto err_exit;
> + }
> +
> + rc = tpm_ltpms_read_from_nvram(tpm_ltpms,
> + tpm_ltpms->save_state_entry_offset,
> + &tpm_ltpms->save_state_entry,
> + tpm_ltpms->save_state_max_size);
> + if (rc) {
> + goto err_exit;
> + }
> +
> + rc = tpm_ltpms_read_from_nvram(tpm_ltpms,
> + tpm_ltpms->vola_state_entry_offset,
> + &tpm_ltpms->vola_state_entry,
> + tpm_ltpms->vola_state_max_size);
> + if (rc) {
> + goto err_exit;
> + }
> +
> + return 0;
> +
> +err_exit:
> + tpm_ltpms->had_fatal_error = true;
> +
> + return rc;
> +}
> +
> +/*
> + * Processes a command request by calling into libtpms, and returns
> + * result to front end
> + */
> +static void tpm_ltpms_process_request(TPMLTPMsState *tpm_ltpms,
> + TPMLTPMsThreadParams *thr_parms)
> +{
> + TPM_RESULT res;
> + uint32_t in_len, out_len;
> + uint8_t *in, *out;
> + uint32_t resp_size;
> + TPMLocality *locty_data;
> +
> + DPRINTF("tpm_libtpms: processing command\n");
> +
> + tpm_ltpms->locty = thr_parms->tpm_state->locty_number;
> +
> + locty_data = thr_parms->tpm_state->locty_data;
> +
> + in = locty_data->w_buffer.buffer;
> + in_len = locty_data->w_offset;
> + out = locty_data->r_buffer.buffer;
> + out_len = locty_data->r_buffer.size;
> +
You would have to grab the initialized mutex here ...
> + if (tpm_ltpms->tpm_initialized) {
> + DPRINTF("tpm_libtpms: received %d bytes from VM in locality %d\n",
> + in_len, tpm_ltpms->locty);
> + DPRINTF_BUFFER(in, in_len);
> +
> + resp_size = 0;
> +
> + res = TPMLIB_Process(&out, &resp_size, &out_len, in, in_len);
> + if (res == TPM_SUCCESS) {
> + goto send_response;
> + }
> + qerror_report(ERROR_CLASS_GENERIC_ERROR,
> + "TPM libtpms command processing failed");
> + } else {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR,
> + "TPM libtpms not initialized");
> + }
> +
... and release it here to protect the TPMLIB_Process call as well.
> + resp_size = tpm_write_fatal_error_response(out, out_len);
> +
> +send_response:
> + DPRINTF("tpm_libtpms: sending %d bytes to TPM front-end\n", resp_size);
> + DPRINTF_BUFFER(out, resp_size);
> +
> + thr_parms->recv_data_callback(thr_parms->tpm_state, tpm_ltpms->locty);
> +
> + return;
> +}
> +
> +static void tpm_ltpms_worker_thread(gpointer data, gpointer user_data)
> +{
> + TPM_RESULT res;
> + TPMLTPMsThreadParams *thr_parms = user_data;
> + TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(thr_parms->tb);
> + TPMBackendCmd cmd = (TPMBackendCmd)data;
> +
> + tpm_backend = thr_parms->tb;
> +
> + DPRINTF("tpm_libtpms: processing command type %d\n", cmd);
> +
> + switch (cmd) {
> + case TPM_BACKEND_CMD_TPM_RESET:
To be consistent, you should probably move the locking to before this check here ...
> + if (tpm_ltpms->tpm_initialized) {
> + qemu_mutex_lock(&tpm_ltpms->tpm_initialized_mutex);
> + tpm_ltpms->tpm_initialized = false;
> + qemu_mutex_unlock(&tpm_ltpms->tpm_initialized_mutex);
> +
> + TPMLIB_Terminate();
> + }
and release it here.
> + /* fall through */
> + case TPM_BACKEND_CMD_INIT:
> + res = TPMLIB_MainInit();
> + if (res == TPM_SUCCESS) {
> + qemu_mutex_lock(&tpm_ltpms->tpm_initialized_mutex);
> + tpm_ltpms->tpm_initialized = true;
> + qemu_mutex_unlock(&tpm_ltpms->tpm_initialized_mutex);
> + } else {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR,
> + "TPM libtpms initialization failed");
> + abort();
> + }
> + break;
> + case TPM_BACKEND_CMD_PROCESS_CMD:
> + tpm_ltpms_process_request(tpm_ltpms, thr_parms);
> + break;
> + case TPM_BACKEND_CMD_END:
Same here ...
> + if (tpm_ltpms->tpm_initialized) {
> + qemu_mutex_lock(&tpm_ltpms->tpm_initialized_mutex);
> + tpm_ltpms->tpm_initialized = false;
> + qemu_mutex_unlock(&tpm_ltpms->tpm_initialized_mutex);
> +
> + TPMLIB_Terminate();
> + tpm_ltpms_free_nvram_all(tpm_ltpms);
> + }
> + break;
> + }
> +}
> +
> +/*****************************************************************
> + * libtpms TPM library callbacks
> + ****************************************************************/
> +
> +/*
> + * Called by libtpms before any access to persistent storage is done
> + */
> +static TPM_RESULT tpm_ltpms_nvram_init(void)
> +{
> + int rc;
> + TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tpm_backend);
> +
> + tpm_ltpms->bdrv = bdrv_find(tpm_backend->nvram_id);
> + if (!tpm_ltpms->bdrv) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR, "TPM 'nvram' drive not found");
> + abort();
> + }
> +
> + rc = tpm_nvram_bdrv_init(tpm_ltpms->bdrv);
> + if (rc) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR, "TPM NVRAM drive init failed");
> + abort();
> + }
> +
> + tpm_ltpms_get_nvram_offsets(tpm_ltpms);
> +
> + rc = tpm_ltpms_load_tpm_state_from_nvram(tpm_ltpms);
> + if (rc) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR, "TPM NVRAM load state failed");
> + abort();
> + }
> +
> + return TPM_SUCCESS;
> +}
> +
> +/*
> + * Called by libtpms when the TPM wants to load state from persistent
> + * storage
> + */
> +static TPM_RESULT tpm_ltpms_nvram_loaddata(unsigned char **data,
> + uint32_t *length,
> + uint32_t tpm_number,
> + const char *name)
> +{
> + TPM_RESULT rc = TPM_SUCCESS;
> + TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tpm_backend);
> + NVRAMEntry **entry = NULL;
> +
> + DPRINTF("tpm_libtpms: Loading NVRAM state '%s' from storage\n", name);
> +
> + if (tpm_ltpms->had_fatal_error) {
> + return TPM_FAIL;
> + }
> +
> + *length = 0;
> +
> + if (!strcmp(name, TPM_PERMANENT_ALL_NAME)) {
> + entry = &tpm_ltpms->perm_state_entry;
> + } else if (!strcmp(name, TPM_SAVESTATE_NAME)) {
> + entry = &tpm_ltpms->save_state_entry;
> + } else if (!strcmp(name, TPM_VOLATILESTATE_NAME)) {
> + entry = &tpm_ltpms->vola_state_entry;
> + }
> +
> + /* In-memory entries are allocated for the life of the backend */
> + assert(entry != NULL);
> +
> + *length = (*entry)->cur_size;
> + if (*length > 0) {
> + rc = TPM_Malloc(data, *length);
> + if (rc == TPM_SUCCESS) {
> + memcpy(*data, (*entry)->buffer, *length);
> + } else {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR,
> + "TPM memory allocation failed");
> + abort();
> + }
> + }
> +
> + if (*length == 0) {
> + rc = TPM_RETRY;
> + }
> +
> + DPRINTF("tpm_libtpms: Read %"PRIu32" bytes from storage\n", *length);
> +
> + return rc;
> +}
> +
> +/*
> + * Called by libtpms when the TPM wants to store state to persistent
> + * storage
> + */
> +static TPM_RESULT tpm_ltpms_nvram_storedata(const unsigned char *data,
> + uint32_t length,
> + uint32_t tpm_number,
> + const char *name)
> +{
> + TPM_RESULT rc = TPM_SUCCESS;
> + TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tpm_backend);
> + NVRAMEntry *entry = NULL;
> + uint32_t offset = 0, max_size = 0;
> +
> + DPRINTF("tpm_libtpms: Storing NVRAM state '%s' to storage\n", name);
> +
> + if (tpm_ltpms->had_fatal_error) {
> + return TPM_FAIL;
> + }
> +
> + if (!strcmp(name, TPM_PERMANENT_ALL_NAME)) {
> + entry = tpm_ltpms->perm_state_entry;
> + offset = tpm_ltpms->perm_state_entry_offset;
> + max_size = tpm_ltpms->perm_state_max_size;
> + } else if (!strcmp(name, TPM_SAVESTATE_NAME)) {
> + entry = tpm_ltpms->save_state_entry;
> + offset = tpm_ltpms->save_state_entry_offset;
> + max_size = tpm_ltpms->save_state_max_size;
> + } else if (!strcmp(name, TPM_VOLATILESTATE_NAME)) {
> + entry = tpm_ltpms->vola_state_entry;
> + offset = tpm_ltpms->vola_state_entry_offset;
> + max_size = tpm_ltpms->vola_state_max_size;
> + }
> +
> + /* In-memory entries are allocated for the life of the backend */
> + assert(entry != NULL);
> +
> + if (length > 0) {
> + rc = TPM_Realloc(&entry->buffer, length);
> + if (rc != TPM_SUCCESS) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR,
> + "TPM memory allocation failed");
> + abort();
> + }
> + memcpy(entry->buffer, data, length);
> + entry->cur_size = length;
> + } else {
> + tpm_ltpms_free_nvram_buffer(entry);
> + }
> +
> + if (tpm_ltpms_write_to_nvram(tpm_ltpms, offset, entry, max_size)) {
> + goto err_exit;
> + }
> +
> + DPRINTF("tpm_libtpms: Wrote %"PRIu32" bytes to storage\n", length);
> +
> + return rc;
> +
> +err_exit:
> + tpm_ltpms->had_fatal_error = true;
> +
> + return TPM_FAIL;
> +}
> +
> +/*
> + * Called by libtpms when the TPM wants to delete state from persistent
> + * storage
> + */
> +static TPM_RESULT tpm_ltpms_nvram_deletename(uint32_t tpm_number,
> + const char *name,
> + TPM_BOOL mustExist)
> +{
> + TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tpm_backend);
> + NVRAMEntry *entry = NULL;
> + uint32_t offset = 0, max_size = 0;
> +
> + DPRINTF("tpm_libtpms: Deleting NVRAM state '%s' from storage\n", name);
> +
> + if (tpm_ltpms->had_fatal_error) {
> + return TPM_FAIL;
> + }
> +
> + if (!strcmp(name, TPM_PERMANENT_ALL_NAME)) {
> + entry = tpm_ltpms->perm_state_entry;
> + offset = tpm_ltpms->perm_state_entry_offset;
> + max_size = tpm_ltpms->perm_state_max_size;
> + } else if (!strcmp(name, TPM_SAVESTATE_NAME)) {
> + entry = tpm_ltpms->save_state_entry;
> + offset = tpm_ltpms->save_state_entry_offset;
> + max_size = tpm_ltpms->save_state_max_size;
> + } else if (!strcmp(name, TPM_VOLATILESTATE_NAME)) {
> + entry = tpm_ltpms->vola_state_entry;
> + offset = tpm_ltpms->vola_state_entry_offset;
> + max_size = tpm_ltpms->vola_state_max_size;
> + }
> +
> + if (entry) {
> + tpm_ltpms_free_nvram_buffer(entry);
> +
> + if (tpm_ltpms_write_to_nvram(tpm_ltpms, offset, entry, max_size)) {
> + goto err_exit;
> + }
> + }
> +
> + DPRINTF("tpm_libtpms: Deleted NVRAM state '%s' from storage\n", name);
> +
> + return TPM_SUCCESS;
> +
> +err_exit:
> + tpm_ltpms->had_fatal_error = true;
> +
> + return TPM_FAIL;
> +}
> +
> +/*
> + * Called by libtpms to initialize the I/O subsystem of the TPM
> + */
> +static TPM_RESULT tpm_ltpms_io_init(void)
> +{
> + return TPM_SUCCESS;
> +}
> +
> +/*
> + * Called by libtpms when the TPM needs to determine the locality under
> + * which a command is supposed to be executed
> + */
> +static TPM_RESULT tpm_ltpms_io_getlocality(TPM_MODIFIER_INDICATOR *
> + localityModifier,
> + uint32_t tpm_number)
> +{
> + TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tpm_backend);
> +
> + *localityModifier = (TPM_MODIFIER_INDICATOR)tpm_ltpms->locty;
> +
> + return TPM_SUCCESS;
> +}
> +
> +/*
> + * Called by libtpms when the TPM needs to determine whether physical
> + * presence has been asserted
> + */
> +static TPM_RESULT tpm_ltpms_io_getphysicalpresence(TPM_BOOL *physicalPresence,
> + uint32_t tpm_number)
> +{
> + *physicalPresence = FALSE;
> +
> + return TPM_SUCCESS;
> +}
> +
> +struct libtpms_callbacks callbacks = {
> + .sizeOfStruct = sizeof(struct libtpms_callbacks),
> + .tpm_nvram_init = tpm_ltpms_nvram_init,
> + .tpm_nvram_loaddata = tpm_ltpms_nvram_loaddata,
> + .tpm_nvram_storedata = tpm_ltpms_nvram_storedata,
> + .tpm_nvram_deletename = tpm_ltpms_nvram_deletename,
> + .tpm_io_init = tpm_ltpms_io_init,
> + .tpm_io_getlocality = tpm_ltpms_io_getlocality,
> + .tpm_io_getphysicalpresence = tpm_ltpms_io_getphysicalpresence,
> +};
> +
> +/*****************************************************************/
> +
> +/*
> + * Start the TPM (thread). If it had been started before, then terminate
> + * and start it again.
> + */
> +static int tpm_ltpms_startup_tpm(TPMBackend *tb)
> +{
> + struct TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tb);
> +
> + /* 'power-reset' a running TPM; if none is running start one */
> + tpm_backend_thread_tpm_reset(&tpm_ltpms->tbt, tpm_ltpms_worker_thread,
> + &tpm_ltpms->tpm_thread_params);
> +
> + return 0;
> +}
> +
> +static void tpm_ltpms_terminate_tpm_thread(TPMBackend *tb)
> +{
> + struct TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tb);
> +
> + tpm_backend_thread_end(&tpm_ltpms->tbt);
> +}
> +
> +static void tpm_ltpms_reset(TPMBackend *tb)
> +{
> + TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tb);
> +
> + DPRINTF("tpm_libtpms: Resetting TPM libtpms backend\n");
> +
> + tpm_ltpms_terminate_tpm_thread(tb);
> +
> + tpm_ltpms->had_fatal_error = false;
> +}
> +
> +static int tpm_ltpms_init(TPMBackend *tb, TPMState *s,
> + TPMRecvDataCB *recv_data_cb)
> +{
> + TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tb);
> +
> + if (TPMLIB_RegisterCallbacks(&callbacks) != TPM_SUCCESS) {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR,
> + "TPM libtpms callback registration failed");
> + return -1;
> + }
> +
> + tpm_ltpms->tpm_thread_params.tpm_state = s;
> + tpm_ltpms->tpm_thread_params.recv_data_callback = recv_data_cb;
> + tpm_ltpms->tpm_thread_params.tb = tb;
> +
> + qemu_mutex_init(&tpm_ltpms->tpm_initialized_mutex);
> +
> + return 0;
> +}
> +
> +static bool tpm_ltpms_get_tpm_established_flag(TPMBackend *tb)
> +{
> + TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tb);
> + TPM_BOOL tpmEstablished = false;
> +
> + qemu_mutex_lock(&tpm_ltpms->tpm_initialized_mutex);
> + if (tpm_ltpms->tpm_initialized) {
> + TPM_IO_TpmEstablished_Get(&tpmEstablished);
> + }
> + qemu_mutex_unlock(&tpm_ltpms->tpm_initialized_mutex);
> +
> + return tpmEstablished;
> +}
> +
> +static bool tpm_ltpms_get_startup_error(TPMBackend *tb)
> +{
> + return false;
> +}
> +
> +static size_t tpm_ltpms_realloc_buffer(TPMSizedBuffer *sb)
> +{
> + size_t wanted_size = tpmlib_get_prop(TPMPROP_TPM_BUFFER_MAX);
> +
> + if (sb->size != wanted_size) {
> + TPM_RESULT res = TPM_Realloc(&sb->buffer, wanted_size);
> + if (res == TPM_SUCCESS) {
> + sb->size = wanted_size;
> + } else {
> + qerror_report(ERROR_CLASS_GENERIC_ERROR,
> + "TPM memory allocation failed");
> + abort();
> + }
> + }
> + return sb->size;
> +}
> +
> +static void tpm_ltpms_deliver_request(TPMBackend *tb)
> +{
> + TPMLTPMsState *tpm_ltpms = TPM_LIBTPMS(tb);
> +
> + tpm_backend_thread_deliver_request(&tpm_ltpms->tbt);
> +}
> +
> +static void tpm_ltpms_cancel_cmd(TPMBackend *be)
> +{
> +}
> +
> +static const char *tpm_ltpms_create_desc(void)
> +{
> + return "libtpms TPM backend driver";
> +}
> +
> +static TPMBackend *tpm_ltpms_create(QemuOpts *opts, const char *id)
> +{
> + Object *obj = object_new(TYPE_TPM_LIBTPMS);
> + TPMBackend *tb = TPM_BACKEND(obj);
> + const char *value;
> +
> + tb->id = g_strdup(id);
> + tb->fe_model = -1;
> + tb->ops = &tpm_ltpms_driver;
> +
> + value = qemu_opt_get(opts, "nvram");
> + if (!value) {
> + qerror_report(QERR_MISSING_PARAMETER, "nvram");
> + goto err_exit;
> + }
> + tb->nvram_id = g_strdup(value);
> +
> + return tb;
> +
> +err_exit:
> + g_free(tb->id);
> +
> + return NULL;
> +}
> +
> +static void tpm_ltpms_destroy(TPMBackend *tb)
> +{
> + tpm_ltpms_terminate_tpm_thread(tb);
> +
> + g_free(tb->id);
> + g_free(tb->nvram_id);
> +}
> +
> +static const QemuOptDesc tpm_ltpms_cmdline_opts[] = {
> + TPM_STANDARD_CMDLINE_OPTS,
> + {
> + .name = "nvram",
> + .type = QEMU_OPT_STRING,
> + .help = "NVRAM drive id",
> + },
> + { /* end of list */ },
> +};
> +
> +static const TPMDriverOps tpm_ltpms_driver = {
> + .type = TPM_TYPE_LIBTPMS,
> + .opts = tpm_ltpms_cmdline_opts,
> + .desc = tpm_ltpms_create_desc,
> + .create = tpm_ltpms_create,
> + .destroy = tpm_ltpms_destroy,
> + .init = tpm_ltpms_init,
> + .startup_tpm = tpm_ltpms_startup_tpm,
> + .realloc_buffer = tpm_ltpms_realloc_buffer,
> + .reset = tpm_ltpms_reset,
> + .had_startup_error = tpm_ltpms_get_startup_error,
> + .deliver_request = tpm_ltpms_deliver_request,
> + .cancel_cmd = tpm_ltpms_cancel_cmd,
> + .get_tpm_established_flag = tpm_ltpms_get_tpm_established_flag,
> +};
> +
> +
> +static void tpm_ltpms_inst_init(Object *obj)
> +{
> +}
> +
> +static void tpm_ltpms_inst_finalize(Object *obj)
> +{
> +}
> +
> +static void tpm_ltpms_class_init(ObjectClass *klass, void *data)
> +{
> + TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
> +
> + tbc->ops = &tpm_ltpms_driver;
> +}
> +
> +static const TypeInfo tpm_ltpms_info = {
> + .name = TYPE_TPM_LIBTPMS,
> + .parent = TYPE_TPM_BACKEND,
> + .instance_size = sizeof(TPMLTPMsState),
> + .class_init = tpm_ltpms_class_init,
> + .instance_init = tpm_ltpms_inst_init,
> + .instance_finalize = tpm_ltpms_inst_finalize,
> +};
> +
> +static void tpm_libtpms_register(void)
> +{
> + type_register_static(&tpm_ltpms_info);
> + tpm_register_driver(&tpm_ltpms_driver);
> +}
> +
> +type_init(tpm_libtpms_register)
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 5dc8b75..356978e 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -2251,7 +2251,9 @@ DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
> "-tpmdev passthrough,id=id[,path=path][,cancel-path=path]\n"
> " use path to provide path to a character device; default is /dev/tpm0\n"
> " use cancel-path to provide path to TPM's cancel sysfs entry; if\n"
> - " not provided it will be searched for in /sys/class/misc/tpm?/device\n",
> + " not provided it will be searched for in /sys/class/misc/tpm?/device\n"
> + "-tpmdev libtpms,id=id,nvram=drive-id\n"
> + " use nvram to provide the NVRAM drive id\n",
> QEMU_ARCH_ALL)
> STEXI
>
> @@ -2261,7 +2263,8 @@ The general form of a TPM device option is:
> @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
> @findex -tpmdev
> Backend type must be:
> -@option{passthrough}.
> +@option{passthrough}, or
> +@option{libtpms}.
>
> The specific backend type will determine the applicable options.
> The @code{-tpmdev} option creates the TPM backend and requires a
> @@ -2311,6 +2314,30 @@ To create a passthrough TPM use the following two options:
> Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
> @code{tpmdev=tpm0} in the device option.
>
> +@item -tpmdev libtpms, id=@var{id}, nvram=@var{drive-id}
> +
> +Enable access to the libtpms-based emulated TPM.
> +
> +@option{nvram} specifies the drive id of the NVRAM drive.
> +
> +Some notes about using the libtpms-based emulated TPM:
> +
> +To create a libtpms-based TPM, use the following options:
> +@example
> +-drive file=<path to image file>,if=none,id=tpm-nvram \
> +-tpmdev libtpms,id=tpm0,nvram=tpm-nvram \
> +-device tpm-tis,tpmdev=tpm0
> +@end example
> +
> +The @code{drive} option provides the path to the image file where the
> +TPM's persistent NVRAM data will be stored. Using the @code{qemu-img} tool,
> +such an image can be created with a size of 500K.
> +
> +Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
> +@code{tpmdev=tpm0} in the @code{-device} option. Similarly, the @code{-drive}
> +id @code{tpm-nvram} is referenced by @code{nvram=tpm-nvram} in the
> +@code{-tpmdev} option.
> +
> @end table
>
> ETEXI
> diff --git a/tpm.c b/tpm.c
> index 2f4ef52..a08e3fd 100644
> --- a/tpm.c
> +++ b/tpm.c
> @@ -26,7 +26,7 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =
>
>
> #define TPM_MAX_MODELS 1
> -#define TPM_MAX_DRIVERS 1
> +#define TPM_MAX_DRIVERS 2
>
> static TPMDriverOps const *be_drivers[TPM_MAX_DRIVERS] = {
> NULL,
Stefan
next prev parent reply other threads:[~2013-11-19 19:38 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-11-06 14:39 [Qemu-devel] [PATCH 4/4] tpm: Provide libtpms software TPM backend Corey Bryant
2013-11-19 19:37 ` Stefan Berger [this message]
-- strict thread matches above, loose matches on Subject: below --
2013-11-25 3:36 Xu, Quan
2013-11-25 13:53 ` Corey Bryant
2013-11-26 3:04 ` Xu, Quan
2013-11-26 14:40 ` Corey Bryant
2013-12-02 4:00 ` Xu, Quan
2013-12-02 14:16 ` Corey Bryant
2013-12-02 15:21 ` Stefan Berger
2013-12-16 12:45 ` Xu, Quan
2014-03-21 11:50 ` Jordi Cucurull Juan
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=528BBE09.4070406@linux.vnet.ibm.com \
--to=stefanb@linux.vnet.ibm.com \
--cc=aliguori@amazon.com \
--cc=coreyb@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).