[PATCH V1 2/6] Parse TPM in domain XML

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

 



Parse the domain XML with TPM support.

Convert the strings from QEMU's QMP TPM commands into
capability flags.

Signed-off-by: Stefan Berger <stefanb@xxxxxxxxxxxxxxxxxx>

---
 docs/schemas/domaincommon.rng |   43 ++++++
 src/conf/domain_conf.c        |  268 ++++++++++++++++++++++++++++++++++++++++++
 src/conf/domain_conf.h        |   34 +++++
 src/libvirt_private.syms      |    5 
 src/qemu/qemu_capabilities.c  |   59 +++++++++
 5 files changed, 409 insertions(+)

Index: libvirt/docs/schemas/domaincommon.rng
===================================================================
--- libvirt.orig/docs/schemas/domaincommon.rng
+++ libvirt/docs/schemas/domaincommon.rng
@@ -2814,6 +2814,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">
@@ -3111,6 +3153,7 @@
             <ref name="redirdev"/>
             <ref name="redirfilter"/>
             <ref name="rng"/>
+            <ref name="tpm"/>
           </choice>
         </zeroOrMore>
         <optional>
Index: libvirt/src/conf/domain_conf.c
===================================================================
--- libvirt.orig/src/conf/domain_conf.c
+++ libvirt/src/conf/domain_conf.c
@@ -709,6 +709,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
 
@@ -1515,6 +1522,24 @@ void virDomainHostdevDefClear(virDomainH
     }
 }
 
+void virDomainTPMDefFree(virDomainTPMDefPtr def)
+{
+    if (!def)
+        return;
+
+    switch (def->type) {
+    case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
+        VIR_FREE(def->data.passthrough.path);
+        VIR_FREE(def->data.passthrough.cancel_path);
+        break;
+    case VIR_DOMAIN_TPM_TYPE_LAST:
+        break;
+    }
+
+    virDomainDeviceInfoClear(&def->info);
+    VIR_FREE(def);
+}
+
 void virDomainHostdevDefFree(virDomainHostdevDefPtr def)
 {
     if (!def)
@@ -1776,6 +1801,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);
@@ -6312,6 +6339,192 @@ error:
     goto cleanup;
 }
 
+/*
+ * 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 'true' is returned and the basebath buffer has '/cancel'
+ * appended.
+ */
+static bool
+virDomainTPMCheckSysfsCancel(char *basepath, size_t bufsz)
+{
+    char *path = NULL;
+    struct stat statbuf;
+
+    if (virAsprintf(&path, "%s/pcrs", basepath) < 0) {
+        virReportOOMError();
+        goto error;
+    }
+    if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode))
+        goto error;
+
+    VIR_FREE(path);
+
+    if (virAsprintf(&path, "%s/cancel", basepath) < 0) {
+        virReportOOMError();
+        goto error;
+    }
+
+    if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode))
+        goto error;
+
+    if (!virStrncpy(basepath, path, strlen(path) + 1, bufsz)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Basepath buffer is too small"));
+        goto error;
+    }
+
+    VIR_FREE(path);
+
+    return true;
+
+error:
+    VIR_FREE(path);
+    return false;
+}
+
+
+static char *
+virDomainTPMFindCancelPath(void)
+{
+    unsigned int idx;
+    int len;
+    DIR *pnp_dir;
+    char path[100], *p;
+    struct dirent entry, *result;
+    bool found = false;
+
+    snprintf(path, sizeof(path), "/sys/class/misc");
+    pnp_dir = opendir(path);
+    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;
+            }
+            snprintf(path, sizeof(path), "/sys/class/misc/%s/device",
+                     entry.d_name);
+            if (!virDomainTPMCheckSysfsCancel(path, sizeof(path))) {
+                continue;
+            }
+
+            found = true;
+            break;
+        }
+        closedir(pnp_dir);
+    }
+
+    if (found) {
+        if (!(p = strdup(path)))
+            virReportOOMError();
+        return p;
+    }
+
+    return NULL;
+}
+
+
+/* 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.path = path;
+        path = NULL;
+        /* cancel_path is read-only */
+        def->data.passthrough.cancel_path = virDomainTPMFindCancelPath();
+        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,
@@ -10659,6 +10872,23 @@ virDomainDefParseXML(virCapsPtr caps,
             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) {
@@ -13589,6 +13819,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.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)
@@ -14905,6 +15168,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
@@ -1063,6 +1063,34 @@ 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 {
+            char *path;
+            char *cancel_path;
+        } passthrough;
+    } data;
+};
+
 enum virDomainInputType {
     VIR_DOMAIN_INPUT_TYPE_MOUSE,
     VIR_DOMAIN_INPUT_TYPE_TABLET,
@@ -1866,6 +1894,7 @@ struct _virDomainDef {
     /* Only 1 */
     virDomainWatchdogDefPtr watchdog;
     virDomainMemballoonDefPtr memballoon;
+    virDomainTPMDefPtr tpm;
     virCPUDefPtr cpu;
     virSysinfoDefPtr sysinfo;
     virDomainRedirFilterDefPtr redirfilter;
@@ -1988,6 +2017,9 @@ int virDomainDeviceInfoCopy(virDomainDev
 void virDomainDeviceInfoClear(virDomainDeviceInfoPtr info);
 void virDomainDefClearPCIAddresses(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,
@@ -2346,6 +2378,8 @@ VIR_ENUM_DECL(virDomainNumatuneMemPlacem
 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
@@ -323,6 +323,11 @@ virDomainTimerTickpolicyTypeFromString;
 virDomainTimerTickpolicyTypeToString;
 virDomainTimerTrackTypeFromString;
 virDomainTimerTrackTypeToString;
+virDomainTPMBackendTypeFromString;
+virDomainTPMBackendTypeToString;
+virDomainTPMDefFree;
+virDomainTPMModelTypeFromString;
+virDomainTPMModelTypeToString;
 virDomainVcpuPinAdd;
 virDomainVcpuPinDefArrayFree;
 virDomainVcpuPinDefCopy;
Index: libvirt/src/qemu/qemu_capabilities.c
===================================================================
--- libvirt.orig/src/qemu/qemu_capabilities.c
+++ libvirt/src/qemu/qemu_capabilities.c
@@ -2110,6 +2110,63 @@ 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 (virStrArrayHasString(entries, nentries, needle))
+                virQEMUCapsSet(qemuCaps, tpmModelsToCaps[i].caps);
+        }
+        for (i = 0; i < nentries; i++)
+            VIR_FREE(entries[i]);
+    }
+    VIR_FREE(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 (virStrArrayHasString(entries, nentries, needle))
+                virQEMUCapsSet(qemuCaps, tpmTypesToCaps[i].caps);
+        }
+        for (i = 0; i < nentries; i++)
+            VIR_FREE(entries[i]);
+    }
+    VIR_FREE(entries);
+
+    return 0;
+}
+
+
+static int
 virQEMUCapsProbeQMPKVMState(virQEMUCapsPtr qemuCaps,
                             qemuMonitorPtr mon)
 {
@@ -2458,6 +2515,8 @@ virQEMUCapsInitQMP(virQEMUCapsPtr qemuCa
         goto cleanup;
     if (virQEMUCapsProbeQMPKVMState(qemuCaps, mon) < 0)
         goto cleanup;
+    if (virQEMUCapsProbeQMPTPM(qemuCaps, mon) < 0)
+        goto cleanup;
 
     ret = 0;
 

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