* [PATCH v12 1/4] hw/mem: add sp-mem device for Specific Purpose Memory
2026-06-16 9:08 [PATCH v12 0/4] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
@ 2026-06-16 9:08 ` fanhuang
2026-06-17 9:16 ` Igor Mammedov
2026-06-16 9:08 ` [PATCH v12 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem fanhuang
` (3 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: fanhuang @ 2026-06-16 9:08 UTC (permalink / raw)
To: qemu-devel, david, imammedo, gourry, philmd
Cc: 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>
---
qapi/machine.json | 43 +++++++++++-
include/hw/mem/sp-mem.h | 33 +++++++++
hw/core/machine-hmp-cmds.c | 11 +++
hw/mem/sp-mem.c | 136 +++++++++++++++++++++++++++++++++++++
hw/mem/Kconfig | 4 ++
hw/mem/meson.build | 1 +
6 files changed, 226 insertions(+), 2 deletions(-)
create mode 100644 include/hw/mem/sp-mem.h
create mode 100644 hw/mem/sp-mem.c
diff --git a/qapi/machine.json b/qapi/machine.json
index 685e4e29b8..777cfc81e1 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
+#
+# @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/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/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
new file mode 100644
index 0000000000..3b46cabc46
--- /dev/null
+++ b/hw/mem/sp-mem.c
@@ -0,0 +1,136 @@
+/*
+ * 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 "migration/vmstate.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_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);
+
+ 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 const VMStateDescription vmstate_sp_mem = {
+ .name = TYPE_SP_MEM,
+ /* boot-time only; no plug/unplug state to migrate */
+ .unmigratable = 1,
+};
+
+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;
+ dc->vmsd = &vmstate_sp_mem;
+ 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;
+ mdc->fill_device_info = sp_mem_fill_device_info;
+}
+
+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] 10+ messages in thread* Re: [PATCH v12 1/4] hw/mem: add sp-mem device for Specific Purpose Memory
2026-06-16 9:08 ` [PATCH v12 1/4] " fanhuang
@ 2026-06-17 9:16 ` Igor Mammedov
2026-06-17 10:00 ` Huang, FangSheng (Jerry)
0 siblings, 1 reply; 10+ messages in thread
From: Igor Mammedov @ 2026-06-17 9:16 UTC (permalink / raw)
To: fanhuang
Cc: qemu-devel, david, gourry, philmd, Zhigang.Luo, Lianjie.Shi,
Peter Xu
On Tue, 16 Jun 2026 17:08:05 +0800
fanhuang <FangSheng.Huang@amd.com> wrote:
> 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).
Modulo nitpicking/patch splitting and a migration question LGTM
>
> Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
> ---
> qapi/machine.json | 43 +++++++++++-
> include/hw/mem/sp-mem.h | 33 +++++++++
> hw/core/machine-hmp-cmds.c | 11 +++
> hw/mem/sp-mem.c | 136 +++++++++++++++++++++++++++++++++++++
> hw/mem/Kconfig | 4 ++
> hw/mem/meson.build | 1 +
> 6 files changed, 226 insertions(+), 2 deletions(-)
> create mode 100644 include/hw/mem/sp-mem.h
> create mode 100644 hw/mem/sp-mem.c
>
> diff --git a/qapi/machine.json b/qapi/machine.json
> index 685e4e29b8..777cfc81e1 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
> +#
> +# @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/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/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();
> }
hmp could be a separate patch.
> diff --git a/hw/mem/sp-mem.c b/hw/mem/sp-mem.c
> new file mode 100644
> index 0000000000..3b46cabc46
> --- /dev/null
> +++ b/hw/mem/sp-mem.c
> @@ -0,0 +1,136 @@
> +/*
> + * 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 "migration/vmstate.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_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;
> +}
if missing this doesn't break anything, I'd bundle it together with hmp patch
> +
> +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 const VMStateDescription vmstate_sp_mem = {
> + .name = TYPE_SP_MEM,
> + /* boot-time only; no plug/unplug state to migrate */
> + .unmigratable = 1,
this is explicit migration blocker, isn't it?
are we sure about setting it un-migratable, if yes/no than why?
I don't see how plug/unplug is involved here,
but I'd speculate that we would want to migrate memory content itself.
CCing Peter,
for a look from migration pov
> +};
> +
> +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;
> + dc->vmsd = &vmstate_sp_mem;
> + 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;
> + mdc->fill_device_info = sp_mem_fill_device_info;
> +}
> +
> +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'))
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v12 1/4] hw/mem: add sp-mem device for Specific Purpose Memory
2026-06-17 9:16 ` Igor Mammedov
@ 2026-06-17 10:00 ` Huang, FangSheng (Jerry)
0 siblings, 0 replies; 10+ messages in thread
From: Huang, FangSheng (Jerry) @ 2026-06-17 10:00 UTC (permalink / raw)
To: Igor Mammedov
Cc: qemu-devel, david, gourry, philmd, Zhigang.Luo, Lianjie.Shi,
Peter Xu
On 6/17/2026 5:16 PM, Igor Mammedov wrote:
> On Tue, 16 Jun 2026 17:08:05 +0800
> fanhuang <FangSheng.Huang@amd.com> wrote:
>
>> 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).
>
> Modulo nitpicking/patch splitting and a migration question LGTM
>
>> + 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();
>> }
>
> hmp could be a separate patch.
>
>
>> +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;
>> +}
>
> if missing this doesn't break anything, I'd bundle it together with hmp patch
>
>
Will split the introspection bits (fill_device_info + the HMP
printer) into their own patch in the next respin.
>> +
>> +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 const VMStateDescription vmstate_sp_mem = {
>> + .name = TYPE_SP_MEM,
>> + /* boot-time only; no plug/unplug state to migrate */
>> + .unmigratable = 1,
>
> this is explicit migration blocker, isn't it?
> are we sure about setting it un-migratable, if yes/no than why?
>
> I don't see how plug/unplug is involved here,
> but I'd speculate that we would want to migrate memory content itself.
>
> CCing Peter,
> for a look from migration pov
>
>
You're right that the "plug/unplug" wording is off.
The device carries no per-device migration state, so with a normal
RAM backend it would migrate like pc-dimm. I'd originally marked it
unmigratable out of caution, being unsure whether the backend could
be migrated -- but on reflection that's a property of the backend,
not of sp-mem: a backend that can't be migrated can express that
itself, independently of this device. So dropping the flag and
letting sp-mem migrate like pc-dimm, leaving any restriction to the
backend, looks like the more flexible choice.
Peter, does that match your view, or is there a reason to keep a gate
at the device?
Best regards,
FangSheng Huang (Jerry)
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v12 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem
2026-06-16 9:08 [PATCH v12 0/4] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
2026-06-16 9:08 ` [PATCH v12 1/4] " fanhuang
@ 2026-06-16 9:08 ` fanhuang
2026-06-17 11:04 ` Igor Mammedov
2026-06-16 9:08 ` [PATCH v12 3/4] hw/i386: hook sp-mem into the pc machine plug path fanhuang
` (2 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: fanhuang @ 2026-06-16 9:08 UTC (permalink / raw)
To: qemu-devel, david, imammedo, gourry, philmd
Cc: 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>
---
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 0d7c83d5e9..db144b5706 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"
@@ -1346,6 +1347,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)
@@ -1482,10 +1573,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] 10+ messages in thread* Re: [PATCH v12 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem
2026-06-16 9:08 ` [PATCH v12 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem fanhuang
@ 2026-06-17 11:04 ` Igor Mammedov
0 siblings, 0 replies; 10+ messages in thread
From: Igor Mammedov @ 2026-06-17 11:04 UTC (permalink / raw)
To: fanhuang; +Cc: qemu-devel, david, gourry, philmd, Zhigang.Luo, Lianjie.Shi
On Tue, 16 Jun 2026 17:08:06 +0800
fanhuang <FangSheng.Huang@amd.com> wrote:
> 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>
LGTM, but i haven't really tested it.
please add test case for this, see tests/qtest/bios-tables-test.c
for examples and process.
With condition adding test to cover it:
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 0d7c83d5e9..db144b5706 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"
> @@ -1346,6 +1347,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)
>
> @@ -1482,10 +1573,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);
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v12 3/4] hw/i386: hook sp-mem into the pc machine plug path
2026-06-16 9:08 [PATCH v12 0/4] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
2026-06-16 9:08 ` [PATCH v12 1/4] " fanhuang
2026-06-16 9:08 ` [PATCH v12 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem fanhuang
@ 2026-06-16 9:08 ` fanhuang
2026-06-17 11:14 ` Igor Mammedov
2026-06-16 9:08 ` [PATCH v12 4/4] MAINTAINERS: cover sp-mem under Memory devices, add R: tag fanhuang
2026-06-17 11:19 ` [PATCH v12 0/4] hw/mem: add sp-mem device for Specific Purpose Memory Igor Mammedov
4 siblings, 1 reply; 10+ messages in thread
From: fanhuang @ 2026-06-16 9:08 UTC (permalink / raw)
To: qemu-devel, david, imammedo, gourry, philmd
Cc: 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>
---
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 7b6ad97e5a..ec3459389b 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"
@@ -1283,11 +1284,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)) {
@@ -1324,6 +1357,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)) {
@@ -1368,6 +1403,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] 10+ messages in thread* Re: [PATCH v12 3/4] hw/i386: hook sp-mem into the pc machine plug path
2026-06-16 9:08 ` [PATCH v12 3/4] hw/i386: hook sp-mem into the pc machine plug path fanhuang
@ 2026-06-17 11:14 ` Igor Mammedov
0 siblings, 0 replies; 10+ messages in thread
From: Igor Mammedov @ 2026-06-17 11:14 UTC (permalink / raw)
To: fanhuang; +Cc: qemu-devel, david, gourry, philmd, Zhigang.Luo, Lianjie.Shi
On Tue, 16 Jun 2026 17:08:07 +0800
fanhuang <FangSheng.Huang@amd.com> wrote:
> 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>
see below comment below wrt testing but otherwise:
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 7b6ad97e5a..ec3459389b 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"
> @@ -1283,11 +1284,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);
We haven't been testing e820 (lack of coverage mostly historical due to
it being mostly static not touched code).
But I think we should add tests for it. (something similar to bios-tables-test)
1st add clean slate test before your change, and then build/add on top to
make sure we do not regress, whatever existed before.
> +}
> +
> 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)) {
> @@ -1324,6 +1357,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)) {
> @@ -1368,6 +1403,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
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v12 4/4] MAINTAINERS: cover sp-mem under Memory devices, add R: tag
2026-06-16 9:08 [PATCH v12 0/4] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (2 preceding siblings ...)
2026-06-16 9:08 ` [PATCH v12 3/4] hw/i386: hook sp-mem into the pc machine plug path fanhuang
@ 2026-06-16 9:08 ` fanhuang
2026-06-17 11:19 ` [PATCH v12 0/4] hw/mem: add sp-mem device for Specific Purpose Memory Igor Mammedov
4 siblings, 0 replies; 10+ messages in thread
From: fanhuang @ 2026-06-16 9:08 UTC (permalink / raw)
To: qemu-devel, david, imammedo, gourry, philmd
Cc: 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 2b5b581e17..192b5337d2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3346,13 +3346,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] 10+ messages in thread* Re: [PATCH v12 0/4] hw/mem: add sp-mem device for Specific Purpose Memory
2026-06-16 9:08 [PATCH v12 0/4] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (3 preceding siblings ...)
2026-06-16 9:08 ` [PATCH v12 4/4] MAINTAINERS: cover sp-mem under Memory devices, add R: tag fanhuang
@ 2026-06-17 11:19 ` Igor Mammedov
4 siblings, 0 replies; 10+ messages in thread
From: Igor Mammedov @ 2026-06-17 11:19 UTC (permalink / raw)
To: fanhuang; +Cc: qemu-devel, david, gourry, philmd, Zhigang.Luo, Lianjie.Shi
On Tue, 16 Jun 2026 17:08:04 +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 reviews [2][3][4][5].
review is done, modulo cosmetic patch restructuring and missing tests
LGTM.
as for adding duplication, I'm expecting cleanup/consolidation
refactoring on top, that I've asked for in earlier reviews.
>
> 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.
>
> Changes since v11
> -----------------
>
> - Drop the unneeded memory-device.h and system/hostmem.h includes
> from sp-mem.h (HostMemoryBackend is forward-declared).
> - Use SP_MEM_MEMDEV_PROP in sp_mem_get_memory_region()'s error
> message, matching realize().
> - Move the .unmigratable comment next to the field it documents.
>
> All v11 patch-1 review comments from Philippe; patches 2-4 unchanged.
> Patch 4 (MAINTAINERS) retains the Acked-by from Igor and David.
>
> 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/
>
> [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/
>
> fanhuang (4):
> hw/mem: add sp-mem device for Specific Purpose Memory
> 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
>
> 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 | 136 +++++++++++++++++++++++++++++++++++
> hw/i386/Kconfig | 2 +
> hw/mem/Kconfig | 4 ++
> hw/mem/meson.build | 1 +
> 11 files changed, 365 insertions(+), 11 deletions(-)
> create mode 100644 include/hw/mem/sp-mem.h
> create mode 100644 hw/mem/sp-mem.c
>
>
> base-commit: 2f28d34ea0aead9830478cd1d3d0dd9d9191d82e
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 10+ messages in thread