diff --git a/gating.yaml b/gating.yaml deleted file mode 100644 index fa93037..0000000 --- a/gating.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# recipients: libvirt-qe ---- !Policy -product_versions: - - rhel-10 -decision_context: osci_compose_gate -subject_type: brew-build -rules: - - !PassingTestCaseRule {test_case_name: libvirt-ci.libvirt.brew-build.gating.x86_64.tier1.functional} - - !PassingTestCaseRule {test_case_name: libvirt-ci.libvirt-python.brew-build.gating.x86_64.tier1.functional} - - !PassingTestCaseRule {test_case_name: libvirt-ci.libvirt.brew-build.gating.s390x.tier1.functional} diff --git a/libvirt-conf-Support-EGM-memory-device-model.patch b/libvirt-conf-Support-EGM-memory-device-model.patch new file mode 100644 index 0000000..95112ea --- /dev/null +++ b/libvirt-conf-Support-EGM-memory-device-model.patch @@ -0,0 +1,275 @@ +From 8049e03589d633edba613c6775dcd5656d851460 Mon Sep 17 00:00:00 2001 +Message-ID: <8049e03589d633edba613c6775dcd5656d851460.1779098642.git.phrdina@redhat.com> +From: Pavel Hrdina +Date: Tue, 25 Nov 2025 11:17:01 -0800 +Subject: [PATCH] conf: Support EGM memory device model + +From: Nathan Chen via Devel + +Add support for EGM memory device model with +'path' source attribute and 'pciDev' target +attribute to denote host EGM device backing path +and PCI device alias to associate the vEGM with, +respectively. + +Signed-off-by: Nathan Chen + +Resolves: https://redhat.atlassian.net/browse/VOYAGER-13 +--- + docs/formatdomain.rst | 18 +++++++++++++++++- + src/conf/domain_conf.c | 29 +++++++++++++++++++++++++++++ + src/conf/domain_conf.h | 7 +++++++ + src/conf/domain_postparse.c | 1 + + src/conf/domain_validate.c | 15 +++++++++++++++ + src/conf/schemas/domaincommon.rng | 6 ++++++ + 6 files changed, 75 insertions(+), 1 deletion(-) + +diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst +index 44f9b6e197..4fde1534d6 100644 +--- a/docs/formatdomain.rst ++++ b/docs/formatdomain.rst +@@ -9119,6 +9119,16 @@ Example: usage of the memory devices + 16384 + + ++ ++ ++ /dev/egm0 ++ ++ ++ 524288 ++ 0 ++ ua-hostdev0 ++ ++ + + ... + +@@ -9130,7 +9140,8 @@ Example: usage of the memory devices + persistent memory device. :since:`Since 7.1.0` Provide ``virtio-mem`` model + to add paravirtualized memory device. :since:`Since 7.9.0` Provide + ``sgx-epc`` model to add a SGX enclave page cache (EPC) memory to the guest. +- :since:`Since 8.10.0 and QEMU 7.0.0` ++ :since:`Since 8.10.0 and QEMU 7.0.0` Provide ``egm`` model to add a EGM ++ (Extended GPU Memory) device. + + ``access`` + An optional attribute ``access`` ( :since:`since 3.2.0` ) that provides +@@ -9265,6 +9276,11 @@ Example: usage of the memory devices + The physical address in memory, where device is mapped. + :since:`Since 9.4.0` + ++ ``pciDev`` ++ For ``egm`` only. ++ The PCI device that is enabled to access the system memory via ++ association with the EGM device. ++ + + IOMMU devices + ~~~~~~~~~~~~~ +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index 06790e0962..038476edd7 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -1522,6 +1522,7 @@ VIR_ENUM_IMPL(virDomainMemoryModel, + "virtio-pmem", + "virtio-mem", + "sgx-epc", ++ "egm", + ); + + VIR_ENUM_IMPL(virDomainShmemModel, +@@ -3638,6 +3639,9 @@ void virDomainMemoryDefFree(virDomainMemoryDef *def) + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: + virBitmapFree(def->source.sgx_epc.nodes); + break; ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ g_free(def->source.egm.path); ++ g_free(def->target.egm.pciDev); + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -14216,6 +14220,10 @@ virDomainMemorySourceDefParseXML(xmlNodePtr node, + } + break; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ def->source.egm.path = virXPathString("string(./path)", ctxt); ++ break; ++ + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -14292,6 +14300,10 @@ virDomainMemoryTargetDefParseXML(xmlNodePtr node, + addr = &def->target.virtio_pmem.address; + break; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ def->target.egm.pciDev = virXPathString("string(./pciDev)", ctxt); ++ break; ++ + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: +@@ -14513,6 +14525,7 @@ virDomainMemoryIsVirtioModel(const virDomainMemoryDef *def) + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; + } +@@ -16415,6 +16428,12 @@ virDomainMemoryFindByDefInternal(virDomainDef *def, + continue; + break; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ if (STRNEQ(tmp->source.egm.path, mem->source.egm.path)) ++ continue; ++ if (STRNEQ(tmp->target.egm.pciDev, mem->target.egm.pciDev)) ++ continue; ++ + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -22430,6 +22449,7 @@ virDomainMemoryDefCheckABIStability(virDomainMemoryDef *src, + + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -26998,6 +27018,10 @@ virDomainMemorySourceDefFormat(virBuffer *buf, + } + break; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ virBufferEscapeString(&childBuf, "%s\n", def->source.egm.path); ++ break; ++ + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -27060,6 +27084,11 @@ virDomainMemoryTargetDefFormat(virBuffer *buf, + } + break; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ if (def->target.egm.pciDev) ++ virBufferAsprintf(&childBuf, "%s\n", def->target.egm.pciDev); ++ break; ++ + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: +diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h +index 06e03deafe..4d03521245 100644 +--- a/src/conf/domain_conf.h ++++ b/src/conf/domain_conf.h +@@ -2755,6 +2755,7 @@ typedef enum { + VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM, /* virtio-pmem memory device */ + VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM, /* virtio-mem memory device */ + VIR_DOMAIN_MEMORY_MODEL_SGX_EPC, /* SGX enclave page cache */ ++ VIR_DOMAIN_MEMORY_MODEL_EGM, /* Extended GPU memory */ + + VIR_DOMAIN_MEMORY_MODEL_LAST + } virDomainMemoryModel; +@@ -2787,6 +2788,9 @@ struct _virDomainMemoryDef { + struct { + virBitmap *nodes; /* source NUMA nodes */ + } sgx_epc; ++ struct { ++ char *path; ++ } egm; + } source; + + union { +@@ -2812,6 +2816,9 @@ struct _virDomainMemoryDef { + } virtio_mem; + struct { + } sgx_epc; ++ struct { ++ char *pciDev; ++ } egm; + } target; + + virDomainDeviceInfo info; +diff --git a/src/conf/domain_postparse.c b/src/conf/domain_postparse.c +index 38e731348d..0181d21f0e 100644 +--- a/src/conf/domain_postparse.c ++++ b/src/conf/domain_postparse.c +@@ -632,6 +632,7 @@ virDomainMemoryDefPostParse(virDomainMemoryDef *mem, + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: + case VIR_DOMAIN_MEMORY_MODEL_DIMM: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c +index f243b119a4..34906ddc84 100644 +--- a/src/conf/domain_validate.c ++++ b/src/conf/domain_validate.c +@@ -2525,6 +2525,7 @@ virDomainMemoryDefCheckConflict(const virDomainMemoryDef *mem, + } + break; + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -2571,6 +2572,7 @@ virDomainMemoryDefCheckConflict(const virDomainMemoryDef *mem, + switch (other->model) { + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + continue; + break; +@@ -2751,6 +2753,19 @@ virDomainMemoryDefValidate(const virDomainMemoryDef *mem, + } + break; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ if (!mem->source.egm.path) { ++ virReportError(VIR_ERR_XML_DETAIL, "%s", ++ _("path is required for model 'egm'")); ++ return -1; ++ } ++ if (!mem->target.egm.pciDev) { ++ virReportError(VIR_ERR_XML_DETAIL, "%s", ++ _("pciDev is required for model 'egm'")); ++ return -1; ++ } ++ break; ++ + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + default: +diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng +index 839a144da8..123e4057b7 100644 +--- a/src/conf/schemas/domaincommon.rng ++++ b/src/conf/schemas/domaincommon.rng +@@ -7561,6 +7561,7 @@ + virtio-pmem + virtio-mem + sgx-epc ++ egm + + + +@@ -7692,6 +7693,11 @@ + + + ++ ++ ++ ++ ++ + + + +-- +2.54.0 diff --git a/libvirt-qemu-Add-cgroup-namespace-and-seclabel-setup-for-EGM-memory-device-model.patch b/libvirt-qemu-Add-cgroup-namespace-and-seclabel-setup-for-EGM-memory-device-model.patch new file mode 100644 index 0000000..2b6c67d --- /dev/null +++ b/libvirt-qemu-Add-cgroup-namespace-and-seclabel-setup-for-EGM-memory-device-model.patch @@ -0,0 +1,166 @@ +From d1f3b404a08e097e6ddac10452b4ed142e35a89b Mon Sep 17 00:00:00 2001 +Message-ID: +From: Pavel Hrdina +Date: Tue, 25 Nov 2025 11:17:02 -0800 +Subject: [PATCH] qemu: Add cgroup, namespace, and seclabel setup for EGM + memory device model + +From: Nathan Chen via Devel + +Implement proper isolation and access control for EGM memory devices: + +- Add device to cgroup for access control +- Set up namespace mappings for device access +- Ensure proper permissions in containerized environments +- Allow EGM device path access to bypass SELinux, AppArmor, + and DAC permissions + +Signed-off-by: Nathan Chen + +Resolves: https://redhat.atlassian.net/browse/VOYAGER-13 +--- + src/qemu/qemu_cgroup.c | 10 ++++++++++ + src/qemu/qemu_namespace.c | 3 +++ + src/security/apparmor/usr.sbin.libvirtd.in | 3 +++ + src/security/security_apparmor.c | 2 ++ + src/security/security_dac.c | 8 ++++++++ + src/security/security_selinux.c | 6 ++++++ + src/security/virt-aa-helper.c | 4 ++++ + 7 files changed, 36 insertions(+) + +diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c +index 0e1815f571..ee3795329a 100644 +--- a/src/qemu/qemu_cgroup.c ++++ b/src/qemu/qemu_cgroup.c +@@ -580,6 +580,11 @@ qemuSetupMemoryDevicesCgroup(virDomainObj *vm, + VIR_CGROUP_DEVICE_RW, false) < 0) + return -1; + break; ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ if (qemuCgroupAllowDevicePath(vm, mem->source.egm.path, ++ VIR_CGROUP_DEVICE_RW, false) < 0) ++ return -1; ++ break; + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: +@@ -618,6 +623,11 @@ qemuTeardownMemoryDevicesCgroup(virDomainObj *vm, + VIR_CGROUP_DEVICE_RW, false) < 0) + return -1; + break; ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ if (qemuCgroupDenyDevicePath(vm, mem->source.egm.path, ++ VIR_CGROUP_DEVICE_RWM, false) < 0) ++ return -1; ++ break; + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: +diff --git a/src/qemu/qemu_namespace.c b/src/qemu/qemu_namespace.c +index 4a063064f1..f17a59545b 100644 +--- a/src/qemu/qemu_namespace.c ++++ b/src/qemu/qemu_namespace.c +@@ -397,6 +397,9 @@ qemuDomainSetupMemory(virDomainMemoryDef *mem, + *paths = g_slist_prepend(*paths, g_strdup(QEMU_DEV_SGX_VEPVC)); + *paths = g_slist_prepend(*paths, g_strdup(QEMU_DEV_SGX_PROVISION)); + break; ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ *paths = g_slist_prepend(*paths, g_strdup(mem->source.egm.path)); ++ break; + + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_DIMM: +diff --git a/src/security/apparmor/usr.sbin.libvirtd.in b/src/security/apparmor/usr.sbin.libvirtd.in +index 6267e4f737..2a6a4b979c 100644 +--- a/src/security/apparmor/usr.sbin.libvirtd.in ++++ b/src/security/apparmor/usr.sbin.libvirtd.in +@@ -47,6 +47,9 @@ profile libvirtd @sbindir@/libvirtd flags=(attach_disconnected) { + mount options=(rw, move) /{,var/}run/libvirt/qemu/*{,/} -> /dev/**, + umount /{,var/}run/libvirt/qemu/*{,/}, + ++ # Allow bind mounting EGM devices into qemu namespaces ++ mount options=(rw, bind) /dev/egm* -> /{,var/}run/libvirt/qemu/**, ++ + network inet stream, + network inet dgram, + network inet6 stream, +diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c +index e53486ee0c..4d8b1e20d3 100644 +--- a/src/security/security_apparmor.c ++++ b/src/security/security_apparmor.c +@@ -631,6 +631,8 @@ AppArmorSetMemoryLabel(virSecurityManager *mgr, + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ path = mem->source.egm.path; + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; + } +diff --git a/src/security/security_dac.c b/src/security/security_dac.c +index b891f6f121..bff0e03f35 100644 +--- a/src/security/security_dac.c ++++ b/src/security/security_dac.c +@@ -1909,6 +1909,9 @@ virSecurityDACRestoreMemoryLabel(virSecurityManager *mgr, + * don't need to restore anything. */ + break; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ return virSecurityDACRestoreFileLabel(mgr, mem->source.egm.path); ++ + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: + case VIR_DOMAIN_MEMORY_MODEL_LAST: +@@ -2140,6 +2143,11 @@ virSecurityDACSetMemoryLabel(virSecurityManager *mgr, + return -1; + break; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ return virSecurityDACSetOwnership(mgr, NULL, ++ mem->source.egm.path, ++ user, group, true); ++ + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: + case VIR_DOMAIN_MEMORY_MODEL_LAST: +diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c +index 2b801aecd5..fd7d5ff703 100644 +--- a/src/security/security_selinux.c ++++ b/src/security/security_selinux.c +@@ -1666,6 +1666,9 @@ virSecuritySELinuxSetMemoryLabel(virSecurityManager *mgr, + seclabel->imagelabel, true) < 0) + return -1; + break; ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ path = mem->source.egm.path; ++ break; + + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_DIMM: +@@ -1709,6 +1712,9 @@ virSecuritySELinuxRestoreMemoryLabel(virSecurityManager *mgr, + if (virSecuritySELinuxRestoreFileLabel(mgr, DEV_SGX_PROVISION, true, false) < 0) + ret = -1; + return ret; ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ path = mem->source.egm.path; ++ break; + + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: +diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c +index af95a64c42..0a62a30da3 100644 +--- a/src/security/virt-aa-helper.c ++++ b/src/security/virt-aa-helper.c +@@ -1196,6 +1196,10 @@ get_files(vahControl * ctl) + return -1; + } + break; ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ if (vah_add_file(&buf, mem->source.egm.path, "rw") != 0) ++ return -1; ++ break; + + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: +-- +2.54.0 diff --git a/libvirt-qemu-Add-qemu-CLI-support-for-EGM.patch b/libvirt-qemu-Add-qemu-CLI-support-for-EGM.patch new file mode 100644 index 0000000..9c3d9c4 --- /dev/null +++ b/libvirt-qemu-Add-qemu-CLI-support-for-EGM.patch @@ -0,0 +1,528 @@ +From 9965f1b7b0fb1810fe32498f7892aa549a754d19 Mon Sep 17 00:00:00 2001 +Message-ID: <9965f1b7b0fb1810fe32498f7892aa549a754d19.1779098642.git.phrdina@redhat.com> +From: Pavel Hrdina +Date: Tue, 25 Nov 2025 11:17:03 -0800 +Subject: [PATCH] qemu: Add qemu CLI support for EGM + +From: Nathan Chen via Devel + +Add qemu CLI support for EGM memory device model: +- Specify EGM device path to memory-backend-file object +- Support acpi-egm-memory object with id, pci-dev, and + node attributes +- Consolidate all acpi-egm-memory objects' memory into + a single memory-backend-file per EGM chardev + specified. + +Signed-off-by: Ian May +Signed-off-by: Nathan Chen + +Resolves: https://redhat.atlassian.net/browse/VOYAGER-13 +--- + src/qemu/qemu_alias.c | 7 +- + src/qemu/qemu_capabilities.c | 2 + + src/qemu/qemu_capabilities.h | 1 + + src/qemu/qemu_command.c | 158 ++++++++++++++++++++++++++++++--- + src/qemu/qemu_domain.c | 13 ++- + src/qemu/qemu_domain_address.c | 3 + + src/qemu/qemu_driver.c | 1 + + src/qemu/qemu_hotplug.c | 1 + + src/qemu/qemu_monitor_json.c | 1 + + src/qemu/qemu_postparse.c | 1 + + src/qemu/qemu_process.c | 2 + + src/qemu/qemu_validate.c | 6 ++ + 12 files changed, 180 insertions(+), 16 deletions(-) + +diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c +index 400ce73283..719224e1ba 100644 +--- a/src/qemu/qemu_alias.c ++++ b/src/qemu/qemu_alias.c +@@ -504,7 +504,8 @@ qemuDeviceMemoryGetAliasID(virDomainDef *def, + * valid */ + if (mem->model != VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM && + mem->model != VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM && +- mem->model != VIR_DOMAIN_MEMORY_MODEL_SGX_EPC) ++ mem->model != VIR_DOMAIN_MEMORY_MODEL_SGX_EPC && ++ mem->model != VIR_DOMAIN_MEMORY_MODEL_EGM) + return mem->info.addr.dimm.slot; + + for (i = 0; i < def->nmems; i++) { +@@ -553,6 +554,10 @@ qemuAssignDeviceMemoryAlias(virDomainDef *def, + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: + prefix = "epc"; + break; ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: { ++ prefix = "egm"; ++ break; ++ } + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + default: +diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c +index ed6aa86da2..010933bc4e 100644 +--- a/src/qemu/qemu_capabilities.c ++++ b/src/qemu/qemu_capabilities.c +@@ -760,6 +760,7 @@ VIR_ENUM_IMPL(virQEMUCaps, + + /* 490 */ + "iommufd", /* QEMU_CAPS_OBJECT_IOMMUFD */ ++ "acpi-egm-memory", /* QEMU_CAPS_DEVICE_ACPI_EGM_MEMORY */ + ); + + +@@ -1468,6 +1469,7 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { + { "tpm-passthrough", QEMU_CAPS_DEVICE_TPM_PASSTHROUGH }, + { "acpi-generic-initiator", QEMU_CAPS_ACPI_GENERIC_INITIATOR }, + { "iommufd", QEMU_CAPS_OBJECT_IOMMUFD }, ++ { "acpi-egm-memory", QEMU_CAPS_DEVICE_ACPI_EGM_MEMORY }, + }; + + +diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h +index f7c8680f94..85615b4524 100644 +--- a/src/qemu/qemu_capabilities.h ++++ b/src/qemu/qemu_capabilities.h +@@ -734,6 +734,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ + + /* 490 */ + QEMU_CAPS_OBJECT_IOMMUFD, /* -object iommufd */ ++ QEMU_CAPS_DEVICE_ACPI_EGM_MEMORY, /* For using extended GPU memory */ + + QEMU_CAPS_LAST /* this must always be the last item */ + } virQEMUCapsFlags; +diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c +index a76f504e6f..63fd9885b7 100644 +--- a/src/qemu/qemu_command.c ++++ b/src/qemu/qemu_command.c +@@ -135,6 +135,21 @@ VIR_ENUM_IMPL(qemuACPITableSIG, + "SLIC", + "MSDM"); + ++typedef struct _qemuEGMBackendInfo { ++ char *alias; ++ unsigned long long totalSize; ++ bool created; ++ virDomainMemoryDef *firstMem; /* Pointer to first device for this path */ ++} qemuEGMBackendInfo; ++ ++static void ++qemuEGMBackendInfoFree(qemuEGMBackendInfo *info) ++{ ++ if (!info) ++ return; ++ g_free(info->alias); ++ g_free(info); ++} + + const char * + qemuAudioDriverTypeToString(virDomainAudioType type) +@@ -992,6 +1007,7 @@ qemuBuildVirtioDevGetConfigDev(const virDomainDeviceDef *device, + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -3166,6 +3182,7 @@ qemuBuildMemoryGetPagesize(virQEMUDriverConfig *cfg, + nvdimmPath = mem->source.virtio_pmem.path; + break; + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -3366,6 +3383,9 @@ qemuBuildMemoryBackendProps(virJSONValue **backendProps, + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: + nvdimmPath = mem->source.virtio_pmem.path; + break; ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ nvdimmPath = mem->source.egm.path; ++ break; + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -3577,12 +3597,17 @@ qemuBuildMemoryDimmBackendStr(virCommand *cmd, + virDomainMemoryDef *mem, + virDomainDef *def, + virQEMUDriverConfig *cfg, +- qemuDomainObjPrivate *priv) ++ qemuDomainObjPrivate *priv, ++ GHashTable *egmBackends) + { + g_autoptr(virJSONValue) props = NULL; + g_autoptr(virJSONValue) tcProps = NULL; + virBitmap *nodemask = NULL; + g_autofree char *alias = NULL; ++ unsigned long long originalSize = 0; ++ bool isEGM = (mem->model == VIR_DOMAIN_MEMORY_MODEL_EGM); ++ bool shouldCreateBackend = true; ++ qemuEGMBackendInfo *egmInfo = NULL; + + if (!mem->info.alias) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", +@@ -3592,19 +3617,65 @@ qemuBuildMemoryDimmBackendStr(virCommand *cmd, + + alias = g_strdup_printf("mem%s", mem->info.alias); + +- if (qemuBuildMemoryBackendProps(&props, alias, cfg, priv, +- def, mem, true, false, &nodemask) < 0) +- return -1; ++ /* Handle EGM shared backend logic */ ++ if (isEGM && egmBackends) { ++ const char *egmPath = mem->source.egm.path; ++ egmInfo = g_hash_table_lookup(egmBackends, egmPath); + +- if (qemuBuildThreadContextProps(&tcProps, &props, def, priv, nodemask) < 0) +- return -1; ++ if (egmInfo) { ++ alias = g_strdup(egmInfo->alias); ++ if (egmInfo->created) { ++ /* Backend already created, skip backend creation */ ++ shouldCreateBackend = false; ++ } else { ++ /* First device for this path - temporarily use accumulated size */ ++ originalSize = mem->size; ++ mem->size = egmInfo->totalSize; ++ egmInfo->created = true; ++ } ++ } ++ } + +- if (tcProps && +- qemuBuildObjectCommandlineFromJSON(cmd, tcProps) < 0) +- return -1; ++ if (shouldCreateBackend) { ++ /* Use existing function unchanged */ ++ if (qemuBuildMemoryBackendProps(&props, alias, cfg, priv, ++ def, mem, true, false, &nodemask) < 0) { ++ if (originalSize > 0) ++ mem->size = originalSize; /* Restore on error */ ++ return -1; ++ } + +- if (qemuBuildObjectCommandlineFromJSON(cmd, props) < 0) +- return -1; ++ /* Restore original size after backend props are built */ ++ if (originalSize > 0) ++ mem->size = originalSize; ++ ++ if (qemuBuildThreadContextProps(&tcProps, &props, def, priv, nodemask) < 0) ++ return -1; ++ ++ if (tcProps && ++ qemuBuildObjectCommandlineFromJSON(cmd, tcProps) < 0) ++ return -1; ++ ++ if (qemuBuildObjectCommandlineFromJSON(cmd, props) < 0) ++ return -1; ++ } ++ ++ if (isEGM) { ++ g_autofree char *egmObjStr = NULL; ++ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; ++ ++ virBufferAsprintf(&buf, "acpi-egm-memory,id=%s", mem->info.alias); ++ ++ if (mem->target.egm.pciDev) ++ virBufferAsprintf(&buf, ",pci-dev=%s", mem->target.egm.pciDev); ++ ++ if (mem->targetNode >= 0) ++ virBufferAsprintf(&buf, ",node=%d", mem->targetNode); ++ ++ egmObjStr = virBufferContentAndReset(&buf); ++ ++ virCommandAddArgList(cmd, "-object", egmObjStr, NULL); ++ } + + return 0; + } +@@ -3675,6 +3746,7 @@ qemuBuildMemoryDeviceProps(virQEMUDriverConfig *cfg, + dynamicMemslots = mem->target.virtio_mem.dynamicMemslots; + break; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: +@@ -7193,6 +7265,7 @@ qemuAppendDomainMemoryMachineParams(virBuffer *buf, + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -7910,6 +7983,8 @@ qemuBuildNumaCommandLine(virQEMUDriverConfig *cfg, + size_t ncells = virDomainNumaGetNodeCount(def->numa); + ssize_t masterInitiator = -1; + int rc; ++ g_autoptr(GHashTable) egmBackends = NULL; ++ size_t egmBackendCount = 0; + + if (!virDomainNumatuneNodesetIsAvailable(def->numa, priv->autoNodeset)) + goto cleanup; +@@ -7924,6 +7999,37 @@ qemuBuildNumaCommandLine(virQEMUDriverConfig *cfg, + hmat = true; + } + ++ /* Pre-scan EGM devices to group by path and calculate total sizes */ ++ egmBackends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, ++ (GDestroyNotify)qemuEGMBackendInfoFree); ++ ++ for (i = 0; i < def->nmems; i++) { ++ if (def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_EGM) { ++ const char *egmPath = def->mems[i]->source.egm.path; ++ qemuEGMBackendInfo *info = g_hash_table_lookup(egmBackends, egmPath); ++ ++ if (!info) { ++ info = g_new0(qemuEGMBackendInfo, 1); ++ info->alias = g_strdup_printf("memegm%zu", egmBackendCount); ++ egmBackendCount++; ++ info->totalSize = def->mems[i]->size; ++ info->created = false; ++ info->firstMem = def->mems[i]; ++ g_hash_table_insert(egmBackends, g_strdup(egmPath), info); ++ } else { ++ info->totalSize += def->mems[i]->size; ++ } ++ } ++ } ++ ++ /* Build the actual backend and device objects */ ++ for (i = 0; i < def->nmems; i++) { ++ if (def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_EGM) { ++ if (qemuBuildMemoryDimmBackendStr(cmd, def->mems[i], def, cfg, priv, egmBackends) < 0) ++ goto cleanup; ++ } ++ } ++ + nodeBackends = g_new0(virJSONValue *, ncells); + nodemask = g_new0(virBitmap *, ncells); + +@@ -7959,8 +8065,18 @@ qemuBuildNumaCommandLine(virQEMUDriverConfig *cfg, + for (i = 0; i < ncells; i++) { + ssize_t initiator = virDomainNumaGetNodeInitiator(def->numa, i); + unsigned long long memSize = virDomainNumaGetNodeMemorySize(def->numa, i); ++ bool egmBacked = false; ++ size_t k; + +- if (needBackend && memSize > 0) { ++ for (k = 0; k < def->nmems; k++) { ++ if (def->mems[k]->model == VIR_DOMAIN_MEMORY_MODEL_EGM && ++ def->mems[k]->targetNode == (int)i) { ++ egmBacked = true; ++ break; ++ } ++ } ++ ++ if (needBackend && memSize > 0 && !egmBacked) { + g_autoptr(virJSONValue) tcProps = NULL; + + if (qemuBuildThreadContextProps(&tcProps, &nodeBackends[i], +@@ -7990,7 +8106,15 @@ qemuBuildNumaCommandLine(virQEMUDriverConfig *cfg, + + if (memSize > 0) { + if (needBackend) { +- virBufferAsprintf(&buf, ",memdev=ram-node%zu", i); ++ if (egmBacked) { ++ /* Look up the actual backend alias for EGM */ ++ const char *egmPath = def->mems[k]->source.egm.path; ++ qemuEGMBackendInfo *egmInfo = g_hash_table_lookup(egmBackends, egmPath); ++ const char *backendAlias = egmInfo ? egmInfo->alias : def->mems[k]->info.alias; ++ virBufferAsprintf(&buf, ",memdev=%s", backendAlias); ++ } else { ++ virBufferAsprintf(&buf, ",memdev=ram-node%zu", i); ++ } + } else { + virBufferAsprintf(&buf, ",mem=%llu", memSize / 1024); + } +@@ -8054,7 +8178,10 @@ qemuBuildMemoryDeviceCommandLine(virCommand *cmd, + for (i = 0; i < def->nmems; i++) { + g_autoptr(virJSONValue) props = NULL; + +- if (qemuBuildMemoryDimmBackendStr(cmd, def->mems[i], def, cfg, priv) < 0) ++ if (def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_EGM) ++ continue; ++ ++ if (qemuBuildMemoryDimmBackendStr(cmd, def->mems[i], def, cfg, priv, NULL) < 0) + return -1; + + switch (def->mems[i]->model) { +@@ -8074,6 +8201,9 @@ qemuBuildMemoryDeviceCommandLine(virCommand *cmd, + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: + break; + ++ /* EGM memory backing is via memory-backend-file object */ ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ break; + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c +index 495cbd4f7d..c5e3d09360 100644 +--- a/src/qemu/qemu_domain.c ++++ b/src/qemu/qemu_domain.c +@@ -7266,6 +7266,7 @@ qemuDomainUpdateMemoryDeviceInfo(virDomainObj *vm, + break; + + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -7500,7 +7501,8 @@ qemuDomainAlignMemorySizes(virDomainDef *def) + def->mems[i]->size = VIR_ROUND_UP(def->mems[i]->size, align); + } + +- hotplugmem += def->mems[i]->size; ++ if (def->mems[i]->model != VIR_DOMAIN_MEMORY_MODEL_EGM) ++ hotplugmem += def->mems[i]->size; + + if (def->mems[i]->size > maxmemkb) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, +@@ -7988,6 +7990,12 @@ qemuDomainDefValidateMemoryHotplugDevice(const virDomainMemoryDef *mem, + virDomainMemoryModelTypeToString(mem->model)); + return -1; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, ++ _("hotplug is not supported for the %1$s device"), ++ virDomainMemoryModelTypeToString(mem->model)); ++ return -1; ++ + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + return -1; +@@ -8046,6 +8054,7 @@ qemuDomainDefValidateMemoryHotplug(const virDomainDef *def, + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + break; +@@ -8093,6 +8102,8 @@ qemuDomainDefValidateMemoryHotplug(const virDomainDef *def, + + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: + /* sgx epc memory does not support hotplug, skip this check */ ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ /* egm memory does not support hotplug, skip this check */ + case VIR_DOMAIN_MEMORY_MODEL_LAST: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + break; +diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c +index 7233df888c..97e533bf9a 100644 +--- a/src/qemu/qemu_domain_address.c ++++ b/src/qemu/qemu_domain_address.c +@@ -3124,6 +3124,7 @@ qemuDomainAssignMemoryDeviceSlot(virDomainObj *vm, + return qemuDomainEnsureVirtioAddress(&releaseaddr, vm, &dev); + + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -3151,6 +3152,7 @@ qemuDomainReleaseMemoryDeviceSlot(virDomainObj *vm, + break; + + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +@@ -3185,6 +3187,7 @@ qemuDomainAssignMemorySlots(virDomainDef *def) + /* handled in qemuDomainAssignPCIAddresses() */ + break; + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c +index 5e63614b4a..247c98d84e 100644 +--- a/src/qemu/qemu_driver.c ++++ b/src/qemu/qemu_driver.c +@@ -6712,6 +6712,7 @@ qemuDomainAttachMemoryConfig(virDomainDef *vmdef, + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + break; + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: +diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c +index 8944062aa4..de47702bc5 100644 +--- a/src/qemu/qemu_hotplug.c ++++ b/src/qemu/qemu_hotplug.c +@@ -7417,6 +7417,7 @@ qemuDomainChangeMemoryLiveValidateChange(const virDomainMemoryDef *oldDef, + case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("cannot modify memory of model '%1$s'"), +diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c +index 45f690d9e2..2bf9bb885e 100644 +--- a/src/qemu/qemu_monitor_json.c ++++ b/src/qemu/qemu_monitor_json.c +@@ -7285,6 +7285,7 @@ qemuMonitorJSONGetMemoryDeviceInfo(qemuMonitor *mon, + switch ((virDomainMemoryModel) model) { + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: + /* While 'id' attribute is marked as optional in QEMU's QAPI +diff --git a/src/qemu/qemu_postparse.c b/src/qemu/qemu_postparse.c +index 840d6a1174..c74824322a 100644 +--- a/src/qemu/qemu_postparse.c ++++ b/src/qemu/qemu_postparse.c +@@ -1839,6 +1839,7 @@ qemuDomainDefNumaAutoAdd(virDomainDef *def, + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; + } +diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c +index 10a4a70f1c..8132f13791 100644 +--- a/src/qemu/qemu_process.c ++++ b/src/qemu/qemu_process.c +@@ -4158,6 +4158,7 @@ qemuProcessDomainMemoryDefNeedHugepagesPath(const virDomainMemoryDef *mem, + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: + pagesize = mem->source.virtio_mem.pagesize; + break; ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: +@@ -4247,6 +4248,7 @@ qemuProcessNeedMemoryBackingPath(virDomainDef *def, + case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + /* Backed by user provided path. Not stored in memory + * backing dir anyway. */ +diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c +index c39d8c869f..e03da3e27e 100644 +--- a/src/qemu/qemu_validate.c ++++ b/src/qemu/qemu_validate.c +@@ -5892,6 +5892,12 @@ qemuValidateDomainDeviceDefMemory(const virDomainMemoryDef *mem, + + break; + ++ case VIR_DOMAIN_MEMORY_MODEL_EGM: ++ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ACPI_EGM_MEMORY)) { ++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", ++ _("ACPI EGM memory device is not supported with this QEMU binary")); ++ return -1; ++ } + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; +-- +2.54.0 diff --git a/libvirt-tests-Add-qemuxmlconftest-for-ACPI-EGM-memory-device.patch b/libvirt-tests-Add-qemuxmlconftest-for-ACPI-EGM-memory-device.patch new file mode 100644 index 0000000..b2a528e --- /dev/null +++ b/libvirt-tests-Add-qemuxmlconftest-for-ACPI-EGM-memory-device.patch @@ -0,0 +1,526 @@ +From 980f098c11773796a35b8e2cb6ae9a1c2296c8b4 Mon Sep 17 00:00:00 2001 +Message-ID: <980f098c11773796a35b8e2cb6ae9a1c2296c8b4.1779098642.git.phrdina@redhat.com> +From: Pavel Hrdina +Date: Tue, 25 Nov 2025 11:17:04 -0800 +Subject: [PATCH] tests: Add qemuxmlconftest for ACPI EGM memory device + +From: Ian May + +Add test coverage for the ACPI EGM memory device feature: + +- Add test case to qemuxmlconftest.c for aarch64 architecture +- Add acpi-egm-memory capability to QEMU 10.0.0 aarch64 capabilities +- Create test input XML with EGM device configuration +- Generate expected output XML and QEMU command line args +- Update validation to skip filesystem checks during tests + +The test validates XML parsing, formatting, device validation, and +QEMU command line generation for the EGM device. + +Signed-off-by: Ian May +Signed-off-by: Nathan Chen + +Resolves: https://redhat.atlassian.net/browse/VOYAGER-13 +--- + src/conf/domain_conf.c | 5 +- + src/conf/domain_postparse.c | 5 +- + src/qemu/qemu_domain.c | 2 + + src/util/virfile.h | 2 +- + tests/meson.build | 1 + + tests/qemuegmmock.c | 67 ++++++++++ + .../acpi-egm-memory.aarch64-latest.args | 47 +++++++ + .../acpi-egm-memory.aarch64-latest.xml | 124 ++++++++++++++++++ + tests/qemuxmlconfdata/acpi-egm-memory.xml | 124 ++++++++++++++++++ + tests/qemuxmlconftest.c | 8 +- + 10 files changed, 381 insertions(+), 4 deletions(-) + create mode 100644 tests/qemuegmmock.c + create mode 100644 tests/qemuxmlconfdata/acpi-egm-memory.aarch64-latest.args + create mode 100644 tests/qemuxmlconfdata/acpi-egm-memory.aarch64-latest.xml + create mode 100644 tests/qemuxmlconfdata/acpi-egm-memory.xml + +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index 038476edd7..d3236b7425 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -8930,8 +8930,11 @@ virDomainDefGetMemoryInitial(const virDomainDef *def) + size_t i; + unsigned long long ret = def->mem.total_memory; + +- for (i = 0; i < def->nmems; i++) ++ for (i = 0; i < def->nmems; i++) { ++ if (def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_EGM) ++ continue; + ret -= def->mems[i]->size; ++ } + + return ret; + } +diff --git a/src/conf/domain_postparse.c b/src/conf/domain_postparse.c +index 0181d21f0e..bb4a61b7d8 100644 +--- a/src/conf/domain_postparse.c ++++ b/src/conf/domain_postparse.c +@@ -44,8 +44,11 @@ virDomainDefPostParseMemory(virDomainDef *def, + numaMemory = virDomainNumaGetMemorySize(def->numa); + + /* calculate the sizes of hotplug memory */ +- for (i = 0; i < def->nmems; i++) ++ for (i = 0; i < def->nmems; i++) { ++ if (def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_EGM) ++ continue; + hotplugMemory += def->mems[i]->size; ++ } + + if (numaMemory) { + /* update the sizes in XML if nothing was set in the XML or ABI update +diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c +index c5e3d09360..d1bc9aa621 100644 +--- a/src/qemu/qemu_domain.c ++++ b/src/qemu/qemu_domain.c +@@ -8085,6 +8085,8 @@ qemuDomainDefValidateMemoryHotplug(const virDomainDef *def, + } + + for (i = 0; i < def->nmems; i++) { ++ if (def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_EGM) ++ continue; + hotplugMemory += def->mems[i]->size; + + switch (def->mems[i]->model) { +diff --git a/src/util/virfile.h b/src/util/virfile.h +index ce2ffb8ed4..5203ef4425 100644 +--- a/src/util/virfile.h ++++ b/src/util/virfile.h +@@ -167,7 +167,7 @@ int virFileReadHeaderQuiet(const char *path, int maxlen, char **buf) + int virFileReadLimFD(int fd, int maxlen, char **buf) + G_GNUC_WARN_UNUSED_RESULT ATTRIBUTE_NONNULL(3); + int virFileReadAll(const char *path, int maxlen, char **buf) +- G_GNUC_WARN_UNUSED_RESULT ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); ++ G_GNUC_WARN_UNUSED_RESULT ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_MOCKABLE; + int virFileReadAllQuiet(const char *path, int maxlen, char **buf) + G_GNUC_WARN_UNUSED_RESULT ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); + int virFileReadBufQuiet(const char *file, char *buf, int len) +diff --git a/tests/meson.build b/tests/meson.build +index bb6ee6b4ee..28ee8591a3 100644 +--- a/tests/meson.build ++++ b/tests/meson.build +@@ -174,6 +174,7 @@ if conf.has('WITH_QEMU') + { 'name': 'qemucaps2xmlmock' }, + { 'name': 'qemucapsprobemock', 'link_with': [ test_qemu_driver_lib ] }, + { 'name': 'qemucpumock' }, ++ { 'name': 'qemuegmmock' }, + { 'name': 'qemuhotplugmock', 'link_with': [ test_qemu_driver_lib, test_utils_qemu_lib, test_utils_lib ] }, + { 'name': 'qemuxml2argvmock' }, + { 'name': 'virhostidmock' }, +diff --git a/tests/qemuegmmock.c b/tests/qemuegmmock.c +new file mode 100644 +index 0000000000..c915212f45 +--- /dev/null ++++ b/tests/qemuegmmock.c +@@ -0,0 +1,67 @@ ++/* ++ * Copyright (C) 2024 Red Hat, Inc. ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ */ ++ ++#include ++#include ++ ++#include "internal.h" ++#include "virfile.h" ++#include "virmock.h" ++ ++static bool (*real_virFileExists)(const char *path); ++static int (*real_access)(const char *path, int mode); ++static int (*real_virFileReadAll)(const char *path, int maxlen, char **buf); ++ ++static void ++init_syms(void) ++{ ++ if (real_virFileExists && real_access && real_virFileReadAll) ++ return; ++ ++ VIR_MOCK_REAL_INIT(virFileExists); ++ VIR_MOCK_REAL_INIT(access); ++ VIR_MOCK_REAL_INIT(virFileReadAll); ++} ++ ++bool ++virFileExists(const char *path) ++{ ++ init_syms(); ++ ++ /* Mock EGM device paths for testing */ ++ if (g_str_has_prefix(path, "/dev/egm") || ++ g_str_has_prefix(path, "/sys/class/egm/")) ++ return true; ++ ++ return real_virFileExists(path); ++} ++ ++int ++access(const char *path, int mode) ++{ ++ init_syms(); ++ ++ /* Mock EGM device paths for testing */ ++ if (g_str_has_prefix(path, "/dev/egm") || ++ g_str_has_prefix(path, "/sys/class/egm/")) ++ return 0; /* success */ ++ ++ return real_access(path, mode); ++} ++ ++int ++virFileReadAll(const char *path, int maxlen, char **buf) ++{ ++ init_syms(); ++ ++ /* Mock EGM GPU device file for testing */ ++ if (g_str_has_prefix(path, "/sys/class/egm/") && ++ g_str_has_suffix(path, "/gpu_devices")) { ++ *buf = g_strdup("0000:01:00.0\n"); ++ return strlen(*buf); ++ } ++ ++ return real_virFileReadAll(path, maxlen, buf); ++} +diff --git a/tests/qemuxmlconfdata/acpi-egm-memory.aarch64-latest.args b/tests/qemuxmlconfdata/acpi-egm-memory.aarch64-latest.args +new file mode 100644 +index 0000000000..41d1fcc026 +--- /dev/null ++++ b/tests/qemuxmlconfdata/acpi-egm-memory.aarch64-latest.args +@@ -0,0 +1,47 @@ ++LC_ALL=C \ ++PATH=/bin \ ++HOME=/var/lib/libvirt/qemu/domain--1-egm \ ++USER=test \ ++LOGNAME=test \ ++XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-egm/.local/share \ ++XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-egm/.cache \ ++XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-egm/.config \ ++/usr/bin/qemu-system-aarch64 \ ++-name guest=egm,debug-threads=on \ ++-S \ ++-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-egm/master-key.aes"}' \ ++-machine virt,usb=off,gic-version=3,dump-guest-core=off,acpi=off \ ++-accel kvm \ ++-cpu host \ ++-m size=524288k,maxmem=524288k \ ++-overcommit mem-lock=off \ ++-smp 4,sockets=2,dies=1,clusters=1,cores=2,threads=1 \ ++-object '{"qom-type":"memory-backend-file","id":"memegm0","mem-path":"/dev/egm4","share":true,"size":268435456}' \ ++-object acpi-egm-memory,id=egm0,pci-dev=ua-hostdev0,node=0 \ ++-object acpi-egm-memory,id=egm1,pci-dev=ua-hostdev1,node=0 \ ++-object '{"qom-type":"memory-backend-file","id":"memegm1","mem-path":"/dev/egm5","share":true,"size":268435456}' \ ++-object acpi-egm-memory,id=egm2,pci-dev=ua-hostdev2,node=1 \ ++-object acpi-egm-memory,id=egm3,pci-dev=ua-hostdev3,node=1 \ ++-numa node,nodeid=0,cpus=0-1,memdev=memegm0 \ ++-numa node,nodeid=1,cpus=2-3,memdev=memegm1 \ ++-uuid 00010203-0405-4607-8809-0a0b0c0d0e0f \ ++-display none \ ++-no-user-config \ ++-nodefaults \ ++-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \ ++-mon chardev=charmonitor,id=monitor,mode=control \ ++-rtc base=utc \ ++-no-shutdown \ ++-boot strict=on \ ++-device '{"driver":"pcie-root-port","port":8,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x1"}' \ ++-device '{"driver":"pcie-root-port","port":9,"chassis":2,"id":"pci.2","bus":"pcie.0","addr":"0x1.0x1"}' \ ++-device '{"driver":"pcie-root-port","port":10,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x2"}' \ ++-device '{"driver":"pcie-root-port","port":11,"chassis":4,"id":"pci.4","bus":"pcie.0","addr":"0x3"}' \ ++-device '{"driver":"pcie-root-port","port":12,"chassis":5,"id":"pci.5","bus":"pcie.0","addr":"0x4"}' \ ++-audiodev '{"id":"audio1","driver":"none"}' \ ++-device '{"driver":"vfio-pci","host":"0000:01:00.0","id":"ua-hostdev0","bus":"pci.1","addr":"0x0"}' \ ++-device '{"driver":"vfio-pci","host":"0000:02:00.0","id":"ua-hostdev1","bus":"pci.3","addr":"0x0"}' \ ++-device '{"driver":"vfio-pci","host":"0000:03:00.0","id":"ua-hostdev2","bus":"pci.4","addr":"0x0"}' \ ++-device '{"driver":"vfio-pci","host":"0000:04:00.0","id":"ua-hostdev3","bus":"pci.5","addr":"0x0"}' \ ++-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ ++-msg timestamp=on +diff --git a/tests/qemuxmlconfdata/acpi-egm-memory.aarch64-latest.xml b/tests/qemuxmlconfdata/acpi-egm-memory.aarch64-latest.xml +new file mode 100644 +index 0000000000..bd73d613e5 +--- /dev/null ++++ b/tests/qemuxmlconfdata/acpi-egm-memory.aarch64-latest.xml +@@ -0,0 +1,124 @@ ++ ++ egm ++ 00010203-0405-4607-8809-0a0b0c0d0e0f ++ 524288 ++ 524288 ++ 524288 ++ 4 ++ ++ hvm ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ destroy ++ restart ++ destroy ++ ++ /usr/bin/qemu-system-aarch64 ++ ++ ++ ++ ++
++ ++ ++ ++ ++
++ ++ ++ ++ ++
++ ++ ++ ++ ++
++ ++ ++ ++ ++
++ ++