This patch adds the capability to parse and generated the XML of the TPM
device. The XML can look like this:
<tpm type='built-in'>
<storage/>
</tpm>
without an explicit pointer to a file for persistent storage or like this:
<tpm type='built-in'>
<storage file='/tmp/tpmstate.bin'/>
</tpm>
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 <stefanb(a)linux.vnet.ibm.com>
---
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
+ *
+ * <tpm type="built-in">
+ * <storage file='path/to/QCoW2/state/file' />
+ * </tpm>
+ * 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, " <storage
file='%s'/>\n",
+ def->data.builtin.storage);
+ break;
+
+ default:
+ break;
+ }
+
+ virBufferVSprintf(buf, " </%s>\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 @@
<text/>
</element>
</define>
+ <define name="tpm-storage">
+ <element name="storage">
+ <optional>
+ <attribute name="file">
+ <ref name="filePath"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+ <define name="tpm">
+ <element name="tpm">
+ <attribute name="type">
+ <choice>
+ <value>built-in</value>
+ </choice>
+ </attribute>
+ <optional>
+ <ref name="tpm-storage"/>
+ </optional>
+ </element>
+ </define>
<define name="input">
<element name="input">
<attribute name="type">
@@ -1900,6 +1921,7 @@
<ref name="serial"/>
<ref name="channel"/>
<ref name="smartcard"/>
+ <ref name="tpm"/>
</choice>
</zeroOrMore>
<optional>
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;