* [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration
@ 2011-03-30 19:42 Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 1/9] Support for TPM command line options Stefan Berger
` (8 more replies)
0 siblings, 9 replies; 18+ messages in thread
From: Stefan Berger @ 2011-03-30 19:42 UTC (permalink / raw)
To: stefanb, qemu-devel; +Cc: andreas.niederl
The following series of patches adds a TPM (Trusted Platform Module)
TIS (TPM Interface Spec) interface to Qemu and with that provides
means to access a backend implementing the actual TPM functionality.
This frontend enables for example Linux's TPM TIS (tpm_tis) driver.
I am also posting the implementation of a backend implementation that is based
on a library (libtpms) providing TPM functionality. This library is currently
undergoing further testing and is not commonly available, yet.
The main purpose of me posting the libtpms-based backend patches now is to
show an example of how to integrate a backend with this TIS frontend. The
frontend is independent of the code in the backend and could be checked-in
separately, though will be of limited use as long as no backend is provided.
The backend driver for Xen, however, should be adapted to work with this
frontend's extensive interface.
My testing is all based on the libtpms-based backend that provides support for
VM suspend/resume, migration and snapshotting. It uses QCoW2 as the file
format for storing its persistent state onto, which is necessary for support
of snapshotting. Using Linux as the OS along with some recently posted patches
for the Linux TPM TIS driver, suspend/resume works fine (using 'virsh
save/restore') along with hibernation and OS suspend (ACPI S3).
Proper support for the TPM requires support in the BIOS since the BIOS
needs to initialize the TPM upon machine start or issue commands to the TPM
when it resumes from suspend (ACPI S3). It also builds and connects the
necessary ACPI tables (SSDT for TPM device, TCPA table for logging) to the
ones that are built by a BIOS. To support this I have fairly extensive
set of extensions for SeaBIOS that I posted to the SeaBIOS mailing list.
V2:
- splitting some of the patches into smaller ones for easier review
- fixes in individual patches
Regards,
Stefan
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH V2 1/9] Support for TPM command line options
2011-03-30 19:42 [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration Stefan Berger
@ 2011-03-30 19:42 ` Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 2/9] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
` (7 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2011-03-30 19:42 UTC (permalink / raw)
To: stefanb, qemu-devel; +Cc: andreas.niederl
[-- Attachment #1: qemu_tpm.diff --]
[-- Type: text/plain, Size: 6554 bytes --]
This patch adds support for TPM command line options.
The command line supported here (considering the libtpms based
backend) are
./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
and
./qemu-... -tpm ?
where the latter works similar to -soundhw ? and shows a list of
available TPM backends (i.e., libtpms-based, Xen).
Only the 'type' is interpreted in arch_init.c. Using this parameter,
the backend is chosen, i.e., 'builtin' for the libtpms-based
builtin TPM. The interpretation of the other parameters along with
determining whether enough parameters were provided is pushed into
the backend driver, which needs to implement the interface function
'handle_options' and return true if the VM can be started or 'false'
if not enough or bad parameters were provided.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
arch_init.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arch_init.h | 2 +
hw/pc.h | 7 +++++
qemu-config.c | 20 ++++++++++++++
qemu-options.hx | 11 ++++++++
vl.c | 11 ++++++++
6 files changed, 128 insertions(+)
Index: qemu-git/hw/pc.h
===================================================================
--- qemu-git.orig/hw/pc.h
+++ qemu-git/hw/pc.h
@@ -6,6 +6,7 @@
#include "isa.h"
#include "fdc.h"
#include "net.h"
+#include "tpm_tis.h"
/* PC-style peripherals (also used by other machines). */
@@ -128,6 +129,12 @@ void pc_register_ferr_irq(qemu_irq irq);
void pc_cmos_set_s3_resume(void *opaque, int irq, int level);
void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
+/* tpm_tis.c */
+extern bool has_tpm;
+const BackendTPMDriver *tis_set_backend_driver(const char *tpm_type);
+void tis_display_backend_drivers(FILE *);
+
+
void pc_cpus_init(const char *cpu_model);
void pc_memory_init(ram_addr_t ram_size,
const char *kernel_filename,
Index: qemu-git/qemu-options.hx
===================================================================
--- qemu-git.orig/qemu-options.hx
+++ qemu-git/qemu-options.hx
@@ -1041,6 +1041,17 @@ Specify SMBIOS type 0 fields
Specify SMBIOS type 1 fields
ETEXI
+#ifndef _WIN32
+# ifdef CONFIG_TPM
+DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \
+ ""
+ "-tpm type=<type>,path=<path>\n" \
+ " enable a TPM with state from file in given path\n"
+ " use -tpm ? to get a list of supported TPM types\n",
+ QEMU_ARCH_I386)
+# endif
+#endif
+
DEFHEADING()
STEXI
@end table
Index: qemu-git/vl.c
===================================================================
--- qemu-git.orig/vl.c
+++ qemu-git/vl.c
@@ -244,6 +244,8 @@ int nb_numa_nodes;
uint64_t node_mem[MAX_NODES];
uint64_t node_cpumask[MAX_NODES];
+bool has_tpm = false;
+
static QEMUTimer *nographic_timer;
uint8_t qemu_uuid[16];
@@ -2420,6 +2422,15 @@ int main(int argc, char **argv, char **e
ram_size = value;
break;
}
+#ifdef CONFIG_TPM
+ case QEMU_OPTION_tpm:
+ if (!(tpm_available())) {
+ printf("Option %s not supported for this target\n", popt->name);
+ exit(1);
+ }
+ select_tpm(optarg);
+ break;
+#endif
case QEMU_OPTION_mempath:
mem_path = optarg;
break;
Index: qemu-git/qemu-config.c
===================================================================
--- qemu-git.orig/qemu-config.c
+++ qemu-git/qemu-config.c
@@ -451,6 +451,25 @@ QemuOptsList qemu_option_rom_opts = {
},
};
+static QemuOptsList qemu_tpm_opts = {
+ .name = "tpm",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
+ .desc = {
+ {
+ .name = "type",
+ .type = QEMU_OPT_STRING,
+ .help = "Type of TPM backend",
+ },
+ {
+ .name = "path",
+ .type = QEMU_OPT_STRING,
+ .help = "Persitent storage for TPM state",
+ },
+ { /* end of list */ }
+ },
+};
+
+
static QemuOptsList *vm_config_groups[32] = {
&qemu_drive_opts,
&qemu_chardev_opts,
@@ -465,6 +484,7 @@ static QemuOptsList *vm_config_groups[32
&qemu_trace_opts,
#endif
&qemu_option_rom_opts,
+ &qemu_tpm_opts,
NULL,
};
Index: qemu-git/arch_init.c
===================================================================
--- qemu-git.orig/arch_init.c
+++ qemu-git/arch_init.c
@@ -41,6 +41,8 @@
#include "net.h"
#include "gdbstub.h"
#include "hw/smbios.h"
+#include "blockdev.h"
+#include "hw/tpm_tis.h"
#ifdef TARGET_SPARC
int graphic_width = 1024;
@@ -726,3 +728,78 @@ int xen_available(void)
return 0;
#endif
}
+
+int tpm_available(void) {
+#ifdef CONFIG_TPM
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+#ifdef CONFIG_TPM
+
+#if defined (TARGET_I386) || defined (TARGET_X86_64)
+
+
+static int configure_tpm(QemuOpts *opts)
+{
+ const char *value;
+ const BackendTPMDriver *be;
+
+ if (has_tpm) {
+ fprintf(stderr,"Only one TPM is allowed\n");
+ return 1;
+ }
+
+ value = qemu_opt_get(opts, "type");
+ if (!value) {
+ fprintf(stderr,
+ "Missing TPM backend type.");
+ tis_display_backend_drivers(stderr);
+ return 1;
+ }
+
+ be = tis_set_backend_driver(value);
+ if (be == NULL) {
+ fprintf(stderr,
+ "A TPM backend driver of type %s is not supported.\n",
+ value);
+ tis_display_backend_drivers(stderr);
+ return 1;
+ }
+
+ has_tpm = be->handle_options(opts);
+ if (!has_tpm)
+ return 1;
+
+ return 0;
+}
+
+
+void select_tpm(const char *optarg)
+{
+ QemuOpts *opts;
+
+ if (strcmp("none", optarg) != 0) {
+ if (*optarg == '?') {
+ tis_display_backend_drivers(stdout);
+ exit(0);
+ }
+ opts = qemu_opts_parse(qemu_find_opts("tpm"), optarg, 0);
+ if (!opts)
+ exit(1);
+ if (configure_tpm(opts))
+ exit(1);
+ }
+}
+
+# else /* CONFIG_TPM */
+
+void select_tpm(const char *optarg)
+{
+ (void)optarg;
+}
+
+# endif
+#endif /* CONFIG_TPM */
Index: qemu-git/arch_init.h
===================================================================
--- qemu-git.orig/arch_init.h
+++ qemu-git/arch_init.h
@@ -31,5 +31,7 @@ int audio_available(void);
void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus);
int kvm_available(void);
int xen_available(void);
+int tpm_available(void);
+void select_tpm(const char *optarg);
#endif
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH V2 2/9] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2011-03-30 19:42 [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 1/9] Support for TPM command line options Stefan Berger
@ 2011-03-30 19:42 ` Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 3/9] Add persistent state handling to TPM TIS frontend driver Stefan Berger
` (6 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2011-03-30 19:42 UTC (permalink / raw)
To: stefanb, qemu-devel; +Cc: andreas.niederl
[-- Attachment #1: qemu_tpm_tis.diff --]
[-- Type: text/plain, Size: 29994 bytes --]
This patch adds the main code of the TPM frontend driver, the TPM TIS
interface, to Qemu. The code is largely based on my previous implementation
for Xen but has been significantly extended to meet the standard's
requirements, such as the support for changing of localities and all the
functionality of the available flags.
Communication with the backend (i.e., for Xen or the libtpms-based one)
is cleanly separated through an interface which the backend driver needs
to implement.
The TPM TIS driver's backend was previously chosen in the code added
to arch_init. The frontend holds a pointer to the chosen backend (interface).
Communication with the backend is largely based on signals and conditions.
Whenever the frontend has collected a complete packet, it will signal
the backend, which then starts processing the command. Once the result
has been returned, the backend invokes a callback function
(tis_tpm_receive_cb()).
The one tricky part is support for VM suspend while the TPM is processing
a command. In this case the frontend driver is waiting for the backend
to return the result of the last command before shutting down. It waits
on a condition for a signal from the backend, which is delivered in
tis_tpm_receive_cb().
Testing the proper functioning of the different flags and localities
cannot be done from user space when running in Linux for example, since
access to the address space of the TPM TIS interface is not possible. Also
the Linux driver itself does not exercise all functionality. So, for
testing there is a fairly extensive test suite as part of the SeaBIOS patches
since from within the BIOS one can have full access to all the TPM's registers.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
hw/pc.c | 3
hw/tpm_tis.c | 826 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/tpm_tis.h | 140 ++++++++++
3 files changed, 969 insertions(+)
Index: qemu-git/hw/tpm_tis.c
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_tis.c
@@ -0,0 +1,826 @@
+/*
+ * tpm_tis.c - QEMU emulator for a 1.2 TPM with TIS interface
+ *
+ * Copyright (C) 2006,2010 IBM Corporation
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ * David Safford <safford@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ *
+ * Implementation of the TIS interface according to specs at
+ * https://www.trustedcomputinggroup.org/groups/pc_client/TCG_PCClientTPMSpecification_1-20_1-00_FINAL.pdf
+ *
+ */
+
+#include "block.h"
+#include "hw/hw.h"
+#include "hw/pc.h"
+#include "hw/tpm_tis.h"
+
+#include <stdio.h>
+
+//#define DEBUG_TIS
+
+/* whether the STS interrupt is supported */
+//#define RAISE_STS_IRQ
+
+/* tis registers */
+#define TIS_REG_ACCESS 0x00
+#define TIS_REG_INT_ENABLE 0x08
+#define TIS_REG_INT_VECTOR 0x0c
+#define TIS_REG_INT_STATUS 0x10
+#define TIS_REG_INTF_CAPABILITY 0x14
+#define TIS_REG_STS 0x18
+#define TIS_REG_DATA_FIFO 0x24
+#define TIS_REG_DID_VID 0xf00
+#define TIS_REG_RID 0xf04
+
+
+#define STS_VALID (1 << 7)
+#define STS_COMMAND_READY (1 << 6)
+#define STS_TPM_GO (1 << 5)
+#define STS_DATA_AVAILABLE (1 << 4)
+#define STS_EXPECT (1 << 3)
+#define STS_RESPONSE_RETRY (1 << 1)
+
+#define ACCESS_TPM_REG_VALID_STS (1 << 7)
+#define ACCESS_ACTIVE_LOCALITY (1 << 5)
+#define ACCESS_BEEN_SEIZED (1 << 4)
+#define ACCESS_SEIZE (1 << 3)
+#define ACCESS_PENDING_REQUEST (1 << 2)
+#define ACCESS_REQUEST_USE (1 << 1)
+#define ACCESS_TPM_ESTABLISHMENT (1 << 0)
+
+#define INT_ENABLED (1 << 31)
+#define INT_DATA_AVAILABLE (1 << 0)
+#define INT_STS_VALID (1 << 1)
+#define INT_LOCALITY_CHANGED (1 << 2)
+#define INT_COMMAND_READY (1 << 7)
+
+#ifndef RAISE_STS_IRQ
+
+# define INTERRUPTS_SUPPORTED (INT_LOCALITY_CHANGED | \
+ INT_DATA_AVAILABLE | \
+ INT_COMMAND_READY)
+
+#else
+
+# define INTERRUPTS_SUPPORTED (INT_LOCALITY_CHANGED | \
+ INT_DATA_AVAILABLE | \
+ INT_STS_VALID | \
+ INT_COMMAND_READY)
+
+#endif
+
+#define CAPABILITIES_SUPPORTED ((1 << 4) | \
+ INTERRUPTS_SUPPORTED)
+
+#define TPM_DID 0x0001
+#define TPM_VID 0x0001
+#define TPM_RID 0x0001
+
+#define TPM_NO_DATA_BYTE 0xff
+
+/* prototypes */
+static uint32_t tis_mem_readl(void *opaque, target_phys_addr_t addr);
+
+
+static const BackendTPMDriver *bes[] = {
+ NULL,
+};
+
+/* active backend driver */
+static const BackendTPMDriver *active_be;
+
+const BackendTPMDriver *tis_get_active_backend(void)
+{
+ return active_be;
+}
+
+const BackendTPMDriver *tis_set_backend_driver(const char *id)
+{
+ int i;
+
+ for (i = 0; bes[i] != NULL; i++) {
+ if (!strcmp(bes[i]->id, id)) {
+ break;
+ }
+ }
+
+ active_be = bes[i];
+
+ return active_be;
+}
+
+void tis_display_backend_drivers(FILE *out)
+{
+ int i;
+
+ fprintf(out, "Supported TPM types (choose only one):\n");
+
+ for (i = 0; bes[i] != NULL; i++) {
+ fprintf(out, "%7s %s",
+ bes[i]->id, bes[i]->desc());
+ fprintf(out, "\n");
+ }
+ fprintf(out, "\n");
+}
+
+
+#ifdef DEBUG_TIS
+static void showBuff(const TPMSizedBuffer *sb, const char *string)
+{
+ uint16_t len;
+
+ len = tpm_get_size_from_buffer(sb);
+ fprintf(stderr,"tpm_tis: %s length = %d\n", string, len);
+ dumpBuffer(stderr, sb->buffer, len);
+}
+#endif
+
+
+static inline uint8_t locality_from_addr(target_phys_addr_t addr)
+{
+ return (uint8_t)((addr >> 12) & 0x7);
+}
+
+
+/*
+ * Send a TPM request.
+ * Call this with the state_lock held so we can sync with the receive
+ * callback.
+ */
+static void tis_tpm_send(TPMState *s, uint8_t locty)
+{
+#ifdef DEBUG_TIS
+ showBuff(&s->loc[locty].w_buffer, "tpm_tis: To TPM");
+#endif
+ s->command_locty = locty;
+
+ /* w_offset serves as length indicator for length of data;
+ it's reset when the response comes back */
+ s->loc[locty].state = STATE_EXECUTION;
+ s->loc[locty].sts &= ~STS_EXPECT;
+
+ s->to_tpm_execute = true;
+ qemu_cond_signal(&s->to_tpm_cond);
+}
+
+
+/* raise an interrupt if allowed */
+static void tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
+{
+ if (!IS_VALID_LOCTY(locty)) {
+ return;
+ }
+
+ if ((s->loc[locty].inte & INT_ENABLED) &&
+ (s->loc[locty].inte & irqmask)) {
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: Raising IRQ for flag %08x\n",irqmask);
+#endif
+ qemu_irq_raise(s->irq);
+ s->loc[locty].ints |= irqmask;
+ }
+}
+
+
+static uint32_t tis_check_request_use_except(TPMState *s, uint8_t locty)
+{
+ uint8_t l;
+
+ for (l = 0; l < NUM_LOCALITIES; l++) {
+ if (l == locty) {
+ continue;
+ }
+ if ((s->loc[l].access & ACCESS_REQUEST_USE)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
+{
+ int change = (s->active_locty != new_active_locty);
+
+ if (change && IS_VALID_LOCTY(s->active_locty)) {
+ /* reset flags on the old active locality */
+ s->loc[s->active_locty].access &= ~(ACCESS_ACTIVE_LOCALITY|
+ ACCESS_REQUEST_USE);
+ if (IS_VALID_LOCTY(new_active_locty) &&
+ s->loc[new_active_locty].access & ACCESS_SEIZE) {
+ s->loc[s->active_locty].access |= ACCESS_BEEN_SEIZED;
+ }
+ }
+
+ s->active_locty = new_active_locty;
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: Active locality is now %d\n", s->active_locty);
+#endif
+
+ if (IS_VALID_LOCTY(new_active_locty)) {
+ /* set flags on the new active locality */
+ s->loc[new_active_locty].access |= ACCESS_ACTIVE_LOCALITY;
+ s->loc[new_active_locty].access &= ~(ACCESS_REQUEST_USE |
+ ACCESS_SEIZE);
+ }
+
+ if (change) {
+ tis_raise_irq(s, s->active_locty, INT_LOCALITY_CHANGED);
+ }
+}
+
+
+/* abort -- this function switches the locality */
+static void tis_abort(TPMState *s, uint8_t locty)
+{
+ s->loc[locty].r_offset = 0;
+ s->loc[locty].w_offset = 0;
+
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: tis_abort: new active locality is %d\n",
+ s->next_locty);
+#endif
+
+ /*
+ * Need to react differently depending on who's aborting now and
+ * which locality will become active afterwards.
+ */
+ if (s->aborting_locty == s->next_locty) {
+ s->loc[s->aborting_locty].state = STATE_READY;
+ s->loc[s->aborting_locty].sts = STS_COMMAND_READY;
+ tis_raise_irq(s, s->aborting_locty, INT_COMMAND_READY );
+ }
+
+ /* locality after abort is another one than the current one */
+ tis_new_active_locality(s, s->next_locty);
+
+ s->next_locty = NO_LOCALITY;
+ s->aborting_locty = NO_LOCALITY; /* nobody's aborting a command anymore */
+}
+
+
+/* prepare aborting current command */
+static void tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
+{
+ uint8_t busy_locty;
+
+ s->aborting_locty = locty;
+ s->next_locty = newlocty; /* locality after successful abort */
+
+ /*
+ * only abort a command using an interrupt if currently executing
+ * a command AND if there's a valid connection to the vTPM.
+ */
+ for (busy_locty = 0; busy_locty < NUM_LOCALITIES; busy_locty++) {
+ if (s->loc[busy_locty].state == STATE_EXECUTION) {
+ /* there is currently no way to interrupt the TPM's operations
+ while it's executing a command; once the TPM is done and
+ returns the buffer, it will switch to the next_locty; */
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: Locality %d is busy - "
+ "deferring abort\n", busy_locty);
+#endif
+ return;
+ }
+ }
+
+ tis_abort(s, locty);
+}
+
+
+/*
+ * Callback from the TPM to indicate that the response was received.
+ */
+static void tis_tpm_receive_cb(TPMState *s, uint8_t locty)
+{
+ qemu_mutex_lock(&s->state_lock);
+
+ s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+ s->loc[locty].state = STATE_COMPLETION;
+ s->loc[locty].r_offset = 0;
+ s->loc[locty].w_offset = 0;
+
+ if (IS_VALID_LOCTY(s->next_locty)) {
+ tis_abort(s, locty);
+ }
+
+ qemu_cond_signal(&s->from_tpm_cond);
+
+ qemu_mutex_unlock(&s->state_lock);
+
+#ifndef RAISE_STS_IRQ
+ tis_raise_irq(s, locty, INT_DATA_AVAILABLE);
+#else
+ tis_raise_irq(s, locty, INT_DATA_AVAILABLE | INT_STS_VALID);
+#endif
+}
+
+
+/*
+ * read a byte of response data
+ */
+static uint32_t tpm_data_read(TPMState *s, uint8_t locty)
+{
+ uint32_t ret = TPM_NO_DATA_BYTE;
+ uint16_t len;
+
+ if ((s->loc[locty].sts & STS_DATA_AVAILABLE)) {
+ len = tpm_get_size_from_buffer(&s->loc[locty].r_buffer);
+
+ ret = s->loc[locty].r_buffer.buffer[s->loc[locty].r_offset++];
+ if (s->loc[locty].r_offset >= len) {
+ /* got last byte */
+ s->loc[locty].sts = STS_VALID;
+#ifdef RAISE_STS_IRQ
+ tis_raise_irq(s, locty, INT_STS_VALID);
+#endif
+ }
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: tpm_data_read byte 0x%02x [%d]\n",
+ ret,s->loc[locty].r_offset-1);
+#endif
+ }
+
+ return ret;
+}
+
+
+/*
+ * Read a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static uint32_t tis_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ TPMState *s = opaque;
+ uint16_t offset = addr & 0xffc;
+ uint8_t shift = (addr & 0x3) * 8;
+ uint32_t val = 0xff;
+ uint8_t locty = locality_from_addr(addr);
+
+ qemu_mutex_lock(&s->state_lock);
+
+ if (!s->tpm_initialized) {
+ active_be->late_startup_tpm();
+ s->tpm_initialized = true;
+ }
+
+ if (active_be->had_startup_error()) {
+ qemu_mutex_unlock(&s->state_lock);
+ return 0xFFFFFFFF;
+ }
+
+ switch (offset) {
+ case TIS_REG_ACCESS:
+ /* never show the SEIZE flag even though we use it internally */
+ val = s->loc[locty].access & ~ACCESS_SEIZE;
+ /* the pending flag is alawys calculated */
+ if (tis_check_request_use_except(s, locty)) {
+ val |= ACCESS_PENDING_REQUEST;
+ }
+ val |= !active_be->get_tpm_established_flag();
+ break;
+ case TIS_REG_INT_ENABLE:
+ val = s->loc[locty].inte;
+ break;
+ case TIS_REG_INT_VECTOR:
+ val = s->irq_num;
+ break;
+ case TIS_REG_INT_STATUS:
+ val = s->loc[locty].ints;
+ break;
+ case TIS_REG_INTF_CAPABILITY:
+ val = CAPABILITIES_SUPPORTED;
+ break;
+ case TIS_REG_STS:
+ if (s->active_locty == locty) {
+ if ((s->loc[locty].sts & STS_DATA_AVAILABLE)) {
+ val = (tpm_get_size_from_buffer(&s->loc[locty].r_buffer) -
+ s->loc[locty].r_offset ) << 8 | s->loc[locty].sts;
+ } else {
+ val = (s->loc[locty].w_buffer.size -
+ s->loc[locty].w_offset) << 8 | s->loc[locty].sts;
+ }
+ }
+ break;
+ case TIS_REG_DATA_FIFO:
+ if (s->active_locty == locty) {
+ switch (s->loc[locty].state) {
+ case STATE_COMPLETION:
+ val = tpm_data_read(s, locty);
+ break;
+ default:
+ val = TPM_NO_DATA_BYTE;
+ break;
+ }
+ }
+ break;
+ case TIS_REG_DID_VID:
+ val = (TPM_DID << 16) | TPM_VID;
+ break;
+ case TIS_REG_RID:
+ val = TPM_RID;
+ break;
+ }
+
+ qemu_mutex_unlock(&s->state_lock);
+
+ if (shift) {
+ val >>= shift;
+ }
+
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: read(%08x) = %08x\n", (int)addr, val);
+#endif
+
+ return val;
+}
+
+
+/*
+ * Write a value to a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static void tis_mem_writel_intern(void *opaque, target_phys_addr_t addr,
+ uint32_t val, bool hw_access)
+{
+ TPMState *s = opaque;
+ uint16_t off = addr & 0xfff;
+ uint8_t locty = locality_from_addr(addr);
+ uint8_t active_locty, l;
+ int c, set_new_locty = 1;
+ uint16_t len;
+
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: write(%08x) = %08x\n", (int)addr, val);
+#endif
+
+ qemu_mutex_lock(&s->state_lock);
+
+ if (!s->tpm_initialized) {
+ active_be->late_startup_tpm();
+ s->tpm_initialized = true;
+ }
+
+ if (active_be->had_startup_error()) {
+ qemu_mutex_unlock(&s->state_lock);
+ return;
+ }
+
+ switch (off) {
+ case TIS_REG_ACCESS:
+
+ if ((val & ACCESS_SEIZE)) {
+ val &= ~(ACCESS_REQUEST_USE | ACCESS_ACTIVE_LOCALITY);
+ }
+
+ active_locty = s->active_locty;
+
+ if ((val & ACCESS_ACTIVE_LOCALITY)) {
+ /* give up locality if currently owned */
+ if (s->active_locty == locty) {
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: Releasing locality %d\n", locty);
+#endif
+ uint8_t newlocty = NO_LOCALITY;
+ /* anybody wants the locality ? */
+ for (c = NUM_LOCALITIES - 1; c >= 0; c--) {
+ if ((s->loc[c].access & ACCESS_REQUEST_USE)) {
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: Locality %d requests use.\n",
+ c);
+#endif
+ newlocty = c;
+ break;
+ }
+ }
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: ACCESS_ACTIVE_LOCALITY: "
+ "Next active locality: %d\n",
+ newlocty);
+#endif
+ if (IS_VALID_LOCTY(newlocty)) {
+ set_new_locty = 0;
+ tis_prep_abort(s, locty, newlocty);
+ } else
+ active_locty = NO_LOCALITY;
+ } else {
+ /* not currently the owner; clear a pending request */
+ s->loc[locty].access &= ~ACCESS_REQUEST_USE;
+ }
+ }
+
+ if ((val & ACCESS_BEEN_SEIZED)) {
+ s->loc[locty].access &= ~ACCESS_BEEN_SEIZED;
+ }
+
+ if ((val & ACCESS_SEIZE)) {
+ /* allow seize if a locality is active and the requesting
+ locality is higher than the one that's active
+ OR
+ allow seize for requesting locality if no locality is
+ active */
+ while ( (IS_VALID_LOCTY(s->active_locty) &&
+ locty > s->active_locty) ||
+ (!IS_VALID_LOCTY(s->active_locty))) {
+
+ /* already a pending SEIZE ? */
+ if ((s->loc[locty].access & ACCESS_SEIZE)) {
+ break;
+ }
+
+ /* check for ongoing seize by a higher locality */
+ for (l = locty + 1; l < NUM_LOCALITIES; l++) {
+ if ((s->loc[l].access & ACCESS_SEIZE)) {
+ break;
+ }
+ }
+
+ /* cancel any seize by a lower locality */
+ for (l = 0; l < locty - 1; l++) {
+ s->loc[l].access &= ~ACCESS_SEIZE;
+ }
+
+ s->loc[locty].access |= ACCESS_SEIZE;
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: ACCESS_SEIZE: "
+ "Locality %d seized from locality %d\n",
+ locty, s->active_locty);
+ fprintf(stderr, "tpm_tis: ACCESS_SEIZE: Initiating abort.\n");
+#endif
+ set_new_locty = 0;
+ tis_prep_abort(s, s->active_locty, locty);
+ break;
+ }
+ }
+
+ if ((val & ACCESS_REQUEST_USE)) {
+ if (s->active_locty != locty) {
+ if (IS_VALID_LOCTY(s->active_locty)) {
+ s->loc[locty].access |= ACCESS_REQUEST_USE;
+ } else {
+ /* no locality active -> make this one active now */
+ active_locty = locty;
+ }
+ }
+ }
+
+ if (set_new_locty) {
+ tis_new_active_locality(s, active_locty);
+ }
+
+ break;
+ case TIS_REG_INT_ENABLE:
+ if (s->active_locty != locty) {
+ break;
+ }
+
+ s->loc[locty].inte = (val & (INT_ENABLED | (0x3 << 3) |
+ INTERRUPTS_SUPPORTED));
+ break;
+ case TIS_REG_INT_VECTOR:
+ /* hard wired -- ignore */
+ break;
+ case TIS_REG_INT_STATUS:
+ if (s->active_locty != locty) {
+ break;
+ }
+
+ /* clearing of interrupt flags */
+ if (((val & INTERRUPTS_SUPPORTED)) &&
+ (s->loc[locty].ints & INTERRUPTS_SUPPORTED)) {
+ s->loc[locty].ints &= ~val;
+ if (s->loc[locty].ints == 0) {
+ qemu_irq_lower(s->irq);
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: Lowering IRQ\n");
+#endif
+ }
+ }
+ s->loc[locty].ints &= ~(val & INTERRUPTS_SUPPORTED);
+ break;
+ case TIS_REG_STS:
+ if (s->active_locty != locty) {
+ break;
+ }
+
+ val &= (STS_COMMAND_READY | STS_TPM_GO | STS_RESPONSE_RETRY);
+
+ if (val == STS_COMMAND_READY) {
+ switch (s->loc[locty].state) {
+
+ case STATE_READY:
+ s->loc[locty].w_offset = 0;
+ s->loc[locty].r_offset = 0;
+ break;
+
+ case STATE_IDLE:
+ s->loc[locty].sts = STS_COMMAND_READY;
+ s->loc[locty].state = STATE_READY;
+ tis_raise_irq(s, locty, INT_COMMAND_READY);
+ break;
+
+ case STATE_EXECUTION:
+ case STATE_RECEPTION:
+ /* abort currently running command */
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: %s: Initiating abort.\n",
+ __FUNCTION__);
+#endif
+ tis_prep_abort(s, locty, locty);
+ break;
+
+ case STATE_COMPLETION:
+ s->loc[locty].w_offset = 0;
+ s->loc[locty].r_offset = 0;
+ /* shortcut to ready state with C/R set */
+ s->loc[locty].state = STATE_READY;
+ if (!(s->loc[locty].sts & STS_COMMAND_READY)) {
+ s->loc[locty].sts = STS_COMMAND_READY;
+ tis_raise_irq(s, locty, INT_COMMAND_READY);
+ }
+ break;
+
+ }
+ } else if (val == STS_TPM_GO) {
+ switch (s->loc[locty].state) {
+ case STATE_RECEPTION:
+ tis_tpm_send(s, locty);
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ } else if (val == STS_RESPONSE_RETRY) {
+ switch (s->loc[locty].state) {
+ case STATE_COMPLETION:
+ s->loc[locty].r_offset = 0;
+ s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ }
+ break;
+ case TIS_REG_DATA_FIFO:
+ /* data fifo */
+ if (s->active_locty != locty) {
+ break;
+ }
+
+ if (s->loc[locty].state == STATE_IDLE ||
+ s->loc[locty].state == STATE_EXECUTION ||
+ s->loc[locty].state == STATE_COMPLETION) {
+ /* drop the byte */
+ } else {
+#ifdef DEBUG_TIS
+ fprintf(stderr,"tpm_tis: Byte to send to TPM: %02x\n", val);
+#endif
+ if (s->loc[locty].state == STATE_READY) {
+ s->loc[locty].state = STATE_RECEPTION;
+ s->loc[locty].sts = STS_EXPECT | STS_VALID;
+ }
+
+ if ((s->loc[locty].sts & STS_EXPECT)) {
+ if (s->loc[locty].w_offset < s->loc[locty].w_buffer.size) {
+ s->loc[locty].w_buffer.buffer[s->loc[locty].w_offset++] =
+ (uint8_t)val;
+ } else {
+ s->loc[locty].sts = STS_VALID;
+ }
+ }
+
+ /* check for complete packet */
+ if ( s->loc[locty].w_offset > 5 &&
+ (s->loc[locty].sts & STS_EXPECT)) {
+ /* we have a packet length - see if we have all of it */
+#ifdef RAISE_STS_IRQ
+ bool needIrq = !(s->loc[locty].sts & STS_VALID);
+#endif
+ len = tpm_get_size_from_buffer(&s->loc[locty].w_buffer);
+ if (len > s->loc[locty].w_offset) {
+ s->loc[locty].sts = STS_EXPECT | STS_VALID;
+ } else {
+ /* packet complete */
+ s->loc[locty].sts = STS_VALID;
+ }
+#ifdef RAISE_STS_IRQ
+ if (needIrq) {
+ tis_raise_irq(s, locty, INT_STS_VALID);
+ }
+#endif
+ }
+ }
+ break;
+ }
+
+ qemu_mutex_unlock(&s->state_lock);
+}
+
+
+static void tis_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ return tis_mem_writel_intern(opaque, addr, val, false);
+}
+
+
+static CPUReadMemoryFunc *tis_readfn[3] = {
+ tis_mem_readl,
+ tis_mem_readl,
+ tis_mem_readl
+};
+
+static CPUWriteMemoryFunc *tis_writefn[3] = {
+ tis_mem_writel,
+ tis_mem_writel,
+ tis_mem_writel
+};
+
+
+/*
+ * This function gets called when resuming a snapshot. In that
+ * case we received the TIS state from persistent storage and
+ * just need to reset.
+ */
+void tis_reset_for_snapshot_resume(TPMState *s)
+{
+ s->tpm_initialized = false;
+ active_be->reset();
+}
+
+
+/*
+ * This function is called when the machine starts, resets or due to
+ * S3 resume.
+ * In case of S3 resume we don't reset the TIS but maintain its state.
+ */
+static void tis_reset(TPMState *s)
+{
+ int c;
+
+ s->tpm_initialized = false;
+
+ active_be->reset();
+
+ s->active_locty = NO_LOCALITY;
+ s->next_locty = NO_LOCALITY;
+ s->aborting_locty = NO_LOCALITY;
+
+ for (c = 0; c < NUM_LOCALITIES; c++) {
+ s->loc[c].access = ACCESS_TPM_REG_VALID_STS;
+ s->loc[c].sts = 0;
+ s->loc[c].inte = (1 << 3);
+ s->loc[c].ints = 0;
+ s->loc[c].state = STATE_IDLE;
+
+ s->loc[c].w_offset = 0;
+ active_be->realloc_buffer(&s->loc[c].w_buffer);
+ s->loc[c].r_offset = 0;
+ active_be->realloc_buffer(&s->loc[c].r_buffer);
+ }
+}
+
+
+static void tpm_tis_reset(DeviceState *d)
+{
+ TPMState *s = container_of(d, TPMState, busdev.qdev);
+ tis_reset(s);
+}
+
+
+static int tpm_tis_init(ISADevice *dev)
+{
+ TPMState *s = DO_UPCAST(TPMState, busdev, dev);
+ int iomemtype;
+
+ qemu_mutex_init(&s->state_lock);
+ qemu_cond_init(&s->from_tpm_cond);
+ qemu_cond_init(&s->to_tpm_cond);
+
+ if (active_be->init(s, tis_tpm_receive_cb)) {
+ goto err_exit;
+ }
+
+ isa_init_irq(dev, &s->irq, s->irq_num);
+
+ iomemtype = cpu_register_io_memory(tis_readfn, tis_writefn, s,
+ DEVICE_LITTLE_ENDIAN);
+ cpu_register_physical_memory(TIS_ADDR_BASE, 0x1000 * NUM_LOCALITIES,
+ iomemtype);
+
+ return 0;
+
+ err_exit:
+ exit(1);
+}
+
Index: qemu-git/hw/tpm_tis.h
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_tis.h
@@ -0,0 +1,140 @@
+/*
+ * tpm_tis.h - include file for tpm_tis.c
+ *
+ * Copyright (C) 2006,2010,2011 IBM Corporation
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ * David Safford <safford@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+#ifndef _HW_TPM_TIS_H
+#define _HW_TPM_TIS_H
+
+#include "isa.h"
+#include "block_int.h"
+#include "qemu-thread.h"
+
+#include <stdint.h>
+
+#define TIS_ADDR_BASE 0xFED40000
+
+#define NUM_LOCALITIES 5 /* per spec */
+#define NO_LOCALITY 0xff
+
+#define IS_VALID_LOCTY(x) ((x) < NUM_LOCALITIES)
+
+
+#define TPM_TIS_IRQ 11
+
+#define TIS_TPM_BUFFER_MAX 4096
+
+
+typedef struct TPMSizedBuffer
+{
+ uint32_t size;
+ uint8_t *buffer;
+} TPMSizedBuffer;
+
+
+enum tis_state {
+ STATE_IDLE = 0,
+ STATE_READY,
+ STATE_COMPLETION,
+ STATE_EXECUTION,
+ STATE_RECEPTION,
+};
+
+
+/* locality data -- all fields are persisted */
+typedef struct TPMLocality {
+ enum tis_state state;
+ uint8_t access;
+ uint8_t sts;
+ uint32_t inte;
+ uint32_t ints;
+
+ uint16_t w_offset;
+ uint16_t r_offset;
+ TPMSizedBuffer w_buffer;
+ TPMSizedBuffer r_buffer;
+} TPMLocality;
+
+
+/* overall state of the TPM interface; 's' marks a persisted field */
+typedef struct TPMState {
+ ISADevice busdev;
+
+ uint32_t offset;
+ uint8_t buf[TIS_TPM_BUFFER_MAX];
+
+ uint8_t active_locty;
+ uint8_t aborting_locty;
+ uint8_t next_locty;
+
+ uint8_t command_locty;
+ TPMLocality loc[NUM_LOCALITIES];
+
+ qemu_irq irq;
+ uint32_t irq_num;
+
+ QemuMutex state_lock;
+ QemuCond from_tpm_cond;
+ QemuCond to_tpm_cond;
+ bool to_tpm_execute;
+
+ bool tpm_initialized;
+} TPMState;
+
+
+typedef void (TPMRecvDataCB)(TPMState *s, uint8_t locty);
+
+typedef struct BackendTPMDriver {
+ const char *id;
+ const char *(*desc)(void);
+
+ bool (*handle_options)(QemuOpts *);
+
+ int (*init)(TPMState *s, TPMRecvDataCB *datacb);
+ int (*late_startup_tpm)(void);
+ bool (*had_startup_error)(void);
+
+ size_t (*realloc_buffer)(TPMSizedBuffer *sb);
+
+ void (*reset)(void);
+
+ int (*save_volatile_data)(void);
+ int (*load_volatile_data)(TPMState *s);
+
+ bool (*get_tpm_established_flag)(void);
+} BackendTPMDriver;
+
+extern BackendTPMDriver builtin;
+
+void tis_reset_for_snapshot_resume(TPMState *s);
+const BackendTPMDriver *tis_get_active_backend(void);
+
+/* utility functions */
+
+static inline uint16_t tpm_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+ return (sb->buffer[4] << 8) + sb->buffer[5];
+}
+
+static inline void dumpBuffer(FILE *stream,
+ unsigned char *buffer, unsigned int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (i && !(i % 16))
+ fprintf(stream, "\n");
+ fprintf(stream, "%.2X ", buffer[i]);
+ }
+ fprintf(stream, "\n");
+}
+
+#endif /* _HW_TPM_TIS_H */
Index: qemu-git/hw/pc.c
===================================================================
--- qemu-git.orig/hw/pc.c
+++ qemu-git/hw/pc.c
@@ -1154,6 +1154,9 @@ void pc_basic_device_init(qemu_irq *isa_
fd[i] = drive_get(IF_FLOPPY, 0, i);
}
fdctrl_init_isa(fd);
+
+ if (has_tpm)
+ isa_create_simple("tpm-tis");
}
void pc_pci_device_init(PCIBus *pci_bus)
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH V2 3/9] Add persistent state handling to TPM TIS frontend driver
2011-03-30 19:42 [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 1/9] Support for TPM command line options Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 2/9] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2011-03-30 19:42 ` Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process Stefan Berger
` (5 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2011-03-30 19:42 UTC (permalink / raw)
To: stefanb, qemu-devel; +Cc: andreas.niederl
[-- Attachment #1: qemu_tpm_tis_persist.diff --]
[-- Type: text/plain, Size: 5941 bytes --]
This patch adds support for handling of persisten state to the TPM TIS
frontend.
The currently used buffer is determined (can only be in currently active
locality and either be a read or a write buffer) and only that buffer's content
is stored. The reverse is done when the state is restored from disk
where the buffer's content are copied into the currently used buffer.
I adapated the structure to those used by the Xen driver in order to
provide compatibility to existing state. For that I am adding Andreas
Niederl as an author to the file.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
hw/tpm_tis.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 156 insertions(+)
Index: qemu-git/hw/tpm_tis.c
===================================================================
--- qemu-git.orig/hw/tpm_tis.c
+++ qemu-git/hw/tpm_tis.c
@@ -6,6 +6,8 @@
* Author: Stefan Berger <stefanb@us.ibm.com>
* David Safford <safford@us.ibm.com>
*
+ * Xen 4 support: Andrease Niederl <andreas.niederl@iaik,tugraz.at>
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2 of the
@@ -824,3 +826,157 @@ static int tpm_tis_init(ISADevice *dev)
exit(1);
}
+/* persistent state handling */
+
+static void tpm_tis_pre_save(void *opaque)
+{
+ TPMState *s = opaque;
+ uint8_t locty = s->active_locty;
+
+ qemu_mutex_lock(&s->state_lock);
+
+ /* wait for outstanding requests to complete */
+ if (IS_VALID_LOCTY(locty) && s->loc[locty].state == STATE_EXECUTION) {
+ qemu_cond_wait(&s->from_tpm_cond, &s->state_lock);
+ }
+
+#ifdef DEBUG_TIS_SR
+ fprintf(stderr,"tpm_tis: suspend: locty 0 : r_offset = %d, w_offset = %d\n",
+ s->loc[0].r_offset,
+ s->loc[0].w_offset);
+ if (s->loc[0].r_offset) {
+ tis_dump_state(opaque, 0);
+ }
+#endif
+
+ qemu_mutex_unlock(&s->state_lock);
+
+ /* copy current active read or write buffer into the buffer
+ written to disk */
+ if (IS_VALID_LOCTY(locty)) {
+ switch (s->loc[locty].state) {
+ case STATE_RECEPTION:
+ memcpy(s->buf,
+ s->loc[locty].w_buffer.buffer,
+ MIN(sizeof(s->buf),
+ s->loc[locty].w_buffer.size));
+ s->offset = s->loc[locty].w_offset;
+ break;
+ case STATE_COMPLETION:
+ memcpy(s->buf,
+ s->loc[locty].r_buffer.buffer,
+ MIN(sizeof(s->buf),
+ s->loc[locty].r_buffer.size));
+ s->offset = s->loc[locty].r_offset;
+ break;
+ default:
+ /* leak nothing */
+ memset(s->buf, 0x0, sizeof(s->buf));
+ break;
+ }
+ }
+
+ tis_get_active_backend()->save_volatile_data();
+}
+
+
+static int tpm_tis_post_load(void *opaque,
+ int version_id __attribute__((unused)))
+{
+ TPMState *s = opaque;
+
+ uint8_t locty = s->active_locty;
+
+ if (IS_VALID_LOCTY(locty)) {
+ switch (s->loc[locty].state) {
+ case STATE_RECEPTION:
+ memcpy(s->loc[locty].w_buffer.buffer,
+ s->buf,
+ MIN(sizeof(s->buf),
+ s->loc[locty].w_buffer.size));
+ s->loc[locty].w_offset = s->offset;
+ break;
+ case STATE_COMPLETION:
+ memcpy(s->loc[locty].r_buffer.buffer,
+ s->buf,
+ MIN(sizeof(s->buf),
+ s->loc[locty].r_buffer.size));
+ s->loc[locty].r_offset = s->offset;
+ break;
+ default:
+ break;
+ }
+ }
+
+#ifdef DEBUG_TIS_SR
+ fprintf(stderr,"tpm_tis: resume : locty 0 : r_offset = %d, w_offset = %d\n",
+ s->loc[0].r_offset,
+ s->loc[0].w_offset);
+#endif
+
+ return tis_get_active_backend()->load_volatile_data(s);
+}
+
+
+static const VMStateDescription vmstate_locty = {
+ .name = "loc",
+ .version_id = 1,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(state , TPMLocality),
+ VMSTATE_UINT32(inte , TPMLocality),
+ VMSTATE_UINT32(ints , TPMLocality),
+ VMSTATE_UINT8 (access , TPMLocality),
+ VMSTATE_UINT8 (sts , TPMLocality),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+
+static const VMStateDescription vmstate_tpm_tis = {
+ .name = "tpm",
+ .version_id = 1,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = tpm_tis_pre_save,
+ .post_load = tpm_tis_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(irq_num , TPMState),
+ VMSTATE_UINT32(offset , TPMState),
+ VMSTATE_BUFFER(buf , TPMState),
+ VMSTATE_UINT8 ( active_locty, TPMState),
+ VMSTATE_UINT8 (aborting_locty, TPMState),
+ VMSTATE_UINT8 ( next_locty, TPMState),
+
+ VMSTATE_STRUCT_ARRAY(loc, TPMState, NUM_LOCALITIES, 1,
+ vmstate_locty, TPMLocality),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+static ISADeviceInfo tpm_tis_device_info = {
+ .init = tpm_tis_init,
+ .qdev.name = "tpm-tis",
+ .qdev.size = sizeof(TPMState),
+ .qdev.no_user = 1,
+ .qdev.vmsd = &vmstate_tpm_tis,
+ .qdev.reset = tpm_tis_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT8("active_locality", TPMState,
+ active_locty, NO_LOCALITY),
+ DEFINE_PROP_UINT32("irq", TPMState,
+ irq_num, TPM_TIS_IRQ),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+
+static void tpm_tis_register_device(void)
+{
+ isa_qdev_register(&tpm_tis_device_info);
+}
+
+device_init(tpm_tis_register_device)
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process
2011-03-30 19:42 [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (2 preceding siblings ...)
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 3/9] Add persistent state handling to TPM TIS frontend driver Stefan Berger
@ 2011-03-30 19:42 ` Stefan Berger
2011-04-01 18:14 ` Blue Swirl
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 5/9] Add a debug register Stefan Berger
` (4 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Stefan Berger @ 2011-03-30 19:42 UTC (permalink / raw)
To: stefanb, qemu-devel; +Cc: andreas.niederl
[-- Attachment #1: qemu_tpm_build.diff --]
[-- Type: text/plain, Size: 2889 bytes --]
The TPM interface (tpm_tis) needs to be explicitly enabled via
./configure --enable-tpm. This restricts the building of the
TPM support to i386 and x86_64 targets since both backends I know
of, the Xen backend and the libtpms-based backend, will likely only
be available for these targets, at least initially. The list can be
easily extend. This measure prevents that one will end up with support
for a frontend but no available backend.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Index:qemu/Makefile.target
===================================================================
---
Makefile.target | 7 +++++++
configure | 20 ++++++++++++++++++++
2 files changed, 27 insertions(+)
Index: qemu-git/Makefile.target
===================================================================
--- qemu-git.orig/Makefile.target
+++ qemu-git/Makefile.target
@@ -303,6 +303,13 @@ obj-sparc-y += cs4231.o eccmemctl.o sbi.
# GRLIB
obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
+
+ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),i386 x86_64))
+
+obj-i386-$(CONFIG_TPM) += tpm_tis.o
+
+endif
+
endif
obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
Index: qemu-git/configure
===================================================================
--- qemu-git.orig/configure
+++ qemu-git/configure
@@ -175,6 +175,7 @@ trace_backend="nop"
trace_file="trace"
spice=""
rbd=""
+tpm="no"
# parse CC options first
for opt do
@@ -708,6 +709,8 @@ for opt do
;;
--kerneldir=*) kerneldir="$optarg"
;;
+ --enable-tpm) tpm="yes"
+ ;;
--with-pkgversion=*) pkgversion=" ($optarg)"
;;
--disable-docs) docs="no"
@@ -921,6 +924,7 @@ echo " Default
echo " --disable-spice disable spice"
echo " --enable-spice enable spice"
echo " --enable-rbd enable building the rados block device (rbd)"
+echo " --enable-tpm enables an emulated TPM"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@@ -2540,6 +2544,7 @@ echo "Trace output file $trace_file-<pid
echo "spice support $spice"
echo "rbd support $rbd"
echo "xfsctl support $xfs"
+echo "TPM support $tpm"
if test $sdl_too_old = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3324,6 +3329,21 @@ if test "$gprof" = "yes" ; then
fi
fi
+if test "$linux" = "yes" && test "$tpm" = "yes"; then
+ has_tpm=0
+ if test "$target_softmmu" = "yes" ; then
+ case "$TARGET_BASE_ARCH" in
+ i386)
+ has_tpm=1
+ ;;
+ esac
+ fi
+
+ if test "$has_tpm" = "1"; then
+ echo "CONFIG_TPM=y" >> $config_host_mak
+ fi
+fi
+
linker_script="-Wl,-T../config-host.ld -Wl,-T,\$(SRC_PATH)/\$(ARCH).ld"
if test "$target_linux_user" = "yes" -o "$target_bsd_user" = "yes" ; then
case "$ARCH" in
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH V2 5/9] Add a debug register
2011-03-30 19:42 [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (3 preceding siblings ...)
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process Stefan Berger
@ 2011-03-30 19:42 ` Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 6/9] Implement qemu_thread_join function Stefan Berger
` (3 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2011-03-30 19:42 UTC (permalink / raw)
To: stefanb, qemu-devel; +Cc: andreas.niederl
[-- Attachment #1: qemu_tpm_tis_debugreg.diff --]
[-- Type: text/plain, Size: 3225 bytes --]
This patch uses the possibility to add a vendor-specific register and
adds a debug register useful for dumping the internal state. This register
is only active in a debug build (#define DEBUG_TIS).
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
hw/tpm_tis.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
Index: qemu-git/hw/tpm_tis.c
===================================================================
--- qemu-git.orig/hw/tpm_tis.c
+++ qemu-git/hw/tpm_tis.c
@@ -42,6 +42,8 @@
#define TIS_REG_DID_VID 0xf00
#define TIS_REG_RID 0xf04
+/* vendor-specific registers */
+#define TIS_REG_DEBUG 0xf90
#define STS_VALID (1 << 7)
#define STS_COMMAND_READY (1 << 6)
@@ -356,6 +358,66 @@ static uint32_t tpm_data_read(TPMState *
}
+#ifdef DEBUG_TIS
+static void tis_dump_state(void *opaque, target_phys_addr_t addr)
+{
+ static const unsigned regs[] = {
+ TIS_REG_ACCESS,
+ TIS_REG_INT_ENABLE,
+ TIS_REG_INT_VECTOR,
+ TIS_REG_INT_STATUS,
+ TIS_REG_INTF_CAPABILITY,
+ TIS_REG_STS,
+ TIS_REG_DID_VID,
+ TIS_REG_RID,
+ 0xfff};
+ int idx;
+ uint8_t locty = locality_from_addr(addr);
+ target_phys_addr_t base = addr & ~0xfff;
+ TPMState *s = opaque;
+
+ fprintf(stdout,
+ "tpm_tis: active locality : %d\n"
+ "tpm_tis: state of locality %d : %d\n"
+ "tpm_tis: register dump:\n",
+ s->active_locty,
+ locty, s->loc[locty].state);
+
+ for (idx = 0; regs[idx] != 0xfff; idx++) {
+ fprintf(stdout, "tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
+ tis_mem_readl(opaque, base + regs[idx]));
+ }
+
+ fprintf(stdout,
+ "tpm_tis: read offset : %d\n"
+ "tpm_tis: result buffer : ",
+ s->loc[locty].r_offset);
+ for (idx = 0;
+ idx < tpm_get_size_from_buffer(&s->loc[locty].r_buffer);
+ idx++) {
+ fprintf(stdout, "%c%02x%s",
+ s->loc[locty].r_offset == idx ? '>' : ' ',
+ s->loc[locty].r_buffer.buffer[idx],
+ ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
+ }
+ fprintf(stdout,
+ "\n"
+ "tpm_tis: write offset : %d\n"
+ "tpm_tis: request buffer: ",
+ s->loc[locty].w_offset);
+ for (idx = 0;
+ idx < tpm_get_size_from_buffer(&s->loc[locty].w_buffer);
+ idx++) {
+ fprintf(stdout, "%c%02x%s",
+ s->loc[locty].w_offset == idx ? '>' : ' ',
+ s->loc[locty].w_buffer.buffer[idx],
+ ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
+ }
+ fprintf(stdout,"\n");
+}
+#endif
+
+
/*
* Read a register of the TIS interface
* See specs pages 33-63 for description of the registers
@@ -431,6 +493,11 @@ static uint32_t tis_mem_readl(void *opaq
case TIS_REG_RID:
val = TPM_RID;
break;
+#ifdef DEBUG_TIS
+ case TIS_REG_DEBUG:
+ tis_dump_state(opaque, addr);
+ break;
+#endif
}
qemu_mutex_unlock(&s->state_lock);
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH V2 6/9] Implement qemu_thread_join function
2011-03-30 19:42 [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (4 preceding siblings ...)
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 5/9] Add a debug register Stefan Berger
@ 2011-03-30 19:42 ` Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 7/9] Add a TPM backend skeleton implementation Stefan Berger
` (2 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2011-03-30 19:42 UTC (permalink / raw)
To: stefanb, qemu-devel; +Cc: andreas.niederl
[-- Attachment #1: qemu_thread_join.patch --]
[-- Type: text/plain, Size: 1401 bytes --]
This patch provides support for 'joining a thread' by wrapping the POSIX
pthread_join with qemu_thread_join.
Since the backend implementation is based on threads and I am stopping
and starting that thread during operations like 'snapshot resume', I
do use this functionality to synchronize with the TPM thread's termination
before terminating the TPM, creating a new one and loading previous state
from the time of the snapshot into the TPM.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
qemu-thread-posix.c | 5 +++++
qemu-thread.h | 1 +
2 files changed, 6 insertions(+)
Index: qemu-git/qemu-thread-posix.c
===================================================================
--- qemu-git.orig/qemu-thread-posix.c
+++ qemu-git/qemu-thread-posix.c
@@ -147,3 +147,8 @@ void qemu_thread_exit(void *retval)
{
pthread_exit(retval);
}
+
+int qemu_thread_join(QemuThread *thread, void **retval)
+{
+ return pthread_join(thread->thread, retval);
+}
Index: qemu-git/qemu-thread.h
===================================================================
--- qemu-git.orig/qemu-thread.h
+++ qemu-git/qemu-thread.h
@@ -35,5 +35,6 @@ void qemu_thread_create(QemuThread *thre
void qemu_thread_get_self(QemuThread *thread);
int qemu_thread_is_self(QemuThread *thread);
void qemu_thread_exit(void *retval);
+int qemu_thread_join(QemuThread *thread, void **retval);
#endif
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH V2 7/9] Add a TPM backend skeleton implementation
2011-03-30 19:42 [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (5 preceding siblings ...)
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 6/9] Implement qemu_thread_join function Stefan Berger
@ 2011-03-30 19:42 ` Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 8/9] Implementation of the libtpms-based backend Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 9/9] Add block storage support for libtpms based TPM backend Stefan Berger
8 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2011-03-30 19:42 UTC (permalink / raw)
To: stefanb, qemu-devel; +Cc: andreas.niederl
[-- Attachment #1: qemu_tpm_be_skeleton.diff --]
[-- Type: text/plain, Size: 10934 bytes --]
This patch provides a TPM backend skelteon implementation. It doesn't do
anything but it compiles.
Signed-off-by: Stefan Berger <stefanb@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,
};
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH V2 8/9] Implementation of the libtpms-based backend
2011-03-30 19:42 [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (6 preceding siblings ...)
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 7/9] Add a TPM backend skeleton implementation Stefan Berger
@ 2011-03-30 19:42 ` Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 9/9] Add block storage support for libtpms based TPM backend Stefan Berger
8 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2011-03-30 19:42 UTC (permalink / raw)
To: stefanb, qemu-devel; +Cc: andreas.niederl
[-- Attachment #1: qemu_tpm_backend.diff --]
[-- Type: text/plain, Size: 19150 bytes --]
This patch provides the glue for the TPM TIS interface (frontend) to
the libtpms that provides the actual TPM functionality.
Some details:
This part of the patch provides support for the spawning of a thread
that will interact with the libtpms-based TPM. It expects a signal
from the frontend to wake and pick up the TPM command that is supposed
to be processed and delivers the response packet using a callback
function provided by the frontend.
The backend connectects itself to the frontend by filling out an interface
structure with pointers to the function implementing support for various
operations.
In this part a structure with callback functions with is registered with
libtpms. Those callback functions mostly deal with persistent storage.
The libtpms-based backend implements functionality to write into a
Qemu block storage device rather than to plain files. With that we
can support VM snapshotting and we also get the possibility to use
encrypted QCoW2 for free. Thanks to Anthony for pointing this out.
The storage part of the driver has been split off into its own patch.
v2:
- the directory's checksum is now calculated using zlib's crc32
- fixes to adhere to the qemu coding style
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
configure | 3
hw/tpm_builtin.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
hw/tpm_tis.h | 17 ++
3 files changed, 423 insertions(+), 21 deletions(-)
Index: qemu-git/hw/tpm_tis.h
===================================================================
--- qemu-git.orig/hw/tpm_tis.h
+++ qemu-git/hw/tpm_tis.h
@@ -137,4 +137,21 @@ static inline void dumpBuffer(FILE *stre
fprintf(stream, "\n");
}
+static inline void clear_sized_buffer(TPMSizedBuffer *tpmsb)
+{
+ if (tpmsb->buffer) {
+ tpmsb->size = 0;
+ qemu_free(tpmsb->buffer);
+ tpmsb->buffer = NULL;
+ }
+}
+
+static inline void set_sized_buffer(TPMSizedBuffer *tpmsb,
+ uint8_t *buffer, uint32_t size)
+{
+ clear_sized_buffer(tpmsb);
+ tpmsb->size = size;
+ tpmsb->buffer = buffer;
+}
+
#endif /* _HW_TPM_TIS_H */
Index: qemu-git/hw/tpm_builtin.c
===================================================================
--- qemu-git.orig/hw/tpm_builtin.c
+++ qemu-git/hw/tpm_builtin.c
@@ -1,4 +1,5 @@
/*
+ * built-in TPM support using libtpms
*
* Copyright (c) 2010, 2011 IBM Corporation
* Copyright (c) 2010, 2011 Stefan Berger
@@ -17,15 +18,32 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
+#include "blockdev.h"
+#include "block_int.h"
#include "qemu-common.h"
#include "hw/hw.h"
#include "hw/tpm_tis.h"
#include "hw/pc.h"
+#include <libtpms/tpm_library.h>
+#include <libtpms/tpm_error.h>
+#include <libtpms/tpm_memory.h>
+#include <libtpms/tpm_nvfilename.h>
+#include <libtpms/tpm_tis.h>
+
+#include <zlib.h>
+
+
+/* following define will be removed once SeaBIOS has TPM support */
+
//#define DEBUG_TPM
//#define DEBUG_TPM_SR /* suspend - resume */
+#define SAVESTATE_TYPE 'S'
+#define PERMSTATE_TYPE 'P'
+#define VOLASTATE_TYPE 'V'
+
/* data structures */
typedef struct ThreadParams {
@@ -40,11 +58,17 @@ typedef struct ThreadParams {
static QemuThread thread;
static QemuMutex state_mutex; /* protects *_state below */
+static QemuCond bs_write_result_cond;
+static TPMSizedBuffer permanent_state = { .size = 0, .buffer = NULL, };
+static TPMSizedBuffer volatile_state = { .size = 0, .buffer = NULL, };
+static TPMSizedBuffer save_state = { .size = 0, .buffer = NULL, };
+static int pipefd[2] = {-1, -1};
static bool thread_terminate = false;
static bool tpm_initialized = false;
static bool had_fatal_error = false;
static bool had_startup_error = false;
+static bool need_read_volatile = false;
static ThreadParams tpm_thread_params;
@@ -58,12 +82,54 @@ static const unsigned char tpm_std_fatal
static char dev_description[80];
+static int tpmlib_get_prop(enum TPMLIB_TPMProperty prop)
+{
+ int result;
+
+ TPM_RESULT res = TPMLIB_GetTPMProperty(prop, &result);
+
+ assert(res == TPM_SUCCESS);
+
+ return result;
+}
+
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+static unsigned int memsum(const unsigned char *buf, int len)
+{
+ int res = 0, i;
+
+ for (i = 0; i < len; i++) {
+ res += buf[i];
+ }
+
+ return res;
+}
+#endif
+
+
/**
* Start the TPM. If it had been started before, then terminate and start
* it again.
*/
static int startup_tpm(void)
{
+ TPM_RESULT res;
+
+ if (tpm_initialized) {
+ TPMLIB_Terminate();
+ tpm_initialized = false;
+ }
+
+ res = TPMLIB_MainInit();
+ if (res != TPM_SUCCESS) {
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ fprintf(stderr,"tpm: Error: Call to TPMLIB_MainInit() failed (rc=%d)\n",
+ res);
+#endif
+ return 1;
+ }
+
tpm_initialized = true;
#if defined DEBUG_TPM || defined DEBUG_TPM_SR
@@ -84,6 +150,13 @@ static int late_startup_tpm(void)
{
int rc;
+ if (startup_bs(bs)) {
+ had_fatal_error = 1;
+ return 1;
+ }
+
+ load_tpm_state_from_bs(bs);
+
rc = startup_tpm();
if (rc) {
had_fatal_error = 1;
@@ -107,6 +180,7 @@ static void terminate_tpm_thread(void)
memset(&thread, 0, sizeof(thread));
if (tpm_initialized) {
+ TPMLIB_Terminate();
tpm_initialized = false;
}
}
@@ -116,15 +190,21 @@ static void terminate_tpm_thread(void)
static void tpm_atexit(void)
{
terminate_tpm_thread();
+
+ close(pipefd[0]);
+ pipefd[0] = -1;
+
+ close(pipefd[1]);
+ pipefd[1] = -1;
}
static void *mainLoop(void *d)
{
- int res = 0;
ThreadParams *tParams = (ThreadParams *)d;
uint32_t in_len, out_len;
uint8_t *in, *out;
+ TPM_RESULT res;
uint32_t resp_size; /* total length of response */
/* start command processing */
@@ -176,15 +256,15 @@ static void *mainLoop(void *d)
resp_size = 0;
+ /* TPMLIB_Process may realloc the response buffer */
+ res = TPMLIB_Process(
+ &tParams->tpm_state->loc[g_locty].r_buffer.buffer,
+ &resp_size, &out_len,
+ in, in_len);
- // !!! Send command to TPM & wait for response
-
-
- if (res != 0) {
+ if (res != TPM_SUCCESS) {
#ifdef DEBUG_TPM
- fprintf(stderr,
- "Sending/receiving TPM request/response "
- "failed\n");
+ fprintf(stderr,"TPMLIB_Process() failed\n");
#endif
had_fatal_error = 1;
}
@@ -200,7 +280,9 @@ static void *mainLoop(void *d)
}
#ifdef DEBUG_TPM
fprintf(stderr,"sending %d bytes to VM\n", resp_size);
- dumpBuffer(stdout, out, resp_size);
+ dumpBuffer(stdout,
+ tParams->tpm_state->loc[g_locty].r_buffer.buffer,
+ resp_size);
#endif
tParams->recv_data_callback(tParams->tpm_state, g_locty);
} while (in_len > 0);
@@ -210,6 +292,203 @@ static void *mainLoop(void *d)
}
+/*****************************************************************
+ * call back functions for the libtpms TPM library
+ ****************************************************************/
+static TPM_RESULT tpm_nvram_init(void)
+{
+ return TPM_SUCCESS;
+}
+
+
+static TPM_RESULT tpm_nvram_loaddata(unsigned char **data,
+ uint32_t *length,
+ size_t tpm_number __attribute__((unused)),
+ const char *name)
+{
+ TPM_RESULT rc = TPM_SUCCESS;
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ fprintf(stderr,"tpm: TPM_NVRAM_LoadData: tpm_number = %d, name = %s\n",
+ (int)tpm_number, name);
+#endif
+ *length = 0;
+
+ if (!strcmp(name, TPM_PERMANENT_ALL_NAME)) {
+ *length = permanent_state.size;
+
+ if (*length == 0) {
+ rc = TPM_RETRY;
+ } else {
+ /*
+ * keep the permanent state for
+ * as long as possible. We may
+ * be in a resume operation and only
+ * get the volatile state later on when
+ * Qemu provides the state.
+ * Once the volatile state is there,
+ * we can discard the permanent state,
+ * otherwise the perment state will be
+ * discarded in other places
+ */
+ if (volatile_state.size == 0) {
+ rc = TPM_Malloc(data, *length);
+ if (rc == 0)
+ memcpy(*data, permanent_state.buffer, *length);
+ } else {
+ *data = permanent_state.buffer;
+
+ permanent_state.size = 0;
+ permanent_state.buffer = NULL;
+ }
+ }
+ } else if (!strcmp(name, TPM_VOLATILESTATE_NAME)) {
+ *length = volatile_state.size;
+ if (*length == 0) {
+ rc = TPM_RETRY;
+ } else {
+ *data = volatile_state.buffer;
+
+ volatile_state.size = 0;
+ volatile_state.buffer = NULL;
+ }
+ } else if (!strcmp(name, TPM_SAVESTATE_NAME)) {
+ *length = save_state.size;
+ if (*length == 0) {
+ rc = TPM_RETRY;
+ } else {
+ *data = save_state.buffer;
+ save_state.size = 0;
+ save_state.buffer = NULL;
+ }
+ }
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ fprintf(stderr, "tpm: Read %d bytes of state [sum=%08x]; rc = %d\n",
+ *length, memsum(*data, *length), rc);
+#endif
+
+ return rc;
+}
+
+
+/*
+ * Called by the TPM when permanent data, savestate or volatile state
+ * is updated or needs to be saved.
+ * Primarily we care about savestate and permanent data here.
+ */
+static TPM_RESULT tpm_nvram_storedata(const unsigned char *data,
+ uint32_t length,
+ size_t tpm_number __attribute__((unused)),
+ const char *name)
+{
+ TPM_RESULT rc = TPM_SUCCESS;
+ char what;
+ TPMSizedBuffer *tsb = NULL;
+
+ if (!strcmp(name, TPM_PERMANENT_ALL_NAME)) {
+ tsb = &permanent_state;
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ fprintf(stderr,"tpm: STORING %5d BYTES OF PERMANENT ALL [sum=%08x]\n",
+ length, memsum(data, length));
+#endif
+ what = PERMSTATE_TYPE;
+ } else if (!strcmp(name, TPM_SAVESTATE_NAME)) {
+ tsb = &save_state;
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ fprintf(stderr,"tpm: STORING %5d BYTES OF SAVESTATE [sum=%08x]\n",
+ length, memsum(data, length));
+#endif
+ what = SAVESTATE_TYPE;
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ } else if (!strcmp(name, TPM_VOLATILESTATE_NAME)) {
+ fprintf(stderr,"tpm: GOT %5d BYTES OF VOLATILE STATE [sum=%08x]\n",
+ length, memsum(data, length));
+#endif
+ }
+
+ if (tsb) {
+ /* we may get called here during TPMLIB_MainInit() rather than
+ while running as a thread. Skip writing the state until we
+ process the first command. */
+ qemu_mutex_lock(&state_mutex);
+
+ set_sized_buffer(tsb, (unsigned char *)data, length);
+
+ if (request_sync_to_bs(what)) {
+ rc = TPM_FAIL;
+ }
+
+ /* TPM library will free */
+ tsb->size = 0;
+ tsb->buffer = NULL;
+
+ qemu_mutex_unlock(&state_mutex);
+ }
+
+ if (had_fatal_error) {
+ rc = TPM_FAIL;
+ }
+
+ return rc;
+}
+
+
+static TPM_RESULT tpm_nvram_deletename(
+ size_t tpm_number __attribute__((unused)),
+ const char *name,
+ TPM_BOOL mustExist)
+{
+ TPM_RESULT rc = TPM_SUCCESS;
+
+ /* only handle the savestate here */
+ if (!strcmp(name, TPM_SAVESTATE_NAME)) {
+ qemu_mutex_lock(&state_mutex);
+
+ clear_sized_buffer(&save_state);
+
+ if (request_sync_to_bs(SAVESTATE_TYPE)) {
+ rc = TPM_FAIL;
+ }
+
+ qemu_mutex_unlock(&state_mutex);
+ } else if (!strcmp(name, TPM_VOLATILESTATE_NAME)) {
+ qemu_mutex_lock(&state_mutex);
+
+ clear_sized_buffer(&volatile_state);
+
+ if (request_sync_to_bs(VOLASTATE_TYPE)) {
+ rc = TPM_FAIL;
+ }
+
+ qemu_mutex_unlock(&state_mutex);
+ }
+
+ return rc;
+}
+
+
+static TPM_RESULT tpm_io_init(void)
+{
+ return TPM_SUCCESS;
+}
+
+
+static TPM_RESULT tpm_io_getlocality(TPM_MODIFIER_INDICATOR *localityModifier)
+{
+ *localityModifier = (TPM_MODIFIER_INDICATOR)g_locty;
+
+ return TPM_SUCCESS;
+}
+
+
+static TPM_RESULT tpm_io_getphysicalpresence(TPM_BOOL *physicalPresence)
+{
+ *physicalPresence = FALSE;
+
+ return TPM_SUCCESS;
+}
+
/*****************************************************************/
@@ -229,8 +508,12 @@ static void reset(void)
terminate_tpm_thread();
}
+ clear_sized_buffer(&permanent_state);
+ clear_sized_buffer(&save_state);
+ clear_sized_buffer(&volatile_state);
had_fatal_error = false;
thread_terminate = false;
+ need_read_volatile = false;
had_startup_error = false;
qemu_thread_create(&thread, mainLoop, &tpm_thread_params);
@@ -261,32 +544,92 @@ static int instantiate_with_volatile_dat
#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);
}
+ /* we need to defer the read since we will not have the encryption key
+ in case storage is encrypted at this point */
+ need_read_volatile = true;
+
return 0;
}
+struct libtpms_callbacks callbacks = {
+ .sizeOfStruct = sizeof(struct libtpms_callbacks),
+ .tpm_nvram_init = tpm_nvram_init,
+ .tpm_nvram_loaddata = tpm_nvram_loaddata,
+ .tpm_nvram_storedata = tpm_nvram_storedata,
+ .tpm_nvram_deletename = tpm_nvram_deletename,
+ .tpm_io_init = tpm_io_init,
+ .tpm_io_getlocality = tpm_io_getlocality,
+ .tpm_io_getphysicalpresence = tpm_io_getphysicalpresence,
+};
+
+
static int init(TPMState *s, TPMRecvDataCB *recv_data_cb)
{
+ int flags;
+
+ bs = bdrv_find("vtpm-nvram");
+ if (bs == NULL) {
+ fprintf(stderr, "The vtpm-nvram driver was not found.\n");
+ goto err_exit;
+ }
+
+ if (TPMLIB_RegisterCallbacks(&callbacks) != TPM_SUCCESS) {
+ goto err_exit;
+ }
+
+ if (check_bs(bs)) {
+ goto err_exit;
+ }
+
tpm_thread_params.tpm_state = s;
tpm_thread_params.recv_data_callback = recv_data_cb;
qemu_mutex_init(&state_mutex);
+ qemu_cond_init(&bs_write_result_cond);
- // !!! Do necessary initialization here
+ if (pipe(pipefd)) {
+ goto err_exit;
+ }
+
+ flags = fcntl(pipefd[0], F_GETFL);
+ if (flags < 0) {
+ goto err_exit_close_pipe;
+ }
+
+ if (fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK ) < 0) {
+ goto err_exit_close_pipe;
+ }
+
+ qemu_set_fd_handler(pipefd[0], fulfill_sync_to_bs_request, NULL, NULL);
atexit(tpm_atexit);
return 0;
+
+err_exit_close_pipe:
+ close(pipefd[0]);
+ pipefd[0] = -1;
+ close(pipefd[1]);
+ pipefd[1] = -1;
+
+err_exit:
+ return 1;
}
static bool get_tpm_established_flag(void)
{
- return false;
+ TPM_BOOL tpmEstablished = false;
+
+ if (tpm_initialized) {
+ TPM_IO_TpmEstablished_Get(&tpmEstablished);
+ }
+
+ return (bool)tpmEstablished;
}
@@ -298,10 +641,16 @@ static bool get_startup_error(void)
/**
* This function is called by tpm_tis.c once the TPM has processed
- * the last command and returned the response to the TIS.
+ * the last command and returned the response to the TIS. Since we
+ * store the volatile state into the block storage device we leave
+ * the provided buffer untouched.
*/
static int save_volatile_data(void)
{
+ TPM_RESULT res;
+ unsigned char *buffer;
+ uint32_t buflen;
+
if (!tpm_initialized) {
/* TPM was never initialized
volatile_state.buffer may be NULL if TPM was never used.
@@ -309,20 +658,50 @@ static int save_volatile_data(void)
return 0;
}
+ /* have the serialized state written to a buffer only */
+#ifdef DEBUG_TPM_SR
+ fprintf(stderr,"tpm: Calling TPMLIB_VolatileAll_Store()\n");
+#endif
+ res = TPMLIB_VolatileAll_Store(&buffer, &buflen);
+
+ if (res != TPM_SUCCESS) {
+#ifdef DEBUG_TPM_SR
+ fprintf(stderr,"tpm: Error: Could not store TPM volatile state\n");
+#endif
+ return 1;
+ }
+
+#ifdef DEBUG_TPM_SR
+ fprintf(stderr,"tpm: got %d bytes of volatilestate [sum=%08x]\n",
+ buflen, memsum(buffer, buflen));
+#endif
+
+ set_sized_buffer(&volatile_state, buffer, buflen);
+ if (write_state_to_bs(VOLASTATE_TYPE)) {
+ return 1;
+ }
+ volatile_state.size = 0;
+ volatile_state.buffer = NULL;
+
+ /* make sure that everything has been written to disk */
+ fulfill_sync_to_bs_request(NULL);
+
return 0;
}
static size_t realloc_buffer(TPMSizedBuffer *sb)
{
- size_t wanted_size = 4096;
+ TPM_RESULT res;
+ size_t wanted_size = tpmlib_get_prop(TPMPROP_TPM_BUFFER_MAX);
if (sb->size != wanted_size) {
- sb->buffer = qemu_realloc(&sb->buffer, wanted_size);
- if (sb->buffer != NULL)
+ res = TPM_Realloc(&sb->buffer, wanted_size);
+ if (res == TPM_SUCCESS) {
sb->size = wanted_size;
- else
+ } else {
sb->size = 0;
+ }
}
return sb->size;
}
@@ -334,7 +713,8 @@ static const char *create_desc(void)
if (!done) {
snprintf(dev_description, sizeof(dev_description),
- "Skeleton TPM backend");
+ "Qemu's built-in TPM; requires %ukb of block storage",
+ MINIMUM_BS_SIZE_KB);
done = 1;
}
@@ -342,13 +722,15 @@ static const char *create_desc(void)
}
+#define TPM_OPTS "id=vtpm-nvram"
+
static bool handle_options(QemuOpts *opts)
{
const char *value;
value = qemu_opt_get(opts, "path");
if (value) {
- // !!! handle path parameter
+ drive_add(IF_NONE, -1, value, TPM_OPTS);
} else {
fprintf(stderr,"-tpm is missing path= parameter\n");
return false;
@@ -357,8 +739,8 @@ static bool handle_options(QemuOpts *opt
}
-BackendTPMDriver skeleton = {
- .id = "skeleton",
+BackendTPMDriver builtin = {
+ .id = "builtin",
.desc = create_desc,
.handle_options = handle_options,
.init = init,
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH V2 9/9] Add block storage support for libtpms based TPM backend
2011-03-30 19:42 [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (7 preceding siblings ...)
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 8/9] Implementation of the libtpms-based backend Stefan Berger
@ 2011-03-30 19:42 ` Stefan Berger
8 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2011-03-30 19:42 UTC (permalink / raw)
To: stefanb, qemu-devel; +Cc: andreas.niederl
[-- Attachment #1: qemu_tpm_backend_bs.diff --]
[-- Type: text/plain, Size: 22746 bytes --]
This patch supports the storage of TPM persisten state.
The TPM creates state of varying size, depending for example how many
keys are loaded into it a a certain time. The worst-case sizes of
the different blobs the TPM can write have been pre-calculated and this
value is used to determine the minimum size of the Qcow2 image. It needs to
be 63kb. 'qemu-... -tpm ?' shows this number when this backend driver is
available.
The layout of the TPM's persistent data in the block storage is as follows:
The first sector (512 bytes) holds a primitive directory for the different
types of blobs that the TPM can write. This directory holds a revision
number, a checksum over its content, the number of entries, and the entries
themselves. The entries are described through their absolute offsets, their
maximum sizes, the number of currently valid bytes (the blobs infalte
and deflate) and what type of blob it is (see below for the types).
typedef struct BSDir {
uint16_t rev;
uint32_t checksum;
uint32_t num_entries;
BSEntry entries[BS_DIR_MAX_NUM_ENTRIES];
} __attribute__((packed)) BSDir;
Their worst case sizes have been calculated and according to these sizes
the blobs are written at certain offsets into the blockstorage. Their offsets
are all aligned to sectors (512 byte boundaries).
The TPM provides three different blobs that are written into the storage:
- volatile state
- permanent state
- save state
The 'save state' is written when the VM suspends (ACPI S3) and read when it
resumes. This is done in concert with the BIOS where the BIOS needs to send
a command to the TPM upon resume (TPM_Startup(ST_STATE)), while the OS
issues the command TPM_SaveState().
The 'perment state' is written when the TPM receives a command that alters
its permenent state, i.e., when the a key is loaded into the TPM that
is expected to be there upon reboot of the machine / VM.
Volatile state is written when the frontend triggers it to do so, i.e.,
when the VM's state is written out during taking of a snapshot, migration
or suspension to disk (as in 'virsh save'). This state serves to resume
at the point where the TPM previously stopped but there is no need for it
after a machine reboot for example.
Tricky parts here are related to encrypted storage where certain operations
need to be deferred since the key for the storage only becomes available
much later than the time that the backend is instantiated.
The backend also tries to check for the validity of the block storage for
example. If the Qcow2 is not encrypted and the checksum is found to be
bad, the block storage directory will be initialized.
In case the Qcow2 is encrypted, initialization will only be done if
the directory is found to be all 0s. In case the directory cannot be
checksummed correctly, but is not all 0s, it is assumed that the user
provided a wrong key. In this case I am not exiting qemu, but black-out
the TPM interface (returns 0xff in all memory location) due to a presumed
fatal error and let the VM run (without TPM functionality).
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
hw/tpm_builtin.c | 685 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 684 insertions(+), 1 deletion(-)
Index: qemu-git/hw/tpm_builtin.c
===================================================================
--- qemu-git.orig/hw/tpm_builtin.c
+++ qemu-git/hw/tpm_builtin.c
@@ -44,6 +44,33 @@
#define PERMSTATE_TYPE 'P'
#define VOLASTATE_TYPE 'V'
+#define ALIGN(VAL, SIZE) \
+ ( ( (VAL) + (SIZE) - 1 ) & ~( (SIZE) - 1 ) )
+
+
+#define DIRECTORY_SIZE BDRV_SECTOR_SIZE
+
+#define PERMSTATE_DISK_OFFSET ALIGN(DIRECTORY_SIZE, BDRV_SECTOR_SIZE)
+#define PERMSTATE_DISK_SPACE \
+ ALIGN(tpmlib_get_prop(TPMPROP_TPM_MAX_NV_SPACE),\
+ BDRV_SECTOR_SIZE)
+#define SAVESTATE_DISK_OFFSET (PERMSTATE_DISK_OFFSET + PERMSTATE_DISK_SPACE)
+#define SAVESTATE_DISK_SPACE \
+ ALIGN(tpmlib_get_prop(TPMPROP_TPM_MAX_SAVESTATE_SPACE),\
+ BDRV_SECTOR_SIZE)
+#define VOLASTATE_DISK_OFFSET (SAVESTATE_DISK_OFFSET + SAVESTATE_DISK_SPACE)
+#define VOLASTATE_DISK_SPACE \
+ ALIGN(tpmlib_get_prop(TPMPROP_TPM_MAX_VOLATILESTATE_SPACE),\
+ BDRV_SECTOR_SIZE)
+
+# define MINIMUM_BS_SIZE ALIGN(ALIGN(VOLASTATE_DISK_OFFSET +\
+ VOLASTATE_DISK_SPACE, \
+ BDRV_SECTOR_SIZE), \
+ 1024)
+
+#define MINIMUM_BS_SIZE_KB (int)(MINIMUM_BS_SIZE / 1024)
+
+
/* data structures */
typedef struct ThreadParams {
@@ -53,6 +80,37 @@ typedef struct ThreadParams {
} ThreadParams;
+enum BSEntryType {
+ BS_ENTRY_PERMSTATE,
+ BS_ENTRY_SAVESTATE,
+ BS_ENTRY_VOLASTATE,
+
+ BS_ENTRY_LAST,
+};
+
+
+typedef struct BSEntry {
+ enum BSEntryType type;
+ uint64_t offset;
+ uint32_t space;
+ uint32_t blobsize;
+} __attribute__((packed)) BSEntry;
+
+
+#define BS_DIR_MAX_NUM_ENTRIES 3 /* permanent, volatile savestate */
+
+typedef struct BSDir {
+ uint16_t rev;
+ uint32_t checksum;
+ uint32_t num_entries;
+ BSEntry entries[BS_DIR_MAX_NUM_ENTRIES];
+} __attribute__((packed)) BSDir;
+
+
+#define BS_DIR_REV1 1
+
+#define BS_DIR_REV_CURRENT BS_DIR_REV1
+
/* local variables */
static QemuThread thread;
@@ -71,6 +129,7 @@ static bool had_startup_error = false;
static bool need_read_volatile = false;
static ThreadParams tpm_thread_params;
+static BlockDriverState *bs;
/* locality of the command being executed by libtpms */
static uint8_t g_locty;
@@ -82,6 +141,10 @@ static const unsigned char tpm_std_fatal
static char dev_description[80];
+static void adjust_data_layout(BlockDriverState *bs, BSDir *dir);
+
+
+
static int tpmlib_get_prop(enum TPMLIB_TPMProperty prop)
{
int result;
@@ -94,7 +157,6 @@ static int tpmlib_get_prop(enum TPMLIB_T
}
-#if defined DEBUG_TPM || defined DEBUG_TPM_SR
static unsigned int memsum(const unsigned char *buf, int len)
{
int res = 0, i;
@@ -105,7 +167,628 @@ static unsigned int memsum(const unsigne
return res;
}
+
+
+/************************************************
+ Block Storage interaction
+ ***********************************************/
+static int find_bs_entry_idx(BSDir *dir, enum BSEntryType type)
+{
+ unsigned int c;
+
+ for (c = 0; c < dir->num_entries; c++) {
+ if (dir->entries[c].type == type) {
+ return c;
+ }
+ }
+
+ return -ENOENT;
+}
+
+
+static unsigned int sizeof_bsdir(BSDir *dir)
+{
+ return offsetof(BSDir, entries) +
+ dir->num_entries * sizeof(BSEntry);
+}
+
+
+static uint32_t calc_dir_checksum(BSDir *dir)
+{
+ uint16_t checksum, orig;
+
+ orig = dir->checksum;
+ dir->checksum = 0;
+
+ checksum = crc32(0, (unsigned char *)dir, sizeof_bsdir(dir));
+
+ dir->checksum = orig;
+
+ return checksum;
+}
+
+
+static void dir_be_to_cpu(BSDir *dir)
+{
+ unsigned int c;
+
+ be16_to_cpus(&dir->rev);
+ be32_to_cpus(&dir->checksum);
+ be32_to_cpus(&dir->num_entries);
+
+ for (c = 0; c < dir->num_entries && c < BS_DIR_MAX_NUM_ENTRIES; c++) {
+ be32_to_cpus(&dir->entries[c].type);
+ be64_to_cpus(&dir->entries[c].offset);
+ be32_to_cpus(&dir->entries[c].space);
+ be32_to_cpus(&dir->entries[c].blobsize);
+ }
+}
+
+
+static void dir_cpu_to_be(BSDir *dir)
+{
+ unsigned int c;
+
+ for (c = 0; c < dir->num_entries && c < BS_DIR_MAX_NUM_ENTRIES; c++) {
+ dir->entries[c].type = cpu_to_be32(dir->entries[c].type);
+ dir->entries[c].offset = cpu_to_be64(dir->entries[c].offset);
+ dir->entries[c].space = cpu_to_be32(dir->entries[c].space);
+ dir->entries[c].blobsize = cpu_to_be32(dir->entries[c].blobsize);
+ }
+
+ dir->rev = cpu_to_be16(dir->rev);
+ dir->checksum = cpu_to_be32(dir->checksum);
+ dir->num_entries = cpu_to_be32(dir->num_entries);
+}
+
+
+static bool is_valid_bsdir(BSDir *dir)
+{
+ if (dir->rev != BS_DIR_REV_CURRENT ||
+ dir->num_entries > BS_DIR_MAX_NUM_ENTRIES) {
+ return false;
+ }
+ return (dir->checksum == calc_dir_checksum(dir));
+}
+
+
+static int create_blank_dir(BlockDriverState *bs)
+{
+ uint8_t buf[BDRV_SECTOR_SIZE];
+ BSDir *dir;
+
+ memset(buf, 0x0, sizeof(buf));
+
+ dir = (BSDir *)buf;
+ dir->rev = BS_DIR_REV_CURRENT;
+ dir->num_entries = 0;
+
+ dir->checksum = calc_dir_checksum(dir);
+
+ dir_cpu_to_be(dir);
+
+ if (bdrv_write(bs, 0, buf, 1) < 0) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Validate the block storage doing some basic tests. That's
+ * all that can be done at this point since we don't have the
+ * key yet in case it is encrypted.
+ */
+static int check_bs(BlockDriverState *bs)
+{
+ int64_t len;
+ char buf[20];
+
+ if (!bs) {
+ fprintf(stderr, "Need a block driver for this vTPM type.\n");
+ goto err_exit;
+ }
+
+ len = bdrv_getlength(bs);
+ if (len < MINIMUM_BS_SIZE) {
+ fprintf(stderr, "Required size for vTPM backing store is %dkb\n",
+ MINIMUM_BS_SIZE_KB);
+ goto err_exit;
+ }
+
+ bdrv_get_format(bs, buf, sizeof(buf));
+ if (strcmp(buf, "qcow2")) {
+ fprintf(stderr, "vTPM backing store must be of type qcow2\n");
+ goto err_exit;
+ }
+
+ return 0;
+
+ err_exit:
+ fprintf(stderr,
+ "Create the drive using 'qemu-img create -f qcow2 "
+ "<filename> %dk'\n", MINIMUM_BS_SIZE_KB);
+ return -EFAULT;
+}
+
+
+static uint32_t get_bs_entry_type_space(enum BSEntryType type)
+{
+ switch (type) {
+ case BS_ENTRY_PERMSTATE:
+ return PERMSTATE_DISK_SPACE;
+ case BS_ENTRY_SAVESTATE:
+ return SAVESTATE_DISK_SPACE;
+ case BS_ENTRY_VOLASTATE:
+ return VOLASTATE_DISK_SPACE;
+ default:
+ assert(false);
+ }
+}
+
+
+/*
+ * Startup the block storage: read the directory and check whether its
+ * checksum is valid. If the checksum is not valid then
+ *
+ * - if the block storage is not encrypted initialize it assuming it's
+ * been freshly created or corrupted
+ *
+ * - if the block storage is encrypted
+ * - check whether it's been freshly created (expecting a 0 sum of the
+ * directory; seems to work with any key) and initialize it in that case
+ * - otherwise, if there are some unreadable data, assume that
+ * the wrong key was given and mark it as a starup error. We log it
+ * but won't exit() here.
+ */
+static int startup_bs(BlockDriverState *bs)
+{
+ uint8_t buf[BDRV_SECTOR_SIZE];
+ BSDir *dir;
+
+ if (bdrv_read(bs, 0, buf, 1) < 0) {
+ return -EIO;
+ }
+
+ dir = (BSDir *)buf;
+
+ dir_be_to_cpu(dir);
+
+ if (!is_valid_bsdir(dir)) {
+ /* if it's encrypted and has something else than null-content,
+ we assume to have the wrong key */
+ if (bdrv_is_encrypted(bs)) {
+ if (memsum(buf, sizeof(buf)) != 0) {
+ fprintf(stderr,
+ "vTPM block storage directory is not valid. "
+ "Assuming the key is wrong.\n");
+ had_startup_error = true;
+ return 1;
+ }
+ }
+#ifdef DEBUG_TPM
+ fprintf(stderr, "*** tpm: Blanking the storage directory.\n");
#endif
+ return create_blank_dir(bs);
+ }
+
+ /* not that we can read the dir, make sure the data are layed out
+ * correctly.
+ */
+ adjust_data_layout(bs, dir);
+
+ return 0;
+}
+
+
+static int create_bs_entry(BlockDriverState *bs,
+ BSDir *dir,
+ enum BSEntryType type,
+ uint32_t blobsize)
+{
+ uint8_t buf[BDRV_SECTOR_SIZE];
+ uint32_t idx = dir->num_entries++;
+ unsigned int bsdir_size;
+
+ dir->entries[idx].offset = (idx == 0)
+ ? ALIGN(DIRECTORY_SIZE, BDRV_SECTOR_SIZE)
+ : dir->entries[idx-1].offset + ALIGN(dir->entries[idx-1].space,
+ BDRV_SECTOR_SIZE);
+
+ dir->entries[idx].type = type;
+
+ dir->entries[idx].space = get_bs_entry_type_space(type);
+ dir->entries[idx].blobsize = blobsize;
+
+ dir->checksum = calc_dir_checksum(dir);
+
+ bsdir_size = sizeof_bsdir(dir);
+
+ dir_cpu_to_be(dir);
+
+ assert(dir->entries[idx].space >= blobsize);
+
+ memset(buf, 0x0, sizeof(buf));
+ memcpy(buf, dir, bsdir_size);
+
+ if (bdrv_write(bs, 0, buf, 1) < 0) {
+ idx = -EIO;
+ }
+
+ dir_be_to_cpu(dir);
+
+ return idx;
+}
+
+
+static int get_bs_entry(BlockDriverState *bs,
+ enum BSEntryType type,
+ BSEntry *entry)
+{
+ uint8_t buf[BDRV_SECTOR_SIZE];
+ BSDir *dir;
+ int idx;
+
+ if (bdrv_read(bs, 0, buf, 1) < 0) {
+ return -EIO;
+ }
+
+ dir = (BSDir *)buf;
+
+ dir_be_to_cpu(dir);
+
+ assert(is_valid_bsdir(dir));
+
+ if ((idx = find_bs_entry_idx(dir, type)) < 0) {
+ if ((idx = create_bs_entry(bs, dir, type, 0)) < 0) {
+ return -EIO;
+ }
+ }
+
+ memcpy(entry, &dir->entries[idx], sizeof(*entry));
+
+ return 0;
+}
+
+
+static int set_bs_entry_size(BlockDriverState *bs,
+ enum BSEntryType type,
+ BSEntry *entry,
+ uint32_t blobsize)
+{
+ uint8_t buf[BDRV_SECTOR_SIZE];
+ BSDir *dir;
+ int idx;
+
+ if (bdrv_read(bs, 0, buf, 1) < 0) {
+ return -EIO;
+ }
+
+ dir = (BSDir *)buf;
+
+ dir_be_to_cpu(dir);
+
+ assert(is_valid_bsdir(dir));
+
+ if ((idx = find_bs_entry_idx(dir, type)) < 0) {
+ if ((idx = create_bs_entry(bs, dir, type, 0)) < 0) {
+ return -EIO;
+ }
+ }
+
+ assert(blobsize <= dir->entries[idx].space);
+ dir->entries[idx].blobsize = blobsize;
+
+ dir->checksum = calc_dir_checksum(dir);
+
+ dir_cpu_to_be(dir);
+
+ if (bdrv_write(bs, 0, buf, 1) < 0) {
+ return -EIO;
+ }
+
+ dir_be_to_cpu(dir);
+
+ memcpy(entry, &dir->entries[idx], sizeof(*entry));
+
+ return 0;
+}
+
+
+static int load_sized_data_from_bs(BlockDriverState *bs,
+ enum BSEntryType be,
+ TPMSizedBuffer *tsb)
+{
+ BSEntry entry;
+ int n;
+
+ if ((n = get_bs_entry(bs, be, &entry)) < 0) {
+ return n;
+ }
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ fprintf(stderr,"load: be-type: %d, offset: %6ld, size: %5d\n",
+ be, entry.offset, entry.blobsize);
+#endif
+
+ if (entry.blobsize == 0) {
+ return 0;
+ }
+
+ tsb->buffer = qemu_malloc(entry.blobsize);
+ if (!tsb->buffer) {
+ return -ENOMEM;
+ }
+
+ tsb->size = entry.blobsize;
+
+ if (bdrv_pread(bs, entry.offset, tsb->buffer, tsb->size) != tsb->size) {
+ clear_sized_buffer(tsb);
+ fprintf(stderr,"tpm: Error while reading sized data!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int load_tpm_permanent_state_from_bs(BlockDriverState *bs,
+ TPMSizedBuffer *tsb)
+{
+ return load_sized_data_from_bs(bs, BS_ENTRY_PERMSTATE, tsb);
+}
+
+
+static int load_tpm_savestate_from_bs(BlockDriverState *bs,
+ TPMSizedBuffer *tsb)
+{
+ return load_sized_data_from_bs(bs, BS_ENTRY_SAVESTATE, tsb);
+}
+
+
+static int load_tpm_volatile_state_from_bs(BlockDriverState *bs,
+ TPMSizedBuffer *tsb)
+{
+ return load_sized_data_from_bs(bs, BS_ENTRY_VOLASTATE, tsb);
+}
+
+
+static int save_sized_data_to_bs(BlockDriverState *bs,
+ enum BSEntryType be,
+ uint8_t *data, uint32_t data_len)
+{
+ BSEntry entry;
+ int n;
+
+ if ((n = set_bs_entry_size(bs, be, &entry, data_len)) < 0) {
+ return n;
+ }
+
+ if (data_len > 0) {
+ if (bdrv_pwrite(bs, entry.offset, data, data_len) != data_len) {
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Write the TPM's state to block storage */
+static int sync_permanent_state_to_disk(BlockDriverState *bs)
+{
+ int rc = 0;
+
+ if (permanent_state.size) {
+ rc = save_sized_data_to_bs(bs, BS_ENTRY_PERMSTATE,
+ permanent_state.buffer,
+ permanent_state.size);
+ }
+
+ return rc;
+}
+
+
+static int sync_savestate_to_disk(BlockDriverState *bs)
+{
+ return save_sized_data_to_bs(bs, BS_ENTRY_SAVESTATE,
+ save_state.buffer, save_state.size);
+}
+
+
+static int sync_volatile_state_to_disk(BlockDriverState *bs)
+{
+ return save_sized_data_to_bs(bs, BS_ENTRY_VOLASTATE,
+ volatile_state.buffer, volatile_state.size);
+}
+
+
+/*
+ * Write a given type of state, identified by the char, to block
+ * storage. If anything goes wrong, set the had_fatal_error variable
+ */
+static int write_state_to_bs(char what)
+{
+ int rc = 0;
+
+ qemu_mutex_lock(&state_mutex);
+
+ switch (what) {
+ case PERMSTATE_TYPE:
+ rc = sync_permanent_state_to_disk(bs);
+ break;
+ case SAVESTATE_TYPE:
+ rc = sync_savestate_to_disk(bs);
+ break;
+ case VOLASTATE_TYPE:
+ rc = sync_volatile_state_to_disk(bs);
+ break;
+ default:
+ assert(false);
+ }
+
+ if (rc) {
+ fprintf(stderr,"tpm: Error while writing TPM state to bs. "
+ "Setting fatal error.");
+ had_fatal_error = true;
+ }
+
+ qemu_mutex_unlock(&state_mutex);
+
+ return rc;
+}
+
+
+/*
+ * Write the 'savestate' or 'permanent state' in the
+ * global buffer to disk. The requester tells us what
+ * to writy by a single byte in the pipe. If anything
+ * goes wrong, we'll set the had_fatal_error flag.
+ * We sync with the requester using signals on a
+ * condition.
+ */
+static void fulfill_sync_to_bs_request(void *opaque)
+{
+ char buf[10];
+ int c, n;
+
+ while ((n = read(pipefd[0], buf, sizeof(buf))) > 0) {
+ for (c = 0; c < n; c++) {
+ write_state_to_bs(buf[c]);
+ }
+ }
+
+ qemu_cond_signal(&bs_write_result_cond);
+}
+
+
+/*
+ * Request that either savestate or permanent state be written
+ * to the disk. Call this function with the state_mutex held.
+ * It will synchronize with the sync_to_bs function that does
+ * the work. In case a previous fatal error occurred, nothing
+ * will be done.
+ */
+static bool request_sync_to_bs(char what)
+{
+ char cmd[1] = { what };
+
+ if (had_fatal_error) {
+ return had_fatal_error;
+ }
+
+ if (write(pipefd[1], cmd, 1) != 1) {
+ had_fatal_error = true;
+ return true;
+ }
+
+ if (tpm_initialized) {
+ qemu_cond_wait(&bs_write_result_cond, &state_mutex);
+ } else {
+ /* during initialization: defer the write */
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ fprintf(stderr,"tpm: deferred write/sync since not in thread\n");
+#endif
+ }
+
+ return had_fatal_error;
+}
+
+
+static void load_tpm_state_from_bs(BlockDriverState *bs)
+{
+ load_tpm_permanent_state_from_bs(bs, &permanent_state);
+ load_tpm_savestate_from_bs(bs, &save_state);
+
+ if (need_read_volatile) {
+ clear_sized_buffer(&volatile_state);
+ load_tpm_volatile_state_from_bs(bs, &volatile_state);
+ need_read_volatile = false;
+ }
+}
+
+
+/*
+ * Adjust the layout of the data. This may be necessary if
+ * we migrated to a TPM implementation with different blob
+ * sizes that would altogether fit onto the disk but write into
+ * the next blob due to their current layout.
+ *
+ * At this point we must already have passed the size check of
+ * the drive and know it is big enough. For encrypted storage
+ * we must have the (right!) key at this point.
+ */
+static void adjust_data_layout(BlockDriverState *bs, BSDir *dir)
+{
+ unsigned int c;
+ int rc;
+ uint64_t exp_offset = ALIGN(DIRECTORY_SIZE, BDRV_SECTOR_SIZE);
+ bool need_move = false;
+ TPMSizedBuffer tsb[BS_ENTRY_LAST];
+ enum BSEntryType type;
+
+ /* check all entries' space and offsets */
+ for (c = 0; c < dir->num_entries; c++) {
+ if (dir->entries[c].offset != exp_offset) {
+ fprintf(stdout,
+ "entry[%d].type = %d, offset = %ld, expected = %ld\n",
+ c,
+ dir->entries[c].type,
+ dir->entries[c].offset,
+ exp_offset);
+ need_move = true;
+ break;
+ }
+ if (dir->entries[c].space !=
+ get_bs_entry_type_space(dir->entries[c].type)) {
+ fprintf(stdout,
+ "entry[%d].type = %d, space = %d, expected = %d\n",
+ c,
+ dir->entries[c].type,
+ dir->entries[c].space,
+ get_bs_entry_type_space(dir->entries[c].type));
+ need_move = true;
+ break;
+ }
+ exp_offset = ALIGN(exp_offset + dir->entries[c].space,
+ BDRV_SECTOR_SIZE);
+ }
+
+#if defined DEBUG_TPM || defined DEBUG_TPM_SR
+ fprintf(stderr," Data move necessary: %s\n",
+ (need_move) ? "true" : "false");
+#endif
+
+ if (need_move) {
+ /*
+ * read data
+ * create blank dir
+ * write all data back
+ */
+
+ for (type = 0; type < BS_ENTRY_LAST; type++) {
+ tsb[type].size = 0;
+ tsb[type].buffer = NULL;
+
+ load_sized_data_from_bs(bs, type, &tsb[type]);
+ }
+
+ create_blank_dir(bs);
+ dir = NULL;
+
+ for (type = 0; type < BS_ENTRY_LAST; type++) {
+ rc = save_sized_data_to_bs(bs, type,
+ tsb[type].buffer,
+ tsb[type].size);
+ if (rc) {
+ had_fatal_error = true;
+ break;
+ }
+ clear_sized_buffer(&tsb[type]);
+ }
+ }
+}
/**
Index: qemu-git/configure
===================================================================
--- qemu-git.orig/configure
+++ qemu-git/configure
@@ -3340,6 +3340,9 @@ if test "$linux" = "yes" && test "$tpm"
fi
if test "$has_tpm" = "1"; then
+ if test -r /usr/include/libtpms/tpm_library.h ; then
+ echo "CONFIG_TPM_BUILTIN=y" >> $config_target_mak
+ fi
echo "CONFIG_TPM=y" >> $config_host_mak
fi
fi
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process Stefan Berger
@ 2011-04-01 18:14 ` Blue Swirl
2011-04-01 19:57 ` Stefan Berger
0 siblings, 1 reply; 18+ messages in thread
From: Blue Swirl @ 2011-04-01 18:14 UTC (permalink / raw)
To: Stefan Berger; +Cc: qemu-devel, andreas.niederl
On Wed, Mar 30, 2011 at 10:42 PM, Stefan Berger
<stefanb@linux.vnet.ibm.com> wrote:
> The TPM interface (tpm_tis) needs to be explicitly enabled via
> ./configure --enable-tpm. This restricts the building of the
> TPM support to i386 and x86_64 targets since both backends I know
> of, the Xen backend and the libtpms-based backend, will likely only
> be available for these targets, at least initially. The list can be
> easily extend. This measure prevents that one will end up with support
> for a frontend but no available backend.
>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
>
> Index:qemu/Makefile.target
> ===================================================================
> ---
> Makefile.target | 7 +++++++
> configure | 20 ++++++++++++++++++++
> 2 files changed, 27 insertions(+)
>
> Index: qemu-git/Makefile.target
> ===================================================================
> --- qemu-git.orig/Makefile.target
> +++ qemu-git/Makefile.target
> @@ -303,6 +303,13 @@ obj-sparc-y += cs4231.o eccmemctl.o sbi.
>
> # GRLIB
> obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
> +
> +ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),i386 x86_64))
The ifeq above is not needed since the line below uses obj-i386.
> +
> +obj-i386-$(CONFIG_TPM) += tpm_tis.o
> +
> +endif
> +
> endif
>
> obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
> Index: qemu-git/configure
> ===================================================================
> --- qemu-git.orig/configure
> +++ qemu-git/configure
> @@ -175,6 +175,7 @@ trace_backend="nop"
> trace_file="trace"
> spice=""
> rbd=""
> +tpm="no"
>
> # parse CC options first
> for opt do
> @@ -708,6 +709,8 @@ for opt do
> ;;
> --kerneldir=*) kerneldir="$optarg"
> ;;
> + --enable-tpm) tpm="yes"
> + ;;
> --with-pkgversion=*) pkgversion=" ($optarg)"
> ;;
> --disable-docs) docs="no"
> @@ -921,6 +924,7 @@ echo " Default
> echo " --disable-spice disable spice"
> echo " --enable-spice enable spice"
> echo " --enable-rbd enable building the rados block device (rbd)"
> +echo " --enable-tpm enables an emulated TPM"
> echo ""
> echo "NOTE: The object files are built at the place where configure is launched"
> exit 1
> @@ -2540,6 +2544,7 @@ echo "Trace output file $trace_file-<pid
> echo "spice support $spice"
> echo "rbd support $rbd"
> echo "xfsctl support $xfs"
> +echo "TPM support $tpm"
>
> if test $sdl_too_old = "yes"; then
> echo "-> Your SDL version is too old - please upgrade to have SDL support"
> @@ -3324,6 +3329,21 @@ if test "$gprof" = "yes" ; then
> fi
> fi
>
> +if test "$linux" = "yes" && test "$tpm" = "yes"; then
> + has_tpm=0
> + if test "$target_softmmu" = "yes" ; then
> + case "$TARGET_BASE_ARCH" in
> + i386)
> + has_tpm=1
This is not a real compile test, please try to build a dummy program
that depends on TPM headers like for example preadv probe. Then it is
not needed to check for Linux or i386 explicitly.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process
2011-04-01 18:14 ` Blue Swirl
@ 2011-04-01 19:57 ` Stefan Berger
2011-04-03 9:20 ` Blue Swirl
0 siblings, 1 reply; 18+ messages in thread
From: Stefan Berger @ 2011-04-01 19:57 UTC (permalink / raw)
To: Blue Swirl; +Cc: qemu-devel, andreas.niederl
On 04/01/2011 02:14 PM, Blue Swirl wrote:
> On Wed, Mar 30, 2011 at 10:42 PM, Stefan Berger
> <stefanb@linux.vnet.ibm.com> wrote:
>> The TPM interface (tpm_tis) needs to be explicitly enabled via
>> ./configure --enable-tpm. This restricts the building of the
>> TPM support to i386 and x86_64 targets since both backends I know
>> of, the Xen backend and the libtpms-based backend, will likely only
>> be available for these targets, at least initially. The list can be
>> easily extend. This measure prevents that one will end up with support
>> for a frontend but no available backend.
>>
>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>>
>> Index:qemu/Makefile.target
>> ===================================================================
>> ---
>> Makefile.target | 7 +++++++
>> configure | 20 ++++++++++++++++++++
>> 2 files changed, 27 insertions(+)
>>
>> Index: qemu-git/Makefile.target
>> ===================================================================
>> --- qemu-git.orig/Makefile.target
>> +++ qemu-git/Makefile.target
>> @@ -303,6 +303,13 @@ obj-sparc-y += cs4231.o eccmemctl.o sbi.
>>
>> # GRLIB
>> obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
>> +
>> +ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),i386 x86_64))
> The ifeq above is not needed since the line below uses obj-i386.
>
Will remove.
>> +
>> +obj-i386-$(CONFIG_TPM) += tpm_tis.o
>> +
>> +endif
>> +
>> endif
>>
>> obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>> Index: qemu-git/configure
>> ===================================================================
>> --- qemu-git.orig/configure
>> +++ qemu-git/configure
>> @@ -175,6 +175,7 @@ trace_backend="nop"
>> trace_file="trace"
>> spice=""
>> rbd=""
>> +tpm="no"
>>
>> # parse CC options first
>> for opt do
>> @@ -708,6 +709,8 @@ for opt do
>> ;;
>> --kerneldir=*) kerneldir="$optarg"
>> ;;
>> + --enable-tpm) tpm="yes"
>> + ;;
>> --with-pkgversion=*) pkgversion=" ($optarg)"
>> ;;
>> --disable-docs) docs="no"
>> @@ -921,6 +924,7 @@ echo " Default
>> echo " --disable-spice disable spice"
>> echo " --enable-spice enable spice"
>> echo " --enable-rbd enable building the rados block device (rbd)"
>> +echo " --enable-tpm enables an emulated TPM"
>> echo ""
>> echo "NOTE: The object files are built at the place where configure is launched"
>> exit 1
>> @@ -2540,6 +2544,7 @@ echo "Trace output file $trace_file-<pid
>> echo "spice support $spice"
>> echo "rbd support $rbd"
>> echo "xfsctl support $xfs"
>> +echo "TPM support $tpm"
>>
>> if test $sdl_too_old = "yes"; then
>> echo "-> Your SDL version is too old - please upgrade to have SDL support"
>> @@ -3324,6 +3329,21 @@ if test "$gprof" = "yes" ; then
>> fi
>> fi
>>
>> +if test "$linux" = "yes"&& test "$tpm" = "yes"; then
>> + has_tpm=0
>> + if test "$target_softmmu" = "yes" ; then
>> + case "$TARGET_BASE_ARCH" in
>> + i386)
>> + has_tpm=1
> This is not a real compile test, please try to build a dummy program
> that depends on TPM headers like for example preadv probe. Then it is
> not needed to check for Linux or i386 explicitly.
At this point there is no compile test needed since all code is 'there'.
It's merely adding the front-end,i.e., the TPM TIS emulation to be
compiled. The (libtpms-based) backend is then added later in patch
[9/9]. There you then find this here:
if test "$has_tpm" = "1"; then
+ if test -r /usr/include/libtpms/tpm_library.h ; then
+ echo "CONFIG_TPM_BUILTIN=y">> $config_target_mak
+ fi
echo "CONFIG_TPM=y">> $config_host_mak
fi
fi
So this is then trying to test for the libtpms-devel package, and if
found, adds the tpm_builtin.c to the build.
Now did it look wrong for patch 4 and it does make sense in combination
with patch 9?
Stefan
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process
2011-04-01 19:57 ` Stefan Berger
@ 2011-04-03 9:20 ` Blue Swirl
2011-04-05 2:08 ` Stefan Berger
0 siblings, 1 reply; 18+ messages in thread
From: Blue Swirl @ 2011-04-03 9:20 UTC (permalink / raw)
To: Stefan Berger; +Cc: qemu-devel, andreas.niederl
On Fri, Apr 1, 2011 at 10:57 PM, Stefan Berger
<stefanb@linux.vnet.ibm.com> wrote:
> On 04/01/2011 02:14 PM, Blue Swirl wrote:
>>
>> On Wed, Mar 30, 2011 at 10:42 PM, Stefan Berger
>> <stefanb@linux.vnet.ibm.com> wrote:
>>>
>>> The TPM interface (tpm_tis) needs to be explicitly enabled via
>>> ./configure --enable-tpm. This restricts the building of the
>>> TPM support to i386 and x86_64 targets since both backends I know
>>> of, the Xen backend and the libtpms-based backend, will likely only
>>> be available for these targets, at least initially. The list can be
>>> easily extend. This measure prevents that one will end up with support
>>> for a frontend but no available backend.
>>>
>>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>>>
>>> Index:qemu/Makefile.target
>>> ===================================================================
>>> ---
>>> Makefile.target | 7 +++++++
>>> configure | 20 ++++++++++++++++++++
>>> 2 files changed, 27 insertions(+)
>>>
>>> Index: qemu-git/Makefile.target
>>> ===================================================================
>>> --- qemu-git.orig/Makefile.target
>>> +++ qemu-git/Makefile.target
>>> @@ -303,6 +303,13 @@ obj-sparc-y += cs4231.o eccmemctl.o sbi.
>>>
>>> # GRLIB
>>> obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
>>> +
>>> +ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),i386 x86_64))
>>
>> The ifeq above is not needed since the line below uses obj-i386.
>>
> Will remove.
>>>
>>> +
>>> +obj-i386-$(CONFIG_TPM) += tpm_tis.o
>>> +
>>> +endif
>>> +
>>> endif
>>>
>>> obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>>> Index: qemu-git/configure
>>> ===================================================================
>>> --- qemu-git.orig/configure
>>> +++ qemu-git/configure
>>> @@ -175,6 +175,7 @@ trace_backend="nop"
>>> trace_file="trace"
>>> spice=""
>>> rbd=""
>>> +tpm="no"
>>>
>>> # parse CC options first
>>> for opt do
>>> @@ -708,6 +709,8 @@ for opt do
>>> ;;
>>> --kerneldir=*) kerneldir="$optarg"
>>> ;;
>>> + --enable-tpm) tpm="yes"
>>> + ;;
>>> --with-pkgversion=*) pkgversion=" ($optarg)"
>>> ;;
>>> --disable-docs) docs="no"
>>> @@ -921,6 +924,7 @@ echo " Default
>>> echo " --disable-spice disable spice"
>>> echo " --enable-spice enable spice"
>>> echo " --enable-rbd enable building the rados block device
>>> (rbd)"
>>> +echo " --enable-tpm enables an emulated TPM"
>>> echo ""
>>> echo "NOTE: The object files are built at the place where configure is
>>> launched"
>>> exit 1
>>> @@ -2540,6 +2544,7 @@ echo "Trace output file $trace_file-<pid
>>> echo "spice support $spice"
>>> echo "rbd support $rbd"
>>> echo "xfsctl support $xfs"
>>> +echo "TPM support $tpm"
>>>
>>> if test $sdl_too_old = "yes"; then
>>> echo "-> Your SDL version is too old - please upgrade to have SDL
>>> support"
>>> @@ -3324,6 +3329,21 @@ if test "$gprof" = "yes" ; then
>>> fi
>>> fi
>>>
>>> +if test "$linux" = "yes"&& test "$tpm" = "yes"; then
>>> + has_tpm=0
>>> + if test "$target_softmmu" = "yes" ; then
>>> + case "$TARGET_BASE_ARCH" in
>>> + i386)
>>> + has_tpm=1
>>
>> This is not a real compile test, please try to build a dummy program
>> that depends on TPM headers like for example preadv probe. Then it is
>> not needed to check for Linux or i386 explicitly.
>
> At this point there is no compile test needed since all code is 'there'.
> It's merely adding the front-end,i.e., the TPM TIS emulation to be compiled.
If the basic device (without the tpms-devel library) can be built on
any OS, the flag should go to default-configs/*86*-softmmu.mak.
> The (libtpms-based) backend is then added later in patch [9/9]. There you
> then find this here:
>
> if test "$has_tpm" = "1"; then
> + if test -r /usr/include/libtpms/tpm_library.h ; then
Here you make assumptions on the header file location, but it could be
in /usr/local, /opt or somewhere where the cross compiler happens to
find it. Please just do the compile test.
> + echo "CONFIG_TPM_BUILTIN=y">> $config_target_mak
> + fi
> echo "CONFIG_TPM=y">> $config_host_mak
> fi
> fi
>
>
> So this is then trying to test for the libtpms-devel package, and if found,
> adds the tpm_builtin.c to the build.
> Now did it look wrong for patch 4 and it does make sense in combination with
> patch 9?
The test should be added when the code which uses the library is added.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process
2011-04-03 9:20 ` Blue Swirl
@ 2011-04-05 2:08 ` Stefan Berger
2011-04-05 17:45 ` Blue Swirl
0 siblings, 1 reply; 18+ messages in thread
From: Stefan Berger @ 2011-04-05 2:08 UTC (permalink / raw)
To: Blue Swirl; +Cc: qemu-devel
On 04/03/2011 05:20 AM, Blue Swirl wrote:
> On Fri, Apr 1, 2011 at 10:57 PM, Stefan Berger
> <stefanb@linux.vnet.ibm.com> wrote:
>> On 04/01/2011 02:14 PM, Blue Swirl wrote:
>>
>> At this point there is no compile test needed since all code is 'there'.
>> It's merely adding the front-end,i.e., the TPM TIS emulation to be compiled.
> If the basic device (without the tpms-devel library) can be built on
> any OS, the flag should go to default-configs/*86*-softmmu.mak.
>
It can be built on any OS, but it is of no use since the backend
(libtpms) is only available on Linux and we don't support it on another
OS. Unless someone else wants to port it to other OSes, I'd say that the
test for Linux is useful.
I'd actually also only compile the TIS if libtpms could be found, and
terminate with an error message otherwise. I would add this restriction
only in the last patch, so that in patch 4 at least for now the TIS can
be built. Does that sound reasonable?
>> The (libtpms-based) backend is then added later in patch [9/9]. There you
>> then find this here:
>>
>> if test "$has_tpm" = "1"; then
>> + if test -r /usr/include/libtpms/tpm_library.h ; then
> Here you make assumptions on the header file location, but it could be
> in /usr/local, /opt or somewhere where the cross compiler happens to
> find it. Please just do the compile test.
>
Fixed that.
Stefan
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process
2011-04-05 2:08 ` Stefan Berger
@ 2011-04-05 17:45 ` Blue Swirl
2011-04-05 18:33 ` Stefan Berger
0 siblings, 1 reply; 18+ messages in thread
From: Blue Swirl @ 2011-04-05 17:45 UTC (permalink / raw)
To: Stefan Berger; +Cc: qemu-devel
On Tue, Apr 5, 2011 at 5:08 AM, Stefan Berger
<stefanb@linux.vnet.ibm.com> wrote:
> On 04/03/2011 05:20 AM, Blue Swirl wrote:
>>
>> On Fri, Apr 1, 2011 at 10:57 PM, Stefan Berger
>> <stefanb@linux.vnet.ibm.com> wrote:
>>>
>>> On 04/01/2011 02:14 PM, Blue Swirl wrote:
>>>
>>> At this point there is no compile test needed since all code is 'there'.
>>> It's merely adding the front-end,i.e., the TPM TIS emulation to be
>>> compiled.
>>
>> If the basic device (without the tpms-devel library) can be built on
>> any OS, the flag should go to default-configs/*86*-softmmu.mak.
>>
> It can be built on any OS, but it is of no use since the backend (libtpms)
> is only available on Linux and we don't support it on another OS. Unless
> someone else wants to port it to other OSes, I'd say that the test for Linux
> is useful.
> I'd actually also only compile the TIS if libtpms could be found, and
> terminate with an error message otherwise. I would add this restriction only
> in the last patch, so that in patch 4 at least for now the TIS can be built.
> Does that sound reasonable?
It should be possible to emulate the device (to some degree) without
relying on backend. See for example the recently committed smart card
device.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process
2011-04-05 17:45 ` Blue Swirl
@ 2011-04-05 18:33 ` Stefan Berger
2011-04-05 18:55 ` Blue Swirl
0 siblings, 1 reply; 18+ messages in thread
From: Stefan Berger @ 2011-04-05 18:33 UTC (permalink / raw)
To: qemu-devel, Blue Swirl
On 04/05/2011 01:45 PM, Blue Swirl wrote:
> On Tue, Apr 5, 2011 at 5:08 AM, Stefan Berger
> <stefanb@linux.vnet.ibm.com> wrote:
>> On 04/03/2011 05:20 AM, Blue Swirl wrote:
>>> On Fri, Apr 1, 2011 at 10:57 PM, Stefan Berger
>>> <stefanb@linux.vnet.ibm.com> wrote:
>>>> On 04/01/2011 02:14 PM, Blue Swirl wrote:
>>>>
>>>> At this point there is no compile test needed since all code is 'there'.
>>>> It's merely adding the front-end,i.e., the TPM TIS emulation to be
>>>> compiled.
>>> If the basic device (without the tpms-devel library) can be built on
>>> any OS, the flag should go to default-configs/*86*-softmmu.mak.
>>>
>> It can be built on any OS, but it is of no use since the backend (libtpms)
>> is only available on Linux and we don't support it on another OS. Unless
>> someone else wants to port it to other OSes, I'd say that the test for Linux
>> is useful.
>> I'd actually also only compile the TIS if libtpms could be found, and
>> terminate with an error message otherwise. I would add this restriction only
>> in the last patch, so that in patch 4 at least for now the TIS can be built.
>> Does that sound reasonable?
> It should be possible to emulate the device (to some degree) without
> relying on backend. See for example the recently committed smart card
> device.
>
In case of a TPM, the specs are huge and translate into multiple 10k
lines of code. If there was to be a dummy backend, all it could send
back would be error messages...
Stefan
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process
2011-04-05 18:33 ` Stefan Berger
@ 2011-04-05 18:55 ` Blue Swirl
2011-04-06 0:12 ` Stefan Berger
0 siblings, 1 reply; 18+ messages in thread
From: Blue Swirl @ 2011-04-05 18:55 UTC (permalink / raw)
To: Stefan Berger; +Cc: qemu-devel
On Tue, Apr 5, 2011 at 9:33 PM, Stefan Berger
<stefanb@linux.vnet.ibm.com> wrote:
> On 04/05/2011 01:45 PM, Blue Swirl wrote:
>>
>> On Tue, Apr 5, 2011 at 5:08 AM, Stefan Berger
>> <stefanb@linux.vnet.ibm.com> wrote:
>>>
>>> On 04/03/2011 05:20 AM, Blue Swirl wrote:
>>>>
>>>> On Fri, Apr 1, 2011 at 10:57 PM, Stefan Berger
>>>> <stefanb@linux.vnet.ibm.com> wrote:
>>>>>
>>>>> On 04/01/2011 02:14 PM, Blue Swirl wrote:
>>>>>
>>>>> At this point there is no compile test needed since all code is
>>>>> 'there'.
>>>>> It's merely adding the front-end,i.e., the TPM TIS emulation to be
>>>>> compiled.
>>>>
>>>> If the basic device (without the tpms-devel library) can be built on
>>>> any OS, the flag should go to default-configs/*86*-softmmu.mak.
>>>>
>>> It can be built on any OS, but it is of no use since the backend
>>> (libtpms)
>>> is only available on Linux and we don't support it on another OS. Unless
>>> someone else wants to port it to other OSes, I'd say that the test for
>>> Linux
>>> is useful.
>>> I'd actually also only compile the TIS if libtpms could be found, and
>>> terminate with an error message otherwise. I would add this restriction
>>> only
>>> in the last patch, so that in patch 4 at least for now the TIS can be
>>> built.
>>> Does that sound reasonable?
>>
>> It should be possible to emulate the device (to some degree) without
>> relying on backend. See for example the recently committed smart card
>> device.
>>
> In case of a TPM, the specs are huge and translate into multiple 10k lines
> of code. If there was to be a dummy backend, all it could send back would be
> error messages...
Then how about emulating the library instead so that all calls return failure?
If a device is built only in special circumstances, it will be more
prone to bit rot. We have a few such devices though, so it's not so
big deal.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process
2011-04-05 18:55 ` Blue Swirl
@ 2011-04-06 0:12 ` Stefan Berger
0 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2011-04-06 0:12 UTC (permalink / raw)
To: Blue Swirl; +Cc: qemu-devel
On 04/05/2011 02:55 PM, Blue Swirl wrote:
> On Tue, Apr 5, 2011 at 9:33 PM, Stefan Berger
> <stefanb@linux.vnet.ibm.com> wrote:
>> On 04/05/2011 01:45 PM, Blue Swirl wrote:
>>> On Tue, Apr 5, 2011 at 5:08 AM, Stefan Berger
>>> <stefanb@linux.vnet.ibm.com> wrote:
>>>> On 04/03/2011 05:20 AM, Blue Swirl wrote:
>>>>> On Fri, Apr 1, 2011 at 10:57 PM, Stefan Berger
>>>>> <stefanb@linux.vnet.ibm.com> wrote:
>>>>>> On 04/01/2011 02:14 PM, Blue Swirl wrote:
>>>>>>
>>>>>> At this point there is no compile test needed since all code is
>>>>>> 'there'.
>>>>>> It's merely adding the front-end,i.e., the TPM TIS emulation to be
>>>>>> compiled.
>>>>> If the basic device (without the tpms-devel library) can be built on
>>>>> any OS, the flag should go to default-configs/*86*-softmmu.mak.
>>>>>
>>>> It can be built on any OS, but it is of no use since the backend
>>>> (libtpms)
>>>> is only available on Linux and we don't support it on another OS. Unless
>>>> someone else wants to port it to other OSes, I'd say that the test for
>>>> Linux
>>>> is useful.
>>>> I'd actually also only compile the TIS if libtpms could be found, and
>>>> terminate with an error message otherwise. I would add this restriction
>>>> only
>>>> in the last patch, so that in patch 4 at least for now the TIS can be
>>>> built.
>>>> Does that sound reasonable?
>>> It should be possible to emulate the device (to some degree) without
>>> relying on backend. See for example the recently committed smart card
>>> device.
>>>
>> In case of a TPM, the specs are huge and translate into multiple 10k lines
>> of code. If there was to be a dummy backend, all it could send back would be
>> error messages...
> Then how about emulating the library instead so that all calls return failure?
That device would be of no use for a user and only serve the purpose of
test-compiling it if it was for detecting bit rot.
> If a device is built only in special circumstances, it will be more
> prone to bit rot. We have a few such devices though, so it's not so
> big deal.
>
I'll be following the project and there is interest to keep this device
working.
Stefan
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2011-04-06 0:13 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-30 19:42 [Qemu-devel] [PATCH V2 0/9] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 1/9] Support for TPM command line options Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 2/9] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 3/9] Add persistent state handling to TPM TIS frontend driver Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 4/9] Add tpm_tis driver to build process Stefan Berger
2011-04-01 18:14 ` Blue Swirl
2011-04-01 19:57 ` Stefan Berger
2011-04-03 9:20 ` Blue Swirl
2011-04-05 2:08 ` Stefan Berger
2011-04-05 17:45 ` Blue Swirl
2011-04-05 18:33 ` Stefan Berger
2011-04-05 18:55 ` Blue Swirl
2011-04-06 0:12 ` Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 5/9] Add a debug register Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 6/9] Implement qemu_thread_join function Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 7/9] Add a TPM backend skeleton implementation Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 8/9] Implementation of the libtpms-based backend Stefan Berger
2011-03-30 19:42 ` [Qemu-devel] [PATCH V2 9/9] Add block storage support for libtpms based TPM backend Stefan Berger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).