Introduce a TYPE_MEMORY_DEVICE subclass `spm-memory` for boot-time
SOFT_RESERVED memory exposed to the guest with a per-device NUMA
proximity domain.
The device targets accelerator memory (HBM and similar) that the
firmware hands to the guest OS as SOFT_RESERVED memory, so a driver
in the guest -- rather than the kernel's general allocator -- owns
the range. Per-device NUMA placement matches the natural shape of
multiple HBM blocks (one block == one driver claim == one PXM).
Usage:
-object memory-backend-ram,id=spm0,size=8G
-numa node,nodeid=N
-device spm-memory,id=dev0,memdev=spm0,node=N[,addr=GPA]
The device:
- inherits TYPE_DEVICE and implements TYPE_MEMORY_DEVICE; placement
in machine->device_memory goes through the standard memory-device
framework (memory_device_pre_plug + memory_device_plug)
- is boot-time only: dc->hotpluggable = false, and realize rejects
attempts past PHASE_MACHINE_READY
- emits one E820 SOFT_RESERVED entry per instance at machine_done
- emits one SRAT memory_affinity entry per instance at acpi-build,
ENABLED-only (no HOTPLUGGABLE flag)
- rejects mixed-memory configurations on the target NUMA node at
realize-time
- is reported by QMP query-memory-devices as a dedicated kind,
MEMORY_DEVICE_INFO_KIND_SPM_MEMORY
The device_memory SRAT umbrella entry in hw/i386/acpi-build.c is
restructured to partition the region into per-kind chunks rather
than emitting a single HOTPLUGGABLE entry covering everything.
For each plugged TYPE_SPM_MEMORY device the partition emits an
ENABLED entry at the device's proximity_domain; the remaining
sub-ranges (gaps between SPM devices, leading and trailing
padding, and ranges occupied by non-SPM memory devices) are
emitted as HOTPLUGGABLE | ENABLED entries at the placeholder
PXM (nb_numa_nodes - 1), preserving the upstream convention.
E820_SOFT_RESERVED is added to hw/i386/e820_memory_layout.h
alongside the other type codes.
CONFIG_SPM_MEMORY is selected by the i386 PC and Q35 machines
(same as DIMM).
MAINTAINERS gets new file entries under the existing "Memory devices"
stanza.
Signed-off-by: FangSheng Huang <[email protected]>
---
MAINTAINERS | 2 +
hw/i386/Kconfig | 2 +
hw/i386/acpi-build.c | 105 ++++++++++++--
hw/i386/e820_memory_layout.h | 11 +-
hw/mem/Kconfig | 4 +
hw/mem/meson.build | 1 +
hw/mem/spm-memory.c | 269 +++++++++++++++++++++++++++++++++++
include/hw/mem/spm-memory.h | 43 ++++++
qapi/machine.json | 43 +++++-
9 files changed, 459 insertions(+), 21 deletions(-)
create mode 100644 hw/mem/spm-memory.c
create mode 100644 include/hw/mem/spm-memory.h
diff --git a/MAINTAINERS b/MAINTAINERS
index cd5c4831e2..2a06515fc8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3361,9 +3361,11 @@ S: Supported
F: hw/mem/memory-device*.c
F: hw/mem/nvdimm.c
F: hw/mem/pc-dimm.c
+F: hw/mem/spm-memory.c
F: include/hw/mem/memory-device.h
F: include/hw/mem/nvdimm.h
F: include/hw/mem/pc-dimm.h
+F: include/hw/mem/spm-memory.h
F: docs/nvdimm.txt
SPICE
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index 12473acaa7..e31a25b634 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -84,6 +84,7 @@ config I440FX
select PCI_I440FX
select PIIX
select DIMM
+ select SPM_MEMORY
select SMBIOS
select SMBIOS_LEGACY
select FW_CFG_DMA
@@ -113,6 +114,7 @@ config Q35
select LPC_ICH9
select AHCI_ICH9
select DIMM
+ select SPM_MEMORY
select SMBIOS
select FW_CFG_DMA
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 0d7c83d5e9..865ab5fa4f 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -52,6 +52,7 @@
#include "migration/vmstate.h"
#include "hw/mem/memory-device.h"
#include "hw/mem/nvdimm.h"
+#include "hw/mem/spm-memory.h"
#include "system/numa.h"
#include "system/reset.h"
#include "hw/hyperv/vmbus-bridge.h"
@@ -1346,6 +1347,95 @@ build_tpm_tcpa(GArray *table_data, BIOSLinker *linker,
GArray *tcpalog,
}
#endif
+typedef struct {
+ uint64_t addr;
+ uint64_t size;
+ uint32_t node;
+} SpmRange;
+
+static int collect_spm_ranges_cb(Object *obj, void *opaque)
+{
+ GArray *ranges = opaque;
+ SpmMemoryDevice *spm;
+ MemoryDeviceClass *mdc;
+ SpmRange r;
+
+ if (!object_dynamic_cast(obj, TYPE_SPM_MEMORY)) {
+ return 0;
+ }
+ spm = SPM_MEMORY(obj);
+ mdc = MEMORY_DEVICE_GET_CLASS(MEMORY_DEVICE(spm));
+ r.addr = mdc->get_addr(MEMORY_DEVICE(spm));
+ r.size = memory_region_size(
+ host_memory_backend_get_memory(spm->hostmem));
+ r.node = spm->node;
+ g_array_append_val(ranges, r);
+ return 0;
+}
+
+static gint spm_range_compare(gconstpointer a, gconstpointer b)
+{
+ const SpmRange *range_a = a;
+ const SpmRange *range_b = b;
+
+ if (range_a->addr < range_b->addr) {
+ return -1;
+ }
+ if (range_a->addr > range_b->addr) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Emit SRAT memory-affinity entries covering the device_memory region:
+ * - ENABLED entry at the device's proximity_domain for each plugged
+ * TYPE_SPM_MEMORY instance.
+ * - HOTPLUGGABLE | ENABLED entry with PXM = nb_numa_nodes - 1 for
+ * every remaining sub-range (gaps, leading/trailing padding, and
+ * ranges occupied by non-SPM memory devices).
+ */
+static void build_srat_device_memory(GArray *table_data, MachineState *ms)
+{
+ g_autoptr(GArray) ranges = g_array_new(FALSE, TRUE, sizeof(SpmRange));
+ uint64_t cursor, end;
+ int nb_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
+ uint32_t hotplug_pxm = nb_nodes > 0 ? nb_nodes - 1 : 0;
+ guint i;
+
+ if (!ms->device_memory) {
+ return;
+ }
+
+ cursor = ms->device_memory->base;
+ end = cursor + memory_region_size(&ms->device_memory->mr);
+
+ object_child_foreach_recursive(qdev_get_machine(),
+ collect_spm_ranges_cb, ranges);
+ g_array_sort(ranges, spm_range_compare);
+
+ for (i = 0; i < ranges->len; i++) {
+ SpmRange *r = &g_array_index(ranges, SpmRange, i);
+
+ if (cursor < r->addr) {
+ build_srat_memory(table_data, cursor, r->addr - cursor,
+ hotplug_pxm,
+ MEM_AFFINITY_HOTPLUGGABLE |
+ MEM_AFFINITY_ENABLED);
+ }
+ build_srat_memory(table_data, r->addr, r->size, r->node,
+ MEM_AFFINITY_ENABLED);
+ cursor = r->addr + r->size;
+ }
+
+ if (cursor < end) {
+ build_srat_memory(table_data, cursor, end - cursor,
+ hotplug_pxm,
+ MEM_AFFINITY_HOTPLUGGABLE |
+ MEM_AFFINITY_ENABLED);
+ }
+}
+
#define HOLE_640K_START (640 * KiB)
#define HOLE_640K_END (1 * MiB)
@@ -1473,20 +1563,7 @@ build_srat(GArray *table_data, BIOSLinker *linker,
MachineState *machine)
build_srat_generic_affinity_structures(table_data);
- /*
- * Entry is required for Windows to enable memory hotplug in OS
- * and for Linux to enable SWIOTLB when booted with less than
- * 4G of RAM. Windows works better if the entry sets proximity
- * to the highest NUMA node in the machine.
- * Memory devices may override proximity set by this entry,
- * providing _PXM method if necessary.
- */
- if (machine->device_memory) {
- build_srat_memory(table_data, machine->device_memory->base,
- memory_region_size(&machine->device_memory->mr),
- nb_numa_nodes - 1,
- MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
- }
+ build_srat_device_memory(table_data, machine);
acpi_table_end(linker, &table);
}
diff --git a/hw/i386/e820_memory_layout.h b/hw/i386/e820_memory_layout.h
index b50acfa201..6ef169db9c 100644
--- a/hw/i386/e820_memory_layout.h
+++ b/hw/i386/e820_memory_layout.h
@@ -10,11 +10,12 @@
#define HW_I386_E820_MEMORY_LAYOUT_H
/* e820 types */
-#define E820_RAM 1
-#define E820_RESERVED 2
-#define E820_ACPI 3
-#define E820_NVS 4
-#define E820_UNUSABLE 5
+#define E820_RAM 1
+#define E820_RESERVED 2
+#define E820_ACPI 3
+#define E820_NVS 4
+#define E820_UNUSABLE 5
+#define E820_SOFT_RESERVED 0xefffffff
struct e820_entry {
uint64_t address;
diff --git a/hw/mem/Kconfig b/hw/mem/Kconfig
index 73c5ae8ad9..4145870881 100644
--- a/hw/mem/Kconfig
+++ b/hw/mem/Kconfig
@@ -16,3 +16,7 @@ config CXL_MEM_DEVICE
bool
default y if CXL
select MEM_DEVICE
+
+config SPM_MEMORY
+ bool
+ select MEM_DEVICE
diff --git a/hw/mem/meson.build b/hw/mem/meson.build
index 8c2beeb7d4..2c28104282 100644
--- a/hw/mem/meson.build
+++ b/hw/mem/meson.build
@@ -4,6 +4,7 @@ mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c'))
mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c'))
mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c'))
mem_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_true: files('cxl_type3.c'))
+mem_ss.add(when: 'CONFIG_SPM_MEMORY', if_true: files('spm-memory.c'))
stub_ss.add(files('cxl_type3_stubs.c'))
stub_ss.add(files('memory-device-stubs.c'))
diff --git a/hw/mem/spm-memory.c b/hw/mem/spm-memory.c
new file mode 100644
index 0000000000..85887b2479
--- /dev/null
+++ b/hw/mem/spm-memory.c
@@ -0,0 +1,269 @@
+/*
+ * Specific Purpose Memory (SPM) device
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * Authors:
+ * FangSheng Huang <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+#include "hw/core/boards.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev.h"
+#include "hw/mem/spm-memory.h"
+#include "hw/mem/memory-device.h"
+#include "hw/i386/e820_memory_layout.h"
+#include "migration/vmstate.h"
+#include "system/hostmem.h"
+#include "system/numa.h"
+#include "system/system.h"
+
+static QLIST_HEAD(, SpmMemoryDevice) spm_memory_list =
+ QLIST_HEAD_INITIALIZER(spm_memory_list);
+static Notifier spm_machine_done_notifier;
+static bool spm_machine_done_registered;
+
+#define SPM_MEMORY_MEMDEV_PROP "memdev"
+#define SPM_MEMORY_NODE_PROP "node"
+#define SPM_MEMORY_ADDR_PROP "addr"
+
+static const Property spm_memory_properties[] = {
+ DEFINE_PROP_LINK(SPM_MEMORY_MEMDEV_PROP, SpmMemoryDevice, hostmem,
+ TYPE_MEMORY_BACKEND, HostMemoryBackend *),
+ DEFINE_PROP_UINT32(SPM_MEMORY_NODE_PROP, SpmMemoryDevice, node, 0),
+ DEFINE_PROP_UINT64(SPM_MEMORY_ADDR_PROP, SpmMemoryDevice, addr, 0),
+};
+
+static uint64_t spm_memory_md_get_addr(const MemoryDeviceState *md)
+{
+ return SPM_MEMORY(md)->addr;
+}
+
+static void spm_memory_md_set_addr(MemoryDeviceState *md, uint64_t addr,
+ Error **errp)
+{
+ SPM_MEMORY(md)->addr = addr;
+}
+
+static MemoryRegion *spm_memory_md_get_memory_region(MemoryDeviceState *md,
+ Error **errp)
+{
+ SpmMemoryDevice *spm = SPM_MEMORY(md);
+
+ if (!spm->hostmem) {
+ error_setg(errp, "'memdev' property must be set");
+ return NULL;
+ }
+ return host_memory_backend_get_memory(spm->hostmem);
+}
+
+static uint64_t spm_memory_md_get_plugged_size(const MemoryDeviceState *md,
+ Error **errp)
+{
+ SpmMemoryDevice *spm = SPM_MEMORY(md);
+ return spm->hostmem ?
+ memory_region_size(host_memory_backend_get_memory(spm->hostmem)) : 0;
+}
+
+static void spm_memory_md_fill_device_info(const MemoryDeviceState *md,
+ MemoryDeviceInfo *info)
+{
+ SpmMemoryDeviceInfo *di = g_new0(SpmMemoryDeviceInfo, 1);
+ SpmMemoryDevice *spm = SPM_MEMORY(md);
+ DeviceState *dev = DEVICE(md);
+
+ di->id = dev->id ? g_strdup(dev->id) : NULL;
+ di->memaddr = spm->addr;
+ di->size = spm->hostmem ? memory_region_size(
+ host_memory_backend_get_memory(spm->hostmem)) : 0;
+ di->node = spm->node;
+ di->memdev = spm->hostmem ?
+ object_get_canonical_path(OBJECT(spm->hostmem)) : NULL;
+
+ info->u.spm_memory.data = di;
+ info->type = MEMORY_DEVICE_INFO_KIND_SPM_MEMORY;
+}
+
+typedef struct {
+ uint32_t node_id;
+ const SpmMemoryDevice *self; /* exclude self when walking */
+ bool conflict;
+} SpmNodeCheckCtx;
+
+static int spm_check_node_collision_cb(Object *obj, void *opaque)
+{
+ SpmNodeCheckCtx *ctx = opaque;
+ uint32_t other_node;
+
+ if (!object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) {
+ return 0;
+ }
+ /*
+ * Skip self. Compare canonical Object* pointers, not interface-cast
+ * MemoryDeviceState* (different address under INTERFACE_CHECK).
+ */
+ if (obj == OBJECT(ctx->self)) {
+ return 0;
+ }
+
+ /*
+ * Not all memory-device subclasses have a "node" property; skip
+ * those silently rather than asserting.
+ */
+ if (!object_property_find(obj, "node")) {
+ return 0;
+ }
+ other_node = (uint32_t)object_property_get_uint(obj, "node", NULL);
+ if (other_node == ctx->node_id) {
+ ctx->conflict = true;
+ return 1; /* stop walk */
+ }
+ return 0;
+}
+
+/*
+ * Require the target NUMA node to be SPM-only: driver-side discovery
+ * uses proximity_domain as the key, so a node mixing SPM with other
+ * memory yields ambiguous discovery.
+ */
+static void spm_memory_check_node_exclusive(SpmMemoryDevice *spm,
+ MachineState *ms, Error **errp)
+{
+ ERRP_GUARD();
+ SpmNodeCheckCtx ctx = { spm->node, spm, false };
+
+ /* Bounds check: spm->node must be a valid NUMA node id */
+ if (!ms->numa_state || spm->node >= ms->numa_state->num_nodes) {
+ error_setg(errp,
+ "spm-memory: node %u out of range "
+ "(numa_state has %d nodes)", spm->node,
+ ms->numa_state ? ms->numa_state->num_nodes : 0);
+ return;
+ }
+
+ /* Check 1: target node must not have memory from -numa node,memdev= */
+ if (ms->numa_state->nodes[spm->node].node_mem > 0) {
+ error_setg(errp,
+ "spm-memory: NUMA node %u already has memory attached "
+ "via -numa node,memdev=; SPM nodes must be SPM-only",
+ spm->node);
+ return;
+ }
+
+ /* Check 2: target node must not already have another memory device */
+ object_child_foreach_recursive(qdev_get_machine(),
+ spm_check_node_collision_cb, &ctx);
+ if (ctx.conflict) {
+ error_setg(errp,
+ "spm-memory: NUMA node %u already has another memory "
+ "device plugged; SPM nodes must be SPM-only", spm->node);
+ return;
+ }
+}
+
+static void spm_memory_machine_done(Notifier *n, void *opaque)
+{
+ SpmMemoryDevice *spm;
+ MemoryDeviceClass *mdc;
+ uint64_t addr, size;
+
+ QLIST_FOREACH(spm, &spm_memory_list, next) {
+ g_assert(spm->hostmem);
+ mdc = MEMORY_DEVICE_GET_CLASS(MEMORY_DEVICE(spm));
+ addr = mdc->get_addr(MEMORY_DEVICE(spm));
+ size = memory_region_size(
+ host_memory_backend_get_memory(spm->hostmem));
+ e820_add_entry(addr, size, E820_SOFT_RESERVED);
+ }
+}
+
+static void spm_memory_realize(DeviceState *dev, Error **errp)
+{
+ ERRP_GUARD();
+ SpmMemoryDevice *spm = SPM_MEMORY(dev);
+ MachineState *ms = MACHINE(qdev_get_machine());
+
+ if (phase_check(PHASE_MACHINE_READY)) {
+ error_setg(errp, "spm-memory: hotplug is not supported "
+ "(boot-time-only device)");
+ return;
+ }
+
+ if (!spm->hostmem) {
+ error_setg(errp, "'%s' property is required", SPM_MEMORY_MEMDEV_PROP);
+ return;
+ }
+ if (host_memory_backend_is_mapped(spm->hostmem)) {
+ error_setg(errp, "memory backend '%s' is already in use",
+ object_get_canonical_path_component(OBJECT(spm->hostmem)));
+ return;
+ }
+
+ spm_memory_check_node_exclusive(spm, ms, errp);
+ if (*errp) {
+ return;
+ }
+
+ memory_device_pre_plug(MEMORY_DEVICE(spm), ms, errp);
+ if (*errp) {
+ return;
+ }
+
+ host_memory_backend_set_mapped(spm->hostmem, true);
+ memory_device_plug(MEMORY_DEVICE(spm), ms);
+
+ QLIST_INSERT_HEAD(&spm_memory_list, spm, next);
+
+ if (!spm_machine_done_registered) {
+ spm_machine_done_notifier.notify = spm_memory_machine_done;
+ qemu_add_machine_init_done_notifier(&spm_machine_done_notifier);
+ spm_machine_done_registered = true;
+ }
+}
+
+static const VMStateDescription vmstate_spm_memory = {
+ .name = TYPE_SPM_MEMORY,
+ .unmigratable = 1,
+};
+
+static void spm_memory_class_init(ObjectClass *oc, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
+
+ dc->desc = "SPM (Specific Purpose Memory) device";
+ dc->hotpluggable = false;
+ dc->realize = spm_memory_realize;
+ dc->vmsd = &vmstate_spm_memory;
+ device_class_set_props(dc, spm_memory_properties);
+
+ mdc->get_addr = spm_memory_md_get_addr;
+ mdc->set_addr = spm_memory_md_set_addr;
+ mdc->get_memory_region = spm_memory_md_get_memory_region;
+ mdc->get_plugged_size = spm_memory_md_get_plugged_size;
+ mdc->fill_device_info = spm_memory_md_fill_device_info;
+}
+
+static const TypeInfo spm_memory_info = {
+ .name = TYPE_SPM_MEMORY,
+ .parent = TYPE_DEVICE,
+ .class_size = sizeof(SpmMemoryDeviceClass),
+ .class_init = spm_memory_class_init,
+ .instance_size = sizeof(SpmMemoryDevice),
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_MEMORY_DEVICE },
+ { }
+ },
+};
+
+static void spm_memory_register_types(void)
+{
+ type_register_static(&spm_memory_info);
+}
+
+type_init(spm_memory_register_types)
diff --git a/include/hw/mem/spm-memory.h b/include/hw/mem/spm-memory.h
new file mode 100644
index 0000000000..c662864b29
--- /dev/null
+++ b/include/hw/mem/spm-memory.h
@@ -0,0 +1,43 @@
+/*
+ * Specific Purpose Memory (SPM) device
+ *
+ * TYPE_MEMORY_DEVICE subclass for boot-time-only memory exposed to the
+ * guest as an E820 SOFT_RESERVED range with a SRAT memory-affinity entry.
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * Authors:
+ * FangSheng Huang <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef QEMU_SPM_MEMORY_H
+#define QEMU_SPM_MEMORY_H
+
+#include "hw/mem/memory-device.h"
+#include "hw/core/qdev.h"
+#include "qom/object.h"
+#include "system/hostmem.h"
+
+#define TYPE_SPM_MEMORY "spm-memory"
+
+OBJECT_DECLARE_TYPE(SpmMemoryDevice, SpmMemoryDeviceClass, SPM_MEMORY)
+
+struct SpmMemoryDevice {
+ /*< private >*/
+ DeviceState parent_obj;
+ QLIST_ENTRY(SpmMemoryDevice) next;
+
+ /*< public >*/
+ HostMemoryBackend *hostmem; /* memdev= backend */
+ uint32_t node; /* NUMA proximity domain (node=) */
+ uint64_t addr; /* GPA (from addr= or framework-assigned) */
+};
+
+struct SpmMemoryDeviceClass {
+ /*< private >*/
+ DeviceClass parent_class;
+};
+
+#endif /* QEMU_SPM_MEMORY_H */
diff --git a/qapi/machine.json b/qapi/machine.json
index 685e4e29b8..51b06d7cba 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1413,6 +1413,32 @@
}
}
+##
+# @SpmMemoryDeviceInfo:
+#
+# spm-memory device state information
+#
+# @id: device's ID
+#
+# @memaddr: physical address in memory, where device is mapped
+#
+# @size: size of memory that the device provides
+#
+# @node: NUMA proximity domain to which the device is assigned
+#
+# @memdev: memory backend linked with device
+#
+# Since: 11.1
+##
+{ 'struct': 'SpmMemoryDeviceInfo',
+ 'data': { '*id': 'str',
+ 'memaddr': 'size',
+ 'size': 'size',
+ 'node': 'int',
+ 'memdev': 'str'
+ }
+}
+
##
# @MemoryDeviceInfoKind:
#
@@ -1426,11 +1452,13 @@
#
# @hv-balloon: since 8.2.
#
+# @spm-memory: since 11.1.
+#
# Since: 2.1
##
{ 'enum': 'MemoryDeviceInfoKind',
'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem', 'sgx-epc',
- 'hv-balloon' ] }
+ 'hv-balloon', 'spm-memory' ] }
##
# @PCDIMMDeviceInfoWrapper:
@@ -1482,6 +1510,16 @@
{ 'struct': 'HvBalloonDeviceInfoWrapper',
'data': { 'data': 'HvBalloonDeviceInfo' } }
+##
+# @SpmMemoryDeviceInfoWrapper:
+#
+# @data: spm-memory device state information
+#
+# Since: 11.1
+##
+{ 'struct': 'SpmMemoryDeviceInfoWrapper',
+ 'data': { 'data': 'SpmMemoryDeviceInfo' } }
+
##
# @MemoryDeviceInfo:
#
@@ -1499,7 +1537,8 @@
'virtio-pmem': 'VirtioPMEMDeviceInfoWrapper',
'virtio-mem': 'VirtioMEMDeviceInfoWrapper',
'sgx-epc': 'SgxEPCDeviceInfoWrapper',
- 'hv-balloon': 'HvBalloonDeviceInfoWrapper'
+ 'hv-balloon': 'HvBalloonDeviceInfoWrapper',
+ 'spm-memory': 'SpmMemoryDeviceInfoWrapper'
}
}
--
2.34.1