Patchwork [V7,01/13] Support for TPM command line options

login
register
mail settings
Submitter Stefan Berger
Date Aug. 10, 2011, 7:29 p.m.
Message ID <20110810193010.022952532@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/109437/
State New
Headers show

Comments

Stefan Berger - Aug. 10, 2011, 7:29 p.m.
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


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(-)

Patch

Index: qemu-git/qemu-options.hx
===================================================================
--- qemu-git.orig/qemu-options.hx
+++ qemu-git/qemu-options.hx
@@ -1756,6 +1756,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"
@@ -2461,6 +2462,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;
@@ -3107,6 +3116,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);
@@ -3357,6 +3372,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
@@ -506,6 +506,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,
@@ -522,6 +566,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, "%7s   %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
@@ -42,6 +42,7 @@ 
 #include "blockdev.h"
 #include "ui/qemu-spice.h"
 #include "memory.h"
+#include "tpm.h"
 
 /* output Bochs bios info messages */
 //#define DEBUG_BIOS
@@ -61,7 +62,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)
@@ -1179,6 +1180,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"
@@ -3139,6 +3140,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)