* [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options
2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
2011-12-12 23:16 ` Anthony Liguori
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
` (6 subsequent siblings)
7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 UTC (permalink / raw)
To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst
This patch adds support for TPM command line options.
The command line options supported here are
./qemu-... -tpmdev passthrough,path=<path to TPM device>,id=<id>
-device tpm-tis,tpmdev=<id>
and
./qemu-... -tpmdev ?
where the latter works similar to -soundhw ? and shows a list of
available TPM backends (for example 'passthrough').
Using the type parameter, the backend is chosen, i.e., 'passthrough' for the
passthrough driver. 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
'create' and return a TPMDriver structure if the VM can be started or 'NULL'
if not enough or bad parameters were provided.
Monitor support for 'info tpm' has been added. It for example prints the
following:
(qemu) info tpm
TPM devices:
tpm0: model=tpm-tis
\ tpm0: type=passthrough,path=/dev/tpm0
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
v12:
- use all 4 bytes of the message length indicator
v10:
- tpm_display_backend_drivers always prints to stderr
v9:
- prefixing all functions with tpm_tis_ and all constants with TPM_TIS_
- splitting off of -tpm command line support into its own patch
- only support TPM passthrough for now
v8:
- adjusting formatting of backend drivers output to accomodate better
formatting of 'passthrough' backend output
v6:
- use #idef CONFIG_TPM to surround TPM calls
- use QLIST_FOREACH_SAFE rather than QLIST_FOREACH in tpm_cleanup
- commented backend ops in tpm.h
- moving to IRQ 5 (11 collided with network cards)
v5:
- fixing typo reported by Serge Hallyn
- Adapting code to split command line parameters supporting
-tpmdev ... -device tpm-tis,tpmdev=...
- moved code out of arch_init.c|h into tpm.c|h
- increasing reserved memory for ACPI tables to 128kb (from 64kb)
- the backend interface has a create() function for interpreting the command
line parameters and returning a TPMDevice structure; previoulsy
this function was called handle_options()
- the backend interface has a destroy() function for cleaning up after
the create() function was called
- added support for 'info tpm' in monitor
v4:
- coding style fixes
v3:
- added hw/tpm_tis.h to this patch so Qemu compiles at this stage
---
hmp-commands.hx | 2 +
hw/tpm_tis.h | 91 ++++++++++++++++++++++++++++++
monitor.c | 10 +++
qemu-config.c | 20 +++++++
qemu-options.hx | 34 +++++++++++
tpm.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
tpm.h | 90 +++++++++++++++++++++++++++++
vl.c | 15 +++++
8 files changed, 429 insertions(+), 0 deletions(-)
create mode 100644 hw/tpm_tis.h
create mode 100644 tpm.c
create mode 100644 tpm.h
diff --git a/hmp-commands.hx b/hmp-commands.hx
index fdbed15..4506f56 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1352,6 +1352,8 @@ show device tree
show qdev device model list
@item info roms
show roms
+@item info tpm
+show the TPM device
@end table
ETEXI
diff --git a/hw/tpm_tis.h b/hw/tpm_tis.h
new file mode 100644
index 0000000..e6a9a4b
--- /dev/null
+++ b/hw/tpm_tis.h
@@ -0,0 +1,91 @@
+/*
+ * tpm_tis.c - QEMU's TPM TIS interface emulator
+ *
+ * Copyright (C) 2006,2010,2011 IBM Corporation
+ *
+ * Authors:
+ * 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 found at
+ * http://www.trustedcomputiggroup.org
+ *
+ */
+#ifndef _HW_TPM_TIS_H
+#define _HW_TPM_TIS_H
+
+#include "isa.h"
+#include "qemu-thread.h"
+
+#include <stdint.h>
+
+#define TPM_TIS_ADDR_BASE 0xFED40000
+
+#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */
+#define TPM_TIS_NO_LOCALITY 0xff
+
+#define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES)
+
+#define TPM_TIS_IRQ 5
+
+#define TPM_TIS_BUFFER_MAX 4096
+
+
+typedef struct TPMSizedBuffer {
+ uint32_t size;
+ uint8_t *buffer;
+} TPMSizedBuffer;
+
+enum tpm_tis_state {
+ TPM_TIS_STATE_IDLE = 0,
+ TPM_TIS_STATE_READY,
+ TPM_TIS_STATE_COMPLETION,
+ TPM_TIS_STATE_EXECUTION,
+ TPM_TIS_STATE_RECEPTION,
+};
+
+/* locality data -- all fields are persisted */
+typedef struct TPMLocality {
+ enum tpm_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;
+
+typedef struct TPMTISState {
+ uint32_t offset;
+ uint8_t buf[TPM_TIS_BUFFER_MAX];
+
+ uint8_t active_locty;
+ uint8_t aborting_locty;
+ uint8_t next_locty;
+
+ TPMLocality loc[TPM_TIS_NUM_LOCALITIES];
+
+ qemu_irq irq;
+ uint32_t irq_num;
+} TPMTISState;
+
+/* utility functions */
+
+static inline uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+ return (sb->buffer[2] << 24) |
+ (sb->buffer[3] << 16) |
+ (sb->buffer[4] << 8) |
+ sb->buffer[5];
+}
+
+#endif /* _HW_TPM_TIS_H */
diff --git a/monitor.c b/monitor.c
index 7334401..28873c3 100644
--- a/monitor.c
+++ b/monitor.c
@@ -47,6 +47,7 @@
#include "migration.h"
#include "kvm.h"
#include "acl.h"
+#include "tpm.h"
#include "qint.h"
#include "qfloat.h"
#include "qlist.h"
@@ -2735,6 +2736,15 @@ static mon_cmd_t info_cmds[] = {
.help = "show available trace-events & their state",
.mhandler.info = do_trace_print_events,
},
+#if defined(CONFIG_TPM)
+ {
+ .name = "tpm",
+ .args_type = "",
+ .params = "",
+ .help = "show the TPM device",
+ .mhandler.info = do_info_tpm,
+ },
+#endif
{
.name = NULL,
},
diff --git a/qemu-config.c b/qemu-config.c
index 18f3020..cc4c31d 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -549,6 +549,25 @@ QemuOptsList qemu_boot_opts = {
},
};
+static QemuOptsList qemu_tpmdev_opts = {
+ .name = "tpmdev",
+ .implied_opt_name = "type",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_tpmdev_opts.head),
+ .desc = {
+ {
+ .name = "type",
+ .type = QEMU_OPT_STRING,
+ .help = "Type of TPM backend",
+ },
+ {
+ .name = "path",
+ .type = QEMU_OPT_STRING,
+ .help = "Persistent storage for TPM state",
+ },
+ { /* end of list */ }
+ },
+};
+
static QemuOptsList *vm_config_groups[32] = {
&qemu_drive_opts,
&qemu_chardev_opts,
@@ -563,6 +582,7 @@ static QemuOptsList *vm_config_groups[32] = {
&qemu_option_rom_opts,
&qemu_machine_opts,
&qemu_boot_opts,
+ &qemu_tpmdev_opts,
NULL,
};
diff --git a/qemu-options.hx b/qemu-options.hx
index b3db10c..58a5dfa 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1903,6 +1903,40 @@ ETEXI
DEFHEADING()
+DEFHEADING(TPM device options:)
+
+#ifndef _WIN32
+# ifdef CONFIG_TPM
+DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
+ "-tpmdev [<type>],id=str[,option][,option][,...]\n",
+ QEMU_ARCH_ALL)
+# endif
+#endif
+STEXI
+
+The general form of a TPM device option is:
+@table @option
+
+@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
+@findex -tpmdev
+Backend type must be:
+
+The specific backend type will determine the applicable options.
+The @code{-tpmdev} options requires a @code{-device} option.
+
+Options to each backend are described below.
+
+Use ? to print all available TPM backend types.
+@example
+qemu -tpmdev ?
+@end example
+
+@end table
+
+ETEXI
+
+DEFHEADING()
+
DEFHEADING(Linux/Multiboot boot specific:)
STEXI
diff --git a/tpm.c b/tpm.c
new file mode 100644
index 0000000..fcab023
--- /dev/null
+++ b/tpm.c
@@ -0,0 +1,167 @@
+/*
+ * TPM configuration
+ *
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Authors:
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Based on net.c
+ */
+#include "config.h"
+
+#include "tpm.h"
+#include "monitor.h"
+#include "qerror.h"
+
+static const TPMDriverOps *bes[] = {
+ NULL,
+};
+
+static QLIST_HEAD(, TPMBackend) tpm_backends =
+ QLIST_HEAD_INITIALIZER(tpm_backends);
+
+const TPMDriverOps *tpm_get_backend_driver(const char *id)
+{
+ int i;
+
+ for (i = 0; bes[i] != NULL; i++) {
+ if (!strcmp(bes[i]->id, id)) {
+ break;
+ }
+ }
+
+ return bes[i];
+}
+
+void tpm_display_backend_drivers(void)
+{
+ int i;
+
+ fprintf(stderr, "Supported TPM types (choose only one):\n");
+
+ for (i = 0; bes[i] != NULL; i++) {
+ fprintf(stderr, "%12s %s\n", bes[i]->id, bes[i]->desc());
+ }
+ fprintf(stderr, "\n");
+}
+
+TPMBackend *qemu_find_tpm(const char *id)
+{
+ TPMBackend *drv;
+
+ QLIST_FOREACH(drv, &tpm_backends, list) {
+ if (!strcmp(drv->id, id)) {
+ return drv;
+ }
+ }
+
+ return NULL;
+}
+
+void do_info_tpm(Monitor *mon)
+{
+ TPMBackend *drv;
+ unsigned int c = 0;
+
+ monitor_printf(mon, "TPM device:\n");
+
+ QLIST_FOREACH(drv, &tpm_backends, list) {
+ monitor_printf(mon, " tpm%d: model=%s\n",
+ c, drv->fe_model);
+ monitor_printf(mon, " \\ %s: type=%s%s%s\n",
+ drv->id, drv->ops->id,
+ drv->parameters ? "," : "",
+ drv->parameters ? drv->parameters : "");
+ c++;
+ }
+}
+
+static int configure_tpm(QemuOpts *opts)
+{
+ const char *value;
+ const char *id;
+ const TPMDriverOps *be;
+ TPMBackend *drv;
+
+ if (!QLIST_EMPTY(&tpm_backends)) {
+ error_report("Only one TPM is allowed.\n");
+ return 1;
+ }
+
+ id = qemu_opts_id(opts);
+ if (id == NULL) {
+ qerror_report(QERR_MISSING_PARAMETER, "id");
+ return 1;
+ }
+
+ value = qemu_opt_get(opts, "type");
+ if (!value) {
+ qerror_report(QERR_MISSING_PARAMETER, "type");
+ tpm_display_backend_drivers();
+ return 1;
+ }
+
+ be = tpm_get_backend_driver(value);
+ if (be == NULL) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
+ "a tpm backend type");
+ tpm_display_backend_drivers();
+ return 1;
+ }
+
+ drv = be->create(opts, id);
+ if (!drv) {
+ return 1;
+ }
+
+ QLIST_INSERT_HEAD(&tpm_backends, drv, list);
+
+ return 0;
+}
+
+static int tpm_init_tpmdev(QemuOpts *opts, void *dummy)
+{
+ return configure_tpm(opts);
+}
+
+void tpm_cleanup(void)
+{
+ TPMBackend *drv, *next;
+
+ QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
+ QLIST_REMOVE(drv, list);
+ drv->ops->destroy(drv);
+ }
+}
+
+int tpm_init(void)
+{
+ if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
+ tpm_init_tpmdev, NULL, 1) != 0) {
+ return -1;
+ }
+
+ atexit(tpm_cleanup);
+
+ return 0;
+}
+
+void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
+{
+ QemuOpts *opts;
+
+ if (strcmp("none", optarg) != 0) {
+ if (*optarg == '?') {
+ tpm_display_backend_drivers();
+ exit(0);
+ }
+ opts = qemu_opts_parse(opts_list, optarg, 1);
+ if (!opts) {
+ exit(1);
+ }
+ }
+}
diff --git a/tpm.h b/tpm.h
new file mode 100644
index 0000000..85c2a35
--- /dev/null
+++ b/tpm.h
@@ -0,0 +1,90 @@
+#ifndef _QEMU_TPM_H
+#define _QEMU_TPM_H
+
+#include "memory.h"
+#include "hw/tpm_tis.h"
+
+struct TPMDriverOps;
+typedef struct TPMDriverOps TPMDriverOps;
+
+typedef struct TPMBackend {
+ char *id;
+ const char *fe_model;
+ char *parameters;
+ const TPMDriverOps *ops;
+
+ QLIST_ENTRY(TPMBackend) list;
+} TPMBackend;
+
+/* overall state of the TPM interface */
+typedef struct TPMState {
+ ISADevice busdev;
+ MemoryRegion mmio;
+
+ union {
+ TPMTISState tis;
+ } s;
+
+ uint8_t command_locty;
+ TPMLocality *cmd_locty;
+
+ QemuMutex state_lock;
+ QemuCond from_tpm_cond;
+ QemuCond to_tpm_cond;
+ bool to_tpm_execute;
+
+ bool tpm_initialized;
+
+ char *backend;
+ TPMBackend *be_driver;
+} TPMState;
+
+typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty);
+
+struct TPMDriverOps {
+ const char *id;
+ /* get a descriptive text of the backend to display to the user */
+ const char *(*desc)(void);
+
+ TPMBackend *(*create)(QemuOpts *opts, const char *id);
+ void (*destroy)(TPMBackend *t);
+
+ /* initialize the backend */
+ int (*init)(TPMBackend *t, TPMState *s, TPMRecvDataCB *datacb);
+ /* start up the TPM on the backend */
+ int (*startup_tpm)(TPMBackend *t);
+ /* returns true if nothing will ever answer TPM requests */
+ bool (*had_startup_error)(TPMBackend *t);
+
+ size_t (*realloc_buffer)(TPMSizedBuffer *sb);
+
+ void (*reset)(TPMBackend *t);
+
+ bool (*get_tpm_established_flag)(TPMBackend *t);
+};
+
+static inline void tpm_dump_buffer(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");
+}
+
+#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
+
+void tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
+int tpm_init(void);
+void tpm_cleanup(void);
+TPMBackend *qemu_find_tpm(const char *id);
+void do_info_tpm(Monitor *mon);
+void tpm_display_backend_drivers(void);
+const TPMDriverOps *tpm_get_backend_driver(const char *id);
+
+#endif /* _QEMU_TPM_H */
diff --git a/vl.c b/vl.c
index 5372a96..12915d0 100644
--- a/vl.c
+++ b/vl.c
@@ -139,6 +139,7 @@ int main(int argc, char **argv)
#include "block.h"
#include "blockdev.h"
#include "block-migration.h"
+#include "tpm.h"
#include "dma.h"
#include "audio/audio.h"
#include "migration.h"
@@ -2550,6 +2551,11 @@ int main(int argc, char **argv, char **envp)
ram_size = value;
break;
}
+#ifdef CONFIG_TPM
+ case QEMU_OPTION_tpmdev:
+ tpm_config_parse(qemu_find_opts("tpmdev"), optarg);
+ break;
+#endif
case QEMU_OPTION_mempath:
mem_path = optarg;
break;
@@ -3243,6 +3249,12 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
+#ifdef CONFIG_TPM
+ if (tpm_init() < 0) {
+ exit(1);
+ }
+#endif
+
/* init the bluetooth world */
if (foreach_device_config(DEV_BT, bt_parse))
exit(1);
@@ -3487,6 +3499,9 @@ int main(int argc, char **argv, char **envp)
pause_all_vcpus();
net_cleanup();
res_free();
+#ifdef CONFIG_TPM
+ tpm_cleanup();
+#endif
return 0;
}
--
1.7.6.4
^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options Stefan Berger
@ 2011-12-12 23:16 ` Anthony Liguori
2011-12-13 2:16 ` Stefan Berger
0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:16 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl
On 12/12/2011 01:12 PM, Stefan Berger wrote:
> This patch adds support for TPM command line options.
> The command line options supported here are
>
> ./qemu-... -tpmdev passthrough,path=<path to TPM device>,id=<id>
> -device tpm-tis,tpmdev=<id>
>
> and
>
> ./qemu-... -tpmdev ?
>
> where the latter works similar to -soundhw ? and shows a list of
> available TPM backends (for example 'passthrough').
>
> Using the type parameter, the backend is chosen, i.e., 'passthrough' for the
> passthrough driver. 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
> 'create' and return a TPMDriver structure if the VM can be started or 'NULL'
> if not enough or bad parameters were provided.
>
> Monitor support for 'info tpm' has been added. It for example prints the
> following:
>
> (qemu) info tpm
> TPM devices:
> tpm0: model=tpm-tis
> \ tpm0: type=passthrough,path=/dev/tpm0
>
> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>
> ---
>
> v12:
> - use all 4 bytes of the message length indicator
>
> v10:
> - tpm_display_backend_drivers always prints to stderr
>
> v9:
> - prefixing all functions with tpm_tis_ and all constants with TPM_TIS_
> - splitting off of -tpm command line support into its own patch
> - only support TPM passthrough for now
>
> v8:
> - adjusting formatting of backend drivers output to accomodate better
> formatting of 'passthrough' backend output
>
> v6:
> - use #idef CONFIG_TPM to surround TPM calls
> - use QLIST_FOREACH_SAFE rather than QLIST_FOREACH in tpm_cleanup
> - commented backend ops in tpm.h
> - moving to IRQ 5 (11 collided with network cards)
>
> v5:
> - fixing typo reported by Serge Hallyn
> - Adapting code to split command line parameters supporting
> -tpmdev ... -device tpm-tis,tpmdev=...
> - moved code out of arch_init.c|h into tpm.c|h
> - increasing reserved memory for ACPI tables to 128kb (from 64kb)
> - the backend interface has a create() function for interpreting the command
> line parameters and returning a TPMDevice structure; previoulsy
> this function was called handle_options()
> - the backend interface has a destroy() function for cleaning up after
> the create() function was called
> - added support for 'info tpm' in monitor
>
> v4:
> - coding style fixes
>
> v3:
> - added hw/tpm_tis.h to this patch so Qemu compiles at this stage
> ---
> hmp-commands.hx | 2 +
> hw/tpm_tis.h | 91 ++++++++++++++++++++++++++++++
> monitor.c | 10 +++
> qemu-config.c | 20 +++++++
> qemu-options.hx | 34 +++++++++++
> tpm.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> tpm.h | 90 +++++++++++++++++++++++++++++
> vl.c | 15 +++++
> 8 files changed, 429 insertions(+), 0 deletions(-)
> create mode 100644 hw/tpm_tis.h
> create mode 100644 tpm.c
> create mode 100644 tpm.h
>
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index fdbed15..4506f56 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -1352,6 +1352,8 @@ show device tree
> show qdev device model list
> @item info roms
> show roms
> +@item info tpm
> +show the TPM device
> @end table
> ETEXI
>
> diff --git a/hw/tpm_tis.h b/hw/tpm_tis.h
> new file mode 100644
> index 0000000..e6a9a4b
> --- /dev/null
> +++ b/hw/tpm_tis.h
> @@ -0,0 +1,91 @@
> +/*
> + * tpm_tis.c - QEMU's TPM TIS interface emulator
> + *
> + * Copyright (C) 2006,2010,2011 IBM Corporation
> + *
> + * Authors:
> + * 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 found at
> + * http://www.trustedcomputiggroup.org
> + *
> + */
> +#ifndef _HW_TPM_TIS_H
> +#define _HW_TPM_TIS_H
Macros shouldn't start with '_' as that namespace is reserved in C.
> +#include "isa.h"
> +#include "qemu-thread.h"
> +
> +#include<stdint.h>
Just include "qemu-common.h"
> +#define TPM_TIS_ADDR_BASE 0xFED40000
> +
> +#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */
> +#define TPM_TIS_NO_LOCALITY 0xff
> +
> +#define TPM_TIS_IS_VALID_LOCTY(x) ((x)< TPM_TIS_NUM_LOCALITIES)
> +
> +#define TPM_TIS_IRQ 5
> +
> +#define TPM_TIS_BUFFER_MAX 4096
> +
> +
> +typedef struct TPMSizedBuffer {
> + uint32_t size;
> + uint8_t *buffer;
> +} TPMSizedBuffer;
> +
> +enum tpm_tis_state {
> + TPM_TIS_STATE_IDLE = 0,
> + TPM_TIS_STATE_READY,
> + TPM_TIS_STATE_COMPLETION,
> + TPM_TIS_STATE_EXECUTION,
> + TPM_TIS_STATE_RECEPTION,
> +};
Typed enums still need to be CamelCase and typedefed.
> +/* locality data -- all fields are persisted */
> +typedef struct TPMLocality {
> + enum tpm_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;
> +
> +typedef struct TPMTISState {
> + uint32_t offset;
> + uint8_t buf[TPM_TIS_BUFFER_MAX];
> +
> + uint8_t active_locty;
> + uint8_t aborting_locty;
> + uint8_t next_locty;
> +
> + TPMLocality loc[TPM_TIS_NUM_LOCALITIES];
> +
> + qemu_irq irq;
> + uint32_t irq_num;
> +} TPMTISState;
> +
> +/* utility functions */
> +
> +static inline uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
> +{
> + return (sb->buffer[2]<< 24) |
> + (sb->buffer[3]<< 16) |
> + (sb->buffer[4]<< 8) |
> + sb->buffer[5];
> +}
I would strongly suggest not making this static inline.
> +
> +#endif /* _HW_TPM_TIS_H */
> diff --git a/monitor.c b/monitor.c
> index 7334401..28873c3 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -47,6 +47,7 @@
> #include "migration.h"
> #include "kvm.h"
> #include "acl.h"
> +#include "tpm.h"
> #include "qint.h"
> #include "qfloat.h"
> #include "qlist.h"
> @@ -2735,6 +2736,15 @@ static mon_cmd_t info_cmds[] = {
> .help = "show available trace-events& their state",
> .mhandler.info = do_trace_print_events,
> },
> +#if defined(CONFIG_TPM)
> + {
> + .name = "tpm",
> + .args_type = "",
> + .params = "",
> + .help = "show the TPM device",
> + .mhandler.info = do_info_tpm,
> + },
> +#endif
Please don't make monitor commands conditional. Make it fail in a predictable
fashion if tpm isn't configured.
> {
> .name = NULL,
> },
> diff --git a/qemu-config.c b/qemu-config.c
> index 18f3020..cc4c31d 100644
> --- a/qemu-config.c
> +++ b/qemu-config.c
> @@ -549,6 +549,25 @@ QemuOptsList qemu_boot_opts = {
> },
> };
>
> +static QemuOptsList qemu_tpmdev_opts = {
> + .name = "tpmdev",
> + .implied_opt_name = "type",
> + .head = QTAILQ_HEAD_INITIALIZER(qemu_tpmdev_opts.head),
> + .desc = {
> + {
> + .name = "type",
> + .type = QEMU_OPT_STRING,
> + .help = "Type of TPM backend",
> + },
> + {
> + .name = "path",
> + .type = QEMU_OPT_STRING,
> + .help = "Persistent storage for TPM state",
> + },
> + { /* end of list */ }
> + },
> +};
> +
> static QemuOptsList *vm_config_groups[32] = {
> &qemu_drive_opts,
> &qemu_chardev_opts,
> @@ -563,6 +582,7 @@ static QemuOptsList *vm_config_groups[32] = {
> &qemu_option_rom_opts,
> &qemu_machine_opts,
> &qemu_boot_opts,
> +&qemu_tpmdev_opts,
I assume this is my mailer or is the whitespace munged here?
> NULL,
> };
>
> diff --git a/qemu-options.hx b/qemu-options.hx
> index b3db10c..58a5dfa 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1903,6 +1903,40 @@ ETEXI
>
> DEFHEADING()
>
> +DEFHEADING(TPM device options:)
> +
> +#ifndef _WIN32
> +# ifdef CONFIG_TPM
> +DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
> + "-tpmdev [<type>],id=str[,option][,option][,...]\n",
> + QEMU_ARCH_ALL)
> +# endif
> +#endif
> +STEXI
> +
> +The general form of a TPM device option is:
> +@table @option
> +
> +@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
> +@findex -tpmdev
> +Backend type must be:
> +
> +The specific backend type will determine the applicable options.
> +The @code{-tpmdev} options requires a @code{-device} option.
> +
> +Options to each backend are described below.
> +
> +Use ? to print all available TPM backend types.
> +@example
> +qemu -tpmdev ?
> +@end example
> +
> +@end table
> +
> +ETEXI
> +
> +DEFHEADING()
> +
> DEFHEADING(Linux/Multiboot boot specific:)
> STEXI
>
> diff --git a/tpm.c b/tpm.c
> new file mode 100644
> index 0000000..fcab023
> --- /dev/null
> +++ b/tpm.c
> @@ -0,0 +1,167 @@
> +/*
> + * TPM configuration
> + *
> + * Copyright (C) 2011 IBM Corporation
> + *
> + * Authors:
> + * Stefan Berger<stefanb@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
>
v2 or later please.
> + * Based on net.c
> + */
> +#include "config.h"
> +
> +#include "tpm.h"
> +#include "monitor.h"
> +#include "qerror.h"
> +
> +static const TPMDriverOps *bes[] = {
> + NULL,
> +};
> +
> +static QLIST_HEAD(, TPMBackend) tpm_backends =
> + QLIST_HEAD_INITIALIZER(tpm_backends);
> +
> +const TPMDriverOps *tpm_get_backend_driver(const char *id)
> +{
> + int i;
> +
> + for (i = 0; bes[i] != NULL; i++) {
> + if (!strcmp(bes[i]->id, id)) {
> + break;
> + }
> + }
> +
> + return bes[i];
> +}
> +
> +void tpm_display_backend_drivers(void)
> +{
> + int i;
> +
> + fprintf(stderr, "Supported TPM types (choose only one):\n");
Having fprintfs to stderr is a sign something is wrong.
In this case, we should have a programatic way to query the backends (like via
qmp_query_tpm) and then in the option handling code, we should use that function
and print to the screen from there.
> + for (i = 0; bes[i] != NULL; i++) {
> + fprintf(stderr, "%12s %s\n", bes[i]->id, bes[i]->desc());
> + }
> + fprintf(stderr, "\n");
> +}
> +
> +TPMBackend *qemu_find_tpm(const char *id)
> +{
> + TPMBackend *drv;
> +
> + QLIST_FOREACH(drv,&tpm_backends, list) {
> + if (!strcmp(drv->id, id)) {
> + return drv;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +void do_info_tpm(Monitor *mon)
> +{
> + TPMBackend *drv;
> + unsigned int c = 0;
> +
> + monitor_printf(mon, "TPM device:\n");
> +
> + QLIST_FOREACH(drv,&tpm_backends, list) {
> + monitor_printf(mon, " tpm%d: model=%s\n",
> + c, drv->fe_model);
> + monitor_printf(mon, " \\ %s: type=%s%s%s\n",
> + drv->id, drv->ops->id,
> + drv->parameters ? "," : "",
> + drv->parameters ? drv->parameters : "");
> + c++;
> + }
> +}
We should do this through sure QAPI now that it's in the the tree with a proper
schema entry and an implementation in hmp.c.
> +
> +static int configure_tpm(QemuOpts *opts)
> +{
> + const char *value;
> + const char *id;
> + const TPMDriverOps *be;
> + TPMBackend *drv;
> +
> + if (!QLIST_EMPTY(&tpm_backends)) {
> + error_report("Only one TPM is allowed.\n");
> + return 1;
> + }
> +
> + id = qemu_opts_id(opts);
> + if (id == NULL) {
> + qerror_report(QERR_MISSING_PARAMETER, "id");
> + return 1;
> + }
> +
> + value = qemu_opt_get(opts, "type");
> + if (!value) {
> + qerror_report(QERR_MISSING_PARAMETER, "type");
> + tpm_display_backend_drivers();
> + return 1;
> + }
> +
> + be = tpm_get_backend_driver(value);
> + if (be == NULL) {
> + qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
> + "a tpm backend type");
> + tpm_display_backend_drivers();
> + return 1;
> + }
> +
> + drv = be->create(opts, id);
> + if (!drv) {
> + return 1;
> + }
> +
> + QLIST_INSERT_HEAD(&tpm_backends, drv, list);
> +
> + return 0;
> +}
> +
> +static int tpm_init_tpmdev(QemuOpts *opts, void *dummy)
> +{
> + return configure_tpm(opts);
> +}
> +
> +void tpm_cleanup(void)
> +{
> + TPMBackend *drv, *next;
> +
> + QLIST_FOREACH_SAFE(drv,&tpm_backends, list, next) {
> + QLIST_REMOVE(drv, list);
> + drv->ops->destroy(drv);
> + }
> +}
> +
> +int tpm_init(void)
> +{
> + if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
> + tpm_init_tpmdev, NULL, 1) != 0) {
> + return -1;
> + }
> +
> + atexit(tpm_cleanup);
> +
> + return 0;
> +}
> +
> +void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
> +{
> + QemuOpts *opts;
> +
> + if (strcmp("none", optarg) != 0) {
> + if (*optarg == '?') {
> + tpm_display_backend_drivers();
> + exit(0);
Don't exit from something other than vl.c. Return an error code and let vl.c exit.
> + }
> + opts = qemu_opts_parse(opts_list, optarg, 1);
> + if (!opts) {
> + exit(1);
> + }
> + }
> +}
> diff --git a/tpm.h b/tpm.h
> new file mode 100644
> index 0000000..85c2a35
> --- /dev/null
> +++ b/tpm.h
> @@ -0,0 +1,90 @@
Needs a copyright.
> +#ifndef _QEMU_TPM_H
> +#define _QEMU_TPM_H
> +
> +#include "memory.h"
> +#include "hw/tpm_tis.h"
> +
> +struct TPMDriverOps;
> +typedef struct TPMDriverOps TPMDriverOps;
> +
> +typedef struct TPMBackend {
> + char *id;
> + const char *fe_model;
> + char *parameters;
> + const TPMDriverOps *ops;
> +
> + QLIST_ENTRY(TPMBackend) list;
> +} TPMBackend;
> +
> +/* overall state of the TPM interface */
> +typedef struct TPMState {
> + ISADevice busdev;
> + MemoryRegion mmio;
> +
> + union {
> + TPMTISState tis;
> + } s;
> +
> + uint8_t command_locty;
> + TPMLocality *cmd_locty;
> +
> + QemuMutex state_lock;
> + QemuCond from_tpm_cond;
> + QemuCond to_tpm_cond;
> + bool to_tpm_execute;
> +
> + bool tpm_initialized;
> +
> + char *backend;
> + TPMBackend *be_driver;
> +} TPMState;
> +
> +typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty);
> +
> +struct TPMDriverOps {
> + const char *id;
> + /* get a descriptive text of the backend to display to the user */
> + const char *(*desc)(void);
> +
> + TPMBackend *(*create)(QemuOpts *opts, const char *id);
> + void (*destroy)(TPMBackend *t);
> +
> + /* initialize the backend */
> + int (*init)(TPMBackend *t, TPMState *s, TPMRecvDataCB *datacb);
> + /* start up the TPM on the backend */
> + int (*startup_tpm)(TPMBackend *t);
> + /* returns true if nothing will ever answer TPM requests */
> + bool (*had_startup_error)(TPMBackend *t);
> +
> + size_t (*realloc_buffer)(TPMSizedBuffer *sb);
> +
> + void (*reset)(TPMBackend *t);
> +
> + bool (*get_tpm_established_flag)(TPMBackend *t);
> +};
> +
> +static inline void tpm_dump_buffer(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");
> +}
This definitely shouldn't be static inline and it's questionable whether it
should exist in the first place.
> +#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
> +
> +void tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
> +int tpm_init(void);
> +void tpm_cleanup(void);
> +TPMBackend *qemu_find_tpm(const char *id);
> +void do_info_tpm(Monitor *mon);
> +void tpm_display_backend_drivers(void);
> +const TPMDriverOps *tpm_get_backend_driver(const char *id);
Please document these functions.
> +#endif /* _QEMU_TPM_H */
> diff --git a/vl.c b/vl.c
> index 5372a96..12915d0 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -139,6 +139,7 @@ int main(int argc, char **argv)
> #include "block.h"
> #include "blockdev.h"
> #include "block-migration.h"
> +#include "tpm.h"
> #include "dma.h"
> #include "audio/audio.h"
> #include "migration.h"
> @@ -2550,6 +2551,11 @@ int main(int argc, char **argv, char **envp)
> ram_size = value;
> break;
> }
> +#ifdef CONFIG_TPM
> + case QEMU_OPTION_tpmdev:
> + tpm_config_parse(qemu_find_opts("tpmdev"), optarg);
> + break;
> +#endif
Don't make options conditional.
Regards,
Anthony Liguori
> case QEMU_OPTION_mempath:
> mem_path = optarg;
> break;
> @@ -3243,6 +3249,12 @@ int main(int argc, char **argv, char **envp)
> exit(1);
> }
>
> +#ifdef CONFIG_TPM
> + if (tpm_init()< 0) {
> + exit(1);
> + }
> +#endif
> +
> /* init the bluetooth world */
> if (foreach_device_config(DEV_BT, bt_parse))
> exit(1);
> @@ -3487,6 +3499,9 @@ int main(int argc, char **argv, char **envp)
> pause_all_vcpus();
> net_cleanup();
> res_free();
> +#ifdef CONFIG_TPM
> + tpm_cleanup();
> +#endif
>
> return 0;
> }
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options
2011-12-12 23:16 ` Anthony Liguori
@ 2011-12-13 2:16 ` Stefan Berger
0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-13 2:16 UTC (permalink / raw)
To: Anthony Liguori; +Cc: andreas.niederl, qemu-devel, mst
On 12/12/2011 06:16 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, Stefan Berger wrote:
>> @@ -2735,6 +2736,15 @@ static mon_cmd_t info_cmds[] = {
>> .help = "show available trace-events& their state",
>> .mhandler.info = do_trace_print_events,
>> },
>> +#if defined(CONFIG_TPM)
>> + {
>> + .name = "tpm",
>> + .args_type = "",
>> + .params = "",
>> + .help = "show the TPM device",
>> + .mhandler.info = do_info_tpm,
>> + },
>> +#endif
>
> Please don't make monitor commands conditional. Make it fail in a
> predictable fashion if tpm isn't configured.
>
This will then require tpm.c to always be compiled. You'll find the
CONFIG_TPM there then.
@@ -563,6 +582,7 @@ static QemuOptsList *vm_config_groups[32] = {
>> &qemu_option_rom_opts,
>> &qemu_machine_opts,
>> &qemu_boot_opts,
>> +&qemu_tpmdev_opts,
>
> I assume this is my mailer or is the whitespace munged here?
>
Must be your mailer.@@ -0,0 +1,167 @@
>> +/*
>> + * TPM configuration
>> + *
>> + * Copyright (C) 2011 IBM Corporation
>> + *
>> + * Authors:
>> + * Stefan Berger<stefanb@us.ibm.com>
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2.
>> See
>> + * the COPYING file in the top-level directory.
>>
>
> v2 or later please.
ok.
>> +
>> +void tpm_display_backend_drivers(void)
>> +{
>> + int i;
>> +
>> + fprintf(stderr, "Supported TPM types (choose only one):\n");
>
> Having fprintfs to stderr is a sign something is wrong.
>
> In this case, we should have a programatic way to query the backends
> (like via qmp_query_tpm) and then in the option handling code, we
> should use that function and print to the screen from there.
>
Will try to convert but keep this function ?
>> +
>> +void do_info_tpm(Monitor *mon)
>> +{
>> + TPMBackend *drv;
>> + unsigned int c = 0;
>> +
>> + monitor_printf(mon, "TPM device:\n");
>> +
>> + QLIST_FOREACH(drv,&tpm_backends, list) {
>> + monitor_printf(mon, " tpm%d: model=%s\n",
>> + c, drv->fe_model);
>> + monitor_printf(mon, " \\ %s: type=%s%s%s\n",
>> + drv->id, drv->ops->id,
>> + drv->parameters ? "," : "",
>> + drv->parameters ? drv->parameters : "");
>> + c++;
>> + }
>> +}
>
> We should do this through sure QAPI now that it's in the the tree with
> a proper schema entry and an implementation in hmp.c.
True...
>> +void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
>> +{
>> + QemuOpts *opts;
>> +
>> + if (strcmp("none", optarg) != 0) {
>> + if (*optarg == '?') {
>> + tpm_display_backend_drivers();
>> + exit(0);
>
> Don't exit from something other than vl.c. Return an error code and
> let vl.c exit.
I implemented this following along the lines of
qemu-system-x86_64 -soundhw ?
which then also shows and error code 0. What error code should it return
to the shell ?
>
>> + }
>> + opts = qemu_opts_parse(opts_list, optarg, 1);
>> + if (!opts) {
>> + exit(1);
>> + }
>> + }
>> +}
>> diff --git a/tpm.h b/tpm.h
>> new file mode 100644
>> index 0000000..85c2a35
>> --- /dev/null
>> +++ b/tpm.h
>> @@ -0,0 +1,90 @@
>
> Needs a copyright.
Ok.
>> +static inline void tpm_dump_buffer(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");
>> +}
>
> This definitely shouldn't be static inline and it's questionable
> whether it should exist in the first place.
Do you have an alternative for this function? Assuming it's useful for
debugging, should I just move it into tpm.c ?
>
>> +#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
>> +
>> +void tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
>> +int tpm_init(void);
>> +void tpm_cleanup(void);
>> +TPMBackend *qemu_find_tpm(const char *id);
>> +void do_info_tpm(Monitor *mon);
>> +void tpm_display_backend_drivers(void);
>> +const TPMDriverOps *tpm_get_backend_driver(const char *id);
>
> Please document these functions.
Will document them in tpm.c where their implementation is.
@@ -2550,6 +2551,11 @@ int main(int argc, char **argv, char **envp)
>> ram_size = value;
>> break;
>> }
>> +#ifdef CONFIG_TPM
>> + case QEMU_OPTION_tpmdev:
>> + tpm_config_parse(qemu_find_opts("tpmdev"), optarg);
>> + break;
>> +#endif
>
> Don't make options conditional.
Can I have an #ifdef-#else-#endif construct there along the lines of
CONFIG_SDL with an exit(1) in the #else branch?
Stefan
^ permalink raw reply [flat|nested] 29+ messages in thread
* [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
2011-12-12 23:23 ` Anthony Liguori
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 3/7] Add a debug register Stefan Berger
` (5 subsequent siblings)
7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 UTC (permalink / raw)
To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst
This patch adds the main code of the TPM frontend driver, the TPM TIS
interface, to Qemu. The code is largely based on the 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>
---
v13:
- don't call destroy callback in error path in tpm_tis_init
v11:
- initialize val with 0xffffffff as default value to return from MMIO
read to an undefined location
- display size of read or write operations in debugging output
v10:
- Use error_report where necessary
- Convert to use memory_region_init_io; use DEVICE_NATIVE_ENDIAN which
works for an x86 VM on x86 or ppc host
- Implement cleanup in tpm_tis_init
- put frontend model name into TPMBackend driver structure for later display
with the monitor
v9:
- prefixing all function with tpm_tis_ and all constants with TPM_TIS_
- adding minimum VMStateDescription and marking device as non-migratable
v5:
- adding comment to tis_data_read
- refactoring following support for split command line options
-tpmdev and -device
- code handling the configuration of the TPM device was moved to tpm.c
- removed empty line at end of file
v3:
- prefixing functions with tis_
- added a function to the backend interface 'early_startup_tpm' that
allows to detect the presence of the block storage and gracefully fails
Qemu if it's not available. This works with migration using shared
storage but doesn't support migration with block storage migration.
For encyrypted QCoW2 and in case of a snapshot resue the late_startup_tpm
interface function is called
---
hw/tpm_tis.c | 818 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 818 insertions(+), 0 deletions(-)
create mode 100644 hw/tpm_tis.c
diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
new file mode 100644
index 0000000..34d0043
--- /dev/null
+++ b/hw/tpm_tis.c
@@ -0,0 +1,818 @@
+/*
+ * tpm_tis.c - QEMU's TPM TIS interface emulator
+ *
+ * Copyright (C) 2006,2010,2011 IBM Corporation
+ *
+ * Authors:
+ * 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
+ * License.
+ *
+ *
+ * Implementation of the TIS interface according to specs found at
+ * http://www.trustedcomputiggroup.org.
+ * In the developers menu choose the PC Client section then find the TIS
+ * specification.
+ */
+
+#include "tpm.h"
+#include "block.h"
+#include "exec-memory.h"
+#include "hw/hw.h"
+#include "hw/pc.h"
+#include "hw/tpm_tis.h"
+#include "qemu-error.h"
+
+#include <stdio.h>
+
+/*#define DEBUG_TIS */
+
+/* whether the STS interrupt is supported */
+/*#define RAISE_STS_IRQ */
+
+/* tis registers */
+#define TPM_TIS_REG_ACCESS 0x00
+#define TPM_TIS_REG_INT_ENABLE 0x08
+#define TPM_TIS_REG_INT_VECTOR 0x0c
+#define TPM_TIS_REG_INT_STATUS 0x10
+#define TPM_TIS_REG_INTF_CAPABILITY 0x14
+#define TPM_TIS_REG_STS 0x18
+#define TPM_TIS_REG_DATA_FIFO 0x24
+#define TPM_TIS_REG_DID_VID 0xf00
+#define TPM_TIS_REG_RID 0xf04
+
+#define TPM_TIS_STS_VALID (1 << 7)
+#define TPM_TIS_STS_COMMAND_READY (1 << 6)
+#define TPM_TIS_STS_TPM_GO (1 << 5)
+#define TPM_TIS_STS_DATA_AVAILABLE (1 << 4)
+#define TPM_TIS_STS_EXPECT (1 << 3)
+#define TPM_TIS_STS_RESPONSE_RETRY (1 << 1)
+
+#define TPM_TIS_ACCESS_TPM_REG_VALID_STS (1 << 7)
+#define TPM_TIS_ACCESS_ACTIVE_LOCALITY (1 << 5)
+#define TPM_TIS_ACCESS_BEEN_SEIZED (1 << 4)
+#define TPM_TIS_ACCESS_SEIZE (1 << 3)
+#define TPM_TIS_ACCESS_PENDING_REQUEST (1 << 2)
+#define TPM_TIS_ACCESS_REQUEST_USE (1 << 1)
+#define TPM_TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0)
+
+#define TPM_TIS_INT_ENABLED (1 << 31)
+#define TPM_TIS_INT_DATA_AVAILABLE (1 << 0)
+#define TPM_TIS_INT_STS_VALID (1 << 1)
+#define TPM_TIS_INT_LOCALITY_CHANGED (1 << 2)
+#define TPM_TIS_INT_COMMAND_READY (1 << 7)
+
+#ifndef RAISE_STS_IRQ
+
+# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
+ TPM_TIS_INT_DATA_AVAILABLE | \
+ TPM_TIS_INT_COMMAND_READY)
+
+#else
+
+# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
+ TPM_TIS_INT_DATA_AVAILABLE | \
+ TPM_TIS_INT_STS_VALID | \
+ TPM_TIS_INT_COMMAND_READY)
+
+#endif
+
+#define TPM_TIS_CAPABILITIES_SUPPORTED ((1 << 4) | \
+ TPM_TIS_INTERRUPTS_SUPPORTED)
+
+#define TPM_TIS_TPM_DID 0x0001
+#define TPM_TIS_TPM_VID 0x0001
+#define TPM_TIS_TPM_RID 0x0001
+
+#define TPM_TIS_NO_DATA_BYTE 0xff
+
+#ifdef DEBUG_TIS
+static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
+{
+ uint16_t len;
+
+ len = tpm_tis_get_size_from_buffer(sb);
+ fprintf(stderr, "tpm_tis: %s length = %d\n", string, len);
+ tpm_dump_buffer(stderr, sb->buffer, len);
+}
+#endif
+
+static inline uint8_t tpm_tis_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 tpm_tis_tpm_send(TPMState *s, uint8_t locty)
+{
+ TPMTISState *tis = &s->s.tis;
+#ifdef DEBUG_TIS
+ tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
+#endif
+ s->command_locty = locty;
+ s->cmd_locty = &tis->loc[locty];
+
+ /* w_offset serves as length indicator for length of data;
+ it's reset when the response comes back */
+ tis->loc[locty].state = TPM_TIS_STATE_EXECUTION;
+ tis->loc[locty].sts &= ~TPM_TIS_STS_EXPECT;
+
+ s->to_tpm_execute = true;
+ qemu_cond_signal(&s->to_tpm_cond);
+}
+
+/* raise an interrupt if allowed */
+static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
+{
+ TPMTISState *tis = &s->s.tis;
+
+ if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
+ return;
+ }
+
+ if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
+ (tis->loc[locty].inte & irqmask)) {
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: Raising IRQ for flag %08x\n", irqmask);
+#endif
+ qemu_irq_raise(s->s.tis.irq);
+ tis->loc[locty].ints |= irqmask;
+ }
+}
+
+static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
+{
+ uint8_t l;
+
+ for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
+ if (l == locty) {
+ continue;
+ }
+ if ((s->s.tis.loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
+{
+ TPMTISState *tis = &s->s.tis;
+ int change = (s->s.tis.active_locty != new_active_locty);
+
+ if (change && TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) {
+ /* reset flags on the old active locality */
+ tis->loc[s->s.tis.active_locty].access &=
+ ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|TPM_TIS_ACCESS_REQUEST_USE);
+ if (TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
+ tis->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE) {
+ tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
+ }
+ }
+
+ tis->active_locty = new_active_locty;
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: Active locality is now %d\n",
+ s->s.tis.active_locty);
+#endif
+
+ if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
+ /* set flags on the new active locality */
+ tis->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
+ tis->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
+ TPM_TIS_ACCESS_SEIZE);
+ }
+
+ if (change) {
+ tpm_tis_raise_irq(s, tis->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
+ }
+}
+
+/* abort -- this function switches the locality */
+static void tpm_tis_abort(TPMState *s, uint8_t locty)
+{
+ TPMTISState *tis = &s->s.tis;
+
+ tis->loc[locty].r_offset = 0;
+ tis->loc[locty].w_offset = 0;
+
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: tis_abort: new active locality is %d\n",
+ tis->next_locty);
+#endif
+
+ /*
+ * Need to react differently depending on who's aborting now and
+ * which locality will become active afterwards.
+ */
+ if (tis->aborting_locty == tis->next_locty) {
+ tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY;
+ tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_READY;
+ tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY);
+ }
+
+ /* locality after abort is another one than the current one */
+ tpm_tis_new_active_locality(s, tis->next_locty);
+
+ tis->next_locty = TPM_TIS_NO_LOCALITY;
+ /* nobody's aborting a command anymore */
+ tis->aborting_locty = TPM_TIS_NO_LOCALITY;
+}
+
+/* prepare aborting current command */
+static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
+{
+ TPMTISState *tis = &s->s.tis;
+ uint8_t busy_locty;
+
+ tis->aborting_locty = locty;
+ tis->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 < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
+ if (tis->loc[busy_locty].state == TPM_TIS_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;
+ }
+ }
+
+ tpm_tis_abort(s, locty);
+}
+
+/*
+ * Callback from the TPM to indicate that the response was received.
+ */
+static void tpm_tis_receive_cb(TPMState *s, uint8_t locty)
+{
+ TPMTISState *tis = &s->s.tis;
+
+ qemu_mutex_lock(&s->state_lock);
+
+ tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE;
+ tis->loc[locty].state = TPM_TIS_STATE_COMPLETION;
+ tis->loc[locty].r_offset = 0;
+ tis->loc[locty].w_offset = 0;
+
+ if (TPM_TIS_IS_VALID_LOCTY(tis->next_locty)) {
+ tpm_tis_abort(s, locty);
+ }
+
+ qemu_cond_signal(&s->from_tpm_cond);
+
+ qemu_mutex_unlock(&s->state_lock);
+
+#ifndef RAISE_STS_IRQ
+ tpm_tis_raise_irq(s, locty, TPM_TIS_INT_DATA_AVAILABLE);
+#else
+ tpm_tis_raise_irq(s, locty,
+ TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
+#endif
+}
+
+/*
+ * read a byte of response data
+ * call this with s->state_lock held
+ */
+static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
+{
+ TPMTISState *tis = &s->s.tis;
+ uint32_t ret = TPM_TIS_NO_DATA_BYTE;
+ uint16_t len;
+
+ if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
+ len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
+
+ ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++];
+ if (tis->loc[locty].r_offset >= len) {
+ /* got last byte */
+ tis->loc[locty].sts = TPM_TIS_STS_VALID;
+#ifdef RAISE_STS_IRQ
+ tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
+#endif
+ }
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n",
+ ret, tis->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 uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
+{
+ TPMState *s = opaque;
+ TPMTISState *tis = &s->s.tis;
+ uint16_t offset = addr & 0xffc;
+ uint8_t shift = (addr & 0x3) * 8;
+ uint32_t val = 0xffffffff;
+ uint8_t locty = tpm_tis_locality_from_addr(addr);
+
+ qemu_mutex_lock(&s->state_lock);
+
+ if (s->be_driver->ops->had_startup_error(s->be_driver)) {
+ qemu_mutex_unlock(&s->state_lock);
+ return val;
+ }
+
+ switch (offset) {
+ case TPM_TIS_REG_ACCESS:
+ /* never show the SEIZE flag even though we use it internally */
+ val = tis->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
+ /* the pending flag is alawys calculated */
+ if (tpm_tis_check_request_use_except(s, locty)) {
+ val |= TPM_TIS_ACCESS_PENDING_REQUEST;
+ }
+ val |= !s->be_driver->ops->get_tpm_established_flag(s->be_driver);
+ break;
+ case TPM_TIS_REG_INT_ENABLE:
+ val = tis->loc[locty].inte;
+ break;
+ case TPM_TIS_REG_INT_VECTOR:
+ val = tis->irq_num;
+ break;
+ case TPM_TIS_REG_INT_STATUS:
+ val = tis->loc[locty].ints;
+ break;
+ case TPM_TIS_REG_INTF_CAPABILITY:
+ val = TPM_TIS_CAPABILITIES_SUPPORTED;
+ break;
+ case TPM_TIS_REG_STS:
+ if (tis->active_locty == locty) {
+ if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
+ val = (tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer)
+ - tis->loc[locty].r_offset) << 8 | tis->loc[locty].sts;
+ } else {
+ val = (tis->loc[locty].w_buffer.size -
+ tis->loc[locty].w_offset) << 8 | tis->loc[locty].sts;
+ }
+ }
+ break;
+ case TPM_TIS_REG_DATA_FIFO:
+ if (tis->active_locty == locty) {
+ switch (tis->loc[locty].state) {
+ case TPM_TIS_STATE_COMPLETION:
+ val = tpm_tis_data_read(s, locty);
+ break;
+ default:
+ val = TPM_TIS_NO_DATA_BYTE;
+ break;
+ }
+ }
+ break;
+ case TPM_TIS_REG_DID_VID:
+ val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
+ break;
+ case TPM_TIS_REG_RID:
+ val = TPM_TIS_TPM_RID;
+ break;
+ }
+
+ qemu_mutex_unlock(&s->state_lock);
+
+ if (shift) {
+ val >>= shift;
+ }
+
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: read.%u(%08x) = %08x\n",
+ size, (int)addr, (uint32_t)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 tpm_tis_mmio_write_intern(void *opaque, target_phys_addr_t addr,
+ uint64_t val, unsigned size,
+ bool hw_access)
+{
+ TPMState *s = opaque;
+ TPMTISState *tis = &s->s.tis;
+ uint16_t off = addr & 0xfff;
+ uint8_t locty = tpm_tis_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.%u(%08x) = %08x\n",
+ size, (int)addr, (uint32_t)val);
+#endif
+
+ qemu_mutex_lock(&s->state_lock);
+
+ if (s->be_driver->ops->had_startup_error(s->be_driver)) {
+ qemu_mutex_unlock(&s->state_lock);
+ return;
+ }
+
+ switch (off) {
+ case TPM_TIS_REG_ACCESS:
+
+ if ((val & TPM_TIS_ACCESS_SEIZE)) {
+ val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+ }
+
+ active_locty = tis->active_locty;
+
+ if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
+ /* give up locality if currently owned */
+ if (tis->active_locty == locty) {
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: Releasing locality %d\n", locty);
+#endif
+ uint8_t newlocty = TPM_TIS_NO_LOCALITY;
+ /* anybody wants the locality ? */
+ for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
+ if ((tis->loc[c].access & TPM_TIS_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: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
+ "Next active locality: %d\n",
+ newlocty);
+#endif
+ if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
+ set_new_locty = 0;
+ tpm_tis_prep_abort(s, locty, newlocty);
+ } else {
+ active_locty = TPM_TIS_NO_LOCALITY;
+ }
+ } else {
+ /* not currently the owner; clear a pending request */
+ tis->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
+ }
+ }
+
+ if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
+ tis->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
+ }
+
+ if ((val & TPM_TIS_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 ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty) &&
+ locty > tis->active_locty) ||
+ (!TPM_TIS_IS_VALID_LOCTY(tis->active_locty))) {
+
+ /* already a pending SEIZE ? */
+ if ((tis->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
+ break;
+ }
+
+ /* check for ongoing seize by a higher locality */
+ for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
+ if ((tis->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
+ break;
+ }
+ }
+
+ /* cancel any seize by a lower locality */
+ for (l = 0; l < locty - 1; l++) {
+ tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
+ }
+
+ tis->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: TPM_TIS_ACCESS_SEIZE: "
+ "Locality %d seized from locality %d\n",
+ locty, tis->active_locty);
+ fprintf(stderr,
+ "tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
+#endif
+ set_new_locty = 0;
+ tpm_tis_prep_abort(s, tis->active_locty, locty);
+ break;
+ }
+ }
+
+ if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
+ if (tis->active_locty != locty) {
+ if (TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) {
+ tis->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
+ } else {
+ /* no locality active -> make this one active now */
+ active_locty = locty;
+ }
+ }
+ }
+
+ if (set_new_locty) {
+ tpm_tis_new_active_locality(s, active_locty);
+ }
+
+ break;
+ case TPM_TIS_REG_INT_ENABLE:
+ if (tis->active_locty != locty) {
+ break;
+ }
+
+ tis->loc[locty].inte = (val & (TPM_TIS_INT_ENABLED | (0x3 << 3) |
+ TPM_TIS_INTERRUPTS_SUPPORTED));
+ break;
+ case TPM_TIS_REG_INT_VECTOR:
+ /* hard wired -- ignore */
+ break;
+ case TPM_TIS_REG_INT_STATUS:
+ if (tis->active_locty != locty) {
+ break;
+ }
+
+ /* clearing of interrupt flags */
+ if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
+ (tis->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
+ tis->loc[locty].ints &= ~val;
+ if (tis->loc[locty].ints == 0) {
+ qemu_irq_lower(tis->irq);
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: Lowering IRQ\n");
+#endif
+ }
+ }
+ tis->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
+ break;
+ case TPM_TIS_REG_STS:
+ if (tis->active_locty != locty) {
+ break;
+ }
+
+ val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
+ TPM_TIS_STS_RESPONSE_RETRY);
+
+ if (val == TPM_TIS_STS_COMMAND_READY) {
+ switch (tis->loc[locty].state) {
+
+ case TPM_TIS_STATE_READY:
+ tis->loc[locty].w_offset = 0;
+ tis->loc[locty].r_offset = 0;
+ break;
+
+ case TPM_TIS_STATE_IDLE:
+ tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
+ tis->loc[locty].state = TPM_TIS_STATE_READY;
+ tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
+ break;
+
+ case TPM_TIS_STATE_EXECUTION:
+ case TPM_TIS_STATE_RECEPTION:
+ /* abort currently running command */
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: %s: Initiating abort.\n",
+ __func__);
+#endif
+ tpm_tis_prep_abort(s, locty, locty);
+ break;
+
+ case TPM_TIS_STATE_COMPLETION:
+ tis->loc[locty].w_offset = 0;
+ tis->loc[locty].r_offset = 0;
+ /* shortcut to ready state with C/R set */
+ tis->loc[locty].state = TPM_TIS_STATE_READY;
+ if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
+ tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
+ tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
+ }
+ break;
+
+ }
+ } else if (val == TPM_TIS_STS_TPM_GO) {
+ switch (tis->loc[locty].state) {
+ case TPM_TIS_STATE_RECEPTION:
+ tpm_tis_tpm_send(s, locty);
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
+ switch (tis->loc[locty].state) {
+ case TPM_TIS_STATE_COMPLETION:
+ tis->loc[locty].r_offset = 0;
+ tis->loc[locty].sts = TPM_TIS_STS_VALID |
+ TPM_TIS_STS_DATA_AVAILABLE;
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ }
+ break;
+ case TPM_TIS_REG_DATA_FIFO:
+ /* data fifo */
+ if (tis->active_locty != locty) {
+ break;
+ }
+
+ if (tis->loc[locty].state == TPM_TIS_STATE_IDLE ||
+ tis->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
+ tis->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
+ /* drop the byte */
+ } else {
+#ifdef DEBUG_TIS
+ fprintf(stderr, "tpm_tis: Byte to send to TPM: %02x\n",
+ (uint8_t)val);
+#endif
+ if (tis->loc[locty].state == TPM_TIS_STATE_READY) {
+ tis->loc[locty].state = TPM_TIS_STATE_RECEPTION;
+ tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID;
+ }
+
+ if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
+ if (tis->loc[locty].w_offset < tis->loc[locty].w_buffer.size) {
+ tis->loc[locty].w_buffer.
+ buffer[tis->loc[locty].w_offset++] = (uint8_t)val;
+ } else {
+ tis->loc[locty].sts = TPM_TIS_STS_VALID;
+ }
+ }
+
+ /* check for complete packet */
+ if (tis->loc[locty].w_offset > 5 &&
+ (tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
+ /* we have a packet length - see if we have all of it */
+#ifdef RAISE_STS_IRQ
+ bool needIrq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID);
+#endif
+ len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
+ if (len > tis->loc[locty].w_offset) {
+ tis->loc[locty].sts = TPM_TIS_STS_EXPECT |
+ TPM_TIS_STS_VALID;
+ } else {
+ /* packet complete */
+ tis->loc[locty].sts = TPM_TIS_STS_VALID;
+ }
+#ifdef RAISE_STS_IRQ
+ if (needIrq) {
+ tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
+ }
+#endif
+ }
+ }
+ break;
+ }
+
+ qemu_mutex_unlock(&s->state_lock);
+}
+
+static void tpm_tis_mmio_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val, unsigned size)
+{
+ return tpm_tis_mmio_write_intern(opaque, addr, val, size, false);
+}
+
+static const MemoryRegionOps tpm_tis_memory_ops = {
+ .read = tpm_tis_mmio_read,
+ .write = tpm_tis_mmio_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static int tpm_tis_do_startup_tpm(TPMState *s)
+{
+ return s->be_driver->ops->startup_tpm(s->be_driver);
+}
+
+/*
+ * This function is called when the machine starts, resets or due to
+ * S3 resume.
+ */
+static void tpm_tis_reset(DeviceState *d)
+{
+ TPMState *s = container_of(d, TPMState, busdev.qdev);
+ TPMTISState *tis = &s->s.tis;
+ int c;
+
+ s->tpm_initialized = false;
+
+ s->be_driver->ops->reset(s->be_driver);
+
+ tis->active_locty = TPM_TIS_NO_LOCALITY;
+ tis->next_locty = TPM_TIS_NO_LOCALITY;
+ tis->aborting_locty = TPM_TIS_NO_LOCALITY;
+
+ for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
+ tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
+ tis->loc[c].sts = 0;
+ tis->loc[c].inte = (1 << 3);
+ tis->loc[c].ints = 0;
+ tis->loc[c].state = TPM_TIS_STATE_IDLE;
+
+ tis->loc[c].w_offset = 0;
+ s->be_driver->ops->realloc_buffer(&tis->loc[c].w_buffer);
+ tis->loc[c].r_offset = 0;
+ s->be_driver->ops->realloc_buffer(&tis->loc[c].r_buffer);
+ }
+
+ tpm_tis_do_startup_tpm(s);
+}
+
+static int tpm_tis_init(ISADevice *dev)
+{
+ TPMState *s = DO_UPCAST(TPMState, busdev, dev);
+ TPMTISState *tis = &s->s.tis;
+ int rc;
+
+ qemu_mutex_init(&s->state_lock);
+ qemu_cond_init(&s->from_tpm_cond);
+ qemu_cond_init(&s->to_tpm_cond);
+
+ s->be_driver = qemu_find_tpm(s->backend);
+ if (!s->be_driver) {
+ error_report("tpm_tis: backend driver with id %s could not be "
+ "found.n\n", s->backend);
+ return -1;
+ }
+
+ s->be_driver->fe_model = "tpm-tis";
+
+ if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) {
+ return -1;
+ }
+
+ isa_init_irq(dev, &tis->irq, tis->irq_num);
+
+ memory_region_init_io(&s->mmio, &tpm_tis_memory_ops, s, "tpm-tis-mmio",
+ 0x1000 * TPM_TIS_NUM_LOCALITIES);
+ memory_region_add_subregion(get_system_memory(), TPM_TIS_ADDR_BASE,
+ &s->mmio);
+
+ rc = tpm_tis_do_startup_tpm(s);
+ if (rc != 0) {
+ goto err_destroy_memory;
+ }
+
+ return 0;
+
+ err_destroy_memory:
+ memory_region_destroy(&s->mmio);
+
+ return -1;
+}
+
+static const VMStateDescription vmstate_tpm_tis = {
+ .name = "tpm",
+ .unmigratable = 1,
+};
+
+static ISADeviceInfo tpm_tis_device_info = {
+ .init = tpm_tis_init,
+ .qdev.name = "tpm-tis",
+ .qdev.size = sizeof(TPMState),
+ .qdev.vmsd = &vmstate_tpm_tis,
+ .qdev.reset = tpm_tis_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("irq", TPMState,
+ s.tis.irq_num, TPM_TIS_IRQ),
+ DEFINE_PROP_STRING("tpmdev", TPMState, backend),
+ 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)
--
1.7.6.4
^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2011-12-12 23:23 ` Anthony Liguori
2011-12-12 23:54 ` Stefan Berger
0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:23 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl
On 12/12/2011 01:12 PM, Stefan Berger wrote:
> This patch adds the main code of the TPM frontend driver, the TPM TIS
> interface, to Qemu. The code is largely based on the 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>
>
> ---
>
> v13:
> - don't call destroy callback in error path in tpm_tis_init
>
> v11:
> - initialize val with 0xffffffff as default value to return from MMIO
> read to an undefined location
> - display size of read or write operations in debugging output
>
> v10:
> - Use error_report where necessary
> - Convert to use memory_region_init_io; use DEVICE_NATIVE_ENDIAN which
> works for an x86 VM on x86 or ppc host
> - Implement cleanup in tpm_tis_init
> - put frontend model name into TPMBackend driver structure for later display
> with the monitor
>
> v9:
> - prefixing all function with tpm_tis_ and all constants with TPM_TIS_
> - adding minimum VMStateDescription and marking device as non-migratable
>
> v5:
> - adding comment to tis_data_read
> - refactoring following support for split command line options
> -tpmdev and -device
> - code handling the configuration of the TPM device was moved to tpm.c
> - removed empty line at end of file
>
> v3:
> - prefixing functions with tis_
> - added a function to the backend interface 'early_startup_tpm' that
> allows to detect the presence of the block storage and gracefully fails
> Qemu if it's not available. This works with migration using shared
> storage but doesn't support migration with block storage migration.
> For encyrypted QCoW2 and in case of a snapshot resue the late_startup_tpm
> interface function is called
> ---
> hw/tpm_tis.c | 818 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 818 insertions(+), 0 deletions(-)
> create mode 100644 hw/tpm_tis.c
>
> diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
> new file mode 100644
> index 0000000..34d0043
> --- /dev/null
> +++ b/hw/tpm_tis.c
> @@ -0,0 +1,818 @@
> +/*
> + * tpm_tis.c - QEMU's TPM TIS interface emulator
> + *
> + * Copyright (C) 2006,2010,2011 IBM Corporation
> + *
> + * Authors:
> + * 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
> + * License.
> + *
> + *
> + * Implementation of the TIS interface according to specs found at
> + * http://www.trustedcomputiggroup.org.
> + * In the developers menu choose the PC Client section then find the TIS
> + * specification.
> + */
> +
> +#include "tpm.h"
> +#include "block.h"
> +#include "exec-memory.h"
> +#include "hw/hw.h"
> +#include "hw/pc.h"
> +#include "hw/tpm_tis.h"
> +#include "qemu-error.h"
> +
> +#include<stdio.h>
#include "qemu-common.h"
> +
> +/*#define DEBUG_TIS */
> +
> +/* whether the STS interrupt is supported */
> +/*#define RAISE_STS_IRQ */
> +
> +/* tis registers */
> +#define TPM_TIS_REG_ACCESS 0x00
> +#define TPM_TIS_REG_INT_ENABLE 0x08
> +#define TPM_TIS_REG_INT_VECTOR 0x0c
> +#define TPM_TIS_REG_INT_STATUS 0x10
> +#define TPM_TIS_REG_INTF_CAPABILITY 0x14
> +#define TPM_TIS_REG_STS 0x18
> +#define TPM_TIS_REG_DATA_FIFO 0x24
> +#define TPM_TIS_REG_DID_VID 0xf00
> +#define TPM_TIS_REG_RID 0xf04
> +
> +#define TPM_TIS_STS_VALID (1<< 7)
> +#define TPM_TIS_STS_COMMAND_READY (1<< 6)
> +#define TPM_TIS_STS_TPM_GO (1<< 5)
> +#define TPM_TIS_STS_DATA_AVAILABLE (1<< 4)
> +#define TPM_TIS_STS_EXPECT (1<< 3)
> +#define TPM_TIS_STS_RESPONSE_RETRY (1<< 1)
> +
> +#define TPM_TIS_ACCESS_TPM_REG_VALID_STS (1<< 7)
> +#define TPM_TIS_ACCESS_ACTIVE_LOCALITY (1<< 5)
> +#define TPM_TIS_ACCESS_BEEN_SEIZED (1<< 4)
> +#define TPM_TIS_ACCESS_SEIZE (1<< 3)
> +#define TPM_TIS_ACCESS_PENDING_REQUEST (1<< 2)
> +#define TPM_TIS_ACCESS_REQUEST_USE (1<< 1)
> +#define TPM_TIS_ACCESS_TPM_ESTABLISHMENT (1<< 0)
> +
> +#define TPM_TIS_INT_ENABLED (1<< 31)
> +#define TPM_TIS_INT_DATA_AVAILABLE (1<< 0)
> +#define TPM_TIS_INT_STS_VALID (1<< 1)
> +#define TPM_TIS_INT_LOCALITY_CHANGED (1<< 2)
> +#define TPM_TIS_INT_COMMAND_READY (1<< 7)
> +
> +#ifndef RAISE_STS_IRQ
> +
> +# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
> + TPM_TIS_INT_DATA_AVAILABLE | \
> + TPM_TIS_INT_COMMAND_READY)
> +
> +#else
> +
> +# define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
> + TPM_TIS_INT_DATA_AVAILABLE | \
> + TPM_TIS_INT_STS_VALID | \
> + TPM_TIS_INT_COMMAND_READY)
> +
> +#endif
> +
> +#define TPM_TIS_CAPABILITIES_SUPPORTED ((1<< 4) | \
> + TPM_TIS_INTERRUPTS_SUPPORTED)
> +
> +#define TPM_TIS_TPM_DID 0x0001
> +#define TPM_TIS_TPM_VID 0x0001
> +#define TPM_TIS_TPM_RID 0x0001
> +
> +#define TPM_TIS_NO_DATA_BYTE 0xff
> +
> +#ifdef DEBUG_TIS
> +static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
> +{
> + uint16_t len;
> +
> + len = tpm_tis_get_size_from_buffer(sb);
> + fprintf(stderr, "tpm_tis: %s length = %d\n", string, len);
> + tpm_dump_buffer(stderr, sb->buffer, len);
> +}
> +#endif
In the very least, move the guards into the function so that there aren't so
many #ifdefs in the code.
> +
> +static inline uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
> +{
> + return (uint8_t)((addr>> 12)& 0x7);
> +}
The compiler ignores inline for the most part. It's just placating you by
letting you think that you're asking it to do something :-)
> +
> +/*
> + * Send a TPM request.
> + * Call this with the state_lock held so we can sync with the receive
> + * callback.
> + */
> +static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
> +{
> + TPMTISState *tis =&s->s.tis;
> +#ifdef DEBUG_TIS
> + tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
> +#endif
> + s->command_locty = locty;
> + s->cmd_locty =&tis->loc[locty];
> +
> + /* w_offset serves as length indicator for length of data;
> + it's reset when the response comes back */
> + tis->loc[locty].state = TPM_TIS_STATE_EXECUTION;
> + tis->loc[locty].sts&= ~TPM_TIS_STS_EXPECT;
> +
> + s->to_tpm_execute = true;
> + qemu_cond_signal(&s->to_tpm_cond);
The locking seems to presume that the device model is re-entrant which it's not
today. Am I missing something here?
> +}
> +
> +/* raise an interrupt if allowed */
> +static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
> +{
> + TPMTISState *tis =&s->s.tis;
> +
> + if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
> + return;
> + }
> +
> + if ((tis->loc[locty].inte& TPM_TIS_INT_ENABLED)&&
> + (tis->loc[locty].inte& irqmask)) {
> +#ifdef DEBUG_TIS
> + fprintf(stderr, "tpm_tis: Raising IRQ for flag %08x\n", irqmask);
> +#endif
I'd much prefer a dprintf.
> + qemu_irq_raise(s->s.tis.irq);
> + tis->loc[locty].ints |= irqmask;
> + }
> +}
> +
> +static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
> +{
> + uint8_t l;
> +
> + for (l = 0; l< TPM_TIS_NUM_LOCALITIES; l++) {
> + if (l == locty) {
> + continue;
> + }
> + if ((s->s.tis.loc[l].access& TPM_TIS_ACCESS_REQUEST_USE)) {
> + return 1;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
> +{
> + TPMTISState *tis =&s->s.tis;
> + int change = (s->s.tis.active_locty != new_active_locty);
> +
> + if (change&& TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) {
> + /* reset flags on the old active locality */
> + tis->loc[s->s.tis.active_locty].access&=
> + ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|TPM_TIS_ACCESS_REQUEST_USE);
> + if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)&&
> + tis->loc[new_active_locty].access& TPM_TIS_ACCESS_SEIZE) {
> + tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
> + }
> + }
> +
> + tis->active_locty = new_active_locty;
> +#ifdef DEBUG_TIS
> + fprintf(stderr, "tpm_tis: Active locality is now %d\n",
> + s->s.tis.active_locty);
> +#endif
> +
> + if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
> + /* set flags on the new active locality */
> + tis->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
> + tis->loc[new_active_locty].access&= ~(TPM_TIS_ACCESS_REQUEST_USE |
> + TPM_TIS_ACCESS_SEIZE);
> + }
> +
> + if (change) {
> + tpm_tis_raise_irq(s, tis->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
> + }
> +}
> +
> +/* abort -- this function switches the locality */
> +static void tpm_tis_abort(TPMState *s, uint8_t locty)
> +{
> + TPMTISState *tis =&s->s.tis;
> +
> + tis->loc[locty].r_offset = 0;
> + tis->loc[locty].w_offset = 0;
> +
> +#ifdef DEBUG_TIS
> + fprintf(stderr, "tpm_tis: tis_abort: new active locality is %d\n",
> + tis->next_locty);
> +#endif
> +
> + /*
> + * Need to react differently depending on who's aborting now and
> + * which locality will become active afterwards.
> + */
> + if (tis->aborting_locty == tis->next_locty) {
> + tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY;
> + tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_READY;
> + tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY);
> + }
> +
> + /* locality after abort is another one than the current one */
> + tpm_tis_new_active_locality(s, tis->next_locty);
> +
> + tis->next_locty = TPM_TIS_NO_LOCALITY;
> + /* nobody's aborting a command anymore */
> + tis->aborting_locty = TPM_TIS_NO_LOCALITY;
> +}
> +
> +/* prepare aborting current command */
> +static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
> +{
> + TPMTISState *tis =&s->s.tis;
> + uint8_t busy_locty;
> +
> + tis->aborting_locty = locty;
> + tis->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< TPM_TIS_NUM_LOCALITIES; busy_locty++) {
> + if (tis->loc[busy_locty].state == TPM_TIS_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;
> + }
> + }
> +
> + tpm_tis_abort(s, locty);
> +}
> +
> +/*
> + * Callback from the TPM to indicate that the response was received.
> + */
> +static void tpm_tis_receive_cb(TPMState *s, uint8_t locty)
> +{
> + TPMTISState *tis =&s->s.tis;
> +
> + qemu_mutex_lock(&s->state_lock);
> +
> + tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE;
> + tis->loc[locty].state = TPM_TIS_STATE_COMPLETION;
> + tis->loc[locty].r_offset = 0;
> + tis->loc[locty].w_offset = 0;
> +
> + if (TPM_TIS_IS_VALID_LOCTY(tis->next_locty)) {
> + tpm_tis_abort(s, locty);
> + }
> +
> + qemu_cond_signal(&s->from_tpm_cond);
> +
> + qemu_mutex_unlock(&s->state_lock);
> +
> +#ifndef RAISE_STS_IRQ
> + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_DATA_AVAILABLE);
> +#else
> + tpm_tis_raise_irq(s, locty,
> + TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
> +#endif
> +}
> +
> +/*
> + * read a byte of response data
> + * call this with s->state_lock held
> + */
> +static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
> +{
> + TPMTISState *tis =&s->s.tis;
> + uint32_t ret = TPM_TIS_NO_DATA_BYTE;
> + uint16_t len;
> +
> + if ((tis->loc[locty].sts& TPM_TIS_STS_DATA_AVAILABLE)) {
> + len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
> +
> + ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++];
> + if (tis->loc[locty].r_offset>= len) {
> + /* got last byte */
> + tis->loc[locty].sts = TPM_TIS_STS_VALID;
> +#ifdef RAISE_STS_IRQ
> + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
> +#endif
> + }
> +#ifdef DEBUG_TIS
> + fprintf(stderr, "tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n",
> + ret, tis->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 uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
> + unsigned size)
> +{
> + TPMState *s = opaque;
> + TPMTISState *tis =&s->s.tis;
> + uint16_t offset = addr& 0xffc;
> + uint8_t shift = (addr& 0x3) * 8;
> + uint32_t val = 0xffffffff;
> + uint8_t locty = tpm_tis_locality_from_addr(addr);
> +
> + qemu_mutex_lock(&s->state_lock);
> +
> + if (s->be_driver->ops->had_startup_error(s->be_driver)) {
> + qemu_mutex_unlock(&s->state_lock);
> + return val;
> + }
> +
> + switch (offset) {
> + case TPM_TIS_REG_ACCESS:
> + /* never show the SEIZE flag even though we use it internally */
> + val = tis->loc[locty].access& ~TPM_TIS_ACCESS_SEIZE;
> + /* the pending flag is alawys calculated */
> + if (tpm_tis_check_request_use_except(s, locty)) {
> + val |= TPM_TIS_ACCESS_PENDING_REQUEST;
> + }
> + val |= !s->be_driver->ops->get_tpm_established_flag(s->be_driver);
> + break;
> + case TPM_TIS_REG_INT_ENABLE:
> + val = tis->loc[locty].inte;
> + break;
> + case TPM_TIS_REG_INT_VECTOR:
> + val = tis->irq_num;
> + break;
> + case TPM_TIS_REG_INT_STATUS:
> + val = tis->loc[locty].ints;
> + break;
> + case TPM_TIS_REG_INTF_CAPABILITY:
> + val = TPM_TIS_CAPABILITIES_SUPPORTED;
> + break;
> + case TPM_TIS_REG_STS:
> + if (tis->active_locty == locty) {
> + if ((tis->loc[locty].sts& TPM_TIS_STS_DATA_AVAILABLE)) {
> + val = (tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer)
> + - tis->loc[locty].r_offset)<< 8 | tis->loc[locty].sts;
> + } else {
> + val = (tis->loc[locty].w_buffer.size -
> + tis->loc[locty].w_offset)<< 8 | tis->loc[locty].sts;
> + }
> + }
> + break;
> + case TPM_TIS_REG_DATA_FIFO:
> + if (tis->active_locty == locty) {
> + switch (tis->loc[locty].state) {
> + case TPM_TIS_STATE_COMPLETION:
> + val = tpm_tis_data_read(s, locty);
> + break;
> + default:
> + val = TPM_TIS_NO_DATA_BYTE;
> + break;
> + }
> + }
> + break;
> + case TPM_TIS_REG_DID_VID:
> + val = (TPM_TIS_TPM_DID<< 16) | TPM_TIS_TPM_VID;
> + break;
> + case TPM_TIS_REG_RID:
> + val = TPM_TIS_TPM_RID;
> + break;
> + }
> +
> + qemu_mutex_unlock(&s->state_lock);
> +
> + if (shift) {
> + val>>= shift;
> + }
> +
> +#ifdef DEBUG_TIS
> + fprintf(stderr, "tpm_tis: read.%u(%08x) = %08x\n",
> + size, (int)addr, (uint32_t)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 tpm_tis_mmio_write_intern(void *opaque, target_phys_addr_t addr,
> + uint64_t val, unsigned size,
> + bool hw_access)
> +{
> + TPMState *s = opaque;
> + TPMTISState *tis =&s->s.tis;
> + uint16_t off = addr& 0xfff;
> + uint8_t locty = tpm_tis_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.%u(%08x) = %08x\n",
> + size, (int)addr, (uint32_t)val);
> +#endif
> +
> + qemu_mutex_lock(&s->state_lock);
> +
> + if (s->be_driver->ops->had_startup_error(s->be_driver)) {
> + qemu_mutex_unlock(&s->state_lock);
> + return;
> + }
> +
> + switch (off) {
> + case TPM_TIS_REG_ACCESS:
> +
> + if ((val& TPM_TIS_ACCESS_SEIZE)) {
> + val&= ~(TPM_TIS_ACCESS_REQUEST_USE |
> + TPM_TIS_ACCESS_ACTIVE_LOCALITY);
> + }
> +
> + active_locty = tis->active_locty;
> +
> + if ((val& TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
> + /* give up locality if currently owned */
> + if (tis->active_locty == locty) {
> +#ifdef DEBUG_TIS
> + fprintf(stderr, "tpm_tis: Releasing locality %d\n", locty);
> +#endif
> + uint8_t newlocty = TPM_TIS_NO_LOCALITY;
> + /* anybody wants the locality ? */
> + for (c = TPM_TIS_NUM_LOCALITIES - 1; c>= 0; c--) {
> + if ((tis->loc[c].access& TPM_TIS_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: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
> + "Next active locality: %d\n",
> + newlocty);
> +#endif
> + if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
> + set_new_locty = 0;
> + tpm_tis_prep_abort(s, locty, newlocty);
> + } else {
> + active_locty = TPM_TIS_NO_LOCALITY;
> + }
> + } else {
> + /* not currently the owner; clear a pending request */
> + tis->loc[locty].access&= ~TPM_TIS_ACCESS_REQUEST_USE;
> + }
> + }
> +
> + if ((val& TPM_TIS_ACCESS_BEEN_SEIZED)) {
> + tis->loc[locty].access&= ~TPM_TIS_ACCESS_BEEN_SEIZED;
> + }
> +
> + if ((val& TPM_TIS_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 ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty)&&
> + locty> tis->active_locty) ||
> + (!TPM_TIS_IS_VALID_LOCTY(tis->active_locty))) {
> +
> + /* already a pending SEIZE ? */
> + if ((tis->loc[locty].access& TPM_TIS_ACCESS_SEIZE)) {
> + break;
> + }
> +
> + /* check for ongoing seize by a higher locality */
> + for (l = locty + 1; l< TPM_TIS_NUM_LOCALITIES; l++) {
> + if ((tis->loc[l].access& TPM_TIS_ACCESS_SEIZE)) {
> + break;
> + }
> + }
> +
> + /* cancel any seize by a lower locality */
> + for (l = 0; l< locty - 1; l++) {
> + tis->loc[l].access&= ~TPM_TIS_ACCESS_SEIZE;
> + }
> +
> + tis->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
> +#ifdef DEBUG_TIS
> + fprintf(stderr, "tpm_tis: TPM_TIS_ACCESS_SEIZE: "
> + "Locality %d seized from locality %d\n",
> + locty, tis->active_locty);
> + fprintf(stderr,
> + "tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
> +#endif
> + set_new_locty = 0;
> + tpm_tis_prep_abort(s, tis->active_locty, locty);
> + break;
> + }
> + }
> +
> + if ((val& TPM_TIS_ACCESS_REQUEST_USE)) {
> + if (tis->active_locty != locty) {
> + if (TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) {
> + tis->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
> + } else {
> + /* no locality active -> make this one active now */
> + active_locty = locty;
> + }
> + }
> + }
> +
> + if (set_new_locty) {
> + tpm_tis_new_active_locality(s, active_locty);
> + }
> +
> + break;
> + case TPM_TIS_REG_INT_ENABLE:
> + if (tis->active_locty != locty) {
> + break;
> + }
> +
> + tis->loc[locty].inte = (val& (TPM_TIS_INT_ENABLED | (0x3<< 3) |
> + TPM_TIS_INTERRUPTS_SUPPORTED));
> + break;
> + case TPM_TIS_REG_INT_VECTOR:
> + /* hard wired -- ignore */
> + break;
> + case TPM_TIS_REG_INT_STATUS:
> + if (tis->active_locty != locty) {
> + break;
> + }
> +
> + /* clearing of interrupt flags */
> + if (((val& TPM_TIS_INTERRUPTS_SUPPORTED))&&
> + (tis->loc[locty].ints& TPM_TIS_INTERRUPTS_SUPPORTED)) {
> + tis->loc[locty].ints&= ~val;
> + if (tis->loc[locty].ints == 0) {
> + qemu_irq_lower(tis->irq);
> +#ifdef DEBUG_TIS
> + fprintf(stderr, "tpm_tis: Lowering IRQ\n");
> +#endif
> + }
> + }
> + tis->loc[locty].ints&= ~(val& TPM_TIS_INTERRUPTS_SUPPORTED);
> + break;
> + case TPM_TIS_REG_STS:
> + if (tis->active_locty != locty) {
> + break;
> + }
> +
> + val&= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
> + TPM_TIS_STS_RESPONSE_RETRY);
> +
> + if (val == TPM_TIS_STS_COMMAND_READY) {
> + switch (tis->loc[locty].state) {
> +
> + case TPM_TIS_STATE_READY:
> + tis->loc[locty].w_offset = 0;
> + tis->loc[locty].r_offset = 0;
> + break;
> +
> + case TPM_TIS_STATE_IDLE:
> + tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
> + tis->loc[locty].state = TPM_TIS_STATE_READY;
> + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
> + break;
> +
> + case TPM_TIS_STATE_EXECUTION:
> + case TPM_TIS_STATE_RECEPTION:
> + /* abort currently running command */
> +#ifdef DEBUG_TIS
> + fprintf(stderr, "tpm_tis: %s: Initiating abort.\n",
> + __func__);
> +#endif
> + tpm_tis_prep_abort(s, locty, locty);
> + break;
> +
> + case TPM_TIS_STATE_COMPLETION:
> + tis->loc[locty].w_offset = 0;
> + tis->loc[locty].r_offset = 0;
> + /* shortcut to ready state with C/R set */
> + tis->loc[locty].state = TPM_TIS_STATE_READY;
> + if (!(tis->loc[locty].sts& TPM_TIS_STS_COMMAND_READY)) {
> + tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
> + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
> + }
> + break;
> +
> + }
> + } else if (val == TPM_TIS_STS_TPM_GO) {
> + switch (tis->loc[locty].state) {
> + case TPM_TIS_STATE_RECEPTION:
> + tpm_tis_tpm_send(s, locty);
> + break;
> + default:
> + /* ignore */
> + break;
> + }
> + } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
> + switch (tis->loc[locty].state) {
> + case TPM_TIS_STATE_COMPLETION:
> + tis->loc[locty].r_offset = 0;
> + tis->loc[locty].sts = TPM_TIS_STS_VALID |
> + TPM_TIS_STS_DATA_AVAILABLE;
> + break;
> + default:
> + /* ignore */
> + break;
> + }
> + }
> + break;
> + case TPM_TIS_REG_DATA_FIFO:
> + /* data fifo */
> + if (tis->active_locty != locty) {
> + break;
> + }
> +
> + if (tis->loc[locty].state == TPM_TIS_STATE_IDLE ||
> + tis->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
> + tis->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
> + /* drop the byte */
> + } else {
> +#ifdef DEBUG_TIS
> + fprintf(stderr, "tpm_tis: Byte to send to TPM: %02x\n",
> + (uint8_t)val);
> +#endif
> + if (tis->loc[locty].state == TPM_TIS_STATE_READY) {
> + tis->loc[locty].state = TPM_TIS_STATE_RECEPTION;
> + tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID;
> + }
> +
> + if ((tis->loc[locty].sts& TPM_TIS_STS_EXPECT)) {
> + if (tis->loc[locty].w_offset< tis->loc[locty].w_buffer.size) {
> + tis->loc[locty].w_buffer.
> + buffer[tis->loc[locty].w_offset++] = (uint8_t)val;
> + } else {
> + tis->loc[locty].sts = TPM_TIS_STS_VALID;
> + }
> + }
> +
> + /* check for complete packet */
> + if (tis->loc[locty].w_offset> 5&&
> + (tis->loc[locty].sts& TPM_TIS_STS_EXPECT)) {
> + /* we have a packet length - see if we have all of it */
> +#ifdef RAISE_STS_IRQ
> + bool needIrq = !(tis->loc[locty].sts& TPM_TIS_STS_VALID);
> +#endif
> + len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
> + if (len> tis->loc[locty].w_offset) {
> + tis->loc[locty].sts = TPM_TIS_STS_EXPECT |
> + TPM_TIS_STS_VALID;
> + } else {
> + /* packet complete */
> + tis->loc[locty].sts = TPM_TIS_STS_VALID;
> + }
> +#ifdef RAISE_STS_IRQ
> + if (needIrq) {
> + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
> + }
> +#endif
> + }
> + }
> + break;
> + }
> +
> + qemu_mutex_unlock(&s->state_lock);
> +}
> +
> +static void tpm_tis_mmio_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val, unsigned size)
> +{
> + return tpm_tis_mmio_write_intern(opaque, addr, val, size, false);
> +}
> +
> +static const MemoryRegionOps tpm_tis_memory_ops = {
> + .read = tpm_tis_mmio_read,
> + .write = tpm_tis_mmio_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 1,
> + .max_access_size = 4,
> + },
> +};
> +
> +static int tpm_tis_do_startup_tpm(TPMState *s)
> +{
> + return s->be_driver->ops->startup_tpm(s->be_driver);
> +}
> +
> +/*
> + * This function is called when the machine starts, resets or due to
> + * S3 resume.
> + */
> +static void tpm_tis_reset(DeviceState *d)
> +{
> + TPMState *s = container_of(d, TPMState, busdev.qdev);
> + TPMTISState *tis =&s->s.tis;
> + int c;
> +
> + s->tpm_initialized = false;
> +
> + s->be_driver->ops->reset(s->be_driver);
> +
> + tis->active_locty = TPM_TIS_NO_LOCALITY;
> + tis->next_locty = TPM_TIS_NO_LOCALITY;
> + tis->aborting_locty = TPM_TIS_NO_LOCALITY;
> +
> + for (c = 0; c< TPM_TIS_NUM_LOCALITIES; c++) {
> + tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
> + tis->loc[c].sts = 0;
> + tis->loc[c].inte = (1<< 3);
> + tis->loc[c].ints = 0;
> + tis->loc[c].state = TPM_TIS_STATE_IDLE;
> +
> + tis->loc[c].w_offset = 0;
> + s->be_driver->ops->realloc_buffer(&tis->loc[c].w_buffer);
> + tis->loc[c].r_offset = 0;
> + s->be_driver->ops->realloc_buffer(&tis->loc[c].r_buffer);
> + }
> +
> + tpm_tis_do_startup_tpm(s);
> +}
> +
> +static int tpm_tis_init(ISADevice *dev)
> +{
> + TPMState *s = DO_UPCAST(TPMState, busdev, dev);
> + TPMTISState *tis =&s->s.tis;
> + int rc;
> +
> + qemu_mutex_init(&s->state_lock);
> + qemu_cond_init(&s->from_tpm_cond);
> + qemu_cond_init(&s->to_tpm_cond);
> +
> + s->be_driver = qemu_find_tpm(s->backend);
> + if (!s->be_driver) {
> + error_report("tpm_tis: backend driver with id %s could not be "
> + "found.n\n", s->backend);
> + return -1;
> + }
> +
> + s->be_driver->fe_model = "tpm-tis";
> +
> + if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) {
> + return -1;
> + }
> +
> + isa_init_irq(dev,&tis->irq, tis->irq_num);
> +
> + memory_region_init_io(&s->mmio,&tpm_tis_memory_ops, s, "tpm-tis-mmio",
> + 0x1000 * TPM_TIS_NUM_LOCALITIES);
> + memory_region_add_subregion(get_system_memory(), TPM_TIS_ADDR_BASE,
> +&s->mmio);
> +
> + rc = tpm_tis_do_startup_tpm(s);
> + if (rc != 0) {
> + goto err_destroy_memory;
> + }
> +
> + return 0;
> +
> + err_destroy_memory:
> + memory_region_destroy(&s->mmio);
> +
> + return -1;
> +}
> +
> +static const VMStateDescription vmstate_tpm_tis = {
> + .name = "tpm",
> + .unmigratable = 1,
> +};
> +
> +static ISADeviceInfo tpm_tis_device_info = {
> + .init = tpm_tis_init,
> + .qdev.name = "tpm-tis",
> + .qdev.size = sizeof(TPMState),
> + .qdev.vmsd =&vmstate_tpm_tis,
> + .qdev.reset = tpm_tis_reset,
> + .qdev.props = (Property[]) {
> + DEFINE_PROP_UINT32("irq", TPMState,
> + s.tis.irq_num, TPM_TIS_IRQ),
> + DEFINE_PROP_STRING("tpmdev", TPMState, backend),
> + 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] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2011-12-12 23:23 ` Anthony Liguori
@ 2011-12-12 23:54 ` Stefan Berger
0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 23:54 UTC (permalink / raw)
To: Anthony Liguori; +Cc: mst, qemu-devel, andreas.niederl
On 12/12/2011 06:23 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, Stefan Berger wrote:
>> This patch adds the main code of the TPM frontend driver, the TPM TIS
>> interface, to Qemu. The code is largely based on the 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>
[...]
>> +
>> +/*
>> + * Send a TPM request.
>> + * Call this with the state_lock held so we can sync with the receive
>> + * callback.
>> + */
>> +static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
>> +{
>> + TPMTISState *tis =&s->s.tis;
>> +#ifdef DEBUG_TIS
>> + tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
>> +#endif
>> + s->command_locty = locty;
>> + s->cmd_locty =&tis->loc[locty];
>> +
>> + /* w_offset serves as length indicator for length of data;
>> + it's reset when the response comes back */
>> + tis->loc[locty].state = TPM_TIS_STATE_EXECUTION;
>> + tis->loc[locty].sts&= ~TPM_TIS_STS_EXPECT;
>> +
>> + s->to_tpm_execute = true;
>> + qemu_cond_signal(&s->to_tpm_cond);
>
> The locking seems to presume that the device model is re-entrant which
> it's not today. Am I missing something here?
>
The TPM TIS frontend communicates with the TPM backend via a condition
notifying it when a complete buffer with a TPM request has been
received. The TPM backend is running as a thread, created via
qemu_thread_create(). This is the design that was driven by the
libtpms-based implementation.
Stefan
^ permalink raw reply [flat|nested] 29+ messages in thread
* [Qemu-devel] [PATCH V13 3/7] Add a debug register
2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 1/7] Support for TPM command line options Stefan Berger
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code Stefan Berger
` (4 subsequent siblings)
7 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 UTC (permalink / raw)
To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst
This patch uses the possibility to add a vendor-specific register and
adds a debug register useful for dumping the TIS's internal state. This
register is only active in a debug build (#define DEBUG_TIS).
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
v9:
- prefixing all function with tpm_tis_ and all constants with TPM_TIS_
v3:
- all output goes to stderr
---
hw/tpm_tis.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 73 insertions(+), 0 deletions(-)
diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
index 34d0043..1fd105e 100644
--- a/hw/tpm_tis.c
+++ b/hw/tpm_tis.c
@@ -47,6 +47,9 @@
#define TPM_TIS_REG_DID_VID 0xf00
#define TPM_TIS_REG_RID 0xf04
+/* vendor-specific registers */
+#define TPM_TIS_REG_DEBUG 0xf90
+
#define TPM_TIS_STS_VALID (1 << 7)
#define TPM_TIS_STS_COMMAND_READY (1 << 6)
#define TPM_TIS_STS_TPM_GO (1 << 5)
@@ -92,6 +95,11 @@
#define TPM_TIS_NO_DATA_BYTE 0xff
+/* local prototypes */
+static uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
+ unsigned size);
+
+
#ifdef DEBUG_TIS
static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
{
@@ -319,6 +327,66 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
return ret;
}
+#ifdef DEBUG_TIS
+static void tpm_tis_dump_state(void *opaque, target_phys_addr_t addr)
+{
+ static const unsigned regs[] = {
+ TPM_TIS_REG_ACCESS,
+ TPM_TIS_REG_INT_ENABLE,
+ TPM_TIS_REG_INT_VECTOR,
+ TPM_TIS_REG_INT_STATUS,
+ TPM_TIS_REG_INTF_CAPABILITY,
+ TPM_TIS_REG_STS,
+ TPM_TIS_REG_DID_VID,
+ TPM_TIS_REG_RID,
+ 0xfff};
+ int idx;
+ uint8_t locty = tpm_tis_locality_from_addr(addr);
+ target_phys_addr_t base = addr & ~0xfff;
+ TPMState *s = opaque;
+ TPMTISState *tis = &s->s.tis;
+
+ fprintf(stderr,
+ "tpm_tis: active locality : %d\n"
+ "tpm_tis: state of locality %d : %d\n"
+ "tpm_tis: register dump:\n",
+ tis->active_locty,
+ locty, tis->loc[locty].state);
+
+ for (idx = 0; regs[idx] != 0xfff; idx++) {
+ fprintf(stderr, "tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
+ (uint32_t)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
+ }
+
+ fprintf(stderr,
+ "tpm_tis: read offset : %d\n"
+ "tpm_tis: result buffer : ",
+ tis->loc[locty].r_offset);
+ for (idx = 0;
+ idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
+ idx++) {
+ fprintf(stderr, "%c%02x%s",
+ tis->loc[locty].r_offset == idx ? '>' : ' ',
+ tis->loc[locty].r_buffer.buffer[idx],
+ ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
+ }
+ fprintf(stderr,
+ "\n"
+ "tpm_tis: write offset : %d\n"
+ "tpm_tis: request buffer: ",
+ tis->loc[locty].w_offset);
+ for (idx = 0;
+ idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
+ idx++) {
+ fprintf(stderr, "%c%02x%s",
+ tis->loc[locty].w_offset == idx ? '>' : ' ',
+ tis->loc[locty].w_buffer.buffer[idx],
+ ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
+ }
+ fprintf(stderr, "\n");
+}
+#endif
+
/*
* Read a register of the TIS interface
* See specs pages 33-63 for description of the registers
@@ -391,6 +459,11 @@ static uint64_t tpm_tis_mmio_read(void *opaque, target_phys_addr_t addr,
case TPM_TIS_REG_RID:
val = TPM_TIS_TPM_RID;
break;
+#ifdef DEBUG_TIS
+ case TPM_TIS_REG_DEBUG:
+ tpm_tis_dump_state(opaque, addr);
+ break;
+#endif
}
qemu_mutex_unlock(&s->state_lock);
--
1.7.6.4
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code
2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (2 preceding siblings ...)
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 3/7] Add a debug register Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
2011-12-12 23:24 ` Anthony Liguori
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
` (3 subsequent siblings)
7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 UTC (permalink / raw)
To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst
Build the TPM frontend code that has been added so far.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
Makefile.target | 1 +
configure | 11 +++++++++++
2 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/Makefile.target b/Makefile.target
index 39b2e5a..37d5d10 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -205,6 +205,7 @@ obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o
obj-$(CONFIG_KVM) += kvm.o kvm-all.o
obj-$(CONFIG_NO_KVM) += kvm-stub.o
obj-y += memory.o
+obj-$(CONFIG_TPM) += tpm.o tpm_tis.o
LIBS+=-lz
QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
diff --git a/configure b/configure
index cc5ae87..385feb4 100755
--- a/configure
+++ b/configure
@@ -185,6 +185,7 @@ opengl=""
zlib="yes"
guest_agent="yes"
libiscsi=""
+tpm="no"
# parse CC options first
for opt do
@@ -784,6 +785,8 @@ for opt do
;;
--disable-guest-agent) guest_agent="no"
;;
+ --enable-tpm) tpm="yes"
+ ;;
*) echo "ERROR: unknown option $opt"; show_help="yes"
;;
esac
@@ -1070,6 +1073,7 @@ echo " --disable-usb-redir disable usb network redirection support"
echo " --enable-usb-redir enable usb network redirection support"
echo " --disable-guest-agent disable building of the QEMU Guest Agent"
echo " --enable-guest-agent enable building of the QEMU Guest Agent"
+echo " --enable-tpm enable TPM support"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@@ -2845,6 +2849,7 @@ echo "usb net redir $usb_redir"
echo "OpenGL support $opengl"
echo "libiscsi support $libiscsi"
echo "build guest agent $guest_agent"
+echo "TPM support $tpm"
if test "$sdl_too_old" = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3714,6 +3719,12 @@ if test "$gprof" = "yes" ; then
fi
fi
+if test "$tpm" = "yes"; then
+ if test "$target_softmmu" = "yes" ; then
+ echo "CONFIG_TPM=y" >> $config_host_mak
+ fi
+fi
+
if test "$ARCH" = "tci"; then
linker_script=""
else
--
1.7.6.4
^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code Stefan Berger
@ 2011-12-12 23:24 ` Anthony Liguori
2011-12-12 23:56 ` Stefan Berger
0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:24 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl
On 12/12/2011 01:12 PM, Stefan Berger wrote:
> Build the TPM frontend code that has been added so far.
>
> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
> ---
> Makefile.target | 1 +
> configure | 11 +++++++++++
> 2 files changed, 12 insertions(+), 0 deletions(-)
>
> diff --git a/Makefile.target b/Makefile.target
> index 39b2e5a..37d5d10 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -205,6 +205,7 @@ obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o
> obj-$(CONFIG_KVM) += kvm.o kvm-all.o
> obj-$(CONFIG_NO_KVM) += kvm-stub.o
> obj-y += memory.o
> +obj-$(CONFIG_TPM) += tpm.o tpm_tis.o
> LIBS+=-lz
>
> QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
> diff --git a/configure b/configure
> index cc5ae87..385feb4 100755
> --- a/configure
> +++ b/configure
> @@ -185,6 +185,7 @@ opengl=""
> zlib="yes"
> guest_agent="yes"
> libiscsi=""
> +tpm="no"
Please probe instead of requiring it to be explicitly enabled.
Regards,
Anthony Liguori
>
> # parse CC options first
> for opt do
> @@ -784,6 +785,8 @@ for opt do
> ;;
> --disable-guest-agent) guest_agent="no"
> ;;
> + --enable-tpm) tpm="yes"
> + ;;
> *) echo "ERROR: unknown option $opt"; show_help="yes"
> ;;
> esac
> @@ -1070,6 +1073,7 @@ echo " --disable-usb-redir disable usb network redirection support"
> echo " --enable-usb-redir enable usb network redirection support"
> echo " --disable-guest-agent disable building of the QEMU Guest Agent"
> echo " --enable-guest-agent enable building of the QEMU Guest Agent"
> +echo " --enable-tpm enable TPM support"
> echo ""
> echo "NOTE: The object files are built at the place where configure is launched"
> exit 1
> @@ -2845,6 +2849,7 @@ echo "usb net redir $usb_redir"
> echo "OpenGL support $opengl"
> echo "libiscsi support $libiscsi"
> echo "build guest agent $guest_agent"
> +echo "TPM support $tpm"
>
> if test "$sdl_too_old" = "yes"; then
> echo "-> Your SDL version is too old - please upgrade to have SDL support"
> @@ -3714,6 +3719,12 @@ if test "$gprof" = "yes" ; then
> fi
> fi
>
> +if test "$tpm" = "yes"; then
> + if test "$target_softmmu" = "yes" ; then
> + echo "CONFIG_TPM=y">> $config_host_mak
> + fi
> +fi
> +
> if test "$ARCH" = "tci"; then
> linker_script=""
> else
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code
2011-12-12 23:24 ` Anthony Liguori
@ 2011-12-12 23:56 ` Stefan Berger
0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 23:56 UTC (permalink / raw)
To: Anthony Liguori; +Cc: qemu-devel, mst
On 12/12/2011 06:24 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, Stefan Berger wrote:
>> Build the TPM frontend code that has been added so far.
>>
>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>> ---
>> Makefile.target | 1 +
>> configure | 11 +++++++++++
>> 2 files changed, 12 insertions(+), 0 deletions(-)
>>
>> diff --git a/Makefile.target b/Makefile.target
>> index 39b2e5a..37d5d10 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -205,6 +205,7 @@ obj-$(CONFIG_REALLY_VIRTFS) +=
>> 9pfs/virtio-9p-device.o
>> obj-$(CONFIG_KVM) += kvm.o kvm-all.o
>> obj-$(CONFIG_NO_KVM) += kvm-stub.o
>> obj-y += memory.o
>> +obj-$(CONFIG_TPM) += tpm.o tpm_tis.o
>> LIBS+=-lz
>>
>> QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
>> diff --git a/configure b/configure
>> index cc5ae87..385feb4 100755
>> --- a/configure
>> +++ b/configure
>> @@ -185,6 +185,7 @@ opengl=""
>> zlib="yes"
>> guest_agent="yes"
>> libiscsi=""
>> +tpm="no"
>
> Please probe instead of requiring it to be explicitly enabled.
At this point there is not much to probe. We're only building the
front-end with no backend, i.e., passthrough, being built in.
Stefan
>
> Regards,
>
> Anthony Liguori
>
>>
>> # parse CC options first
>> for opt do
>> @@ -784,6 +785,8 @@ for opt do
>> ;;
>> --disable-guest-agent) guest_agent="no"
>> ;;
>> + --enable-tpm) tpm="yes"
>> + ;;
>> *) echo "ERROR: unknown option $opt"; show_help="yes"
>> ;;
>> esac
>> @@ -1070,6 +1073,7 @@ echo " --disable-usb-redir disable usb
>> network redirection support"
>> echo " --enable-usb-redir enable usb network redirection
>> support"
>> echo " --disable-guest-agent disable building of the QEMU Guest
>> Agent"
>> echo " --enable-guest-agent enable building of the QEMU Guest
>> Agent"
>> +echo " --enable-tpm enable TPM support"
>> echo ""
>> echo "NOTE: The object files are built at the place where configure
>> is launched"
>> exit 1
>> @@ -2845,6 +2849,7 @@ echo "usb net redir $usb_redir"
>> echo "OpenGL support $opengl"
>> echo "libiscsi support $libiscsi"
>> echo "build guest agent $guest_agent"
>> +echo "TPM support $tpm"
>>
>> if test "$sdl_too_old" = "yes"; then
>> echo "-> Your SDL version is too old - please upgrade to have SDL
>> support"
>> @@ -3714,6 +3719,12 @@ if test "$gprof" = "yes" ; then
>> fi
>> fi
>>
>> +if test "$tpm" = "yes"; then
>> + if test "$target_softmmu" = "yes" ; then
>> + echo "CONFIG_TPM=y">> $config_host_mak
>> + fi
>> +fi
>> +
>> if test "$ARCH" = "tci"; then
>> linker_script=""
>> else
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation
2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (3 preceding siblings ...)
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 4/7] Build the TPM frontend code Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
2011-12-12 23:27 ` Anthony Liguori
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
` (2 subsequent siblings)
7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 UTC (permalink / raw)
To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst
>From Andreas Niederl's original posting with adaptations where necessary:
This patch is based of off version 9 of Stefan Berger's patch series
"Qemu Trusted Platform Module (TPM) integration"
and adds a new backend driver for it.
This patch adds a passthrough backend driver for passing commands sent to the
emulated TPM device directly to a TPM device opened on the host machine.
Thus it is possible to use a hardware TPM device in a system running on QEMU,
providing the ability to access a TPM in a special state (e.g. after a Trusted
Boot).
This functionality is being used in the acTvSM Trusted Virtualization Platform
which is available on [1].
Usage example:
qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
-device tpm-tis,tpmdev=tpm0 \
-cdrom test.iso -boot d
Some notes about the host TPM:
The TPM needs to be enabled and activated. If that's not the case one
has to go through the BIOS/UEFI and enable and activate that TPM for TPM
commands to work as expected.
It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
command line or 'modprobe tpm_tis force=1' in case of using it as a module.
Regards,
Andreas Niederl, Stefan Berger
[1] http://trustedjava.sourceforge.net/
Signed-off-by: Andreas Niederl <andreas.niederl@iaik.tugraz.at>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
Changes for v12:
- check size indicator in response from TPM to match that of the received
packet
Changes for v10:
- clarified documentation
- using /dev/tpm0 as default device if path option is not given
- refactored code handling 'device' option into its own function
- fixed name of structure to TPMPassthruThreadParams
- only add tpm_passthrough_driver to collection of backends if
it is compiled on the host
Changes for v9:
- prefixing of all functions and variables with tpm_passthrough_
- cleanup of all variables into a structure that is now accessed
using TPMBackend (tb->s.tpm_pt)
- build it on Linux machines
- added function to test whether given device is a TPM and refuse
startup if it is not
---
Makefile.target | 1 +
configure | 3 +
hw/tpm_passthrough.c | 477 ++++++++++++++++++++++++++++++++++++++++++++++++++
qemu-options.hx | 33 ++++
tpm.c | 23 +++
tpm.h | 34 ++++
6 files changed, 571 insertions(+), 0 deletions(-)
create mode 100644 hw/tpm_passthrough.c
diff --git a/Makefile.target b/Makefile.target
index 37d5d10..4fc96e6 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -206,6 +206,7 @@ obj-$(CONFIG_KVM) += kvm.o kvm-all.o
obj-$(CONFIG_NO_KVM) += kvm-stub.o
obj-y += memory.o
obj-$(CONFIG_TPM) += tpm.o tpm_tis.o
+obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
LIBS+=-lz
QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
diff --git a/configure b/configure
index 385feb4..25995bc 100755
--- a/configure
+++ b/configure
@@ -3721,6 +3721,9 @@ fi
if test "$tpm" = "yes"; then
if test "$target_softmmu" = "yes" ; then
+ if test "$linux" = "yes" ; then
+ echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
+ fi
echo "CONFIG_TPM=y" >> $config_host_mak
fi
fi
diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
new file mode 100644
index 0000000..f9cfe3d
--- /dev/null
+++ b/hw/tpm_passthrough.c
@@ -0,0 +1,477 @@
+/*
+ * passthrough TPM driver
+ *
+ * Copyright (c) 2010, 2011 IBM Corporation
+ * Authors:
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * Copyright (C) 2011 IAIK, Graz University of Technology
+ * Author: Andreas Niederl
+ *
+ * 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 "qemu-error.h"
+#include "tpm.h"
+#include "hw/hw.h"
+#include "hw/tpm_tis.h"
+#include "hw/pc.h"
+
+/* #define DEBUG_TPM */
+
+/* data structures */
+
+typedef struct TPMPassthruThreadParams {
+ TPMState *tpm_state;
+
+ TPMRecvDataCB *recv_data_callback;
+ TPMBackend *tb;
+} TPMPassthruThreadParams;
+
+struct TPMPassthruState {
+ QemuThread thread;
+ bool thread_terminate;
+ bool thread_running;
+
+ TPMPassthruThreadParams tpm_thread_params;
+
+ char tpm_dev[64];
+ int tpm_fd;
+ bool had_startup_error;
+};
+
+#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
+
+/* borrowed from qemu-char.c */
+static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
+{
+ int ret, len1;
+
+ len1 = len;
+ while (len1 > 0) {
+ ret = write(fd, buf, len1);
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ return -1;
+ }
+ } else if (ret == 0) {
+ break;
+ } else {
+ buf += ret;
+ len1 -= ret;
+ }
+ }
+ return len - len1;
+}
+
+static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
+{
+ int ret, len1;
+ uint8_t *buf1;
+
+ len1 = len;
+ buf1 = buf;
+ while ((len1 > 0) && (ret = read(fd, buf1, len1)) != 0) {
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ return -1;
+ }
+ } else {
+ buf1 += ret;
+ len1 -= ret;
+ }
+ }
+ return len - len1;
+}
+
+static void *tpm_passthrough_main_loop(void *d)
+{
+ TPMPassthruThreadParams *thr_parms = d;
+ TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
+ uint32_t in_len, out_len;
+ uint8_t *in, *out;
+ uint8_t locty;
+ TPMLocality *cmd_locty;
+ int ret;
+
+#ifdef DEBUG_TPM
+ fprintf(stderr, "tpm_passthrough: THREAD IS STARTING\n");
+#endif
+
+ /* start command processing */
+ while (!tpm_pt->thread_terminate) {
+ /* receive and handle commands */
+ in_len = 0;
+ do {
+#ifdef DEBUG_TPM
+ fprintf(stderr, "tpm_passthrough: waiting for commands...\n");
+#endif
+
+ if (tpm_pt->thread_terminate) {
+ break;
+ }
+
+ qemu_mutex_lock(&thr_parms->tpm_state->state_lock);
+
+ /* in case we were to slow and missed the signal, the
+ to_tpm_execute boolean tells us about a pending command */
+ if (!thr_parms->tpm_state->to_tpm_execute) {
+ qemu_cond_wait(&thr_parms->tpm_state->to_tpm_cond,
+ &thr_parms->tpm_state->state_lock);
+ }
+
+ thr_parms->tpm_state->to_tpm_execute = false;
+
+ qemu_mutex_unlock(&thr_parms->tpm_state->state_lock);
+
+ if (tpm_pt->thread_terminate) {
+ break;
+ }
+
+ locty = thr_parms->tpm_state->command_locty;
+
+ cmd_locty = thr_parms->tpm_state->cmd_locty;
+
+ in = cmd_locty->w_buffer.buffer;
+ in_len = cmd_locty->w_offset;
+ out = cmd_locty->r_buffer.buffer;
+ out_len = cmd_locty->r_buffer.size;
+
+ ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
+ if (ret < 0) {
+ error_report("tpm_passthrough: error while transmitting data "
+ "to host tpm: %s (%i)\n",
+ strerror(errno), errno);
+ tpm_write_std_fatal_error_response(out, out_len, in, in_len);
+ goto send_resp;
+ }
+
+ ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
+ if (ret < 0) {
+ error_report("tpm_passthrough: error while reading data from "
+ "host tpm : %s (%i)\n",
+ strerror(errno), errno);
+ tpm_write_std_fatal_error_response(out, out_len, in, in_len);
+ } else if (ret < sizeof(struct tpm_resp_hdr) ||
+ tpm_tis_get_size_from_buffer(&cmd_locty->r_buffer) !=
+ ret) {
+ error_report("tpm_passthrough: received invalid response "
+ "packet from TPM\n");
+ tpm_write_std_fatal_error_response(out, out_len, in, in_len);
+ }
+
+send_resp:
+ thr_parms->recv_data_callback(thr_parms->tpm_state, locty);
+ } while (in_len > 0);
+ }
+
+#ifdef DEBUG_TPM
+ fprintf(stderr, "tpm_passthrough: THREAD IS ENDING\n");
+#endif
+
+ tpm_pt->thread_running = false;
+
+ return NULL;
+}
+
+static void tpm_passthrough_terminate_tpm_thread(TPMBackend *tb)
+{
+ TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+ if (!tpm_pt->thread_running) {
+ return;
+ }
+
+#if defined DEBUG_TPM
+ fprintf(stderr, "tpm_passthrough: TERMINATING RUNNING TPM THREAD\n");
+#endif
+
+ if (!tpm_pt->thread_terminate) {
+ tpm_pt->thread_terminate = true;
+
+ qemu_mutex_lock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
+ qemu_cond_signal(&tpm_pt->tpm_thread_params.tpm_state->to_tpm_cond);
+ qemu_mutex_unlock(&tpm_pt->tpm_thread_params.tpm_state->state_lock);
+
+ while (tpm_pt->thread_running) {
+ usleep(100000);
+ }
+ memset(&tpm_pt->thread, 0, sizeof(tpm_pt->thread));
+ }
+}
+
+/**
+ * Start the TPM (thread). If it had been started before, then terminate
+ * and start it again.
+ */
+static int tpm_passthrough_do_startup_tpm(TPMBackend *tb)
+{
+ TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+ /* terminate a running TPM */
+ tpm_passthrough_terminate_tpm_thread(tb);
+
+ /* reset the flag so the thread keeps on running */
+ tpm_pt->thread_terminate = false;
+
+ qemu_thread_create(&tpm_pt->thread, tpm_passthrough_main_loop,
+ &tpm_pt->tpm_thread_params);
+
+ tpm_pt->thread_running = true;
+
+ return 0;
+}
+
+static int tpm_passthrough_startup_tpm(TPMBackend *tb)
+{
+ TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+ int rc;
+
+ rc = tpm_passthrough_do_startup_tpm(tb);
+ if (rc) {
+ tpm_pt->had_startup_error = true;
+ }
+ return rc;
+}
+
+static void tpm_passthrough_reset(TPMBackend *tb)
+{
+ TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+#if defined DEBUG_TPM
+ fprintf(stderr, "tpm_passthrough: CALL TO TPM_RESET!\n");
+#endif
+
+ tpm_passthrough_terminate_tpm_thread(tb);
+
+ tpm_pt->had_startup_error = false;
+}
+
+static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
+ TPMRecvDataCB *recv_data_cb)
+{
+ TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+ tpm_pt->tpm_thread_params.tpm_state = s;
+ tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb;
+ tpm_pt->tpm_thread_params.tb = tb;
+
+ tpm_pt->thread_running = false;
+
+ return 0;
+}
+
+static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
+{
+ return false;
+}
+
+static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
+{
+ TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+ return tpm_pt->had_startup_error;
+}
+
+static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
+{
+ size_t wanted_size = 4096; /* Linux tpm.c buffer size */
+
+ if (sb->size != wanted_size) {
+ sb->buffer = g_realloc(sb->buffer, wanted_size);
+ if (sb->buffer != NULL) {
+ sb->size = wanted_size;
+ } else {
+ sb->size = 0;
+ }
+ }
+ return sb->size;
+}
+
+static const char *tpm_passthrough_create_desc(void)
+{
+ return "Passthrough TPM backend driver";
+}
+
+/* A basic test of a TPM device. We expect a well formatted response header
+ * (error response is fine) within one second.
+ */
+static int tpm_passthrough_test_tpmdev(int fd)
+{
+ struct tpm_req_hdr req = {
+ .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
+ .len = cpu_to_be32(sizeof(req)),
+ .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
+ };
+ struct tpm_resp_hdr *resp;
+ fd_set readfds;
+ int n;
+ struct timeval tv = {
+ .tv_sec = 1,
+ .tv_usec = 0,
+ };
+ unsigned char buf[1024];
+
+ n = write(fd, &req, sizeof(req));
+ if (n < 0) {
+ return errno;
+ }
+ if (n != sizeof(req)) {
+ return EFAULT;
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ /* wait for a second */
+ n = select(fd + 1, &readfds, NULL, NULL, &tv);
+ if (n != 1) {
+ return errno;
+ }
+
+ n = read(fd, &buf, sizeof(buf));
+ if (n < sizeof(struct tpm_resp_hdr)) {
+ return EFAULT;
+ }
+
+ resp = (struct tpm_resp_hdr *)buf;
+ /* check the header */
+ if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND ||
+ be32_to_cpu(resp->len) != n) {
+ return EBADMSG;
+ }
+
+ return 0;
+}
+
+static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
+{
+ const char *value;
+ char buf[64];
+ int n;
+
+ value = qemu_opt_get(opts, "path");
+ if (!value) {
+ value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+ }
+
+ n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
+ "%s", value);
+
+ if (n >= sizeof(tb->s.tpm_pt->tpm_dev)) {
+ error_report("TPM device path is too long.\n");
+ goto err_exit;
+ }
+
+ snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
+
+ tb->parameters = g_strdup(buf);
+
+ if (tb->parameters == NULL) {
+ return 1;
+ }
+
+ tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
+ if (tb->s.tpm_pt->tpm_fd < 0) {
+ error_report("Cannot access TPM device using '%s'.\n",
+ tb->s.tpm_pt->tpm_dev);
+ goto err_exit;
+ }
+
+ if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) {
+ error_report("'%s' is not a TPM device.\n",
+ tb->s.tpm_pt->tpm_dev);
+ goto err_close_tpmdev;
+ }
+
+ return 0;
+
+ err_close_tpmdev:
+ close(tb->s.tpm_pt->tpm_fd);
+ tb->s.tpm_pt->tpm_fd = -1;
+
+ err_exit:
+ g_free(tb->parameters);
+ tb->parameters = NULL;
+
+ return 1;
+}
+
+
+static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
+{
+ TPMBackend *tb;
+
+ tb = g_malloc0(sizeof(TPMBackend));
+ if (tb == NULL) {
+ error_report("tpm_passthrough: Could not allocate memory.\n");
+ return NULL;
+ }
+
+ tb->s.tpm_pt = g_malloc0(sizeof(TPMPassthruState));
+ if (tb->s.tpm_pt == NULL) {
+ error_report("tpm_passthrough: Could not allocate memory.\n");
+ g_free(tb);
+ return NULL;
+ }
+
+ tb->id = g_strdup(id);
+ if (tb->id == NULL) {
+ error_report("tpm_passthrough: Could not allocate memory.\n");
+ goto err_exit;
+ }
+
+ tb->ops = &tpm_passthrough_driver;
+
+ if (tpm_passthrough_handle_device_opts(opts, tb)) {
+ goto err_exit;
+ }
+
+ return tb;
+
+err_exit:
+ g_free(tb->id);
+ g_free(tb->s.tpm_pt);
+ g_free(tb);
+
+ return NULL;
+}
+
+static void tpm_passthrough_destroy(TPMBackend *tb)
+{
+ TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+ tpm_passthrough_terminate_tpm_thread(tb);
+
+ close(tpm_pt->tpm_fd);
+
+ g_free(tb->id);
+ g_free(tb->parameters);
+ g_free(tb->s.tpm_pt);
+ g_free(tb);
+}
+
+const TPMDriverOps tpm_passthrough_driver = {
+ .id = "passthrough",
+ .desc = tpm_passthrough_create_desc,
+ .create = tpm_passthrough_create,
+ .destroy = tpm_passthrough_destroy,
+ .init = tpm_passthrough_init,
+ .startup_tpm = tpm_passthrough_startup_tpm,
+ .realloc_buffer = tpm_passthrough_realloc_buffer,
+ .reset = tpm_passthrough_reset,
+ .had_startup_error = tpm_passthrough_get_startup_error,
+ .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
+};
diff --git a/qemu-options.hx b/qemu-options.hx
index 58a5dfa..4916baf 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1920,6 +1920,7 @@ The general form of a TPM device option is:
@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
@findex -tpmdev
Backend type must be:
+@option{passthrough}.
The specific backend type will determine the applicable options.
The @code{-tpmdev} options requires a @code{-device} option.
@@ -1931,6 +1932,38 @@ Use ? to print all available TPM backend types.
qemu -tpmdev ?
@end example
+@item -tpmdev passthrough, id=@var{id}, path=@var{path}
+
+(Linux-host only) Enable access to the host's TPM using the passthrough
+driver.
+
+@option{path} specifies the path to the host's TPM device, i.e., on
+a Linux host this would be @code{/dev/tpm0}.
+@option{path} is optional and by default @code{/dev/tpm0} is used.
+
+Some notes about using the host's TPM with the passthrough driver:
+
+The TPM device accessed by the passthrough driver must not be
+used by any other application on the host.
+
+Since the host's firmware (BIOS/UEFI) has already initialized the TPM,
+the VM's firmware (BIOS/UEFI) will not be able to initialize the
+TPM again and may therefore not show a TPM-specific menu that would
+otherwise allow the user to configure the TPM, e.g., allow the user to
+enable/disable or activate/deactivate the TPM.
+Further, if TPM ownership is released from within a VM then the host's TPM
+will get disabled and deactivated. To enable and activate the
+TPM again afterwards, the host has to be rebooted and the user is
+required to enter the firmware's menu to enable and activate the TPM.
+If the TPM is left disabled and/or deactivated most TPM commands will fail.
+
+To create a passthrough TPM use the following two options:
+@example
+-tpmdev passthrough,id=tpm0 -device tpm-tis,tpmdev=tpm0
+@end example
+Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
+@code{tpmdev=tpm0} in the device option.
+
@end table
ETEXI
diff --git a/tpm.c b/tpm.c
index fcab023..6470e59 100644
--- a/tpm.c
+++ b/tpm.c
@@ -18,12 +18,35 @@
#include "qerror.h"
static const TPMDriverOps *bes[] = {
+#ifdef CONFIG_TPM_PASSTHROUGH
+ &tpm_passthrough_driver,
+#endif
NULL,
};
static QLIST_HEAD(, TPMBackend) tpm_backends =
QLIST_HEAD_INITIALIZER(tpm_backends);
+void tpm_write_std_fatal_error_response(uint8_t *out, uint32_t out_len,
+ uint8_t *in, uint32_t in_len)
+{
+ if (out_len >= sizeof(struct tpm_resp_hdr)) {
+ struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
+ struct tpm_req_hdr *req = (struct tpm_req_hdr *)in;
+ uint16_t tag = be16_to_cpu(req->tag);
+
+ resp->tag = cpu_to_be16(
+ (in_len > 2 &&
+ tag >= TPM_TAG_RQU_COMMAND &&
+ tag <= TPM_TAG_RQU_AUTH2_COMMAND)
+ ? tag + 3
+ : TPM_TAG_RSP_COMMAND);
+
+ resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
+ resp->errcode = cpu_to_be32(TPM_FAIL);
+ }
+}
+
const TPMDriverOps *tpm_get_backend_driver(const char *id)
{
int i;
diff --git a/tpm.h b/tpm.h
index 85c2a35..505a535 100644
--- a/tpm.h
+++ b/tpm.h
@@ -7,12 +7,18 @@
struct TPMDriverOps;
typedef struct TPMDriverOps TPMDriverOps;
+typedef struct TPMPassthruState TPMPassthruState;
+
typedef struct TPMBackend {
char *id;
const char *fe_model;
char *parameters;
const TPMDriverOps *ops;
+ union {
+ TPMPassthruState *tpm_pt;
+ } s;
+
QLIST_ENTRY(TPMBackend) list;
} TPMBackend;
@@ -79,6 +85,30 @@ static inline void tpm_dump_buffer(FILE *stream,
#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
+struct tpm_req_hdr {
+ uint16_t tag;
+ uint32_t len;
+ uint32_t ordinal;
+} __attribute__((packed));
+
+struct tpm_resp_hdr {
+ uint16_t tag;
+ uint32_t len;
+ uint32_t errcode;
+} __attribute__((packed));
+
+#define TPM_TAG_RQU_COMMAND 0xc1
+#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2
+#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3
+
+#define TPM_TAG_RSP_COMMAND 0xc4
+#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5
+#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6
+
+#define TPM_FAIL 9
+
+#define TPM_ORD_GetTicks 0xf1
+
void tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
int tpm_init(void);
void tpm_cleanup(void);
@@ -86,5 +116,9 @@ TPMBackend *qemu_find_tpm(const char *id);
void do_info_tpm(Monitor *mon);
void tpm_display_backend_drivers(void);
const TPMDriverOps *tpm_get_backend_driver(const char *id);
+void tpm_write_std_fatal_error_response(uint8_t *out, uint32_t out_len,
+ uint8_t *in, uint32_t in_len);
+
+extern const TPMDriverOps tpm_passthrough_driver;
#endif /* _QEMU_TPM_H */
--
1.7.6.4
^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2011-12-12 23:27 ` Anthony Liguori
2011-12-12 23:59 ` Stefan Berger
0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:27 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl
On 12/12/2011 01:12 PM, Stefan Berger wrote:
> From Andreas Niederl's original posting with adaptations where necessary:
>
> This patch is based of off version 9 of Stefan Berger's patch series
> "Qemu Trusted Platform Module (TPM) integration"
> and adds a new backend driver for it.
>
> This patch adds a passthrough backend driver for passing commands sent to the
> emulated TPM device directly to a TPM device opened on the host machine.
>
> Thus it is possible to use a hardware TPM device in a system running on QEMU,
> providing the ability to access a TPM in a special state (e.g. after a Trusted
> Boot).
>
> This functionality is being used in the acTvSM Trusted Virtualization Platform
> which is available on [1].
>
> Usage example:
> qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
> -device tpm-tis,tpmdev=tpm0 \
> -cdrom test.iso -boot d
>
> Some notes about the host TPM:
> The TPM needs to be enabled and activated. If that's not the case one
> has to go through the BIOS/UEFI and enable and activate that TPM for TPM
> commands to work as expected.
> It may be necessary to boot the kernel using tpm_tis.force=1 in the boot
> command line or 'modprobe tpm_tis force=1' in case of using it as a module.
>
> Regards,
> Andreas Niederl, Stefan Berger
>
> [1] http://trustedjava.sourceforge.net/
>
> Signed-off-by: Andreas Niederl<andreas.niederl@iaik.tugraz.at>
> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>
> ---
>
> Changes for v12:
> - check size indicator in response from TPM to match that of the received
> packet
>
> Changes for v10:
> - clarified documentation
> - using /dev/tpm0 as default device if path option is not given
> - refactored code handling 'device' option into its own function
> - fixed name of structure to TPMPassthruThreadParams
> - only add tpm_passthrough_driver to collection of backends if
> it is compiled on the host
>
> Changes for v9:
> - prefixing of all functions and variables with tpm_passthrough_
> - cleanup of all variables into a structure that is now accessed
> using TPMBackend (tb->s.tpm_pt)
> - build it on Linux machines
> - added function to test whether given device is a TPM and refuse
> startup if it is not
> ---
> Makefile.target | 1 +
> configure | 3 +
> hw/tpm_passthrough.c | 477 ++++++++++++++++++++++++++++++++++++++++++++++++++
> qemu-options.hx | 33 ++++
> tpm.c | 23 +++
> tpm.h | 34 ++++
> 6 files changed, 571 insertions(+), 0 deletions(-)
> create mode 100644 hw/tpm_passthrough.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 37d5d10..4fc96e6 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -206,6 +206,7 @@ obj-$(CONFIG_KVM) += kvm.o kvm-all.o
> obj-$(CONFIG_NO_KVM) += kvm-stub.o
> obj-y += memory.o
> obj-$(CONFIG_TPM) += tpm.o tpm_tis.o
> +obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
> LIBS+=-lz
>
> QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
> diff --git a/configure b/configure
> index 385feb4..25995bc 100755
> --- a/configure
> +++ b/configure
> @@ -3721,6 +3721,9 @@ fi
>
> if test "$tpm" = "yes"; then
> if test "$target_softmmu" = "yes" ; then
> + if test "$linux" = "yes" ; then
> + echo "CONFIG_TPM_PASSTHROUGH=y">> $config_target_mak
> + fi
> echo "CONFIG_TPM=y">> $config_host_mak
> fi
> fi
> diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
> new file mode 100644
> index 0000000..f9cfe3d
> --- /dev/null
> +++ b/hw/tpm_passthrough.c
> @@ -0,0 +1,477 @@
> +/*
> + * passthrough TPM driver
> + *
> + * Copyright (c) 2010, 2011 IBM Corporation
> + * Authors:
> + * Stefan Berger<stefanb@us.ibm.com>
> + *
> + * Copyright (C) 2011 IAIK, Graz University of Technology
> + * Author: Andreas Niederl
> + *
> + * 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 "qemu-error.h"
> +#include "tpm.h"
> +#include "hw/hw.h"
> +#include "hw/tpm_tis.h"
> +#include "hw/pc.h"
> +
> +/* #define DEBUG_TPM */
> +
> +/* data structures */
> +
> +typedef struct TPMPassthruThreadParams {
> + TPMState *tpm_state;
> +
> + TPMRecvDataCB *recv_data_callback;
> + TPMBackend *tb;
> +} TPMPassthruThreadParams;
> +
> +struct TPMPassthruState {
> + QemuThread thread;
> + bool thread_terminate;
> + bool thread_running;
> +
> + TPMPassthruThreadParams tpm_thread_params;
> +
> + char tpm_dev[64];
> + int tpm_fd;
> + bool had_startup_error;
> +};
> +
> +#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
> +
> +/* borrowed from qemu-char.c */
> +static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
> +{
> + int ret, len1;
> +
> + len1 = len;
> + while (len1> 0) {
> + ret = write(fd, buf, len1);
> + if (ret< 0) {
> + if (errno != EINTR&& errno != EAGAIN) {
> + return -1;
> + }
> + } else if (ret == 0) {
> + break;
> + } else {
> + buf += ret;
> + len1 -= ret;
> + }
> + }
> + return len - len1;
> +}
> +
> +static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
> +{
> + int ret, len1;
> + uint8_t *buf1;
> +
> + len1 = len;
> + buf1 = buf;
> + while ((len1> 0)&& (ret = read(fd, buf1, len1)) != 0) {
> + if (ret< 0) {
> + if (errno != EINTR&& errno != EAGAIN) {
> + return -1;
> + }
> + } else {
> + buf1 += ret;
> + len1 -= ret;
> + }
> + }
> + return len - len1;
> +}
> +
> +static void *tpm_passthrough_main_loop(void *d)
> +{
> + TPMPassthruThreadParams *thr_parms = d;
> + TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
> + uint32_t in_len, out_len;
> + uint8_t *in, *out;
> + uint8_t locty;
> + TPMLocality *cmd_locty;
> + int ret;
This is rather scary. I'd rather see us make use of a GThreadPool in order to
submit read/write requests asynchronously to the /dev/tpm device. I don't
think the code should be structured expecting synchronous command execution.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation
2011-12-12 23:27 ` Anthony Liguori
@ 2011-12-12 23:59 ` Stefan Berger
0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 23:59 UTC (permalink / raw)
To: qemu-devel
On 12/12/2011 06:27 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, Stefan Berger wrote:
>> From Andreas Niederl's original posting with adaptations where
>> necessary:
>>
>> This patch is based of off version 9 of Stefan Berger's patch series
>> "Qemu Trusted Platform Module (TPM) integration"
>> and adds a new backend driver for it.
>>
>> This patch adds a passthrough backend driver for passing commands
>> sent to the
>> emulated TPM device directly to a TPM device opened on the host machine.
>>
>> Thus it is possible to use a hardware TPM device in a system running
>> on QEMU,
>> providing the ability to access a TPM in a special state (e.g. after
>> a Trusted
>> Boot).
>>
>> This functionality is being used in the acTvSM Trusted Virtualization
>> Platform
>> which is available on [1].
[...]
>>
>> +static void *tpm_passthrough_main_loop(void *d)
>> +{
>> + TPMPassthruThreadParams *thr_parms = d;
>> + TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
>> + uint32_t in_len, out_len;
>> + uint8_t *in, *out;
>> + uint8_t locty;
>> + TPMLocality *cmd_locty;
>> + int ret;
>
> This is rather scary. I'd rather see us make use of a GThreadPool in
> order to submit read/write requests asynchronously to the /dev/tpm
> device. I don't think the code should be structured expecting
> synchronous command execution.
This part here is running as a thread, create via qemu_thread_create().
Relative to the main thread this is of course running asynchronously.
The same design will re-appear when the libtpms based TPM backend
appears. Here we will need a thread for concurrent execution of more
time consuming crypto functions.
Regards,
Stefan
^ permalink raw reply [flat|nested] 29+ messages in thread
* [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (4 preceding siblings ...)
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
2011-12-12 23:27 ` Anthony Liguori
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
2011-12-13 5:45 ` [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 UTC (permalink / raw)
To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst
Introduce --enable-tpm-passthrough configure option.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
configure | 16 +++++++++++++++-
1 files changed, 15 insertions(+), 1 deletions(-)
diff --git a/configure b/configure
index 25995bc..ffb599e 100755
--- a/configure
+++ b/configure
@@ -186,6 +186,7 @@ zlib="yes"
guest_agent="yes"
libiscsi=""
tpm="no"
+tpm_passthrough="no"
# parse CC options first
for opt do
@@ -787,11 +788,20 @@ for opt do
;;
--enable-tpm) tpm="yes"
;;
+ --enable-tpm-passthrough) tpm_passthrough="yes"
+ ;;
*) echo "ERROR: unknown option $opt"; show_help="yes"
;;
esac
done
+if test "$tpm" = "no" ; then
+ if test "$tpm_passthrough" = "yes"; then
+ echo "ERROR: --enable-tpm-passthrough requires --enable-tpm"
+ exit 1
+ fi
+fi
+
#
# If cpu ~= sparc and sparc_cpu hasn't been defined, plug in the right
# QEMU_CFLAGS/LDFLAGS (assume sparc_v8plus for 32-bit and sparc_v9 for 64-bit)
@@ -1074,6 +1084,7 @@ echo " --enable-usb-redir enable usb network redirection support"
echo " --disable-guest-agent disable building of the QEMU Guest Agent"
echo " --enable-guest-agent enable building of the QEMU Guest Agent"
echo " --enable-tpm enable TPM support"
+echo " --enable-tpm-passthrough enable TPM passthrough driver"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@@ -2850,6 +2861,7 @@ echo "OpenGL support $opengl"
echo "libiscsi support $libiscsi"
echo "build guest agent $guest_agent"
echo "TPM support $tpm"
+echo "TPM passthrough $tpm_passthrough"
if test "$sdl_too_old" = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3722,7 +3734,9 @@ fi
if test "$tpm" = "yes"; then
if test "$target_softmmu" = "yes" ; then
if test "$linux" = "yes" ; then
- echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
+ if test "$tpm_passthrough" = "yes" ; then
+ echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_target_mak
+ fi
fi
echo "CONFIG_TPM=y" >> $config_host_mak
fi
--
1.7.6.4
^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
@ 2011-12-12 23:27 ` Anthony Liguori
2011-12-13 0:12 ` Stefan Berger
0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:27 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl
On 12/12/2011 01:12 PM, Stefan Berger wrote:
> Introduce --enable-tpm-passthrough configure option.
>
> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
> ---
> configure | 16 +++++++++++++++-
> 1 files changed, 15 insertions(+), 1 deletions(-)
>
> diff --git a/configure b/configure
> index 25995bc..ffb599e 100755
> --- a/configure
> +++ b/configure
> @@ -186,6 +186,7 @@ zlib="yes"
> guest_agent="yes"
> libiscsi=""
> tpm="no"
> +tpm_passthrough="no"
Same as before, please probe for existence.
Regards,
Anthony Liguori
>
> # parse CC options first
> for opt do
> @@ -787,11 +788,20 @@ for opt do
> ;;
> --enable-tpm) tpm="yes"
> ;;
> + --enable-tpm-passthrough) tpm_passthrough="yes"
> + ;;
> *) echo "ERROR: unknown option $opt"; show_help="yes"
> ;;
> esac
> done
>
> +if test "$tpm" = "no" ; then
> + if test "$tpm_passthrough" = "yes"; then
> + echo "ERROR: --enable-tpm-passthrough requires --enable-tpm"
> + exit 1
> + fi
> +fi
> +
> #
> # If cpu ~= sparc and sparc_cpu hasn't been defined, plug in the right
> # QEMU_CFLAGS/LDFLAGS (assume sparc_v8plus for 32-bit and sparc_v9 for 64-bit)
> @@ -1074,6 +1084,7 @@ echo " --enable-usb-redir enable usb network redirection support"
> echo " --disable-guest-agent disable building of the QEMU Guest Agent"
> echo " --enable-guest-agent enable building of the QEMU Guest Agent"
> echo " --enable-tpm enable TPM support"
> +echo " --enable-tpm-passthrough enable TPM passthrough driver"
> echo ""
> echo "NOTE: The object files are built at the place where configure is launched"
> exit 1
> @@ -2850,6 +2861,7 @@ echo "OpenGL support $opengl"
> echo "libiscsi support $libiscsi"
> echo "build guest agent $guest_agent"
> echo "TPM support $tpm"
> +echo "TPM passthrough $tpm_passthrough"
>
> if test "$sdl_too_old" = "yes"; then
> echo "-> Your SDL version is too old - please upgrade to have SDL support"
> @@ -3722,7 +3734,9 @@ fi
> if test "$tpm" = "yes"; then
> if test "$target_softmmu" = "yes" ; then
> if test "$linux" = "yes" ; then
> - echo "CONFIG_TPM_PASSTHROUGH=y">> $config_target_mak
> + if test "$tpm_passthrough" = "yes" ; then
> + echo "CONFIG_TPM_PASSTHROUGH=y">> $config_target_mak
> + fi
> fi
> echo "CONFIG_TPM=y">> $config_host_mak
> fi
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
2011-12-12 23:27 ` Anthony Liguori
@ 2011-12-13 0:12 ` Stefan Berger
2011-12-13 4:51 ` Paul Brook
0 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-13 0:12 UTC (permalink / raw)
To: Anthony Liguori; +Cc: mst, qemu-devel, andreas.niederl
On 12/12/2011 06:27 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, Stefan Berger wrote:
>> Introduce --enable-tpm-passthrough configure option.
>>
>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>> ---
>> configure | 16 +++++++++++++++-
>> 1 files changed, 15 insertions(+), 1 deletions(-)
>>
>> diff --git a/configure b/configure
>> index 25995bc..ffb599e 100755
>> --- a/configure
>> +++ b/configure
>> @@ -186,6 +186,7 @@ zlib="yes"
>> guest_agent="yes"
>> libiscsi=""
>> tpm="no"
>> +tpm_passthrough="no"
>
> Same as before, please probe for existence.
We would be probing for /dev/tpm0. Is that really what we want that this
driver only gets compiled if /dev/tpm0 is (currently) available?
Regards,
Stefan
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
2011-12-13 0:12 ` Stefan Berger
@ 2011-12-13 4:51 ` Paul Brook
2011-12-13 12:51 ` Stefan Berger
0 siblings, 1 reply; 29+ messages in thread
From: Paul Brook @ 2011-12-13 4:51 UTC (permalink / raw)
To: qemu-devel; +Cc: mst, andreas.niederl, Stefan Berger
> >> +tpm_passthrough="no"
> >
> > Same as before, please probe for existence.
>
> We would be probing for /dev/tpm0. Is that really what we want that this
> driver only gets compiled if /dev/tpm0 is (currently) available?
If what you say is true then this code should always be enabled.
Paul
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
2011-12-13 4:51 ` Paul Brook
@ 2011-12-13 12:51 ` Stefan Berger
2011-12-13 13:51 ` Michael S. Tsirkin
2011-12-13 17:25 ` Paul Brook
0 siblings, 2 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-13 12:51 UTC (permalink / raw)
To: Paul Brook; +Cc: andreas.niederl, qemu-devel, mst
On 12/12/2011 11:51 PM, Paul Brook wrote:
>>>> +tpm_passthrough="no"
>>> Same as before, please probe for existence.
>> We would be probing for /dev/tpm0. Is that really what we want that this
>> driver only gets compiled if /dev/tpm0 is (currently) available?
> If what you say is true then this code should always be enabled.
>
Michael Tsirkin previously requested that there be an option for the TPM
passthrough driver to be selectively enabled since at least using
/dev/tpm0 may not be what everybody wants. The passthrough driver at
some point will also be able to use sockets to communicate with a TPM
when a file descriptor is passed to Qemu, so maybe that changes then?
Stefan
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
2011-12-13 12:51 ` Stefan Berger
@ 2011-12-13 13:51 ` Michael S. Tsirkin
2011-12-13 17:41 ` Anthony Liguori
2011-12-13 17:25 ` Paul Brook
1 sibling, 1 reply; 29+ messages in thread
From: Michael S. Tsirkin @ 2011-12-13 13:51 UTC (permalink / raw)
To: Stefan Berger; +Cc: andreas.niederl, Paul Brook, qemu-devel
On Tue, Dec 13, 2011 at 07:51:17AM -0500, Stefan Berger wrote:
> On 12/12/2011 11:51 PM, Paul Brook wrote:
> >>>>+tpm_passthrough="no"
> >>>Same as before, please probe for existence.
> >>We would be probing for /dev/tpm0. Is that really what we want that this
> >>driver only gets compiled if /dev/tpm0 is (currently) available?
> >If what you say is true then this code should always be enabled.
> >
> Michael Tsirkin previously requested that there be an option for the
> TPM passthrough driver to be selectively enabled since at least
> using /dev/tpm0 may not be what everybody wants. The passthrough
> driver at some point will also be able to use sockets to communicate
> with a TPM when a file descriptor is passed to Qemu, so maybe that
> changes then?
>
>
> Stefan
The passthrough as it is, is pretty easy to misuse.
This is a hardware problem, not software, and
I don't think it's fixable.
So I do not think all downstreams will want to support this
mode, making it easy to disable this is IMO important.
--
MST
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
2011-12-13 13:51 ` Michael S. Tsirkin
@ 2011-12-13 17:41 ` Anthony Liguori
2011-12-13 17:48 ` Stefan Berger
0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-13 17:41 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: qemu-devel, Paul Brook, andreas.niederl, Stefan Berger
On 12/13/2011 07:51 AM, Michael S. Tsirkin wrote:
> On Tue, Dec 13, 2011 at 07:51:17AM -0500, Stefan Berger wrote:
>> On 12/12/2011 11:51 PM, Paul Brook wrote:
>>>>>> +tpm_passthrough="no"
>>>>> Same as before, please probe for existence.
>>>> We would be probing for /dev/tpm0. Is that really what we want that this
>>>> driver only gets compiled if /dev/tpm0 is (currently) available?
>>> If what you say is true then this code should always be enabled.
>>>
>> Michael Tsirkin previously requested that there be an option for the
>> TPM passthrough driver to be selectively enabled since at least
>> using /dev/tpm0 may not be what everybody wants. The passthrough
>> driver at some point will also be able to use sockets to communicate
>> with a TPM when a file descriptor is passed to Qemu, so maybe that
>> changes then?
>>
>>
>> Stefan
>
> The passthrough as it is, is pretty easy to misuse.
> This is a hardware problem, not software, and
> I don't think it's fixable.
Can you elaborate? And can this be documented such that users are aware of this.
Regards,
Anthony Liguori
>
> So I do not think all downstreams will want to support this
> mode, making it easy to disable this is IMO important.
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
2011-12-13 17:41 ` Anthony Liguori
@ 2011-12-13 17:48 ` Stefan Berger
2011-12-13 20:33 ` Paul Brook
0 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-13 17:48 UTC (permalink / raw)
To: Anthony Liguori
Cc: Paul Brook, andreas.niederl, qemu-devel, Michael S. Tsirkin
On 12/13/2011 12:41 PM, Anthony Liguori wrote:
> On 12/13/2011 07:51 AM, Michael S. Tsirkin wrote:
>> On Tue, Dec 13, 2011 at 07:51:17AM -0500, Stefan Berger wrote:
>>> On 12/12/2011 11:51 PM, Paul Brook wrote:
>>>>>>> +tpm_passthrough="no"
>>>>>> Same as before, please probe for existence.
>>>>> We would be probing for /dev/tpm0. Is that really what we want
>>>>> that this
>>>>> driver only gets compiled if /dev/tpm0 is (currently) available?
>>>> If what you say is true then this code should always be enabled.
>>>>
>>> Michael Tsirkin previously requested that there be an option for the
>>> TPM passthrough driver to be selectively enabled since at least
>>> using /dev/tpm0 may not be what everybody wants. The passthrough
>>> driver at some point will also be able to use sockets to communicate
>>> with a TPM when a file descriptor is passed to Qemu, so maybe that
>>> changes then?
>>>
>>>
>>> Stefan
>>
>> The passthrough as it is, is pretty easy to misuse.
>> This is a hardware problem, not software, and
>> I don't think it's fixable.
>
> Can you elaborate? And can this be documented such that users are
> aware of this.
>
From qemu-doc.html:
Some notes about using the host's TPM with the passthrough driver:
The TPM device accessed by the passthrough driver must not be used by
any other application on the host.
Since the host's firmware (BIOS/UEFI) has already initialized the TPM,
the VM's firmware (BIOS/UEFI) will not be able to initialize the TPM
again and may therefore not show a TPM-specific menu that would
otherwise allow the user to configure the TPM, e.g., allow the user to
enable/disable or activate/deactivate the TPM. Further, if TPM ownership
is released from within a VM then the host's TPM will get disabled and
deactivated. To enable and activate the TPM again afterwards, the host
has to be rebooted and the user is required to enter the firmware's menu
to enable and activate the TPM. If the TPM is left disabled and/or
deactivated most TPM commands will fail.
> Regards,
>
> Anthony Liguori
>
Stefan
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
2011-12-13 17:48 ` Stefan Berger
@ 2011-12-13 20:33 ` Paul Brook
0 siblings, 0 replies; 29+ messages in thread
From: Paul Brook @ 2011-12-13 20:33 UTC (permalink / raw)
To: Stefan Berger; +Cc: andreas.niederl, qemu-devel, Michael S. Tsirkin
> The TPM device accessed by the passthrough driver must not be used by
> any other application on the host.
>
> Since the host's firmware (BIOS/UEFI) has already initialized the TPM,
> the VM's firmware (BIOS/UEFI) will not be able to initialize the TPM
> again and may therefore not show a TPM-specific menu that would
> otherwise allow the user to configure the TPM, e.g., allow the user to
> enable/disable or activate/deactivate the TPM. Further, if TPM ownership
> is released from within a VM then the host's TPM will get disabled and
> deactivated. To enable and activate the TPM again afterwards, the host
> has to be rebooted and the user is required to enter the firmware's menu
> to enable and activate the TPM. If the TPM is left disabled and/or
> deactivated most TPM commands will fail.
Presumably the same is true of any other application that has access to
/dev/tpm0?
This doesn't sound any different to any other kind of device passthrough (e.g.
USB). If the host is also talking to the device then it's likely to get
confused. If the guest tells the device to do something suicidal then it'll
most likely die.
Complicated of cource by the fact that the whole point of the TPM is you don't
really trust yourself, so the usual failure mode is a complete nervous
breakdown. But that comes with the territory.
Paul
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option
2011-12-13 12:51 ` Stefan Berger
2011-12-13 13:51 ` Michael S. Tsirkin
@ 2011-12-13 17:25 ` Paul Brook
1 sibling, 0 replies; 29+ messages in thread
From: Paul Brook @ 2011-12-13 17:25 UTC (permalink / raw)
To: Stefan Berger; +Cc: andreas.niederl, qemu-devel, mst
> On 12/12/2011 11:51 PM, Paul Brook wrote:
> >>>> +tpm_passthrough="no"
> >>>
> >>> Same as before, please probe for existence.
> >>
> >> We would be probing for /dev/tpm0. Is that really what we want that this
> >> driver only gets compiled if /dev/tpm0 is (currently) available?
> >
> > If what you say is true then this code should always be enabled.
>
> Michael Tsirkin previously requested that there be an option for the TPM
> passthrough driver to be selectively enabled since at least using
> /dev/tpm0 may not be what everybody wants. The passthrough driver at
> some point will also be able to use sockets to communicate with a TPM
> when a file descriptor is passed to Qemu, so maybe that changes then?
Surely that's a runtime decision made by the qemu user, not a compile time
decision made by the distribution vendor. Testing functionality of the build
machine is fundamentally wrong. At best it completely breaks cross compiling,
at worst it subtly breaks building on a dev machine and running on a
production server.
Configure time probing only makes sense for code that will fail to build if
external compile time dependencies are not present.
If you really think building qemu without this functionality is useful, then
maybe add a configure option to disable it. With the possible exception of
developer debugging aids, code that is not enabled by default is clearly not
worth having and should be removed altogether.
Paul
^ permalink raw reply [flat|nested] 29+ messages in thread
* [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver
2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (5 preceding siblings ...)
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
@ 2011-12-12 19:12 ` Stefan Berger
2011-12-12 23:30 ` Anthony Liguori
2011-12-13 5:45 ` [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
7 siblings, 1 reply; 29+ messages in thread
From: Stefan Berger @ 2011-12-12 19:12 UTC (permalink / raw)
To: stefanb, qemu-devel, anthony; +Cc: andreas.niederl, mst
Enable the passing of a file descriptor via fd=<..> to access the host's
TPM device using the TPM passthrough driver.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
v13:
- Only accepting a character device's file descriptor
v12:
- added documentation part
---
hw/tpm_passthrough.c | 73 +++++++++++++++++++++++++++++++++++++-------------
qemu-config.c | 5 +++
qemu-options.hx | 6 +++-
3 files changed, 64 insertions(+), 20 deletions(-)
diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
index f9cfe3d..57bb77a 100644
--- a/hw/tpm_passthrough.c
+++ b/hw/tpm_passthrough.c
@@ -361,33 +361,68 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
const char *value;
char buf[64];
int n;
+ struct stat statbuf;
- value = qemu_opt_get(opts, "path");
- if (!value) {
- value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
- }
+ value = qemu_opt_get(opts, "fd");
+ if (value) {
+ if (qemu_opt_get(opts, "path")) {
+ error_report("fd= is invalid with path=");
+ return -1;
+ }
+
+ tb->s.tpm_pt->tpm_fd = qemu_parse_fd(value);
+ if (tb->s.tpm_pt->tpm_fd < 0) {
+ error_report("Illegal file descriptor for TPM device.\n");
+ return -1;
+ }
- n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
- "%s", value);
+ snprintf(buf, sizeof(buf), "fd=%d", tb->s.tpm_pt->tpm_fd);
- if (n >= sizeof(tb->s.tpm_pt->tpm_dev)) {
- error_report("TPM device path is too long.\n");
- goto err_exit;
- }
+ tb->parameters = g_strdup(buf);
- snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
+ if (tb->parameters == NULL) {
+ goto err_close_tpmdev;
+ }
+ } else {
+ value = qemu_opt_get(opts, "path");
+ if (!value) {
+ value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+ }
+
+ n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
+ "%s", value);
+
+ if (n >= sizeof(tb->s.tpm_pt->tpm_dev)) {
+ error_report("TPM device path is too long.\n");
+ goto err_exit;
+ }
- tb->parameters = g_strdup(buf);
+ snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
- if (tb->parameters == NULL) {
- return 1;
+ tb->parameters = g_strdup(buf);
+
+ if (tb->parameters == NULL) {
+ return 1;
+ }
+
+ tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
+ if (tb->s.tpm_pt->tpm_fd < 0) {
+ error_report("Cannot access TPM device using '%s'.\n",
+ tb->s.tpm_pt->tpm_dev);
+ goto err_exit;
+ }
}
- tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
- if (tb->s.tpm_pt->tpm_fd < 0) {
- error_report("Cannot access TPM device using '%s'.\n",
- tb->s.tpm_pt->tpm_dev);
- goto err_exit;
+ if (fstat(tb->s.tpm_pt->tpm_fd, &statbuf) != 0) {
+ error_report("Cannot determine file descriptor type for TPM "
+ "device: %s", strerror(errno));
+ goto err_close_tpmdev;
+ }
+
+ /* only allow character devices for now */
+ if (!S_ISCHR(statbuf.st_mode)) {
+ error_report("TPM file descriptor is not a character device");
+ goto err_close_tpmdev;
}
if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) {
diff --git a/qemu-config.c b/qemu-config.c
index cc4c31d..e49f4d8 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -564,6 +564,11 @@ static QemuOptsList qemu_tpmdev_opts = {
.type = QEMU_OPT_STRING,
.help = "Persistent storage for TPM state",
},
+ {
+ .name = "fd",
+ .type = QEMU_OPT_STRING,
+ .help = "Filedescriptor for accessing the TPM",
+ },
{ /* end of list */ }
},
};
diff --git a/qemu-options.hx b/qemu-options.hx
index 4916baf..7eca701 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1932,7 +1932,7 @@ Use ? to print all available TPM backend types.
qemu -tpmdev ?
@end example
-@item -tpmdev passthrough, id=@var{id}, path=@var{path}
+@item -tpmdev passthrough, id=@var{id}, path=@var{path}, fd=@var{h}
(Linux-host only) Enable access to the host's TPM using the passthrough
driver.
@@ -1941,6 +1941,10 @@ driver.
a Linux host this would be @code{/dev/tpm0}.
@option{path} is optional and by default @code{/dev/tpm0} is used.
+@option{fd} specifies the file descriptor of the host's TPM device.
+@option{fd} and @option{path} are mutually exclusive.
+@option{fd} is optional.
+
Some notes about using the host's TPM with the passthrough driver:
The TPM device accessed by the passthrough driver must not be
--
1.7.6.4
^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
@ 2011-12-12 23:30 ` Anthony Liguori
2011-12-13 0:17 ` Stefan Berger
0 siblings, 1 reply; 29+ messages in thread
From: Anthony Liguori @ 2011-12-12 23:30 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl
On 12/12/2011 01:12 PM, Stefan Berger wrote:
> Enable the passing of a file descriptor via fd=<..> to access the host's
> TPM device using the TPM passthrough driver.
>
> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>
> ---
>
> v13:
> - Only accepting a character device's file descriptor
>
> v12:
> - added documentation part
> ---
> hw/tpm_passthrough.c | 73 +++++++++++++++++++++++++++++++++++++-------------
> qemu-config.c | 5 +++
> qemu-options.hx | 6 +++-
> 3 files changed, 64 insertions(+), 20 deletions(-)
>
> diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
> index f9cfe3d..57bb77a 100644
> --- a/hw/tpm_passthrough.c
> +++ b/hw/tpm_passthrough.c
> @@ -361,33 +361,68 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
> const char *value;
> char buf[64];
> int n;
> + struct stat statbuf;
>
> - value = qemu_opt_get(opts, "path");
> - if (!value) {
> - value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
> - }
> + value = qemu_opt_get(opts, "fd");
> + if (value) {
> + if (qemu_opt_get(opts, "path")) {
> + error_report("fd= is invalid with path=");
> + return -1;
> + }
> +
> + tb->s.tpm_pt->tpm_fd = qemu_parse_fd(value);
> + if (tb->s.tpm_pt->tpm_fd< 0) {
> + error_report("Illegal file descriptor for TPM device.\n");
> + return -1;
> + }
>
> - n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
> - "%s", value);
> + snprintf(buf, sizeof(buf), "fd=%d", tb->s.tpm_pt->tpm_fd);
>
> - if (n>= sizeof(tb->s.tpm_pt->tpm_dev)) {
> - error_report("TPM device path is too long.\n");
> - goto err_exit;
> - }
> + tb->parameters = g_strdup(buf);
>
> - snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
> + if (tb->parameters == NULL) {
> + goto err_close_tpmdev;
> + }
> + } else {
> + value = qemu_opt_get(opts, "path");
> + if (!value) {
> + value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
> + }
> +
> + n = snprintf(tb->s.tpm_pt->tpm_dev, sizeof(tb->s.tpm_pt->tpm_dev),
> + "%s", value);
> +
> + if (n>= sizeof(tb->s.tpm_pt->tpm_dev)) {
> + error_report("TPM device path is too long.\n");
> + goto err_exit;
> + }
>
> - tb->parameters = g_strdup(buf);
> + snprintf(buf, sizeof(buf), "path=%s", tb->s.tpm_pt->tpm_dev);
>
> - if (tb->parameters == NULL) {
> - return 1;
> + tb->parameters = g_strdup(buf);
> +
> + if (tb->parameters == NULL) {
> + return 1;
> + }
> +
> + tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
> + if (tb->s.tpm_pt->tpm_fd< 0) {
> + error_report("Cannot access TPM device using '%s'.\n",
> + tb->s.tpm_pt->tpm_dev);
> + goto err_exit;
> + }
> }
>
> - tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
> - if (tb->s.tpm_pt->tpm_fd< 0) {
> - error_report("Cannot access TPM device using '%s'.\n",
> - tb->s.tpm_pt->tpm_dev);
> - goto err_exit;
> + if (fstat(tb->s.tpm_pt->tpm_fd,&statbuf) != 0) {
> + error_report("Cannot determine file descriptor type for TPM "
> + "device: %s", strerror(errno));
> + goto err_close_tpmdev;
> + }
> +
> + /* only allow character devices for now */
> + if (!S_ISCHR(statbuf.st_mode)) {
> + error_report("TPM file descriptor is not a character device");
> + goto err_close_tpmdev;
> }
I think you're being overzealous here. The backend only uses read/write to
interact with the passthrough device. You could use this as a mechanism to tie
in an emulated VTPM by using a socket. I'm not suggesting we do that for
libvtpm, but I think we don't gain anything from being overly restrictive here.
I don't think a user passing the wrong type of fd is the common case to optimize
for wrt usability.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver
2011-12-12 23:30 ` Anthony Liguori
@ 2011-12-13 0:17 ` Stefan Berger
0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-13 0:17 UTC (permalink / raw)
To: Anthony Liguori; +Cc: mst, qemu-devel, andreas.niederl
On 12/12/2011 06:30 PM, Anthony Liguori wrote:
> On 12/12/2011 01:12 PM, Stefan Berger wrote:
>> Enable the passing of a file descriptor via fd=<..> to access the
>> host's
>> TPM device using the TPM passthrough driver.
>>
>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>>
[...]
>> - tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR);
>> - if (tb->s.tpm_pt->tpm_fd< 0) {
>> - error_report("Cannot access TPM device using '%s'.\n",
>> - tb->s.tpm_pt->tpm_dev);
>> - goto err_exit;
>> + if (fstat(tb->s.tpm_pt->tpm_fd,&statbuf) != 0) {
>> + error_report("Cannot determine file descriptor type for TPM "
>> + "device: %s", strerror(errno));
>> + goto err_close_tpmdev;
>> + }
>> +
>> + /* only allow character devices for now */
>> + if (!S_ISCHR(statbuf.st_mode)) {
>> + error_report("TPM file descriptor is not a character device");
>> + goto err_close_tpmdev;
>> }
>
> I think you're being overzealous here. The backend only uses
> read/write to interact with the passthrough device. You could use
> this as a mechanism to tie in an emulated VTPM by using a socket. I'm
> not suggesting we do that for libvtpm, but I think we don't gain
> anything from being overly restrictive here.
We prevent files, pipes, sockets and block devices using this check.
Sockets may make sense in the future, but would like to enable that
separately.
>
> I don't think a user passing the wrong type of fd is the common case
> to optimize for wrt usability.
I don't think it makes sense to have the TPM passthrough driver write()
into a block device or file, so therefore I prevented that. The above
check is just a single line...
Stefan
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration
2011-12-12 19:12 [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (6 preceding siblings ...)
2011-12-12 19:12 ` [Qemu-devel] [PATCH V13 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
@ 2011-12-13 5:45 ` Stefan Weil
2011-12-13 12:43 ` Stefan Berger
7 siblings, 1 reply; 29+ messages in thread
From: Stefan Weil @ 2011-12-13 5:45 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, andreas.niederl
Am 12.12.2011 20:12, schrieb Stefan Berger:
> The following series of patches adds TPM (Trusted Platform Module) support
> to Qemu. An emulator for the TIS (TPM Interface Spec) interface is
> added that provides the basis for accessing a 'backend' implementing the actual
> TPM functionality. The TIS emulator serves as a 'frontend' enabling for
> example Linux's TPM TIS (tpm_tis) driver.
>
> In this series I am posting a backend implementation that makes use of the
> host's TPM through a passthrough driver, which on Linux is accessed
> using /dev/tpm0.
>
Hi Stefan,
please use "GPL V2 or later" (not GPL V2 only) for new files.
"Qemu" should be replaced by "QEMU".
Regards,
Stefan Weil
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration
2011-12-13 5:45 ` [Qemu-devel] [PATCH V13 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
@ 2011-12-13 12:43 ` Stefan Berger
0 siblings, 0 replies; 29+ messages in thread
From: Stefan Berger @ 2011-12-13 12:43 UTC (permalink / raw)
To: Stefan Weil; +Cc: andreas.niederl, qemu-devel, mst
On 12/13/2011 12:45 AM, Stefan Weil wrote:
> Am 12.12.2011 20:12, schrieb Stefan Berger:
>> The following series of patches adds TPM (Trusted Platform Module)
>> support
>> to Qemu. An emulator for the TIS (TPM Interface Spec) interface is
>> added that provides the basis for accessing a 'backend' implementing
>> the actual
>> TPM functionality. The TIS emulator serves as a 'frontend' enabling for
>> example Linux's TPM TIS (tpm_tis) driver.
>>
>> In this series I am posting a backend implementation that makes use
>> of the
>> host's TPM through a passthrough driver, which on Linux is accessed
>> using /dev/tpm0.
>>
>
> Hi Stefan,
>
> please use "GPL V2 or later" (not GPL V2 only) for new files.
> "Qemu" should be replaced by "QEMU".
Not a problem. I fixed this last night.
Stefan
^ permalink raw reply [flat|nested] 29+ messages in thread