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 <[email protected]> Signed-off-by: Nathan Chen <[email protected]> --- 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 92b863a826..3fc8fee4b3 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -755,6 +755,7 @@ VIR_ENUM_IMPL(virQEMUCaps, "disk-timed-stats", /* QEMU_CAPS_DISK_TIMED_STATS */ "query-accelerators", /* QEMU_CAPS_QUERY_ACCELERATORS */ "mshv", /* QEMU_CAPS_MSHV */ + "acpi-egm-memory", /* QEMU_CAPS_DEVICE_ACPI_EGM_MEMORY */ ); @@ -1462,6 +1463,7 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { { "tpm-emulator", QEMU_CAPS_DEVICE_TPM_EMULATOR }, { "tpm-passthrough", QEMU_CAPS_DEVICE_TPM_PASSTHROUGH }, { "acpi-generic-initiator", QEMU_CAPS_ACPI_GENERIC_INITIATOR }, + { "acpi-egm-memory", QEMU_CAPS_DEVICE_ACPI_EGM_MEMORY }, }; diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index f180844e66..3eb12235f4 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -730,6 +730,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ QEMU_CAPS_DISK_TIMED_STATS, /* timed stats support ('stats-intervals' property of disk frontends) */ QEMU_CAPS_QUERY_ACCELERATORS, /* query-accelerators command */ QEMU_CAPS_MSHV, /* -accel mshv */ + 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 b69fe23236..33848aa781 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; @@ -3162,6 +3178,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; @@ -3362,6 +3379,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; @@ -3573,12 +3593,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", @@ -3588,19 +3613,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; } @@ -3671,6 +3742,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: @@ -7104,6 +7176,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; @@ -7821,6 +7894,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; @@ -7835,6 +7910,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); @@ -7870,8 +7976,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; + + 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) { + if (needBackend && memSize > 0 && !egmBacked) { g_autoptr(virJSONValue) tcProps = NULL; if (qemuBuildThreadContextProps(&tcProps, &nodeBackends[i], @@ -7901,7 +8017,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); } @@ -7965,7 +8089,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) { @@ -7985,6 +8112,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 ac56fc7cb4..14f2b3ec5d 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -7219,6 +7219,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; @@ -7453,7 +7454,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, @@ -7941,6 +7943,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; @@ -7999,6 +8007,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; @@ -8046,6 +8055,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 f2e024dae3..e0ee056b92 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 fb426deb1a..890bb052b6 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -7348,6 +7348,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 494d7ef515..081f0cedfd 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -7213,6 +7213,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 dc5ade829a..3c2867edb3 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 0e50cd1ccc..d451c12dd0 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4156,6 +4156,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: @@ -4245,6 +4246,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 da08fd17cd..e026e2fdd0 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -5861,6 +5861,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.43.0
