From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:39003) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SCe3d-0004mq-Eo for qemu-devel@nongnu.org; Tue, 27 Mar 2012 17:36:04 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SCe3a-0004cv-4n for qemu-devel@nongnu.org; Tue, 27 Mar 2012 17:36:01 -0400 Received: from mail-ob0-f173.google.com ([209.85.214.173]:56380) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SCe3Z-0004ck-TI for qemu-devel@nongnu.org; Tue, 27 Mar 2012 17:35:58 -0400 Received: by obbwd20 with SMTP id wd20so424867obb.4 for ; Tue, 27 Mar 2012 14:35:56 -0700 (PDT) Message-ID: <4F7232B8.2080001@codemonkey.ws> Date: Tue, 27 Mar 2012 16:35:52 -0500 From: Anthony Liguori MIME-Version: 1.0 References: <1332879879-29460-1-git-send-email-stefanb@linux.vnet.ibm.com> <1332879879-29460-2-git-send-email-stefanb@linux.vnet.ibm.com> In-Reply-To: <1332879879-29460-2-git-send-email-stefanb@linux.vnet.ibm.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [Qemu-devel] [PATCH V15 1/7] Support for TPM command line options List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Stefan Berger Cc: mst@redhat.com, qemu-devel@nongnu.org, andreas.niederl@iaik.tugraz.at On 03/27/2012 03:24 PM, Stefan Berger wrote: > This patch adds support for TPM command line options. > The command line options supported here are > > ./qemu-... -tpmdev passthrough,path=,id= > -device tpm-tis,tpmdev= > > 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 > --- > hmp-commands.hx | 2 + > hmp.c | 28 +++++++ > hmp.h | 1 + > hw/tpm_tis.h | 81 +++++++++++++++++++++ > monitor.c | 8 ++ > qapi-schema.json | 29 ++++++++ > qemu-config.c | 20 +++++ > qemu-options.hx | 30 ++++++++ > tpm.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > tpm.h | 81 +++++++++++++++++++++ > vl.c | 20 +++++ > 11 files changed, 513 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 bd35a3e..7bbb5b8 100644 > --- a/hmp-commands.hx > +++ b/hmp-commands.hx > @@ -1402,6 +1402,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 9cf2d13..bac6b5a 100644 > --- a/hmp.c > +++ b/hmp.c > @@ -546,6 +546,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->parameters ? "," : "", > + ti->parameters ? ti->parameters : ""); > + 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 8807853..4884739 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..f906390 > --- /dev/null > +++ b/hw/tpm_tis.h > @@ -0,0 +1,81 @@ > +/* > + * tpm_tis.c - QEMU's TPM TIS interface emulator > + * > + * Copyright (C) 2006,2010,2011 IBM Corporation > + * > + * Authors: > + * Stefan Berger > + * David Safford > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation, version 2 of the > + * License. Or later please. We're sticking with GPLv2 as our effective license but asking that all new code is GPLv2 or later. > + * > + * > + * Implementation of the TIS interface according to specs found at > + * http://www.trustedcomputiggroup.org > + * > + */ > +#ifndef HW_TPM_TIS_H > +#define HW_TPM_TIS_H > + > +#include "isa.h" > +#include "qemu-thread.h" This shouldn't be needed anymore. > +#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 { > + 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; I'm a bit confused here. If TPMTISState has an irq, shouldn't it be a DeviceState? > +} TPMTISState; > + > +#endif /* HW_TPM_TIS_H */ > diff --git a/monitor.c b/monitor.c > index 2ff1e0b..427992b 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 0d11d6e..4ad6d29 100644 > --- a/qapi-schema.json > +++ b/qapi-schema.json > @@ -1701,3 +1701,32 @@ > # Since: 1.1 > ## > { 'command': 'xen-save-devices-state', 'data': {'filename': '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 > +# > +# @parameters: Additional parameters of the TPM backend device This is in some sort of key=value format? Why not specify those parameters properly in the schema as optional items? > +# Since: 1.1 > +## > +{ 'type': 'TPMInfo', > + 'data': {'model': 'str', 'id': 'str', 'type': 'str', 'parameters': '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 662f571..355a9c1 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -2003,6 +2003,36 @@ ETEXI > > DEFHEADING() > > +DEFHEADING(TPM device options:) > + > +DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \ > + "-tpmdev [],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() > + > DEFHEADING(Linux/Multiboot boot specific:) > STEXI > > diff --git a/tpm.c b/tpm.c > new file mode 100644 > index 0000000..6324c1e > --- /dev/null > +++ b/tpm.c > @@ -0,0 +1,213 @@ > +/* > + * TPM configuration > + * > + * Copyright (C) 2011 IBM Corporation > + * > + * Authors: > + * Stefan Berger > + * > + * 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->parameters) { > + res->parameters = g_strdup(drv->parameters); > + } > + 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..4e871d9 > --- /dev/null > +++ b/tpm.h > @@ -0,0 +1,81 @@ > +/* > + * TPM configuration > + * > + * Copyright (C) 2011 IBM Corporation > + * > + * Authors: > + * Stefan Berger > + * > + * 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 *parameters; > + const TPMDriverOps *ops; > + > + QLIST_ENTRY(TPMBackend) list; > +} TPMBackend; > + > +/* overall state of the TPM interface */ > +typedef struct TPMState { > + ISADevice busdev; > + MemoryRegion mmio; > + > + union { > + TPMTISState tis; > + } s; > + > + uint8_t command_locty; > + TPMLocality *cmd_locty; > + > + char *backend; > + TPMBackend *be_driver; > +} TPMState; I'm a bit confused at what the relationship between TPMTISState and TPMSTate. Regards, Anthony Liguori