From patchwork Wed Nov 6 14:38:51 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Bryant X-Patchwork-Id: 288936 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id BAAE12C008E for ; Thu, 7 Nov 2013 01:39:47 +1100 (EST) Received: from localhost ([::1]:34305 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ve4Gn-0005VV-SM for incoming@patchwork.ozlabs.org; Wed, 06 Nov 2013 09:39:45 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:47546) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ve4GC-0005Nv-IY for qemu-devel@nongnu.org; Wed, 06 Nov 2013 09:39:17 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Ve4G3-0000Au-Iy for qemu-devel@nongnu.org; Wed, 06 Nov 2013 09:39:08 -0500 Received: from e31.co.us.ibm.com ([32.97.110.149]:46111) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ve4G3-0000Ap-Az for qemu-devel@nongnu.org; Wed, 06 Nov 2013 09:38:59 -0500 Received: from /spool/local by e31.co.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Wed, 6 Nov 2013 07:38:58 -0700 Received: from d03dlp02.boulder.ibm.com (9.17.202.178) by e31.co.us.ibm.com (192.168.1.131) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Wed, 6 Nov 2013 07:38:56 -0700 Received: from d03relay01.boulder.ibm.com (d03relay01.boulder.ibm.com [9.17.195.226]) by d03dlp02.boulder.ibm.com (Postfix) with ESMTP id DF8C93E40044 for ; Wed, 6 Nov 2013 07:38:55 -0700 (MST) Received: from d03av02.boulder.ibm.com (d03av02.boulder.ibm.com [9.17.195.168]) by d03relay01.boulder.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id rA6EcsCB325868 for ; Wed, 6 Nov 2013 07:38:54 -0700 Received: from d03av02.boulder.ibm.com (localhost [127.0.0.1]) by d03av02.boulder.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id rA6EcsGW007100 for ; Wed, 6 Nov 2013 07:38:54 -0700 Received: from localhost ([9.80.103.90]) by d03av02.boulder.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id rA6Ecrqk007060; Wed, 6 Nov 2013 07:38:53 -0700 From: Corey Bryant To: qemu-devel@nongnu.org Date: Wed, 6 Nov 2013 09:38:51 -0500 Message-Id: <1383748731-8776-1-git-send-email-coreyb@linux.vnet.ibm.com> X-Mailer: git-send-email 1.8.1.4 X-TM-AS-MML: disable X-Content-Scanned: Fidelis XPS MAILER x-cbid: 13110614-8236-0000-0000-0000037BDBE2 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] X-Received-From: 32.97.110.149 Cc: Corey Bryant , aliguori@amazon.com Subject: [Qemu-devel] [PATCH 1/4] tpm: Add TPM NVRAM implementation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Provides TPM NVRAM implementation that enables storing of TPM NVRAM data in a persistent image file. The block driver is used to read/write the drive image. This will enable, for example, an encrypted QCOW2 image to be used to store sensitive keys. This patch provides APIs that a TPM backend can use to read and write data. Signed-off-by: Corey Bryant Reviewed-by: Stefan Hajnoczi --- v1 -This patch was formerly known as: "[PATCH v3 1/3] nvram: Add TPM NVRAM implementation" -Updated g_malloc to TPM_Malloc in tpm_nvram_co_read -Added tpm_nvram_bh NULL check to tpm_nvram_read/write -Change API names to tpm_nvram_bdrv_* to avoid conflict with libtpms' tpm_nvram_init -Moved makefile support to libtpms patch --- hw/tpm/tpm_nvram.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/tpm/tpm_nvram.h | 25 ++++ 2 files changed, 365 insertions(+), 0 deletions(-) create mode 100644 hw/tpm/tpm_nvram.c create mode 100644 hw/tpm/tpm_nvram.h diff --git a/hw/tpm/tpm_nvram.c b/hw/tpm/tpm_nvram.c new file mode 100644 index 0000000..ded6d66 --- /dev/null +++ b/hw/tpm/tpm_nvram.c @@ -0,0 +1,340 @@ +/* + * TPM NVRAM - enables storage of persistent NVRAM data on an image file + * + * Copyright (C) 2013 IBM Corporation + * + * Authors: + * Stefan Berger + * Corey Bryant + * + * 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 "tpm_nvram.h" +#include "block/block_int.h" +#include "qemu/thread.h" +#include "sysemu/sysemu.h" + +#include +#include + +#define TPM_NVRAM_DEBUG 0 +#define DPRINTF(fmt, ...) \ + do { \ + if (TPM_NVRAM_DEBUG) { \ + fprintf(stderr, fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +/* Read/write request data */ +typedef struct TPMNvramRWRequest { + BlockDriverState *bdrv; + bool is_write; + uint64_t offset; + uint8_t **blob_r; + uint8_t *blob_w; + uint32_t size; + int rc; + + QemuMutex completion_mutex; + QemuCond completion; + + QSIMPLEQ_ENTRY(TPMNvramRWRequest) list; +} TPMNvramRWRequest; + +/* Mutex protected queue of read/write requests */ +static QemuMutex tpm_nvram_rwrequests_mutex; +static QSIMPLEQ_HEAD(, TPMNvramRWRequest) tpm_nvram_rwrequests = + QSIMPLEQ_HEAD_INITIALIZER(tpm_nvram_rwrequests); + +static QEMUBH *tpm_nvram_bh; + +/* + * Get the disk size in kilobytes needed to store a blob (rounded up to next kb) + */ +static uint64_t tpm_nvram_required_size_kb(uint64_t offset, uint32_t size) +{ + uint64_t required_size = offset + size; + return DIV_ROUND_UP(required_size, 1024); +} + +/* + * Increase the drive size if it's too small to store the blob + */ +static int tpm_nvram_adjust_size(BlockDriverState *bdrv, uint64_t offset, + uint32_t size) +{ + int rc = 0; + int64_t drive_size, required_size; + + rc = bdrv_getlength(bdrv); + if (rc < 0) { + DPRINTF("%s: Unable to determine TPM NVRAM drive size\n", __func__); + return rc; + } + + drive_size = rc; + required_size = tpm_nvram_required_size_kb(offset, size) * 1024; + + if (drive_size < required_size) { + rc = bdrv_truncate(bdrv, required_size); + if (rc < 0) { + DPRINTF("%s: TPM NVRAM drive too small\n", __func__); + } + } + + return rc; +} + +/* + * Coroutine that reads a blob from the drive asynchronously + */ +static void coroutine_fn tpm_nvram_co_read(void *opaque) +{ + int rc; + TPMNvramRWRequest *rwr = opaque; + + rc = TPM_Malloc((unsigned char **)rwr->blob_r, rwr->size); + if (rc != TPM_SUCCESS) { + goto exit; + } + + rc = bdrv_pread(rwr->bdrv, rwr->offset, *rwr->blob_r, rwr->size); + if (rc != rwr->size) { + TPM_Free(*rwr->blob_r); + *rwr->blob_r = NULL; + } + +exit: + qemu_mutex_lock(&rwr->completion_mutex); + rwr->rc = rc; + qemu_cond_signal(&rwr->completion); + qemu_mutex_unlock(&rwr->completion_mutex); +} + +/* + * Coroutine that writes a blob to the drive asynchronously + */ +static void coroutine_fn tpm_nvram_co_write(void *opaque) +{ + int rc; + TPMNvramRWRequest *rwr = opaque; + + rc = tpm_nvram_adjust_size(rwr->bdrv, rwr->offset, rwr->size); + if (rc < 0) { + goto exit; + } + + rc = bdrv_pwrite(rwr->bdrv, rwr->offset, rwr->blob_w, rwr->size); + +exit: + qemu_mutex_lock(&rwr->completion_mutex); + rwr->rc = rc; + qemu_cond_signal(&rwr->completion); + qemu_mutex_unlock(&rwr->completion_mutex); +} + +/* + * Enter a coroutine to read a blob from the drive + */ +static void tpm_nvram_do_co_read(TPMNvramRWRequest *rwr) +{ + Coroutine *co; + + co = qemu_coroutine_create(tpm_nvram_co_read); + qemu_coroutine_enter(co, rwr); +} + +/* + * Enter a coroutine to write a blob to the drive + */ +static void tpm_nvram_do_co_write(TPMNvramRWRequest *rwr) +{ + Coroutine *co; + + co = qemu_coroutine_create(tpm_nvram_co_write); + qemu_coroutine_enter(co, rwr); +} + +/* + * Initialization for read requests + */ +static TPMNvramRWRequest *tpm_nvram_rwrequest_init_read(BlockDriverState *bdrv, + uint64_t offset, + uint8_t **blob, + uint32_t size) +{ + TPMNvramRWRequest *rwr; + + rwr = g_new0(TPMNvramRWRequest, 1); + rwr->bdrv = bdrv; + rwr->is_write = false; + rwr->offset = offset; + rwr->blob_r = blob; + rwr->size = size; + rwr->rc = -EINPROGRESS; + + qemu_mutex_init(&rwr->completion_mutex); + qemu_cond_init(&rwr->completion); + + return rwr; +} + +/* + * Initialization for write requests + */ +static TPMNvramRWRequest *tpm_nvram_rwrequest_init_write(BlockDriverState *bdrv, + uint64_t offset, + uint8_t *blob, + uint32_t size) +{ + TPMNvramRWRequest *rwr; + + rwr = g_new0(TPMNvramRWRequest, 1); + rwr->bdrv = bdrv; + rwr->is_write = true; + rwr->offset = offset; + rwr->blob_w = blob; + rwr->size = size; + rwr->rc = -EINPROGRESS; + + qemu_mutex_init(&rwr->completion_mutex); + qemu_cond_init(&rwr->completion); + + return rwr; +} + +/* + * Execute a read or write of TPM NVRAM blob data + */ +static void tpm_nvram_rwrequest_exec(TPMNvramRWRequest *rwr) +{ + if (rwr->is_write) { + tpm_nvram_do_co_write(rwr); + } else { + tpm_nvram_do_co_read(rwr); + } +} + +/* + * Bottom-half callback that is invoked by QEMU's main thread to + * process TPM NVRAM read/write requests. + */ +static void tpm_nvram_rwrequest_callback(void *opaque) +{ + TPMNvramRWRequest *rwr, *next; + + qemu_mutex_lock(&tpm_nvram_rwrequests_mutex); + + QSIMPLEQ_FOREACH_SAFE(rwr, &tpm_nvram_rwrequests, list, next) { + QSIMPLEQ_REMOVE(&tpm_nvram_rwrequests, rwr, TPMNvramRWRequest, list); + + qemu_mutex_unlock(&tpm_nvram_rwrequests_mutex); + tpm_nvram_rwrequest_exec(rwr); + qemu_mutex_lock(&tpm_nvram_rwrequests_mutex); + } + + qemu_mutex_unlock(&tpm_nvram_rwrequests_mutex); +} + +/* + * Schedule a bottom-half to read or write a blob to the TPM NVRAM drive + */ +static void tpm_nvram_rwrequest_schedule(TPMNvramRWRequest *rwr) +{ + qemu_mutex_lock(&tpm_nvram_rwrequests_mutex); + QSIMPLEQ_INSERT_TAIL(&tpm_nvram_rwrequests, rwr, list); + qemu_mutex_unlock(&tpm_nvram_rwrequests_mutex); + + qemu_bh_schedule(tpm_nvram_bh); + + /* Wait for completion of the read/write request */ + qemu_mutex_lock(&rwr->completion_mutex); + while (rwr->rc == -EINPROGRESS) { + qemu_cond_wait(&rwr->completion, &rwr->completion_mutex); + } + qemu_mutex_unlock(&rwr->completion_mutex); +} + +/* + * Initialize a TPM NVRAM drive + */ +int tpm_nvram_bdrv_init(BlockDriverState *bdrv) +{ + qemu_mutex_init(&tpm_nvram_rwrequests_mutex); + tpm_nvram_bh = qemu_bh_new(tpm_nvram_rwrequest_callback, NULL); + + if (bdrv_is_read_only(bdrv)) { + DPRINTF("%s: TPM NVRAM drive '%s' is read-only\n", __func__, + bdrv->filename); + return -EPERM; + } + + bdrv_lock_medium(bdrv, true); + + DPRINTF("%s: TPM NVRAM drive '%s' initialized successfully\n", __func__, + bdrv->filename); + + return 0; +} + +/* + * Read a TPM NVRAM blob from the drive + */ +int tpm_nvram_bdrv_read(BlockDriverState *bdrv, uint64_t offset, + uint8_t **blob, uint32_t size) +{ + int rc; + TPMNvramRWRequest *rwr; + + if (!tpm_nvram_bh) { + DPRINTF("%s: tpm_nvram_bdrv_init must be called first\n", __func__); + return -EPERM; + } + + *blob = NULL; + + rwr = tpm_nvram_rwrequest_init_read(bdrv, offset, blob, size); + tpm_nvram_rwrequest_schedule(rwr); + rc = rwr->rc; + + if (rc != rwr->size) { + DPRINTF("%s: TPM NVRAM read failed\n", __func__); + } else { + DPRINTF("%s: TPM NVRAM read successful: offset=%"PRIu64", " + "size=%"PRIu32"\n", __func__, rwr->offset, rwr->size); + } + + g_free(rwr); + return rc; +} + +/* + * Write a TPM NVRAM blob to the drive + */ +int tpm_nvram_bdrv_write(BlockDriverState *bdrv, uint64_t offset, + uint8_t *blob, uint32_t size) +{ + int rc; + TPMNvramRWRequest *rwr; + + if (!tpm_nvram_bh) { + DPRINTF("%s: tpm_nvram_bdrv_init must be called first\n", __func__); + return -EPERM; + } + + rwr = tpm_nvram_rwrequest_init_write(bdrv, offset, blob, size); + tpm_nvram_rwrequest_schedule(rwr); + rc = rwr->rc; + + if (rc != rwr->size) { + DPRINTF("%s: TPM NVRAM write failed\n", __func__); + } else { + DPRINTF("%s: TPM NVRAM write successful: offset=%"PRIu64", " + "size=%"PRIu32"\n", __func__, rwr->offset, rwr->size); + } + + g_free(rwr); + return rc; +} diff --git a/hw/tpm/tpm_nvram.h b/hw/tpm/tpm_nvram.h new file mode 100644 index 0000000..75e9234 --- /dev/null +++ b/hw/tpm/tpm_nvram.h @@ -0,0 +1,25 @@ +/* + * TPM NVRAM - enables storage of persistent NVRAM data on an image file + * + * Copyright (C) 2013 IBM Corporation + * + * Authors: + * Stefan Berger + * Corey Bryant + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TPM_TPM_NVRAM_H +#define TPM_TPM_NVRAM_H + +#include "block/block.h" + +int tpm_nvram_bdrv_init(BlockDriverState *bdrv); +int tpm_nvram_bdrv_read(BlockDriverState *bdrv, uint64_t offset, + uint8_t **blob, uint32_t size); +int tpm_nvram_bdrv_write(BlockDriverState *bdrv, uint64_t offset, + uint8_t *blob, uint32_t size); + +#endif