From patchwork Fri May 6 17:32:30 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Berger X-Patchwork-Id: 94418 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 7EFD1B6FE0 for ; Sat, 7 May 2011 03:37:02 +1000 (EST) Received: from localhost ([::1]:36046 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QIOxX-0004Df-Tt for incoming@patchwork.ozlabs.org; Fri, 06 May 2011 13:36:59 -0400 Received: from eggs.gnu.org ([140.186.70.92]:43861) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QIOu1-0006kI-3l for qemu-devel@nongnu.org; Fri, 06 May 2011 13:33:22 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QIOtx-0003rp-2y for qemu-devel@nongnu.org; Fri, 06 May 2011 13:33:21 -0400 Received: from e34.co.us.ibm.com ([32.97.110.152]:54441) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QIOtw-0003rc-QG for qemu-devel@nongnu.org; Fri, 06 May 2011 13:33:17 -0400 Received: from d03relay01.boulder.ibm.com (d03relay01.boulder.ibm.com [9.17.195.226]) by e34.co.us.ibm.com (8.14.4/8.13.1) with ESMTP id p46HKo1Q024010 for ; Fri, 6 May 2011 11:20:50 -0600 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 p46HWmbV061810 for ; Fri, 6 May 2011 11:32:50 -0600 Received: from d03av02.boulder.ibm.com (loopback [127.0.0.1]) by d03av02.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p46BWK9l004503 for ; Fri, 6 May 2011 05:32:21 -0600 Received: from localhost.localdomain (d941e-10.watson.ibm.com [9.59.241.154]) by d03av02.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p46BWKhm004403 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Fri, 6 May 2011 05:32:20 -0600 Received: from localhost.localdomain (d941e-10 [127.0.0.1]) by localhost.localdomain (8.14.4/8.14.3) with ESMTP id p46HWkKH001400; Fri, 6 May 2011 13:32:46 -0400 Received: (from root@localhost) by localhost.localdomain (8.14.4/8.14.4/Submit) id p46HWkpS001399; Fri, 6 May 2011 13:32:46 -0400 Message-Id: <20110506173246.617414609@linux.vnet.ibm.com> User-Agent: quilt/0.48-1 Date: Fri, 06 May 2011 13:32:30 -0400 From: Stefan Berger To: stefanb@linux.vnet.ibm.com, qemu-devel@nongnu.org References: <20110506173224.278066589@linux.vnet.ibm.com> Content-Disposition: inline; filename=qemu_tpm_be_skeleton.diff X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 32.97.110.152 Cc: andreas.niederl@iaik.tugraz.at, serge@hallyn.com Subject: [Qemu-devel] [PATCH V4 06/10] Add a TPM backend skeleton 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 This patch provides a TPM backend skeleton implementation. It doesn't do anything useful (except for returning error response for every TPM command) but it compiles. v3: - in tpm_builtin.c all functions prefixed with tpm_builtin_ - build the builtin TPM driver available at this point; it returns a failure response message for every command - do not try to join the TPM thread but poll for its termination; the libtpms-based driver will require Qemu's main thread to write data to the block storage device while trying to join V2: - only terminating thread in tpm_atexit if it's running Signed-off-by: Stefan Berger --- Makefile.target | 5 configure | 1 hw/tpm_builtin.c | 425 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/tpm_tis.c | 3 hw/tpm_tis.h | 2 5 files changed, 436 insertions(+) Index: qemu-git/hw/tpm_builtin.c =================================================================== --- /dev/null +++ qemu-git/hw/tpm_builtin.c @@ -0,0 +1,425 @@ +/* + * builtin 'null' TPM driver + * + * Copyright (c) 2010, 2011 IBM Corporation + * Copyright (c) 2010, 2011 Stefan Berger + * + * 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 + */ + +#include "qemu-common.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; +} ThreadParams; + + +/* local variables */ + +static QemuThread thread; + +static QemuMutex state_mutex; /* protects *_state below */ +static QemuMutex tpm_initialized_mutex; /* protect tpm_initialized */ + +static bool thread_terminate = false; +static bool tpm_initialized = false; +static bool had_fatal_error = false; +static bool had_startup_error = false; +static bool thread_running = false; + +static ThreadParams tpm_thread_params; + +/* locality of the command being executed by libtpms */ +static uint8_t g_locty; + +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 void *tpm_builtin_main_loop(void *d) +{ + int res = 1; + ThreadParams *thr_parms = d; + uint32_t in_len, out_len; + uint8_t *in, *out; + uint32_t resp_size; /* total length of response */ + +#ifdef DEBUG_TPM + fprintf(stderr, "tpm: THREAD IS STARTING\n"); +#endif + + if (res != 0) { +#if defined DEBUG_TPM || defined DEBUG_TPM_SR + fprintf(stderr, "tpm: Error: TPM initialization failed (rc=%d)\n", + res); +#endif + had_fatal_error = true; + } else { + qemu_mutex_lock(&tpm_initialized_mutex); + + tpm_initialized = true; + + qemu_mutex_unlock(&tpm_initialized_mutex); + } + + /* 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; + } + + g_locty = thr_parms->tpm_state->command_locty; + + in = thr_parms->tpm_state->loc[g_locty].w_buffer.buffer; + in_len = thr_parms->tpm_state->loc[g_locty].w_offset; + + if (!had_fatal_error) { + + out_len = thr_parms->tpm_state->loc[g_locty].r_buffer.size; + +#ifdef DEBUG_TPM + fprintf(stderr, + "tpm: received %d bytes from VM in locality %d\n", + in_len, + g_locty); + dumpBuffer(stderr, in, in_len); +#endif + + resp_size = 0; + + /* !!! Send command to TPM & wait for response */ + + if (res != 0) { +#ifdef DEBUG_TPM + fprintf(stderr, + "tpm: Sending/receiving TPM request/response " + "failed.\n"); +#endif + had_fatal_error = 1; + } + } + + if (had_fatal_error) { + out = thr_parms->tpm_state->loc[g_locty].r_buffer.buffer; + resp_size = sizeof(tpm_std_fatal_error_response); + memcpy(out, tpm_std_fatal_error_response, resp_size); + out[1] = (in_len > 2 && in[1] >= 0xc1 && in[1] <= 0xc3) + ? in[1] + 3 + : 0xc4; + } +#ifdef DEBUG_TPM + fprintf(stderr, "tpm: sending %d bytes to VM\n", resp_size); + dumpBuffer(stderr, + thr_parms->tpm_state->loc[g_locty].r_buffer.buffer, + resp_size); +#endif + thr_parms->recv_data_callback(thr_parms->tpm_state, g_locty); + } while (in_len > 0); + } + + qemu_mutex_lock(&tpm_initialized_mutex); + + if (tpm_initialized) { + tpm_initialized = false; + } + + qemu_mutex_unlock(&tpm_initialized_mutex); + +#ifdef DEBUG_TPM + fprintf(stderr, "tpm: THREAD IS ENDING\n"); +#endif + + thread_running = false; + + return NULL; +} + + +static void tpm_builtin_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); + + /* The thread will set thread_running = false; it may + * still ask us to write data to the disk, though. + */ + while (thread_running) { + /* !!! write data to disk if necessary */ + usleep(100000); + } + + memset(&thread, 0, sizeof(thread)); + } +} + + +static void tpm_builtin_tpm_atexit(void) +{ + tpm_builtin_terminate_tpm_thread(); +} + + +/** + * Start the TPM (thread). If it had been started before, then terminate + * and start it again. + */ +static int tpm_builtin_startup_tpm(void) +{ + /* terminate a running TPM */ + tpm_builtin_terminate_tpm_thread(); + + /* reset the flag so the thread keeps on running */ + thread_terminate = false; + + qemu_thread_create(&thread, tpm_builtin_main_loop, &tpm_thread_params); + + thread_running = true; + + return 0; +} + + +static int tpm_builtin_do_startup_tpm(void) +{ + return tpm_builtin_startup_tpm(); +} + + +/* + * Startup the TPM early. This only works for non-encrytped + * BlockStorage, since we would not have the key yet. + */ +static int tpm_builtin_early_startup_tpm(void) +{ + return tpm_builtin_do_startup_tpm(); +} + + +/* + * Start up the TPM before it sees the first command. + * We need to do this late since only now we will have the + * block storage encryption key and can read the previous + * TPM state. During 'reset' the key would not be available. + */ +static int tpm_builtin_late_startup_tpm(void) +{ + /* give it a new try */ + had_fatal_error = false; + had_startup_error = false; + + if (tpm_builtin_do_startup_tpm()) { + had_fatal_error = true; + } + + return had_fatal_error; +} + + +static void tpm_builtin_reset(void) +{ +#if defined DEBUG_TPM || defined DEBUG_TPM_SR + fprintf(stderr, "tpm: CALL TO TPM_RESET!\n"); +#endif + + tpm_builtin_terminate_tpm_thread(); + + had_fatal_error = false; + had_startup_error = false; +} + + +/* + * restore TPM volatile state from given data + * + * The data are ignore by this driver, instead we read the volatile state + * from the TPM block store. + * + * This function gets called by Qemu when + * (1) resuming after a suspend + * (2) resuming a snapshot + * + * (1) works fine since we get call to the reset function as well + * (2) requires us to call the reset function ourselves; we do this + * indirectly by calling the tis_reset_for_snapshot_resume(); + * a sure indicator of whether this function is called due to a resume + * of a snapshot is that the tread_running variable is 'true'. + * + */ +static int tpm_builtin_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_builtin_init(TPMState *s, TPMRecvDataCB *recv_data_cb) +{ + tpm_thread_params.tpm_state = s; + tpm_thread_params.recv_data_callback = recv_data_cb; + + qemu_mutex_init(&state_mutex); + qemu_mutex_init(&tpm_initialized_mutex); + + atexit(tpm_builtin_tpm_atexit); + + return 0; +} + + +static bool tpm_builtin_get_tpm_established_flag(void) +{ + return false; +} + + +static bool tpm_builtin_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_builtin_save_volatile_data(void) +{ + if (!tpm_initialized) { + /* TPM was never initialized + volatile_state.buffer may be NULL if TPM was never used. + */ + return 0; + } + + return 0; +} + + +static size_t tpm_builtin_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_builtin_create_desc(void) +{ + static int done; + + if (!done) { + snprintf(dev_description, sizeof(dev_description), + "Skeleton TPM backend"); + done = 1; + } + + return dev_description; +} + + +static bool tpm_builtin_handle_options(QemuOpts *opts) +{ + const char *value; + + value = qemu_opt_get(opts, "path"); + if (value) { + /* !!! handle file path */ + } else { + fprintf(stderr, "-tpm is missing path= parameter\n"); + return false; + } + return true; +} + + +BackendTPMDriver tpm_builtin = { + .id = "builtin", + .desc = tpm_builtin_create_desc, + .job_for_main_thread = NULL, + .handle_options = tpm_builtin_handle_options, + .init = tpm_builtin_init, + .early_startup_tpm = tpm_builtin_early_startup_tpm, + .late_startup_tpm = tpm_builtin_late_startup_tpm, + .realloc_buffer = tpm_builtin_realloc_buffer, + .reset = tpm_builtin_reset, + .had_startup_error = tpm_builtin_get_startup_error, + .save_volatile_data = tpm_builtin_save_volatile_data, + .load_volatile_data = tpm_builtin_instantiate_with_volatile_data, + .get_tpm_established_flag = tpm_builtin_get_tpm_established_flag, +}; Index: qemu-git/Makefile.target =================================================================== --- qemu-git.orig/Makefile.target +++ qemu-git/Makefile.target @@ -227,6 +227,11 @@ obj-i386-y += debugcon.o multiboot.o obj-i386-y += pc_piix.o kvmclock.o obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o obj-i386-$(CONFIG_TPM) += tpm_tis.o +obj-i386-$(CONFIG_TPM_BUILTIN) += tpm_builtin.o + +ifdef CONFIG_TPM_BUILTIN +LIBS+=-ltpms +endif # shared objects obj-ppc-y = ppc.o Index: qemu-git/hw/tpm_tis.c =================================================================== --- qemu-git.orig/hw/tpm_tis.c +++ qemu-git/hw/tpm_tis.c @@ -95,6 +95,9 @@ static uint32_t tis_mem_readl(void *opaq static const BackendTPMDriver *bes[] = { +#ifdef CONFIG_TPM_BUILTIN + &tpm_builtin, +#endif NULL, }; Index: qemu-git/configure =================================================================== --- qemu-git.orig/configure +++ qemu-git/configure @@ -3449,6 +3449,7 @@ if test "$tpm" = "yes"; then fi if test "$has_tpm" = "1"; then + echo "CONFIG_TPM_BUILTIN=y" >> $config_target_mak echo "CONFIG_TPM=y" >> $config_host_mak fi fi Index: qemu-git/hw/tpm_tis.h =================================================================== --- qemu-git.orig/hw/tpm_tis.h +++ qemu-git/hw/tpm_tis.h @@ -116,6 +116,8 @@ typedef struct BackendTPMDriver { } BackendTPMDriver; +extern BackendTPMDriver tpm_builtin; + void tis_reset_for_snapshot_resume(TPMState *s); const BackendTPMDriver *tis_get_active_backend(void);