diff mbox

[V8,01/14] Support for TPM command line options

Message ID 20110831143616.411365666@linux.vnet.ibm.com
State New
Headers show

Commit Message

Stefan Berger Aug. 31, 2011, 2:35 p.m. UTC
This patch adds support for TPM command line options.
The command line supported here (considering the libtpms based
backend) are

./qemu-... -tpm builtin,path=<path to blockstorage file>

and

./qemu-... -tpmdev builtin,path=<path to blockstorage file>,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 ('builtin').

To show the available TPM models do:

./qemu-... -tpm model=?


In case of -tpm, 'type' (above 'builtin') and 'model' are interpreted in tpm.c.
In case of -tpmdev 'type' and 'id' are interpreted in tpm.c
Using the type parameter, the backend is chosen, i.e., 'builtin' for the
libtpms-based builtin TPM. The interpretation of the other parameters along
with determining whether enough parameters were provided is pushed into
the backend driver, which needs to implement the interface function
'create' and return a TPMDriver structure if the VM can be started or 'NULL'
if not enough or bad parameters were provided.

Since SeaBIOS will now use 128kb for ACPI tables the amount of reserved
memory for ACPI tables needs to be increased -- increasing it to 128kb.

Monitor support for 'info tpm' has been added. It for example prints the
following:

TPM devices:
  builtin: model=tpm-tis,id=tpm0

v8:
 - adjusting formatting of backend drivers output to accomodate better
   formatting of 'passthrough' backend output

v6:
 - use #idef CONFIG_TPM to surround TPM calls
 - use QLIST_FOREACH_SAFE rather than QLIST_FOREACH in tpm_cleanup
 - commented backend ops in tpm.h
 - moving to IRQ 5 (11 collided with network cards)

v5:
 - fixing typo reported by Serge Hallyn
 - Adapting code to split command line parameters supporting 
   -tpmdev ... -device tpm-tis,tpmdev=...
 - moved code out of arch_init.c|h into tpm.c|h
 - increasing reserved memory for ACPI tables to 128kb (from 64kb)
 - the backend interface has a create() function for interpreting the command
   line parameters and returning a TPMDevice structure; previoulsy
   this function was called handle_options()
 - the backend interface has a destroy() function for cleaning up after
   the create() function was called
 - added support for 'info tpm' in monitor

v4:
 - coding style fixes

v3:
 - added hw/tpm_tis.h to this patch so Qemu compiles at this stage

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
 Makefile.target |    1 
 hmp-commands.hx |    2 
 hw/pc.c         |    7 +
 hw/tpm_tis.h    |   75 +++++++++++++++
 monitor.c       |   10 ++
 qemu-config.c   |   46 +++++++++
 qemu-options.hx |   80 ++++++++++++++++
 tpm.c           |  279 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tpm.h           |  112 ++++++++++++++++++++++
 vl.c            |   18 +++
 10 files changed, 629 insertions(+), 1 deletion(-)

Comments

Michael S. Tsirkin Sept. 1, 2011, 5:14 p.m. UTC | #1
On Wed, Aug 31, 2011 at 10:35:52AM -0400, Stefan Berger wrote:
> This patch adds support for TPM command line options.
> The command line supported here (considering the libtpms based
> backend) are
> 
> ./qemu-... -tpm builtin,path=<path to blockstorage file>
> 
> and
> 
> ./qemu-... -tpmdev builtin,path=<path to blockstorage file>,id=<id>
>            -device tpm-tis,tpmdev=<id>

do we really need both?

> and
> 
> ./qemu-... -tpmdev ?
> 
> where the latter works similar to -soundhw ? and shows a list of
> available TPM backends ('builtin').
> 
> To show the available TPM models do:
> 
> ./qemu-... -tpm model=?

Can we live with -tpmdev for backend and plain device_add for frontend?
Frontend would be connected to backend using a tpmdev matching the id
of the frontend...

> In case of -tpm, 'type' (above 'builtin') and 'model' are interpreted in tpm.c.
> In case of -tpmdev 'type' and 'id' are interpreted in tpm.c
> Using the type parameter, the backend is chosen, i.e., 'builtin' for the
> libtpms-based builtin TPM. The interpretation of the other parameters along
> with determining whether enough parameters were provided is pushed into
> the backend driver, which needs to implement the interface function
> 'create' and return a TPMDriver structure if the VM can be started or 'NULL'
> if not enough or bad parameters were provided.
> 
> Since SeaBIOS will now use 128kb for ACPI tables the amount of reserved
> memory for ACPI tables needs to be increased -- increasing it to 128kb.

Increasing from which value to which?

> Monitor support for 'info tpm' has been added. It for example prints the
> following:
> 
> TPM devices:
>   builtin: model=tpm-tis,id=tpm0

This mixes frontend and backend properties.

> 
> v8:
>  - adjusting formatting of backend drivers output to accomodate better
>    formatting of 'passthrough' backend output
> 
> v6:
>  - use #idef CONFIG_TPM to surround TPM calls
>  - use QLIST_FOREACH_SAFE rather than QLIST_FOREACH in tpm_cleanup
>  - commented backend ops in tpm.h
>  - moving to IRQ 5 (11 collided with network cards)
> 
> v5:
>  - fixing typo reported by Serge Hallyn
>  - Adapting code to split command line parameters supporting 
>    -tpmdev ... -device tpm-tis,tpmdev=...
>  - moved code out of arch_init.c|h into tpm.c|h
>  - increasing reserved memory for ACPI tables to 128kb (from 64kb)
>  - the backend interface has a create() function for interpreting the command
>    line parameters and returning a TPMDevice structure; previoulsy
>    this function was called handle_options()
>  - the backend interface has a destroy() function for cleaning up after
>    the create() function was called
>  - added support for 'info tpm' in monitor
> 
> v4:
>  - coding style fixes
> 
> v3:
>  - added hw/tpm_tis.h to this patch so Qemu compiles at this stage
> 
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> 
> ---
>  Makefile.target |    1 
>  hmp-commands.hx |    2 
>  hw/pc.c         |    7 +
>  hw/tpm_tis.h    |   75 +++++++++++++++
>  monitor.c       |   10 ++
>  qemu-config.c   |   46 +++++++++
>  qemu-options.hx |   80 ++++++++++++++++
>  tpm.c           |  279 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  tpm.h           |  112 ++++++++++++++++++++++
>  vl.c            |   18 +++
>  10 files changed, 629 insertions(+), 1 deletion(-)
> 
> Index: qemu-git/qemu-options.hx
> ===================================================================
> --- qemu-git.orig/qemu-options.hx
> +++ qemu-git/qemu-options.hx
> @@ -1760,6 +1760,86 @@ ETEXI
>  
>  DEFHEADING()
>  
> +DEFHEADING(TPM device options:)
> +
> +#ifndef _WIN32
> +# ifdef CONFIG_TPM
> +DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \
> +    "" \
> +    "-tpm builtin,path=<path>[,model=<model>]\n" \
> +    "                enable a builtin TPM with state in file in path\n" \
> +    "-tpm model=?    to list available TPM device models\n" \
> +    "-tpm ?          to list available TPM backend types\n",
> +    QEMU_ARCH_I386)
> +DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
> +    "-tpmdev [builtin],id=str[,option][,option][,...]\n",
> +    QEMU_ARCH_I386)
> +# endif
> +#endif
> +STEXI
> +
> +The general form of a TPM device option is:
> +@table @option
> +
> +@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
> +@findex -tpmdev
> +Backend type must be:
> +@option{builtin}.
> +
> +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
> +
> +@item -tpmdev builtin ,id=@var{id}, path=@var{path}
> +
> +Creates an instance of the built-in TPM.
> +
> +@option{path} specifies the path to the QCoW2 image that will store
> +the TPM's persistent data. @option{path} is required.
> +
> +To create a built-in TPM use the following two options:
> +@example
> +-tpmdev builtin,id=tpm0,path=<path_to_qcow2> -device tpm-tis,tpmdev=tpm0
> +@end example
> +Not that the @code{-tpmdev} id is @code{tpm0} and is referenced by
> +@code{tpmdev=tpm0} in the device option.
> +
> +@end table
> +
> +The short form of a TPM device option is:
> +@table @option
> +
> +@item -tpm @var{backend-type}, path=@var{path} [,model=@var{model}]
> +@findex -tpm
> +
> +@option{model} specifies the device model. The default device model is a
> +@code{tpm-tis} device model. @code{model} is optional.
> +
> +Use ? to print all available TPM models.
> +@example
> +qemu -tpm model=?
> +@end example
> +
> +The other options have the same meaning as explained above.
> +
> +To create a built-in TPM use the following option:
> +@example
> +-tpm builtin, path=<path_to_qcow2>
> +@end example
> +
> +@end table
> +
> +ETEXI
> +
> +
> +DEFHEADING()
> +
>  DEFHEADING(Linux/Multiboot boot specific:)
>  STEXI
>  
> Index: qemu-git/vl.c
> ===================================================================
> --- qemu-git.orig/vl.c
> +++ qemu-git/vl.c
> @@ -137,6 +137,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"
> @@ -2498,6 +2499,14 @@ int main(int argc, char **argv, char **e
>                  ram_size = value;
>                  break;
>              }
> +#ifdef CONFIG_TPM
> +            case QEMU_OPTION_tpm:
> +                tpm_config_parse(qemu_find_opts("tpm"), optarg);
> +                break;
> +            case QEMU_OPTION_tpmdev:
> +                tpm_config_parse(qemu_find_opts("tpmdev"), optarg);
> +                break;
> +#endif
>              case QEMU_OPTION_mempath:
>                  mem_path = optarg;
>                  break;
> @@ -3149,6 +3158,12 @@ int main(int argc, char **argv, char **e
>          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);
> @@ -3394,6 +3409,9 @@ int main(int argc, char **argv, char **e
>      quit_timers();
>      net_cleanup();
>      res_free();
> +#ifdef CONFIG_TPM
> +    tpm_cleanup();
> +#endif
>  
>      return 0;
>  }
> Index: qemu-git/qemu-config.c
> ===================================================================
> --- qemu-git.orig/qemu-config.c
> +++ qemu-git/qemu-config.c
> @@ -507,6 +507,50 @@ 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 qemu_tpm_opts = {
> +    .name = "tpm",
> +    .implied_opt_name = "type",
> +    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
> +    .desc = {
> +        {
> +            .name = "type",
> +            .type = QEMU_OPT_STRING,
> +            .help = "Type of TPM backend",
> +        },
> +        {
> +            .name = "model",
> +            .type = QEMU_OPT_STRING,
> +            .help = "Model of TPM frontend",
> +        },
> +        {
> +            .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,
> @@ -523,6 +567,8 @@ static QemuOptsList *vm_config_groups[32
>      &qemu_option_rom_opts,
>      &qemu_machine_opts,
>      &qemu_boot_opts,
> +    &qemu_tpmdev_opts,
> +    &qemu_tpm_opts,
>      NULL,
>  };
>  
> Index: qemu-git/hw/tpm_tis.h
> ===================================================================
> --- /dev/null
> +++ qemu-git/hw/tpm_tis.h
> @@ -0,0 +1,75 @@
> +/*
> + * tpm_tis.h - include file for tpm_tis.c
> + *
> + * Copyright (C) 2006,2010,2011 IBM Corporation
> + *
> + * Author: Stefan Berger <stefanb@us.ibm.com>
> + *         David Safford <safford@us.ibm.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation, version 2 of the
> + * License.
> + */
> +#ifndef _HW_TPM_TIS_H
> +#define _HW_TPM_TIS_H
> +
> +#include "isa.h"
> +#include "block_int.h"
> +#include "qemu-thread.h"
> +
> +#include <stdint.h>
> +
> +#define TIS_ADDR_BASE       0xFED40000
> +
> +#define NUM_LOCALITIES      5     /* per spec */
> +#define NO_LOCALITY         0xff

Please use consistent prefixes to avoid namespace
pollution. E.g. tpm_tis_ for stuff in tpm_tis.h, etc.


> +
> +#define IS_VALID_LOCTY(x)   ((x) < NUM_LOCALITIES)
> +
> +
> +#define TPM_TIS_IRQ         5
> +
> +#define TIS_TPM_BUFFER_MAX  4096
> +
> +
> +typedef struct TPMSizedBuffer {
> +    uint32_t size;
> +    uint8_t  *buffer;
> +} TPMSizedBuffer;
> +
> +
> +enum tis_state {
> +    STATE_IDLE = 0,
> +    STATE_READY,
> +    STATE_COMPLETION,
> +    STATE_EXECUTION,
> +    STATE_RECEPTION,
> +};
> +
> +
> +void tis_reset_for_snapshot_resume(TPMState *s);
> +
> +
> +/* utility functions */
> +
> +static inline uint16_t tis_get_size_from_buffer(const TPMSizedBuffer *sb)
> +{
> +    return (sb->buffer[4] << 8) + sb->buffer[5];
> +}
> +
> +static inline void dumpBuffer(FILE *stream,
> +                              unsigned char *buffer, unsigned int len)
> +{
> +    int i;
> +
> +    for (i = 0; i < len; i++) {
> +        if (i && !(i % 16)) {
> +            fprintf(stream, "\n");
> +        }
> +        fprintf(stream, "%.2X ", buffer[i]);
> +    }
> +    fprintf(stream, "\n");
> +}
> +
> +#endif /* _HW_TPM_TIS_H */

> Index: qemu-git/tpm.c
> ===================================================================
> --- /dev/null
> +++ qemu-git/tpm.c
> @@ -0,0 +1,279 @@
> +/*
> + * TPM configuraion
> + *
> + * Copyright (C) 2011 IBM Corporation
> + * Copyright (C) 2011 Stefan Berger
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See
> + * the COPYING file in the top-level directory.
> + *
> + * Based on net.c
> + */
> +#include "config.h"
> +
> +#include "tpm.h"
> +#include "monitor.h"
> +#include "qerror.h"
> +
> +
> +#ifdef CONFIG_TPM
> +
> +#if defined(TARGET_I386) || defined(TARGET_X86_64)
> +
> +static const TPMDriverOps *bes[] = {
> +    NULL,
> +};
> +
> +
> +static const char *tpm_models[] = {
> +    TPM_DEFAULT_DEVICE_MODEL,
> +    NULL,
> +};
> +
> +
> +static QLIST_HEAD(, TPMBackend) tpm_backends =
> +    QLIST_HEAD_INITIALIZER(tpm_backends);
> +
> +
> +const TPMDriverOps *tpm_get_backend_driver(const char *id)
> +{
> +    int i;
> +
> +    for (i = 0; bes[i] != NULL; i++) {
> +        if (!strcmp(bes[i]->id, id)) {
> +            break;
> +        }
> +    }
> +
> +    return bes[i];
> +}
> +
> +
> +static void tpm_display_models(FILE *out)
> +{
> +    int i;
> +
> +    fprintf(stderr, "qemu: Supported TPM models: ");
> +    for (i = 0 ; tpm_models[i]; i++) {
> +        fprintf(stderr, "%s%c", tpm_models[i], tpm_models[i+1] ? ',' : '\n');
> +    }
> +}
> +
> +
> +static int tpm_check_model(const char *model)
> +{
> +    int i;
> +
> +    for (i = 0 ; tpm_models[i]; i++) {
> +        if (strcmp(tpm_models[i], model) == 0) {
> +            return 1;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +
> +void tpm_display_backend_drivers(FILE *out)
> +{
> +    int i;
> +
> +    fprintf(out, "Supported TPM types (choose only one):\n");
> +
> +    for (i = 0; bes[i] != NULL; i++) {
> +        fprintf(out, "%12s   %s",
> +                bes[i]->id, bes[i]->desc());
> +        fprintf(out, "\n");
> +    }
> +    fprintf(out, "\n");
> +}
> +
> +
> +TPMBackend *qemu_find_tpm(const char *id)
> +{
> +    TPMBackend *drv;
> +
> +    QLIST_FOREACH(drv, &tpm_backends, list) {
> +        if (!strcmp(drv->id, id)) {
> +            return drv;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +
> +void do_info_tpm(Monitor *mon)
> +{
> +    TPMBackend *drv;
> +    const char *model;
> +
> +    monitor_printf(mon, "TPM devices:\n");
> +
> +    QLIST_FOREACH(drv, &tpm_backends, list) {
> +        model = drv->model ? drv->model : TPM_DEFAULT_DEVICE_MODEL;
> +        monitor_printf(mon, "  %s: model=%s,id=%s\n",
> +                       drv->ops->id, model, drv->id);
> +    }
> +}
> +
> +/*
> + * Create those TPMs that were created with -tpm rather than -tpmdev.
> + * The ones created with -tpm have a 'model' name.
> + */
> +void qemu_create_tpm(void)
> +{
> +    TPMBackend *drv;
> +
> +    QLIST_FOREACH(drv, &tpm_backends, list) {
> +        if (drv->model) {
> +            if (strcmp(drv->model, TPM_DEFAULT_DEVICE_MODEL) == 0) {
> +                isa_create_simple(drv->model);
> +            }
> +        }
> +    }
> +}
> +
> +
> +static int configure_tpm(QemuOpts *opts, int is_tpmdev)
> +{
> +    const char *value;
> +    const char *id = TPM_DEFAULT_DEVICE_ID;
> +    const char *model =  NULL;
> +    const TPMDriverOps *be;
> +    TPMBackend *drv;
> +
> +    if (!QLIST_EMPTY(&tpm_backends)) {
> +        fprintf(stderr, "Only one TPM is allowed.\n");
> +        return 1;
> +    }
> +
> +    if (is_tpmdev) {
> +        id = qemu_opts_id(opts);
> +        if (id == NULL) {
> +            qerror_report(QERR_MISSING_PARAMETER, "id");
> +            return 1;
> +        }
> +    } else {
> +        model = qemu_opt_get(opts, "model");
> +        if (model) {
> +            if (strcmp(model, "?") == 0) {
> +                tpm_display_models(stdout);
> +                return 1;
> +            }
> +            if (!tpm_check_model(model)) {
> +                qerror_report(QERR_INVALID_PARAMETER_VALUE, "model",
> +                              "a tpm model");
> +                tpm_display_models(stderr);
> +                return 1;
> +            }
> +        } else {
> +            model = TPM_DEFAULT_DEVICE_MODEL;
> +        }
> +    }
> +
> +    value = qemu_opt_get(opts, "type");
> +    if (!value) {
> +        qerror_report(QERR_MISSING_PARAMETER, "type");
> +        tpm_display_backend_drivers(stderr);
> +        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(stderr);
> +        return 1;
> +    }
> +
> +    assert((is_tpmdev && model == NULL) || (!is_tpmdev && model != NULL));

Why isn't this using qdev for parameter passing?

> +
> +    drv = be->create(opts, id, model);
> +    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, 1);
> +}
> +
> +
> +static int tpm_init_tpm(QemuOpts *opts, void *dummy)
> +{
> +    return configure_tpm(opts, 0);
> +}
> +
> +
> +int tpm_init(void)
> +{
> +    if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
> +                          tpm_init_tpmdev, NULL, 1) != 0) {
> +        return -1;
> +    }
> +
> +    if (qemu_opts_foreach(qemu_find_opts("tpm"),
> +                          tpm_init_tpm, NULL, 1) != 0) {
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +
> +void tpm_cleanup(void)
> +{
> +    TPMBackend *drv, *next;
> +
> +    QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
> +        QLIST_REMOVE(drv, list);
> +        drv->ops->destroy(drv);
> +    }
> +}
> +
> +
> +void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
> +{
> +    QemuOpts *opts;
> +
> +    if (strcmp("none", optarg) != 0) {
> +        if (*optarg == '?') {
> +            tpm_display_backend_drivers(stdout);
> +            exit(0);
> +        }
> +        opts = qemu_opts_parse(opts_list, optarg, 1);
> +        if (!opts) {
> +            exit(1);
> +        }
> +    }
> +}
> +
> +# else /* TARGET_I386 || TARGET_X86_64 */
> +
> +void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
> +{
> +}
> +
> +int tpm_init(void)
> +{
> +    return 0;
> +}
> +
> +void tpm_cleanup(void)
> +{
> +}
> +
> +void do_info_tpm(Monitor *mon)
> +{
> +    monitor_printf(mon, "TPM support: not compiled\n");
> +}
> +
> +# endif
> +#endif /* CONFIG_TPM */
> Index: qemu-git/tpm.h
> ===================================================================
> --- /dev/null
> +++ qemu-git/tpm.h
> @@ -0,0 +1,112 @@
> +#ifndef _HW_TPM_CONFIG_H
> +#define _HW_TPM_CONFIG_H
> +
> +struct TPMState;
> +typedef struct TPMState TPMState;
> +
> +#include "hw/tpm_tis.h"
> +
> +struct TPMDriverOps;
> +typedef struct TPMDriverOps TPMDriverOps;
> +
> +typedef struct TPMBackend {
> +    char *id;
> +    char *model;
> +    TPMDriverOps *ops;
> +
> +    QLIST_ENTRY(TPMBackend) list;
> +} TPMBackend;
> +
> +
> +/* locality data  -- all fields are persisted */
> +typedef struct TPMLocality {
> +    enum tis_state state;
> +    uint8_t access;
> +    uint8_t sts;
> +    uint32_t inte;
> +    uint32_t ints;
> +
> +    uint16_t w_offset;
> +    uint16_t r_offset;
> +    TPMSizedBuffer w_buffer;
> +    TPMSizedBuffer r_buffer;
> +} TPMLocality;
> +
> +
> +/* overall state of the TPM interface */
> +struct TPMState {
> +    ISADevice busdev;
> +
> +    uint32_t offset;
> +    uint8_t buf[TIS_TPM_BUFFER_MAX];
> +
> +    uint8_t active_locty;
> +    uint8_t aborting_locty;
> +    uint8_t next_locty;
> +
> +    uint8_t command_locty;
> +    TPMLocality loc[NUM_LOCALITIES];
> +
> +    qemu_irq irq;
> +    uint32_t irq_num;
> +
> +    QemuMutex state_lock;
> +    QemuCond  from_tpm_cond;
> +    QemuCond  to_tpm_cond;
> +    bool      to_tpm_execute;
> +
> +    bool      tpm_initialized;
> +
> +    char *backend;
> +    TPMBackend *be_driver;
> +};
> +
> +
> +typedef void (TPMRecvDataCB)(TPMState *s, uint8_t locty);
> +
> +struct TPMDriverOps {
> +    const char *id;
> +    /* get a descriptive text of the backend to display to the user */
> +    const char *(*desc)(void);
> +
> +    void (*job_for_main_thread)(void *);
> +
> +    TPMBackend *(*create)(QemuOpts *, const char *id, const char *model);
> +    void (*destroy)(TPMBackend *drv);
> +
> +    /* initialize the backend */
> +    int (*init)(TPMState *s, TPMRecvDataCB *datacb);
> +    /* start up the TPM on the backend early if possible */
> +    int (*early_startup_tpm)(void);
> +    /* start up the TPM on the backend late if necessary */
> +    int (*late_startup_tpm)(void);
> +    /* returns true if nothing will ever answer TPM requests */
> +    bool (*had_startup_error)(void);
> +
> +    size_t (*realloc_buffer)(TPMSizedBuffer *sb);
> +
> +    void (*reset)(void);
> +
> +    /* called to trigger the saving of the volatile data;
> +       called before the VM suspends / migrates */
> +    int (*save_volatile_data)(void);
> +    /* triggers the loading of the volatile data */
> +    int (*load_volatile_data)(TPMState *s);
> +
> +    bool (*get_tpm_established_flag)(void);
> +};
> +
> +#define TPM_DEFAULT_DEVICE_ID    "tpm0"
> +#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
> +
> +void tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
> +int tpm_init(void);
> +void tpm_cleanup(void);
> +void qemu_create_tpm(void);
> +TPMBackend *qemu_find_tpm(const char *id);
> +void do_info_tpm(Monitor *mon);
> +void tpm_display_backend_drivers(FILE *out);
> +const TPMDriverOps *tpm_get_backend_driver(const char *id);
> +
> +
> +#endif /* _HW_TPM_CONFIG_H */
> Index: qemu-git/hw/pc.c
> ===================================================================
> --- qemu-git.orig/hw/pc.c
> +++ qemu-git/hw/pc.c
> @@ -43,6 +43,7 @@
>  #include "ui/qemu-spice.h"
>  #include "memory.h"
>  #include "exec-memory.h"
> +#include "tpm.h"
>  
>  /* output Bochs bios info messages */
>  //#define DEBUG_BIOS
> @@ -62,7 +63,7 @@
>  #define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
>  
>  /* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables.  */
> -#define ACPI_DATA_SIZE       0x10000
> +#define ACPI_DATA_SIZE       0x20000
>  #define BIOS_CFG_IOPORT 0x510
>  #define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
>  #define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
> @@ -1183,6 +1184,10 @@ void pc_basic_device_init(qemu_irq *isa_
>          fd[i] = drive_get(IF_FLOPPY, 0, i);
>      }
>      fdctrl_init_isa(fd);
> +
> +#ifdef CONFIG_TPM
> +    qemu_create_tpm();
> +#endif
>  }
>  
>  void pc_pci_device_init(PCIBus *pci_bus)
> Index: qemu-git/monitor.c
> ===================================================================
> --- qemu-git.orig/monitor.c
> +++ qemu-git/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"
> @@ -3151,6 +3152,15 @@ static const mon_cmd_t info_cmds[] = {
>          .mhandler.info = do_info_trace_events,
>      },
>  #endif
> +#if defined(CONFIG_TPM)
> +    {
> +        .name       = "tpm",
> +        .args_type  = "",
> +        .params     = "",
> +        .help       = "show the TPM devices",
> +        .mhandler.info = do_info_tpm,
> +    },
> +#endif
>      {
>          .name       = NULL,
>      },
> Index: qemu-git/hmp-commands.hx
> ===================================================================
> --- qemu-git.orig/hmp-commands.hx
> +++ qemu-git/hmp-commands.hx
> @@ -1351,6 +1351,8 @@ show device tree
>  show qdev device model list
>  @item info roms
>  show roms
> +@item info tpm
> +show the TPM devices
>  @end table
>  ETEXI
>  
> Index: qemu-git/Makefile.target
> ===================================================================
> --- qemu-git.orig/Makefile.target
> +++ qemu-git/Makefile.target
> @@ -200,6 +200,7 @@ obj-$(CONFIG_KVM) += kvm.o kvm-all.o
>  obj-$(CONFIG_NO_KVM) += kvm-stub.o
>  obj-y += memory.o
>  LIBS+=-lz
> +obj-y += tpm.o
>  
>  QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
>  QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
>
Michael S. Tsirkin Sept. 1, 2011, 6:14 p.m. UTC | #2
On Wed, Aug 31, 2011 at 10:35:52AM -0400, Stefan Berger wrote:
> This patch adds support for TPM command line options.
> The command line supported here (considering the libtpms based
> backend) are
> 
> ./qemu-... -tpm builtin,path=<path to blockstorage file>
> 
> and
> 
> ./qemu-... -tpmdev builtin,path=<path to blockstorage file>,id=<id>
>            -device tpm-tis,tpmdev=<id>
> 

builtin is a weird name in fact. Rename to libtpms?
Stefan Berger Sept. 2, 2011, 1:01 a.m. UTC | #3
On 09/01/2011 01:14 PM, Michael S. Tsirkin wrote:
> On Wed, Aug 31, 2011 at 10:35:52AM -0400, Stefan Berger wrote:
>> This patch adds support for TPM command line options.
>> The command line supported here (considering the libtpms based
>> backend) are
>>
>> ./qemu-... -tpm builtin,path=<path to blockstorage file>
>>
>> and
>>
>> ./qemu-... -tpmdev builtin,path=<path to blockstorage file>,id=<id>
>>             -device tpm-tis,tpmdev=<id>
> do we really need both?
I had chatted with Anthony about this. I am following the existing 
pattern is use for example for -netdev / -net.

>> and
>>
>> ./qemu-... -tpmdev ?
>>
>> where the latter works similar to -soundhw ? and shows a list of
>> available TPM backends ('builtin').
>>
>> To show the available TPM models do:
>>
>> ./qemu-... -tpm model=?
> Can we live with -tpmdev for backend and plain device_add for frontend?
Can you give a more specific example? Is device_add a function call or a 
command line parameter in this context?
> Frontend would be connected to backend using a tpmdev matching the id
> of the frontend...

qemu-... -tpmdev builtin,path=<path to blockstorage file>,id=<id>
          -device tpm-tis,tpmdev=<id>


Isn't that what I am doing?

>> In case of -tpm, 'type' (above 'builtin') and 'model' are interpreted in tpm.c.
>> In case of -tpmdev 'type' and 'id' are interpreted in tpm.c
>> Using the type parameter, the backend is chosen, i.e., 'builtin' for the
>> libtpms-based builtin TPM. The interpretation of the other parameters along
>> with determining whether enough parameters were provided is pushed into
>> the backend driver, which needs to implement the interface function
>> 'create' and return a TPMDriver structure if the VM can be started or 'NULL'
>> if not enough or bad parameters were provided.
>>
>> Since SeaBIOS will now use 128kb for ACPI tables the amount of reserved
>> memory for ACPI tables needs to be increased -- increasing it to 128kb.
> Increasing from which value to which?
 From 64kb to 128kb.
>> Monitor support for 'info tpm' has been added. It for example prints the
>> following:
>>
>> TPM devices:
>>    builtin: model=tpm-tis,id=tpm0
> This mixes frontend and backend properties.
>
There's currently only one frontend 'model' and that's the 'tpm-tis'. In 
case someone would want to write a virtio equivalent it would show the 
that the 'builtin' backend is connected to the 'virtio' frontend model. 
If above is not correct, how should it look like?
>> v8:
>>   - adjusting formatting of backend drivers output to accomodate better
>>     formatting of 'passthrough' backend output
>>
>> v6:
>>   - use #idef CONFIG_TPM to surround TPM calls
>>   - use QLIST_FOREACH_SAFE rather than QLIST_FOREACH in tpm_cleanup
>>   - commented backend ops in tpm.h
>>   - moving to IRQ 5 (11 collided with network cards)
>>
>> v5:
>>   - fixing typo reported by Serge Hallyn
>>   - Adapting code to split command line parameters supporting
>>     -tpmdev ... -device tpm-tis,tpmdev=...
>>   - moved code out of arch_init.c|h into tpm.c|h
>>   - increasing reserved memory for ACPI tables to 128kb (from 64kb)
>>   - the backend interface has a create() function for interpreting the command
>>     line parameters and returning a TPMDevice structure; previoulsy
>>     this function was called handle_options()
>>   - the backend interface has a destroy() function for cleaning up after
>>     the create() function was called
>>   - added support for 'info tpm' in monitor
>>
>> v4:
>>   - coding style fixes
>>
>> v3:
>>   - added hw/tpm_tis.h to this patch so Qemu compiles at this stage
>>
>> Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
>>
>> ---
>>   Makefile.target |    1
>>   hmp-commands.hx |    2
>>   hw/pc.c         |    7 +
>>   hw/tpm_tis.h    |   75 +++++++++++++++
>>   monitor.c       |   10 ++
>>   qemu-config.c   |   46 +++++++++
>>   qemu-options.hx |   80 ++++++++++++++++
>>   tpm.c           |  279 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   tpm.h           |  112 ++++++++++++++++++++++
>>   vl.c            |   18 +++
>>   10 files changed, 629 insertions(+), 1 deletion(-)
>>
>> Index: qemu-git/qemu-options.hx
>> ===================================================================
>> --- qemu-git.orig/qemu-options.hx
>> +++ qemu-git/qemu-options.hx
>> @@ -1760,6 +1760,86 @@ ETEXI
>>
>>   DEFHEADING()
>>
>> +DEFHEADING(TPM device options:)
>> +
>> +#ifndef _WIN32
>> +# ifdef CONFIG_TPM
>> +DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \
>> +    "" \
>> +    "-tpm builtin,path=<path>[,model=<model>]\n" \
>> +    "                enable a builtin TPM with state in file in path\n" \
>> +    "-tpm model=?    to list available TPM device models\n" \
>> +    "-tpm ?          to list available TPM backend types\n",
>> +    QEMU_ARCH_I386)
>> +DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
>> +    "-tpmdev [builtin],id=str[,option][,option][,...]\n",
>> +    QEMU_ARCH_I386)
>> +# endif
>> +#endif
>> +STEXI
>> +
>> +The general form of a TPM device option is:
>> +@table @option
>> +
>> +@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
>> +@findex -tpmdev
>> +Backend type must be:
>> +@option{builtin}.
>> +
>> +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
>> +
>> +@item -tpmdev builtin ,id=@var{id}, path=@var{path}
>> +
>> +Creates an instance of the built-in TPM.
>> +
>> +@option{path} specifies the path to the QCoW2 image that will store
>> +the TPM's persistent data. @option{path} is required.
>> +
>> +To create a built-in TPM use the following two options:
>> +@example
>> +-tpmdev builtin,id=tpm0,path=<path_to_qcow2>  -device tpm-tis,tpmdev=tpm0
>> +@end example
>> +Not that the @code{-tpmdev} id is @code{tpm0} and is referenced by
>> +@code{tpmdev=tpm0} in the device option.
>> +
>> +@end table
>> +
>> +The short form of a TPM device option is:
>> +@table @option
>> +
>> +@item -tpm @var{backend-type}, path=@var{path} [,model=@var{model}]
>> +@findex -tpm
>> +
>> +@option{model} specifies the device model. The default device model is a
>> +@code{tpm-tis} device model. @code{model} is optional.
>> +
>> +Use ? to print all available TPM models.
>> +@example
>> +qemu -tpm model=?
>> +@end example
>> +
>> +The other options have the same meaning as explained above.
>> +
>> +To create a built-in TPM use the following option:
>> +@example
>> +-tpm builtin, path=<path_to_qcow2>
>> +@end example
>> +
>> +@end table
>> +
>> +ETEXI
>> +
>> +
>> +DEFHEADING()
>> +
>>   DEFHEADING(Linux/Multiboot boot specific:)
>>   STEXI
>>
>> Index: qemu-git/vl.c
>> ===================================================================
>> --- qemu-git.orig/vl.c
>> +++ qemu-git/vl.c
>> @@ -137,6 +137,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"
>> @@ -2498,6 +2499,14 @@ int main(int argc, char **argv, char **e
>>                   ram_size = value;
>>                   break;
>>               }
>> +#ifdef CONFIG_TPM
>> +            case QEMU_OPTION_tpm:
>> +                tpm_config_parse(qemu_find_opts("tpm"), optarg);
>> +                break;
>> +            case QEMU_OPTION_tpmdev:
>> +                tpm_config_parse(qemu_find_opts("tpmdev"), optarg);
>> +                break;
>> +#endif
>>               case QEMU_OPTION_mempath:
>>                   mem_path = optarg;
>>                   break;
>> @@ -3149,6 +3158,12 @@ int main(int argc, char **argv, char **e
>>           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);
>> @@ -3394,6 +3409,9 @@ int main(int argc, char **argv, char **e
>>       quit_timers();
>>       net_cleanup();
>>       res_free();
>> +#ifdef CONFIG_TPM
>> +    tpm_cleanup();
>> +#endif
>>
>>       return 0;
>>   }
>> Index: qemu-git/qemu-config.c
>> ===================================================================
>> --- qemu-git.orig/qemu-config.c
>> +++ qemu-git/qemu-config.c
>> @@ -507,6 +507,50 @@ 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 qemu_tpm_opts = {
>> +    .name = "tpm",
>> +    .implied_opt_name = "type",
>> +    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
>> +    .desc = {
>> +        {
>> +            .name = "type",
>> +            .type = QEMU_OPT_STRING,
>> +            .help = "Type of TPM backend",
>> +        },
>> +        {
>> +            .name = "model",
>> +            .type = QEMU_OPT_STRING,
>> +            .help = "Model of TPM frontend",
>> +        },
>> +        {
>> +            .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,
>> @@ -523,6 +567,8 @@ static QemuOptsList *vm_config_groups[32
>>       &qemu_option_rom_opts,
>>       &qemu_machine_opts,
>>       &qemu_boot_opts,
>> +&qemu_tpmdev_opts,
>> +&qemu_tpm_opts,
>>       NULL,
>>   };
>>
>> Index: qemu-git/hw/tpm_tis.h
>> ===================================================================
>> --- /dev/null
>> +++ qemu-git/hw/tpm_tis.h
>> @@ -0,0 +1,75 @@
>> +/*
>> + * tpm_tis.h - include file for tpm_tis.c
>> + *
>> + * Copyright (C) 2006,2010,2011 IBM Corporation
>> + *
>> + * Author: Stefan Berger<stefanb@us.ibm.com>
>> + *         David Safford<safford@us.ibm.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation, version 2 of the
>> + * License.
>> + */
>> +#ifndef _HW_TPM_TIS_H
>> +#define _HW_TPM_TIS_H
>> +
>> +#include "isa.h"
>> +#include "block_int.h"
>> +#include "qemu-thread.h"
>> +
>> +#include<stdint.h>
>> +
>> +#define TIS_ADDR_BASE       0xFED40000
>> +
>> +#define NUM_LOCALITIES      5     /* per spec */
>> +#define NO_LOCALITY         0xff
> Please use consistent prefixes to avoid namespace
> pollution. E.g. tpm_tis_ for stuff in tpm_tis.h, etc.
>
>
Ok. I'll change the functions. Also the #define's ?

[...]
>> +
>> +static int configure_tpm(QemuOpts *opts, int is_tpmdev)
>> +{
>> +    const char *value;
>> +    const char *id = TPM_DEFAULT_DEVICE_ID;
>> +    const char *model =  NULL;
>> +    const TPMDriverOps *be;
>> +    TPMBackend *drv;
>> +
>> +    if (!QLIST_EMPTY(&tpm_backends)) {
>> +        fprintf(stderr, "Only one TPM is allowed.\n");
>> +        return 1;
>> +    }
>> +
>> +    if (is_tpmdev) {
>> +        id = qemu_opts_id(opts);
>> +        if (id == NULL) {
>> +            qerror_report(QERR_MISSING_PARAMETER, "id");
>> +            return 1;
>> +        }
>> +    } else {
>> +        model = qemu_opt_get(opts, "model");
>> +        if (model) {
>> +            if (strcmp(model, "?") == 0) {
>> +                tpm_display_models(stdout);
>> +                return 1;
>> +            }
>> +            if (!tpm_check_model(model)) {
>> +                qerror_report(QERR_INVALID_PARAMETER_VALUE, "model",
>> +                              "a tpm model");
>> +                tpm_display_models(stderr);
>> +                return 1;
>> +            }
>> +        } else {
>> +            model = TPM_DEFAULT_DEVICE_MODEL;
>> +        }
>> +    }
>> +
>> +    value = qemu_opt_get(opts, "type");
>> +    if (!value) {
>> +        qerror_report(QERR_MISSING_PARAMETER, "type");
>> +        tpm_display_backend_drivers(stderr);
>> +        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(stderr);
>> +        return 1;
>> +    }
>> +
>> +    assert((is_tpmdev&&  model == NULL) || (!is_tpmdev&&  model != NULL));
> Why isn't this using qdev for parameter passing?
>
Can you point me to a device that is using qdev for parameter passing. 
Also this part is very similar to how the networking works (net.c).

    Stefan
Stefan Berger Sept. 2, 2011, 1:02 a.m. UTC | #4
On 09/01/2011 02:14 PM, Michael S. Tsirkin wrote:
> On Wed, Aug 31, 2011 at 10:35:52AM -0400, Stefan Berger wrote:
>> This patch adds support for TPM command line options.
>> The command line supported here (considering the libtpms based
>> backend) are
>>
>> ./qemu-... -tpm builtin,path=<path to blockstorage file>
>>
>> and
>>
>> ./qemu-... -tpmdev builtin,path=<path to blockstorage file>,id=<id>
>>             -device tpm-tis,tpmdev=<id>
>>
> builtin is a weird name in fact. Rename to libtpms?
Anyone have a particular opinion? If I renamed it, should I rename the 
files as well along with the function names that now all start with 
tpm_builtin_?

    Stefan
Michael S. Tsirkin Sept. 4, 2011, 4:29 p.m. UTC | #5
On Thu, Sep 01, 2011 at 09:01:32PM -0400, Stefan Berger wrote:
> On 09/01/2011 01:14 PM, Michael S. Tsirkin wrote:
> >On Wed, Aug 31, 2011 at 10:35:52AM -0400, Stefan Berger wrote:
> >>This patch adds support for TPM command line options.
> >>The command line supported here (considering the libtpms based
> >>backend) are
> >>
> >>./qemu-... -tpm builtin,path=<path to blockstorage file>
> >>
> >>and
> >>
> >>./qemu-... -tpmdev builtin,path=<path to blockstorage file>,id=<id>
> >>            -device tpm-tis,tpmdev=<id>
> >do we really need both?
> I had chatted with Anthony about this. I am following the existing
> pattern is use for example for -netdev / -net.

Must be some kind of misunderstanding.
Please do not add multiple ways to do one thing.
We only support -net for legacy reasons.
Michael S. Tsirkin Sept. 4, 2011, 4:50 p.m. UTC | #6
On Thu, Sep 01, 2011 at 09:01:32PM -0400, Stefan Berger wrote:
> >>Monitor support for 'info tpm' has been added. It for example prints the
> >>following:
> >>
> >>TPM devices:
> >>   builtin: model=tpm-tis,id=tpm0
> >This mixes frontend and backend properties.
> >
> There's currently only one frontend 'model' and that's the
> 'tpm-tis'. In case someone would want to write a virtio equivalent
> it would show the that the 'builtin' backend is connected to the
> 'virtio' frontend model. If above is not correct, how should it look
> like?

E.g. for net: we list backends and frontends each with its
properties.

  virtio-net-pci.0: type=nic,model=virtio-net-pci,macaddr=52:54:00:12:34:56
   \ foo: type=tap,ifname=msttap0,script=/home/mst/ifup,downscript=no



> >>+
> >>+    value = qemu_opt_get(opts, "type");
> >>+    if (!value) {
> >>+        qerror_report(QERR_MISSING_PARAMETER, "type");
> >>+        tpm_display_backend_drivers(stderr);
> >>+        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(stderr);
> >>+        return 1;
> >>+    }
> >>+
> >>+    assert((is_tpmdev&&  model == NULL) || (!is_tpmdev&&  model != NULL));
> >Why isn't this using qdev for parameter passing?
> >
> Can you point me to a device that is using qdev for parameter
> passing.


virtio-pci devices: block, net - have a huge number of these.

I'm talking about frontend primarily.

> Also this part is very similar to how the networking works
> (net.c).
> 
>    Stefan

A large part of that is legacy processing.
diff mbox

Patch

Index: qemu-git/qemu-options.hx
===================================================================
--- qemu-git.orig/qemu-options.hx
+++ qemu-git/qemu-options.hx
@@ -1760,6 +1760,86 @@  ETEXI
 
 DEFHEADING()
 
+DEFHEADING(TPM device options:)
+
+#ifndef _WIN32
+# ifdef CONFIG_TPM
+DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \
+    "" \
+    "-tpm builtin,path=<path>[,model=<model>]\n" \
+    "                enable a builtin TPM with state in file in path\n" \
+    "-tpm model=?    to list available TPM device models\n" \
+    "-tpm ?          to list available TPM backend types\n",
+    QEMU_ARCH_I386)
+DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
+    "-tpmdev [builtin],id=str[,option][,option][,...]\n",
+    QEMU_ARCH_I386)
+# endif
+#endif
+STEXI
+
+The general form of a TPM device option is:
+@table @option
+
+@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
+@findex -tpmdev
+Backend type must be:
+@option{builtin}.
+
+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
+
+@item -tpmdev builtin ,id=@var{id}, path=@var{path}
+
+Creates an instance of the built-in TPM.
+
+@option{path} specifies the path to the QCoW2 image that will store
+the TPM's persistent data. @option{path} is required.
+
+To create a built-in TPM use the following two options:
+@example
+-tpmdev builtin,id=tpm0,path=<path_to_qcow2> -device tpm-tis,tpmdev=tpm0
+@end example
+Not that the @code{-tpmdev} id is @code{tpm0} and is referenced by
+@code{tpmdev=tpm0} in the device option.
+
+@end table
+
+The short form of a TPM device option is:
+@table @option
+
+@item -tpm @var{backend-type}, path=@var{path} [,model=@var{model}]
+@findex -tpm
+
+@option{model} specifies the device model. The default device model is a
+@code{tpm-tis} device model. @code{model} is optional.
+
+Use ? to print all available TPM models.
+@example
+qemu -tpm model=?
+@end example
+
+The other options have the same meaning as explained above.
+
+To create a built-in TPM use the following option:
+@example
+-tpm builtin, path=<path_to_qcow2>
+@end example
+
+@end table
+
+ETEXI
+
+
+DEFHEADING()
+
 DEFHEADING(Linux/Multiboot boot specific:)
 STEXI
 
Index: qemu-git/vl.c
===================================================================
--- qemu-git.orig/vl.c
+++ qemu-git/vl.c
@@ -137,6 +137,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"
@@ -2498,6 +2499,14 @@  int main(int argc, char **argv, char **e
                 ram_size = value;
                 break;
             }
+#ifdef CONFIG_TPM
+            case QEMU_OPTION_tpm:
+                tpm_config_parse(qemu_find_opts("tpm"), optarg);
+                break;
+            case QEMU_OPTION_tpmdev:
+                tpm_config_parse(qemu_find_opts("tpmdev"), optarg);
+                break;
+#endif
             case QEMU_OPTION_mempath:
                 mem_path = optarg;
                 break;
@@ -3149,6 +3158,12 @@  int main(int argc, char **argv, char **e
         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);
@@ -3394,6 +3409,9 @@  int main(int argc, char **argv, char **e
     quit_timers();
     net_cleanup();
     res_free();
+#ifdef CONFIG_TPM
+    tpm_cleanup();
+#endif
 
     return 0;
 }
Index: qemu-git/qemu-config.c
===================================================================
--- qemu-git.orig/qemu-config.c
+++ qemu-git/qemu-config.c
@@ -507,6 +507,50 @@  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 qemu_tpm_opts = {
+    .name = "tpm",
+    .implied_opt_name = "type",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
+    .desc = {
+        {
+            .name = "type",
+            .type = QEMU_OPT_STRING,
+            .help = "Type of TPM backend",
+        },
+        {
+            .name = "model",
+            .type = QEMU_OPT_STRING,
+            .help = "Model of TPM frontend",
+        },
+        {
+            .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,
@@ -523,6 +567,8 @@  static QemuOptsList *vm_config_groups[32
     &qemu_option_rom_opts,
     &qemu_machine_opts,
     &qemu_boot_opts,
+    &qemu_tpmdev_opts,
+    &qemu_tpm_opts,
     NULL,
 };
 
Index: qemu-git/hw/tpm_tis.h
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_tis.h
@@ -0,0 +1,75 @@ 
+/*
+ * tpm_tis.h - include file for tpm_tis.c
+ *
+ * Copyright (C) 2006,2010,2011 IBM Corporation
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ *         David Safford <safford@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+#ifndef _HW_TPM_TIS_H
+#define _HW_TPM_TIS_H
+
+#include "isa.h"
+#include "block_int.h"
+#include "qemu-thread.h"
+
+#include <stdint.h>
+
+#define TIS_ADDR_BASE       0xFED40000
+
+#define NUM_LOCALITIES      5     /* per spec */
+#define NO_LOCALITY         0xff
+
+#define IS_VALID_LOCTY(x)   ((x) < NUM_LOCALITIES)
+
+
+#define TPM_TIS_IRQ         5
+
+#define TIS_TPM_BUFFER_MAX  4096
+
+
+typedef struct TPMSizedBuffer {
+    uint32_t size;
+    uint8_t  *buffer;
+} TPMSizedBuffer;
+
+
+enum tis_state {
+    STATE_IDLE = 0,
+    STATE_READY,
+    STATE_COMPLETION,
+    STATE_EXECUTION,
+    STATE_RECEPTION,
+};
+
+
+void tis_reset_for_snapshot_resume(TPMState *s);
+
+
+/* utility functions */
+
+static inline uint16_t tis_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+    return (sb->buffer[4] << 8) + sb->buffer[5];
+}
+
+static inline void dumpBuffer(FILE *stream,
+                              unsigned char *buffer, unsigned int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++) {
+        if (i && !(i % 16)) {
+            fprintf(stream, "\n");
+        }
+        fprintf(stream, "%.2X ", buffer[i]);
+    }
+    fprintf(stream, "\n");
+}
+
+#endif /* _HW_TPM_TIS_H */
Index: qemu-git/tpm.c
===================================================================
--- /dev/null
+++ qemu-git/tpm.c
@@ -0,0 +1,279 @@ 
+/*
+ * TPM configuraion
+ *
+ * Copyright (C) 2011 IBM Corporation
+ * Copyright (C) 2011 Stefan Berger
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ * Based on net.c
+ */
+#include "config.h"
+
+#include "tpm.h"
+#include "monitor.h"
+#include "qerror.h"
+
+
+#ifdef CONFIG_TPM
+
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+
+static const TPMDriverOps *bes[] = {
+    NULL,
+};
+
+
+static const char *tpm_models[] = {
+    TPM_DEFAULT_DEVICE_MODEL,
+    NULL,
+};
+
+
+static QLIST_HEAD(, TPMBackend) tpm_backends =
+    QLIST_HEAD_INITIALIZER(tpm_backends);
+
+
+const TPMDriverOps *tpm_get_backend_driver(const char *id)
+{
+    int i;
+
+    for (i = 0; bes[i] != NULL; i++) {
+        if (!strcmp(bes[i]->id, id)) {
+            break;
+        }
+    }
+
+    return bes[i];
+}
+
+
+static void tpm_display_models(FILE *out)
+{
+    int i;
+
+    fprintf(stderr, "qemu: Supported TPM models: ");
+    for (i = 0 ; tpm_models[i]; i++) {
+        fprintf(stderr, "%s%c", tpm_models[i], tpm_models[i+1] ? ',' : '\n');
+    }
+}
+
+
+static int tpm_check_model(const char *model)
+{
+    int i;
+
+    for (i = 0 ; tpm_models[i]; i++) {
+        if (strcmp(tpm_models[i], model) == 0) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+
+void tpm_display_backend_drivers(FILE *out)
+{
+    int i;
+
+    fprintf(out, "Supported TPM types (choose only one):\n");
+
+    for (i = 0; bes[i] != NULL; i++) {
+        fprintf(out, "%12s   %s",
+                bes[i]->id, bes[i]->desc());
+        fprintf(out, "\n");
+    }
+    fprintf(out, "\n");
+}
+
+
+TPMBackend *qemu_find_tpm(const char *id)
+{
+    TPMBackend *drv;
+
+    QLIST_FOREACH(drv, &tpm_backends, list) {
+        if (!strcmp(drv->id, id)) {
+            return drv;
+        }
+    }
+
+    return NULL;
+}
+
+
+void do_info_tpm(Monitor *mon)
+{
+    TPMBackend *drv;
+    const char *model;
+
+    monitor_printf(mon, "TPM devices:\n");
+
+    QLIST_FOREACH(drv, &tpm_backends, list) {
+        model = drv->model ? drv->model : TPM_DEFAULT_DEVICE_MODEL;
+        monitor_printf(mon, "  %s: model=%s,id=%s\n",
+                       drv->ops->id, model, drv->id);
+    }
+}
+
+/*
+ * Create those TPMs that were created with -tpm rather than -tpmdev.
+ * The ones created with -tpm have a 'model' name.
+ */
+void qemu_create_tpm(void)
+{
+    TPMBackend *drv;
+
+    QLIST_FOREACH(drv, &tpm_backends, list) {
+        if (drv->model) {
+            if (strcmp(drv->model, TPM_DEFAULT_DEVICE_MODEL) == 0) {
+                isa_create_simple(drv->model);
+            }
+        }
+    }
+}
+
+
+static int configure_tpm(QemuOpts *opts, int is_tpmdev)
+{
+    const char *value;
+    const char *id = TPM_DEFAULT_DEVICE_ID;
+    const char *model =  NULL;
+    const TPMDriverOps *be;
+    TPMBackend *drv;
+
+    if (!QLIST_EMPTY(&tpm_backends)) {
+        fprintf(stderr, "Only one TPM is allowed.\n");
+        return 1;
+    }
+
+    if (is_tpmdev) {
+        id = qemu_opts_id(opts);
+        if (id == NULL) {
+            qerror_report(QERR_MISSING_PARAMETER, "id");
+            return 1;
+        }
+    } else {
+        model = qemu_opt_get(opts, "model");
+        if (model) {
+            if (strcmp(model, "?") == 0) {
+                tpm_display_models(stdout);
+                return 1;
+            }
+            if (!tpm_check_model(model)) {
+                qerror_report(QERR_INVALID_PARAMETER_VALUE, "model",
+                              "a tpm model");
+                tpm_display_models(stderr);
+                return 1;
+            }
+        } else {
+            model = TPM_DEFAULT_DEVICE_MODEL;
+        }
+    }
+
+    value = qemu_opt_get(opts, "type");
+    if (!value) {
+        qerror_report(QERR_MISSING_PARAMETER, "type");
+        tpm_display_backend_drivers(stderr);
+        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(stderr);
+        return 1;
+    }
+
+    assert((is_tpmdev && model == NULL) || (!is_tpmdev && model != NULL));
+
+    drv = be->create(opts, id, model);
+    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, 1);
+}
+
+
+static int tpm_init_tpm(QemuOpts *opts, void *dummy)
+{
+    return configure_tpm(opts, 0);
+}
+
+
+int tpm_init(void)
+{
+    if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
+                          tpm_init_tpmdev, NULL, 1) != 0) {
+        return -1;
+    }
+
+    if (qemu_opts_foreach(qemu_find_opts("tpm"),
+                          tpm_init_tpm, NULL, 1) != 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+
+void tpm_cleanup(void)
+{
+    TPMBackend *drv, *next;
+
+    QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
+        QLIST_REMOVE(drv, list);
+        drv->ops->destroy(drv);
+    }
+}
+
+
+void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
+{
+    QemuOpts *opts;
+
+    if (strcmp("none", optarg) != 0) {
+        if (*optarg == '?') {
+            tpm_display_backend_drivers(stdout);
+            exit(0);
+        }
+        opts = qemu_opts_parse(opts_list, optarg, 1);
+        if (!opts) {
+            exit(1);
+        }
+    }
+}
+
+# else /* TARGET_I386 || TARGET_X86_64 */
+
+void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
+{
+}
+
+int tpm_init(void)
+{
+    return 0;
+}
+
+void tpm_cleanup(void)
+{
+}
+
+void do_info_tpm(Monitor *mon)
+{
+    monitor_printf(mon, "TPM support: not compiled\n");
+}
+
+# endif
+#endif /* CONFIG_TPM */
Index: qemu-git/tpm.h
===================================================================
--- /dev/null
+++ qemu-git/tpm.h
@@ -0,0 +1,112 @@ 
+#ifndef _HW_TPM_CONFIG_H
+#define _HW_TPM_CONFIG_H
+
+struct TPMState;
+typedef struct TPMState TPMState;
+
+#include "hw/tpm_tis.h"
+
+struct TPMDriverOps;
+typedef struct TPMDriverOps TPMDriverOps;
+
+typedef struct TPMBackend {
+    char *id;
+    char *model;
+    TPMDriverOps *ops;
+
+    QLIST_ENTRY(TPMBackend) list;
+} TPMBackend;
+
+
+/* locality data  -- all fields are persisted */
+typedef struct TPMLocality {
+    enum tis_state state;
+    uint8_t access;
+    uint8_t sts;
+    uint32_t inte;
+    uint32_t ints;
+
+    uint16_t w_offset;
+    uint16_t r_offset;
+    TPMSizedBuffer w_buffer;
+    TPMSizedBuffer r_buffer;
+} TPMLocality;
+
+
+/* overall state of the TPM interface */
+struct TPMState {
+    ISADevice busdev;
+
+    uint32_t offset;
+    uint8_t buf[TIS_TPM_BUFFER_MAX];
+
+    uint8_t active_locty;
+    uint8_t aborting_locty;
+    uint8_t next_locty;
+
+    uint8_t command_locty;
+    TPMLocality loc[NUM_LOCALITIES];
+
+    qemu_irq irq;
+    uint32_t irq_num;
+
+    QemuMutex state_lock;
+    QemuCond  from_tpm_cond;
+    QemuCond  to_tpm_cond;
+    bool      to_tpm_execute;
+
+    bool      tpm_initialized;
+
+    char *backend;
+    TPMBackend *be_driver;
+};
+
+
+typedef void (TPMRecvDataCB)(TPMState *s, uint8_t locty);
+
+struct TPMDriverOps {
+    const char *id;
+    /* get a descriptive text of the backend to display to the user */
+    const char *(*desc)(void);
+
+    void (*job_for_main_thread)(void *);
+
+    TPMBackend *(*create)(QemuOpts *, const char *id, const char *model);
+    void (*destroy)(TPMBackend *drv);
+
+    /* initialize the backend */
+    int (*init)(TPMState *s, TPMRecvDataCB *datacb);
+    /* start up the TPM on the backend early if possible */
+    int (*early_startup_tpm)(void);
+    /* start up the TPM on the backend late if necessary */
+    int (*late_startup_tpm)(void);
+    /* returns true if nothing will ever answer TPM requests */
+    bool (*had_startup_error)(void);
+
+    size_t (*realloc_buffer)(TPMSizedBuffer *sb);
+
+    void (*reset)(void);
+
+    /* called to trigger the saving of the volatile data;
+       called before the VM suspends / migrates */
+    int (*save_volatile_data)(void);
+    /* triggers the loading of the volatile data */
+    int (*load_volatile_data)(TPMState *s);
+
+    bool (*get_tpm_established_flag)(void);
+};
+
+#define TPM_DEFAULT_DEVICE_ID    "tpm0"
+#define TPM_DEFAULT_DEVICE_MODEL "tpm-tis"
+
+void tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
+int tpm_init(void);
+void tpm_cleanup(void);
+void qemu_create_tpm(void);
+TPMBackend *qemu_find_tpm(const char *id);
+void do_info_tpm(Monitor *mon);
+void tpm_display_backend_drivers(FILE *out);
+const TPMDriverOps *tpm_get_backend_driver(const char *id);
+
+
+#endif /* _HW_TPM_CONFIG_H */
Index: qemu-git/hw/pc.c
===================================================================
--- qemu-git.orig/hw/pc.c
+++ qemu-git/hw/pc.c
@@ -43,6 +43,7 @@ 
 #include "ui/qemu-spice.h"
 #include "memory.h"
 #include "exec-memory.h"
+#include "tpm.h"
 
 /* output Bochs bios info messages */
 //#define DEBUG_BIOS
@@ -62,7 +63,7 @@ 
 #define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
 
 /* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables.  */
-#define ACPI_DATA_SIZE       0x10000
+#define ACPI_DATA_SIZE       0x20000
 #define BIOS_CFG_IOPORT 0x510
 #define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
 #define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
@@ -1183,6 +1184,10 @@  void pc_basic_device_init(qemu_irq *isa_
         fd[i] = drive_get(IF_FLOPPY, 0, i);
     }
     fdctrl_init_isa(fd);
+
+#ifdef CONFIG_TPM
+    qemu_create_tpm();
+#endif
 }
 
 void pc_pci_device_init(PCIBus *pci_bus)
Index: qemu-git/monitor.c
===================================================================
--- qemu-git.orig/monitor.c
+++ qemu-git/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"
@@ -3151,6 +3152,15 @@  static const mon_cmd_t info_cmds[] = {
         .mhandler.info = do_info_trace_events,
     },
 #endif
+#if defined(CONFIG_TPM)
+    {
+        .name       = "tpm",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show the TPM devices",
+        .mhandler.info = do_info_tpm,
+    },
+#endif
     {
         .name       = NULL,
     },
Index: qemu-git/hmp-commands.hx
===================================================================
--- qemu-git.orig/hmp-commands.hx
+++ qemu-git/hmp-commands.hx
@@ -1351,6 +1351,8 @@  show device tree
 show qdev device model list
 @item info roms
 show roms
+@item info tpm
+show the TPM devices
 @end table
 ETEXI
 
Index: qemu-git/Makefile.target
===================================================================
--- qemu-git.orig/Makefile.target
+++ qemu-git/Makefile.target
@@ -200,6 +200,7 @@  obj-$(CONFIG_KVM) += kvm.o kvm-all.o
 obj-$(CONFIG_NO_KVM) += kvm-stub.o
 obj-y += memory.o
 LIBS+=-lz
+obj-y += tpm.o
 
 QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
 QEMU_CFLAGS += $(VNC_SASL_CFLAGS)