From: Ian May <[email protected]>

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. Filesystem validation
is conditionally skipped in test environments while preserving full
validation for production use.

Signed-off-by: Ian May <[email protected]>
Signed-off-by: Nathan Chen <[email protected]>
---
 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       | 36 ++++++++++
 .../acpi-egm-memory.aarch64-latest.xml        | 57 ++++++++++++++++
 tests/qemuxmlconfdata/acpi-egm-memory.xml     | 33 +++++++++
 tests/qemuxmlconftest.c                       |  8 ++-
 10 files changed, 212 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 ac97b44b9e..077303fc19 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -8781,8 +8781,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 75095b4de8..72b7fd7047 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -8039,6 +8039,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 0d76d37959..312c05a5f1 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..938b085d56
--- /dev/null
+++ b/tests/qemuxmlconfdata/acpi-egm-memory.aarch64-latest.args
@@ -0,0 +1,36 @@
+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 1,sockets=1,dies=1,clusters=1,cores=1,threads=1 \
+-object 
'{"qom-type":"memory-backend-file","id":"memegm0","mem-path":"/dev/egm0","share":true,"size":536870912}'
 \
+-object acpi-egm-memory,id=egm0,pci-dev=ua-hostdev0,node=0 \
+-numa node,nodeid=0,cpus=0,memdev=memegm0 \
+-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"}'
 \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-device 
'{"driver":"vfio-pci","host":"0000:01:00.0","id":"ua-hostdev0","bus":"pci.1","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..81928b316c
--- /dev/null
+++ b/tests/qemuxmlconfdata/acpi-egm-memory.aarch64-latest.xml
@@ -0,0 +1,57 @@
+<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'>1</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='1' dies='1' clusters='1' cores='1' threads='1'/>
+    <numa>
+      <cell id='0' cpus='0' memory='524288' 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>
+    <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>
+    <memory model='egm' access='shared'>
+      <source>
+        <path>/dev/egm0</path>
+      </source>
+      <target>
+        <size unit='KiB'>524288</size>
+        <node>0</node>
+        <pciDev>ua-hostdev0</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..b35368bdcf
--- /dev/null
+++ b/tests/qemuxmlconfdata/acpi-egm-memory.xml
@@ -0,0 +1,33 @@
+<domain type="kvm">
+  <name>egm</name>
+  <maxMemory unit="MiB">512</maxMemory>
+  <memory unit="MiB">512</memory>
+  <vcpu>1</vcpu>
+  <os>
+    <type arch="aarch64" machine="virt">hvm</type>
+  </os>
+  <cpu mode="host-passthrough">
+    <topology sockets="1" cores="1" threads="1"/>
+    <numa>
+      <cell id="0" cpus="0" memory="512" unit="MiB"/>
+    </numa>
+  </cpu>
+  <devices>
+    <hostdev mode="subsystem" type="pci" managed="yes">
+      <alias name="ua-hostdev0"/>
+      <source>
+        <address domain="0x0000" bus="0x01" slot="0x00" function="0x0"/>
+      </source>
+    </hostdev>
+    <memory model='egm' access='shared'>
+      <source>
+        <path>/dev/egm0</path>
+      </source>
+      <target>
+        <size unit='KiB'>524288</size>
+        <node>0</node>
+        <pciDev>ua-hostdev0</pciDev>
+      </target>
+    </memory>
+  </devices>
+</domain>
diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c
index e4d80faa99..68150aca8d 100644
--- a/tests/qemuxmlconftest.c
+++ b/tests/qemuxmlconftest.c
@@ -3204,6 +3204,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 */
@@ -3304,7 +3308,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

Reply via email to