From: Ian May <ianm@nvidia.com>
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 <ianm@nvidia.com>
Signed-off-by: Nathan Chen <nathanc@nvidia.com>
---
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 2cda32fa6e..3fba40476f 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -8813,8 +8813,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 14f2b3ec5d..1b0b375a49 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -8038,6 +8038,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 <config.h>
+#include <unistd.h>
+
+#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 @@
+<domain type='kvm'>
+ <name>egm</name>
+ <uuid>00010203-0405-4607-8809-0a0b0c0d0e0f</uuid>
+ <maxMemory unit='KiB'>524288</maxMemory>
+ <memory unit='KiB'>524288</memory>
+ <currentMemory unit='KiB'>524288</currentMemory>
+ <vcpu placement='static'>4</vcpu>
+ <os>
+ <type arch='aarch64' machine='virt'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <gic version='3'/>
+ </features>
+ <cpu mode='host-passthrough' check='none'>
+ <topology sockets='2' dies='1' clusters='1' cores='2' threads='1'/>
+ <numa>
+ <cell id='0' cpus='0-1' memory='262144' unit='KiB'/>
+ <cell id='1' cpus='2-3' memory='262144' unit='KiB'/>
+ </numa>
+ </cpu>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-aarch64</emulator>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x8'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x9'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0xa'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0xb'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0xc'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+ </controller>
+ <audio id='1' type='none'/>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <source>
+ <address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </source>
+ <alias name='ua-hostdev0'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <source>
+ <address domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </source>
+ <alias name='ua-hostdev1'/>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <source>
+ <address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </source>
+ <alias name='ua-hostdev2'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <source>
+ <address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </source>
+ <alias name='ua-hostdev3'/>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </hostdev>
+ <memory model='egm' access='shared'>
+ <source>
+ <path>/dev/egm4</path>
+ </source>
+ <target>
+ <size unit='KiB'>131072</size>
+ <node>0</node>
+ <pciDev>ua-hostdev0</pciDev>
+ </target>
+ </memory>
+ <memory model='egm' access='shared'>
+ <source>
+ <path>/dev/egm4</path>
+ </source>
+ <target>
+ <size unit='KiB'>131072</size>
+ <node>0</node>
+ <pciDev>ua-hostdev1</pciDev>
+ </target>
+ </memory>
+ <memory model='egm' access='shared'>
+ <source>
+ <path>/dev/egm5</path>
+ </source>
+ <target>
+ <size unit='KiB'>131072</size>
+ <node>1</node>
+ <pciDev>ua-hostdev2</pciDev>
+ </target>
+ </memory>
+ <memory model='egm' access='shared'>
+ <source>
+ <path>/dev/egm5</path>
+ </source>
+ <target>
+ <size unit='KiB'>131072</size>
+ <node>1</node>
+ <pciDev>ua-hostdev3</pciDev>
+ </target>
+ </memory>
+ </devices>
+</domain>
diff --git a/tests/qemuxmlconfdata/acpi-egm-memory.xml b/tests/qemuxmlconfdata/acpi-egm-memory.xml
new file mode 100644
index 0000000000..bd73d613e5
--- /dev/null
+++ b/tests/qemuxmlconfdata/acpi-egm-memory.xml
@@ -0,0 +1,124 @@
+<domain type='kvm'>
+ <name>egm</name>
+ <uuid>00010203-0405-4607-8809-0a0b0c0d0e0f</uuid>
+ <maxMemory unit='KiB'>524288</maxMemory>
+ <memory unit='KiB'>524288</memory>
+ <currentMemory unit='KiB'>524288</currentMemory>
+ <vcpu placement='static'>4</vcpu>
+ <os>
+ <type arch='aarch64' machine='virt'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <gic version='3'/>
+ </features>
+ <cpu mode='host-passthrough' check='none'>
+ <topology sockets='2' dies='1' clusters='1' cores='2' threads='1'/>
+ <numa>
+ <cell id='0' cpus='0-1' memory='262144' unit='KiB'/>
+ <cell id='1' cpus='2-3' memory='262144' unit='KiB'/>
+ </numa>
+ </cpu>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-aarch64</emulator>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x8'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x9'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0xa'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0xb'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0xc'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+ </controller>
+ <audio id='1' type='none'/>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <source>
+ <address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </source>
+ <alias name='ua-hostdev0'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <source>
+ <address domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </source>
+ <alias name='ua-hostdev1'/>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <source>
+ <address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </source>
+ <alias name='ua-hostdev2'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <source>
+ <address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </source>
+ <alias name='ua-hostdev3'/>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </hostdev>
+ <memory model='egm' access='shared'>
+ <source>
+ <path>/dev/egm4</path>
+ </source>
+ <target>
+ <size unit='KiB'>131072</size>
+ <node>0</node>
+ <pciDev>ua-hostdev0</pciDev>
+ </target>
+ </memory>
+ <memory model='egm' access='shared'>
+ <source>
+ <path>/dev/egm4</path>
+ </source>
+ <target>
+ <size unit='KiB'>131072</size>
+ <node>0</node>
+ <pciDev>ua-hostdev1</pciDev>
+ </target>
+ </memory>
+ <memory model='egm' access='shared'>
+ <source>
+ <path>/dev/egm5</path>
+ </source>
+ <target>
+ <size unit='KiB'>131072</size>
+ <node>1</node>
+ <pciDev>ua-hostdev2</pciDev>
+ </target>
+ </memory>
+ <memory model='egm' access='shared'>
+ <source>
+ <path>/dev/egm5</path>
+ </source>
+ <target>
+ <size unit='KiB'>131072</size>
+ <node>1</node>
+ <pciDev>ua-hostdev3</pciDev>
+ </target>
+ </memory>
+ </devices>
+</domain>
diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c
index de03c58c8a..07ffdf164c 100644
--- a/tests/qemuxmlconftest.c
+++ b/tests/qemuxmlconftest.c
@@ -3211,6 +3211,10 @@ mymain(void)
DO_TEST_CAPS_LATEST("devices-acpi-index");
+ DO_TEST_CAPS_ARCH_LATEST_FULL("acpi-egm-memory", "aarch64",
+ ARG_QEMU_CAPS, QEMU_CAPS_DEVICE_ACPI_EGM_MEMORY,
+ ARG_END);
+
DO_TEST_CAPS_ARCH_LATEST_FULL("hvf-x86_64-q35-headless", "x86_64", ARG_CAPS_VARIANT, "+hvf", ARG_END);
DO_TEST_CAPS_ARCH_LATEST_FULL("hvf-aarch64-virt-headless", "aarch64", ARG_CAPS_VARIANT, "+hvf", ARG_END);
/* HVF guests should not work on Linux with KVM */
@@ -3317,7 +3321,9 @@ VIR_TEST_MAIN_PRELOAD(mymain,
VIR_TEST_MOCK("domaincaps"),
VIR_TEST_MOCK("virrandom"),
VIR_TEST_MOCK("qemucpu"),
- VIR_TEST_MOCK("virnuma"))
+ VIR_TEST_MOCK("virnuma"),
+ VIR_TEST_MOCK("virpci"),
+ VIR_TEST_MOCK("qemuegm"))
#else
--
2.43.0
© 2016 - 2025 Red Hat, Inc.