This patch adds the capability to parse and generated the XML of the TPM device. The XML can look like this: without an explicit pointer to a file for persistent storage or like this: with an explicit file mentioned in the XML. The file must be of type QCoW2 and be of a size that qemu tells the user using: qemu-system-x86_64 -tpm ? Supported TPM types (choose only one): builtin Qemu's built-in TPM; requires 63kb of block storage This patch also provides a function that generates the filename for the storage file if the user did not provide one. Also, the schema extensions for the domain XML is included. Signed-off-by: Stefan Berger --- docs/schemas/domain.rng | 25 +++++ src/conf/domain_conf.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 28 +++++ src/libvirt_private.syms | 5 + 4 files changed, 285 insertions(+) Index: libvirt-acl/src/conf/domain_conf.c =================================================================== --- libvirt-acl.orig/src/conf/domain_conf.c +++ libvirt-acl/src/conf/domain_conf.c @@ -384,6 +384,9 @@ VIR_ENUM_IMPL(virDomainTimerMode, VIR_DO "paravirt", "smpsafe"); +VIR_ENUM_IMPL(virDomainTPM, VIR_DOMAIN_TPM_TYPE_LAST, + "built-in") + #define virDomainReportError(code, ...) \ virReportErrorHelper(NULL, VIR_FROM_DOMAIN, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__) @@ -779,6 +782,22 @@ void virDomainVideoDefFree(virDomainVide VIR_FREE(def); } +void virDomainTPMDefFree(virDomainTPMDefPtr def) +{ + if (!def) + return; + + switch (def->type) { + case VIR_DOMAIN_TPM_TYPE_BUILTIN: + VIR_FREE(def->data.builtin.storage); + break; + default: + break; + } + + VIR_FREE(def); +} + void virDomainHostdevDefFree(virDomainHostdevDefPtr def) { if (!def) @@ -911,6 +930,7 @@ void virDomainDefFree(virDomainDefPtr de virDomainChrDefFree(def->channels[i]); VIR_FREE(def->channels); + virDomainTPMDefFree(def->tpm); virDomainChrDefFree(def->console); for (i = 0 ; i < def->nsounds ; i++) @@ -3453,6 +3473,130 @@ error: goto cleanup; } + +static char * +virDomainTPMDefaultStorageFile(const unsigned char *vmuuid) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char uuid[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(vmuuid, uuid); + + virBufferVSprintf(&buf, + "%s/lib/libvirt/tpm/%s.bin", + LOCALSTATEDIR, uuid); + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + virReportOOMError(); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + + +char * +virDomainTPMGetStorageFilename(virDomainTPMDefPtr def, + const unsigned char *vmuuid) +{ + + if (def->data.builtin.storage) + return strdup(def->data.builtin.storage); + else + return virDomainTPMDefaultStorageFile(vmuuid); +} + + +/* Parse the XML definition for a TPM device + * + * The XML we're dealing with looks like + * + * + * + * + * The 'storage' node is optional. If none is provided, + * libvirt is going to create the necessary storage using + * the VM's UUID as the name of the file. + * + */ +static virDomainTPMDefPtr +virDomainTPMDefParseXML(xmlNodePtr node) { + xmlNodePtr cur; + char *type = NULL; + char *path = NULL; + virDomainTPMDefPtr def; + char *tpmStateDir = NULL; + int err; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + return NULL; + } + + def->type = VIR_DOMAIN_TPM_TYPE_BUILTIN; + type = virXMLPropString(node, "type"); + if (type != NULL) { + if (STREQ(type, "builtin") || STREQ(type, "built-in")) + def->type = VIR_DOMAIN_TPM_TYPE_BUILTIN; + } + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "storage")) { + switch (def->type) { + case VIR_DOMAIN_TPM_TYPE_BUILTIN: + if (path == NULL) + path = virXMLPropString(cur, "file"); + break; + default: + break; + } + } + } + cur = cur->next; + } + + if (!path) { + if (virAsprintf(&tpmStateDir, + "%s/lib/libvirt/tpm", LOCALSTATEDIR) < 0) { + virReportOOMError(); + goto error; + } + + if ((err = virFileMakePath(tpmStateDir))) { + virReportSystemError(errno, + _("cannot create TPM state directory '%s'"), + tpmStateDir); + goto error; + } + } + + switch (def->type) { + case VIR_DOMAIN_TPM_TYPE_BUILTIN: + def->data.builtin.storage = path; + path = NULL; + break; + + default: + break; + } + +cleanup: + VIR_FREE(type); + VIR_FREE(path); + VIR_FREE(tpmStateDir); + + return def; + +error: + virDomainTPMDefFree(def); + def = NULL; + goto cleanup; +} + + /* Parse the XML definition for a network interface */ static virDomainInputDefPtr virDomainInputDefParseXML(const char *ostype, @@ -5587,6 +5731,14 @@ static virDomainDefPtr virDomainDefParse } VIR_FREE(nodes); + if ((node = virXPathNode("./devices/tpm[1]", ctxt)) != NULL) { + virDomainTPMDefPtr tpm = virDomainTPMDefParseXML(node); + if (!tpm) + goto error; + + def->tpm = tpm; + } + /* analysis of the controller devices */ if ((n = virXPathNodeSet("./devices/controller", ctxt, &nodes)) < 0) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -7390,6 +7542,39 @@ virDomainSmartcardDefFormat(virBufferPtr } static int +virDomainTPMDefFormat(virBufferPtr buf, + virDomainTPMDefPtr def, + const char *name) +{ + const char *type = virDomainTPMTypeToString(def->type); + + if (!type) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected TPM type " + " %d"), def->type); + return -1; + } + + virBufferVSprintf(buf, " <%s type='%s'>\n", + name, type); + switch (def->type) { + case VIR_DOMAIN_TPM_TYPE_BUILTIN: + if (def->data.builtin.storage) + virBufferEscapeString(buf, " \n", + def->data.builtin.storage); + break; + + default: + break; + } + + virBufferVSprintf(buf, " \n", + name); + + return 0; +} + + +static int virDomainSoundDefFormat(virBufferPtr buf, virDomainSoundDefPtr def, int flags) @@ -8205,6 +8390,11 @@ char *virDomainDefFormat(virDomainDefPtr virDomainInputDefFormat(&buf, def->inputs[n], flags) < 0) goto cleanup; + if (def->tpm) { + if (virDomainTPMDefFormat(&buf, def->tpm, "tpm") < 0) + goto cleanup; + } + if (def->ngraphics > 0) { /* If graphics is enabled, add the implicit mouse */ virDomainInputDef autoInput = { @@ -8598,6 +8788,43 @@ cleanup: return ret; } +int virDomainTPMDelete(virDomainObjPtr dom, + bool afterMigration) +{ + int ret = -1; + char *tpmStateFile = NULL; + + if (dom->def->tpm) + if ((tpmStateFile = + virDomainTPMDefaultStorageFile(dom->def->uuid)) == NULL) + goto cleanup; + + /* + * remove tpm state file + * - if libvirt created it (in that case virDomainTPMDefaultStorageFile + * returned the name of a file libvirt may have created) + * - if we were not called due to an finished migration + */ + + if (tpmStateFile && + (!afterMigration || + (afterMigration && !virStorageFileIsSharedFS(tpmStateFile))) && + unlink(tpmStateFile) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("cannot remove TPM state file %s"), + tpmStateFile); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(tpmStateFile); + return ret; +} + + char *virDomainConfigFile(const char *dir, const char *name) { Index: libvirt-acl/src/conf/domain_conf.h =================================================================== --- libvirt-acl.orig/src/conf/domain_conf.h +++ libvirt-acl/src/conf/domain_conf.h @@ -40,6 +40,7 @@ # include "nwfilter_conf.h" # include "macvtap.h" # include "sysinfo.h" +# include "configmake.h" /* Private component of virDomainXMLFlags */ typedef enum { @@ -1009,6 +1010,26 @@ struct _virDomainSnapshotObjList { virHashTable *objs; }; + +enum virDomainTPMModel { + VIR_DOMAIN_TPM_TYPE_BUILTIN, + + VIR_DOMAIN_TPM_TYPE_LAST +}; + +typedef struct _virDomainTPMDef virDomainTPMDef; +typedef virDomainTPMDef *virDomainTPMDefPtr; +struct _virDomainTPMDef { + enum virDomainTPMModel type; + union { + struct { + char *storage; + } builtin; + } data; +}; + + + virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, int newSnapshot); void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def); @@ -1133,6 +1154,7 @@ struct _virDomainDef { virSecurityLabelDef seclabel; virDomainWatchdogDefPtr watchdog; virDomainMemballoonDefPtr memballoon; + virDomainTPMDefPtr tpm; virCPUDefPtr cpu; virSysinfoDefPtr sysinfo; @@ -1215,6 +1237,11 @@ int virDomainDeviceInfoIsSet(virDomainDe void virDomainDeviceInfoClear(virDomainDeviceInfoPtr info); void virDomainDefClearPCIAddresses(virDomainDefPtr def); void virDomainDefClearDeviceAliases(virDomainDefPtr def); +void virDomainTPMDefFree(virDomainTPMDefPtr def); +int virDomainTPMDelete(virDomainObjPtr dom, + bool afterMigration); +char *virDomainTPMGetStorageFilename(virDomainTPMDefPtr def, + const unsigned char *vmuuid); typedef int (*virDomainDeviceInfoCallback)(virDomainDefPtr def, virDomainDeviceInfoPtr dev, @@ -1423,6 +1450,7 @@ VIR_ENUM_DECL(virDomainInputBus) VIR_ENUM_DECL(virDomainGraphics) VIR_ENUM_DECL(virDomainGraphicsSpiceChannelName) VIR_ENUM_DECL(virDomainGraphicsSpiceChannelMode) +VIR_ENUM_DECL(virDomainTPM) /* from libvirt.h */ VIR_ENUM_DECL(virDomainState) VIR_ENUM_DECL(virDomainSeclabel) Index: libvirt-acl/docs/schemas/domain.rng =================================================================== --- libvirt-acl.orig/docs/schemas/domain.rng +++ libvirt-acl/docs/schemas/domain.rng @@ -1724,6 +1724,27 @@ + + + + + + + + + + + + + + built-in + + + + + + + @@ -1900,6 +1921,7 @@ + Index: libvirt-acl/src/libvirt_private.syms =================================================================== --- libvirt-acl.orig/src/libvirt_private.syms +++ libvirt-acl/src/libvirt_private.syms @@ -320,6 +320,11 @@ virDomainTimerTickpolicyTypeFromString; virDomainTimerTickpolicyTypeToString; virDomainTimerTrackTypeFromString; virDomainTimerTrackTypeToString; +virDomainTPMDefFree; +virDomainTPMDelete; +virDomainTPMGetStorageFilename; +virDomainTPMTypeFromString; +virDomainTPMTypeToString; virDomainVcpupinAdd; virDomainVcpupinFindByVcpu; virDomainVcpupinIsDuplicate;