[libvirt] [RFC PATCH 2/2] conf: Add VM Generation ID device

John Ferlan jferlan at redhat.com
Tue Mar 20 22:55:44 UTC 2018


Add VM Generation ID device XML schema, parse, format, and documentation.

Signed-off-by: John Ferlan <jferlan at redhat.com>
---
 docs/formatdomain.html.in                 |  54 ++++++++++++++
 docs/schemas/domaincommon.rng             |  21 ++++++
 src/conf/domain_conf.c                    | 112 +++++++++++++++++++++++++++++-
 src/conf/domain_conf.h                    |  11 +++
 tests/qemuxml2argvdata/vmgenid-auto.xml   |  32 +++++++++
 tests/qemuxml2argvdata/vmgenid.xml        |  32 +++++++++
 tests/qemuxml2xmloutdata/vmgenid-auto.xml |   1 +
 tests/qemuxml2xmloutdata/vmgenid.xml      |   1 +
 tests/qemuxml2xmltest.c                   |   3 +
 9 files changed, 266 insertions(+), 1 deletion(-)
 create mode 100644 tests/qemuxml2argvdata/vmgenid-auto.xml
 create mode 100644 tests/qemuxml2argvdata/vmgenid.xml
 create mode 120000 tests/qemuxml2xmloutdata/vmgenid-auto.xml
 create mode 120000 tests/qemuxml2xmloutdata/vmgenid.xml

diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 6fd2189cd2..895e51b343 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -8027,6 +8027,60 @@ qemu-kvm -net nic,model=? /dev/null
       </dd>
     </dl>
 
+    <h4><a id="elementsVmgenid">VM Generation ID device</a></h4>
+
+    <p>
+      <span class="since">Since 4.2.0</span>, the <code>vmgenid</code>
+      element can be used to add a Virtual Machine Generation ID device.
+      A <code>vmgenid</code> device is an emulated device which exposes
+      a 128-bit, cryptographically random, integer value identifier,
+      referred to as a Globally Unique Identifier, or GUID. The value is
+      stored within the virtual machine's BIOS so that programs running in
+      the virtual machine can protect themselves from potential corruption
+      by checking that the Generation ID has not changed immediately prior
+      to committing a transaction.
+
+      The <code>vmgenid</code> device will update the BIOS entry each time
+      the virtual machine executes from a different configuration file such
+      as executing from a recovered snapshot or executing after restoring
+      from backup. Programs running in a virtual machine can protect themselves
+      from potential corruption by checking that the generation ID has not
+      changed immediately prior to committing a transaction, they can also
+      use the data provided in the 128-bit identifier as a high entropy
+      random data source.
+    </p>
+
+    <p>
+      Example:
+    </p>
+<pre>
+...
+<devices>
+  <vmgenid guid='3e3fce45-4f53-4fa7-bb32-11f34168b82b'/>
+</devices>
+...
+</pre>
+
+<pre>
+...
+<devices>
+  <vmgenid guid='auto'/>
+</devices>
+...
+</pre>
+
+    <dl>
+      <dt><code>guid</code></dt>
+      <dd>
+        <p>
+         The required <code>guid</code> attribute can be either a provided
+         (<a href="#elementsMetadata"><code>uuid</code></a>) formatted value
+         or the string 'auto' if the underlying hypervisor supports creating
+         its own value.
+        </p>
+      </dd>
+    </dl>
+
     <h3><a id="seclabel">Security label</a></h3>
 
     <p>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 8165e699d6..692cc8d5a0 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -4644,6 +4644,24 @@
       </attribute>
     </element>
   </define>
+
+  <define name="vmgenid">
+    <element name="vmgenid">
+      <choice>
+        <group>
+          <attribute name="guid">
+            <ref name="UUID"/>
+          </attribute>
+        </group>
+        <group>
+          <attribute name="guid">
+            <value>auto</value>
+          </attribute>
+        </group>
+      </choice>
+    </element>
+  </define>
+
   <define name="devices">
     <element name="devices">
       <interleave>
@@ -4691,6 +4709,9 @@
         <optional>
           <ref name="iommu"/>
         </optional>
+        <optional>
+          <ref name="vmgenid"/>
+        </optional>
       </interleave>
     </element>
   </define>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 2f07180faa..aaba2a47f7 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2741,6 +2741,8 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
         VIR_FREE(def->data.iommu);
         break;
     case VIR_DOMAIN_DEVICE_VMGENID:
+        VIR_FREE(def->data.vmgenid);
+        break;
     case VIR_DOMAIN_DEVICE_LAST:
     case VIR_DOMAIN_DEVICE_NONE:
         break;
@@ -15690,6 +15692,45 @@ virDomainIOMMUDefParseXML(xmlNodePtr node,
 }
 
 
+static virDomainVMGenIDDefPtr
+virDomainVMGenIDDefParseXML(xmlNodePtr node,
+                            xmlXPathContextPtr ctxt)
+{
+    virDomainVMGenIDDefPtr vmgenid = NULL;
+    virDomainVMGenIDDefPtr ret = NULL;
+    xmlNodePtr save = ctxt->node;
+    char *guidxml = NULL;
+
+    ctxt->node = node;
+
+    if (VIR_ALLOC(vmgenid) < 0)
+        goto cleanup;
+
+    if (!(guidxml = virXMLPropString(node, "guid"))) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("missing required 'guid' attribute"));
+        goto cleanup;
+    }
+
+    if (STREQ(guidxml, "auto")) {
+        vmgenid->autogenerate = true;
+    } else {
+        if (virUUIDParse(guidxml, vmgenid->guidstr) < 0) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("malformed guid='%s' provided"), guidxml);
+            goto cleanup;
+        }
+    }
+
+    VIR_STEAL_PTR(ret, vmgenid);
+
+ cleanup:
+    VIR_FREE(vmgenid);
+    ctxt->node = save;
+    return ret;
+}
+
+
 virDomainDeviceDefPtr
 virDomainDeviceDefParse(const char *xmlStr,
                         const virDomainDef *def,
@@ -15846,6 +15887,9 @@ virDomainDeviceDefParse(const char *xmlStr,
             goto error;
         break;
     case VIR_DOMAIN_DEVICE_VMGENID:
+        if (!(dev->data.vmgenid = virDomainVMGenIDDefParseXML(node, ctxt)))
+            goto error;
+        break;
     case VIR_DOMAIN_DEVICE_NONE:
     case VIR_DOMAIN_DEVICE_LAST:
         break;
@@ -20249,6 +20293,21 @@ virDomainDefParseXML(xmlDocPtr xml,
     }
     VIR_FREE(nodes);
 
+    if ((n = virXPathNodeSet("./devices/vmgenid", ctxt, &nodes)) < 0)
+        goto error;
+
+    if (n > 1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("only a single vmgenid device is supported"));
+        goto error;
+    }
+
+    if (n > 0) {
+        if (!(def->vmgenid = virDomainVMGenIDDefParseXML(nodes[0], ctxt)))
+            goto error;
+    }
+    VIR_FREE(nodes);
+
     /* analysis of the user namespace mapping */
     if ((n = virXPathNodeSet("./idmap/uid", ctxt, &nodes)) < 0)
         goto error;
@@ -21812,6 +21871,25 @@ virDomainIOMMUDefCheckABIStability(virDomainIOMMUDefPtr src,
 
 
 static bool
+virDomainVMGenIDDefCheckABIStability(virDomainVMGenIDDefPtr src,
+                                     virDomainVMGenIDDefPtr dst)
+{
+    if (memcmp(src->guidstr, dst->guidstr, VIR_UUID_BUFLEN) != 0) {
+        char guidsrc[VIR_UUID_STRING_BUFLEN];
+        char guiddst[VIR_UUID_STRING_BUFLEN];
+        virUUIDFormat(src->guidstr, guidsrc);
+        virUUIDFormat(dst->guidstr, guiddst);
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Target domain vmgenid guid '%s' does not match "
+                         "source '%s'"),
+                       guiddst, guidsrc);
+        return false;
+    }
+    return true;
+}
+
+
+static bool
 virDomainDefVcpuCheckAbiStability(virDomainDefPtr src,
                                   virDomainDefPtr dst)
 {
@@ -22256,6 +22334,17 @@ virDomainDefCheckABIStabilityFlags(virDomainDefPtr src,
         !virDomainIOMMUDefCheckABIStability(src->iommu, dst->iommu))
         goto error;
 
+    if (!!src->vmgenid != !!dst->vmgenid) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("Target domain vmgenid device count does not "
+                         "match source"));
+        goto error;
+    }
+
+    if (src->vmgenid &&
+        !virDomainVMGenIDDefCheckABIStability(src->vmgenid, dst->vmgenid))
+        goto error;
+
     if (xmlopt && xmlopt->abi.domain &&
         !xmlopt->abi.domain(src, dst))
         goto error;
@@ -26470,6 +26559,21 @@ virDomainIOMMUDefFormat(virBufferPtr buf,
 }
 
 
+static void
+virDomainVMGenIDDefFormat(virBufferPtr buf,
+                          const virDomainVMGenIDDef *vmgenid)
+{
+    char guidstr[VIR_UUID_STRING_BUFLEN];
+
+    if (vmgenid->autogenerate) {
+        virBufferAddLit(buf, "<vmgenid guid='auto'/>\n");
+    } else {
+        virUUIDFormat(vmgenid->guidstr, guidstr);
+        virBufferAsprintf(buf, "<vmgenid guid='%s'/>\n", guidstr);
+    }
+}
+
+
 /* This internal version appends to an existing buffer
  * (possibly with auto-indent), rather than flattening
  * to string.
@@ -27251,6 +27355,9 @@ virDomainDefFormatInternal(virDomainDefPtr def,
         virDomainIOMMUDefFormat(buf, def->iommu) < 0)
         goto error;
 
+    if (def->vmgenid)
+        virDomainVMGenIDDefFormat(buf, def->vmgenid);
+
     virBufferAdjustIndent(buf, -2);
     virBufferAddLit(buf, "</devices>\n");
 
@@ -28371,13 +28478,16 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src,
     case VIR_DOMAIN_DEVICE_SHMEM:
         rc = virDomainShmemDefFormat(&buf, src->data.shmem, flags);
         break;
+    case VIR_DOMAIN_DEVICE_VMGENID:
+        virDomainVMGenIDDefFormat(&buf, src->data.vmgenid);
+        rc = 0;
+        break;
 
     case VIR_DOMAIN_DEVICE_NONE:
     case VIR_DOMAIN_DEVICE_SMARTCARD:
     case VIR_DOMAIN_DEVICE_MEMBALLOON:
     case VIR_DOMAIN_DEVICE_NVRAM:
     case VIR_DOMAIN_DEVICE_IOMMU:
-    case VIR_DOMAIN_DEVICE_VMGENID:
     case VIR_DOMAIN_DEVICE_LAST:
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Copying definition of '%d' type "
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 6e204d31fb..7805ad1819 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -157,6 +157,9 @@ typedef virDomainTPMDef *virDomainTPMDefPtr;
 typedef struct _virDomainIOMMUDef virDomainIOMMUDef;
 typedef virDomainIOMMUDef *virDomainIOMMUDefPtr;
 
+typedef struct _virDomainVMGenIDDef virDomainVMGenIDDef;
+typedef virDomainVMGenIDDef *virDomainVMGenIDDefPtr;
+
 typedef struct _virDomainVirtioOptions virDomainVirtioOptions;
 typedef virDomainVirtioOptions *virDomainVirtioOptionsPtr;
 
@@ -219,6 +222,7 @@ struct _virDomainDeviceDef {
         virDomainPanicDefPtr panic;
         virDomainMemoryDefPtr memory;
         virDomainIOMMUDefPtr iommu;
+        virDomainVMGenIDDefPtr vmgenid;
     } data;
 };
 
@@ -2309,6 +2313,12 @@ struct _virDomainVirtioOptions {
     virTristateSwitch ats;
 };
 
+struct _virDomainVMGenIDDef {
+    bool autogenerate;
+    unsigned char guidstr[VIR_UUID_STRING_BUFLEN];
+};
+
+
 /*
  * Guest VM main configuration
  *
@@ -2449,6 +2459,7 @@ struct _virDomainDef {
     virSysinfoDefPtr sysinfo;
     virDomainRedirFilterDefPtr redirfilter;
     virDomainIOMMUDefPtr iommu;
+    virDomainVMGenIDDefPtr vmgenid;
 
     void *namespaceData;
     virDomainXMLNamespace ns;
diff --git a/tests/qemuxml2argvdata/vmgenid-auto.xml b/tests/qemuxml2argvdata/vmgenid-auto.xml
new file mode 100644
index 0000000000..d111dbec8e
--- /dev/null
+++ b/tests/qemuxml2argvdata/vmgenid-auto.xml
@@ -0,0 +1,32 @@
+<domain type='qemu'>
+  <name>QEMUGuest1</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219136</memory>
+  <currentMemory unit='KiB'>219136</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type arch='x86_64' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <features>
+    <acpi/>
+  </features>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <controller type='usb' index='0'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
+    </controller>
+    <controller type='ide' index='0'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+    </controller>
+    <controller type='pci' index='0' model='pci-root'/>
+    <input type='mouse' bus='ps2'/>
+    <input type='keyboard' bus='ps2'/>
+    <memballoon model='none'/>
+    <vmgenid guid='auto'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2argvdata/vmgenid.xml b/tests/qemuxml2argvdata/vmgenid.xml
new file mode 100644
index 0000000000..14c94706df
--- /dev/null
+++ b/tests/qemuxml2argvdata/vmgenid.xml
@@ -0,0 +1,32 @@
+<domain type='qemu'>
+  <name>QEMUGuest1</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219136</memory>
+  <currentMemory unit='KiB'>219136</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type arch='x86_64' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <features>
+    <acpi/>
+  </features>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <controller type='usb' index='0'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
+    </controller>
+    <controller type='ide' index='0'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+    </controller>
+    <controller type='pci' index='0' model='pci-root'/>
+    <input type='mouse' bus='ps2'/>
+    <input type='keyboard' bus='ps2'/>
+    <memballoon model='none'/>
+    <vmgenid guid='e9392370-2917-565e-692b-d057f46512d6'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2xmloutdata/vmgenid-auto.xml b/tests/qemuxml2xmloutdata/vmgenid-auto.xml
new file mode 120000
index 0000000000..498b582ddc
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/vmgenid-auto.xml
@@ -0,0 +1 @@
+../qemuxml2argvdata/vmgenid-auto.xml
\ No newline at end of file
diff --git a/tests/qemuxml2xmloutdata/vmgenid.xml b/tests/qemuxml2xmloutdata/vmgenid.xml
new file mode 120000
index 0000000000..37bb1c6b9c
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/vmgenid.xml
@@ -0,0 +1 @@
+../qemuxml2argvdata/vmgenid.xml
\ No newline at end of file
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 28ba46efb2..868a774bbf 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -1193,6 +1193,9 @@ mymain(void)
     DO_TEST("intel-iommu-eim", NONE);
     DO_TEST("intel-iommu-device-iotlb", NONE);
 
+    DO_TEST("vmgenid", NONE);
+    DO_TEST("vmgenid-auto", NONE);
+
     DO_TEST("cpu-check-none", NONE);
     DO_TEST("cpu-check-partial", NONE);
     DO_TEST("cpu-check-full", NONE);
-- 
2.13.6




More information about the libvir-list mailing list