* [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration
@ 2012-06-04 19:37 Stefan Berger
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 1/7] Support for TPM command line options Stefan Berger
` (8 more replies)
0 siblings, 9 replies; 30+ messages in thread
From: Stefan Berger @ 2012-06-04 19:37 UTC (permalink / raw)
To: stefanb, qemu-devel, anthony; +Cc: root, andreas.niederl, mst
From: root <root@d941f-5.watson.ibm.com>
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.
v19:
- applies to checkout of 8cc9b43 (Jun 4)
v18:
- applies to checkout of 563987d (May 1)
- removed some dead variable in 7/7
v17:
- applies to checkout of 6507470 (Apr 30)
- split up path and fd into two optional parameters
v16:
- applied to checkout of 42fe1c2 (Apr 27)
- followed Anthony's suggestions for v15
- changed qemu-options.hx and vl.c to not show anything TPM-related if
--enable-tpm-passthrough was not used on configure line
v15:
- applies to checkout of 8a22565 (Mar 27)
- replacing g_malloc's with g_new; no more checks for NULL after allocs
- introducing usage of bottom half in TIS frontend to deliver responses
- get rid of locks since global lock is held by all threads entering TIS
code
- cleanups
v14:
- applies to checkout of da5361c (Dec 12)
- implemented Anthony Liguori's suggestions
- dropping the version log on individual patches
v13:
- applies to checkout of 61a5872 (Dec 12)
- only allowing character devices as fd parameter
- fixing error path in tpm_tis_init
v12:
- applies to checkout of ebffe2a (Oct 11)
- added documentation for fd parameter
- nits
v11:
- applies to checkout of 46f3069 (Sep 28)
- some filing on the documentation
- small nits fixed
v10:
- applies to checkout of 1ce9ce6 (Sep 27)
- addressed Michael Tsirkin's comments on v9
v9:
- addressed Michael Tsirkin's and other reviewers' comments
- only posting Andreas Niederl's passthrough driver as the backend driver
v8:
- applies to checkout of f0fb8b7 (Aug 30)
- fixing compilation error pointed out by Andreas Niederl
- adding patch that allows to feed an initial state into the libtpms TPM
- following memory API changes (glib) where necessary
v7:
- applies to checkout of b9c6cbf (Aug 9)
- measuring the modules if multiboot is used
- coding style fixes
v6:
- applies to checkout of 75ef849 (July 2nd)
- some fixes and improvements to existing patches; see individual patches
- added a patch with a null driver responding to all TPM requests with
a response indicating failure; this backend has no dependencies and
can alwayy be built;
- added a patch to support the hashing of kernel, ramfs and command line
if those were passed to Qemu using -kernel, -initrd and -append
respectively. Measurements are taken, logged, and passed to SeaBIOS using
the firmware interface.
- libtpms revision 7 now requires 83kb of block storage due to having more
NVRAM space
v5:
- applies to checkout of 1fddfba1
- adding support for split command line using the -tpmdev ... -device ...
options while keeping the -tpm option
- support for querying the device models using -tpm model=?
- support for monitor 'info tpm'
- adding documentation of command line options for man page and web page
- increasing room for ACPI tables that qemu reserves to 128kb (from 64kb)
- adding (experimental) support for block migration
- adding (experimental) support for taking measurements when kernel,
initrd and kernel command line are directly passed to Qemu
v4:
- applies to checkout of d2d979c6
- more coding style fixes
- adding patch for supporting blob encryption (in addition to the existing
QCoW2-level encryption)
- this allows for graceful termination of a migration if the target
is detected to have a wrong key
- tested with big and little endian hosts
- main thread releases mutex while checking for work to do on behalf of
backend
- introducing file locking (fcntl) on the block layer for serializing access
to shared (QCoW2) files (used during migration)
v3:
- Building a null driver at patch 5/8 that responds to all requests
with an error response; subsequently this driver is transformed to the
libtpms-based driver for real TPM functionality
- Reworked the threading; dropped the patch for qemu_thread_join; the
main thread synchronizing with the TPM thread termination may need
to write data to the block storage while waiting for the thread to
terminate; did not previously show a problem but is safer
- A lot of testing based on recent git checkout 4b4a72e5 (4/10):
- migration of i686 VM from x86_64 host to i686 host to ppc64 host while
running tests inside the VM
- tests with S3 suspend/resume
- tests with snapshots
- multiple-hour tests with VM suspend/resume (using virsh save/restore)
while running a TPM test suite inside the VM
All tests passed; [not all of them were done on the ppc64 host]
v2:
- splitting some of the patches into smaller ones for easier review
- fixes in individual patches
Regards,
Stefan
Stefan Berger (7):
Support for TPM command line options
Add TPM (frontend) hardware interface (TPM TIS) to Qemu
Add a debug register
Build the TPM frontend code
Add a TPM Passthrough backend driver implementation
Introduce --enable-tpm-passthrough configure option
Add fd parameter for TPM passthrough driver
Makefile.target | 3 +
configure | 28 ++
hmp-commands.hx | 2 +
hmp.c | 33 ++
hmp.h | 1 +
hw/tpm_backend.c | 58 ++++
hw/tpm_backend.h | 43 +++
hw/tpm_passthrough.c | 427 ++++++++++++++++++++++++
hw/tpm_tis.c | 892 ++++++++++++++++++++++++++++++++++++++++++++++++++
hw/tpm_tis.h | 78 +++++
monitor.c | 8 +
qapi-schema.json | 31 ++
qemu-config.c | 25 ++
qemu-options.hx | 74 +++++
qmp-commands.hx | 5 +
tpm.c | 235 +++++++++++++
tpm.h | 115 +++++++
vl.c | 19 +
18 files changed, 2077 insertions(+), 0 deletions(-)
create mode 100644 hw/tpm_backend.c
create mode 100644 hw/tpm_backend.h
create mode 100644 hw/tpm_passthrough.c
create mode 100644 hw/tpm_tis.c
create mode 100644 hw/tpm_tis.h
create mode 100644 tpm.c
create mode 100644 tpm.h
--
1.7.7.6
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH V19 1/7] Support for TPM command line options
2012-06-04 19:37 [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
@ 2012-06-04 19:37 ` Stefan Berger
2012-09-27 14:12 ` Corey Bryant
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
` (7 subsequent siblings)
8 siblings, 1 reply; 30+ messages in thread
From: Stefan Berger @ 2012-06-04 19:37 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>
---
hmp-commands.hx | 2 +
hmp.c | 28 +++++++
hmp.h | 1 +
hw/tpm_tis.h | 78 ++++++++++++++++++++
monitor.c | 8 ++
qapi-schema.json | 29 ++++++++
qemu-config.c | 20 +++++
qemu-options.hx | 33 +++++++++
qmp-commands.hx | 5 ++
tpm.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
tpm.h | 81 +++++++++++++++++++++
vl.c | 17 +++++
12 files changed, 515 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 18cb415..08f6942 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1401,6 +1401,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/hmp.c b/hmp.c
index bb0952e..9aedaff 100644
--- a/hmp.c
+++ b/hmp.c
@@ -548,6 +548,34 @@ void hmp_info_block_jobs(Monitor *mon)
}
}
+void hmp_info_tpm(Monitor *mon)
+{
+ TPMInfoList *info_list, *info;
+ Error *err = NULL;
+ unsigned int c = 0;
+
+ info_list = qmp_query_tpm(&err);
+ if (err) {
+ monitor_printf(mon, "TPM device not supported\n");
+ error_free(err);
+ return;
+ }
+
+ monitor_printf(mon, "TPM device:\n");
+
+ for (info = info_list; info; info = info->next) {
+ TPMInfo *ti = info->value;
+ monitor_printf(mon, " tpm%d: model=%s\n",
+ c, ti->model);
+ monitor_printf(mon, " \\ %s: type=%s%s%s\n",
+ ti->id, ti->type,
+ ti->has_path ? ",path=" : "",
+ ti->has_path ? ti->path : "");
+ c++;
+ }
+ qapi_free_TPMInfoList(info_list);
+}
+
void hmp_quit(Monitor *mon, const QDict *qdict)
{
monitor_suspend(mon);
diff --git a/hmp.h b/hmp.h
index 443b812..8e2a858 100644
--- a/hmp.h
+++ b/hmp.h
@@ -33,6 +33,7 @@ void hmp_info_spice(Monitor *mon);
void hmp_info_balloon(Monitor *mon);
void hmp_info_pci(Monitor *mon);
void hmp_info_block_jobs(Monitor *mon);
+void hmp_info_tpm(Monitor *mon);
void hmp_quit(Monitor *mon, const QDict *qdict);
void hmp_stop(Monitor *mon, const QDict *qdict);
void hmp_system_reset(Monitor *mon, const QDict *qdict);
diff --git a/hw/tpm_tis.h b/hw/tpm_tis.h
new file mode 100644
index 0000000..5e1f731
--- /dev/null
+++ b/hw/tpm_tis.h
@@ -0,0 +1,78 @@
+/*
+ * 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 work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * 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-common.h"
+
+#define TPM_TIS_ADDR_BASE 0xFED40000
+
+#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */
+#define TPM_TIS_LOCALITY_SHIFT 12
+#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;
+
+typedef enum {
+ TPM_TIS_STATUS_IDLE = 0,
+ TPM_TIS_STATUS_READY,
+ TPM_TIS_STATUS_COMPLETION,
+ TPM_TIS_STATUS_EXECUTION,
+ TPM_TIS_STATUS_RECEPTION,
+} TPMTISStatus;
+
+/* locality data -- all fields are persisted */
+typedef struct TPMLocality {
+ TPMTISStatus status;
+ 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 {
+ QEMUBH *bh;
+ 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;
+
+#endif /* HW_TPM_TIS_H */
diff --git a/monitor.c b/monitor.c
index 12a6fe2..aec06ce 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"
@@ -2602,6 +2603,13 @@ static mon_cmd_t info_cmds[] = {
.mhandler.info = do_trace_print_events,
},
{
+ .name = "tpm",
+ .args_type = "",
+ .params = "",
+ .help = "show the TPM device",
+ .mhandler.info = hmp_info_tpm,
+ },
+ {
.name = NULL,
},
};
diff --git a/qapi-schema.json b/qapi-schema.json
index 2ca7195..8799322 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1755,3 +1755,32 @@
# Since: 0.14.0
##
{ 'command': 'device_del', 'data': {'id': 'str'} }
+
+##
+# @TPMInfo:
+#
+# Information about the TPM
+#
+# @model: The TPM frontend model, i.e., tpm-tis
+#
+# @id: The ID of the TPM
+#
+# @type: The type of TPM backend, i.e., passthrough
+#
+# @path: #optional Path to the TPM backend device
+#
+# Since: 1.1
+##
+{ 'type': 'TPMInfo',
+ 'data': {'model': 'str', 'id': 'str', 'type': 'str', '*path': 'str' } }
+
+##
+# @query-tpm
+#
+# Return information about the TPM device.
+#
+# Returns: @TPMInfo on success
+#
+# Since: 1.1
+##
+{ 'command': 'query-tpm', 'returns': ['TPMInfo'] }
diff --git a/qemu-config.c b/qemu-config.c
index be84a03..edc8d5d 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -613,6 +613,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,
@@ -628,6 +647,7 @@ static QemuOptsList *vm_config_groups[32] = {
&qemu_machine_opts,
&qemu_boot_opts,
&qemu_iscsi_opts,
+ &qemu_tpmdev_opts,
NULL,
};
diff --git a/qemu-options.hx b/qemu-options.hx
index 8b66264..143d92d 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2012,6 +2012,39 @@ ETEXI
DEFHEADING()
+#ifdef CONFIG_TPM
+DEFHEADING(TPM device options:)
+
+DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
+ "-tpmdev [<type>],id=str[,option][,option][,...]\n",
+ QEMU_ARCH_ALL)
+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()
+
+#endif
+
DEFHEADING(Linux/Multiboot boot specific:)
STEXI
diff --git a/qmp-commands.hx b/qmp-commands.hx
index db980fa..82f1a6d 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2142,3 +2142,8 @@ EQMP
.args_type = "implements:s?,abstract:b?",
.mhandler.cmd_new = qmp_marshal_input_qom_list_types,
},
+ {
+ .name = "query-tpm",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_tpm,
+ },
diff --git a/tpm.c b/tpm.c
new file mode 100644
index 0000000..778de42
--- /dev/null
+++ b/tpm.c
@@ -0,0 +1,213 @@
+/*
+ * 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 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * Based on net.c
+ */
+#include "config.h"
+
+#include "monitor.h"
+#include "qerror.h"
+#include "tpm.h"
+#include "qmp-commands.h"
+
+static QLIST_HEAD(, TPMBackend) tpm_backends =
+ QLIST_HEAD_INITIALIZER(tpm_backends);
+
+#ifdef CONFIG_TPM
+
+static const TPMDriverOps *bes[] = {
+ NULL,
+};
+
+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];
+}
+
+/*
+ * Walk the list of available TPM backend drivers and display them on the
+ * screen.
+ */
+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");
+}
+
+/*
+ * Find the TPM with the given Id
+ */
+TPMBackend *qemu_find_tpm(const char *id)
+{
+ TPMBackend *drv;
+
+ QLIST_FOREACH(drv, &tpm_backends, list) {
+ if (!strcmp(drv->id, id)) {
+ return drv;
+ }
+ }
+
+ return NULL;
+}
+
+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);
+}
+
+/*
+ * Walk the list of TPM backend drivers that are in use and call their
+ * destroy function to have them cleaned up.
+ */
+void tpm_cleanup(void)
+{
+ TPMBackend *drv, *next;
+
+ QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
+ QLIST_REMOVE(drv, list);
+ drv->ops->destroy(drv);
+ }
+}
+
+/*
+ * Initialize the TPM. Process the tpmdev command line options describing the
+ * TPM backend.
+ */
+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;
+}
+
+/*
+ * Parse the TPM configuration options.
+ * It is possible to pass an option '-tpmdev none' to not activate any TPM.
+ * To display all available TPM backends the user may use '-tpmdev ?'
+ */
+int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
+{
+ QemuOpts *opts;
+
+ if (strcmp("none", optarg) != 0) {
+ if (*optarg == '?') {
+ tpm_display_backend_drivers();
+ return -1;
+ }
+ opts = qemu_opts_parse(opts_list, optarg, 1);
+ if (!opts) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+#endif /* CONFIG_TPM */
+
+static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
+{
+ TPMInfo *res = g_new0(TPMInfo, 1);
+
+ res->model = g_strdup(drv->fe_model);
+ res->id = g_strdup(drv->id);
+ if (drv->path) {
+ res->path = g_strdup(drv->path);
+ }
+ res->type = g_strdup(drv->ops->id);
+
+ return res;
+}
+
+/*
+ * Walk the list of active TPM backends and collect information about them
+ * following the schema description in qapi-schema.json.
+ */
+TPMInfoList *qmp_query_tpm(Error **errp)
+{
+ TPMBackend *drv;
+ TPMInfoList *info, *head = NULL, *cur_item = NULL;
+
+ QLIST_FOREACH(drv, &tpm_backends, list) {
+ info = g_new0(TPMInfoList, 1);
+ info->value = qmp_query_tpm_inst(drv);
+
+ if (!cur_item) {
+ head = cur_item = info;
+ } else {
+ cur_item->next = info;
+ cur_item = info;
+ }
+ }
+
+ return head;
+}
diff --git a/tpm.h b/tpm.h
new file mode 100644
index 0000000..7fdd1db
--- /dev/null
+++ b/tpm.h
@@ -0,0 +1,81 @@
+/*
+ * 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 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#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 *path;
+ 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;
+
+ 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 (*deliver_request)(TPMBackend *t);
+
+ void (*reset)(TPMBackend *t);
+
+ bool (*get_tpm_established_flag)(TPMBackend *t);
+};
+
+#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
+
+int 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 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 23ab3a3..3e784f5 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"
@@ -2674,6 +2675,13 @@ int main(int argc, char **argv, char **envp)
ram_size = value;
break;
}
+#ifdef CONFIG_TPM
+ case QEMU_OPTION_tpmdev:
+ if (tpm_config_parse(qemu_find_opts("tpmdev"), optarg) < 0) {
+ exit(1);
+ }
+ break;
+#endif
case QEMU_OPTION_mempath:
mem_path = optarg;
break;
@@ -3405,6 +3413,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);
@@ -3657,6 +3671,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.7.6
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2012-06-04 19:37 [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 1/7] Support for TPM command line options Stefan Berger
@ 2012-06-04 19:37 ` Stefan Berger
2012-09-27 14:22 ` Corey Bryant
2012-10-03 18:35 ` Corey Bryant
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 3/7] Add a debug register Stefan Berger
` (6 subsequent siblings)
8 siblings, 2 replies; 30+ messages in thread
From: Stefan Berger @ 2012-06-04 19:37 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>
---
hw/tpm_tis.c | 822 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 822 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..02b9c2e
--- /dev/null
+++ b/hw/tpm_tis.c
@@ -0,0 +1,822 @@
+/*
+ * 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 work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * 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/pci_ids.h"
+#include "hw/tpm_tis.h"
+#include "qemu-error.h"
+#include "qemu-common.h"
+
+/*#define DEBUG_TIS */
+
+#ifdef DEBUG_TIS
+#define dprintf(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+ do { } while (0)
+#endif
+
+/* 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 PCI_VENDOR_ID_IBM
+#define TPM_TIS_TPM_RID 0x0001
+
+#define TPM_TIS_NO_DATA_BYTE 0xff
+
+/* utility functions */
+
+static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
+{
+ return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
+}
+
+static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+ return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
+}
+
+static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
+{
+#ifdef DEBUG_TIS
+ uint32_t len, i;
+
+ len = tpm_tis_get_size_from_buffer(sb);
+ dprintf("tpm_tis: %s length = %d\n", string, len);
+ for (i = 0; i < len; i++) {
+ if (i && !(i % 16)) {
+ dprintf("\n");
+ }
+ dprintf("%.2X ", sb->buffer[i]);
+ }
+ dprintf("\n");
+#endif
+}
+
+/*
+ * Send a request to the TPM.
+ */
+static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
+{
+ TPMTISState *tis = &s->s.tis;
+
+ tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
+
+ 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].status = TPM_TIS_STATUS_EXECUTION;
+ tis->loc[locty].sts &= ~TPM_TIS_STS_EXPECT;
+
+ s->be_driver->ops->deliver_request(s->be_driver);
+}
+
+/* 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)) {
+ dprintf("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
+ 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;
+
+ dprintf("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty);
+
+ 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;
+
+ dprintf("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty);
+
+ /*
+ * 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].status = TPM_TIS_STATUS_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].status == TPM_TIS_STATUS_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;
+ */
+ dprintf("tpm_tis: Locality %d is busy - deferring abort\n",
+ busy_locty);
+ return;
+ }
+ }
+
+ tpm_tis_abort(s, locty);
+}
+
+static void tpm_tis_receive_bh(void *opaque)
+{
+ TPMState *s = opaque;
+ TPMTISState *tis = &s->s.tis;
+ uint8_t locty = s->command_locty;
+
+ tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE;
+ tis->loc[locty].status = TPM_TIS_STATUS_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);
+ }
+
+#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
+}
+
+/*
+ * 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;
+
+ assert(s->command_locty == locty);
+
+ qemu_bh_schedule(tis->bh);
+}
+
+/*
+ * Read a byte of response data
+ */
+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
+ }
+ dprintf("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n",
+ ret, tis->loc[locty].r_offset-1);
+ }
+
+ 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);
+
+ if (s->be_driver->ops->had_startup_error(s->be_driver)) {
+ 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].status) {
+ case TPM_TIS_STATUS_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;
+ }
+
+ if (shift) {
+ val >>= shift;
+ }
+
+ dprintf("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
+
+ 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;
+
+ dprintf("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
+
+ if (locty == 4 && !hw_access) {
+ dprintf("tpm_tis: Access to locality 4 only allowed from hardware\n");
+ return;
+ }
+
+ if (s->be_driver->ops->had_startup_error(s->be_driver)) {
+ 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) {
+ dprintf("tpm_tis: Releasing locality %d\n", locty);
+
+ 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)) {
+ dprintf("tpm_tis: Locality %d requests use.\n", c);
+ newlocty = c;
+ break;
+ }
+ }
+ dprintf("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
+ "Next active locality: %d\n",
+ newlocty);
+
+ 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;
+ dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
+ "Locality %d seized from locality %d\n",
+ locty, tis->active_locty);
+ dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
+ 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);
+ dprintf("tpm_tis: Lowering IRQ\n");
+ }
+ }
+ 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].status) {
+
+ case TPM_TIS_STATUS_READY:
+ tis->loc[locty].w_offset = 0;
+ tis->loc[locty].r_offset = 0;
+ break;
+
+ case TPM_TIS_STATUS_IDLE:
+ tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
+ tis->loc[locty].status = TPM_TIS_STATUS_READY;
+ tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
+ break;
+
+ case TPM_TIS_STATUS_EXECUTION:
+ case TPM_TIS_STATUS_RECEPTION:
+ /* abort currently running command */
+ dprintf("tpm_tis: %s: Initiating abort.\n",
+ __func__);
+ tpm_tis_prep_abort(s, locty, locty);
+ break;
+
+ case TPM_TIS_STATUS_COMPLETION:
+ tis->loc[locty].w_offset = 0;
+ tis->loc[locty].r_offset = 0;
+ /* shortcut to ready state with C/R set */
+ tis->loc[locty].status = TPM_TIS_STATUS_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].status) {
+ case TPM_TIS_STATUS_RECEPTION:
+ tpm_tis_tpm_send(s, locty);
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
+ switch (tis->loc[locty].status) {
+ case TPM_TIS_STATUS_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].status == TPM_TIS_STATUS_IDLE ||
+ tis->loc[locty].status == TPM_TIS_STATUS_EXECUTION ||
+ tis->loc[locty].status == TPM_TIS_STATUS_COMPLETION) {
+ /* drop the byte */
+ } else {
+ dprintf("tpm_tis: Byte to send to TPM: %02x\n", (uint8_t)val);
+ if (tis->loc[locty].status == TPM_TIS_STATUS_READY) {
+ tis->loc[locty].status = TPM_TIS_STATUS_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;
+ }
+}
+
+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_LITTLE_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 = DO_UPCAST(TPMState, busdev.qdev, d);
+ TPMTISState *tis = &s->s.tis;
+ int c;
+
+ 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].status = TPM_TIS_STATUS_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;
+
+ 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);
+ goto err_exit;
+ }
+
+ s->be_driver->fe_model = "tpm-tis";
+
+ if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) {
+ goto err_exit;
+ }
+
+ tis->bh = qemu_bh_new(tpm_tis_receive_bh, s);
+
+ if (tis->irq_num > 15) {
+ error_report("IRQ %d for TPM TIS is outside valid range of 0 to 15.\n",
+ tis->irq_num);
+ goto err_exit;
+ }
+
+ isa_init_irq(dev, &tis->irq, tis->irq_num);
+
+ memory_region_init_io(&s->mmio, &tpm_tis_memory_ops, s, "tpm-tis-mmio",
+ TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT);
+ 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_del_subregion(get_system_memory(), &s->mmio);
+ memory_region_destroy(&s->mmio);
+
+ err_exit:
+ return -1;
+}
+
+static const VMStateDescription vmstate_tpm_tis = {
+ .name = "tpm",
+ .unmigratable = 1,
+};
+
+static Property tpm_tis_properties[] = {
+ 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_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+
+ ic->init = tpm_tis_init;
+
+ dc->props = tpm_tis_properties;
+ dc->reset = tpm_tis_reset;
+ dc->vmsd = &vmstate_tpm_tis;
+}
+
+static TypeInfo tpm_tis_info = {
+ .name = "tpm-tis",
+ .parent = TYPE_ISA_DEVICE,
+ .class_init = tpm_tis_class_initfn,
+ .instance_size = sizeof(TPMState),
+};
+
+static void tpm_tis_register(void)
+{
+ type_register_static(&tpm_tis_info);
+}
+
+type_init(tpm_tis_register)
--
1.7.7.6
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH V19 3/7] Add a debug register
2012-06-04 19:37 [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 1/7] Support for TPM command line options Stefan Berger
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2012-06-04 19:37 ` Stefan Berger
2012-09-27 14:23 ` Corey Bryant
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 4/7] Build the TPM frontend code Stefan Berger
` (5 subsequent siblings)
8 siblings, 1 reply; 30+ messages in thread
From: Stefan Berger @ 2012-06-04 19:37 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>
---
hw/tpm_tis.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 70 insertions(+), 0 deletions(-)
diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
index 02b9c2e..5f8899d 100644
--- a/hw/tpm_tis.c
+++ b/hw/tpm_tis.c
@@ -52,6 +52,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)
@@ -97,6 +100,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);
+
/* utility functions */
static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
@@ -331,6 +339,63 @@ 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;
+
+ dprintf("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].status);
+
+ for (idx = 0; regs[idx] != 0xfff; idx++) {
+ dprintf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
+ (uint32_t)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
+ }
+
+ dprintf("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++) {
+ dprintf("%c%02x%s",
+ tis->loc[locty].r_offset == idx ? '>' : ' ',
+ tis->loc[locty].r_buffer.buffer[idx],
+ ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
+ }
+ dprintf("\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++) {
+ dprintf("%c%02x%s",
+ tis->loc[locty].w_offset == idx ? '>' : ' ',
+ tis->loc[locty].w_buffer.buffer[idx],
+ ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
+ }
+ dprintf("\n");
+}
+#endif
+
/*
* Read a register of the TIS interface
* See specs pages 33-63 for description of the registers
@@ -400,6 +465,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
}
if (shift) {
--
1.7.7.6
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH V19 4/7] Build the TPM frontend code
2012-06-04 19:37 [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (2 preceding siblings ...)
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 3/7] Add a debug register Stefan Berger
@ 2012-06-04 19:37 ` Stefan Berger
2012-09-27 14:24 ` Corey Bryant
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
` (4 subsequent siblings)
8 siblings, 1 reply; 30+ messages in thread
From: Stefan Berger @ 2012-06-04 19:37 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 | 2 ++
configure | 11 +++++++++++
2 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/Makefile.target b/Makefile.target
index 1582904..cdf108a 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -192,6 +192,8 @@ obj-$(CONFIG_KVM) += kvm.o kvm-all.o
obj-$(CONFIG_NO_KVM) += kvm-stub.o
obj-$(CONFIG_VGA) += vga.o
obj-y += memory.o savevm.o cputlb.o
+obj-y += tpm.o
+obj-$(CONFIG_TPM) += tpm_tis.o
LIBS+=-lz
obj-i386-$(CONFIG_KVM) += hyperv.o
diff --git a/configure b/configure
index 1f338f8..d805c20 100755
--- a/configure
+++ b/configure
@@ -194,6 +194,7 @@ zlib="yes"
guest_agent="yes"
libiscsi=""
coroutine=""
+tpm="no"
# parse CC options first
for opt do
@@ -824,6 +825,8 @@ for opt do
;;
--disable-guest-agent) guest_agent="no"
;;
+ --enable-tpm) tpm="yes"
+ ;;
*) echo "ERROR: unknown option $opt"; show_help="yes"
;;
esac
@@ -1110,6 +1113,7 @@ echo " --disable-guest-agent disable building of the QEMU Guest Agent"
echo " --enable-guest-agent enable building of the QEMU Guest Agent"
echo " --with-coroutine=BACKEND coroutine backend. Supported options:"
echo " gthread, ucontext, sigaltstack, windows"
+echo " --enable-tpm enable TPM support"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@@ -3029,6 +3033,7 @@ echo "OpenGL support $opengl"
echo "libiscsi support $libiscsi"
echo "build guest agent $guest_agent"
echo "coroutine backend $coroutine_backend"
+echo "TPM support $tpm"
if test "$sdl_too_old" = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3911,6 +3916,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.7.6
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH V19 5/7] Add a TPM Passthrough backend driver implementation
2012-06-04 19:37 [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (3 preceding siblings ...)
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 4/7] Build the TPM frontend code Stefan Berger
@ 2012-06-04 19:37 ` Stefan Berger
2012-09-27 14:28 ` Corey Bryant
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
` (3 subsequent siblings)
8 siblings, 1 reply; 30+ messages in thread
From: Stefan Berger @ 2012-06-04 19:37 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>
---
Makefile.target | 3 +-
configure | 3 +
hw/tpm_backend.c | 58 ++++++++
hw/tpm_backend.h | 43 ++++++
hw/tpm_passthrough.c | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++
qemu-options.hx | 38 +++++-
tpm.c | 18 +++
tpm.h | 33 ++++
vl.c | 2 +
9 files changed, 594 insertions(+), 2 deletions(-)
create mode 100644 hw/tpm_backend.c
create mode 100644 hw/tpm_backend.h
create mode 100644 hw/tpm_passthrough.c
diff --git a/Makefile.target b/Makefile.target
index cdf108a..ef063c8 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -193,7 +193,8 @@ obj-$(CONFIG_NO_KVM) += kvm-stub.o
obj-$(CONFIG_VGA) += vga.o
obj-y += memory.o savevm.o cputlb.o
obj-y += tpm.o
-obj-$(CONFIG_TPM) += tpm_tis.o
+obj-$(CONFIG_TPM) += tpm_tis.o tpm_backend.o
+obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
LIBS+=-lz
obj-i386-$(CONFIG_KVM) += hyperv.o
diff --git a/configure b/configure
index d805c20..804d8c8 100755
--- a/configure
+++ b/configure
@@ -3918,6 +3918,9 @@ fi
if test "$tpm" = "yes"; then
if test "$target_softmmu" = "yes" ; then
+ if test "$linux" = "yes" ; then
+ echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
+ fi
echo "CONFIG_TPM=y" >> $config_host_mak
fi
fi
diff --git a/hw/tpm_backend.c b/hw/tpm_backend.c
new file mode 100644
index 0000000..4cf0809
--- /dev/null
+++ b/hw/tpm_backend.c
@@ -0,0 +1,58 @@
+/*
+ * common TPM backend driver functions
+ *
+ * Copyright (c) 2012 IBM Corporation
+ * Authors:
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * 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 "tpm.h"
+#include "qemu-thread.h"
+#include "hw/tpm_backend.h"
+
+void tpm_backend_thread_deliver_request(TPMBackendThread *tbt)
+{
+ g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL);
+}
+
+void tpm_backend_thread_create(TPMBackendThread *tbt,
+ GFunc func, gpointer user_data)
+{
+ if (!tbt->pool) {
+ tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL);
+ g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
+ }
+}
+
+void tpm_backend_thread_end(TPMBackendThread *tbt)
+{
+ if (tbt->pool) {
+ g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
+ g_thread_pool_free(tbt->pool, FALSE, TRUE);
+ tbt->pool = NULL;
+ }
+}
+
+void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt,
+ GFunc func, gpointer user_data)
+{
+ if (!tbt->pool) {
+ tpm_backend_thread_create(tbt, func, user_data);
+ } else {
+ g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_TPM_RESET,
+ NULL);
+ }
+}
diff --git a/hw/tpm_backend.h b/hw/tpm_backend.h
new file mode 100644
index 0000000..f5fe198
--- /dev/null
+++ b/hw/tpm_backend.h
@@ -0,0 +1,43 @@
+/*
+ * common TPM backend driver functions
+ *
+ * Copyright (c) 2012 IBM Corporation
+ * Authors:
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * 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/>
+ */
+
+#ifndef HW_TPM_BACKEND_H
+#define HW_TPM_BACKEND_H
+
+typedef struct TPMBackendThread {
+ GThreadPool *pool;
+} TPMBackendThread;
+
+void tpm_backend_thread_deliver_request(TPMBackendThread *tbt);
+void tpm_backend_thread_create(TPMBackendThread *tbt,
+ GFunc func, gpointer user_data);
+void tpm_backend_thread_end(TPMBackendThread *tbt);
+void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt,
+ GFunc func, gpointer user_data);
+
+typedef enum TPMBackendCmd {
+ TPM_BACKEND_CMD_INIT = 1,
+ TPM_BACKEND_CMD_PROCESS_CMD,
+ TPM_BACKEND_CMD_END,
+ TPM_BACKEND_CMD_TPM_RESET,
+} TPMBackendCmd;
+
+#endif /* HW_TPM_BACKEND_H */
diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
new file mode 100644
index 0000000..8914705
--- /dev/null
+++ b/hw/tpm_passthrough.c
@@ -0,0 +1,398 @@
+/*
+ * 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 "qemu_socket.h"
+#include "tpm.h"
+#include "hw/hw.h"
+#include "hw/tpm_tis.h"
+#include "hw/tpm_backend.h"
+#include "hw/pc.h"
+
+/* #define DEBUG_TPM */
+
+#ifdef DEBUG_TPM
+#define dprintf(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+ do { } while (0)
+#endif
+
+/* data structures */
+
+typedef struct TPMPassthruThreadParams {
+ TPMState *tpm_state;
+
+ TPMRecvDataCB *recv_data_callback;
+ TPMBackend *tb;
+} TPMPassthruThreadParams;
+
+struct TPMPassthruState {
+ TPMBackendThread tbt;
+
+ TPMPassthruThreadParams tpm_thread_params;
+
+ char *tpm_dev;
+ 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)
+{
+ return send_all(fd, buf, len);
+}
+
+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 uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
+{
+ return be32_to_cpu(*(uint32_t *)&buf[2]);
+}
+
+static int tpm_passthrough_unix_tx_bufs(int tpm_fd,
+ const uint8_t *in, uint32_t in_len,
+ uint8_t *out, uint32_t out_len)
+{
+ int ret;
+
+ ret = tpm_passthrough_unix_write(tpm_fd, in, in_len);
+ if (ret != in_len) {
+ error_report("tpm_passthrough: error while transmitting data "
+ "to TPM: %s (%i)\n",
+ strerror(errno), errno);
+ goto err_exit;
+ }
+
+ ret = tpm_passthrough_unix_read(tpm_fd, out, out_len);
+ if (ret < 0) {
+ error_report("tpm_passthrough: error while reading data from "
+ "TPM: %s (%i)\n",
+ strerror(errno), errno);
+ } else if (ret < sizeof(struct tpm_resp_hdr) ||
+ tpm_passthrough_get_size_from_buffer(out) != ret) {
+ ret = -1;
+ error_report("tpm_passthrough: received invalid response "
+ "packet from TPM\n");
+ }
+
+err_exit:
+ if (ret < 0) {
+ tpm_write_fatal_error_response(out, out_len);
+ }
+
+ return ret;
+}
+
+static int tpm_passthrough_unix_transfer(int tpm_fd,
+ const TPMLocality *cmd_locty)
+{
+ return tpm_passthrough_unix_tx_bufs(tpm_fd,
+ cmd_locty->w_buffer.buffer,
+ cmd_locty->w_offset,
+ cmd_locty->r_buffer.buffer,
+ cmd_locty->r_buffer.size);
+}
+
+static void tpm_passthrough_worker_thread(gpointer data,
+ gpointer user_data)
+{
+ TPMPassthruThreadParams *thr_parms = user_data;
+ TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
+ TPMBackendCmd cmd = (TPMBackendCmd)data;
+
+ dprintf("tpm_passthrough: processing command type %ld\n", cmd);
+
+ switch (cmd) {
+ case TPM_BACKEND_CMD_PROCESS_CMD:
+ tpm_passthrough_unix_transfer(tpm_pt->tpm_fd,
+ thr_parms->tpm_state->cmd_locty);
+
+ thr_parms->recv_data_callback(thr_parms->tpm_state,
+ thr_parms->tpm_state->command_locty);
+ break;
+ case TPM_BACKEND_CMD_INIT:
+ case TPM_BACKEND_CMD_END:
+ case TPM_BACKEND_CMD_TPM_RESET:
+ /* nothing to do */
+ break;
+ }
+}
+
+/*
+ * 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_backend_thread_end(&tpm_pt->tbt);
+
+ tpm_backend_thread_create(&tpm_pt->tbt,
+ tpm_passthrough_worker_thread,
+ &tb->s.tpm_pt->tpm_thread_params);
+
+ 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;
+
+ dprintf("tpm_passthrough: CALL TO TPM_RESET!\n");
+
+ tpm_backend_thread_end(&tpm_pt->tbt);
+
+ 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;
+
+ 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);
+ sb->size = wanted_size;
+ }
+ return sb->size;
+}
+
+static void tpm_passthrough_deliver_request(TPMBackend *tb)
+{
+ TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+ tpm_backend_thread_deliver_request(&tpm_pt->tbt);
+}
+
+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;
+
+ value = qemu_opt_get(opts, "path");
+ if (!value) {
+ value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+ }
+
+ tb->s.tpm_pt->tpm_dev = g_strdup(value);
+
+ tb->path = g_strdup(tb->s.tpm_pt->tpm_dev);
+
+ 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_free_parameters;
+ }
+
+ 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_free_parameters:
+ g_free(tb->path);
+ tb->path = NULL;
+
+ g_free(tb->s.tpm_pt->tpm_dev);
+ tb->s.tpm_pt->tpm_dev = NULL;
+
+ return 1;
+}
+
+static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
+{
+ TPMBackend *tb;
+
+ tb = g_new0(TPMBackend, 1);
+ tb->s.tpm_pt = g_new0(TPMPassthruState, 1);
+ tb->id = g_strdup(id);
+
+ 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_backend_thread_end(&tpm_pt->tbt);
+
+ close(tpm_pt->tpm_fd);
+
+ g_free(tb->id);
+ g_free(tb->path);
+ g_free(tb->s.tpm_pt->tpm_dev);
+ 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,
+ .deliver_request = tpm_passthrough_deliver_request,
+ .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
+};
diff --git a/qemu-options.hx b/qemu-options.hx
index 143d92d..0b9fa0b 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2013,10 +2013,12 @@ ETEXI
DEFHEADING()
#ifdef CONFIG_TPM
+# ifdef CONFIG_TPM_PASSTHROUGH
DEFHEADING(TPM device options:)
DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
- "-tpmdev [<type>],id=str[,option][,option][,...]\n",
+ "-tpmdev passthrough,id=id[,path=path]\n"
+ " use path to provide path to a character device; default is /dev/tpm0\n",
QEMU_ARCH_ALL)
STEXI
@@ -2026,6 +2028,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.
@@ -2037,12 +2040,45 @@ 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
DEFHEADING()
+# endif
#endif
DEFHEADING(Linux/Multiboot boot specific:)
diff --git a/tpm.c b/tpm.c
index 778de42..79530c9 100644
--- a/tpm.c
+++ b/tpm.c
@@ -24,9 +24,26 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =
#ifdef CONFIG_TPM
static const TPMDriverOps *bes[] = {
+#ifdef CONFIG_TPM_PASSTHROUGH
+ &tpm_passthrough_driver,
+#endif
NULL,
};
+/*
+ * Write an error message in the given output buffer.
+ */
+void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
+{
+ if (out_len >= sizeof(struct tpm_resp_hdr)) {
+ struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
+
+ resp->tag = cpu_to_be16(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;
@@ -182,6 +199,7 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
res->id = g_strdup(drv->id);
if (drv->path) {
res->path = g_strdup(drv->path);
+ res->has_path = true;
}
res->type = g_strdup(drv->ops->id);
diff --git a/tpm.h b/tpm.h
index 7fdd1db..bacaff3 100644
--- a/tpm.h
+++ b/tpm.h
@@ -18,12 +18,18 @@
struct TPMDriverOps;
typedef struct TPMDriverOps TPMDriverOps;
+typedef struct TPMPassthruState TPMPassthruState;
+
typedef struct TPMBackend {
char *id;
const char *fe_model;
char *path;
const TPMDriverOps *ops;
+ union {
+ TPMPassthruState *tpm_pt;
+ } s;
+
QLIST_ENTRY(TPMBackend) list;
} TPMBackend;
@@ -71,11 +77,38 @@ struct TPMDriverOps {
#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
+
int 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 tpm_display_backend_drivers(void);
const TPMDriverOps *tpm_get_backend_driver(const char *id);
+void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len);
+
+extern const TPMDriverOps tpm_passthrough_driver;
#endif /* QEMU_TPM_H */
diff --git a/vl.c b/vl.c
index 3e784f5..6928e67 100644
--- a/vl.c
+++ b/vl.c
@@ -2676,11 +2676,13 @@ int main(int argc, char **argv, char **envp)
break;
}
#ifdef CONFIG_TPM
+# ifdef CONFIG_TPM_PASSTHROUGH
case QEMU_OPTION_tpmdev:
if (tpm_config_parse(qemu_find_opts("tpmdev"), optarg) < 0) {
exit(1);
}
break;
+# endif
#endif
case QEMU_OPTION_mempath:
mem_path = optarg;
--
1.7.7.6
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH V19 6/7] Introduce --enable-tpm-passthrough configure option
2012-06-04 19:37 [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (4 preceding siblings ...)
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2012-06-04 19:37 ` Stefan Berger
2012-09-27 14:29 ` Corey Bryant
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
` (2 subsequent siblings)
8 siblings, 1 reply; 30+ messages in thread
From: Stefan Berger @ 2012-06-04 19:37 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 804d8c8..8d8bee8 100755
--- a/configure
+++ b/configure
@@ -195,6 +195,7 @@ guest_agent="yes"
libiscsi=""
coroutine=""
tpm="no"
+tpm_passthrough="no"
# parse CC options first
for opt do
@@ -827,11 +828,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)
@@ -1114,6 +1124,7 @@ echo " --enable-guest-agent enable building of the QEMU Guest Agent"
echo " --with-coroutine=BACKEND coroutine backend. Supported options:"
echo " gthread, ucontext, sigaltstack, windows"
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
@@ -3034,6 +3045,7 @@ echo "libiscsi support $libiscsi"
echo "build guest agent $guest_agent"
echo "coroutine backend $coroutine_backend"
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"
@@ -3919,7 +3931,9 @@ fi
if test "$tpm" = "yes"; then
if test "$target_softmmu" = "yes" ; then
if test "$linux" = "yes" ; then
- echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
+ if test "$tpm_passthrough" = "yes" ; then
+ echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
+ fi
fi
echo "CONFIG_TPM=y" >> $config_host_mak
fi
--
1.7.7.6
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH V19 7/7] Add fd parameter for TPM passthrough driver
2012-06-04 19:37 [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (5 preceding siblings ...)
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
@ 2012-06-04 19:37 ` Stefan Berger
2012-09-27 14:35 ` Corey Bryant
2012-10-03 18:46 ` Corey Bryant
2012-06-04 19:56 ` [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
2012-09-27 14:59 ` Corey Bryant
8 siblings, 2 replies; 30+ messages in thread
From: Stefan Berger @ 2012-06-04 19:37 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>
---
hmp.c | 7 +++++-
hw/tpm_passthrough.c | 53 ++++++++++++++++++++++++++++++++++++++-----------
qapi-schema.json | 4 ++-
qemu-config.c | 5 ++++
qemu-options.hx | 11 +++++++--
tpm.c | 4 +++
tpm.h | 1 +
7 files changed, 68 insertions(+), 17 deletions(-)
diff --git a/hmp.c b/hmp.c
index 9aedaff..3176c74 100644
--- a/hmp.c
+++ b/hmp.c
@@ -567,10 +567,15 @@ void hmp_info_tpm(Monitor *mon)
TPMInfo *ti = info->value;
monitor_printf(mon, " tpm%d: model=%s\n",
c, ti->model);
- monitor_printf(mon, " \\ %s: type=%s%s%s\n",
+ monitor_printf(mon, " \\ %s: type=%s%s%s",
ti->id, ti->type,
ti->has_path ? ",path=" : "",
ti->has_path ? ti->path : "");
+ if (ti->has_fd) {
+ monitor_printf(mon, ",fd=%" PRId64,
+ ti->fd);
+ }
+ monitor_printf(mon, "\n");
c++;
}
qapi_free_TPMInfoList(info_list);
diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
index 8914705..44cf272 100644
--- a/hw/tpm_passthrough.c
+++ b/hw/tpm_passthrough.c
@@ -305,26 +305,54 @@ static int tpm_passthrough_test_tpmdev(int fd)
static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
{
const char *value;
+ 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=");
+ goto err_exit;
+ }
+
+ 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");
+ goto err_exit;
+ }
+
+ tb->tpm_fd = &tb->s.tpm_pt->tpm_fd;
+ } else {
+ value = qemu_opt_get(opts, "path");
+ if (!value) {
+ value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+ }
- tb->s.tpm_pt->tpm_dev = g_strdup(value);
+ tb->s.tpm_pt->tpm_dev = g_strdup(value);
- tb->path = g_strdup(tb->s.tpm_pt->tpm_dev);
+ tb->path = g_strdup(value);
+
+ 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_free_parameters;
+ }
+ }
+
+ 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;
+ }
- 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);
+ /* only allow character devices for now */
+ if (!S_ISCHR(statbuf.st_mode)) {
+ error_report("TPM file descriptor is not a character device");
goto err_free_parameters;
}
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);
+ error_report("Device is not a TPM.\n");
goto err_close_tpmdev;
}
@@ -341,6 +369,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
g_free(tb->s.tpm_pt->tpm_dev);
tb->s.tpm_pt->tpm_dev = NULL;
+ err_exit:
return 1;
}
diff --git a/qapi-schema.json b/qapi-schema.json
index 8799322..9d6f664 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1769,10 +1769,12 @@
#
# @path: #optional Path to the TPM backend device
#
+# @fd: #optional File descriptor for the TPM backend device
# Since: 1.1
##
{ 'type': 'TPMInfo',
- 'data': {'model': 'str', 'id': 'str', 'type': 'str', '*path': 'str' } }
+ 'data': {'model': 'str', 'id': 'str', 'type': 'str', '*path': 'str',
+ '*fd' : 'int' } }
##
# @query-tpm
diff --git a/qemu-config.c b/qemu-config.c
index edc8d5d..a5e2677 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -628,6 +628,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 0b9fa0b..cb9892b 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2017,8 +2017,9 @@ DEFHEADING()
DEFHEADING(TPM device options:)
DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
- "-tpmdev passthrough,id=id[,path=path]\n"
- " use path to provide path to a character device; default is /dev/tpm0\n",
+ "-tpmdev passthrough,id=id[,path=path][,fd=h]\n"
+ " use path to provide path to a character device; default is /dev/tpm0\n"
+ " use fd to provide a file descriptor to a character device\n",
QEMU_ARCH_ALL)
STEXI
@@ -2040,7 +2041,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.
@@ -2049,6 +2050,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
diff --git a/tpm.c b/tpm.c
index 79530c9..64d3140 100644
--- a/tpm.c
+++ b/tpm.c
@@ -201,6 +201,10 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
res->path = g_strdup(drv->path);
res->has_path = true;
}
+ if (drv->tpm_fd != NULL && *drv->tpm_fd >= 0) {
+ res->fd = *drv->tpm_fd;
+ res->has_fd = true;
+ }
res->type = g_strdup(drv->ops->id);
return res;
diff --git a/tpm.h b/tpm.h
index bacaff3..00f7600 100644
--- a/tpm.h
+++ b/tpm.h
@@ -24,6 +24,7 @@ typedef struct TPMBackend {
char *id;
const char *fe_model;
char *path;
+ int *tpm_fd;
const TPMDriverOps *ops;
union {
--
1.7.7.6
^ permalink raw reply related [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration
2012-06-04 19:37 [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (6 preceding siblings ...)
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
@ 2012-06-04 19:56 ` Stefan Weil
2012-06-04 23:08 ` Anthony Liguori
2012-09-27 14:59 ` Corey Bryant
8 siblings, 1 reply; 30+ messages in thread
From: Stefan Weil @ 2012-06-04 19:56 UTC (permalink / raw)
To: Stefan Berger; +Cc: root, mst, qemu-devel, anthony, andreas.niederl
Am 04.06.2012 21:37, schrieb Stefan Berger:
> From: root <root@d941f-5.watson.ibm.com>
>
> 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,
would you mind replacing 'Qemu' by 'QEMU' in all mail subjects, commit
messages and maybe also in your patches (which I did not review) when
you create the next version of these patches?
'QEMU' is the official spelling.
Regards,
Stefan Weil
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration
2012-06-04 19:56 ` [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
@ 2012-06-04 23:08 ` Anthony Liguori
0 siblings, 0 replies; 30+ messages in thread
From: Anthony Liguori @ 2012-06-04 23:08 UTC (permalink / raw)
To: Stefan Weil; +Cc: mst, andreas.niederl, qemu-devel, Stefan Berger
On 06/05/2012 03:56 AM, Stefan Weil wrote:
> Am 04.06.2012 21:37, schrieb Stefan Berger:
>> From: root <root@d941f-5.watson.ibm.com>
>>
>> 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,
>
> would you mind replacing 'Qemu' by 'QEMU' in all mail subjects, commit
> messages and maybe also in your patches (which I did not review) when
> you create the next version of these patches?
>
> 'QEMU' is the official spelling.
Although don't do a v20 just for this please.
Regards,
Anthony Liguori
>
> Regards,
>
> Stefan Weil
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 1/7] Support for TPM command line options
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 1/7] Support for TPM command line options Stefan Berger
@ 2012-09-27 14:12 ` Corey Bryant
2012-10-24 19:06 ` Stefan Berger
0 siblings, 1 reply; 30+ messages in thread
From: Corey Bryant @ 2012-09-27 14:12 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 06/04/2012 03:37 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>
> ---
> hmp-commands.hx | 2 +
> hmp.c | 28 +++++++
> hmp.h | 1 +
> hw/tpm_tis.h | 78 ++++++++++++++++++++
> monitor.c | 8 ++
> qapi-schema.json | 29 ++++++++
> qemu-config.c | 20 +++++
> qemu-options.hx | 33 +++++++++
> qmp-commands.hx | 5 ++
> tpm.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> tpm.h | 81 +++++++++++++++++++++
> vl.c | 17 +++++
> 12 files changed, 515 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 18cb415..08f6942 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -1401,6 +1401,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/hmp.c b/hmp.c
> index bb0952e..9aedaff 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -548,6 +548,34 @@ void hmp_info_block_jobs(Monitor *mon)
> }
> }
>
> +void hmp_info_tpm(Monitor *mon)
> +{
> + TPMInfoList *info_list, *info;
> + Error *err = NULL;
> + unsigned int c = 0;
> +
> + info_list = qmp_query_tpm(&err);
> + if (err) {
> + monitor_printf(mon, "TPM device not supported\n");
> + error_free(err);
> + return;
> + }
> +
> + monitor_printf(mon, "TPM device:\n");
> +
> + for (info = info_list; info; info = info->next) {
> + TPMInfo *ti = info->value;
> + monitor_printf(mon, " tpm%d: model=%s\n",
> + c, ti->model);
> + monitor_printf(mon, " \\ %s: type=%s%s%s\n",
> + ti->id, ti->type,
> + ti->has_path ? ",path=" : "",
> + ti->has_path ? ti->path : "");
> + c++;
> + }
> + qapi_free_TPMInfoList(info_list);
> +}
> +
> void hmp_quit(Monitor *mon, const QDict *qdict)
> {
> monitor_suspend(mon);
> diff --git a/hmp.h b/hmp.h
> index 443b812..8e2a858 100644
> --- a/hmp.h
> +++ b/hmp.h
> @@ -33,6 +33,7 @@ void hmp_info_spice(Monitor *mon);
> void hmp_info_balloon(Monitor *mon);
> void hmp_info_pci(Monitor *mon);
> void hmp_info_block_jobs(Monitor *mon);
> +void hmp_info_tpm(Monitor *mon);
> void hmp_quit(Monitor *mon, const QDict *qdict);
> void hmp_stop(Monitor *mon, const QDict *qdict);
> void hmp_system_reset(Monitor *mon, const QDict *qdict);
> diff --git a/hw/tpm_tis.h b/hw/tpm_tis.h
> new file mode 100644
> index 0000000..5e1f731
> --- /dev/null
> +++ b/hw/tpm_tis.h
> @@ -0,0 +1,78 @@
> +/*
> + * 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 work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + * 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-common.h"
> +
> +#define TPM_TIS_ADDR_BASE 0xFED40000
> +
> +#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */
> +#define TPM_TIS_LOCALITY_SHIFT 12
> +#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;
> +
> +typedef enum {
> + TPM_TIS_STATUS_IDLE = 0,
> + TPM_TIS_STATUS_READY,
> + TPM_TIS_STATUS_COMPLETION,
> + TPM_TIS_STATUS_EXECUTION,
> + TPM_TIS_STATUS_RECEPTION,
> +} TPMTISStatus;
> +
> +/* locality data -- all fields are persisted */
> +typedef struct TPMLocality {
> + TPMTISStatus status;
> + 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 {
> + QEMUBH *bh;
> + 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;
> +
> +#endif /* HW_TPM_TIS_H */
> diff --git a/monitor.c b/monitor.c
> index 12a6fe2..aec06ce 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"
> @@ -2602,6 +2603,13 @@ static mon_cmd_t info_cmds[] = {
> .mhandler.info = do_trace_print_events,
> },
> {
> + .name = "tpm",
> + .args_type = "",
> + .params = "",
> + .help = "show the TPM device",
> + .mhandler.info = hmp_info_tpm,
> + },
> + {
> .name = NULL,
> },
> };
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 2ca7195..8799322 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1755,3 +1755,32 @@
> # Since: 0.14.0
> ##
> { 'command': 'device_del', 'data': {'id': 'str'} }
> +
> +##
> +# @TPMInfo:
> +#
> +# Information about the TPM
> +#
> +# @model: The TPM frontend model, i.e., tpm-tis
> +#
> +# @id: The ID of the TPM
> +#
> +# @type: The type of TPM backend, i.e., passthrough
> +#
> +# @path: #optional Path to the TPM backend device
> +#
> +# Since: 1.1
> +##
> +{ 'type': 'TPMInfo',
> + 'data': {'model': 'str', 'id': 'str', 'type': 'str', '*path': 'str' } }
> +
> +##
> +# @query-tpm
> +#
> +# Return information about the TPM device.
> +#
> +# Returns: @TPMInfo on success
> +#
> +# Since: 1.1
> +##
> +{ 'command': 'query-tpm', 'returns': ['TPMInfo'] }
> diff --git a/qemu-config.c b/qemu-config.c
> index be84a03..edc8d5d 100644
> --- a/qemu-config.c
> +++ b/qemu-config.c
> @@ -613,6 +613,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,
> @@ -628,6 +647,7 @@ static QemuOptsList *vm_config_groups[32] = {
> &qemu_machine_opts,
> &qemu_boot_opts,
> &qemu_iscsi_opts,
> + &qemu_tpmdev_opts,
> NULL,
> };
>
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 8b66264..143d92d 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -2012,6 +2012,39 @@ ETEXI
>
> DEFHEADING()
>
> +#ifdef CONFIG_TPM
> +DEFHEADING(TPM device options:)
> +
> +DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
> + "-tpmdev [<type>],id=str[,option][,option][,...]\n",
> + QEMU_ARCH_ALL)
> +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()
> +
> +#endif
> +
> DEFHEADING(Linux/Multiboot boot specific:)
> STEXI
>
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index db980fa..82f1a6d 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -2142,3 +2142,8 @@ EQMP
> .args_type = "implements:s?,abstract:b?",
> .mhandler.cmd_new = qmp_marshal_input_qom_list_types,
> },
> + {
> + .name = "query-tpm",
> + .args_type = "",
> + .mhandler.cmd_new = qmp_marshal_input_query_tpm,
> + },
> diff --git a/tpm.c b/tpm.c
> new file mode 100644
> index 0000000..778de42
> --- /dev/null
> +++ b/tpm.c
> @@ -0,0 +1,213 @@
> +/*
> + * 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 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + * Based on net.c
> + */
> +#include "config.h"
> +
> +#include "monitor.h"
> +#include "qerror.h"
> +#include "tpm.h"
> +#include "qmp-commands.h"
> +
> +static QLIST_HEAD(, TPMBackend) tpm_backends =
> + QLIST_HEAD_INITIALIZER(tpm_backends);
> +
> +#ifdef CONFIG_TPM
> +
> +static const TPMDriverOps *bes[] = {
I think bes[] would be more descriptive if it were named be_drivers[] or
be_ops[]?
> + NULL,
> +};
> +
> +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];
> +}
> +
> +/*
> + * Walk the list of available TPM backend drivers and display them on the
> + * screen.
> + */
> +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");
> +}
> +
> +/*
> + * Find the TPM with the given Id
> + */
> +TPMBackend *qemu_find_tpm(const char *id)
> +{
> + TPMBackend *drv;
> +
> + QLIST_FOREACH(drv, &tpm_backends, list) {
> + if (!strcmp(drv->id, id)) {
> + return drv;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +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;
> + }
A list of tpm_backends is maintained and walked in a few places, but
only one is allowed to be added to the list. Will it ever make sense to
enable multiple backends at one time?
> +
> + 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);
The "type" value is being passed but the tpm_get_backend_driver()
defines the parameter as "id". Maybe "id" could be renamed to "type"
for consistency. See similar comment further down in this email.
> + 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);
> +}
> +
> +/*
> + * Walk the list of TPM backend drivers that are in use and call their
> + * destroy function to have them cleaned up.
> + */
> +void tpm_cleanup(void)
> +{
> + TPMBackend *drv, *next;
> +
> + QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
> + QLIST_REMOVE(drv, list);
> + drv->ops->destroy(drv);
> + }
> +}
> +
> +/*
> + * Initialize the TPM. Process the tpmdev command line options describing the
> + * TPM backend.
> + */
> +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;
> +}
> +
> +/*
> + * Parse the TPM configuration options.
> + * It is possible to pass an option '-tpmdev none' to not activate any TPM.
> + * To display all available TPM backends the user may use '-tpmdev ?'
> + */
> +int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
> +{
> + QemuOpts *opts;
> +
> + if (strcmp("none", optarg) != 0) {
What's the point of supporting "-tpmdev none"?
> + if (*optarg == '?') {
> + tpm_display_backend_drivers();
> + return -1;
> + }
> + opts = qemu_opts_parse(opts_list, optarg, 1);
> + if (!opts) {
> + return -1;
> + }
> + }
> + return 0;
> +}
> +
> +#endif /* CONFIG_TPM */
> +
> +static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
> +{
> + TPMInfo *res = g_new0(TPMInfo, 1);
> +
> + res->model = g_strdup(drv->fe_model);
> + res->id = g_strdup(drv->id);
> + if (drv->path) {
> + res->path = g_strdup(drv->path);
> + }
> + res->type = g_strdup(drv->ops->id);
> +
> + return res;
> +}
> +
> +/*
> + * Walk the list of active TPM backends and collect information about them
> + * following the schema description in qapi-schema.json.
> + */
> +TPMInfoList *qmp_query_tpm(Error **errp)
> +{
> + TPMBackend *drv;
> + TPMInfoList *info, *head = NULL, *cur_item = NULL;
> +
> + QLIST_FOREACH(drv, &tpm_backends, list) {
> + info = g_new0(TPMInfoList, 1);
> + info->value = qmp_query_tpm_inst(drv);
> +
> + if (!cur_item) {
> + head = cur_item = info;
> + } else {
> + cur_item->next = info;
> + cur_item = info;
> + }
> + }
> +
> + return head;
> +}
> diff --git a/tpm.h b/tpm.h
> new file mode 100644
> index 0000000..7fdd1db
> --- /dev/null
> +++ b/tpm.h
> @@ -0,0 +1,81 @@
> +/*
> + * 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 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +#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;
For consistency, this could be named "type" instead of "id" since it
corresponds to -tpmdev's type.
> + const char *fe_model;
> + char *path;
> + 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;
It would be easier to read if locality was spelled out fully, instead of
locty. But if that causes lines to be too long then maybe it's not
worth it.
> + TPMLocality *cmd_locty;
There's a cmd_locty and a command_locty. command_locty is the locality
number and cmd_locty is the locality data. Could these variable names
be updated to be more unique and descriptive?
> +
> + 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 (*deliver_request)(TPMBackend *t);
> +
> + void (*reset)(TPMBackend *t);
> +
> + bool (*get_tpm_established_flag)(TPMBackend *t);
> +};
> +
> +#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
> +
> +int 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 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 23ab3a3..3e784f5 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"
> @@ -2674,6 +2675,13 @@ int main(int argc, char **argv, char **envp)
> ram_size = value;
> break;
> }
> +#ifdef CONFIG_TPM
> + case QEMU_OPTION_tpmdev:
> + if (tpm_config_parse(qemu_find_opts("tpmdev"), optarg) < 0) {
> + exit(1);
> + }
> + break;
> +#endif
> case QEMU_OPTION_mempath:
> mem_path = optarg;
> break;
> @@ -3405,6 +3413,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);
> @@ -3657,6 +3671,9 @@ int main(int argc, char **argv, char **envp)
> pause_all_vcpus();
> net_cleanup();
> res_free();
> +#ifdef CONFIG_TPM
> + tpm_cleanup();
> +#endif
>
> return 0;
> }
>
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
@ 2012-09-27 14:22 ` Corey Bryant
2012-10-24 18:46 ` Stefan Berger
2012-10-03 18:35 ` Corey Bryant
1 sibling, 1 reply; 30+ messages in thread
From: Corey Bryant @ 2012-09-27 14:22 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 06/04/2012 03:37 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>
> ---
> hw/tpm_tis.c | 822 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 822 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..02b9c2e
> --- /dev/null
> +++ b/hw/tpm_tis.c
> @@ -0,0 +1,822 @@
> +/*
> + * 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 work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + * 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/pci_ids.h"
> +#include "hw/tpm_tis.h"
> +#include "qemu-error.h"
> +#include "qemu-common.h"
> +
> +/*#define DEBUG_TIS */
> +
> +#ifdef DEBUG_TIS
> +#define dprintf(fmt, ...) \
> + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define dprintf(fmt, ...) \
> + do { } while (0)
> +#endif
> +
> +/* whether the STS interrupt is supported */
> +/*#define RAISE_STS_IRQ */
Why is this commented out?
> +
> +/* 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 PCI_VENDOR_ID_IBM
> +#define TPM_TIS_TPM_RID 0x0001
> +
> +#define TPM_TIS_NO_DATA_BYTE 0xff
> +
> +/* utility functions */
> +
> +static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
> +{
> + return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
> +}
> +
> +static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
> +{
> + return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
> +}
> +
> +static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
> +{
> +#ifdef DEBUG_TIS
> + uint32_t len, i;
> +
> + len = tpm_tis_get_size_from_buffer(sb);
> + dprintf("tpm_tis: %s length = %d\n", string, len);
> + for (i = 0; i < len; i++) {
> + if (i && !(i % 16)) {
> + dprintf("\n");
> + }
> + dprintf("%.2X ", sb->buffer[i]);
> + }
> + dprintf("\n");
> +#endif
> +}
> +
> +/*
> + * Send a request to the TPM.
> + */
> +static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
> +{
> + TPMTISState *tis = &s->s.tis;
> +
> + tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
> +
> + 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].status = TPM_TIS_STATUS_EXECUTION;
> + tis->loc[locty].sts &= ~TPM_TIS_STS_EXPECT;
> +
> + s->be_driver->ops->deliver_request(s->be_driver);
> +}
> +
> +/* 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)) {
> + dprintf("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
> + qemu_irq_raise(s->s.tis.irq);
> + tis->loc[locty].ints |= irqmask;
Should the ints irqmask be set before the interrupt is raised?
> + }
> +}
> +
> +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);
Should TPM_TIS_ACCESS_REQUEST_USE be modified for the old locality when
we are in here for TPM_TIS_ACCESS_SEIZE?
The TPM TIS document says the following (in the TPM_ACCESS_x table,
under the Seize bit description):
"2. Setting this bit does not affect the state of the
TPM_ACCESS_x.requestUse bit for any locality except the one issuing the
Seize bit."
> + 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;
> +
> + dprintf("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty);
> +
> + 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;
> +
> + dprintf("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty);
> +
> + /*
> + * 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].status = TPM_TIS_STATUS_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].status == TPM_TIS_STATUS_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;
> + */
> + dprintf("tpm_tis: Locality %d is busy - deferring abort\n",
> + busy_locty);
> + return;
> + }
> + }
> +
> + tpm_tis_abort(s, locty);
> +}
> +
> +static void tpm_tis_receive_bh(void *opaque)
> +{
> + TPMState *s = opaque;
> + TPMTISState *tis = &s->s.tis;
> + uint8_t locty = s->command_locty;
> +
> + tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE;
> + tis->loc[locty].status = TPM_TIS_STATUS_COMPLETION;
Can tis->loc[locty].status be changed to tis->loc[locty].state? This is
very confusing when named "status" because it is easy to confuse with
the TPM_INT_STATUS register, which in actuality it is unrelated.
> + 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);
> + }
> +
> +#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
> +}
> +
> +/*
> + * 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;
> +
> + assert(s->command_locty == locty);
> +
> + qemu_bh_schedule(tis->bh);
> +}
> +
> +/*
> + * Read a byte of response data
> + */
> +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;
Should dataAvail be turned off here?
> +#ifdef RAISE_STS_IRQ
> + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
> +#endif
> + }
> + dprintf("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n",
> + ret, tis->loc[locty].r_offset-1);
> + }
> +
> + 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);
> +
> + if (s->be_driver->ops->had_startup_error(s->be_driver)) {
> + 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 */
s/alawys/always
> + 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;
Can you create/use a #define for burstCount instead of using a hard-coded 8?
> + } 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].status) {
> + case TPM_TIS_STATUS_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;
> + }
> +
> + if (shift) {
> + val >>= shift;
> + }
> +
> + dprintf("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
> +
> + 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;
> +
> + dprintf("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
> +
> + if (locty == 4 && !hw_access) {
> + dprintf("tpm_tis: Access to locality 4 only allowed from hardware\n");
> + return;
> + }
> +
> + if (s->be_driver->ops->had_startup_error(s->be_driver)) {
> + 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);
> + }
Is there a reason why this code can't be merged with the "(val &
TPM_TIS_ACCESS_SEIZE)" check that is down below?
> +
> + active_locty = tis->active_locty;
> +
> + if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
> + /* give up locality if currently owned */
> + if (tis->active_locty == locty) {
> + dprintf("tpm_tis: Releasing locality %d\n", locty);
> +
> + 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)) {
> + dprintf("tpm_tis: Locality %d requests use.\n", c);
> + newlocty = c;
> + break;
> + }
> + }
> + dprintf("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
> + "Next active locality: %d\n",
> + newlocty);
> +
> + 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;
Were you intending to break from the for loop or the while?
> + }
> + }
> +
> + /* 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;
> + dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
> + "Locality %d seized from locality %d\n",
> + locty, tis->active_locty);
> + dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
> + 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));
Is 0x3 << 3 == typePolarity? Could a #define be introduced for this
instead of hard coding it?
> + 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);
> + dprintf("tpm_tis: Lowering IRQ\n");
> + }
> + }
> + 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].status) {
> +
> + case TPM_TIS_STATUS_READY:
> + tis->loc[locty].w_offset = 0;
> + tis->loc[locty].r_offset = 0;
> + break;
> +
> + case TPM_TIS_STATUS_IDLE:
> + tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
> + tis->loc[locty].status = TPM_TIS_STATUS_READY;
> + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
> + break;
> +
> + case TPM_TIS_STATUS_EXECUTION:
> + case TPM_TIS_STATUS_RECEPTION:
> + /* abort currently running command */
> + dprintf("tpm_tis: %s: Initiating abort.\n",
> + __func__);
> + tpm_tis_prep_abort(s, locty, locty);
> + break;
> +
> + case TPM_TIS_STATUS_COMPLETION:
Does this path need to abort if TPM_STS_x.dataAvail is on? This comment
is based on "Table 19: State Transition Table." from the TPM TIS document.
> + tis->loc[locty].w_offset = 0;
> + tis->loc[locty].r_offset = 0;
> + /* shortcut to ready state with C/R set */
> + tis->loc[locty].status = TPM_TIS_STATUS_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].status) {
> + case TPM_TIS_STATUS_RECEPTION:
> + tpm_tis_tpm_send(s, locty);
> + break;
> + default:
> + /* ignore */
> + break;
> + }
> + } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
> + switch (tis->loc[locty].status) {
> + case TPM_TIS_STATUS_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].status == TPM_TIS_STATUS_IDLE ||
> + tis->loc[locty].status == TPM_TIS_STATUS_EXECUTION ||
> + tis->loc[locty].status == TPM_TIS_STATUS_COMPLETION) {
> + /* drop the byte */
> + } else {
> + dprintf("tpm_tis: Byte to send to TPM: %02x\n", (uint8_t)val);
> + if (tis->loc[locty].status == TPM_TIS_STATUS_READY) {
> + tis->loc[locty].status = TPM_TIS_STATUS_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;
Should EXPECT be turned off here instead of where it is currently turned
off in tpm_tis_tpm_send?
> + }
> +#ifdef RAISE_STS_IRQ
> + if (needIrq) {
> + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
> + }
> +#endif
> + }
> + }
> + break;
> + }
> +}
> +
> +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_LITTLE_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 = DO_UPCAST(TPMState, busdev.qdev, d);
> + TPMTISState *tis = &s->s.tis;
> + int c;
> +
> + 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].status = TPM_TIS_STATUS_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;
> +
> + 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);
> + goto err_exit;
> + }
> +
> + s->be_driver->fe_model = "tpm-tis";
> +
> + if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) {
> + goto err_exit;
> + }
> +
> + tis->bh = qemu_bh_new(tpm_tis_receive_bh, s);
> +
> + if (tis->irq_num > 15) {
> + error_report("IRQ %d for TPM TIS is outside valid range of 0 to 15.\n",
> + tis->irq_num);
> + goto err_exit;
> + }
> +
> + isa_init_irq(dev, &tis->irq, tis->irq_num);
> +
> + memory_region_init_io(&s->mmio, &tpm_tis_memory_ops, s, "tpm-tis-mmio",
> + TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT);
> + 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_del_subregion(get_system_memory(), &s->mmio);
> + memory_region_destroy(&s->mmio);
> +
> + err_exit:
> + return -1;
> +}
> +
> +static const VMStateDescription vmstate_tpm_tis = {
> + .name = "tpm",
> + .unmigratable = 1,
> +};
> +
> +static Property tpm_tis_properties[] = {
> + 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_class_initfn(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
> +
> + ic->init = tpm_tis_init;
> +
> + dc->props = tpm_tis_properties;
> + dc->reset = tpm_tis_reset;
> + dc->vmsd = &vmstate_tpm_tis;
> +}
> +
> +static TypeInfo tpm_tis_info = {
> + .name = "tpm-tis",
> + .parent = TYPE_ISA_DEVICE,
> + .class_init = tpm_tis_class_initfn,
> + .instance_size = sizeof(TPMState),
> +};
> +
> +static void tpm_tis_register(void)
> +{
> + type_register_static(&tpm_tis_info);
> +}
> +
> +type_init(tpm_tis_register)
>
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 3/7] Add a debug register
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 3/7] Add a debug register Stefan Berger
@ 2012-09-27 14:23 ` Corey Bryant
0 siblings, 0 replies; 30+ messages in thread
From: Corey Bryant @ 2012-09-27 14:23 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 06/04/2012 03:37 PM, Stefan Berger wrote:
> 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>
> ---
> hw/tpm_tis.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 70 insertions(+), 0 deletions(-)
>
> diff --git a/hw/tpm_tis.c b/hw/tpm_tis.c
> index 02b9c2e..5f8899d 100644
> --- a/hw/tpm_tis.c
> +++ b/hw/tpm_tis.c
> @@ -52,6 +52,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)
> @@ -97,6 +100,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);
> +
> /* utility functions */
>
> static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
> @@ -331,6 +339,63 @@ 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;
> +
> + dprintf("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].status);
> +
> + for (idx = 0; regs[idx] != 0xfff; idx++) {
> + dprintf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
> + (uint32_t)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
> + }
> +
> + dprintf("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++) {
> + dprintf("%c%02x%s",
> + tis->loc[locty].r_offset == idx ? '>' : ' ',
> + tis->loc[locty].r_buffer.buffer[idx],
> + ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
> + }
> + dprintf("\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++) {
> + dprintf("%c%02x%s",
> + tis->loc[locty].w_offset == idx ? '>' : ' ',
> + tis->loc[locty].w_buffer.buffer[idx],
> + ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
> + }
> + dprintf("\n");
> +}
> +#endif
> +
> /*
> * Read a register of the TIS interface
> * See specs pages 33-63 for description of the registers
> @@ -400,6 +465,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
> }
>
> if (shift) {
>
This patch looks okay to me.
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 4/7] Build the TPM frontend code
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 4/7] Build the TPM frontend code Stefan Berger
@ 2012-09-27 14:24 ` Corey Bryant
0 siblings, 0 replies; 30+ messages in thread
From: Corey Bryant @ 2012-09-27 14:24 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 06/04/2012 03:37 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 | 2 ++
> configure | 11 +++++++++++
> 2 files changed, 13 insertions(+), 0 deletions(-)
>
> diff --git a/Makefile.target b/Makefile.target
> index 1582904..cdf108a 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -192,6 +192,8 @@ obj-$(CONFIG_KVM) += kvm.o kvm-all.o
> obj-$(CONFIG_NO_KVM) += kvm-stub.o
> obj-$(CONFIG_VGA) += vga.o
> obj-y += memory.o savevm.o cputlb.o
> +obj-y += tpm.o
> +obj-$(CONFIG_TPM) += tpm_tis.o
> LIBS+=-lz
>
> obj-i386-$(CONFIG_KVM) += hyperv.o
> diff --git a/configure b/configure
> index 1f338f8..d805c20 100755
> --- a/configure
> +++ b/configure
> @@ -194,6 +194,7 @@ zlib="yes"
> guest_agent="yes"
> libiscsi=""
> coroutine=""
> +tpm="no"
>
> # parse CC options first
> for opt do
> @@ -824,6 +825,8 @@ for opt do
> ;;
> --disable-guest-agent) guest_agent="no"
> ;;
> + --enable-tpm) tpm="yes"
> + ;;
> *) echo "ERROR: unknown option $opt"; show_help="yes"
> ;;
> esac
> @@ -1110,6 +1113,7 @@ echo " --disable-guest-agent disable building of the QEMU Guest Agent"
> echo " --enable-guest-agent enable building of the QEMU Guest Agent"
> echo " --with-coroutine=BACKEND coroutine backend. Supported options:"
> echo " gthread, ucontext, sigaltstack, windows"
> +echo " --enable-tpm enable TPM support"
> echo ""
> echo "NOTE: The object files are built at the place where configure is launched"
> exit 1
> @@ -3029,6 +3033,7 @@ echo "OpenGL support $opengl"
> echo "libiscsi support $libiscsi"
> echo "build guest agent $guest_agent"
> echo "coroutine backend $coroutine_backend"
> +echo "TPM support $tpm"
>
> if test "$sdl_too_old" = "yes"; then
> echo "-> Your SDL version is too old - please upgrade to have SDL support"
> @@ -3911,6 +3916,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
>
This patch looks okay to me.
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 5/7] Add a TPM Passthrough backend driver implementation
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
@ 2012-09-27 14:28 ` Corey Bryant
2012-10-24 19:07 ` Stefan Berger
0 siblings, 1 reply; 30+ messages in thread
From: Corey Bryant @ 2012-09-27 14:28 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 06/04/2012 03:37 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>
> ---
> Makefile.target | 3 +-
> configure | 3 +
> hw/tpm_backend.c | 58 ++++++++
> hw/tpm_backend.h | 43 ++++++
> hw/tpm_passthrough.c | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++
> qemu-options.hx | 38 +++++-
> tpm.c | 18 +++
> tpm.h | 33 ++++
> vl.c | 2 +
> 9 files changed, 594 insertions(+), 2 deletions(-)
> create mode 100644 hw/tpm_backend.c
> create mode 100644 hw/tpm_backend.h
> create mode 100644 hw/tpm_passthrough.c
>
> diff --git a/Makefile.target b/Makefile.target
> index cdf108a..ef063c8 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -193,7 +193,8 @@ obj-$(CONFIG_NO_KVM) += kvm-stub.o
> obj-$(CONFIG_VGA) += vga.o
> obj-y += memory.o savevm.o cputlb.o
> obj-y += tpm.o
> -obj-$(CONFIG_TPM) += tpm_tis.o
> +obj-$(CONFIG_TPM) += tpm_tis.o tpm_backend.o
> +obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
> LIBS+=-lz
>
> obj-i386-$(CONFIG_KVM) += hyperv.o
> diff --git a/configure b/configure
> index d805c20..804d8c8 100755
> --- a/configure
> +++ b/configure
> @@ -3918,6 +3918,9 @@ fi
>
> if test "$tpm" = "yes"; then
> if test "$target_softmmu" = "yes" ; then
> + if test "$linux" = "yes" ; then
> + echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
> + fi
> echo "CONFIG_TPM=y" >> $config_host_mak
> fi
> fi
> diff --git a/hw/tpm_backend.c b/hw/tpm_backend.c
> new file mode 100644
> index 0000000..4cf0809
> --- /dev/null
> +++ b/hw/tpm_backend.c
> @@ -0,0 +1,58 @@
> +/*
> + * common TPM backend driver functions
> + *
> + * Copyright (c) 2012 IBM Corporation
> + * Authors:
> + * Stefan Berger <stefanb@us.ibm.com>
> + *
> + * 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 "tpm.h"
> +#include "qemu-thread.h"
> +#include "hw/tpm_backend.h"
> +
> +void tpm_backend_thread_deliver_request(TPMBackendThread *tbt)
> +{
> + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL);
> +}
> +
> +void tpm_backend_thread_create(TPMBackendThread *tbt,
> + GFunc func, gpointer user_data)
> +{
> + if (!tbt->pool) {
> + tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL);
> + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
> + }
> +}
> +
> +void tpm_backend_thread_end(TPMBackendThread *tbt)
> +{
> + if (tbt->pool) {
> + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
> + g_thread_pool_free(tbt->pool, FALSE, TRUE);
> + tbt->pool = NULL;
> + }
> +}
> +
> +void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt,
> + GFunc func, gpointer user_data)
> +{
> + if (!tbt->pool) {
> + tpm_backend_thread_create(tbt, func, user_data);
> + } else {
> + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_TPM_RESET,
> + NULL);
> + }
> +}
> diff --git a/hw/tpm_backend.h b/hw/tpm_backend.h
> new file mode 100644
> index 0000000..f5fe198
> --- /dev/null
> +++ b/hw/tpm_backend.h
> @@ -0,0 +1,43 @@
> +/*
> + * common TPM backend driver functions
> + *
> + * Copyright (c) 2012 IBM Corporation
> + * Authors:
> + * Stefan Berger <stefanb@us.ibm.com>
> + *
> + * 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/>
> + */
> +
> +#ifndef HW_TPM_BACKEND_H
> +#define HW_TPM_BACKEND_H
> +
> +typedef struct TPMBackendThread {
> + GThreadPool *pool;
> +} TPMBackendThread;
> +
> +void tpm_backend_thread_deliver_request(TPMBackendThread *tbt);
> +void tpm_backend_thread_create(TPMBackendThread *tbt,
> + GFunc func, gpointer user_data);
> +void tpm_backend_thread_end(TPMBackendThread *tbt);
> +void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt,
> + GFunc func, gpointer user_data);
> +
> +typedef enum TPMBackendCmd {
> + TPM_BACKEND_CMD_INIT = 1,
> + TPM_BACKEND_CMD_PROCESS_CMD,
> + TPM_BACKEND_CMD_END,
> + TPM_BACKEND_CMD_TPM_RESET,
> +} TPMBackendCmd;
> +
> +#endif /* HW_TPM_BACKEND_H */
> diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
> new file mode 100644
> index 0000000..8914705
> --- /dev/null
> +++ b/hw/tpm_passthrough.c
> @@ -0,0 +1,398 @@
> +/*
> + * 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 "qemu_socket.h"
> +#include "tpm.h"
> +#include "hw/hw.h"
> +#include "hw/tpm_tis.h"
> +#include "hw/tpm_backend.h"
> +#include "hw/pc.h"
> +
> +/* #define DEBUG_TPM */
> +
> +#ifdef DEBUG_TPM
> +#define dprintf(fmt, ...) \
> + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define dprintf(fmt, ...) \
> + do { } while (0)
> +#endif
> +
> +/* data structures */
> +
> +typedef struct TPMPassthruThreadParams {
> + TPMState *tpm_state;
> +
> + TPMRecvDataCB *recv_data_callback;
> + TPMBackend *tb;
> +} TPMPassthruThreadParams;
> +
> +struct TPMPassthruState {
> + TPMBackendThread tbt;
> +
> + TPMPassthruThreadParams tpm_thread_params;
> +
> + char *tpm_dev;
> + 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)
> +{
> + return send_all(fd, buf, len);
> +}
> +
> +static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
It would probably be useful to other parts of QEMU if you defined this
function as recv_all() and put it in qemu-char.c (to correspond with
send_all()).
> +{
> + 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 uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
> +{
> + return be32_to_cpu(*(uint32_t *)&buf[2]);
> +}
> +
> +static int tpm_passthrough_unix_tx_bufs(int tpm_fd,
> + const uint8_t *in, uint32_t in_len,
> + uint8_t *out, uint32_t out_len)
> +{
> + int ret;
> +
> + ret = tpm_passthrough_unix_write(tpm_fd, in, in_len);
> + if (ret != in_len) {
> + error_report("tpm_passthrough: error while transmitting data "
> + "to TPM: %s (%i)\n",
> + strerror(errno), errno);
> + goto err_exit;
> + }
> +
> + ret = tpm_passthrough_unix_read(tpm_fd, out, out_len);
> + if (ret < 0) {
> + error_report("tpm_passthrough: error while reading data from "
> + "TPM: %s (%i)\n",
> + strerror(errno), errno);
> + } else if (ret < sizeof(struct tpm_resp_hdr) ||
> + tpm_passthrough_get_size_from_buffer(out) != ret) {
> + ret = -1;
> + error_report("tpm_passthrough: received invalid response "
> + "packet from TPM\n");
> + }
> +
> +err_exit:
> + if (ret < 0) {
> + tpm_write_fatal_error_response(out, out_len);
> + }
> +
> + return ret;
> +}
> +
> +static int tpm_passthrough_unix_transfer(int tpm_fd,
> + const TPMLocality *cmd_locty)
> +{
> + return tpm_passthrough_unix_tx_bufs(tpm_fd,
> + cmd_locty->w_buffer.buffer,
> + cmd_locty->w_offset,
> + cmd_locty->r_buffer.buffer,
> + cmd_locty->r_buffer.size);
> +}
> +
> +static void tpm_passthrough_worker_thread(gpointer data,
> + gpointer user_data)
> +{
> + TPMPassthruThreadParams *thr_parms = user_data;
> + TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
> + TPMBackendCmd cmd = (TPMBackendCmd)data;
> +
> + dprintf("tpm_passthrough: processing command type %ld\n", cmd);
> +
> + switch (cmd) {
> + case TPM_BACKEND_CMD_PROCESS_CMD:
> + tpm_passthrough_unix_transfer(tpm_pt->tpm_fd,
> + thr_parms->tpm_state->cmd_locty);
> +
> + thr_parms->recv_data_callback(thr_parms->tpm_state,
> + thr_parms->tpm_state->command_locty);
> + break;
> + case TPM_BACKEND_CMD_INIT:
> + case TPM_BACKEND_CMD_END:
> + case TPM_BACKEND_CMD_TPM_RESET:
> + /* nothing to do */
> + break;
> + }
> +}
> +
> +/*
> + * 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_backend_thread_end(&tpm_pt->tbt);
> +
> + tpm_backend_thread_create(&tpm_pt->tbt,
> + tpm_passthrough_worker_thread,
> + &tb->s.tpm_pt->tpm_thread_params);
> +
> + 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;
tpm_passthrough_do_startup_tpm() always returns zero, so
had_startup_error will never be set to true.
> + }
> + return rc;
> +}
> +
> +static void tpm_passthrough_reset(TPMBackend *tb)
> +{
> + TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> + dprintf("tpm_passthrough: CALL TO TPM_RESET!\n");
> +
> + tpm_backend_thread_end(&tpm_pt->tbt);
Should the thread be restarted here?
> +
> + 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;
> +
> + return 0;
> +}
> +
> +static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
> +{
> + return false;
Can a T/OS never be established with the passthrough backend?
> +}
> +
> +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);
> + sb->size = wanted_size;
> + }
> + return sb->size;
> +}
> +
> +static void tpm_passthrough_deliver_request(TPMBackend *tb)
> +{
> + TPMPassthruState *tpm_pt = tb->s.tpm_pt;
> +
> + tpm_backend_thread_deliver_request(&tpm_pt->tbt);
> +}
> +
> +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;
> +
> + value = qemu_opt_get(opts, "path");
> + if (!value) {
> + value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
> + }
> +
> + tb->s.tpm_pt->tpm_dev = g_strdup(value);
> +
> + tb->path = g_strdup(tb->s.tpm_pt->tpm_dev);
> +
> + 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_free_parameters;
> + }
> +
> + 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_free_parameters:
> + g_free(tb->path);
> + tb->path = NULL;
> +
> + g_free(tb->s.tpm_pt->tpm_dev);
> + tb->s.tpm_pt->tpm_dev = NULL;
> +
> + return 1;
> +}
> +
> +static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
> +{
> + TPMBackend *tb;
> +
> + tb = g_new0(TPMBackend, 1);
> + tb->s.tpm_pt = g_new0(TPMPassthruState, 1);
> + tb->id = g_strdup(id);
> +
> + 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_backend_thread_end(&tpm_pt->tbt);
> +
> + close(tpm_pt->tpm_fd);
> +
> + g_free(tb->id);
> + g_free(tb->path);
> + g_free(tb->s.tpm_pt->tpm_dev);
> + 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,
> + .deliver_request = tpm_passthrough_deliver_request,
> + .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
> +};
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 143d92d..0b9fa0b 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -2013,10 +2013,12 @@ ETEXI
> DEFHEADING()
>
> #ifdef CONFIG_TPM
> +# ifdef CONFIG_TPM_PASSTHROUGH
> DEFHEADING(TPM device options:)
>
> DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
> - "-tpmdev [<type>],id=str[,option][,option][,...]\n",
> + "-tpmdev passthrough,id=id[,path=path]\n"
> + " use path to provide path to a character device; default is /dev/tpm0\n",
> QEMU_ARCH_ALL)
> STEXI
>
> @@ -2026,6 +2028,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.
> @@ -2037,12 +2040,45 @@ 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
>
> DEFHEADING()
>
> +# endif
> #endif
>
> DEFHEADING(Linux/Multiboot boot specific:)
> diff --git a/tpm.c b/tpm.c
> index 778de42..79530c9 100644
> --- a/tpm.c
> +++ b/tpm.c
> @@ -24,9 +24,26 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =
> #ifdef CONFIG_TPM
>
> static const TPMDriverOps *bes[] = {
> +#ifdef CONFIG_TPM_PASSTHROUGH
> + &tpm_passthrough_driver,
> +#endif
> NULL,
> };
>
> +/*
> + * Write an error message in the given output buffer.
> + */
> +void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
> +{
> + if (out_len >= sizeof(struct tpm_resp_hdr)) {
> + struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
> +
> + resp->tag = cpu_to_be16(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;
> @@ -182,6 +199,7 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
> res->id = g_strdup(drv->id);
> if (drv->path) {
> res->path = g_strdup(drv->path);
> + res->has_path = true;
> }
> res->type = g_strdup(drv->ops->id);
>
> diff --git a/tpm.h b/tpm.h
> index 7fdd1db..bacaff3 100644
> --- a/tpm.h
> +++ b/tpm.h
> @@ -18,12 +18,18 @@
> struct TPMDriverOps;
> typedef struct TPMDriverOps TPMDriverOps;
>
> +typedef struct TPMPassthruState TPMPassthruState;
> +
> typedef struct TPMBackend {
> char *id;
> const char *fe_model;
> char *path;
> const TPMDriverOps *ops;
>
> + union {
> + TPMPassthruState *tpm_pt;
> + } s;
> +
> QLIST_ENTRY(TPMBackend) list;
> } TPMBackend;
>
> @@ -71,11 +77,38 @@ struct TPMDriverOps {
>
> #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
> +
> int 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 tpm_display_backend_drivers(void);
> const TPMDriverOps *tpm_get_backend_driver(const char *id);
> +void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len);
> +
> +extern const TPMDriverOps tpm_passthrough_driver;
>
> #endif /* QEMU_TPM_H */
> diff --git a/vl.c b/vl.c
> index 3e784f5..6928e67 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2676,11 +2676,13 @@ int main(int argc, char **argv, char **envp)
> break;
> }
> #ifdef CONFIG_TPM
> +# ifdef CONFIG_TPM_PASSTHROUGH
> case QEMU_OPTION_tpmdev:
> if (tpm_config_parse(qemu_find_opts("tpmdev"), optarg) < 0) {
> exit(1);
> }
> break;
> +# endif
> #endif
> case QEMU_OPTION_mempath:
> mem_path = optarg;
>
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 6/7] Introduce --enable-tpm-passthrough configure option
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
@ 2012-09-27 14:29 ` Corey Bryant
0 siblings, 0 replies; 30+ messages in thread
From: Corey Bryant @ 2012-09-27 14:29 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 06/04/2012 03:37 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 804d8c8..8d8bee8 100755
> --- a/configure
> +++ b/configure
> @@ -195,6 +195,7 @@ guest_agent="yes"
> libiscsi=""
> coroutine=""
> tpm="no"
> +tpm_passthrough="no"
>
> # parse CC options first
> for opt do
> @@ -827,11 +828,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)
> @@ -1114,6 +1124,7 @@ echo " --enable-guest-agent enable building of the QEMU Guest Agent"
> echo " --with-coroutine=BACKEND coroutine backend. Supported options:"
> echo " gthread, ucontext, sigaltstack, windows"
> 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
> @@ -3034,6 +3045,7 @@ echo "libiscsi support $libiscsi"
> echo "build guest agent $guest_agent"
> echo "coroutine backend $coroutine_backend"
> 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"
> @@ -3919,7 +3931,9 @@ fi
> if test "$tpm" = "yes"; then
> if test "$target_softmmu" = "yes" ; then
> if test "$linux" = "yes" ; then
> - echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
> + if test "$tpm_passthrough" = "yes" ; then
> + echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
> + fi
> fi
> echo "CONFIG_TPM=y" >> $config_host_mak
> fi
>
This patch looks okay to me.
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 7/7] Add fd parameter for TPM passthrough driver
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
@ 2012-09-27 14:35 ` Corey Bryant
2012-10-03 18:46 ` Corey Bryant
1 sibling, 0 replies; 30+ messages in thread
From: Corey Bryant @ 2012-09-27 14:35 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 06/04/2012 03:37 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>
> ---
> hmp.c | 7 +++++-
> hw/tpm_passthrough.c | 53 ++++++++++++++++++++++++++++++++++++++-----------
> qapi-schema.json | 4 ++-
> qemu-config.c | 5 ++++
> qemu-options.hx | 11 +++++++--
> tpm.c | 4 +++
> tpm.h | 1 +
> 7 files changed, 68 insertions(+), 17 deletions(-)
>
> diff --git a/hmp.c b/hmp.c
> index 9aedaff..3176c74 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -567,10 +567,15 @@ void hmp_info_tpm(Monitor *mon)
> TPMInfo *ti = info->value;
> monitor_printf(mon, " tpm%d: model=%s\n",
> c, ti->model);
> - monitor_printf(mon, " \\ %s: type=%s%s%s\n",
> + monitor_printf(mon, " \\ %s: type=%s%s%s",
> ti->id, ti->type,
> ti->has_path ? ",path=" : "",
> ti->has_path ? ti->path : "");
> + if (ti->has_fd) {
> + monitor_printf(mon, ",fd=%" PRId64,
> + ti->fd);
> + }
> + monitor_printf(mon, "\n");
> c++;
> }
> qapi_free_TPMInfoList(info_list);
> diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
> index 8914705..44cf272 100644
> --- a/hw/tpm_passthrough.c
> +++ b/hw/tpm_passthrough.c
> @@ -305,26 +305,54 @@ static int tpm_passthrough_test_tpmdev(int fd)
> static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
> {
> const char *value;
> + 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=");
> + goto err_exit;
> + }
> +
> + 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");
> + goto err_exit;
> + }
> +
> + tb->tpm_fd = &tb->s.tpm_pt->tpm_fd;
> + } else {
> + value = qemu_opt_get(opts, "path");
> + if (!value) {
> + value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
> + }
>
> - tb->s.tpm_pt->tpm_dev = g_strdup(value);
> + tb->s.tpm_pt->tpm_dev = g_strdup(value);
>
> - tb->path = g_strdup(tb->s.tpm_pt->tpm_dev);
> + tb->path = g_strdup(value);
> +
> + 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_free_parameters;
> + }
> + }
> +
> + 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;
> + }
>
> - 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);
> + /* only allow character devices for now */
> + if (!S_ISCHR(statbuf.st_mode)) {
> + error_report("TPM file descriptor is not a character device");
> goto err_free_parameters;
> }
>
> 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);
> + error_report("Device is not a TPM.\n");
> goto err_close_tpmdev;
> }
>
> @@ -341,6 +369,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
> g_free(tb->s.tpm_pt->tpm_dev);
> tb->s.tpm_pt->tpm_dev = NULL;
>
> + err_exit:
> return 1;
> }
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 8799322..9d6f664 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1769,10 +1769,12 @@
> #
> # @path: #optional Path to the TPM backend device
> #
> +# @fd: #optional File descriptor for the TPM backend device
> # Since: 1.1
> ##
> { 'type': 'TPMInfo',
> - 'data': {'model': 'str', 'id': 'str', 'type': 'str', '*path': 'str' } }
> + 'data': {'model': 'str', 'id': 'str', 'type': 'str', '*path': 'str',
> + '*fd' : 'int' } }
>
> ##
> # @query-tpm
> diff --git a/qemu-config.c b/qemu-config.c
> index edc8d5d..a5e2677 100644
> --- a/qemu-config.c
> +++ b/qemu-config.c
> @@ -628,6 +628,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 0b9fa0b..cb9892b 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -2017,8 +2017,9 @@ DEFHEADING()
> DEFHEADING(TPM device options:)
>
> DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
> - "-tpmdev passthrough,id=id[,path=path]\n"
> - " use path to provide path to a character device; default is /dev/tpm0\n",
> + "-tpmdev passthrough,id=id[,path=path][,fd=h]\n"
> + " use path to provide path to a character device; default is /dev/tpm0\n"
> + " use fd to provide a file descriptor to a character device\n",
> QEMU_ARCH_ALL)
> STEXI
>
> @@ -2040,7 +2041,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.
> @@ -2049,6 +2050,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
> diff --git a/tpm.c b/tpm.c
> index 79530c9..64d3140 100644
> --- a/tpm.c
> +++ b/tpm.c
> @@ -201,6 +201,10 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
> res->path = g_strdup(drv->path);
> res->has_path = true;
> }
> + if (drv->tpm_fd != NULL && *drv->tpm_fd >= 0) {
> + res->fd = *drv->tpm_fd;
> + res->has_fd = true;
> + }
> res->type = g_strdup(drv->ops->id);
>
> return res;
> diff --git a/tpm.h b/tpm.h
> index bacaff3..00f7600 100644
> --- a/tpm.h
> +++ b/tpm.h
> @@ -24,6 +24,7 @@ typedef struct TPMBackend {
> char *id;
> const char *fe_model;
> char *path;
> + int *tpm_fd;
> const TPMDriverOps *ops;
>
> union {
>
This patch looks okay to me.
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration
2012-06-04 19:37 [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
` (7 preceding siblings ...)
2012-06-04 19:56 ` [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
@ 2012-09-27 14:59 ` Corey Bryant
2012-09-28 22:43 ` Stefan Berger
8 siblings, 1 reply; 30+ messages in thread
From: Corey Bryant @ 2012-09-27 14:59 UTC (permalink / raw)
To: Stefan Berger; +Cc: root, mst, qemu-devel, anthony, andreas.niederl
On 06/04/2012 03:37 PM, Stefan Berger wrote:
> From: root <root@d941f-5.watson.ibm.com>
>
> 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.
>
> v19:
> - applies to checkout of 8cc9b43 (Jun 4)
>
> v18:
> - applies to checkout of 563987d (May 1)
> - removed some dead variable in 7/7
>
> v17:
> - applies to checkout of 6507470 (Apr 30)
> - split up path and fd into two optional parameters
>
> v16:
> - applied to checkout of 42fe1c2 (Apr 27)
> - followed Anthony's suggestions for v15
> - changed qemu-options.hx and vl.c to not show anything TPM-related if
> --enable-tpm-passthrough was not used on configure line
>
> v15:
> - applies to checkout of 8a22565 (Mar 27)
> - replacing g_malloc's with g_new; no more checks for NULL after allocs
> - introducing usage of bottom half in TIS frontend to deliver responses
> - get rid of locks since global lock is held by all threads entering TIS
> code
> - cleanups
>
> v14:
> - applies to checkout of da5361c (Dec 12)
> - implemented Anthony Liguori's suggestions
> - dropping the version log on individual patches
>
> v13:
> - applies to checkout of 61a5872 (Dec 12)
> - only allowing character devices as fd parameter
> - fixing error path in tpm_tis_init
>
> v12:
> - applies to checkout of ebffe2a (Oct 11)
> - added documentation for fd parameter
> - nits
>
> v11:
> - applies to checkout of 46f3069 (Sep 28)
> - some filing on the documentation
> - small nits fixed
>
> v10:
> - applies to checkout of 1ce9ce6 (Sep 27)
> - addressed Michael Tsirkin's comments on v9
>
> v9:
> - addressed Michael Tsirkin's and other reviewers' comments
> - only posting Andreas Niederl's passthrough driver as the backend driver
>
> v8:
> - applies to checkout of f0fb8b7 (Aug 30)
> - fixing compilation error pointed out by Andreas Niederl
> - adding patch that allows to feed an initial state into the libtpms TPM
> - following memory API changes (glib) where necessary
>
> v7:
> - applies to checkout of b9c6cbf (Aug 9)
> - measuring the modules if multiboot is used
> - coding style fixes
>
> v6:
> - applies to checkout of 75ef849 (July 2nd)
> - some fixes and improvements to existing patches; see individual patches
> - added a patch with a null driver responding to all TPM requests with
> a response indicating failure; this backend has no dependencies and
> can alwayy be built;
> - added a patch to support the hashing of kernel, ramfs and command line
> if those were passed to Qemu using -kernel, -initrd and -append
> respectively. Measurements are taken, logged, and passed to SeaBIOS using
> the firmware interface.
> - libtpms revision 7 now requires 83kb of block storage due to having more
> NVRAM space
>
> v5:
> - applies to checkout of 1fddfba1
> - adding support for split command line using the -tpmdev ... -device ...
> options while keeping the -tpm option
> - support for querying the device models using -tpm model=?
> - support for monitor 'info tpm'
> - adding documentation of command line options for man page and web page
> - increasing room for ACPI tables that qemu reserves to 128kb (from 64kb)
> - adding (experimental) support for block migration
> - adding (experimental) support for taking measurements when kernel,
> initrd and kernel command line are directly passed to Qemu
>
> v4:
> - applies to checkout of d2d979c6
> - more coding style fixes
> - adding patch for supporting blob encryption (in addition to the existing
> QCoW2-level encryption)
> - this allows for graceful termination of a migration if the target
> is detected to have a wrong key
> - tested with big and little endian hosts
> - main thread releases mutex while checking for work to do on behalf of
> backend
> - introducing file locking (fcntl) on the block layer for serializing access
> to shared (QCoW2) files (used during migration)
>
> v3:
> - Building a null driver at patch 5/8 that responds to all requests
> with an error response; subsequently this driver is transformed to the
> libtpms-based driver for real TPM functionality
> - Reworked the threading; dropped the patch for qemu_thread_join; the
> main thread synchronizing with the TPM thread termination may need
> to write data to the block storage while waiting for the thread to
> terminate; did not previously show a problem but is safer
> - A lot of testing based on recent git checkout 4b4a72e5 (4/10):
> - migration of i686 VM from x86_64 host to i686 host to ppc64 host while
> running tests inside the VM
> - tests with S3 suspend/resume
> - tests with snapshots
> - multiple-hour tests with VM suspend/resume (using virsh save/restore)
> while running a TPM test suite inside the VM
> All tests passed; [not all of them were done on the ppc64 host]
>
> v2:
> - splitting some of the patches into smaller ones for easier review
> - fixes in individual patches
>
> Regards,
> Stefan
>
>
> Stefan Berger (7):
> Support for TPM command line options
> Add TPM (frontend) hardware interface (TPM TIS) to Qemu
> Add a debug register
> Build the TPM frontend code
> Add a TPM Passthrough backend driver implementation
> Introduce --enable-tpm-passthrough configure option
> Add fd parameter for TPM passthrough driver
>
> Makefile.target | 3 +
> configure | 28 ++
> hmp-commands.hx | 2 +
> hmp.c | 33 ++
> hmp.h | 1 +
> hw/tpm_backend.c | 58 ++++
> hw/tpm_backend.h | 43 +++
> hw/tpm_passthrough.c | 427 ++++++++++++++++++++++++
> hw/tpm_tis.c | 892 ++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/tpm_tis.h | 78 +++++
> monitor.c | 8 +
> qapi-schema.json | 31 ++
> qemu-config.c | 25 ++
> qemu-options.hx | 74 +++++
> qmp-commands.hx | 5 +
> tpm.c | 235 +++++++++++++
> tpm.h | 115 +++++++
> vl.c | 19 +
> 18 files changed, 2077 insertions(+), 0 deletions(-)
> create mode 100644 hw/tpm_backend.c
> create mode 100644 hw/tpm_backend.h
> create mode 100644 hw/tpm_passthrough.c
> create mode 100644 hw/tpm_tis.c
> create mode 100644 hw/tpm_tis.h
> create mode 100644 tpm.c
> create mode 100644 tpm.h
>
I reviewed this series and have sent all my comments. In general the
patches look good to me other than the comments I've already made.
The external interfaces (command line and monitor commands) look to be
in good shape. In particular -tpmdev can easily be modified to support
future backend vTPM implementations, such a software vTPM. And the
monitor commands are simple and straight forward (info TPM and query-TPM
with no arguments).
I haven't had the chance to run any tests with the patch series, but I
plan to soon. In the mean time I have a couple of general questions:
I understand that the passthrough driver won't support migration. What
happens on a migration attempt? Does it fail gracefully?
What happens when a 2nd guest attempts to use the passthrough vTPM after
the 1st guest is already using it? Does it also fail gracefully?
Are there any architecture dependencies for this support?
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration
2012-09-27 14:59 ` Corey Bryant
@ 2012-09-28 22:43 ` Stefan Berger
0 siblings, 0 replies; 30+ messages in thread
From: Stefan Berger @ 2012-09-28 22:43 UTC (permalink / raw)
To: qemu-devel; +Cc: Andreas Niederl, Anthony Liguori, Michael S. Tsirkin
On 09/27/2012 10:59 AM, Corey Bryant wrote:
>
>
> I reviewed this series and have sent all my comments. In general the
> patches look good to me other than the comments I've already made.
Thanks for the review.
>
> The external interfaces (command line and monitor commands) look to be
> in good shape. In particular -tpmdev can easily be modified to
> support future backend vTPM implementations, such a software vTPM.
> And the monitor commands are simple and straight forward (info TPM and
> query-TPM with no arguments).
>
> I haven't had the chance to run any tests with the patch series, but I
> plan to soon. In the mean time I have a couple of general questions:
>
> I understand that the passthrough driver won't support migration. What
> happens on a migration attempt? Does it fail gracefully?
QEMU refuses to migrate.
>
> What happens when a 2nd guest attempts to use the passthrough vTPM
> after the 1st guest is already using it? Does it also fail gracefully?
>
Yes, if something blocks /dev/tpm0 then an error message is printed. I
may adjust that error message suggesting that something else is using
the device.
> Are there any architecture dependencies for this support?
>
There are some specifics for x86: on Linux one has to do 'modprobe
tpm_tis force=1' to due missing ACPI support in SeaBIOS so far. I have
patches for SeaBIOS.
I haven't tried the passthrough driver in VMs running on any other
architectures, but I don't see reasons why it shouldn't work.
Stefan
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
2012-09-27 14:22 ` Corey Bryant
@ 2012-10-03 18:35 ` Corey Bryant
1 sibling, 0 replies; 30+ messages in thread
From: Corey Bryant @ 2012-10-03 18:35 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, anthony, andreas.niederl
I'm adding a few more comments spread throughout this series that I
forgot to mention previously..
On 06/04/2012 03:37 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()).
s/tis_tpm_receive_cb()/tpm_tis_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().
>
s/tis_tpm_receive_cb()/tpm_tis_receive_cb()/
--
Regards,
Corey Bryant
> Testing the proper functioning of the different flags and localities
> cannot be done from user space when running in Linux for example, since
> access to the address space of the TPM TIS interface is not possible. Also
> the Linux driver itself does not exercise all functionality. So, for
> testing there is a fairly extensive test suite as part of the SeaBIOS patches
> since from within the BIOS one can have full access to all the TPM's registers.
>
>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> ---
> hw/tpm_tis.c | 822 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 822 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..02b9c2e
> --- /dev/null
> +++ b/hw/tpm_tis.c
> @@ -0,0 +1,822 @@
> +/*
> + * 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 work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + * 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/pci_ids.h"
> +#include "hw/tpm_tis.h"
> +#include "qemu-error.h"
> +#include "qemu-common.h"
> +
> +/*#define DEBUG_TIS */
> +
> +#ifdef DEBUG_TIS
> +#define dprintf(fmt, ...) \
> + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define dprintf(fmt, ...) \
> + do { } while (0)
> +#endif
> +
> +/* 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 PCI_VENDOR_ID_IBM
> +#define TPM_TIS_TPM_RID 0x0001
> +
> +#define TPM_TIS_NO_DATA_BYTE 0xff
> +
> +/* utility functions */
> +
> +static uint8_t tpm_tis_locality_from_addr(target_phys_addr_t addr)
> +{
> + return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
> +}
> +
> +static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
> +{
> + return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
> +}
> +
> +static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
> +{
> +#ifdef DEBUG_TIS
> + uint32_t len, i;
> +
> + len = tpm_tis_get_size_from_buffer(sb);
> + dprintf("tpm_tis: %s length = %d\n", string, len);
> + for (i = 0; i < len; i++) {
> + if (i && !(i % 16)) {
> + dprintf("\n");
> + }
> + dprintf("%.2X ", sb->buffer[i]);
> + }
> + dprintf("\n");
> +#endif
> +}
> +
> +/*
> + * Send a request to the TPM.
> + */
> +static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
> +{
> + TPMTISState *tis = &s->s.tis;
> +
> + tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
> +
> + 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].status = TPM_TIS_STATUS_EXECUTION;
> + tis->loc[locty].sts &= ~TPM_TIS_STS_EXPECT;
> +
> + s->be_driver->ops->deliver_request(s->be_driver);
> +}
> +
> +/* 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)) {
> + dprintf("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
> + 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;
> +
> + dprintf("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty);
> +
> + 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;
> +
> + dprintf("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty);
> +
> + /*
> + * 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].status = TPM_TIS_STATUS_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].status == TPM_TIS_STATUS_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;
> + */
> + dprintf("tpm_tis: Locality %d is busy - deferring abort\n",
> + busy_locty);
> + return;
> + }
> + }
> +
> + tpm_tis_abort(s, locty);
> +}
> +
> +static void tpm_tis_receive_bh(void *opaque)
> +{
> + TPMState *s = opaque;
> + TPMTISState *tis = &s->s.tis;
> + uint8_t locty = s->command_locty;
> +
> + tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE;
> + tis->loc[locty].status = TPM_TIS_STATUS_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);
> + }
> +
> +#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
> +}
> +
> +/*
> + * 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;
> +
> + assert(s->command_locty == locty);
> +
> + qemu_bh_schedule(tis->bh);
> +}
> +
> +/*
> + * Read a byte of response data
> + */
> +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
> + }
> + dprintf("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n",
> + ret, tis->loc[locty].r_offset-1);
> + }
> +
> + 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);
> +
> + if (s->be_driver->ops->had_startup_error(s->be_driver)) {
> + 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].status) {
> + case TPM_TIS_STATUS_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;
> + }
> +
> + if (shift) {
> + val >>= shift;
> + }
> +
> + dprintf("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
> +
> + 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;
> +
> + dprintf("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
> +
> + if (locty == 4 && !hw_access) {
> + dprintf("tpm_tis: Access to locality 4 only allowed from hardware\n");
> + return;
> + }
> +
> + if (s->be_driver->ops->had_startup_error(s->be_driver)) {
> + 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) {
> + dprintf("tpm_tis: Releasing locality %d\n", locty);
> +
> + 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)) {
> + dprintf("tpm_tis: Locality %d requests use.\n", c);
> + newlocty = c;
> + break;
> + }
> + }
> + dprintf("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
> + "Next active locality: %d\n",
> + newlocty);
> +
> + 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;
> + dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
> + "Locality %d seized from locality %d\n",
> + locty, tis->active_locty);
> + dprintf("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
> + 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);
> + dprintf("tpm_tis: Lowering IRQ\n");
> + }
> + }
> + 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].status) {
> +
> + case TPM_TIS_STATUS_READY:
> + tis->loc[locty].w_offset = 0;
> + tis->loc[locty].r_offset = 0;
> + break;
> +
> + case TPM_TIS_STATUS_IDLE:
> + tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
> + tis->loc[locty].status = TPM_TIS_STATUS_READY;
> + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
> + break;
> +
> + case TPM_TIS_STATUS_EXECUTION:
> + case TPM_TIS_STATUS_RECEPTION:
> + /* abort currently running command */
> + dprintf("tpm_tis: %s: Initiating abort.\n",
> + __func__);
> + tpm_tis_prep_abort(s, locty, locty);
> + break;
> +
> + case TPM_TIS_STATUS_COMPLETION:
> + tis->loc[locty].w_offset = 0;
> + tis->loc[locty].r_offset = 0;
> + /* shortcut to ready state with C/R set */
> + tis->loc[locty].status = TPM_TIS_STATUS_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].status) {
> + case TPM_TIS_STATUS_RECEPTION:
> + tpm_tis_tpm_send(s, locty);
> + break;
> + default:
> + /* ignore */
> + break;
> + }
> + } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
> + switch (tis->loc[locty].status) {
> + case TPM_TIS_STATUS_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].status == TPM_TIS_STATUS_IDLE ||
> + tis->loc[locty].status == TPM_TIS_STATUS_EXECUTION ||
> + tis->loc[locty].status == TPM_TIS_STATUS_COMPLETION) {
> + /* drop the byte */
> + } else {
> + dprintf("tpm_tis: Byte to send to TPM: %02x\n", (uint8_t)val);
> + if (tis->loc[locty].status == TPM_TIS_STATUS_READY) {
> + tis->loc[locty].status = TPM_TIS_STATUS_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;
> + }
> +}
> +
> +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_LITTLE_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 = DO_UPCAST(TPMState, busdev.qdev, d);
> + TPMTISState *tis = &s->s.tis;
> + int c;
> +
> + 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].status = TPM_TIS_STATUS_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;
> +
> + 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);
> + goto err_exit;
> + }
> +
> + s->be_driver->fe_model = "tpm-tis";
> +
> + if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) {
> + goto err_exit;
> + }
> +
> + tis->bh = qemu_bh_new(tpm_tis_receive_bh, s);
> +
> + if (tis->irq_num > 15) {
> + error_report("IRQ %d for TPM TIS is outside valid range of 0 to 15.\n",
> + tis->irq_num);
> + goto err_exit;
> + }
> +
> + isa_init_irq(dev, &tis->irq, tis->irq_num);
> +
> + memory_region_init_io(&s->mmio, &tpm_tis_memory_ops, s, "tpm-tis-mmio",
> + TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT);
> + 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_del_subregion(get_system_memory(), &s->mmio);
> + memory_region_destroy(&s->mmio);
> +
> + err_exit:
> + return -1;
> +}
> +
> +static const VMStateDescription vmstate_tpm_tis = {
> + .name = "tpm",
> + .unmigratable = 1,
> +};
> +
> +static Property tpm_tis_properties[] = {
> + 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_class_initfn(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
> +
> + ic->init = tpm_tis_init;
> +
> + dc->props = tpm_tis_properties;
> + dc->reset = tpm_tis_reset;
> + dc->vmsd = &vmstate_tpm_tis;
> +}
> +
> +static TypeInfo tpm_tis_info = {
> + .name = "tpm-tis",
> + .parent = TYPE_ISA_DEVICE,
> + .class_init = tpm_tis_class_initfn,
> + .instance_size = sizeof(TPMState),
> +};
> +
> +static void tpm_tis_register(void)
> +{
> + type_register_static(&tpm_tis_info);
> +}
> +
> +type_init(tpm_tis_register)
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 7/7] Add fd parameter for TPM passthrough driver
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
2012-09-27 14:35 ` Corey Bryant
@ 2012-10-03 18:46 ` Corey Bryant
2012-10-24 19:06 ` Stefan Berger
1 sibling, 1 reply; 30+ messages in thread
From: Corey Bryant @ 2012-10-03 18:46 UTC (permalink / raw)
To: Stefan Berger; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 06/04/2012 03:37 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>
> ---
> hmp.c | 7 +++++-
> hw/tpm_passthrough.c | 53 ++++++++++++++++++++++++++++++++++++++-----------
> qapi-schema.json | 4 ++-
> qemu-config.c | 5 ++++
> qemu-options.hx | 11 +++++++--
> tpm.c | 4 +++
> tpm.h | 1 +
> 7 files changed, 68 insertions(+), 17 deletions(-)
>
> diff --git a/hmp.c b/hmp.c
> index 9aedaff..3176c74 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -567,10 +567,15 @@ void hmp_info_tpm(Monitor *mon)
> TPMInfo *ti = info->value;
> monitor_printf(mon, " tpm%d: model=%s\n",
> c, ti->model);
> - monitor_printf(mon, " \\ %s: type=%s%s%s\n",
> + monitor_printf(mon, " \\ %s: type=%s%s%s",
> ti->id, ti->type,
> ti->has_path ? ",path=" : "",
> ti->has_path ? ti->path : "");
> + if (ti->has_fd) {
> + monitor_printf(mon, ",fd=%" PRId64,
> + ti->fd);
> + }
> + monitor_printf(mon, "\n");
> c++;
> }
> qapi_free_TPMInfoList(info_list);
> diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
> index 8914705..44cf272 100644
> --- a/hw/tpm_passthrough.c
> +++ b/hw/tpm_passthrough.c
> @@ -305,26 +305,54 @@ static int tpm_passthrough_test_tpmdev(int fd)
> static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
> {
> const char *value;
> + 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=");
> + goto err_exit;
> + }
> +
> + 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");
> + goto err_exit;
> + }
> +
> + tb->tpm_fd = &tb->s.tpm_pt->tpm_fd;
> + } else {
> + value = qemu_opt_get(opts, "path");
> + if (!value) {
> + value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
> + }
>
> - tb->s.tpm_pt->tpm_dev = g_strdup(value);
> + tb->s.tpm_pt->tpm_dev = g_strdup(value);
>
> - tb->path = g_strdup(tb->s.tpm_pt->tpm_dev);
> + tb->path = g_strdup(value);
> +
> + 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_free_parameters;
> + }
> + }
> +
> + 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;
> + }
>
> - 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);
> + /* only allow character devices for now */
> + if (!S_ISCHR(statbuf.st_mode)) {
> + error_report("TPM file descriptor is not a character device");
> goto err_free_parameters;
> }
>
> 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);
> + error_report("Device is not a TPM.\n");
> goto err_close_tpmdev;
> }
>
> @@ -341,6 +369,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
> g_free(tb->s.tpm_pt->tpm_dev);
> tb->s.tpm_pt->tpm_dev = NULL;
>
> + err_exit:
> return 1;
> }
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 8799322..9d6f664 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1769,10 +1769,12 @@
> #
> # @path: #optional Path to the TPM backend device
> #
> +# @fd: #optional File descriptor for the TPM backend device
> # Since: 1.1
> ##
> { 'type': 'TPMInfo',
> - 'data': {'model': 'str', 'id': 'str', 'type': 'str', '*path': 'str' } }
> + 'data': {'model': 'str', 'id': 'str', 'type': 'str', '*path': 'str',
> + '*fd' : 'int' } }
>
> ##
> # @query-tpm
> diff --git a/qemu-config.c b/qemu-config.c
> index edc8d5d..a5e2677 100644
> --- a/qemu-config.c
> +++ b/qemu-config.c
> @@ -628,6 +628,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",
Filedescriptor needs a space.
> + },
> { /* end of list */ }
> },
> };
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 0b9fa0b..cb9892b 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -2017,8 +2017,9 @@ DEFHEADING()
> DEFHEADING(TPM device options:)
>
> DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
> - "-tpmdev passthrough,id=id[,path=path]\n"
> - " use path to provide path to a character device; default is /dev/tpm0\n",
> + "-tpmdev passthrough,id=id[,path=path][,fd=h]\n"
> + " use path to provide path to a character device; default is /dev/tpm0\n"
> + " use fd to provide a file descriptor to a character device\n",
> QEMU_ARCH_ALL)
> STEXI
>
> @@ -2040,7 +2041,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.
> @@ -2049,6 +2050,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
> diff --git a/tpm.c b/tpm.c
> index 79530c9..64d3140 100644
> --- a/tpm.c
> +++ b/tpm.c
> @@ -201,6 +201,10 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
> res->path = g_strdup(drv->path);
> res->has_path = true;
> }
> + if (drv->tpm_fd != NULL && *drv->tpm_fd >= 0) {
> + res->fd = *drv->tpm_fd;
> + res->has_fd = true;
> + }
Is an else path needed to set res->has_fd = false if there's no tpm_fd?
--
Regards,
Corey Bryant
> res->type = g_strdup(drv->ops->id);
>
> return res;
> diff --git a/tpm.h b/tpm.h
> index bacaff3..00f7600 100644
> --- a/tpm.h
> +++ b/tpm.h
> @@ -24,6 +24,7 @@ typedef struct TPMBackend {
> char *id;
> const char *fe_model;
> char *path;
> + int *tpm_fd;
> const TPMDriverOps *ops;
>
> union {
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2012-09-27 14:22 ` Corey Bryant
@ 2012-10-24 18:46 ` Stefan Berger
2012-11-08 15:39 ` Corey Bryant
0 siblings, 1 reply; 30+ messages in thread
From: Stefan Berger @ 2012-10-24 18:46 UTC (permalink / raw)
To: Corey Bryant; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 09/27/2012 10:22 AM, Corey Bryant wrote:
>
>
> On 06/04/2012 03:37 PM, Stefan Berger wrote:
>> +
>> +/* whether the STS interrupt is supported */
>> +/*#define RAISE_STS_IRQ */
>
> Why is this commented out?
>
Will activate it.
>> + if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
>> + (tis->loc[locty].inte & irqmask)) {
>> + dprintf("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
>> + qemu_irq_raise(s->s.tis.irq);
>> + tis->loc[locty].ints |= irqmask;
>
> Should the ints irqmask be set before the interrupt is raised?
>
I don't think the order matters. If multiple threads were in here we'd
need to lock the access to the variable altogether.
>> + 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);
>
> Should TPM_TIS_ACCESS_REQUEST_USE be modified for the old locality
> when we are in here for TPM_TIS_ACCESS_SEIZE?
>
> The TPM TIS document says the following (in the TPM_ACCESS_x table,
> under the Seize bit description):
>
> "2. Setting this bit does not affect the state of the
> TPM_ACCESS_x.requestUse bit for any locality except the one issuing
> the Seize bit."
>
Good catch. In the case of the function being called as part of a seize,
the REQUEST_USE flag will not be touched anymore.
>> + tis->loc[locty].sts = TPM_TIS_STS_VALID |
>> TPM_TIS_STS_DATA_AVAILABLE;
>> + tis->loc[locty].status = TPM_TIS_STATUS_COMPLETION;
>
> Can tis->loc[locty].status be changed to tis->loc[locty].state? This
> is very confusing when named "status" because it is easy to confuse
> with the TPM_INT_STATUS register, which in actuality it is unrelated.
>
Yes, it's now called TPMTISState and the previously existing TPMTISState
has now been renamed to TPMTISEmuState.
>> + 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;
>
> Should dataAvail be turned off here?
>
The data available flag TPM_TIS_STS_DATA_AVAILABLE is part of the .sts
field and is turned off due to the above value assignment.
>> + 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;
>
> Can you create/use a #define for burstCount instead of using a
> hard-coded 8?
>
Ok.
>> + switch (off) {
>> + case TPM_TIS_REG_ACCESS:
>> +
>> + if ((val & TPM_TIS_ACCESS_SEIZE)) {
>> + val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
>> + TPM_TIS_ACCESS_ACTIVE_LOCALITY);
>> + }
>
> Is there a reason why this code can't be merged with the "(val &
> TPM_TIS_ACCESS_SEIZE)" check that is down below?
>
The above code means that in case the SEIZE flag is set, all other flags
are ignored. It makes the subsequent tests for single bits a lot easier
to handle. I prefer to do the masking of those bits at the beginning.
>> + /* 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;
>
> Were you intending to break from the for loop or the while?
>
Right. I am setting a flag here now to then leave the while loop.
>> +
>> + tis->loc[locty].inte = (val & (TPM_TIS_INT_ENABLED | (0x3 <<
>> 3) |
>> + TPM_TIS_INTERRUPTS_SUPPORTED));
>
> Is 0x3 << 3 == typePolarity? Could a #define be introduced for this
> instead of hard coding it?
>
Ok.
>> + case TPM_TIS_STATUS_EXECUTION:
>> + case TPM_TIS_STATUS_RECEPTION:
>> + /* abort currently running command */
>> + dprintf("tpm_tis: %s: Initiating abort.\n",
>> + __func__);
>> + tpm_tis_prep_abort(s, locty, locty);
>> + break;
>> +
>> + case TPM_TIS_STATUS_COMPLETION:
>
> Does this path need to abort if TPM_STS_x.dataAvail is on? This
> comment is based on "Table 19: State Transition Table." from the TPM
> TIS document.
>
If TPM_TIS_STATUS_COMPLETION is the current state, then independent of
the TPM_TIS_STS_DATA_AVAILABLE flag the state transition is to idle
(states 30 and 37 in the spec). Following state 0.B in the spec, we
implement a TPM without idle state and so we transition to READY state
immediately. The data available flag should be reset, though.
>> + tis->loc[locty].sts = TPM_TIS_STS_EXPECT |
>> + TPM_TIS_STS_VALID;
>> + } else {
>> + /* packet complete */
>> + tis->loc[locty].sts = TPM_TIS_STS_VALID;
>
> Should EXPECT be turned off here instead of where it is currently
> turned off in tpm_tis_tpm_send?
>
The TPM_TIS_STS_EXPECT is turned off as part of the above value
assignment but I removed the unnecessary masking of this bit in
tpm_tis_tpm_send now and let the GO flag only submit the command if the
EXPECT flag is not set anymore.
I hope this addresses your concerns in this part.
Regards,
Stefan
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 1/7] Support for TPM command line options
2012-09-27 14:12 ` Corey Bryant
@ 2012-10-24 19:06 ` Stefan Berger
2012-11-08 15:52 ` Corey Bryant
0 siblings, 1 reply; 30+ messages in thread
From: Stefan Berger @ 2012-10-24 19:06 UTC (permalink / raw)
To: Corey Bryant; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 09/27/2012 10:12 AM, Corey Bryant wrote:
>
>
> On 06/04/2012 03:37 PM, Stefan Berger wrote:
>> +
>> +#ifdef CONFIG_TPM
>> +
>> +static const TPMDriverOps *bes[] = {
>
> I think bes[] would be more descriptive if it were named be_drivers[]
> or be_ops[]?
>
Renamed to be_drivers.
>> + if (!QLIST_EMPTY(&tpm_backends)) {
>> + error_report("Only one TPM is allowed.\n");
>> + return 1;
>> + }
>
> A list of tpm_backends is maintained and walked in a few places, but
> only one is allowed to be added to the list. Will it ever make sense
> to enable multiple backends at one time?
>
A list is also returned through the monitor. This list can at the moment
only have maximum of one entry. I would keep that list there unless
someone else opposes. It may be possible to create different types of
hardware emulation interfaces or simply replicate the TPM TIS at
different addresses. So I cannot say whether it will 'ever make sense'
to do that but I'd rather keep the opportunity there than close it and
with that also let the monitor return a list of items rather than a
single item.
I removed the processing of the lists in this part of the code at least.
>> +
>> + 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);
>
> The "type" value is being passed but the tpm_get_backend_driver()
> defines the parameter as "id". Maybe "id" could be renamed to "type"
> for consistency. See similar comment further down in this email.
>
Done.
>> + */
>> +int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
>> +{
>> + QemuOpts *opts;
>> +
>> + if (strcmp("none", optarg) != 0) {
>
> What's the point of supporting "-tpmdev none"?
>
Removed.
>> +typedef struct TPMBackend {
>> + char *id;
>
> For consistency, this could be named "type" instead of "id" since it
> corresponds to -tpmdev's type.
>
Yes.
>> + uint8_t command_locty;
>
> It would be easier to read if locality was spelled out fully, instead
> of locty. But if that causes lines to be too long then maybe it's not
> worth it.
I rather keep it 'locty'.
>
>> + TPMLocality *cmd_locty;
>
> There's a cmd_locty and a command_locty. command_locty is the locality
> number and cmd_locty is the locality data. Could these variable names
> be updated to be more unique and descriptive?
>
Will rename them command_locty -> locty_number and cmd_locty -> locty_data.
Regards,
Stefan
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 7/7] Add fd parameter for TPM passthrough driver
2012-10-03 18:46 ` Corey Bryant
@ 2012-10-24 19:06 ` Stefan Berger
0 siblings, 0 replies; 30+ messages in thread
From: Stefan Berger @ 2012-10-24 19:06 UTC (permalink / raw)
To: Corey Bryant; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 10/03/2012 02:46 PM, Corey Bryant wrote:
>
>
> On 06/04/2012 03:37 PM, Stefan Berger wrote:
>> @@ -201,6 +201,10 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
>> res->path = g_strdup(drv->path);
>> res->has_path = true;
>> }
>> + if (drv->tpm_fd != NULL && *drv->tpm_fd >= 0) {
>> + res->fd = *drv->tpm_fd;
>> + res->has_fd = true;
>> + }
>
> Is an else path needed to set res->has_fd = false if there's no tpm_fd?
>
res is allocated using g_new0, so all fields are set to 0. With that
has_fd is also set to false.
Regards,
Stefan
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 5/7] Add a TPM Passthrough backend driver implementation
2012-09-27 14:28 ` Corey Bryant
@ 2012-10-24 19:07 ` Stefan Berger
0 siblings, 0 replies; 30+ messages in thread
From: Stefan Berger @ 2012-10-24 19:07 UTC (permalink / raw)
To: Corey Bryant; +Cc: mst, qemu-devel, anthony, andreas.niederl
On 09/27/2012 10:28 AM, Corey Bryant wrote:
>
>
> On 06/04/2012 03:37 PM, Stefan Berger wrote:
>> +
>> +/* borrowed from qemu-char.c */
>> +static int tpm_passthrough_unix_write(int fd, const uint8_t *buf,
>> uint32_t len)
>> +{
>> + return send_all(fd, buf, len);
>> +}
>> +
>> +static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t
>> len)
>
> It would probably be useful to other parts of QEMU if you defined this
> function as recv_all() and put it in qemu-char.c (to correspond with
> send_all()).
>
Ok. I'd keep this patch to qemu-char.c inside of this patch, though,
unless someone opposes.
>> +
>> +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;
>
> tpm_passthrough_do_startup_tpm() always returns zero, so
> had_startup_error will never be set to true.
>
I am consolidating the code into tpm_passthrough_startup_tpm() but let
that return an int considering that other backend drivers may return an
error code.
>> +static void tpm_passthrough_reset(TPMBackend *tb)
>> +{
>> + TPMPassthruState *tpm_pt = tb->s.tpm_pt;
>> +
>> + dprintf("tpm_passthrough: CALL TO TPM_RESET!\n");
>> +
>> + tpm_backend_thread_end(&tpm_pt->tbt);
>
> Should the thread be restarted here?
>
No. If the backend needs to be restarted, the frontend will invoke the
startup_tpm function.
>> +static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
>> +{
>> + return false;
>
> Can a T/OS never be established with the passthrough backend?
>
Following the specs, this bit is set when locality 5 is used and a hash
operation is started. See part 2 of the TPM specs.
The establishment bit could be read from the hardware TPM's permanent
flags. The question is what it means to show this bit inside the VM. I
wasn't sure, so I hardcoded it to always return false.
Stefan
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2012-10-24 18:46 ` Stefan Berger
@ 2012-11-08 15:39 ` Corey Bryant
2012-11-12 13:16 ` Stefan Berger
0 siblings, 1 reply; 30+ messages in thread
From: Corey Bryant @ 2012-11-08 15:39 UTC (permalink / raw)
To: Stefan Berger; +Cc: andreas.niederl, qemu-devel, anthony, mst
Thanks for your responses. I have a few comments below.
On 10/24/2012 02:46 PM, Stefan Berger wrote:
> On 09/27/2012 10:22 AM, Corey Bryant wrote:
>>
>>
>> On 06/04/2012 03:37 PM, Stefan Berger wrote:
>>> +
>>> +/* whether the STS interrupt is supported */
>>> +/*#define RAISE_STS_IRQ */
>>
>> Why is this commented out?
>>
> Will activate it.
>
>>> + if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
>>> + (tis->loc[locty].inte & irqmask)) {
>>> + dprintf("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
>>> + qemu_irq_raise(s->s.tis.irq);
>>> + tis->loc[locty].ints |= irqmask;
>>
>> Should the ints irqmask be set before the interrupt is raised?
>>
>
> I don't think the order matters. If multiple threads were in here we'd
> need to lock the access to the variable altogether.
>
>
>>> + 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);
>>
>> Should TPM_TIS_ACCESS_REQUEST_USE be modified for the old locality
>> when we are in here for TPM_TIS_ACCESS_SEIZE?
>>
>> The TPM TIS document says the following (in the TPM_ACCESS_x table,
>> under the Seize bit description):
>>
>> "2. Setting this bit does not affect the state of the
>> TPM_ACCESS_x.requestUse bit for any locality except the one issuing
>> the Seize bit."
>>
>
> Good catch. In the case of the function being called as part of a seize,
> the REQUEST_USE flag will not be touched anymore.
>
>>> + tis->loc[locty].sts = TPM_TIS_STS_VALID |
>>> TPM_TIS_STS_DATA_AVAILABLE;
>>> + tis->loc[locty].status = TPM_TIS_STATUS_COMPLETION;
>>
>> Can tis->loc[locty].status be changed to tis->loc[locty].state? This
>> is very confusing when named "status" because it is easy to confuse
>> with the TPM_INT_STATUS register, which in actuality it is unrelated.
>>
>
> Yes, it's now called TPMTISState and the previously existing TPMTISState
> has now been renamed to TPMTISEmuState.
>
>
>>> + 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;
>>
>> Should dataAvail be turned off here?
>>
>
> The data available flag TPM_TIS_STS_DATA_AVAILABLE is part of the .sts
> field and is turned off due to the above value assignment.
>
>>> + 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;
>>
>> Can you create/use a #define for burstCount instead of using a
>> hard-coded 8?
>>
> Ok.
>
>>> + switch (off) {
>>> + case TPM_TIS_REG_ACCESS:
>>> +
>>> + if ((val & TPM_TIS_ACCESS_SEIZE)) {
>>> + val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
>>> + TPM_TIS_ACCESS_ACTIVE_LOCALITY);
>>> + }
>>
>> Is there a reason why this code can't be merged with the "(val &
>> TPM_TIS_ACCESS_SEIZE)" check that is down below?
>>
>
> The above code means that in case the SEIZE flag is set, all other flags
> are ignored. It makes the subsequent tests for single bits a lot easier
> to handle. I prefer to do the masking of those bits at the beginning.
>
>
>>> + /* 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;
>>
>> Were you intending to break from the for loop or the while?
>>
>
> Right. I am setting a flag here now to then leave the while loop.
>
Are you setting the flag or testing it? I'm not sure this code is
serving any purpose the way it is, since it is testing the flag and then
breaking from the for loop if it's on. That's why I was wondering if
you meant to break from the while loop instead.
>>> +
>>> + tis->loc[locty].inte = (val & (TPM_TIS_INT_ENABLED | (0x3 <<
>>> 3) |
>>> + TPM_TIS_INTERRUPTS_SUPPORTED));
>>
>> Is 0x3 << 3 == typePolarity? Could a #define be introduced for this
>> instead of hard coding it?
>>
>
> Ok.
>
>>> + case TPM_TIS_STATUS_EXECUTION:
>>> + case TPM_TIS_STATUS_RECEPTION:
>>> + /* abort currently running command */
>>> + dprintf("tpm_tis: %s: Initiating abort.\n",
>>> + __func__);
>>> + tpm_tis_prep_abort(s, locty, locty);
>>> + break;
>>> +
>>> + case TPM_TIS_STATUS_COMPLETION:
>>
>> Does this path need to abort if TPM_STS_x.dataAvail is on? This
>> comment is based on "Table 19: State Transition Table." from the TPM
>> TIS document.
>>
>
> If TPM_TIS_STATUS_COMPLETION is the current state, then independent of
> the TPM_TIS_STS_DATA_AVAILABLE flag the state transition is to idle
> (states 30 and 37 in the spec). Following state 0.B in the spec, we
> implement a TPM without idle state and so we transition to READY state
> immediately. The data available flag should be reset, though.
>
Ok. But row 30 in the table also says it aborts the command in the
"Action Taken" column.
>
>>> + tis->loc[locty].sts = TPM_TIS_STS_EXPECT |
>>> + TPM_TIS_STS_VALID;
>>> + } else {
>>> + /* packet complete */
>>> + tis->loc[locty].sts = TPM_TIS_STS_VALID;
>>
>> Should EXPECT be turned off here instead of where it is currently
>> turned off in tpm_tis_tpm_send?
>>
>
> The TPM_TIS_STS_EXPECT is turned off as part of the above value
> assignment but I removed the unnecessary masking of this bit in
> tpm_tis_tpm_send now and let the GO flag only submit the command if the
> EXPECT flag is not set anymore.
>
> I hope this addresses your concerns in this part.
Thanks. Yes, you've addressed my concerns minus the few comments I have
above.
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 1/7] Support for TPM command line options
2012-10-24 19:06 ` Stefan Berger
@ 2012-11-08 15:52 ` Corey Bryant
2012-11-12 13:04 ` Stefan Berger
0 siblings, 1 reply; 30+ messages in thread
From: Corey Bryant @ 2012-11-08 15:52 UTC (permalink / raw)
To: Stefan Berger; +Cc: andreas.niederl, qemu-devel, anthony, mst
On 10/24/2012 03:06 PM, Stefan Berger wrote:
> On 09/27/2012 10:12 AM, Corey Bryant wrote:
>>
>>
>> On 06/04/2012 03:37 PM, Stefan Berger wrote:
>>> +
>>> +#ifdef CONFIG_TPM
>>> +
>>> +static const TPMDriverOps *bes[] = {
>>
>> I think bes[] would be more descriptive if it were named be_drivers[]
>> or be_ops[]?
>>
>
> Renamed to be_drivers.
>
>>> + if (!QLIST_EMPTY(&tpm_backends)) {
>>> + error_report("Only one TPM is allowed.\n");
>>> + return 1;
>>> + }
>>
>> A list of tpm_backends is maintained and walked in a few places, but
>> only one is allowed to be added to the list. Will it ever make sense
>> to enable multiple backends at one time?
>>
>
> A list is also returned through the monitor. This list can at the moment
> only have maximum of one entry. I would keep that list there unless
> someone else opposes. It may be possible to create different types of
> hardware emulation interfaces or simply replicate the TPM TIS at
> different addresses. So I cannot say whether it will 'ever make sense'
> to do that but I'd rather keep the opportunity there than close it and
> with that also let the monitor return a list of items rather than a
> single item.
>
> I removed the processing of the lists in this part of the code at least.
>
Ok and it doesn't hurt to keep the list processing. In that case you
might as well keep the list processing code everywhere that you already
have it.
>>> +
>>> + 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);
>>
>> The "type" value is being passed but the tpm_get_backend_driver()
>> defines the parameter as "id". Maybe "id" could be renamed to "type"
>> for consistency. See similar comment further down in this email.
>>
>
> Done.
>
>>> + */
>>> +int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
>>> +{
>>> + QemuOpts *opts;
>>> +
>>> + if (strcmp("none", optarg) != 0) {
>>
>> What's the point of supporting "-tpmdev none"?
>>
>
> Removed.
>
There must have been a reason you added it in the first place that I'm
just not aware of. Did someone else suggest adding it?
>>> +typedef struct TPMBackend {
>>> + char *id;
>>
>> For consistency, this could be named "type" instead of "id" since it
>> corresponds to -tpmdev's type.
>>
>
> Yes.
>
>>> + uint8_t command_locty;
>>
>> It would be easier to read if locality was spelled out fully, instead
>> of locty. But if that causes lines to be too long then maybe it's not
>> worth it.
>
> I rather keep it 'locty'.
>
>>
>>> + TPMLocality *cmd_locty;
>>
>> There's a cmd_locty and a command_locty. command_locty is the locality
>> number and cmd_locty is the locality data. Could these variable names
>> be updated to be more unique and descriptive?
>>
>
> Will rename them command_locty -> locty_number and cmd_locty -> locty_data.
>
> Regards,
>
> Stefan
>
>
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 1/7] Support for TPM command line options
2012-11-08 15:52 ` Corey Bryant
@ 2012-11-12 13:04 ` Stefan Berger
0 siblings, 0 replies; 30+ messages in thread
From: Stefan Berger @ 2012-11-12 13:04 UTC (permalink / raw)
To: Corey Bryant; +Cc: andreas.niederl, qemu-devel, anthony, mst
On 11/08/2012 10:52 AM, Corey Bryant wrote:
>
>
> On 10/24/2012 03:06 PM, Stefan Berger wrote:
>> On 09/27/2012 10:12 AM, Corey Bryant wrote:
>>>
>>>
>>> On 06/04/2012 03:37 PM, Stefan Berger wrote:
>>
>>>> + if (!QLIST_EMPTY(&tpm_backends)) {
>>>> + error_report("Only one TPM is allowed.\n");
>>>> + return 1;
>>>> + }
>>>
>>> A list of tpm_backends is maintained and walked in a few places, but
>>> only one is allowed to be added to the list. Will it ever make sense
>>> to enable multiple backends at one time?
>>>
>>
>> A list is also returned through the monitor. This list can at the moment
>> only have maximum of one entry. I would keep that list there unless
>> someone else opposes. It may be possible to create different types of
>> hardware emulation interfaces or simply replicate the TPM TIS at
>> different addresses. So I cannot say whether it will 'ever make sense'
>> to do that but I'd rather keep the opportunity there than close it and
>> with that also let the monitor return a list of items rather than a
>> single item.
>>
>> I removed the processing of the lists in this part of the code at least.
>>
>
> Ok and it doesn't hurt to keep the list processing. In that case you
> might as well keep the list processing code everywhere that you
> already have it.
>
I was only going to keep it in the monitor part now...
+ */
>>>> +int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
>>>> +{
>>>> + QemuOpts *opts;
>>>> +
>>>> + if (strcmp("none", optarg) != 0) {
>>>
>>> What's the point of supporting "-tpmdev none"?
>>>
>>
>> Removed.
>>
>
> There must have been a reason you added it in the first place that I'm
> just not aware of. Did someone else suggest adding it?
Not that I can remember.The option would have been useful if every VM by
default had a TPM, similar to the physical world today, but it's
unlikely that this will happen.
Regards,
Stefan
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2012-11-08 15:39 ` Corey Bryant
@ 2012-11-12 13:16 ` Stefan Berger
2012-11-12 18:48 ` Corey Bryant
0 siblings, 1 reply; 30+ messages in thread
From: Stefan Berger @ 2012-11-12 13:16 UTC (permalink / raw)
To: Corey Bryant; +Cc: andreas.niederl, qemu-devel, anthony, mst
On 11/08/2012 10:39 AM, Corey Bryant wrote:
> Thanks for your responses. I have a few comments below.
>
> On 10/24/2012 02:46 PM, Stefan Berger wrote:
>> On 09/27/2012 10:22 AM, Corey Bryant wrote:
>>>
>>>
>>> On 06/04/2012 03:37 PM, Stefan Berger wrote:
>>
>>>> + /* 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;
>>>
>>> Were you intending to break from the for loop or the while?
>>>
>>
>> Right. I am setting a flag here now to then leave the while loop.
>>
>
> Are you setting the flag or testing it? I'm not sure this code is
> serving any purpose the way it is, since it is testing the flag and
> then breaking from the for loop if it's on. That's why I was
> wondering if you meant to break from the while loop instead.
>
Here's how the patch looks now:
+ 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)) {
+ bool higher_seize = FALSE;
+
+ /* 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)) {
+ higher_seize = TRUE;
+ break;
+ }
+ }
+
+ if (higher_seize) {
+ break;
+ }
+
+ /* cancel any seize by a lower locality */
+ for (l = 0; l < locty - 1; l++) {
+ tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
+ }
[...]
>>>> + case TPM_TIS_STATUS_EXECUTION:
>>>> + case TPM_TIS_STATUS_RECEPTION:
>>>> + /* abort currently running command */
>>>> + dprintf("tpm_tis: %s: Initiating abort.\n",
>>>> + __func__);
>>>> + tpm_tis_prep_abort(s, locty, locty);
>>>> + break;
>>>> +
>>>> + case TPM_TIS_STATUS_COMPLETION:
>>>
>>> Does this path need to abort if TPM_STS_x.dataAvail is on? This
>>> comment is based on "Table 19: State Transition Table." from the TPM
>>> TIS document.
>>>
>>
>> If TPM_TIS_STATUS_COMPLETION is the current state, then independent of
>> the TPM_TIS_STS_DATA_AVAILABLE flag the state transition is to idle
>> (states 30 and 37 in the spec). Following state 0.B in the spec, we
>> implement a TPM without idle state and so we transition to READY state
>> immediately. The data available flag should be reset, though.
>>
>
> Ok. But row 30 in the table also says it aborts the command in the
> "Action Taken" column.
>
Row 30 describes that abort for while the TPM is in 'Completion' state,
meaning the TPM has delivered the response from the TPM to the TIS and
now an application can pick up the response. The abort in this case is
achieved through changing the state and resetting the (response) buffer
pointers so that the application will not receive more response bytes.
Regards,
Stefan
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu
2012-11-12 13:16 ` Stefan Berger
@ 2012-11-12 18:48 ` Corey Bryant
0 siblings, 0 replies; 30+ messages in thread
From: Corey Bryant @ 2012-11-12 18:48 UTC (permalink / raw)
To: Stefan Berger; +Cc: andreas.niederl, qemu-devel, anthony, mst
On 11/12/2012 08:16 AM, Stefan Berger wrote:
> On 11/08/2012 10:39 AM, Corey Bryant wrote:
>> Thanks for your responses. I have a few comments below.
>>
>> On 10/24/2012 02:46 PM, Stefan Berger wrote:
>>> On 09/27/2012 10:22 AM, Corey Bryant wrote:
>>>>
>>>>
>>>> On 06/04/2012 03:37 PM, Stefan Berger wrote:
>>>
>>>>> + /* 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;
>>>>
>>>> Were you intending to break from the for loop or the while?
>>>>
>>>
>>> Right. I am setting a flag here now to then leave the while loop.
>>>
>>
>> Are you setting the flag or testing it? I'm not sure this code is
>> serving any purpose the way it is, since it is testing the flag and
>> then breaking from the for loop if it's on. That's why I was
>> wondering if you meant to break from the while loop instead.
>>
>
> Here's how the patch looks now:
>
>
> + 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)) {
> + bool higher_seize = FALSE;
> +
> + /* 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)) {
> + higher_seize = TRUE;
> + break;
> + }
> + }
> +
> + if (higher_seize) {
> + break;
> + }
> +
> + /* cancel any seize by a lower locality */
> + for (l = 0; l < locty - 1; l++) {
> + tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
> + }
> [...]
>
>
Ok that looks good.
--
Regards,
Corey Bryant
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2012-11-12 18:49 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-04 19:37 [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 1/7] Support for TPM command line options Stefan Berger
2012-09-27 14:12 ` Corey Bryant
2012-10-24 19:06 ` Stefan Berger
2012-11-08 15:52 ` Corey Bryant
2012-11-12 13:04 ` Stefan Berger
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 2/7] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
2012-09-27 14:22 ` Corey Bryant
2012-10-24 18:46 ` Stefan Berger
2012-11-08 15:39 ` Corey Bryant
2012-11-12 13:16 ` Stefan Berger
2012-11-12 18:48 ` Corey Bryant
2012-10-03 18:35 ` Corey Bryant
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 3/7] Add a debug register Stefan Berger
2012-09-27 14:23 ` Corey Bryant
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 4/7] Build the TPM frontend code Stefan Berger
2012-09-27 14:24 ` Corey Bryant
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 5/7] Add a TPM Passthrough backend driver implementation Stefan Berger
2012-09-27 14:28 ` Corey Bryant
2012-10-24 19:07 ` Stefan Berger
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 6/7] Introduce --enable-tpm-passthrough configure option Stefan Berger
2012-09-27 14:29 ` Corey Bryant
2012-06-04 19:37 ` [Qemu-devel] [PATCH V19 7/7] Add fd parameter for TPM passthrough driver Stefan Berger
2012-09-27 14:35 ` Corey Bryant
2012-10-03 18:46 ` Corey Bryant
2012-10-24 19:06 ` Stefan Berger
2012-06-04 19:56 ` [Qemu-devel] [PATCH V19 0/7] Qemu Trusted Platform Module (TPM) integration Stefan Weil
2012-06-04 23:08 ` Anthony Liguori
2012-09-27 14:59 ` Corey Bryant
2012-09-28 22:43 ` Stefan Berger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).