[libvirt] [PATCH v4 00/11] Add support for guests with TPM passthrough device

Hello! The following set of patches adds support to libvirt for adding a TPM passthrough device to a QEMU guest. Support for this was recently accepted into QEMU. This set of patches borrows a lot from the recently added support for rng's. Regards, Stefan --- v3->v4: - followed tree to ce65b435 - followed comments on v3 v2->v3: - followed tree to 03122d7b - some nits I found fixed v1->v2: - Followed Daniel Berrange's comments (except for the one on de-consolidating the JSON monitor code)

Probe for QEMU's QMP TPM support by querying the lists of supported TPM models (query-tpm-models) and backend types (query-tpm-types). The setting of the capability flags following the strings returned from the commands above is only provided in the patch where domain_conf.c gets TPM support due to dependencies on functions only introduced there. Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- src/qemu/qemu_capabilities.c | 2 src/qemu/qemu_capabilities.h | 3 + src/qemu/qemu_monitor.c | 44 +++++++++++++++++++++ src/qemu/qemu_monitor.h | 6 ++ src/qemu/qemu_monitor_json.c | 90 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 8 +++ 6 files changed, 153 insertions(+) Index: libvirt/src/qemu/qemu_capabilities.c =================================================================== --- libvirt.orig/src/qemu/qemu_capabilities.c +++ libvirt/src/qemu/qemu_capabilities.c @@ -216,6 +216,8 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAS "ipv6-migration", /* 135 */ "machine-opt", + "tpm-passthrough", + "tpm-tis", ); struct _virQEMUCaps { Index: libvirt/src/qemu/qemu_capabilities.h =================================================================== --- libvirt.orig/src/qemu/qemu_capabilities.h +++ libvirt/src/qemu/qemu_capabilities.h @@ -176,6 +176,8 @@ enum virQEMUCapsFlags { QEMU_CAPS_SCSI_MEGASAS = 134, /* -device megasas */ QEMU_CAPS_IPV6_MIGRATION = 135, /* -incoming [::] */ QEMU_CAPS_MACHINE_OPT = 136, /* -machine xxxx*/ + QEMU_CAPS_DEVICE_TPM_PASSTHROUGH = 137, /* -tpmdev passthrough */ + QEMU_CAPS_DEVICE_TPM_TIS = 138, /* -device tpm_tis */ QEMU_CAPS_LAST, /* this must always be the last item */ }; @@ -257,4 +259,5 @@ int virQEMUCapsParseDeviceStr(virQEMUCap VIR_ENUM_DECL(virQEMUCaps); bool virQEMUCapsUsedQMP(virQEMUCapsPtr qemuCaps); + #endif /* __QEMU_CAPABILITIES_H__*/ Index: libvirt/src/qemu/qemu_monitor.c =================================================================== --- libvirt.orig/src/qemu/qemu_monitor.c +++ libvirt/src/qemu/qemu_monitor.c @@ -3525,3 +3525,47 @@ int qemuMonitorNBDServerStop(qemuMonitor return qemuMonitorJSONNBDServerStop(mon); } + + +int qemuMonitorGetTPMModels(qemuMonitorPtr mon, + char ***tpmmodels) +{ + VIR_DEBUG("mon=%p tpmmodels=%p", + mon, tpmmodels); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!mon->json) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return qemuMonitorJSONGetTPMModels(mon, tpmmodels); +} + + +int qemuMonitorGetTPMTypes(qemuMonitorPtr mon, + char ***tpmtypes) +{ + VIR_DEBUG("mon=%p tpmtypes=%p", + mon, tpmtypes); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!mon->json) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return qemuMonitorJSONGetTPMTypes(mon, tpmtypes); +} Index: libvirt/src/qemu/qemu_monitor.h =================================================================== --- libvirt.orig/src/qemu/qemu_monitor.h +++ libvirt/src/qemu/qemu_monitor.h @@ -683,6 +683,12 @@ int qemuMonitorNBDServerAdd(qemuMonitorP const char *deviceID, bool writable); int qemuMonitorNBDServerStop(qemuMonitorPtr); +int qemuMonitorGetTPMModels(qemuMonitorPtr mon, + char ***tpmmodels); + +int qemuMonitorGetTPMTypes(qemuMonitorPtr mon, + char ***tpmtypes); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that Index: libvirt/src/qemu/qemu_monitor_json.c =================================================================== --- libvirt.orig/src/qemu/qemu_monitor_json.c +++ libvirt/src/qemu/qemu_monitor_json.c @@ -41,6 +41,7 @@ #include "datatypes.h" #include "virerror.h" #include "virjson.h" +#include "virstring.h" #ifdef WITH_DTRACE_PROBES # include "libvirt_qemu_probes.h" @@ -4693,3 +4694,92 @@ qemuMonitorJSONNBDServerStop(qemuMonitor virJSONValueFree(reply); return ret; } + + +static int +qemuMonitorJSONGetStringArray(qemuMonitorPtr mon, const char *qmpCmd, + char ***array) +{ + int ret; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + virJSONValuePtr data; + char **list = NULL; + int n = 0; + size_t i; + + *array = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand(qmpCmd, NULL))) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret < 0) + goto cleanup; + + ret = -1; + + if (!(data = virJSONValueObjectGet(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("%s reply was missing return data"), + qmpCmd); + goto cleanup; + } + + if ((n = virJSONValueArraySize(data)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("%s reply data was not an array"), + qmpCmd); + goto cleanup; + } + + /* null-terminated list */ + if (VIR_ALLOC_N(list, n + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0 ; i < n ; i++) { + virJSONValuePtr child = virJSONValueArrayGet(data, i); + const char *tmp; + + if (!(tmp = virJSONValueGetString(child))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("%s array element does not contain data"), + qmpCmd); + goto cleanup; + } + + if (!(list[i] = strdup(tmp))) { + virReportOOMError(); + goto cleanup; + } + } + + ret = n; + *array = list; + +cleanup: + if (ret < 0) + virStringFreeList(list); + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + +int qemuMonitorJSONGetTPMModels(qemuMonitorPtr mon, + char ***tpmmodels) +{ + return qemuMonitorJSONGetStringArray(mon, "query-tpm-models", tpmmodels); +} + + +int qemuMonitorJSONGetTPMTypes(qemuMonitorPtr mon, + char ***tpmtypes) +{ + return qemuMonitorJSONGetStringArray(mon, "query-tpm-types", tpmtypes); +} Index: libvirt/src/qemu/qemu_monitor_json.h =================================================================== --- libvirt.orig/src/qemu/qemu_monitor_json.h +++ libvirt/src/qemu/qemu_monitor_json.h @@ -341,4 +341,12 @@ int qemuMonitorJSONNBDServerAdd(qemuMoni const char *deviceID, bool writable); int qemuMonitorJSONNBDServerStop(qemuMonitorPtr mon); +int qemuMonitorJSONGetTPMModels(qemuMonitorPtr mon, + char ***tpmmodels) + ATTRIBUTE_NONNULL(2); + +int qemuMonitorJSONGetTPMTypes(qemuMonitorPtr mon, + char ***tpmtypes) + ATTRIBUTE_NONNULL(2); + #endif /* QEMU_MONITOR_JSON_H */

On Fri, Apr 05, 2013 at 10:05:52AM -0400, Stefan Berger wrote:
Probe for QEMU's QMP TPM support by querying the lists of supported TPM models (query-tpm-models) and backend types (query-tpm-types).
The setting of the capability flags following the strings returned from the commands above is only provided in the patch where domain_conf.c gets TPM support due to dependencies on functions only introduced there.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- src/qemu/qemu_capabilities.c | 2 src/qemu/qemu_capabilities.h | 3 + src/qemu/qemu_monitor.c | 44 +++++++++++++++++++++ src/qemu/qemu_monitor.h | 6 ++ src/qemu/qemu_monitor_json.c | 90 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 8 +++ 6 files changed, 153 insertions(+)
ACK, but extra bonus points if you write a patch to extend tests/qemumonitorjsontest.c to validate this handling. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Fri, Apr 05, 2013 at 10:05:52AM -0400, Stefan Berger wrote:
Probe for QEMU's QMP TPM support by querying the lists of supported TPM models (query-tpm-models) and backend types (query-tpm-types).
The setting of the capability flags following the strings returned from the commands above is only provided in the patch where domain_conf.c gets TPM support due to dependencies on functions only introduced there.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- src/qemu/qemu_capabilities.c | 2 src/qemu/qemu_capabilities.h | 3 + src/qemu/qemu_monitor.c | 44 +++++++++++++++++++++ src/qemu/qemu_monitor.h | 6 ++ src/qemu/qemu_monitor_json.c | 90 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 8 +++ 6 files changed, 153 insertions(+)
Index: libvirt/src/qemu/qemu_capabilities.c =================================================================== --- libvirt.orig/src/qemu/qemu_capabilities.c +++ libvirt/src/qemu/qemu_capabilities.c @@ -216,6 +216,8 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAS
"ipv6-migration", /* 135 */ "machine-opt", + "tpm-passthrough", + "tpm-tis", );
struct _virQEMUCaps { Index: libvirt/src/qemu/qemu_capabilities.h =================================================================== --- libvirt.orig/src/qemu/qemu_capabilities.h +++ libvirt/src/qemu/qemu_capabilities.h @@ -176,6 +176,8 @@ enum virQEMUCapsFlags { QEMU_CAPS_SCSI_MEGASAS = 134, /* -device megasas */ QEMU_CAPS_IPV6_MIGRATION = 135, /* -incoming [::] */ QEMU_CAPS_MACHINE_OPT = 136, /* -machine xxxx*/ + QEMU_CAPS_DEVICE_TPM_PASSTHROUGH = 137, /* -tpmdev passthrough */ + QEMU_CAPS_DEVICE_TPM_TIS = 138, /* -device tpm_tis */
QEMU_CAPS_LAST, /* this must always be the last item */ }; @@ -257,4 +259,5 @@ int virQEMUCapsParseDeviceStr(virQEMUCap VIR_ENUM_DECL(virQEMUCaps);
bool virQEMUCapsUsedQMP(virQEMUCapsPtr qemuCaps); + #endif /* __QEMU_CAPABILITIES_H__*/ Index: libvirt/src/qemu/qemu_monitor.c =================================================================== --- libvirt.orig/src/qemu/qemu_monitor.c +++ libvirt/src/qemu/qemu_monitor.c @@ -3525,3 +3525,47 @@ int qemuMonitorNBDServerStop(qemuMonitor
return qemuMonitorJSONNBDServerStop(mon); } + + +int qemuMonitorGetTPMModels(qemuMonitorPtr mon, + char ***tpmmodels) +{ + VIR_DEBUG("mon=%p tpmmodels=%p", + mon, tpmmodels); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!mon->json) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return qemuMonitorJSONGetTPMModels(mon, tpmmodels); +} + + +int qemuMonitorGetTPMTypes(qemuMonitorPtr mon, + char ***tpmtypes) +{ + VIR_DEBUG("mon=%p tpmtypes=%p", + mon, tpmtypes); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!mon->json) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return qemuMonitorJSONGetTPMTypes(mon, tpmtypes); +} Index: libvirt/src/qemu/qemu_monitor.h =================================================================== --- libvirt.orig/src/qemu/qemu_monitor.h +++ libvirt/src/qemu/qemu_monitor.h @@ -683,6 +683,12 @@ int qemuMonitorNBDServerAdd(qemuMonitorP const char *deviceID, bool writable); int qemuMonitorNBDServerStop(qemuMonitorPtr); +int qemuMonitorGetTPMModels(qemuMonitorPtr mon, + char ***tpmmodels); + +int qemuMonitorGetTPMTypes(qemuMonitorPtr mon, + char ***tpmtypes); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that Index: libvirt/src/qemu/qemu_monitor_json.c =================================================================== --- libvirt.orig/src/qemu/qemu_monitor_json.c +++ libvirt/src/qemu/qemu_monitor_json.c @@ -41,6 +41,7 @@ #include "datatypes.h" #include "virerror.h" #include "virjson.h" +#include "virstring.h"
#ifdef WITH_DTRACE_PROBES # include "libvirt_qemu_probes.h" @@ -4693,3 +4694,92 @@ qemuMonitorJSONNBDServerStop(qemuMonitor virJSONValueFree(reply); return ret; } + + +static int +qemuMonitorJSONGetStringArray(qemuMonitorPtr mon, const char *qmpCmd, + char ***array) +{ + int ret; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + virJSONValuePtr data; + char **list = NULL; + int n = 0; + size_t i; + + *array = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand(qmpCmd, NULL))) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret < 0) + goto cleanup; + + ret = -1; + + if (!(data = virJSONValueObjectGet(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("%s reply was missing return data"), + qmpCmd); + goto cleanup; + } + + if ((n = virJSONValueArraySize(data)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("%s reply data was not an array"), + qmpCmd); + goto cleanup; + } + + /* null-terminated list */ + if (VIR_ALLOC_N(list, n + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0 ; i < n ; i++) { + virJSONValuePtr child = virJSONValueArrayGet(data, i); + const char *tmp; + + if (!(tmp = virJSONValueGetString(child))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("%s array element does not contain data"), + qmpCmd); + goto cleanup; + } + + if (!(list[i] = strdup(tmp))) { + virReportOOMError(); + goto cleanup; + } + } + + ret = n; + *array = list; + +cleanup: + if (ret < 0) + virStringFreeList(list); + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + +int qemuMonitorJSONGetTPMModels(qemuMonitorPtr mon, + char ***tpmmodels) +{ + return qemuMonitorJSONGetStringArray(mon, "query-tpm-models", tpmmodels); +} + + +int qemuMonitorJSONGetTPMTypes(qemuMonitorPtr mon, + char ***tpmtypes) +{ + return qemuMonitorJSONGetStringArray(mon, "query-tpm-types", tpmtypes); +}
Guess you never tested this on a QEMU which lacked those commands. Probing capabilities now fails for any QEMU using QMP which lacks these commands # ./daemon/libvirtd 2013-04-15 16:55:57.035+0000: 18459: info : libvirt version: 1.0.4 2013-04-15 16:55:57.035+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found 2013-04-15 16:55:57.136+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found 2013-04-15 16:55:57.220+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found 2013-04-15 16:55:57.313+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found 2013-04-15 16:55:57.415+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found 2013-04-15 16:55:57.504+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 04/15/2013 12:56 PM, Daniel P. Berrange wrote:
Guess you never tested this on a QEMU which lacked those commands. Probing capabilities now fails for any QEMU using QMP which lacks these commands
Indeed, all the executables it checks have TPM enabled by now. What's the best way of suppressing this? Only probe starting with QEMU version 1.5 ? Or suppress the error somehow ? Stefan
# ./daemon/libvirtd 2013-04-15 16:55:57.035+0000: 18459: info : libvirt version: 1.0.4 2013-04-15 16:55:57.035+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found 2013-04-15 16:55:57.136+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found 2013-04-15 16:55:57.220+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found 2013-04-15 16:55:57.313+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found 2013-04-15 16:55:57.415+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found 2013-04-15 16:55:57.504+0000: 18459: error : qemuMonitorJSONCheckError:352 : internal error unable to execute QEMU command 'query-tpm-models': The command query-tpm-models has not been found
Daniel

On Mon, Apr 15, 2013 at 01:32:53PM -0400, Stefan Berger wrote:
On 04/15/2013 12:56 PM, Daniel P. Berrange wrote:
Guess you never tested this on a QEMU which lacked those commands. Probing capabilities now fails for any QEMU using QMP which lacks these commands
Indeed, all the executables it checks have TPM enabled by now. What's the best way of suppressing this? Only probe starting with QEMU version 1.5 ? Or suppress the error somehow ?
See how qemuMonitorJSONGetEvents checks "CommandNotFound" and returns a zero length list. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- src/libvirt_private.syms | 1 + src/util/virstring.c | 14 ++++++++++++++ src/util/virstring.h | 2 ++ 3 files changed, 17 insertions(+) Index: libvirt/src/util/virstring.c =================================================================== --- libvirt.orig/src/util/virstring.c +++ libvirt/src/util/virstring.c @@ -166,3 +166,17 @@ void virStringFreeList(char **strings) } VIR_FREE(strings); } + + +bool +virStringArrayHasString(char **strings, const char *needle) +{ + size_t i = 0; + + while (strings[i]) { + if (STREQ(strings[i++], needle)) + return true; + } + + return false; +} Index: libvirt/src/util/virstring.h =================================================================== --- libvirt.orig/src/util/virstring.h +++ libvirt/src/util/virstring.h @@ -35,4 +35,6 @@ char *virStringJoin(const char **strings void virStringFreeList(char **strings); +bool virStringArrayHasString(char **strings, const char *needle); + #endif /* __VIR_STRING_H__ */ Index: libvirt/src/libvirt_private.syms =================================================================== --- libvirt.orig/src/libvirt_private.syms +++ libvirt/src/libvirt_private.syms @@ -1720,6 +1720,7 @@ virStorageFileResize; # util/virstring.h +virStringArrayHasString; virStringFreeList; virStringJoin; virStringSplit;

On Fri, Apr 05, 2013 at 10:05:53AM -0400, Stefan Berger wrote:
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- src/libvirt_private.syms | 1 + src/util/virstring.c | 14 ++++++++++++++ src/util/virstring.h | 2 ++ 3 files changed, 17 insertions(+)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Supported TPM passthrough XML may look as follows: <tpm model='tpm-tis'> <backend type='passthrough'> <device path='/dev/tpm0'/> </backend> </tpm> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- docs/formatdomain.html.in | 58 ++++++++++++++++++++++++++++++++++++++++++ docs/schemas/domaincommon.rng | 43 +++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) Index: libvirt/docs/formatdomain.html.in =================================================================== --- libvirt.orig/docs/formatdomain.html.in +++ libvirt/docs/formatdomain.html.in @@ -4385,6 +4385,64 @@ qemu-kvm -net nic,model=? /dev/null </dl> + <h4><a name="elementsTpm">TPM device</a></h4> + + <p> + The TPM device enables a QEMU guest to have access to TPM + functionality. + </p> + <p> + The TPM passthrough device type provides access to the host's TPM + for one QEMU guest. No other software may be is using the TPM device, + typically /dev/tpm0, at the time the QEMU guest is started. + <span class="since">'passthrough' since 1.0.5</span> + </p> + + <p> + Example: usage of the TPM passthrough device + </p> +<pre> + ... + <devices> + <tpm model='tpm-tis'> + <backend type='passthrough'> + <backend path='/dev/tpm0'/> + </backend> + </tpm> + </devices> + ... +</pre> + <dl> + <dt><code>model</code></dt> + <dd> + <p> + The <code>model</code> attribute specifies what device + model QEMU provides to the guest. If no model name is provided, + <code>tpm-tis</code> will automatically be chosen. + </p> + </dd> + <dt><code>backend</code></dt> + <dd> + <p> + The <code>backend</code> element specifies the type of + TPM device. The following types are supported: + </p> + <ul> + <li>'passthrough' — use the host's TPM device. + </ul> + </dd> + <dt><code>backend type='passthrough'</code></dt> + <dd> + <p> + This backend type requires exclusive access to a TPM device on + the host. + An example for such a device is /dev/tpm0. The filename is + specified as path attribute of the <code>source</code> element. + If no file name is specified then /dev/tpm0 is automatically used. + </p> + </dd> + </dl> + <h3><a name="seclabel">Security label</a></h3> <p> Index: libvirt/docs/schemas/domaincommon.rng =================================================================== --- libvirt.orig/docs/schemas/domaincommon.rng +++ libvirt/docs/schemas/domaincommon.rng @@ -2847,6 +2847,48 @@ <text/> </element> </define> + + <define name="tpm"> + <element name="tpm"> + <optional> + <attribute name="model"> + <choice> + <value>tpm-tis</value> + </choice> + </attribute> + </optional> + <ref name="tpm-backend"/> + <optional> + <ref name="alias"/> + </optional> + </element> + </define> + + <define name="tpm-backend"> + <element name="backend"> + <choice> + <group> + <attribute name="type"> + <value>passthrough</value> + </attribute> + <ref name="tpm-passthrough-device"/> + </group> + </choice> + </element> + </define> + + <define name="tpm-passthrough-device"> + <optional> + <element name="device"> + <optional> + <attribute name="path"> + <ref name="filePath"/> + </attribute> + </optional> + </element> + </optional> + </define> + <define name="input"> <element name="input"> <attribute name="type"> @@ -3157,6 +3199,7 @@ <ref name="redirdev"/> <ref name="redirfilter"/> <ref name="rng"/> + <ref name="tpm"/> </choice> </zeroOrMore> <optional>

On Fri, Apr 05, 2013 at 10:05:54AM -0400, Stefan Berger wrote:
Supported TPM passthrough XML may look as follows:
<tpm model='tpm-tis'> <backend type='passthrough'> <device path='/dev/tpm0'/> </backend> </tpm>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- docs/formatdomain.html.in | 58 ++++++++++++++++++++++++++++++++++++++++++ docs/schemas/domaincommon.rng | 43 +++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+)
For future reference, I recommend that the docs + schema changes go in the same patch as the parser code which introduces support for the syntax. ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Implement helper functions to find the TPM's sysfs cancel file. Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- po/POTFILES.in | 1 src/Makefile.am | 1 src/libvirt_private.syms | 4 + src/util/virtpm.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virtpm.h | 27 +++++++++++ 5 files changed, 144 insertions(+) Index: libvirt/src/Makefile.am =================================================================== --- libvirt.orig/src/Makefile.am +++ libvirt/src/Makefile.am @@ -122,6 +122,7 @@ UTIL_SOURCES = \ util/virthreadwin32.h \ util/virthreadpool.c util/virthreadpool.h \ util/virtime.h util/virtime.c \ + util/virtpm.h util/virtpm.c \ util/virtypedparam.c util/virtypedparam.h \ util/virusb.c util/virusb.h \ util/viruri.h util/viruri.c \ Index: libvirt/src/util/virtpm.c =================================================================== --- /dev/null +++ libvirt/src/util/virtpm.c @@ -0,0 +1,111 @@ +/* + * virtpm.c: TPM support + * + * Copyright (C) 2013 IBM Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Stefan Berger <stefanb@linux.vnet.ibm.com> + */ + +#include <config.h> + +#include <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "virobject.h" +#include "viralloc.h" +#include "virutil.h" +#include "virerror.h" +#include "virbuffer.h" +#include "virtpm.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +/* + * Check whether the given base path, e.g., /sys/class/misc/tpm0/device, + * is the sysfs entry of a TPM. A TPM sysfs entry should be uniquely + * recognizable by the file entries 'pcrs' and 'cancel'. + * Upon success the basepath with '/cancel' appended is returned, NULL + * otherwise. + */ +static char * +virTPMCheckSysfsCancel(char *basepath) +{ + char *path = NULL; + struct stat statbuf; + + if (virAsprintf(&path, "%s/pcrs", basepath) < 0) { + virReportOOMError(); + goto cleanup; + } + if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) + goto cleanup; + + VIR_FREE(path); + + if (virAsprintf(&path, "%s/cancel", basepath) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) + goto cleanup; + + return path; + +cleanup: + VIR_FREE(path); + return NULL; +} + + +char * +virTPMFindCancelPath(void) +{ + unsigned int idx; + int len; + DIR *pnp_dir; + char *path = NULL, *basepath = NULL; + struct dirent entry, *result; + + pnp_dir = opendir("/sys/class/misc"); + if (pnp_dir != NULL) { + while (readdir_r(pnp_dir, &entry, &result) == 0 && + result != NULL) { + if (sscanf(entry.d_name, "tpm%u%n", &idx, &len) < 1 || + len <= strlen("tpm") || + len != strlen(entry.d_name)) { + continue; + } + if (virAsprintf(&basepath, "/sys/class/misc/%s/device", + entry.d_name) < 0) { + virReportOOMError(); + break; + } + if ((path = virTPMCheckSysfsCancel(basepath))) + break; + + VIR_FREE(basepath); + } + closedir(pnp_dir); + } + + VIR_FREE(basepath); + + return path; +} Index: libvirt/src/libvirt_private.syms =================================================================== --- libvirt.orig/src/libvirt_private.syms +++ libvirt/src/libvirt_private.syms @@ -1778,6 +1778,10 @@ virTimeStringThen; virTimeStringThenRaw; +# util/virtpm.h +virTPMFindCancelPath; + + # util/virtypedparam.h virTypedParameterArrayValidate; virTypedParameterAssign; Index: libvirt/src/util/virtpm.h =================================================================== --- /dev/null +++ libvirt/src/util/virtpm.h @@ -0,0 +1,27 @@ +/* + * virtpm.h: TPM support + * + * Copyright (C) 2013 IBM Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Stefan Berger <stefanb@linux.vnet.ibm.com> + */ +#ifndef __VIR_TPM_H__ +# define __VIR_TPM_H__ + +char *virTPMFindCancelPath(void); + +#endif /* __VIR_TPM_H__ */ Index: libvirt/po/POTFILES.in =================================================================== --- libvirt.orig/po/POTFILES.in +++ libvirt/po/POTFILES.in @@ -181,6 +181,7 @@ src/util/virsysinfo.c src/util/virerror.c src/util/virerror.h src/util/virtime.c +src/util/virtpm.c src/util/virtypedparam.c src/util/viruri.c src/util/virusb.c

On Fri, Apr 05, 2013 at 10:05:55AM -0400, Stefan Berger wrote:
Implement helper functions to find the TPM's sysfs cancel file.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- po/POTFILES.in | 1 src/Makefile.am | 1 src/libvirt_private.syms | 4 + src/util/virtpm.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virtpm.h | 27 +++++++++++ 5 files changed, 144 insertions(+)
Index: libvirt/src/Makefile.am =================================================================== --- libvirt.orig/src/Makefile.am +++ libvirt/src/Makefile.am @@ -122,6 +122,7 @@ UTIL_SOURCES = \ util/virthreadwin32.h \ util/virthreadpool.c util/virthreadpool.h \ util/virtime.h util/virtime.c \ + util/virtpm.h util/virtpm.c \ util/virtypedparam.c util/virtypedparam.h \ util/virusb.c util/virusb.h \ util/viruri.h util/viruri.c \ Index: libvirt/src/util/virtpm.c =================================================================== --- /dev/null +++ libvirt/src/util/virtpm.c @@ -0,0 +1,111 @@ +/* + * virtpm.c: TPM support + * + * Copyright (C) 2013 IBM Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Stefan Berger <stefanb@linux.vnet.ibm.com> + */ + +#include <config.h> + +#include <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "virobject.h" +#include "viralloc.h" +#include "virutil.h" +#include "virerror.h" +#include "virbuffer.h" +#include "virtpm.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +/* + * Check whether the given base path, e.g., /sys/class/misc/tpm0/device, + * is the sysfs entry of a TPM. A TPM sysfs entry should be uniquely + * recognizable by the file entries 'pcrs' and 'cancel'. + * Upon success the basepath with '/cancel' appended is returned, NULL + * otherwise. + */ +static char * +virTPMCheckSysfsCancel(char *basepath) +{ + char *path = NULL; + struct stat statbuf; + + if (virAsprintf(&path, "%s/pcrs", basepath) < 0) { + virReportOOMError(); + goto cleanup; + } + if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) + goto cleanup;
This jumps to an error exit path, but without calling virReportError
+ + VIR_FREE(path); + + if (virAsprintf(&path, "%s/cancel", basepath) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) + goto cleanup;
So does this.
+ + return path; + +cleanup: + VIR_FREE(path); + return NULL; +}
All error paths must report errors.
+ + +char * +virTPMFindCancelPath(void) +{ + unsigned int idx; + int len; + DIR *pnp_dir; + char *path = NULL, *basepath = NULL; + struct dirent entry, *result; + + pnp_dir = opendir("/sys/class/misc"); + if (pnp_dir != NULL) {
You should report an error if this fails, not silently ignore it.
+ while (readdir_r(pnp_dir, &entry, &result) == 0 && + result != NULL) {
readdir_r is not required. Standard readdir() is threadsafe when you only have 1 thread using the "DIR *" pointer.
+ if (sscanf(entry.d_name, "tpm%u%n", &idx, &len) < 1 || + len <= strlen("tpm") || + len != strlen(entry.d_name)) { + continue; + }
As a general rule we prefer to avoid use of scanf & friends. I'd do something more like if (!STRPREFIX(entry.d_name, "tpm")) continue; if (virStrToLong_i(entry.d_name + strlen("tpm"), NULL, &idx) < 0) ...report parsing error...
+ if (virAsprintf(&basepath, "/sys/class/misc/%s/device", + entry.d_name) < 0) { + virReportOOMError(); + break; + } + if ((path = virTPMCheckSysfsCancel(basepath))) + break;
The virTPMCheckSysfsCancel() method will raise an error, but you are silently ignoring errors here. Either clear the error with virResetLastError() if you really need to ignore it, or propagate the error to the caller
+ + VIR_FREE(basepath); + }
When leaving the loop should also report an error if readdir() return an error code, rather than EOF.
+ closedir(pnp_dir); + } + + VIR_FREE(basepath); + + return path; +}
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Parse the domain XML with TPM passthrough support. The TPM passthrough XML may look like this: <tpm model='tpm-tis'> <backend type='passthrough'> <device path='/dev/tpm0'/> </backend> </tpm> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- src/conf/domain_conf.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 33 ++++++++ src/libvirt_private.syms | 5 + 3 files changed, 217 insertions(+) Index: libvirt/src/conf/domain_conf.c =================================================================== --- libvirt.orig/src/conf/domain_conf.c +++ libvirt/src/conf/domain_conf.c @@ -51,6 +51,7 @@ #include "netdev_bandwidth_conf.h" #include "netdev_vlan_conf.h" #include "device_conf.h" +#include "virtpm.h" #define VIR_FROM_THIS VIR_FROM_DOMAIN @@ -719,6 +720,13 @@ VIR_ENUM_IMPL(virDomainRNGBackend, "random", "egd"); +VIR_ENUM_IMPL(virDomainTPMModel, VIR_DOMAIN_TPM_MODEL_LAST, + "tpm-tis") + +VIR_ENUM_IMPL(virDomainTPMBackend, VIR_DOMAIN_TPM_TYPE_LAST, + "passthrough") + + #define VIR_DOMAIN_XML_WRITE_FLAGS VIR_DOMAIN_XML_SECURE #define VIR_DOMAIN_XML_READ_FLAGS VIR_DOMAIN_XML_INACTIVE @@ -1601,6 +1609,23 @@ void virDomainHostdevDefClear(virDomainH } } +void virDomainTPMDefFree(virDomainTPMDefPtr def) +{ + if (!def) + return; + + switch (def->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + VIR_FREE(def->data.passthrough.source.data.file.path); + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + + virDomainDeviceInfoClear(&def->info); + VIR_FREE(def); +} + void virDomainHostdevDefFree(virDomainHostdevDefPtr def) { if (!def) @@ -1862,6 +1887,8 @@ void virDomainDefFree(virDomainDefPtr de virDomainRNGDefFree(def->rng); + virDomainTPMDefFree(def->tpm); + VIR_FREE(def->os.type); VIR_FREE(def->os.machine); VIR_FREE(def->os.init); @@ -6677,6 +6704,103 @@ error: goto cleanup; } +/* Parse the XML definition for a TPM device + * + * The XML looks like this: + * + * <tpm model='tpm-tis'> + * <backend type='passthrough'> + * <device path='/dev/tpm0'/> + * </backend> + * </tpm> + * + */ +static virDomainTPMDefPtr +virDomainTPMDefParseXML(const xmlNodePtr node, + xmlXPathContextPtr ctxt, + unsigned int flags) +{ + char *type = NULL; + char *path = NULL; + char *model = NULL; + char *backend = NULL; + virDomainTPMDefPtr def; + xmlNodePtr save = ctxt->node; + xmlNodePtr *backends = NULL; + int nbackends; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + return NULL; + } + + model = virXMLPropString(node, "model"); + if (model != NULL && + (int)(def->model = virDomainTPMModelTypeFromString(model)) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("Unknown TPM frontend model '%s'"), model); + goto error; + } else { + def->model = VIR_DOMAIN_TPM_MODEL_TIS; + } + + ctxt->node = node; + + if ((nbackends = virXPathNodeSet("./backend", ctxt, &backends)) < 0) + goto error; + + if (nbackends > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("only one TPM backend is supported")); + goto error; + } + + if (!(backend = virXMLPropString(backends[0], "type"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing TPM device backend type")); + goto error; + } + + if ((int)(def->type = virDomainTPMBackendTypeFromString(backend)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Unknown TPM backend type '%s'"), + backend); + goto error; + } + + switch (def->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + path = virXPathString("string(./backend/device/@path)", ctxt); + if (!path && !(path = strdup(VIR_DOMAIN_TPM_DEFAULT_DEVICE))) { + virReportOOMError(); + goto error; + } + def->data.passthrough.source.data.file.path = path; + def->data.passthrough.source.type = VIR_DOMAIN_CHR_TYPE_DEV; + path = NULL; + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + goto error; + } + + if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) + goto error; + +cleanup: + VIR_FREE(type); + VIR_FREE(path); + VIR_FREE(model); + VIR_FREE(backend); + VIR_FREE(backends); + ctxt->node = save; + return def; + +error: + virDomainTPMDefFree(def); + def = NULL; + goto cleanup; +} + /* Parse the XML definition for an input device */ static virDomainInputDefPtr virDomainInputDefParseXML(const char *ostype, @@ -10998,6 +11122,23 @@ virDomainDefParseXML(xmlDocPtr xml, goto error; VIR_FREE(nodes); } + VIR_FREE(nodes); + + /* Parse the TPM devices */ + if ((n = virXPathNodeSet("./devices/tpm", ctxt, &nodes)) < 0) + goto error; + + if (n > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("only a single TPM device is supported")); + goto error; + } + + if (n > 0) { + if (!(def->tpm = virDomainTPMDefParseXML(nodes[0], ctxt, flags))) + goto error; + } + VIR_FREE(nodes); /* analysis of the hub devices */ if ((n = virXPathNodeSet("./devices/hub", ctxt, &nodes)) < 0) { @@ -13945,6 +14086,39 @@ virDomainSoundCodecDefFormat(virBufferPt } static int +virDomainTPMDefFormat(virBufferPtr buf, + virDomainTPMDefPtr def, + unsigned int flags) +{ + virBufferAsprintf(buf, " <tpm model='%s'>\n", + virDomainTPMModelTypeToString(def->model)); + + virBufferAsprintf(buf, " <backend type='%s'>\n", + virDomainTPMBackendTypeToString(def->type)); + + switch (def->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + virBufferEscapeString(buf, " <device path='%s'/>\n", + def->data.passthrough.source.data.file.path); + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + + virBufferAddLit(buf, " </backend>\n"); + + if (virDomainDeviceInfoIsSet(&def->info, flags)) { + if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0) + return -1; + } + + virBufferAddLit(buf, " </tpm>\n"); + + return 0; +} + + +static int virDomainSoundDefFormat(virBufferPtr buf, virDomainSoundDefPtr def, unsigned int flags) @@ -15270,6 +15444,11 @@ virDomainDefFormatInternal(virDomainDefP virDomainInputDefFormat(buf, def->inputs[n], flags) < 0) goto error; + if (def->tpm) { + if (virDomainTPMDefFormat(buf, def->tpm, flags) < 0) + goto error; + } + if (def->ngraphics > 0) { /* If graphics is enabled, add the implicit mouse */ virDomainInputDef autoInput = { Index: libvirt/src/conf/domain_conf.h =================================================================== --- libvirt.orig/src/conf/domain_conf.h +++ libvirt/src/conf/domain_conf.h @@ -1082,6 +1082,33 @@ struct _virDomainHubDef { virDomainDeviceInfo info; }; +enum virDomainTPMModel { + VIR_DOMAIN_TPM_MODEL_TIS, + + VIR_DOMAIN_TPM_MODEL_LAST +}; + +enum virDomainTPMBackendType { + VIR_DOMAIN_TPM_TYPE_PASSTHROUGH, + + VIR_DOMAIN_TPM_TYPE_LAST +}; + +# define VIR_DOMAIN_TPM_DEFAULT_DEVICE "/dev/tpm0" + +typedef struct _virDomainTPMDef virDomainTPMDef; +typedef virDomainTPMDef *virDomainTPMDefPtr; +struct _virDomainTPMDef { + enum virDomainTPMBackendType type; + virDomainDeviceInfo info; + enum virDomainTPMModel model; + union { + struct { + virDomainChrSourceDef source; + } passthrough; + } data; +}; + enum virDomainInputType { VIR_DOMAIN_INPUT_TYPE_MOUSE, VIR_DOMAIN_INPUT_TYPE_TABLET, @@ -1868,6 +1895,7 @@ struct _virDomainDef { /* Only 1 */ virDomainWatchdogDefPtr watchdog; virDomainMemballoonDefPtr memballoon; + virDomainTPMDefPtr tpm; virCPUDefPtr cpu; virSysinfoDefPtr sysinfo; virDomainRedirFilterDefPtr redirfilter; @@ -2047,6 +2075,9 @@ void virDomainDeviceInfoClear(virDomainD void virDomainDefClearPCIAddresses(virDomainDefPtr def); void virDomainDefClearCCWAddresses(virDomainDefPtr def); void virDomainDefClearDeviceAliases(virDomainDefPtr def); +void virDomainTPMDefFree(virDomainTPMDefPtr def); +char *virDomainTPMGetPathCopy(virDomainTPMDefPtr def); +char *virDomainTPMGetCancelPath(virDomainTPMDefPtr def); typedef int (*virDomainDeviceInfoCallback)(virDomainDefPtr def, virDomainDeviceDefPtr dev, @@ -2418,6 +2449,8 @@ VIR_ENUM_DECL(virDomainGraphicsSpiceMous VIR_ENUM_DECL(virDomainHyperv) VIR_ENUM_DECL(virDomainRNGModel) VIR_ENUM_DECL(virDomainRNGBackend) +VIR_ENUM_DECL(virDomainTPMModel) +VIR_ENUM_DECL(virDomainTPMBackend) /* from libvirt.h */ VIR_ENUM_DECL(virDomainState) VIR_ENUM_DECL(virDomainNostateReason) Index: libvirt/src/libvirt_private.syms =================================================================== --- libvirt.orig/src/libvirt_private.syms +++ libvirt/src/libvirt_private.syms @@ -320,6 +320,11 @@ virDomainTimerTickpolicyTypeFromString; virDomainTimerTickpolicyTypeToString; virDomainTimerTrackTypeFromString; virDomainTimerTrackTypeToString; +virDomainTPMBackendTypeFromString; +virDomainTPMBackendTypeToString; +virDomainTPMDefFree; +virDomainTPMModelTypeFromString; +virDomainTPMModelTypeToString; virDomainVcpuPinAdd; virDomainVcpuPinDefArrayFree; virDomainVcpuPinDefCopy;

On Fri, Apr 05, 2013 at 10:05:56AM -0400, Stefan Berger wrote:
Parse the domain XML with TPM passthrough support. The TPM passthrough XML may look like this:
<tpm model='tpm-tis'> <backend type='passthrough'> <device path='/dev/tpm0'/> </backend> </tpm>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- src/conf/domain_conf.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 33 ++++++++ src/libvirt_private.syms | 5 + 3 files changed, 217 insertions(+)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- src/qemu/qemu_capabilities.c | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) Index: libvirt/src/qemu/qemu_capabilities.c =================================================================== --- libvirt.orig/src/qemu/qemu_capabilities.c +++ libvirt/src/qemu/qemu_capabilities.c @@ -38,6 +38,7 @@ #include "virbitmap.h" #include "virnodesuspend.h" #include "qemu_monitor.h" +#include "virstring.h" #include <fcntl.h> #include <sys/stat.h> @@ -2115,6 +2116,59 @@ virQEMUCapsProbeQMPCPUDefinitions(virQEM static int +virQEMUCapsProbeQMPTPM(virQEMUCapsPtr qemuCaps, + qemuMonitorPtr mon) +{ + int nentries, i; + char **entries = NULL; + struct typeToCaps { + int type; + enum virQEMUCapsFlags caps; + }; + const struct typeToCaps tpmTypesToCaps[] = { + { + .type = VIR_DOMAIN_TPM_TYPE_PASSTHROUGH, + .caps = QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, + }, + }; + const struct typeToCaps tpmModelsToCaps[] = { + { + .type = VIR_DOMAIN_TPM_MODEL_TIS, + .caps = QEMU_CAPS_DEVICE_TPM_TIS, + }, + }; + + if ((nentries = qemuMonitorGetTPMModels(mon, &entries)) < 0) + return -1; + + if (nentries > 0) { + for (i = 0; i < ARRAY_CARDINALITY(tpmModelsToCaps); i++) { + const char *needle = virDomainTPMModelTypeToString( + tpmModelsToCaps[i].type); + if (virStringArrayHasString(entries, needle)) + virQEMUCapsSet(qemuCaps, tpmModelsToCaps[i].caps); + } + } + virStringFreeList(entries); + + if ((nentries = qemuMonitorGetTPMTypes(mon, &entries)) < 0) + return -1; + + if (nentries > 0) { + for (i = 0; i < ARRAY_CARDINALITY(tpmTypesToCaps); i++) { + const char *needle = virDomainTPMBackendTypeToString( + tpmTypesToCaps[i].type); + if (virStringArrayHasString(entries, needle)) + virQEMUCapsSet(qemuCaps, tpmTypesToCaps[i].caps); + } + } + virStringFreeList(entries); + + return 0; +} + + +static int virQEMUCapsProbeQMPKVMState(virQEMUCapsPtr qemuCaps, qemuMonitorPtr mon) { @@ -2467,6 +2521,8 @@ virQEMUCapsInitQMP(virQEMUCapsPtr qemuCa goto cleanup; if (virQEMUCapsProbeQMPKVMState(qemuCaps, mon) < 0) goto cleanup; + if (virQEMUCapsProbeQMPTPM(qemuCaps, mon) < 0) + goto cleanup; ret = 0;

On Fri, Apr 05, 2013 at 10:05:57AM -0400, Stefan Berger wrote:
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- src/qemu/qemu_capabilities.c | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+)
Index: libvirt/src/qemu/qemu_capabilities.c =================================================================== --- libvirt.orig/src/qemu/qemu_capabilities.c +++ libvirt/src/qemu/qemu_capabilities.c @@ -38,6 +38,7 @@ #include "virbitmap.h" #include "virnodesuspend.h" #include "qemu_monitor.h" +#include "virstring.h"
#include <fcntl.h> #include <sys/stat.h> @@ -2115,6 +2116,59 @@ virQEMUCapsProbeQMPCPUDefinitions(virQEM
static int +virQEMUCapsProbeQMPTPM(virQEMUCapsPtr qemuCaps, + qemuMonitorPtr mon) +{ + int nentries, i; + char **entries = NULL; + struct typeToCaps { + int type; + enum virQEMUCapsFlags caps; + }; + const struct typeToCaps tpmTypesToCaps[] = { + { + .type = VIR_DOMAIN_TPM_TYPE_PASSTHROUGH, + .caps = QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, + }, + }; + const struct typeToCaps tpmModelsToCaps[] = { + { + .type = VIR_DOMAIN_TPM_MODEL_TIS, + .caps = QEMU_CAPS_DEVICE_TPM_TIS, + }, + };
Nit-picking, I'd move these 2 outside the method and make them static, as we have for other data tables in this file.
+ + if ((nentries = qemuMonitorGetTPMModels(mon, &entries)) < 0) + return -1; + + if (nentries > 0) { + for (i = 0; i < ARRAY_CARDINALITY(tpmModelsToCaps); i++) { + const char *needle = virDomainTPMModelTypeToString( + tpmModelsToCaps[i].type); + if (virStringArrayHasString(entries, needle)) + virQEMUCapsSet(qemuCaps, tpmModelsToCaps[i].caps); + } + } + virStringFreeList(entries); + + if ((nentries = qemuMonitorGetTPMTypes(mon, &entries)) < 0) + return -1; + + if (nentries > 0) { + for (i = 0; i < ARRAY_CARDINALITY(tpmTypesToCaps); i++) { + const char *needle = virDomainTPMBackendTypeToString( + tpmTypesToCaps[i].type); + if (virStringArrayHasString(entries, needle)) + virQEMUCapsSet(qemuCaps, tpmTypesToCaps[i].caps); + } + } + virStringFreeList(entries); + + return 0; +} + + +static int virQEMUCapsProbeQMPKVMState(virQEMUCapsPtr qemuCaps, qemuMonitorPtr mon) { @@ -2467,6 +2521,8 @@ virQEMUCapsInitQMP(virQEMUCapsPtr qemuCa goto cleanup; if (virQEMUCapsProbeQMPKVMState(qemuCaps, mon) < 0) goto cleanup; + if (virQEMUCapsProbeQMPTPM(qemuCaps, mon) < 0) + goto cleanup;
ret = 0;
ACK regardless Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- src/security/security_dac.c | 53 ++++++++++++++++++++++ src/security/security_selinux.c | 96 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) Index: libvirt/src/security/security_selinux.c =================================================================== --- libvirt.orig/src/security/security_selinux.c +++ libvirt/src/security/security_selinux.c @@ -45,6 +45,7 @@ #include "virrandom.h" #include "virutil.h" #include "virconf.h" +#include "virtpm.h" #define VIR_FROM_THIS VIR_FROM_SECURITY @@ -76,6 +77,12 @@ struct _virSecuritySELinuxCallbackData { #define SECURITY_SELINUX_VOID_DOI "0" #define SECURITY_SELINUX_NAME "selinux" +static int +virSecuritySELinuxRestoreSecurityTPMFileLabelInt(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainTPMDefPtr tpm); + + /* * Returns 0 on success, 1 if already reserved, or -1 on fatal error */ @@ -1062,6 +1069,84 @@ err: return rc; } + +static int +virSecuritySELinuxSetSecurityTPMFileLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainTPMDefPtr tpm) +{ + int rc; + virSecurityLabelDefPtr seclabel; + char *cancel_path; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (seclabel == NULL) + return -1; + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + rc = virSecuritySELinuxSetFilecon( + tpm->data.passthrough.source.data.file.path, + seclabel->imagelabel); + if (rc < 0) + return -1; + + if ((cancel_path = virTPMFindCancelPath()) != NULL) { + rc = virSecuritySELinuxSetFilecon(cancel_path, + seclabel->imagelabel); + VIR_FREE(cancel_path); + if (rc < 0) { + virSecuritySELinuxRestoreSecurityTPMFileLabelInt(mgr, def, + tpm); + return -1; + } + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot determine TPM command cancel path")); + return -1; + } + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + + return 0; +} + + +static int +virSecuritySELinuxRestoreSecurityTPMFileLabelInt(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainTPMDefPtr tpm) +{ + int rc = 0; + virSecurityLabelDefPtr seclabel; + char *cancel_path; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (seclabel == NULL) + return -1; + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + rc = virSecuritySELinuxRestoreSecurityFileLabel( + mgr, tpm->data.passthrough.source.data.file.path); + + if ((cancel_path = virTPMFindCancelPath()) != NULL) { + if (virSecuritySELinuxRestoreSecurityFileLabel(mgr, + cancel_path) < 0) + rc = -1; + VIR_FREE(cancel_path); + } + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + + return rc; +} + + static int virSecuritySELinuxRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr, virDomainDefPtr def, @@ -1734,6 +1819,12 @@ virSecuritySELinuxRestoreSecurityAllLabe if (secdef->norelabel || data->skipAllLabel) return 0; + if (def->tpm) { + if (virSecuritySELinuxRestoreSecurityTPMFileLabelInt(mgr, def, + def->tpm) < 0) + rc = -1; + } + for (i = 0 ; i < def->nhostdevs ; i++) { if (virSecuritySELinuxRestoreSecurityHostdevLabel(mgr, def, @@ -2148,6 +2239,11 @@ virSecuritySELinuxSetSecurityAllLabel(vi NULL) < 0) return -1; } + if (def->tpm) { + if (virSecuritySELinuxSetSecurityTPMFileLabel(mgr, def, + def->tpm) < 0) + return -1; + } if (virDomainChrDefForeach(def, true, Index: libvirt/src/security/security_dac.c =================================================================== --- libvirt.orig/src/security/security_dac.c +++ libvirt/src/security/security_dac.c @@ -716,6 +716,46 @@ virSecurityDACRestoreChardevCallback(vir static int +virSecurityDACSetSecurityTPMFileLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainTPMDefPtr tpm) +{ + int ret = 0; + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + ret = virSecurityDACSetChardevLabel(mgr, def, + &tpm->data.passthrough.source); + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + + return ret; +} + + +static int +virSecurityDACRestoreSecurityTPMFileLabel( + virSecurityManagerPtr mgr, + virDomainTPMDefPtr tpm) +{ + int ret = 0; + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + ret = virSecurityDACRestoreChardevLabel(mgr, + &tpm->data.passthrough.source); + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + + return ret; +} + + +static int virSecurityDACRestoreSecurityAllLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, int migrated) @@ -752,6 +792,12 @@ virSecurityDACRestoreSecurityAllLabel(vi mgr) < 0) rc = -1; + if (def->tpm) { + if (virSecurityDACRestoreSecurityTPMFileLabel(mgr, + def->tpm) < 0) + rc = -1; + } + if (def->os.kernel && virSecurityDACRestoreSecurityFileLabel(def->os.kernel) < 0) rc = -1; @@ -815,6 +861,13 @@ virSecurityDACSetSecurityAllLabel(virSec mgr) < 0) return -1; + if (def->tpm) { + if (virSecurityDACSetSecurityTPMFileLabel(mgr, + def, + def->tpm) < 0) + return -1; + } + if (virSecurityDACGetImageIds(def, priv, &user, &group)) return -1;

On Fri, Apr 05, 2013 at 10:05:58AM -0400, Stefan Berger wrote:
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- src/security/security_dac.c | 53 ++++++++++++++++++++++ src/security/security_selinux.c | 96 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+)
Index: libvirt/src/security/security_selinux.c =================================================================== --- libvirt.orig/src/security/security_selinux.c +++ libvirt/src/security/security_selinux.c @@ -45,6 +45,7 @@ #include "virrandom.h" #include "virutil.h" #include "virconf.h" +#include "virtpm.h"
#define VIR_FROM_THIS VIR_FROM_SECURITY
@@ -76,6 +77,12 @@ struct _virSecuritySELinuxCallbackData { #define SECURITY_SELINUX_VOID_DOI "0" #define SECURITY_SELINUX_NAME "selinux"
+static int +virSecuritySELinuxRestoreSecurityTPMFileLabelInt(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainTPMDefPtr tpm); + + /* * Returns 0 on success, 1 if already reserved, or -1 on fatal error */ @@ -1062,6 +1069,84 @@ err: return rc; }
+ +static int +virSecuritySELinuxSetSecurityTPMFileLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainTPMDefPtr tpm) +{ + int rc; + virSecurityLabelDefPtr seclabel; + char *cancel_path; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (seclabel == NULL) + return -1; + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + rc = virSecuritySELinuxSetFilecon( + tpm->data.passthrough.source.data.file.path, + seclabel->imagelabel); + if (rc < 0) + return -1; + + if ((cancel_path = virTPMFindCancelPath()) != NULL) { + rc = virSecuritySELinuxSetFilecon(cancel_path, + seclabel->imagelabel); + VIR_FREE(cancel_path); + if (rc < 0) { + virSecuritySELinuxRestoreSecurityTPMFileLabelInt(mgr, def, + tpm); + return -1; + } + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot determine TPM command cancel path"));
The virTPMFindCancelPath method has already reported an error, so you shouldn't overwrite it here with a less specific message.
+static int +virSecuritySELinuxRestoreSecurityTPMFileLabelInt(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainTPMDefPtr tpm) +{ + int rc = 0; + virSecurityLabelDefPtr seclabel; + char *cancel_path; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (seclabel == NULL) + return -1; + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + rc = virSecuritySELinuxRestoreSecurityFileLabel( + mgr, tpm->data.passthrough.source.data.file.path); + + if ((cancel_path = virTPMFindCancelPath()) != NULL) { + if (virSecuritySELinuxRestoreSecurityFileLabel(mgr, + cancel_path) < 0) + rc = -1; + VIR_FREE(cancel_path); + }
Since we want to ignore errors, we should call virResetLastError() if virTPMFindCancelPath fails. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

When a VM with a TPM passthrough device is started, the audit daemon logs the following type of message: type=VIRT_RESOURCE msg=audit(1365170222.460:3378): pid=16382 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:virtd_t:s0-s0:c0.c1023 msg='virt=kvm resrc=dev reason=start vm="TPM-PT" uuid=a4d7cd22-da89-3094-6212-079a48a309a1 device="/dev/tpm0" exe="/usr/sbin/libvirtd" hostname=? addr=? terminal=? res=success' Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- src/conf/domain_audit.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) Index: libvirt/src/conf/domain_audit.c =================================================================== --- libvirt.orig/src/conf/domain_audit.c +++ libvirt/src/conf/domain_audit.c @@ -524,6 +524,58 @@ cleanup: /** + * virDomainAuditTPM: + * @vm: domain making a change in pass-through host device + * @tpm: TPM device being attached or removed + * @reason: one of "start", "attach", or "detach" + * @success: true if the device passthrough operation succeeded + * + * Log an audit message about an attempted device passthrough change. + */ +static void +virDomainAuditTPM(virDomainObjPtr vm, virDomainTPMDefPtr tpm, + const char *reason, bool success) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *vmname; + char *path = NULL; + char *device = NULL; + const char *virt; + + virUUIDFormat(vm->def->uuid, uuidstr); + if (!(vmname = virAuditEncode("vm", vm->def->name))) { + VIR_WARN("OOM while encoding audit message"); + return; + } + + if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) { + VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType); + virt = "?"; + } + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + path = tpm->data.passthrough.source.data.file.path; + if (!(device = virAuditEncode("device", VIR_AUDIT_STR(path)))) { + VIR_WARN("OOM while encoding audit message"); + goto cleanup; + } + + VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, + "virt=%s resrc=dev reason=%s %s uuid=%s %s", + virt, reason, vmname, uuidstr, device); + break; + default: + break; + } + +cleanup: + VIR_FREE(vmname); + VIR_FREE(device); +} + + +/** * virDomainAuditCgroup: * @vm: domain making the cgroups ACL change * @cgroup: cgroup that manages the devices @@ -761,6 +813,9 @@ virDomainAuditStart(virDomainObjPtr vm, if (vm->def->rng) virDomainAuditRNG(vm, vm->def->rng, NULL, "start", true); + if (vm->def->tpm) + virDomainAuditTPM(vm, vm->def->tpm, "start", true); + virDomainAuditMemory(vm, 0, vm->def->mem.cur_balloon, "start", true); virDomainAuditVcpu(vm, 0, vm->def->vcpus, "start", true);

On Fri, Apr 05, 2013 at 10:05:59AM -0400, Stefan Berger wrote:
When a VM with a TPM passthrough device is started, the audit daemon logs the following type of message:
type=VIRT_RESOURCE msg=audit(1365170222.460:3378): pid=16382 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:virtd_t:s0-s0:c0.c1023 msg='virt=kvm resrc=dev reason=start vm="TPM-PT" uuid=a4d7cd22-da89-3094-6212-079a48a309a1 device="/dev/tpm0" exe="/usr/sbin/libvirtd" hostname=? addr=? terminal=? res=success'
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- src/conf/domain_audit.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Some refactoring for virDomainChrSourceDef type of devices so we can use common code. Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- src/qemu/qemu_cgroup.c | 53 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-) Index: libvirt/src/qemu/qemu_cgroup.c =================================================================== --- libvirt.orig/src/qemu/qemu_cgroup.c +++ libvirt/src/qemu/qemu_cgroup.c @@ -142,34 +142,62 @@ int qemuTeardownDiskCgroup(virDomainObjP &data); } - static int -qemuSetupChardevCgroup(virDomainDefPtr def, - virDomainChrDefPtr dev, - void *opaque) +qemuSetupChrSourceCgroup(virDomainDefPtr def, + virDomainChrSourceDefPtr dev, + qemuCgroupData *data) { - qemuCgroupData *data = opaque; int rc; - if (dev->source.type != VIR_DOMAIN_CHR_TYPE_DEV) + if (dev->type != VIR_DOMAIN_CHR_TYPE_DEV) return 0; + VIR_DEBUG("Process path '%s' for device", dev->data.file.path); - VIR_DEBUG("Process path '%s' for disk", dev->source.data.file.path); - rc = virCgroupAllowDevicePath(data->cgroup, dev->source.data.file.path, + rc = virCgroupAllowDevicePath(data->cgroup, dev->data.file.path, VIR_CGROUP_DEVICE_RW); virDomainAuditCgroupPath(data->vm, data->cgroup, "allow", - dev->source.data.file.path, "rw", rc); + dev->data.file.path, "rw", rc); if (rc < 0) { virReportSystemError(-rc, _("Unable to allow device %s for %s"), - dev->source.data.file.path, def->name); + dev->data.file.path, def->name); return -1; } return 0; } +static int +qemuSetupChardevCgroup(virDomainDefPtr def, + virDomainChrDefPtr dev, + void *opaque) +{ + qemuCgroupData *data = opaque; + + return qemuSetupChrSourceCgroup(def, &dev->source, data); +} + + +static int +qemuSetupTPMCgroup(virDomainDefPtr def, + virDomainTPMDefPtr dev, + qemuCgroupData *data) +{ + int rc = 0; + + switch (dev->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + rc = qemuSetupChrSourceCgroup(def, &dev->data.passthrough.source, + data); + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + + return rc; +} + int qemuSetupHostUsbDeviceCgroup(virUSBDevicePtr dev ATTRIBUTE_UNUSED, const char *path, @@ -287,6 +315,11 @@ int qemuSetupCgroup(virQEMUDriverPtr dri &data) < 0) goto cleanup; + if (vm->def->tpm) + qemuSetupTPMCgroup(vm->def, + vm->def->tpm, + &data); + for (i = 0; i < vm->def->nhostdevs; i++) { virDomainHostdevDefPtr hostdev = vm->def->hostdevs[i]; virUSBDevicePtr usb;

On Fri, Apr 05, 2013 at 10:06:00AM -0400, Stefan Berger wrote:
Some refactoring for virDomainChrSourceDef type of devices so we can use common code.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- src/qemu/qemu_cgroup.c | 53 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

For TPM passthrough device support create command line parameters like: -tpmdev passthrough,id=tpm-tpm0,path=/dev/tpm0,cancel-path=/sys/class/misc/tpm0/device/cancel -device tpm-tis,tpmdev=tpm-tpm0,id=tpm0 Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- src/qemu/qemu_command.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) Index: libvirt/src/qemu/qemu_command.c =================================================================== --- libvirt.orig/src/qemu/qemu_command.c +++ libvirt/src/qemu/qemu_command.c @@ -46,6 +46,7 @@ #include "base64.h" #include "device_conf.h" #include "virstoragefile.h" +#include "virtpm.h" #include <sys/stat.h> #include <fcntl.h> @@ -799,6 +800,10 @@ qemuAssignDeviceAliases(virDomainDefPtr if (virAsprintf(&def->rng->info.alias, "rng%d", 0) < 0) goto no_memory; } + if (def->tpm) { + if (virAsprintf(&def->tpm->info.alias, "tpm%d", 0) < 0) + goto no_memory; + } return 0; @@ -4791,6 +4796,92 @@ cleanup: } +static char *qemuBuildTPMBackendStr(const virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + const char *emulator) +{ + const virDomainTPMDefPtr tpm = def->tpm; + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *type = virDomainTPMBackendTypeToString(tpm->type); + const char *cancel_path; + + virBufferAsprintf(&buf, "%s,id=tpm-%s", type, tpm->info.alias); + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_PASSTHROUGH)) + goto no_support; + + virBufferAddLit(&buf, ",path="); + virBufferEscape(&buf, ',', ",", "%s", + tpm->data.passthrough.source.data.file.path); + + if (!(cancel_path = virTPMFindCancelPath())) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("TPM cancel path could not be determined")); + goto error; + } + + virBufferAddLit(&buf, ",cancel-path="); + virBufferEscape(&buf, ',', ",", "%s", cancel_path); + VIR_FREE(cancel_path); + + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + goto error; + } + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + + no_support: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("The QEMU executable %s does not support TPM " + "backend type %s"), + emulator, type); + + error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +static char *qemuBuildTPMDevStr(const virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + const char *emulator) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + const virDomainTPMDefPtr tpm = def->tpm; + const char *model = virDomainTPMModelTypeToString(tpm->model); + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_TIS)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("The QEMU executable %s does not support TPM " + "model %s"), + emulator, model); + goto error; + } + + virBufferAsprintf(&buf, "%s,tpmdev=tpm-%s,id=%s", + model, tpm->info.alias, tpm->info.alias); + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + + error: + virBufferFreeAndReset(&buf); + return NULL; +} + + static char *qemuBuildSmbiosBiosStr(virSysinfoDefPtr def) { virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -7101,6 +7192,22 @@ qemuBuildCommandLine(virConnectPtr conn, } } + if (def->tpm) { + char *optstr; + + if (!(optstr = qemuBuildTPMBackendStr(def, qemuCaps, emulator))) + goto error; + + virCommandAddArgList(cmd, "-tpmdev", optstr, NULL); + VIR_FREE(optstr); + + if (!(optstr = qemuBuildTPMDevStr(def, qemuCaps, emulator))) + goto error; + + virCommandAddArgList(cmd, "-device", optstr, NULL); + VIR_FREE(optstr); + } + for (i = 0 ; i < def->ninputs ; i++) { virDomainInputDefPtr input = def->inputs[i]; @@ -8934,6 +9041,112 @@ error: static int +qemuParseCommandLineTPM(virDomainDefPtr dom, + const char *val) +{ + int rc = 0; + virDomainTPMDefPtr tpm; + char **keywords; + char **values; + int nkeywords; + int i; + + if (dom->tpm) + goto error; + + nkeywords = qemuParseKeywords(val, &keywords, &values, 1); + if (nkeywords < 0) + goto error; + + if (VIR_ALLOC(tpm) < 0) + goto no_memory; + + tpm->model = VIR_DOMAIN_TPM_MODEL_TIS; + + for (i = 0; i < nkeywords; i++) { + if (STREQ(keywords[i], "type")) { + if (values[i] && + STREQ(values[i], + virDomainTPMBackendTypeToString(VIR_DOMAIN_TPM_TYPE_PASSTHROUGH))) + tpm->type = VIR_DOMAIN_TPM_TYPE_PASSTHROUGH; + } else if (STREQ(keywords[i], + virDomainTPMBackendTypeToString(VIR_DOMAIN_TPM_TYPE_PASSTHROUGH))) { + tpm->type = VIR_DOMAIN_TPM_TYPE_PASSTHROUGH; + } else if (STREQ(keywords[i], "path")) { + if (values[i]) { + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + tpm->data.passthrough.source.data.file.path = values[i]; + values[i] = NULL; + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + } else { + goto syntax; + } + } + } + + /* sanity checks */ + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + if (!tpm->data.passthrough.source.data.file.path) { + if (!(tpm->data.passthrough.source.data.file.path = + strdup(VIR_DOMAIN_TPM_DEFAULT_DEVICE))) { + virReportOOMError(); + goto bad_definition; + } + } + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", + _("unknown TPM type")); + goto bad_definition; + } + + /* all ok */ + dom->tpm = tpm; + +cleanup: + for (i = 0 ; i < nkeywords ; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + + + return rc; + +syntax: + virDomainTPMDefFree(tpm); + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown TPM syntax '%s'"), val); + rc = -1; + goto cleanup; + +bad_definition: + virDomainTPMDefFree(tpm); + + rc = -1; + goto cleanup; + +no_memory: + virReportOOMError(); + + rc = -1; + goto cleanup; + + +error: + return -1; +} + + +static int qemuParseCommandLineSmp(virDomainDefPtr dom, const char *val) { @@ -9738,6 +9951,10 @@ virDomainDefPtr qemuParseCommandLine(vir } else if (STREQ(arg, "-S")) { /* ignore, always added by libvirt */ + } else if (STREQ(arg, "-tpmdev")) { + WANT_VALUE(); + if (qemuParseCommandLineTPM(def, val) < 0) + goto error; } else { /* something we can't yet parse. Add it to the qemu namespace * cmdline/environment advanced options and hope for the best

On Fri, Apr 05, 2013 at 10:06:01AM -0400, Stefan Berger wrote:
For TPM passthrough device support create command line parameters like:
-tpmdev passthrough,id=tpm-tpm0,path=/dev/tpm0,cancel-path=/sys/class/misc/tpm0/device/cancel -device tpm-tis,tpmdev=tpm-tpm0,id=tpm0
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- src/qemu/qemu_command.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+)
Index: libvirt/src/qemu/qemu_command.c =================================================================== --- libvirt.orig/src/qemu/qemu_command.c +++ libvirt/src/qemu/qemu_command.c @@ -46,6 +46,7 @@ #include "base64.h" #include "device_conf.h" #include "virstoragefile.h" +#include "virtpm.h"
#include <sys/stat.h> #include <fcntl.h> @@ -799,6 +800,10 @@ qemuAssignDeviceAliases(virDomainDefPtr if (virAsprintf(&def->rng->info.alias, "rng%d", 0) < 0) goto no_memory; } + if (def->tpm) { + if (virAsprintf(&def->tpm->info.alias, "tpm%d", 0) < 0) + goto no_memory; + }
return 0;
@@ -4791,6 +4796,92 @@ cleanup: }
+static char *qemuBuildTPMBackendStr(const virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + const char *emulator) +{ + const virDomainTPMDefPtr tpm = def->tpm; + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *type = virDomainTPMBackendTypeToString(tpm->type); + const char *cancel_path; + + virBufferAsprintf(&buf, "%s,id=tpm-%s", type, tpm->info.alias); + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_PASSTHROUGH)) + goto no_support; + + virBufferAddLit(&buf, ",path="); + virBufferEscape(&buf, ',', ",", "%s", + tpm->data.passthrough.source.data.file.path); + + if (!(cancel_path = virTPMFindCancelPath())) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("TPM cancel path could not be determined"));
Again, don't overwrite errors raised by virTPMFindCancelPath().
+ goto error; + } + + virBufferAddLit(&buf, ",cancel-path="); + virBufferEscape(&buf, ',', ",", "%s", cancel_path); + VIR_FREE(cancel_path); + + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + goto error; + } + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + + no_support: + virReportError(VIR_ERR_INTERNAL_ERROR,
s/INTERNAL_ERROR/CONFIG_UNSUPPORTED/
+ _("The QEMU executable %s does not support TPM " + "backend type %s"), + emulator, type); + + error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +static char *qemuBuildTPMDevStr(const virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + const char *emulator) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + const virDomainTPMDefPtr tpm = def->tpm; + const char *model = virDomainTPMModelTypeToString(tpm->model); + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_TIS)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("The QEMU executable %s does not support TPM " + "model %s"), + emulator, model);
s/INTERNAL_ERROR/CONFIG_UNSUPPORTED/ Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com> --- tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.args | 6 +++ tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.xml | 29 +++++++++++++++ tests/qemuxml2argvtest.c | 3 + tests/qemuxml2xmltest.c | 2 + 4 files changed, 40 insertions(+) Index: libvirt/tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.args =================================================================== --- /dev/null +++ libvirt/tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.args @@ -0,0 +1,6 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test \ +/usr/bin/qemu -S -M pc-0.12 -m 2048 -smp 1 -nographic -nodefaults \ +-monitor unix:/tmp/test-monitor,server,nowait -boot c -usb \ +-tpmdev passthrough,id=tpm-tpm0,path=/dev/tpm0,cancel-path=/sys/class/misc/tpm0/device/cancel \ +-device tpm-tis,tpmdev=tpm-tpm0,id=tpm0 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 Index: libvirt/tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.xml =================================================================== --- /dev/null +++ libvirt/tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.xml @@ -0,0 +1,29 @@ +<domain type='qemu'> + <name>TPM-VM</name> + <uuid>11d7cd22-da89-3094-6212-079a48a309a1</uuid> + <memory unit='KiB'>2097152</memory> + <currentMemory unit='KiB'>512288</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc-0.12'>hvm</type> + <boot dev='hd'/> + <bootmenu enable='yes'/> + </os> + <features> + <acpi/> + </features> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'/> + <tpm model='tpm-tis'> + <backend type='passthrough'> + <device path='/dev/tpm0'/> + </backend> + </tpm> + <memballoon model='virtio'/> + </devices> +</domain> Index: libvirt/tests/qemuxml2argvtest.c =================================================================== --- libvirt.orig/tests/qemuxml2argvtest.c +++ libvirt/tests/qemuxml2argvtest.c @@ -932,6 +932,9 @@ mymain(void) DO_TEST("ppc-dtb", QEMU_CAPS_KVM, QEMU_CAPS_DTB); + DO_TEST("tpm-passthrough", QEMU_CAPS_DEVICE, + QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, QEMU_CAPS_DEVICE_TPM_TIS); + virObjectUnref(driver.config); virObjectUnref(driver.caps); virObjectUnref(driver.xmlopt); Index: libvirt/tests/qemuxml2xmltest.c =================================================================== --- libvirt.orig/tests/qemuxml2xmltest.c +++ libvirt/tests/qemuxml2xmltest.c @@ -272,6 +272,8 @@ mymain(void) DO_TEST_DIFFERENT("metadata"); + DO_TEST("tpm-passthrough"); + virObjectUnref(driver.caps); virObjectUnref(driver.xmlopt);

On Fri, Apr 05, 2013 at 10:06:02AM -0400, Stefan Berger wrote:
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.args | 6 +++ tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.xml | 29 +++++++++++++++ tests/qemuxml2argvtest.c | 3 + tests/qemuxml2xmltest.c | 2 + 4 files changed, 40 insertions(+)
Since you implemented support for parsing -tpmdev args, you should also add to tests/qemuargv2xmltest.c - the same data files can be used for all 3 testcases. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 04/11/2013 11:43 AM, Daniel P. Berrange wrote:
On Fri, Apr 05, 2013 at 10:06:02AM -0400, Stefan Berger wrote:
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.args | 6 +++ tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.xml | 29 +++++++++++++++ tests/qemuxml2argvtest.c | 3 + tests/qemuxml2xmltest.c | 2 + 4 files changed, 40 insertions(+) Since you implemented support for parsing -tpmdev args, you should also add to tests/qemuargv2xmltest.c - the same data files can be used for all 3 testcases.
Ok, so this creates quite some problems to have an XML generated that one doesn't have to edit afterwards, which I suppose is not expect. Should I just drop the parser code ? Stefan

On Thu, Apr 11, 2013 at 01:24:21PM -0400, Stefan Berger wrote:
On 04/11/2013 11:43 AM, Daniel P. Berrange wrote:
On Fri, Apr 05, 2013 at 10:06:02AM -0400, Stefan Berger wrote:
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.args | 6 +++ tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.xml | 29 +++++++++++++++ tests/qemuxml2argvtest.c | 3 + tests/qemuxml2xmltest.c | 2 + 4 files changed, 40 insertions(+) Since you implemented support for parsing -tpmdev args, you should also add to tests/qemuargv2xmltest.c - the same data files can be used for all 3 testcases.
Ok, so this creates quite some problems to have an XML generated that one doesn't have to edit afterwards, which I suppose is not expect. Should I just drop the parser code ?
I'm not sure I understand what your problem is ? Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 04/12/2013 04:47 AM, Daniel P. Berrange wrote:
On Thu, Apr 11, 2013 at 01:24:21PM -0400, Stefan Berger wrote:
On Fri, Apr 05, 2013 at 10:06:02AM -0400, Stefan Berger wrote:
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Tested-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
--- tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.args | 6 +++ tests/qemuxml2argvdata/qemuxml2argv-tpm-passthrough.xml | 29 +++++++++++++++ tests/qemuxml2argvtest.c | 3 + tests/qemuxml2xmltest.c | 2 + 4 files changed, 40 insertions(+) Since you implemented support for parsing -tpmdev args, you should also add to tests/qemuargv2xmltest.c - the same data files can be used for all 3 testcases. Ok, so this creates quite some problems to have an XML generated
On 04/11/2013 11:43 AM, Daniel P. Berrange wrote: that one doesn't have to edit afterwards, which I suppose is not expect. Should I just drop the parser code ? I'm not sure I understand what your problem is ?
-device is not handled and generates warnings in the parser. The XML for the TPM was generated correctly, but -device ended up showing up in the XML again along with its parameters, which would make it appear on the command line twice when trying to start a VM with this XML. Further, if it encounters a command line with a balloon device, it will also create the XML with -device and not the specific XML for the balloon device. Stefan
participants (2)
-
Daniel P. Berrange
-
Stefan Berger