From patchwork Thu Feb 24 20:13:37 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Berger X-Patchwork-Id: 84481 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 724CDB7106 for ; Fri, 25 Feb 2011 07:31:54 +1100 (EST) Received: from localhost ([127.0.0.1]:60613 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Pshqo-0001gc-9M for incoming@patchwork.ozlabs.org; Thu, 24 Feb 2011 15:31:50 -0500 Received: from [140.186.70.92] (port=48923 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PshZG-0001dI-C7 for qemu-devel@nongnu.org; Thu, 24 Feb 2011 15:14:10 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PshZD-0006PZ-QT for qemu-devel@nongnu.org; Thu, 24 Feb 2011 15:13:41 -0500 Received: from e31.co.us.ibm.com ([32.97.110.149]:40874) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PshZD-0006PQ-IH for qemu-devel@nongnu.org; Thu, 24 Feb 2011 15:13:39 -0500 Received: from d03relay02.boulder.ibm.com (d03relay02.boulder.ibm.com [9.17.195.227]) by e31.co.us.ibm.com (8.14.4/8.13.1) with ESMTP id p1OJwjJb024153 for ; Thu, 24 Feb 2011 12:58:45 -0700 Received: from d03av06.boulder.ibm.com (d03av06.boulder.ibm.com [9.17.195.245]) by d03relay02.boulder.ibm.com (8.13.8/8.13.8/NCO v9.1) with ESMTP id p1OKDcCc104666 for ; Thu, 24 Feb 2011 13:13:38 -0700 Received: from d03av06.boulder.ibm.com (loopback [127.0.0.1]) by d03av06.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p1OKIH6N007464 for ; Thu, 24 Feb 2011 13:18:17 -0700 Received: from [9.59.241.154] (d941e-10.watson.ibm.com [9.59.241.154]) by d03av06.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p1OKIHUR007451; Thu, 24 Feb 2011 13:18:17 -0700 From: Stefan Berger To: "qemu-devel@nongnu.org" In-Reply-To: <1298576432.17424.1.camel@d941e-10> References: <1298576432.17424.1.camel@d941e-10> Date: Thu, 24 Feb 2011 15:13:37 -0500 Message-ID: <1298578417.17969.9.camel@d941e-10> Mime-Version: 1.0 X-Mailer: Evolution 2.32.1 (2.32.1-1.fc14) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 32.97.110.149 Cc: Andreas Niederl Subject: [Qemu-devel] [PATCH Appendix] Skeleton backend driver X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org It should be possible to recycle a lot of the backend code. So, below is a skeleton backend driver with all libtpms and block storage dependencies removed. It compiled, but doesn't work. In the areas where I know that code needs to be written into, I marked it with '// !!!'. Signed-off-by: Stefan Berger Index: qemu-git/hw/tpm_be_skeleton.c =================================================================== --- /dev/null +++ qemu-git/hw/tpm_be_skeleton.c @@ -0,0 +1,369 @@ +/* + * + * 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 bool thread_terminate = false; +static bool tpm_initialized = false; +static bool had_fatal_error = false; +static bool had_startup_error = 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]; + + +/** + * Start the TPM. If it had been started before, then terminate and start + * it again. + */ +static int startup_tpm(void) +{ + tpm_initialized = true; + +#if defined DEBUG_TPM || defined DEBUG_TPM_SR + fprintf(stderr,"tpm: *** tpm startup was successful! ***\n"); +#endif + + return 0; +} + + +/* + * 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 late_startup_tpm(void) +{ + int rc; + + rc = startup_tpm(); + if (rc) { + had_fatal_error = 1; + return 1; + } + + return 0; +} + + +static void terminate_tpm_thread(void) +{ + 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); + + qemu_thread_join(&thread, NULL); + memset(&thread, 0, sizeof(thread)); + + if (tpm_initialized) { + tpm_initialized = false; + } + } +} + + +static void tpm_atexit(void) +{ + terminate_tpm_thread(); +} + + +static void *mainLoop(void *d) +{ + int res = 0; + ThreadParams *tParams = (ThreadParams *)d; + uint32_t in_len, out_len; + uint8_t *in, *out; + uint32_t resp_size; /* total length of response */ + + /* start command processing */ + while (!thread_terminate) { + /* receive and handle commands */ + in_len = 0; + do { +#ifdef DEBUG_TPM + fprintf(stderr,"waiting for commands...\n"); +#endif + + if (thread_terminate) + break; + + qemu_mutex_lock(&tParams->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 (!tParams->tpm_state->to_tpm_execute) + qemu_cond_wait(&tParams->tpm_state->to_tpm_cond, + &tParams->tpm_state->state_lock); + + tParams->tpm_state->to_tpm_execute = false; + + qemu_mutex_unlock(&tParams->tpm_state->state_lock); + + if (thread_terminate) + break; + + g_locty = tParams->tpm_state->command_locty; + + in = tParams->tpm_state->loc[g_locty].w_buffer.buffer; + in_len = tParams->tpm_state->loc[g_locty].w_offset; + + if (!had_fatal_error) { + + out_len = tParams->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(stdout, in, in_len); +#endif + + resp_size = 0; + + + // !!! Send command to TPM & wait for response + + + if (res != 0) { +#ifdef DEBUG_TPM + fprintf(stderr, + "Sending/receiving TPM request/response " + "failed\n"); +#endif + had_fatal_error = 1; + } + } + + if (had_fatal_error) { + out = tParams->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,"sending %d bytes to VM\n", resp_size); + dumpBuffer(stdout, out, resp_size); +#endif + tParams->recv_data_callback(tParams->tpm_state, g_locty); + } while (in_len > 0); + } + + return NULL; +} + + + +/*****************************************************************/ + + +static void reset(void) +{ + static bool thread_running; + +#if defined DEBUG_TPM || defined DEBUG_TPM_SR + fprintf(stderr,"tpm: CALL TO TPM_RESET!\n"); +#endif + + if (thread_running) { +#if defined DEBUG_TPM || defined DEBUG_TPM_SR + fprintf(stderr,"tpm: TERMINATING RUNNING TPM THREAD\n"); +#endif + terminate_tpm_thread(); + } + + had_fatal_error = false; + thread_terminate = false; + had_startup_error = false; + + qemu_thread_create(&thread, mainLoop, &tpm_thread_params); + thread_running = true; +} + + +/* + * 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 tpm_initialized variable is 'true'. + * + */ +static int instantiate_with_volatile_data(TPMState *s) +{ + if (tpm_initialized) { +#ifdef DEBUG_TPM_SR + fprintf(stderr,"tpm: This is resume of a SNAPSHOT?!\n"); +#endif + // !!! Xen does not support this ... + tis_reset_for_snapshot_resume(s); + } + + return 0; +} + + +static int 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); + + // !!! Do necessary initialization here + + atexit(tpm_atexit); + + return 0; +} + + +static bool get_tpm_established_flag(void) +{ + return false; +} + + +static bool 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 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 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 *create_desc(void) +{ + static int done; + + if (!done) { + snprintf(dev_description, sizeof(dev_description), + "Skeleton TPM backend"); + done = 1; + } + + return dev_description; +} + + +static bool handle_options(QemuOpts *opts) +{ + const char *value; + + value = qemu_opt_get(opts, "path"); + if (value) { + // !!! handle path parameter + } else { + fprintf(stderr,"-tpm is missing path= parameter\n"); + return false; + } + return true; +} + + +struct backend_tpm_driver skeleton = { + .id = "skeleton", + .desc = create_desc, + .handle_options = handle_options, + .init = init, + .late_startup_tpm = late_startup_tpm, + .realloc_buffer = realloc_buffer, + .reset = reset, + .had_startup_error = get_startup_error, + .save_volatile_data = save_volatile_data, + .load_volatile_data = instantiate_with_volatile_data, + .get_tpm_established_flag = get_tpm_established_flag, +};