diff mbox

[V4,01/10] Support for TPM command line options

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

Commit Message

Stefan Berger May 6, 2011, 5:32 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 type=<type>,path=<path to blockstorage file>,

and

./qemu-... -tpm ?

where the latter works similar to -soundhw ? and shows a list of
available TPM backends (i.e., libtpms-based, Xen).

Only the 'type' is interpreted in arch_init.c. Using this 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
'handle_options' and return true if the VM can be started or 'false'
if not enough or bad parameters were provided.

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>

---
 arch_init.c     |   80 +++++++++++++++++++++++++++++++
 arch_init.h     |    2 
 hw/pc.c         |    4 +
 hw/pc.h         |    7 ++
 hw/tpm_tis.h    |  143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-config.c   |   20 +++++++
 qemu-options.hx |   11 ++++
 vl.c            |   12 ++++
 8 files changed, 279 insertions(+)

Comments

Serge E. Hallyn May 6, 2011, 8:23 p.m. UTC | #1
Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> This patch adds support for TPM command line options.
> The command line supported here (considering the libtpms based
> backend) are
> 
> ./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
> 
> and
> 
> ./qemu-... -tpm ?
>
> where the latter works similar to -soundhw ? and shows a list of
> available TPM backends (i.e., libtpms-based, Xen).
> 
> Only the 'type' is interpreted in arch_init.c. Using this 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
> 'handle_options' and return true if the VM can be started or 'false'
> if not enough or bad parameters were provided.
> 
> 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>

Thanks, Stefan.  Two nits:

> +static QemuOptsList qemu_tpm_opts = {
> +    .name = "tpm",
> +    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
> +    .desc = {
> +        {
> +            .name = "type",
> +            .type = QEMU_OPT_STRING,
> +            .help = "Type of TPM backend",
> +        },
> +        {
> +            .name = "path",
> +            .type = QEMU_OPT_STRING,
> +            .help = "Persitent storage for TPM state",

Persistent.

...

> +# else /* CONFIG_TPM */
> +
> +void select_tpm(const char *optarg)
> +{
> +    (void)optarg;
> +}

I realize this should never get called if !CONFIG_TPM, but that still
doesn't seem like cause to go directly calling a potentially NULL
string.

Otherwise, fwiw

Acked-by: Serge Hallyn <serge.hallyn@ubuntu.com>

thanks,
-serge
Stefan Berger May 6, 2011, 8:32 p.m. UTC | #2
On 05/06/2011 04:23 PM, Serge E. Hallyn wrote:
> Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
>> This patch adds support for TPM command line options.
>> The command line supported here (considering the libtpms based
>> backend) are
>>
>> ./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
>>
>> and
>>
>> ./qemu-... -tpm ?
>>
>> where the latter works similar to -soundhw ? and shows a list of
>> available TPM backends (i.e., libtpms-based, Xen).
>>
>> Only the 'type' is interpreted in arch_init.c. Using this 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
>> 'handle_options' and return true if the VM can be started or 'false'
>> if not enough or bad parameters were provided.
>>
>> 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>
> Thanks, Stefan.  Two nits:
>
>> +static QemuOptsList qemu_tpm_opts = {
>> +    .name = "tpm",
>> +    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
>> +    .desc = {
>> +        {
>> +            .name = "type",
>> +            .type = QEMU_OPT_STRING,
>> +            .help = "Type of TPM backend",
>> +        },
>> +        {
>> +            .name = "path",
>> +            .type = QEMU_OPT_STRING,
>> +            .help = "Persitent storage for TPM state",
> Persistent.
Oh, typo. Will fix it.
> ...
>
>> +# else /* CONFIG_TPM */
>> +
>> +void select_tpm(const char *optarg)
>> +{
>> +    (void)optarg;
>> +}
> I realize this should never get called if !CONFIG_TPM, but that still
> doesn't seem like cause to go directly calling a potentially NULL
> string.
(void)optarg is just there to make the compiler not put out a warning 
about an unused parameter. I could have used __attribute__((unused)) 
instead but chose this one here. It's not calling anything.

Thanks!

    Stefan
> Otherwise, fwiw
>
> Acked-by: Serge Hallyn<serge.hallyn@ubuntu.com>
>
> thanks,
> -serge
Serge E. Hallyn May 6, 2011, 8:33 p.m. UTC | #3
Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> On 05/06/2011 04:23 PM, Serge E. Hallyn wrote:
> >Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> >>This patch adds support for TPM command line options.
> >>The command line supported here (considering the libtpms based
> >>backend) are
> >>
> >>./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
> >>
> >>and
> >>
> >>./qemu-... -tpm ?
> >>
> >>where the latter works similar to -soundhw ? and shows a list of
> >>available TPM backends (i.e., libtpms-based, Xen).
> >>
> >>Only the 'type' is interpreted in arch_init.c. Using this 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
> >>'handle_options' and return true if the VM can be started or 'false'
> >>if not enough or bad parameters were provided.
> >>
> >>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>
> >Thanks, Stefan.  Two nits:
> >
> >>+static QemuOptsList qemu_tpm_opts = {
> >>+    .name = "tpm",
> >>+    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
> >>+    .desc = {
> >>+        {
> >>+            .name = "type",
> >>+            .type = QEMU_OPT_STRING,
> >>+            .help = "Type of TPM backend",
> >>+        },
> >>+        {
> >>+            .name = "path",
> >>+            .type = QEMU_OPT_STRING,
> >>+            .help = "Persitent storage for TPM state",
> >Persistent.
> Oh, typo. Will fix it.
> >...
> >
> >>+# else /* CONFIG_TPM */
> >>+
> >>+void select_tpm(const char *optarg)
> >>+{
> >>+    (void)optarg;
> >>+}
> >I realize this should never get called if !CONFIG_TPM, but that still
> >doesn't seem like cause to go directly calling a potentially NULL
> >string.
> (void)optarg is just there to make the compiler not put out a
> warning about an unused parameter. I could have used
> __attribute__((unused)) instead but chose this one here. It's not
> calling anything.

Haha, yes, I must have been seeing things :)

-serge
Serge E. Hallyn May 17, 2011, 8:58 p.m. UTC | #4
Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> This patch adds support for TPM command line options.
> The command line supported here (considering the libtpms based
> backend) are
> 
> ./qemu-... -tpm type=<type>,path=<path to blockstorage file>,

Hm, I did

kvm -tpm type=builtin,path=tpm.img -m 1G disk.img -vnc :1

with disk.img being a newly installed VM.  I installed trousers
and tpm-tools, tried loading the tpm, tpm_tis, tpm_infineon, and
some other modules, /dev/tpm was never created, and

  tpm_takeownership

continued to give me:

Tspi_Context_Connect failed: 0x00003011 - layer=tsp, code=0011 (17), Communication failure

Which kernel module should work with builtin?

thanks,
-serge
Stefan Berger May 17, 2011, 11:15 p.m. UTC | #5
On 05/17/2011 04:58 PM, Serge E. Hallyn wrote:
> Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
>> This patch adds support for TPM command line options.
>> The command line supported here (considering the libtpms based
>> backend) are
>>
>> ./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
> Hm, I did
>
> kvm -tpm type=builtin,path=tpm.img -m 1G disk.img -vnc :1
>
> with disk.img being a newly installed VM.  I installed trousers
> and tpm-tools, tried loading the tpm, tpm_tis, tpm_infineon, and
> some other modules, /dev/tpm was never created, and
>
>    tpm_takeownership
>
> continued to give me:
>
> Tspi_Context_Connect failed: 0x00003011 - layer=tsp, code=0011 (17), Communication failure
>
> Which kernel module should work with builtin?
The device model is a tpm-tis. So modprobe tpm_tis should create a 
/dev/tpm0. If nothing else helps enable the DEBUG_TIS in hw/tpm_tis and 
recompile.

Did you start it with SeaBIOS and the TPM-related patches applied to it? 
If not, you'll have to initialize the TPM that otherwise the BIOS would 
do. Otherwise the debugging output from the tpm-tis should begin very 
early once the BIOS sends commands to the TIS/TPM.

Regards,
   Stefan
> thanks,
> -serge
>
Stefan Berger May 17, 2011, 11:16 p.m. UTC | #6
On 05/17/2011 04:58 PM, Serge E. Hallyn wrote:
> Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
>> This patch adds support for TPM command line options.
>> The command line supported here (considering the libtpms based
>> backend) are
>>
>> ./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
> Hm, I did
>
> kvm -tpm type=builtin,path=tpm.img -m 1G disk.img -vnc :1
>
> with disk.img being a newly installed VM.  I installed trousers
> and tpm-tools, tried loading the tpm, tpm_tis, tpm_infineon, and
> some other modules, /dev/tpm was never created, and
>
>    tpm_takeownership
>
> continued to give me:
>
> Tspi_Context_Connect failed: 0x00003011 - layer=tsp, code=0011 (17), Communication failure
>
> Which kernel module should work with builtin?
The device model is a tpm-tis. So modprobe tpm_tis should create a 
/dev/tpm0. If nothing else helps enable the DEBUG_TIS in hw/tpm_tis and 
recompile.

Did you start it with SeaBIOS and the TPM-related patches applied to it? 
If not, you'll have to initialize the TPM that otherwise the BIOS would 
do. Otherwise the debugging output from the tpm-tis should begin very 
early once the BIOS sends commands to the TIS/TPM.

Regards,
   Stefan
> thanks,
> -serge
Serge E. Hallyn May 18, 2011, 1:52 a.m. UTC | #7
Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> On 05/17/2011 04:58 PM, Serge E. Hallyn wrote:
> >Quoting Stefan Berger (stefanb@linux.vnet.ibm.com):
> >>This patch adds support for TPM command line options.
> >>The command line supported here (considering the libtpms based
> >>backend) are
> >>
> >>./qemu-... -tpm type=<type>,path=<path to blockstorage file>,
> >Hm, I did
> >
> >kvm -tpm type=builtin,path=tpm.img -m 1G disk.img -vnc :1
> >
> >with disk.img being a newly installed VM.  I installed trousers
> >and tpm-tools, tried loading the tpm, tpm_tis, tpm_infineon, and
> >some other modules, /dev/tpm was never created, and
> >
> >   tpm_takeownership
> >
> >continued to give me:
> >
> >Tspi_Context_Connect failed: 0x00003011 - layer=tsp, code=0011 (17), Communication failure
> >
> >Which kernel module should work with builtin?
> The device model is a tpm-tis. So modprobe tpm_tis should create a
> /dev/tpm0. If nothing else helps enable the DEBUG_TIS in hw/tpm_tis
> and recompile.
> 
> Did you start it with SeaBIOS and the TPM-related patches applied to

No, feh!  I'll go grab those, thanks.

Sorry, I had apparently moved 0/10 (bc it screws up git am), and
then thought 1/10 was also the intro bc it looks like it has an
intro.  (Now I see 0/10 at
http://lists.gnu.org/archive/html/qemu-devel/2011-05/msg00541.html)

thanks,
-serge
diff mbox

Patch

Index: qemu-git/hw/pc.h
===================================================================
--- qemu-git.orig/hw/pc.h
+++ qemu-git/hw/pc.h
@@ -6,6 +6,7 @@ 
 #include "isa.h"
 #include "fdc.h"
 #include "net.h"
+#include "tpm_tis.h"
 
 /* PC-style peripherals (also used by other machines).  */
 
@@ -128,6 +129,12 @@  void pc_register_ferr_irq(qemu_irq irq);
 void pc_cmos_set_s3_resume(void *opaque, int irq, int level);
 void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
 
+/* tpm_tis.c */
+extern bool has_tpm;
+const BackendTPMDriver *tis_set_backend_driver(const char *tpm_type);
+void tis_display_backend_drivers(FILE *);
+
+
 void pc_cpus_init(const char *cpu_model);
 void pc_memory_init(ram_addr_t ram_size,
                     const char *kernel_filename,
Index: qemu-git/qemu-options.hx
===================================================================
--- qemu-git.orig/qemu-options.hx
+++ qemu-git/qemu-options.hx
@@ -1041,6 +1041,17 @@  Specify SMBIOS type 0 fields
 Specify SMBIOS type 1 fields
 ETEXI
 
+#ifndef _WIN32
+# ifdef CONFIG_TPM
+DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \
+    ""
+    "-tpm type=<type>,path=<path>\n" \
+    "                enable a TPM with state from file in given path\n"
+    "                use -tpm ? to get a list of supported TPM types\n",
+    QEMU_ARCH_I386)
+# endif
+#endif
+
 DEFHEADING()
 STEXI
 @end table
Index: qemu-git/vl.c
===================================================================
--- qemu-git.orig/vl.c
+++ qemu-git/vl.c
@@ -244,6 +244,8 @@  int nb_numa_nodes;
 uint64_t node_mem[MAX_NODES];
 uint64_t node_cpumask[MAX_NODES];
 
+bool has_tpm = false;
+
 static QEMUTimer *nographic_timer;
 
 uint8_t qemu_uuid[16];
@@ -2333,6 +2335,16 @@  int main(int argc, char **argv, char **e
                 ram_size = value;
                 break;
             }
+#ifdef CONFIG_TPM
+            case QEMU_OPTION_tpm:
+                if (!(tpm_available())) {
+                    printf("Option %s not supported for this target\n",
+                           popt->name);
+                    exit(1);
+                }
+                select_tpm(optarg);
+                break;
+#endif
             case QEMU_OPTION_mempath:
                 mem_path = optarg;
                 break;
Index: qemu-git/qemu-config.c
===================================================================
--- qemu-git.orig/qemu-config.c
+++ qemu-git/qemu-config.c
@@ -450,6 +450,25 @@  QemuOptsList qemu_option_rom_opts = {
     },
 };
 
+static QemuOptsList qemu_tpm_opts = {
+    .name = "tpm",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpm_opts.head),
+    .desc = {
+        {
+            .name = "type",
+            .type = QEMU_OPT_STRING,
+            .help = "Type of TPM backend",
+        },
+        {
+            .name = "path",
+            .type = QEMU_OPT_STRING,
+            .help = "Persitent storage for TPM state",
+        },
+        { /* end of list */ }
+    },
+};
+
+
 static QemuOptsList *vm_config_groups[32] = {
     &qemu_drive_opts,
     &qemu_chardev_opts,
@@ -464,6 +483,7 @@  static QemuOptsList *vm_config_groups[32
     &qemu_trace_opts,
 #endif
     &qemu_option_rom_opts,
+    &qemu_tpm_opts,
     NULL,
 };
 
Index: qemu-git/arch_init.c
===================================================================
--- qemu-git.orig/arch_init.c
+++ qemu-git/arch_init.c
@@ -41,6 +41,8 @@ 
 #include "net.h"
 #include "gdbstub.h"
 #include "hw/smbios.h"
+#include "blockdev.h"
+#include "hw/tpm_tis.h"
 
 #ifdef TARGET_SPARC
 int graphic_width = 1024;
@@ -726,3 +728,81 @@  int xen_available(void)
     return 0;
 #endif
 }
+
+int tpm_available(void) {
+#ifdef CONFIG_TPM
+    return 1;
+#else
+    return 0;
+#endif
+}
+
+#ifdef CONFIG_TPM
+
+#if defined (TARGET_I386) || defined (TARGET_X86_64)
+
+
+static int configure_tpm(QemuOpts *opts)
+{
+    const char *value;
+    const BackendTPMDriver *be;
+
+    if (has_tpm) {
+        fprintf(stderr,"Only one TPM is allowed\n");
+        return 1;
+    }
+
+    value = qemu_opt_get(opts, "type");
+    if (!value) {
+        fprintf(stderr,
+                "Missing TPM backend type.");
+        tis_display_backend_drivers(stderr);
+        return 1;
+    }
+
+    be = tis_set_backend_driver(value);
+    if (be == NULL) {
+        fprintf(stderr,
+                "A TPM backend driver of type %s is not supported.\n",
+                value);
+        tis_display_backend_drivers(stderr);
+        return 1;
+    }
+
+    has_tpm = be->handle_options(opts);
+    if (!has_tpm) {
+        return 1;
+    }
+
+    return 0;
+}
+
+
+void select_tpm(const char *optarg)
+{
+    QemuOpts *opts;
+
+    if (strcmp("none", optarg) != 0) {
+        if (*optarg == '?') {
+            tis_display_backend_drivers(stdout);
+            exit(0);
+        }
+        opts = qemu_opts_parse(qemu_find_opts("tpm"), optarg, 0);
+        if (!opts) {
+            exit(1);
+        }
+        if (configure_tpm(opts)) {
+            exit(1);
+        }
+    }
+}
+
+# else /* CONFIG_TPM */
+
+void select_tpm(const char *optarg)
+{
+    (void)optarg;
+}
+
+# endif
+#endif /* CONFIG_TPM */
Index: qemu-git/arch_init.h
===================================================================
--- qemu-git.orig/arch_init.h
+++ qemu-git/arch_init.h
@@ -29,5 +29,7 @@  int audio_available(void);
 void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus);
 int kvm_available(void);
 int xen_available(void);
+int tpm_available(void);
+void select_tpm(const char *optarg);
 
 #endif
Index: qemu-git/hw/tpm_tis.h
===================================================================
--- /dev/null
+++ qemu-git/hw/tpm_tis.h
@@ -0,0 +1,143 @@ 
+/*
+ * 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         11
+
+#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,
+};
+
+
+/* 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; 's' marks a persisted field */
+typedef 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;
+} TPMState;
+
+
+typedef void (TPMRecvDataCB)(TPMState *s, uint8_t locty);
+
+typedef struct BackendTPMDriver {
+    const char *id;
+    const char *(*desc)(void);
+
+    void (*job_for_main_thread)(void *);
+
+    bool (*handle_options)(QemuOpts *);
+
+    int (*init)(TPMState *s, TPMRecvDataCB *datacb);
+    int (*early_startup_tpm)(void);
+    int (*late_startup_tpm)(void);
+    bool (*had_startup_error)(void);
+
+    size_t (*realloc_buffer)(TPMSizedBuffer *sb);
+
+    void (*reset)(void);
+
+    int (*save_volatile_data)(void);
+    int (*load_volatile_data)(TPMState *s);
+
+    bool (*get_tpm_established_flag)(void);
+} BackendTPMDriver;
+
+
+void tis_reset_for_snapshot_resume(TPMState *s);
+const BackendTPMDriver *tis_get_active_backend(void);
+
+/* 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/hw/pc.c
===================================================================
--- qemu-git.orig/hw/pc.c
+++ qemu-git/hw/pc.c
@@ -1154,6 +1154,10 @@  void pc_basic_device_init(qemu_irq *isa_
         fd[i] = drive_get(IF_FLOPPY, 0, i);
     }
     fdctrl_init_isa(fd);
+
+    if (has_tpm) {
+        isa_create_simple("tpm-tis");
+    }
 }
 
 void pc_pci_device_init(PCIBus *pci_bus)