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 <stef...@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 <stef...@us.ibm.com> > + * David Safford <saff...@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) >