This patch provides a TPM backend skelteon implementation. It doesn't do anything but it compiles.
Signed-off-by: Stefan Berger <stef...@linux.vnet.ibm.com> --- Makefile.target | 5 hw/tpm_builtin.c | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/tpm_tis.c | 3 3 files changed, 380 insertions(+) Index: qemu-git/hw/tpm_builtin.c =================================================================== --- /dev/null +++ qemu-git/hw/tpm_builtin.c @@ -0,0 +1,372 @@ +/* + * + * 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 <http://www.gnu.org/licenses/> + */ + +#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; +} + + +BackendTPMDriver 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, +}; Index: qemu-git/Makefile.target =================================================================== --- qemu-git.orig/Makefile.target +++ qemu-git/Makefile.target @@ -307,6 +307,11 @@ obj-sparc-y += grlib_gptimer.o grlib_irq ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),i386 x86_64)) obj-i386-$(CONFIG_TPM) += tpm_tis.o +obj-i386-$(CONFIG_TPM_BUILTIN) += tpm_builtin.o + +ifdef CONFIG_TPM_BUILTIN +LIBS+=-ltpms +endif endif 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 + &builtin, +#endif NULL, };