From patchwork Wed Aug 31 14:36:04 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Berger X-Patchwork-Id: 112563 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 603D3B6F77 for ; Thu, 1 Sep 2011 00:59:51 +1000 (EST) Received: from localhost ([::1]:41487 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QylvE-0005rx-P8 for incoming@patchwork.ozlabs.org; Wed, 31 Aug 2011 10:37:44 -0400 Received: from eggs.gnu.org ([140.186.70.92]:43123) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qylu5-0002zY-Gd for qemu-devel@nongnu.org; Wed, 31 Aug 2011 10:36:39 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Qylu2-0006Ry-IH for qemu-devel@nongnu.org; Wed, 31 Aug 2011 10:36:33 -0400 Received: from e8.ny.us.ibm.com ([32.97.182.138]:53688) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qylu2-0006Rn-Bc for qemu-devel@nongnu.org; Wed, 31 Aug 2011 10:36:30 -0400 Received: from d01relay04.pok.ibm.com (d01relay04.pok.ibm.com [9.56.227.236]) by e8.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p7VEMVq4010988 for ; Wed, 31 Aug 2011 10:22:31 -0400 Received: from d03av02.boulder.ibm.com (d03av02.boulder.ibm.com [9.17.195.168]) by d01relay04.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p7VEaRQB230184 for ; Wed, 31 Aug 2011 10:36:28 -0400 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 p7V8ZxRc032678 for ; Wed, 31 Aug 2011 02:35:59 -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 p7V8ZwDI032543 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 31 Aug 2011 02:35:58 -0600 Received: from localhost.localdomain (d941e-10 [127.0.0.1]) by localhost.localdomain (8.14.4/8.14.3) with ESMTP id p7VEaPpo032731; Wed, 31 Aug 2011 10:36:25 -0400 Received: (from root@localhost) by localhost.localdomain (8.14.4/8.14.4/Submit) id p7VEaOqc032730; Wed, 31 Aug 2011 10:36:24 -0400 Message-Id: <20110831143624.826299937@linux.vnet.ibm.com> User-Agent: quilt/0.48-1 Date: Wed, 31 Aug 2011 10:36:04 -0400 From: Stefan Berger To: stefanb@linux.vnet.ibm.com, qemu-devel@nongnu.org References: <20110831143551.127339744@linux.vnet.ibm.com> Content-Disposition: inline; filename=qemu_tpm_be_null.diff X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 32.97.182.138 Cc: chrisw@redhat.com, anbang.ruan@cs.ox.ac.uk, rrelyea@redhat.com, alevy@redhat.com, andreas.niederl@iaik.tugraz.at, serge@hallyn.com Subject: [Qemu-devel] [PATCH V8 13/14] Add a TPM backend null driver 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 adds a TPM null driver implementation acting as a backend for the TIS hardware emulation. The NULL driver responds to all commands with a TPM fault response. To use this null driver, use either -tpm null or -tpmdev null,id=tpm0 -device tpm-tis,tpmdev=tpm0 as parameters on the command line. If TPM support is chosen via './configure --enable-tpm ...' TPM support is now always compiled into Qemu and at least the null driver will be available on emulators for x86_64 and i386. v8: - initializing 'in' variable Signed-off-by: Stefan Berger --- Makefile.target | 2 configure | 8 - hw/tpm_null.c | 327 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ qemu-options.hx | 13 +- tpm.c | 1 tpm.h | 1 6 files changed, 341 insertions(+), 11 deletions(-) Index: qemu-git/hw/tpm_null.c =================================================================== --- /dev/null +++ qemu-git/hw/tpm_null.c @@ -0,0 +1,327 @@ +/* + * 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 "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; +} ThreadParams; + + +/* local variables */ + +static QemuThread thread; + +static bool thread_terminate; +static bool thread_running; + +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 void *tpm_null_main_loop(void *d) +{ + ThreadParams *thr_parms = d; + uint32_t in_len; + uint8_t *in, *out; + uint8_t locty; + +#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; + + 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; +#ifdef DEBUG_TPM + fprintf(stderr, "tpm_null: sending fault response to VM\n"); +#endif + 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_null_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); + + memset(&thread, 0, sizeof(thread)); + } +} + + +static void tpm_null_tpm_atexit(void) +{ + tpm_null_terminate_tpm_thread(); +} + + +/** + * Start the TPM (thread). If it had been started before, then terminate + * and start it again. + */ +static int tpm_null_startup_tpm(void) +{ + /* terminate a running TPM */ + tpm_null_terminate_tpm_thread(); + + /* reset the flag so the thread keeps on running */ + thread_terminate = false; + + qemu_thread_create(&thread, tpm_null_main_loop, &tpm_thread_params); + + thread_running = true; + + return 0; +} + + +static int tpm_null_do_startup_tpm(void) +{ + return tpm_null_startup_tpm(); +} + + +static int tpm_null_early_startup_tpm(void) +{ + return tpm_null_do_startup_tpm(); +} + + +static int tpm_null_late_startup_tpm(void) +{ + return tpm_null_do_startup_tpm(); +} + + +static void tpm_null_reset(void) +{ +#if defined DEBUG_TPM || defined DEBUG_TPM_SR + fprintf(stderr, "tpm: CALL TO TPM_RESET!\n"); +#endif + + tpm_null_terminate_tpm_thread(); +} + + +/* + * Since the null driver does not have much persistent storage + * there is not much to do here... + */ +static int tpm_null_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_null_init(TPMState *s, TPMRecvDataCB *recv_data_cb) +{ + tpm_thread_params.tpm_state = s; + tpm_thread_params.recv_data_callback = recv_data_cb; + + atexit(tpm_null_tpm_atexit); + + return 0; +} + + +static bool tpm_null_get_tpm_established_flag(void) +{ + return false; +} + + +static bool tpm_null_get_startup_error(void) +{ + return false; +} + + +/** + * 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_null_save_volatile_data(void) +{ + return 0; +} + + +static size_t tpm_null_realloc_buffer(TPMSizedBuffer *sb) +{ + size_t wanted_size = 4096; + + if (sb->size != wanted_size) { + sb->buffer = g_realloc(sb->buffer, wanted_size); + if (sb->buffer != NULL) { + sb->size = wanted_size; + } else { + sb->size = 0; + } + } + return sb->size; +} + + +static const char *tpm_null_create_desc(void) +{ + static int done; + + if (!done) { + snprintf(dev_description, sizeof(dev_description), + "Null TPM backend driver"); + done = 1; + } + + return dev_description; +} + + +static TPMBackend *tpm_null_create(QemuOpts *opts, const char *id, + const char *model) +{ + TPMBackend *driver; + + driver = g_malloc(sizeof(TPMBackend)); + if (!driver) { + fprintf(stderr, "Could not allocate memory.\n"); + return NULL; + } + driver->id = g_strdup(id); + if (model) { + driver->model = g_strdup(model); + } + driver->ops = &tpm_null_driver; + + return driver; +} + + +static void tpm_null_destroy(TPMBackend *driver) +{ + g_free(driver->id); + g_free(driver->model); + g_free(driver); +} + + +TPMDriverOps tpm_null_driver = { + .id = "null", + .desc = tpm_null_create_desc, + .job_for_main_thread = NULL, + .create = tpm_null_create, + .destroy = tpm_null_destroy, + .init = tpm_null_init, + .early_startup_tpm = tpm_null_early_startup_tpm, + .late_startup_tpm = tpm_null_late_startup_tpm, + .realloc_buffer = tpm_null_realloc_buffer, + .reset = tpm_null_reset, + .had_startup_error = tpm_null_get_startup_error, + .save_volatile_data = tpm_null_save_volatile_data, + .load_volatile_data = tpm_null_instantiate_with_volatile_data, + .get_tpm_established_flag = tpm_null_get_tpm_established_flag, +}; Index: qemu-git/Makefile.target =================================================================== --- qemu-git.orig/Makefile.target +++ qemu-git/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 +obj-i386-$(CONFIG_TPM) += tpm_tis.o sha1.o tpm_null.o obj-i386-$(CONFIG_TPM_BUILTIN) += tpm_builtin.o ifdef CONFIG_TPM_BUILTIN Index: qemu-git/tpm.c =================================================================== --- qemu-git.orig/tpm.c +++ qemu-git/tpm.c @@ -24,6 +24,7 @@ #if defined(TARGET_I386) || defined(TARGET_X86_64) static const TPMDriverOps *bes[] = { + &tpm_null_driver, #ifdef CONFIG_TPM_BUILTIN &tpm_builtin, #endif Index: qemu-git/tpm.h =================================================================== --- qemu-git.orig/tpm.h +++ qemu-git/tpm.h @@ -141,6 +141,7 @@ void tpm_measure_buffer(const void *buff TPMMeasureType type, uint8_t pcrindex, const void *data, uint32_t data_len); +extern TPMDriverOps tpm_null_driver; extern TPMDriverOps tpm_builtin; #endif /* _HW_TPM_CONFIG_H */ Index: qemu-git/qemu-options.hx =================================================================== --- qemu-git.orig/qemu-options.hx +++ qemu-git/qemu-options.hx @@ -1769,6 +1769,8 @@ DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \ "-tpm builtin,path=[,model=][,key=]\n" \ " enable a builtin TPM with state in file in path\n" \ " and encrypt the TPM's state with the given AES key\n" \ + "-tpm null enable a TPM null driver that responds with a fault\n" \ + " message to every TPM request\n" \ "-tpm model=? to list available TPM device models\n" \ "-tpm ? to list available TPM backend types\n", QEMU_ARCH_I386) @@ -1784,8 +1786,9 @@ The general form of a TPM device option @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}] @findex -tpmdev -Backend type must be: -@option{builtin}. +Backend type must be one of: +@option{builtin}, +@option{null}. The specific backend type will determine the applicable options. The @code{-tpmdev} options requires a @code{-device} option. @@ -1826,6 +1829,12 @@ using AES-CBC encryption scheme supply t @example -tpmdev builtin,id=tpm0,path=,key=aes-cbc:0x1234567890abcdef01234567890abcdef -device tpm-tis,tpmdev=tpm0 @end example + +@item -tpmdev null + +Creates an instance of a TPM null driver that responds to every command +with a fault message. + @end table The short form of a TPM device option is: Index: qemu-git/configure =================================================================== --- qemu-git.orig/configure +++ qemu-git/configure @@ -2593,8 +2593,6 @@ EOF libtpms=no if compile_prog "" "-ltpms" ; then libtpms=yes - else - tpm_need_pkgs="libtpms development package" fi fi @@ -3598,12 +3596,6 @@ if test "$tpm" = "yes"; then if test "$has_tpm" = "1"; then if test "$libtpms" = "yes" ; then echo "CONFIG_TPM_BUILTIN=y" >> $config_target_mak - else - echo - echo "TPM support cannot be added since no TPM backend can be compiled." - echo "Please install the $tpm_need_pkgs." - echo - exit 1 fi echo "CONFIG_TPM=y" >> $config_host_mak fi