Parse the domain XML with TPM passthrough support. The TPM passthrough XML may look like this: Signed-off-by: Stefan Berger Reviewed-by: Corey Bryant Tested-by: Corey Bryant --- src/conf/domain_conf.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 31 ++++++++ src/libvirt_private.syms | 5 + 3 files changed, 215 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 @@ -721,6 +722,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 @@ -1619,6 +1627,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) @@ -1880,6 +1905,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); @@ -6771,6 +6798,103 @@ error: goto cleanup; } +/* Parse the XML definition for a TPM device + * + * The XML looks like this: + * + * + * + * + * + * + * + */ +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, @@ -11099,6 +11223,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) { @@ -14083,6 +14224,39 @@ virDomainSoundCodecDefFormat(virBufferPt } static int +virDomainTPMDefFormat(virBufferPtr buf, + virDomainTPMDefPtr def, + unsigned int flags) +{ + virBufferAsprintf(buf, " \n", + virDomainTPMModelTypeToString(def->model)); + + virBufferAsprintf(buf, " \n", + virDomainTPMBackendTypeToString(def->type)); + + switch (def->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + virBufferEscapeString(buf, " \n", + def->data.passthrough.source.data.file.path); + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + + virBufferAddLit(buf, " \n"); + + if (virDomainDeviceInfoIsSet(&def->info, flags)) { + if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0) + return -1; + } + + virBufferAddLit(buf, " \n"); + + return 0; +} + + +static int virDomainSoundDefFormat(virBufferPtr buf, virDomainSoundDefPtr def, unsigned int flags) @@ -15408,6 +15582,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 @@ -1097,6 +1097,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, @@ -1883,6 +1910,7 @@ struct _virDomainDef { /* Only 1 */ virDomainWatchdogDefPtr watchdog; virDomainMemballoonDefPtr memballoon; + virDomainTPMDefPtr tpm; virCPUDefPtr cpu; virSysinfoDefPtr sysinfo; virDomainRedirFilterDefPtr redirfilter; @@ -2062,6 +2090,7 @@ void virDomainDeviceInfoClear(virDomainD void virDomainDefClearPCIAddresses(virDomainDefPtr def); void virDomainDefClearCCWAddresses(virDomainDefPtr def); void virDomainDefClearDeviceAliases(virDomainDefPtr def); +void virDomainTPMDefFree(virDomainTPMDefPtr def); typedef int (*virDomainDeviceInfoCallback)(virDomainDefPtr def, virDomainDeviceDefPtr dev, @@ -2433,6 +2462,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;