[RFC PATCH V1 1/5] tpm: Parse the TPM device XML

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxxxxxx>

---
 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;

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list


[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]