* [PATCH v14 01/10] hw/mem: add sp-mem device for Specific Purpose Memory
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
@ 2026-06-23 7:50 ` fanhuang
2026-06-23 7:50 ` [PATCH v14 02/10] qapi, hmp: introspection for the sp-mem device fanhuang
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: fanhuang @ 2026-06-23 7:50 UTC (permalink / raw)
To: qemu-devel, imammedo, mst, david, gourry
Cc: philmd, berrange, peterx, Zhigang.Luo, Lianjie.Shi, fanhuang
Introduce a TYPE_MEMORY_DEVICE subclass `sp-mem` 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.
Usage:
-object memory-backend-ram,id=spm0,size=$SIZE
-numa node,nodeid=$N
-device sp-mem,id=dev0,memdev=spm0,node=$N[,addr=$GPA]
The device is boot-time only (no hotplug).
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
---
include/hw/mem/sp-mem.h | 33 ++++++++++++
hw/mem/sp-mem.c | 109 ++++++++++++++++++++++++++++++++++++++++
hw/mem/Kconfig | 4 ++
hw/mem/meson.build | 1 +
4 files changed, 147 insertions(+)
create mode 100644 include/hw/mem/sp-mem.h
create mode 100644 hw/mem/sp-mem.c
diff --git a/include/hw/mem/sp-mem.h b/include/hw/mem/sp-mem.h
new file mode 100644
index 0000000000..a8951b49e6
--- /dev/null
+++ b/include/hw/mem/sp-mem.h
@@ -0,0 +1,33 @@
+/*
+ * 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 <FangSheng.Huang@amd.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef QEMU_SP_MEM_H
+#define QEMU_SP_MEM_H
+
+#include "hw/core/qdev.h"
+#include "qom/object.h"
+
+#define TYPE_SP_MEM "sp-mem"
+
+OBJECT_DECLARE_SIMPLE_TYPE(SpMemDevice, SP_MEM)
+
+struct SpMemDevice {
+ DeviceState parent_obj;
+
+ HostMemoryBackend *hostmem;
+ uint32_t node;
+ uint64_t addr;
+};
+
+#endif /* QEMU_SP_MEM_H */
diff --git a/hw/mem/sp-mem.c b/hw/mem/sp-mem.c
new file mode 100644
index 0000000000..d088222f54
--- /dev/null
+++ b/hw/mem/sp-mem.c
@@ -0,0 +1,109 @@
+/*
+ * Specific Purpose Memory (SPM) device
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * Authors:
+ * FangSheng Huang <FangSheng.Huang@amd.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev.h"
+#include "hw/mem/sp-mem.h"
+#include "hw/mem/memory-device.h"
+#include "system/hostmem.h"
+
+#define SP_MEM_MEMDEV_PROP "memdev"
+#define SP_MEM_NODE_PROP "node"
+#define SP_MEM_ADDR_PROP "addr"
+
+static const Property sp_mem_properties[] = {
+ DEFINE_PROP_LINK(SP_MEM_MEMDEV_PROP, SpMemDevice, hostmem,
+ TYPE_MEMORY_BACKEND, HostMemoryBackend *),
+ DEFINE_PROP_UINT32(SP_MEM_NODE_PROP, SpMemDevice, node, 0),
+ DEFINE_PROP_UINT64(SP_MEM_ADDR_PROP, SpMemDevice, addr, 0),
+};
+
+static uint64_t sp_mem_get_addr(const MemoryDeviceState *md)
+{
+ return object_property_get_uint(OBJECT(md), SP_MEM_ADDR_PROP,
+ &error_abort);
+}
+
+static void sp_mem_set_addr(MemoryDeviceState *md, uint64_t addr,
+ Error **errp)
+{
+ object_property_set_uint(OBJECT(md), SP_MEM_ADDR_PROP, addr, errp);
+}
+
+static MemoryRegion *sp_mem_get_memory_region(MemoryDeviceState *md,
+ Error **errp)
+{
+ SpMemDevice *spm = SP_MEM(md);
+
+ if (!spm->hostmem) {
+ error_setg(errp, "'%s' property must be set", SP_MEM_MEMDEV_PROP);
+ return NULL;
+ }
+ return host_memory_backend_get_memory(spm->hostmem);
+}
+
+static void sp_mem_realize(DeviceState *dev, Error **errp)
+{
+ SpMemDevice *spm = SP_MEM(dev);
+
+ if (!spm->hostmem) {
+ error_setg(errp, "'%s' property is required", SP_MEM_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;
+ }
+ host_memory_backend_set_mapped(spm->hostmem, true);
+}
+
+static void sp_mem_unrealize(DeviceState *dev)
+{
+ SpMemDevice *spm = SP_MEM(dev);
+
+ host_memory_backend_set_mapped(spm->hostmem, false);
+}
+
+static void sp_mem_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 = sp_mem_realize;
+ dc->unrealize = sp_mem_unrealize;
+ device_class_set_props(dc, sp_mem_properties);
+
+ mdc->get_addr = sp_mem_get_addr;
+ mdc->set_addr = sp_mem_set_addr;
+ mdc->get_memory_region = sp_mem_get_memory_region;
+ mdc->get_plugged_size = memory_device_get_region_size;
+}
+
+static const TypeInfo sp_mem_types[] = {
+ {
+ .name = TYPE_SP_MEM,
+ .parent = TYPE_DEVICE,
+ .class_init = sp_mem_class_init,
+ .instance_size = sizeof(SpMemDevice),
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_MEMORY_DEVICE },
+ { }
+ },
+ },
+};
+
+DEFINE_TYPES(sp_mem_types)
diff --git a/hw/mem/Kconfig b/hw/mem/Kconfig
index 73c5ae8ad9..39ddb36710 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 SP_MEM
+ bool
+ select MEM_DEVICE
diff --git a/hw/mem/meson.build b/hw/mem/meson.build
index 8c2beeb7d4..f410d75475 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_SP_MEM', if_true: files('sp-mem.c'))
stub_ss.add(files('cxl_type3_stubs.c'))
stub_ss.add(files('memory-device-stubs.c'))
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v14 02/10] qapi, hmp: introspection for the sp-mem device
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
2026-06-23 7:50 ` [PATCH v14 01/10] " fanhuang
@ 2026-06-23 7:50 ` fanhuang
2026-06-23 7:50 ` [PATCH v14 03/10] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem fanhuang
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: fanhuang @ 2026-06-23 7:50 UTC (permalink / raw)
To: qemu-devel, imammedo, mst, david, gourry
Cc: philmd, berrange, peterx, Zhigang.Luo, Lianjie.Shi, fanhuang
Add a SpMemDeviceInfo variant to MemoryDeviceInfo so `query-memory-devices`
reports each sp-mem instance (id, addr, size, node, memdev), and print it
from HMP `info memory-devices`.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
---
qapi/machine.json | 43 ++++++++++++++++++++++++++++++++++++--
hw/core/machine-hmp-cmds.c | 11 ++++++++++
hw/mem/sp-mem.c | 19 +++++++++++++++++
3 files changed, 71 insertions(+), 2 deletions(-)
diff --git a/qapi/machine.json b/qapi/machine.json
index 685e4e29b8..9b2248038f 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1413,6 +1413,32 @@
}
}
+##
+# @SpMemDeviceInfo:
+#
+# sp-mem device state information
+#
+# @id: device's ID
+#
+# @addr: physical address, where device is mapped
+#
+# @size: size of memory that the device provides, in bytes
+#
+# @node: NUMA proximity domain to which the device is assigned
+#
+# @memdev: memory backend linked with device
+#
+# Since: 11.1
+##
+{ 'struct': 'SpMemDeviceInfo',
+ 'data': { '*id': 'str',
+ 'addr': 'size',
+ 'size': 'size',
+ 'node': 'int',
+ 'memdev': 'str'
+ }
+}
+
##
# @MemoryDeviceInfoKind:
#
@@ -1426,11 +1452,13 @@
#
# @hv-balloon: since 8.2.
#
+# @sp-mem: since 11.1.
+#
# Since: 2.1
##
{ 'enum': 'MemoryDeviceInfoKind',
'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem', 'sgx-epc',
- 'hv-balloon' ] }
+ 'hv-balloon', 'sp-mem' ] }
##
# @PCDIMMDeviceInfoWrapper:
@@ -1482,6 +1510,16 @@
{ 'struct': 'HvBalloonDeviceInfoWrapper',
'data': { 'data': 'HvBalloonDeviceInfo' } }
+##
+# @SpMemDeviceInfoWrapper:
+#
+# @data: sp-mem device state information
+#
+# Since: 11.1
+##
+{ 'struct': 'SpMemDeviceInfoWrapper',
+ 'data': { 'data': 'SpMemDeviceInfo' } }
+
##
# @MemoryDeviceInfo:
#
@@ -1499,7 +1537,8 @@
'virtio-pmem': 'VirtioPMEMDeviceInfoWrapper',
'virtio-mem': 'VirtioMEMDeviceInfoWrapper',
'sgx-epc': 'SgxEPCDeviceInfoWrapper',
- 'hv-balloon': 'HvBalloonDeviceInfoWrapper'
+ 'hv-balloon': 'HvBalloonDeviceInfoWrapper',
+ 'sp-mem': 'SpMemDeviceInfoWrapper'
}
}
diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c
index 46846f741a..686304bafa 100644
--- a/hw/core/machine-hmp-cmds.c
+++ b/hw/core/machine-hmp-cmds.c
@@ -279,6 +279,7 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
PCDIMMDeviceInfo *di;
SgxEPCDeviceInfo *se;
HvBalloonDeviceInfo *hi;
+ SpMemDeviceInfo *spmi;
for (info = info_list; info; info = info->next) {
value = info->value;
@@ -350,6 +351,16 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
monitor_printf(mon, " memdev: %s\n", hi->memdev);
}
break;
+ case MEMORY_DEVICE_INFO_KIND_SP_MEM:
+ spmi = value->u.sp_mem.data;
+ monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
+ MemoryDeviceInfoKind_str(value->type),
+ spmi->id ? spmi->id : "");
+ monitor_printf(mon, " addr: 0x%" PRIx64 "\n", spmi->addr);
+ monitor_printf(mon, " node: %" PRId64 "\n", spmi->node);
+ monitor_printf(mon, " size: %" PRIu64 "\n", spmi->size);
+ monitor_printf(mon, " memdev: %s\n", spmi->memdev);
+ break;
default:
g_assert_not_reached();
}
diff --git a/hw/mem/sp-mem.c b/hw/mem/sp-mem.c
index d088222f54..962d0f937e 100644
--- a/hw/mem/sp-mem.c
+++ b/hw/mem/sp-mem.c
@@ -53,6 +53,24 @@ static MemoryRegion *sp_mem_get_memory_region(MemoryDeviceState *md,
return host_memory_backend_get_memory(spm->hostmem);
}
+static void sp_mem_fill_device_info(const MemoryDeviceState *md,
+ MemoryDeviceInfo *info)
+{
+ SpMemDeviceInfo *di = g_new0(SpMemDeviceInfo, 1);
+ SpMemDevice *spm = SP_MEM(md);
+ DeviceState *dev = DEVICE(md);
+
+ di->id = dev->id ? g_strdup(dev->id) : NULL;
+ di->addr = spm->addr;
+ di->size = memory_region_size(
+ host_memory_backend_get_memory(spm->hostmem));
+ di->node = spm->node;
+ di->memdev = object_get_canonical_path(OBJECT(spm->hostmem));
+
+ info->u.sp_mem.data = di;
+ info->type = MEMORY_DEVICE_INFO_KIND_SP_MEM;
+}
+
static void sp_mem_realize(DeviceState *dev, Error **errp)
{
SpMemDevice *spm = SP_MEM(dev);
@@ -91,6 +109,7 @@ static void sp_mem_class_init(ObjectClass *oc, const void *data)
mdc->set_addr = sp_mem_set_addr;
mdc->get_memory_region = sp_mem_get_memory_region;
mdc->get_plugged_size = memory_device_get_region_size;
+ mdc->fill_device_info = sp_mem_fill_device_info;
}
static const TypeInfo sp_mem_types[] = {
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v14 03/10] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
2026-06-23 7:50 ` [PATCH v14 01/10] " fanhuang
2026-06-23 7:50 ` [PATCH v14 02/10] qapi, hmp: introspection for the sp-mem device fanhuang
@ 2026-06-23 7:50 ` fanhuang
2026-06-23 7:50 ` [PATCH v14 04/10] hw/i386: hook sp-mem into the pc machine plug path fanhuang
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: fanhuang @ 2026-06-23 7:50 UTC (permalink / raw)
To: qemu-devel, imammedo, mst, david, gourry
Cc: philmd, berrange, peterx, Zhigang.Luo, Lianjie.Shi, fanhuang
Restructure the device_memory SRAT umbrella entry into a per-kind
partition: each TYPE_SP_MEM device gets an ENABLED entry at its own
proximity_domain; the remaining sub-ranges get HOTPLUGGABLE | ENABLED
placeholders at the highest PXM, preserving the existing umbrella
convention.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
---
hw/i386/acpi-build.c | 96 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 92 insertions(+), 4 deletions(-)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 2ee061558c..8837b69687 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/sp-mem.h"
#include "system/numa.h"
#include "system/reset.h"
#include "hw/hyperv/vmbus-bridge.h"
@@ -1351,6 +1352,96 @@ build_tpm_tcpa(GArray *table_data, BIOSLinker *linker, GArray *tcpalog,
}
#endif
+typedef struct {
+ uint64_t addr;
+ uint64_t size;
+ uint32_t node;
+} SpMemRange;
+
+static int sp_mem_collect_ranges_cb(Object *obj, void *opaque)
+{
+ GArray *ranges = opaque;
+ SpMemDevice *spm;
+ MemoryDeviceClass *mdc;
+ SpMemRange r;
+
+ if (!object_dynamic_cast(obj, TYPE_SP_MEM)) {
+ return 0;
+ }
+ spm = SP_MEM(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 sp_mem_range_compare(gconstpointer a, gconstpointer b)
+{
+ const SpMemRange *range_a = a;
+ const SpMemRange *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.
+ *
+ * For each plugged TYPE_SP_MEM device, emit an ENABLED entry at the
+ * device's own proximity_domain. All remaining sub-ranges (gaps
+ * between sp-mem devices, leading and trailing padding, and ranges
+ * occupied by other memory devices) are covered by HOTPLUGGABLE |
+ * ENABLED placeholder entries at PXM = nb_numa_nodes - 1.
+ */
+static void build_srat_device_memory(GArray *table_data, MachineState *ms)
+{
+ g_autoptr(GArray) ranges = g_array_new(FALSE, TRUE, sizeof(SpMemRange));
+ uint32_t hotplug_pxm = ms->numa_state->num_nodes - 1;
+ uint64_t region_start, region_end;
+ guint i;
+
+ region_start = ms->device_memory->base;
+ region_end = region_start + memory_region_size(&ms->device_memory->mr);
+
+ object_child_foreach_recursive(qdev_get_machine(),
+ sp_mem_collect_ranges_cb, ranges);
+ g_array_sort(ranges, sp_mem_range_compare);
+
+ for (i = 0; i < ranges->len; i++) {
+ SpMemRange *r = &g_array_index(ranges, SpMemRange, i);
+
+ if (region_start < r->addr) {
+ build_srat_memory(table_data, region_start, r->addr - region_start,
+ hotplug_pxm,
+ MEM_AFFINITY_HOTPLUGGABLE |
+ MEM_AFFINITY_ENABLED);
+ }
+ build_srat_memory(table_data, r->addr, r->size, r->node,
+ MEM_AFFINITY_ENABLED);
+ region_start = r->addr + r->size;
+ }
+
+ /*
+ * Cover the rest of the device_memory window that no sp-mem device
+ * occupies. Keeping it HOTPLUGGABLE preserves the umbrella entry's
+ * role for future pc-dimm / virtio-mem hot-add into this window.
+ */
+ if (region_start < region_end) {
+ build_srat_memory(table_data, region_start, region_end - region_start,
+ hotplug_pxm,
+ MEM_AFFINITY_HOTPLUGGABLE |
+ MEM_AFFINITY_ENABLED);
+ }
+}
+
#define HOLE_640K_START (640 * KiB)
#define HOLE_640K_END (1 * MiB)
@@ -1487,10 +1578,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
* 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);
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v14 04/10] hw/i386: hook sp-mem into the pc machine plug path
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (2 preceding siblings ...)
2026-06-23 7:50 ` [PATCH v14 03/10] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem fanhuang
@ 2026-06-23 7:50 ` fanhuang
2026-06-23 7:50 ` [PATCH v14 05/10] MAINTAINERS: cover sp-mem under Memory devices, add R: tag fanhuang
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: fanhuang @ 2026-06-23 7:50 UTC (permalink / raw)
To: qemu-devel, imammedo, mst, david, gourry
Cc: philmd, berrange, peterx, Zhigang.Luo, Lianjie.Shi, fanhuang
Add the pc machine hookup for TYPE_SP_MEM so each sp-mem instance is
placed by the memory-device framework and reported to the guest as
E820_SOFT_RESERVED.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
---
hw/i386/e820_memory_layout.h | 11 ++++++-----
hw/i386/pc.c | 36 ++++++++++++++++++++++++++++++++++++
hw/i386/Kconfig | 2 ++
3 files changed, 44 insertions(+), 5 deletions(-)
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/i386/pc.c b/hw/i386/pc.c
index 73a625327c..2bacece249 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -63,6 +63,7 @@
#include "hw/i386/kvm/xen_gnttab.h"
#include "hw/i386/kvm/xen_xenstore.h"
#include "hw/mem/memory-device.h"
+#include "hw/mem/sp-mem.h"
#include "e820_memory_layout.h"
#include "trace.h"
#include "sev.h"
@@ -1285,11 +1286,43 @@ static void pc_hv_balloon_plug(HotplugHandler *hotplug_dev,
memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev));
}
+static void pc_sp_mem_pre_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ MachineState *ms = MACHINE(hotplug_dev);
+ SpMemDevice *spm = SP_MEM(dev);
+
+ if (ms->numa_state && spm->node >= ms->numa_state->num_nodes) {
+ error_setg(errp,
+ "'node' property value %" PRIu32
+ " exceeds the number of NUMA nodes (%d)",
+ spm->node, ms->numa_state->num_nodes);
+ return;
+ }
+ memory_device_pre_plug(MEMORY_DEVICE(dev), ms, errp);
+}
+
+static void pc_sp_mem_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ SpMemDevice *spm = SP_MEM(dev);
+ MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(MEMORY_DEVICE(dev));
+ uint64_t addr, size;
+
+ memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev));
+
+ addr = mdc->get_addr(MEMORY_DEVICE(dev));
+ size = memory_region_size(host_memory_backend_get_memory(spm->hostmem));
+ e820_add_entry(addr, size, E820_SOFT_RESERVED);
+}
+
static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
pc_memory_pre_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_SP_MEM)) {
+ pc_sp_mem_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
x86_cpu_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) {
@@ -1326,6 +1359,8 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
pc_memory_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_SP_MEM)) {
+ pc_sp_mem_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
x86_cpu_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) {
@@ -1370,6 +1405,7 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
+ object_dynamic_cast(OBJECT(dev), TYPE_SP_MEM) ||
object_dynamic_cast(OBJECT(dev), TYPE_CPU) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) ||
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index 12473acaa7..e27d8816e5 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -84,6 +84,7 @@ config I440FX
select PCI_I440FX
select PIIX
select DIMM
+ select SP_MEM
select SMBIOS
select SMBIOS_LEGACY
select FW_CFG_DMA
@@ -113,6 +114,7 @@ config Q35
select LPC_ICH9
select AHCI_ICH9
select DIMM
+ select SP_MEM
select SMBIOS
select FW_CFG_DMA
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v14 05/10] MAINTAINERS: cover sp-mem under Memory devices, add R: tag
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (3 preceding siblings ...)
2026-06-23 7:50 ` [PATCH v14 04/10] hw/i386: hook sp-mem into the pc machine plug path fanhuang
@ 2026-06-23 7:50 ` fanhuang
2026-06-23 7:50 ` [PATCH v14 06/10] tests/acpi: add empty expected blobs for sp-mem SRAT test fanhuang
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: fanhuang @ 2026-06-23 7:50 UTC (permalink / raw)
To: qemu-devel, imammedo, mst, david, gourry
Cc: philmd, berrange, peterx, Zhigang.Luo, Lianjie.Shi, fanhuang
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
Acked-by: David Hildenbrand <david@kernel.org>
---
MAINTAINERS | 3 +++
1 file changed, 3 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 93df53d87f..08f0d11a74 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3364,13 +3364,16 @@ F: scripts/coccinelle/memory-region-housekeeping.cocci
Memory devices
M: David Hildenbrand <david@kernel.org>
M: Igor Mammedov <imammedo@redhat.com>
+R: FangSheng Huang <FangSheng.Huang@amd.com>
S: Supported
F: hw/mem/memory-device*.c
F: hw/mem/nvdimm.c
F: hw/mem/pc-dimm.c
+F: hw/mem/sp-mem.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/sp-mem.h
F: docs/nvdimm.txt
SPICE
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v14 06/10] tests/acpi: add empty expected blobs for sp-mem SRAT test
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (4 preceding siblings ...)
2026-06-23 7:50 ` [PATCH v14 05/10] MAINTAINERS: cover sp-mem under Memory devices, add R: tag fanhuang
@ 2026-06-23 7:50 ` fanhuang
2026-06-23 7:50 ` [PATCH v14 07/10] tests/acpi: add bios-tables-test case for sp-mem fanhuang
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: fanhuang @ 2026-06-23 7:50 UTC (permalink / raw)
To: qemu-devel, imammedo, mst, david, gourry
Cc: philmd, berrange, peterx, Zhigang.Luo, Lianjie.Shi, fanhuang
Add empty SRAT.spmem and DSDT.spmem stubs and list them in
bios-tables-test-allowed-diff.h.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
---
tests/qtest/bios-tables-test-allowed-diff.h | 2 ++
tests/data/acpi/x86/q35/DSDT.spmem | 0
tests/data/acpi/x86/q35/SRAT.spmem | 0
3 files changed, 2 insertions(+)
create mode 100644 tests/data/acpi/x86/q35/DSDT.spmem
create mode 100644 tests/data/acpi/x86/q35/SRAT.spmem
diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h
index dfb8523c8b..188003fa90 100644
--- a/tests/qtest/bios-tables-test-allowed-diff.h
+++ b/tests/qtest/bios-tables-test-allowed-diff.h
@@ -1 +1,3 @@
/* List of comma-separated changed AML files to ignore */
+"tests/data/acpi/x86/q35/DSDT.spmem",
+"tests/data/acpi/x86/q35/SRAT.spmem",
diff --git a/tests/data/acpi/x86/q35/DSDT.spmem b/tests/data/acpi/x86/q35/DSDT.spmem
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/data/acpi/x86/q35/SRAT.spmem b/tests/data/acpi/x86/q35/SRAT.spmem
new file mode 100644
index 0000000000..e69de29bb2
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v14 07/10] tests/acpi: add bios-tables-test case for sp-mem
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (5 preceding siblings ...)
2026-06-23 7:50 ` [PATCH v14 06/10] tests/acpi: add empty expected blobs for sp-mem SRAT test fanhuang
@ 2026-06-23 7:50 ` fanhuang
2026-06-23 7:50 ` [PATCH v14 08/10] tests/acpi: generate expected blobs for sp-mem SRAT test fanhuang
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: fanhuang @ 2026-06-23 7:50 UTC (permalink / raw)
To: qemu-devel, imammedo, mst, david, gourry
Cc: philmd, berrange, peterx, Zhigang.Luo, Lianjie.Shi, fanhuang
Add a q35 bios-tables-test case that boots two sp-mem devices on
distinct NUMA nodes within the device_memory window, exercising the
per-kind SRAT partition (per-device ENABLED entries plus HOTPLUGGABLE
placeholders for the remaining sub-ranges).
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
---
tests/qtest/bios-tables-test.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index af6d9b5136..45abd8bd8c 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -1416,6 +1416,26 @@ static void test_acpi_q35_tcg_numamem(void)
free_test_data(&data);
}
+static void test_acpi_q35_tcg_sp_mem(void)
+{
+ test_data data = {};
+
+ data.machine = MACHINE_Q35;
+ data.arch = "x86",
+ data.variant = ".spmem";
+ test_acpi_one(" -m 128M,slots=4,maxmem=1G"
+ " -object memory-backend-ram,id=ram0,size=128M"
+ " -numa node,nodeid=0,memdev=ram0"
+ " -numa node,nodeid=1"
+ " -numa node,nodeid=2"
+ " -object memory-backend-ram,id=spm0,size=128M"
+ " -object memory-backend-ram,id=spm1,size=128M"
+ " -device sp-mem,id=sp0,memdev=spm0,node=1"
+ " -device sp-mem,id=sp1,memdev=spm1,node=2",
+ &data);
+ free_test_data(&data);
+}
+
static void test_acpi_q35_kvm_xapic(void)
{
test_data data = {};
@@ -2807,6 +2827,7 @@ int main(int argc, char *argv[])
if (strcmp(arch, "i386")) {
qtest_add_func("acpi/q35/memhp", test_acpi_q35_tcg_memhp);
qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm);
+ qtest_add_func("acpi/q35/sp-mem", test_acpi_q35_tcg_sp_mem);
qtest_add_func("acpi/q35/acpihmat",
test_acpi_q35_tcg_acpi_hmat);
qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64);
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v14 08/10] tests/acpi: generate expected blobs for sp-mem SRAT test
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (6 preceding siblings ...)
2026-06-23 7:50 ` [PATCH v14 07/10] tests/acpi: add bios-tables-test case for sp-mem fanhuang
@ 2026-06-23 7:50 ` fanhuang
2026-06-23 7:50 ` [PATCH v14 09/10] tests/qtest: add e820 fw_cfg test fanhuang
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: fanhuang @ 2026-06-23 7:50 UTC (permalink / raw)
To: qemu-devel, imammedo, mst, david, gourry
Cc: philmd, berrange, peterx, Zhigang.Luo, Lianjie.Shi, fanhuang
Populate the expected ACPI blobs for the sp-mem test and clear the
allowed-diff list.
SRAT memory-affinity entries for the device_memory window (q35,
-m 128M,maxmem=1G, sp0 on node 1 and sp1 on node 2, each 128M):
Proximity Domain : 1 Base : 0x100000000 Length : 0x08000000 (Enabled)
Proximity Domain : 2 Base : 0x108000000 Length : 0x08000000 (Enabled)
Proximity Domain : 2 Base : 0x110000000 Length : 0x128000000 (Hot Pluggable)
Each sp-mem device gets an ENABLED entry at its own proximity domain;
the remaining device_memory window is covered by a HOTPLUGGABLE
placeholder at the highest proximity domain.
(DSDT.spmem differs from the base only by the memory-hotplug AML
enabled by -m,maxmem.)
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
---
tests/qtest/bios-tables-test-allowed-diff.h | 2 --
tests/data/acpi/x86/q35/DSDT.spmem | Bin 0 -> 9910 bytes
tests/data/acpi/x86/q35/SRAT.spmem | Bin 0 -> 384 bytes
3 files changed, 2 deletions(-)
diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h
index 188003fa90..dfb8523c8b 100644
--- a/tests/qtest/bios-tables-test-allowed-diff.h
+++ b/tests/qtest/bios-tables-test-allowed-diff.h
@@ -1,3 +1 @@
/* List of comma-separated changed AML files to ignore */
-"tests/data/acpi/x86/q35/DSDT.spmem",
-"tests/data/acpi/x86/q35/SRAT.spmem",
diff --git a/tests/data/acpi/x86/q35/DSDT.spmem b/tests/data/acpi/x86/q35/DSDT.spmem
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7e6f850e7a25aaa3cb7abc0ff2aeab4ca7fdb716 100644
GIT binary patch
literal 9910
zcmcgyO>7&-6`tjfv|3UUDXnB%c5K3S(lkjEN^+c_KWWHa{#jg^Ch4DI4R9q{t>mQ0
z!m*1ufdN?o633@Htb_K(7z)r^d&;#2dTDRIH9${2^pa~)#HXm=o7s_PNE(O^DG$qe
zv+sLvzM0wic4yaZx$Q5XW6Zc(JnK|jx#FdUlf%zoj8U2XGquEJ=5M*hO2<gW5{7>}
z#mhJpP5ULcc)nu%ek1rS2(Et|7%L%LeUQ1ox)OePCs<|VbYnHNEGlG{raP@-$KS3^
zHk?}C>{M%Ry70&{&5k2VakF#2<}$lJ+Bw^D5_jKXj(c&wx18*5WT&l+@BMS}n`1}b
z{OscO!o#2a<Ck~evJ!Auz`u$A#gGoc2Z41o=w&|bZW^};&d<&Fra$fS)1odzXTpGL
zK2M7(3N^p;mc5iLxh-$Jce;C(h1s~lg5KPxq5+Ry`}@CJufFfyJMgu4=&z2q6ZA&!
zZ(6aPAhd4Z2^d4gk?vJ%G@MGt*f<N&3vV;F60V4o{`Tu`v*2*%<BjD;-pS81uy3}q
z$E+hCA7Cs^T~5D0@9ysYMHE@)Cd(|(yiA=(9p`T~oL1XNJxp7se>+2lgZlG19Ne70
z)heCAQ2i}!B`{WGIK_PF3{Di6y{3_RBw%DX#pUx}u2FL@<$R-9cb1s_Y{My?iTNkU
z+T9H|$RoUIY%+@B|A4A@nEhe_m|<QNE2KjZ`3AOYHF$rzBm3%5^cp4M9f*U$Rxu2>
zx~r_annvI3vRf<0%B_`~hBf-6gL|*>-tPm3Q?VEg;=L#6aFs?R9A|d|1EXd(YEGHi
z=??ylaD%MO_637_JmY-LXKSO`ZcHt`yET<cq>#P7yi{VMY@l3bQ)#NVVlXPwh{LF|
zsqq9_jDZz?kwqvtPKa|H2Ulql67jijj3eP2S%j3XF~*T_<w%%>L~FQjoFiom;*5~e
zH7<~F<w%%>lvxrSDO-@>Na>moNZELpD$k_GGs%(CH7Ss?@i0}MBO1>Uj+Cw=I!~A?
z&ryx%sK#?t=Lu8gX=*%8ji;&egsJiz(|C?)JjZmNFjbzG#?#VxS~^ddD$kV0Go|rN
z={#YoJkuJ_w8k^7^MtAL9M^b`Ydptwo-kFO6B^G6jpu~U6Q;^@QsX(P@to9o!c=)?
zG@coaXGZ4<Q{|b}cxE-8S)C_LmFJYkb4uemrSpWT@|@OqPHQ};b)GO)p0>u*)_B@F
zPnast8I9+R#&bsJ2~*`ctMQ!Gc+ToPVX8bKY+}vgtI`3D=K-B3OqJ(Bjpsp)=Rut(
zOqJ&$jx0ng;USJJMvL7cfpngjgmfd{ISo0dA?I`?Oci-pLmt+Uhjk=O6?sHM9?_6T
zbR<j_`HY5qMngWMBVnq@qZ}#M_@f*t*Z89XDHm>-gp^8hOv`yp%Xv)C2~*@WA)p0C
zKr5{pQgbmiP)6cNKw%)QFyb&|QV6K<<CDl>gp%zukW{$t2`EfS4FW2BrGP4~B%s1|
zPe5U!HBvx@Z)8zUM3st3NZDf~pfJ%Q?h64GzL6Di<VX@w;VMm)Cj?Z4<wyvq;z|Mv
zQ>r2fC``!{0xEnt5(28Yl7I?Vo|6O=rsN3$6}}t^0aaW{K!qzul7PaLJRzXMmm?vd
ziYp1IaOFr6P?&N&A)vySBO#!QD+#D@<wz1xn35+1RQPfv1XOV)0Tr$sNdgK}@`Qj2
zUyg);Dy}4;!j&UQKw(Or5K!UEkq}VDl>}6{awG{TOvw`hDttK-0;;%@fC^WRBmspf
zc|t&iFGoT^6;~2a;mVODpfDv*2&nMoNC>FnN&+feIg$hvrsN3$6}}t^0aaW{K!qzu
zl7PaLJRzXMmm?vdiYp1IaOFr6P?(Y@1XTEPBm`7(B>@$#97zHSQ}Tp>3SW+dfGVye
zpu&|SNkCyro)A#s%aIUJ#gzn9xN;;3C``!{0xEnt5(28Yl7I?VjwAtvDS1Lbg)c`!
zKowUKP~pmvB%m-QPY9^+<wyvq;z|N4Tse{i6sF_}0TsR+2?14HNkD}wN0NZTlsqAz
zuoMA>r3xraRX|}X0;(V(pbC-%R6&w}Do6;Zf`ou7ND@#5Ndl@MA)pEp0;(WMKoukj
zsDgxmDo6;Zf+PV|kR+fA5&{ZKjxGcg7DkMBCJ87^41+WYDf<=z3QP7a1QeF+TM|&1
zvTsR1VPaQgt^)~Wy>yp9tnSbQ>E|Q#SFopR=ddHt8~ec8+F(1@*p8L5Pu0#j%`wPs
zGoPKXQlE^lF}}HQlij5{D~V$*{9wbHWK=?+X5Qbfk3#1Qa&IO*DRyK`q;6$rt?5rD
z*jOA2#jpl5tfuTHvnM;Pc7g3s6B^BP1v}euYb|z*%GMUInWQZa@^G5mKG113TgKgu
zs11~!>Z9z%zU;<6vKN)?MV`Hg_Nm#6_VDaQf7>JOVs{4nQW1A6wvY6VlHTFzoxb$W
zQ_?$8dbf|fg*~zF-1n}M-sS1tzVz->(z{XmQh)lyKGK(z^d+9Y)R(^Wl=P)2eYrn<
zav$l-O8PQSU+zm^eoFdsl)loReq<l%D@ytbPhaUvUwKOUN|e6ZpMG>7>8ncmDo<bS
zOJ99T`YO^VXgTUnpAqTh;(=QOT<&7KQm?Z|_$7}&x~`*e{g<(o)x+U_VIZ8S8yarl
zx}4I(;U;1roTwWbZs0nb*2Ce>Vj!HT8yarly0P_exZM~CC+dcV8@LW;^l-Qb83-rp
zhK3uM*Ry&!+?Wi66Lmww4b0O6dN|yz41^PPdbo^diWdSUv)U-N<@7VK<k4#Yf2-PP
zdklBpkG@SYY-VT3p1x38c5{uU+sWCdx$=Zm6NFT?@_t}tf?oPwH(=cW`>{K$H*v4K
z!hW}sX8-zcatGoFNuv$-tTpiw9ziHL!^0Z3*zHcGA;xjM;52IaeEu}cEEPo)m&Pu?
z5|=yhjb^DOTWevBKx@aH#`#?Jo$KM-H-447w*KxrH?FO%zsr2XY1EgZi)P%iD#lmo
zuhK^PCfH$F$1eU?X)EpDex8g1%Q=nGvf<R0TZYs0E*RL0&(oo7u(;!RF82GU$TRM=
zO7$Gv-9kI(Z<k-c%-;^64WeBUf%!aT7VlP)cGhW^e-Z0h-3|7U19qEsbp-DY3S<6>
z%daF<Zq#Pl{O_+u(vV(<bo)<Gt3zuNoBji5>PtI;?InrH{t_Aep%7tce7iR1w47!0
zDKt!HxpZ+VwripZb^M2HEi|q?I6Kq5Vub8UXapf%Ah7iZ%c<XX1LOW0YJ+gCKRf5%
zUx^{<9k}0Li$PiYg3mi^x4UlbBHb6^FsI(hfu5wH57+Kcn^)M46|`=M6HN_r0-<$w
zV;xR6*677sDIwmx5qg!a(RhQ+nm<3B&Oi?%Io{SGP!WOF@$!l?$Us*7J4Qk4ZmbR;
zDR0a<+iItuGE&1Q+(0YQ>EY&(?hLJ1e=WIQ?XSe}3~inN;Tv>W(3-<@Wq}bB8Vv
zTtr@>olRLL!*&PVMB5X1LD^0b8Md)`70cfxkJ*QD6=DhLl=2HKS}o{`q{#_QOezbA
zS8WIZ;%@;9SZKr|;;sMsoohF98w2ZzShM&7f?IEk9i#=M+MxG&%(4#p+m*Ih0uWg!
zWnW~s=6Sg*>%ZBgRmt?+<tDuzd|BxmY7<3_d(C1!heH*II+f}uH7<2>{_U5B_!rol
zmRD+Z43FlnM|0DoH<14AlS91Y9<5y7+0q3ZT29W!J7M&2Z>&pgVzfY~e8c1KA`2rP
zx=uNV3)4SA=XQ4={Pcc6h%lglz3HyVju{^qA#cj(vpu^jnnSgizPxeQ0t3qw1=%s|
zA9=I}D`CSwY%m@$)#aSTqQ<=gyKvBha$pq`O_08(*F1eHyJ+R_zK9pNc;}}7zNNR7
z_{myf?e5|^jDXezT3#2$DHN-s_*}gtYhOSys-5);mm=Re6ssW(6M?8ur$8vws|27z
zoq}AWYAO(x41Su>J^Mb@(fMm+8@w{hD~;=WHB_q8xq8nX?NT)+P;+r%@16A?Wui(n
zi6W=BxL{cb-vK8SCxDHLvuIE!q|9_Wq~LTiq;}G&XoLv1S3G^nK8bng8T|tg((IIj
z8raWcVzm<kPMb98!mBjvL`eHM=4l>pvGw7^G{N*0F1Tz*yha}1Dv-qI-^1!4bw9l&
zZ?9=m(0SN2m3L@PMK&!9JJe=wXKB%hQ|rjYx&Z_UJT-&YMCs=D($Uue;wy>oG2Oym
zx`qEoy2ZV8i{EQH`;_<OeuoBelWu*Ck5HW7?4`OJR*cFEq8z94;-<2OFUmEXmQ-4Z
zGTw|>WBK<0{3h~D%KGxoIE&#Pm8Lx%jEeC%{Wxuys5iuF@C{BsPVd>{{37`e|J)<G
zbSzvlw3|Y|7}GukF>x*ZNt|JS)XqG_7b4$^GY{VwSbN^YTMxPre@;6!>JJizyIp_V
zNkFT_S2%rILG{W#EN;@<1uLaUP;MkQV&JP3fBQ_LQEfE&B<%}=P8xDQJQ(X?`eE9D
zoMnD^8PlP)*qIth7|>XhPj#lugcY;E1lFr`hKOW-@?;qN?U&0J9r3s{ey-%5hk(;8
z?r(7>nMu-Jccy&!GTrHBmgi^`{q0mMpYNERg4+a}7h3t(g@F+>7@px;^DZ=r3JbEZ
zNCkg;$!?XOI3J_?`C9W_%nGb@#!2ub{_P*X5~noeNoNh(<-(5Kxgs4LI=Cjo!_2u7
zmCJOf(4lJj+ozU_E*c{)|2UqBh*R|Na?qNo7y%2If$4UIUBUnH7$4P>-r4hes7XB3
zDk#%R9;c6q{OyH$qm5NY#7>~UupVKXP4UfG;}zq*_<M2FJif&vFFI$vJxmhb0~kC~
zVe-cx$C-H|!*5ZxpKZ3Cz2>Blh#tLo?2v`bFpo2X+B4ZI7ov`jG4v%>6r+kbDl%!h
MvBJ}12_H=T4{{!rhyVZp
literal 0
HcmV?d00001
diff --git a/tests/data/acpi/x86/q35/SRAT.spmem b/tests/data/acpi/x86/q35/SRAT.spmem
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9ecd266eda68d035f72f170cb1346b97da623c28 100644
GIT binary patch
literal 384
zcmWFzatvu;WME+AbMklg2v%^42yj*a0-z8Bhz+7a7!Cwb`HUKH87PAbE`Y2Lp$=#O
z7<^!dDa5W0CX1a$n1@{+CQ7_Ige#yd4!9XG29RddV1n^Dh*T#4Ri^>7ff;53kOlxB
Ct_cAE
literal 0
HcmV?d00001
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v14 09/10] tests/qtest: add e820 fw_cfg test
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (7 preceding siblings ...)
2026-06-23 7:50 ` [PATCH v14 08/10] tests/acpi: generate expected blobs for sp-mem SRAT test fanhuang
@ 2026-06-23 7:50 ` fanhuang
2026-06-23 7:50 ` [PATCH v14 10/10] tests/qtest: cover sp-mem SOFT_RESERVED e820 entry fanhuang
2026-06-25 14:34 ` [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory Igor Mammedov
10 siblings, 0 replies; 12+ messages in thread
From: fanhuang @ 2026-06-23 7:50 UTC (permalink / raw)
To: qemu-devel, imammedo, mst, david, gourry
Cc: philmd, berrange, peterx, Zhigang.Luo, Lianjie.Shi, fanhuang
Add a qtest that reads the "etc/e820" fw_cfg table and checks its
structural invariants: the file is a whole number of e820 entries and
every entry has a non-zero length. The baseline q35 case asserts the
guest sees RAM and, with no sp-mem device, no SOFT_RESERVED range.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
---
tests/qtest/e820-test.c | 95 +++++++++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 1 +
2 files changed, 96 insertions(+)
create mode 100644 tests/qtest/e820-test.c
diff --git a/tests/qtest/e820-test.c b/tests/qtest/e820-test.c
new file mode 100644
index 0000000000..1db0744c08
--- /dev/null
+++ b/tests/qtest/e820-test.c
@@ -0,0 +1,95 @@
+/*
+ * qtest e820 fw_cfg test case
+ *
+ * Validate the "etc/e820" fw_cfg table that QEMU hands to the firmware.
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * Authors:
+ * FangSheng Huang <FangSheng.Huang@amd.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "libqtest.h"
+#include "libqos/fw_cfg.h"
+#include "qemu/bswap.h"
+
+/* e820 entry layout and types (cf. hw/i386/e820_memory_layout.h) */
+#define E820_RAM 1
+#define E820_SOFT_RESERVED 0xefffffff
+
+struct e820_entry {
+ uint64_t address;
+ uint64_t length;
+ uint32_t type;
+} QEMU_PACKED;
+
+#define E820_MAX_ENTRIES 128
+
+/*
+ * Read and structurally validate "etc/e820": the file is a packed array
+ * of struct e820_entry, so its size must be a whole multiple of the entry
+ * size and every entry must have a non-zero length. Returns the entry
+ * count and fills @table.
+ */
+static size_t get_e820_table(QFWCFG *fw_cfg, struct e820_entry *table)
+{
+ size_t filesize, n, i;
+
+ filesize = qfw_cfg_get_file(fw_cfg, "etc/e820", table,
+ E820_MAX_ENTRIES * sizeof(*table));
+ g_assert_cmpint(filesize, >, 0);
+ g_assert_cmpint(filesize % sizeof(struct e820_entry), ==, 0);
+
+ n = filesize / sizeof(struct e820_entry);
+ g_assert_cmpint(n, <=, E820_MAX_ENTRIES);
+
+ for (i = 0; i < n; i++) {
+ g_assert_cmpint(le64_to_cpu(table[i].length), >, 0);
+ }
+
+ return n;
+}
+
+static void test_e820_basic(void)
+{
+ struct e820_entry table[E820_MAX_ENTRIES];
+ QFWCFG *fw_cfg;
+ QTestState *s;
+ size_t n, i;
+ bool found_ram = false, found_soft_reserved = false;
+
+ s = qtest_init("-machine q35 -m 256M");
+ fw_cfg = pc_fw_cfg_init(s);
+
+ n = get_e820_table(fw_cfg, table);
+ for (i = 0; i < n; i++) {
+ switch (le32_to_cpu(table[i].type)) {
+ case E820_RAM:
+ found_ram = true;
+ break;
+ case E820_SOFT_RESERVED:
+ found_soft_reserved = true;
+ break;
+ }
+ }
+
+ /* baseline: RAM present, no SOFT_RESERVED range */
+ g_assert_true(found_ram);
+ g_assert_false(found_soft_reserved);
+
+ pc_fw_cfg_uninit(fw_cfg);
+ qtest_quit(s);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("e820/basic", test_e820_basic);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 4897325d84..d69ee27fa4 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -58,6 +58,7 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_AHCI_ICH9') ? ['tco-test'] : []) + \
(config_all_devices.has_key('CONFIG_FDC_ISA') ? ['fdc-test'] : []) + \
(config_all_devices.has_key('CONFIG_I440FX') ? ['fw_cfg-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_Q35') ? ['e820-test'] : []) + \
(config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['vmcoreinfo-test'] : []) + \
(config_all_devices.has_key('CONFIG_I440FX') ? ['i440fx-test'] : []) + \
(config_all_devices.has_key('CONFIG_I440FX') ? ['ide-test'] : []) + \
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v14 10/10] tests/qtest: cover sp-mem SOFT_RESERVED e820 entry
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (8 preceding siblings ...)
2026-06-23 7:50 ` [PATCH v14 09/10] tests/qtest: add e820 fw_cfg test fanhuang
@ 2026-06-23 7:50 ` fanhuang
2026-06-25 14:34 ` [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory Igor Mammedov
10 siblings, 0 replies; 12+ messages in thread
From: fanhuang @ 2026-06-23 7:50 UTC (permalink / raw)
To: qemu-devel, imammedo, mst, david, gourry
Cc: philmd, berrange, peterx, Zhigang.Luo, Lianjie.Shi, fanhuang
Boot one sp-mem device and assert the guest's e820 table gains exactly
one E820_SOFT_RESERVED range whose length matches the device's backend
size.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
---
tests/qtest/e820-test.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/tests/qtest/e820-test.c b/tests/qtest/e820-test.c
index 1db0744c08..aafa3c1aa2 100644
--- a/tests/qtest/e820-test.c
+++ b/tests/qtest/e820-test.c
@@ -16,6 +16,7 @@
#include "libqtest.h"
#include "libqos/fw_cfg.h"
#include "qemu/bswap.h"
+#include "qemu/units.h"
/* e820 entry layout and types (cf. hw/i386/e820_memory_layout.h) */
#define E820_RAM 1
@@ -85,11 +86,44 @@ static void test_e820_basic(void)
qtest_quit(s);
}
+static void test_e820_sp_mem(void)
+{
+ struct e820_entry table[E820_MAX_ENTRIES];
+ QFWCFG *fw_cfg;
+ QTestState *s;
+ size_t n, i;
+ int soft_reserved = 0;
+ uint64_t soft_reserved_len = 0;
+
+ s = qtest_init("-machine q35 -m 256M,slots=2,maxmem=2G "
+ "-object memory-backend-ram,id=ram0,size=256M "
+ "-numa node,nodeid=0,memdev=ram0 "
+ "-object memory-backend-ram,id=spm0,size=128M "
+ "-device sp-mem,id=sp0,memdev=spm0,node=0");
+ fw_cfg = pc_fw_cfg_init(s);
+
+ n = get_e820_table(fw_cfg, table);
+ for (i = 0; i < n; i++) {
+ if (le32_to_cpu(table[i].type) == E820_SOFT_RESERVED) {
+ soft_reserved++;
+ soft_reserved_len = le64_to_cpu(table[i].length);
+ }
+ }
+
+ /* exactly one SOFT_RESERVED range, sized to the backend */
+ g_assert_cmpint(soft_reserved, ==, 1);
+ g_assert_cmpint(soft_reserved_len, ==, 128 * MiB);
+
+ pc_fw_cfg_uninit(fw_cfg);
+ qtest_quit(s);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("e820/basic", test_e820_basic);
+ qtest_add_func("e820/sp-mem", test_e820_sp_mem);
return g_test_run();
}
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory
2026-06-23 7:50 [PATCH v14 00/10] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (9 preceding siblings ...)
2026-06-23 7:50 ` [PATCH v14 10/10] tests/qtest: cover sp-mem SOFT_RESERVED e820 entry fanhuang
@ 2026-06-25 14:34 ` Igor Mammedov
10 siblings, 0 replies; 12+ messages in thread
From: Igor Mammedov @ 2026-06-25 14:34 UTC (permalink / raw)
To: mst
Cc: fanhuang, qemu-devel, david, gourry, philmd, berrange, peterx,
Zhigang.Luo, Lianjie.Shi
On Tue, 23 Jun 2026 15:50:41 +0800
fanhuang <FangSheng.Huang@amd.com> wrote:
> This series adds a TYPE_MEMORY_DEVICE subclass `sp-mem` for boot-time
> SOFT_RESERVED guest memory, following the direction from the v7
> thread [1] and the v8 / v9 / v10 / v11 / v12 / v13 reviews
> [2][3][4][5][6][7].
review is done at this point,
I guess Michael will take it via his tree.
>
> Background
> ----------
>
> This series targets coherent CPU + accelerator shared-address-space
> systems, where the accelerator's HBM is not a device-private
> framebuffer behind a PCIe BAR but a tier of host system memory:
> visible to the CPU in the platform physical address space, shared
> coherently with the accelerator over the platform fabric, and bound
> to a NUMA proximity domain set by platform firmware at boot fabric
> training.
>
> For such a region to function correctly in the guest, two things
> must hold simultaneously: the CPU memory subsystem has to see it in
> the system memory map (so the CPU side can address it), and it has
> to be reserved exclusively for the accelerator's driver (so the
> kernel's general allocator does not hand SPM pages to unrelated
> workloads). The SOFT_RESERVED memory type in E820 plus a matching
> SRAT memory-affinity entry is the mechanism that delivers both: a
> firmware-produced topology that the CPU memory subsystem honors and
> the accelerator's driver consumes for its own range.
>
> Approach
> --------
>
> The series introduces a new TYPE_MEMORY_DEVICE subclass `sp-mem`.
> Each instance binds one host memory backend to a single NUMA
> proximity domain and is boot-time only; placement, mapped-state
> enforcement, and QMP introspection come from the existing
> memory-device framework.
>
> Testing
> -------
>
> Verified end-to-end on q35 + KVM, with both SeaBIOS and OVMF, for:
>
> - single sp-mem instance
> - two sp-mem instances on different NUMA nodes
>
> Guest observations: /proc/iomem shows one SOFT_RESERVED entry per
> sp-mem device, dmesg SRAT parsing reports the matching
> memory_affinity entries with correct PXM, and the umbrella
> HOTPLUGGABLE entry covers the remaining hotplug-memory window
> without overlapping the sp-mem ranges.
>
> In-tree coverage: a bios-tables-test case validates the SRAT
> partition for two sp-mem devices on distinct nodes, and an e820
> fw_cfg test checks the SOFT_RESERVED range.
>
> Changes since v13
> -----------------
>
> - qapi: note that SpMemDeviceInfo's @size is in bytes.
> - pc: fix the indentation of the pc_sp_mem_plug() continuation line.
> - Collected the Reviewed-by / Acked-by tags from the v13 review.
>
> Previous versions
> -----------------
>
> v1: https://lore.kernel.org/qemu-devel/20250924103324.2074819-1-FangSheng.Huang@amd.com/
> v2: https://lore.kernel.org/qemu-devel/20251020090701.4036748-1-FangSheng.Huang@amd.com/
> v3: https://lore.kernel.org/qemu-devel/20251208105137.2058928-1-FangSheng.Huang@amd.com/
> v4: https://lore.kernel.org/qemu-devel/20251209093841.2250527-1-FangSheng.Huang@amd.com/
> v5: https://lore.kernel.org/qemu-devel/20260123024312.1601732-1-FangSheng.Huang@amd.com/
> v6: https://lore.kernel.org/qemu-devel/20260226105023.256568-1-FangSheng.Huang@amd.com/
> v7: https://lore.kernel.org/qemu-devel/20260306082735.1106690-1-FangSheng.Huang@amd.com/
> v8: https://lore.kernel.org/qemu-devel/20260527074215.229119-1-FangSheng.Huang@amd.com/
> v9: https://lore.kernel.org/qemu-devel/20260602084447.1100554-1-FangSheng.Huang@amd.com/
> v10: https://lore.kernel.org/qemu-devel/20260605104609.1739911-1-FangSheng.Huang@amd.com/
> v11: https://lore.kernel.org/qemu-devel/20260611100637.2460507-1-FangSheng.Huang@amd.com/
> v12: https://lore.kernel.org/qemu-devel/20260616090808.3047939-1-FangSheng.Huang@amd.com/
> v13: https://lore.kernel.org/qemu-devel/20260619111136.3481329-1-FangSheng.Huang@amd.com/
>
> [1] v7 thread closeout:
> https://lore.kernel.org/qemu-devel/666a7ba1-5d3a-4732-b872-0d9fb2fe8461@amd.com/
> [2] v8 review:
> https://lore.kernel.org/qemu-devel/20260601105057.2d764e55@imammedo/
> [3] v9 review:
> https://lore.kernel.org/qemu-devel/20260602084447.1100554-1-FangSheng.Huang@amd.com/T/
> [4] v10 review:
> https://lore.kernel.org/qemu-devel/20260605104609.1739911-1-FangSheng.Huang@amd.com/T/
> [5] v11 review:
> https://lore.kernel.org/qemu-devel/20260611100637.2460507-1-FangSheng.Huang@amd.com/T/
> [6] v12 review:
> https://lore.kernel.org/qemu-devel/20260616090808.3047939-1-FangSheng.Huang@amd.com/T/
> [7] v13 review:
> https://lore.kernel.org/qemu-devel/20260619111136.3481329-1-FangSheng.Huang@amd.com/T/
>
> fanhuang (10):
> hw/mem: add sp-mem device for Specific Purpose Memory
> qapi, hmp: introspection for the sp-mem device
> i386/acpi-build: partition device_memory SRAT umbrella for sp-mem
> hw/i386: hook sp-mem into the pc machine plug path
> MAINTAINERS: cover sp-mem under Memory devices, add R: tag
> tests/acpi: add empty expected blobs for sp-mem SRAT test
> tests/acpi: add bios-tables-test case for sp-mem
> tests/acpi: generate expected blobs for sp-mem SRAT test
> tests/qtest: add e820 fw_cfg test
> tests/qtest: cover sp-mem SOFT_RESERVED e820 entry
>
> MAINTAINERS | 3 +
> qapi/machine.json | 43 +++++++++-
> hw/i386/e820_memory_layout.h | 11 +--
> include/hw/mem/sp-mem.h | 33 ++++++++
> hw/core/machine-hmp-cmds.c | 11 +++
> hw/i386/acpi-build.c | 96 ++++++++++++++++++++-
> hw/i386/pc.c | 36 ++++++++
> hw/mem/sp-mem.c | 128 ++++++++++++++++++++++++++++
> tests/qtest/bios-tables-test.c | 21 +++++
> tests/qtest/e820-test.c | 129 +++++++++++++++++++++++++++++
> hw/i386/Kconfig | 2 +
> hw/mem/Kconfig | 4 +
> hw/mem/meson.build | 1 +
> tests/data/acpi/x86/q35/DSDT.spmem | Bin 0 -> 9910 bytes
> tests/data/acpi/x86/q35/SRAT.spmem | Bin 0 -> 384 bytes
> tests/qtest/meson.build | 1 +
> 16 files changed, 508 insertions(+), 11 deletions(-)
> create mode 100644 include/hw/mem/sp-mem.h
> create mode 100644 hw/mem/sp-mem.c
> create mode 100644 tests/qtest/e820-test.c
> create mode 100644 tests/data/acpi/x86/q35/DSDT.spmem
> create mode 100644 tests/data/acpi/x86/q35/SRAT.spmem
>
>
> base-commit: b83371668192a705b878e909c5ae9c1233cbd5fb
^ permalink raw reply [flat|nested] 12+ messages in thread