Add VM Generation ID device XML schema, parse, format, and documentation. Signed-off-by: John Ferlan <jferlan@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list