* [PATCH v10 1/4] hw/mem: add sp-mem device for Specific Purpose Memory
2026-06-05 10:46 [PATCH v10 0/4] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
@ 2026-06-05 10:46 ` fanhuang
2026-06-05 15:10 ` David Hildenbrand (Arm)
` (2 more replies)
2026-06-05 10:46 ` [PATCH v10 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem fanhuang
` (2 subsequent siblings)
3 siblings, 3 replies; 14+ messages in thread
From: fanhuang @ 2026-06-05 10:46 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 | 35 +++++++++++
hw/mem/sp-mem.c | 130 ++++++++++++++++++++++++++++++++++++++++
hw/mem/Kconfig | 4 ++
hw/mem/meson.build | 1 +
5 files changed, 211 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..41cc3a188a 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1413,6 +1413,32 @@
}
}
+##
+# @SpMemDeviceInfo:
+#
+# sp-mem device state information
+#
+# @id: device's ID
+#
+# @memaddr: physical address in memory, where device is mapped
+#
+# @size: size of memory that the device provides
+#
+# @node: NUMA proximity domain to which the device is assigned
+#
+# @memdev: memory backend linked with device
+#
+# Since: 11.1
+##
+{ 'struct': 'SpMemDeviceInfo',
+ 'data': { '*id': 'str',
+ 'memaddr': '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..5c0b6ec4f8
--- /dev/null
+++ b/include/hw/mem/sp-mem.h
@@ -0,0 +1,35 @@
+/*
+ * 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/mem/memory-device.h"
+#include "hw/core/qdev.h"
+#include "qom/object.h"
+#include "system/hostmem.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..f6ba9a6d7c
--- /dev/null
+++ b/hw/mem/sp-mem.c
@@ -0,0 +1,130 @@
+/*
+ * 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 SP_MEM(md)->addr;
+}
+
+static void sp_mem_set_addr(MemoryDeviceState *md, uint64_t addr,
+ Error **errp)
+{
+ SP_MEM(md)->addr = addr;
+}
+
+static MemoryRegion *sp_mem_get_memory_region(MemoryDeviceState *md,
+ Error **errp)
+{
+ SpMemDevice *spm = SP_MEM(md);
+
+ if (!spm->hostmem) {
+ error_setg(errp, "'memdev' property must be set");
+ return NULL;
+ }
+ return host_memory_backend_get_memory(spm->hostmem);
+}
+
+static uint64_t sp_mem_get_plugged_size(const MemoryDeviceState *md,
+ Error **errp)
+{
+ SpMemDevice *spm = SP_MEM(md);
+ return spm->hostmem ?
+ memory_region_size(host_memory_backend_get_memory(spm->hostmem)) : 0;
+}
+
+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->memaddr = spm->addr;
+ di->size = spm->hostmem ? memory_region_size(
+ host_memory_backend_get_memory(spm->hostmem)) : 0;
+ di->node = spm->node;
+ di->memdev = spm->hostmem ?
+ object_get_canonical_path(OBJECT(spm->hostmem)) : NULL;
+
+ info->u.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;
+ }
+}
+
+/* boot-time only; no plug/unplug state to migrate */
+static const VMStateDescription vmstate_sp_mem = {
+ .name = TYPE_SP_MEM,
+ .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->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 = sp_mem_get_plugged_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] 14+ messages in thread* Re: [PATCH v10 1/4] hw/mem: add sp-mem device for Specific Purpose Memory
2026-06-05 10:46 ` [PATCH v10 1/4] " fanhuang
@ 2026-06-05 15:10 ` David Hildenbrand (Arm)
2026-06-05 21:22 ` Gregory Price
2026-06-09 9:03 ` Igor Mammedov
2 siblings, 0 replies; 14+ messages in thread
From: David Hildenbrand (Arm) @ 2026-06-05 15:10 UTC (permalink / raw)
To: fanhuang, qemu-devel, imammedo, gourry, philmd; +Cc: Zhigang.Luo, Lianjie.Shi
On 6/5/26 12:46, fanhuang 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).
>
> Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
> ---
Reviewed-by: David Hildenbrand <david@kernel.org>
--
Cheers,
David
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v10 1/4] hw/mem: add sp-mem device for Specific Purpose Memory
2026-06-05 10:46 ` [PATCH v10 1/4] " fanhuang
2026-06-05 15:10 ` David Hildenbrand (Arm)
@ 2026-06-05 21:22 ` Gregory Price
2026-06-09 9:03 ` Igor Mammedov
2 siblings, 0 replies; 14+ messages in thread
From: Gregory Price @ 2026-06-05 21:22 UTC (permalink / raw)
To: fanhuang; +Cc: qemu-devel, david, imammedo, philmd, Zhigang.Luo, Lianjie.Shi
On Fri, Jun 05, 2026 at 06:46:06PM +0800, fanhuang 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).
>
> Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Gregory Price <gourry@gourry.net>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v10 1/4] hw/mem: add sp-mem device for Specific Purpose Memory
2026-06-05 10:46 ` [PATCH v10 1/4] " fanhuang
2026-06-05 15:10 ` David Hildenbrand (Arm)
2026-06-05 21:22 ` Gregory Price
@ 2026-06-09 9:03 ` Igor Mammedov
2 siblings, 0 replies; 14+ messages in thread
From: Igor Mammedov @ 2026-06-09 9:03 UTC (permalink / raw)
To: fanhuang; +Cc: qemu-devel, david, gourry, philmd, Zhigang.Luo, Lianjie.Shi
On Fri, 5 Jun 2026 18:46:06 +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).
>
> Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
> ---
> qapi/machine.json | 43 ++++++++++++-
> include/hw/mem/sp-mem.h | 35 +++++++++++
> hw/mem/sp-mem.c | 130 ++++++++++++++++++++++++++++++++++++++++
> hw/mem/Kconfig | 4 ++
> hw/mem/meson.build | 1 +
> 5 files changed, 211 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..41cc3a188a 100644
> --- a/qapi/machine.json
> +++ b/qapi/machine.json
> @@ -1413,6 +1413,32 @@
> }
> }
>
> +##
> +# @SpMemDeviceInfo:
> +#
> +# sp-mem device state information
> +#
> +# @id: device's ID
> +#
> +# @memaddr: physical address in memory, where device is mapped
> +#
> +# @size: size of memory that the device provides
> +#
> +# @node: NUMA proximity domain to which the device is assigned
> +#
> +# @memdev: memory backend linked with device
> +#
> +# Since: 11.1
> +##
> +{ 'struct': 'SpMemDeviceInfo',
> + 'data': { '*id': 'str',
> + 'memaddr': 'size',
why not 'addr', like DIMM?
> + '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..5c0b6ec4f8
> --- /dev/null
> +++ b/include/hw/mem/sp-mem.h
> @@ -0,0 +1,35 @@
> +/*
> + * 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/mem/memory-device.h"
> +#include "hw/core/qdev.h"
> +#include "qom/object.h"
> +#include "system/hostmem.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..f6ba9a6d7c
> --- /dev/null
> +++ b/hw/mem/sp-mem.c
> @@ -0,0 +1,130 @@
> +/*
> + * 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 SP_MEM(md)->addr;
device cast + deref, should be separate. or even better look at
pc_dimm_md_get_addr()
> +}
> +
> +static void sp_mem_set_addr(MemoryDeviceState *md, uint64_t addr,
> + Error **errp)
> +{
> + SP_MEM(md)->addr = addr;
> +}
ditto
> +
> +static MemoryRegion *sp_mem_get_memory_region(MemoryDeviceState *md,
> + Error **errp)
> +{
> + SpMemDevice *spm = SP_MEM(md);
> +
> + if (!spm->hostmem) {
> + error_setg(errp, "'memdev' property must be set");
> + return NULL;
> + }
> + return host_memory_backend_get_memory(spm->hostmem);
> +}
> +
> +static uint64_t sp_mem_get_plugged_size(const MemoryDeviceState *md,
> + Error **errp)
> +{
> + SpMemDevice *spm = SP_MEM(md);
> + return spm->hostmem ?
> + memory_region_size(host_memory_backend_get_memory(spm->hostmem)) : 0;
> +}
> +
> +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->memaddr = spm->addr;
> + di->size = spm->hostmem ? memory_region_size(
> + host_memory_backend_get_memory(spm->hostmem)) : 0;
why conditional?
> + di->node = spm->node;
> + di->memdev = spm->hostmem ?
> + object_get_canonical_path(OBJECT(spm->hostmem)) : NULL;
ditto
> +
> + 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;
> + }
> +}
> +
> +/* boot-time only; no plug/unplug state to migrate */
> +static const VMStateDescription vmstate_sp_mem = {
> + .name = TYPE_SP_MEM,
> + .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->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 = sp_mem_get_plugged_size;
^^^^
wouldn't memory_device_get_region_size() do here?
> + 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] 14+ messages in thread
* [PATCH v10 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem
2026-06-05 10:46 [PATCH v10 0/4] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
2026-06-05 10:46 ` [PATCH v10 1/4] " fanhuang
@ 2026-06-05 10:46 ` fanhuang
2026-06-05 21:27 ` Gregory Price
2026-06-09 11:14 ` Igor Mammedov
2026-06-05 10:46 ` [PATCH v10 3/4] hw/i386: hook sp-mem into the pc machine plug path fanhuang
2026-06-05 10:46 ` [PATCH v10 4/4] MAINTAINERS: cover sp-mem under Memory devices, add R: tag fanhuang
3 siblings, 2 replies; 14+ messages in thread
From: fanhuang @ 2026-06-05 10:46 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 | 98 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 92 insertions(+), 6 deletions(-)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 0d7c83d5e9..76efa83808 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 collect_sp_mem_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));
+ uint64_t cursor, end;
+ int nb_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
+ uint32_t hotplug_pxm = nb_nodes > 0 ? nb_nodes - 1 : 0;
+ guint i;
+
+ if (!ms->device_memory) {
+ return;
+ }
+
+ cursor = ms->device_memory->base;
+ end = cursor + memory_region_size(&ms->device_memory->mr);
+
+ object_child_foreach_recursive(qdev_get_machine(),
+ collect_sp_mem_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 (cursor < r->addr) {
+ build_srat_memory(table_data, cursor, r->addr - cursor,
+ hotplug_pxm,
+ MEM_AFFINITY_HOTPLUGGABLE |
+ MEM_AFFINITY_ENABLED);
+ }
+ build_srat_memory(table_data, r->addr, r->size, r->node,
+ MEM_AFFINITY_ENABLED);
+ cursor = r->addr + r->size;
+ }
+
+ if (cursor < end) {
+ build_srat_memory(table_data, cursor, end - cursor,
+ hotplug_pxm,
+ MEM_AFFINITY_HOTPLUGGABLE |
+ MEM_AFFINITY_ENABLED);
+ }
+}
+
#define HOLE_640K_START (640 * KiB)
#define HOLE_640K_END (1 * MiB)
@@ -1481,12 +1572,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
* Memory devices may override proximity set by this entry,
* providing _PXM method if necessary.
*/
- if (machine->device_memory) {
- build_srat_memory(table_data, machine->device_memory->base,
- memory_region_size(&machine->device_memory->mr),
- nb_numa_nodes - 1,
- MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
- }
+ build_srat_device_memory(table_data, machine);
acpi_table_end(linker, &table);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v10 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem
2026-06-05 10:46 ` [PATCH v10 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem fanhuang
@ 2026-06-05 21:27 ` Gregory Price
2026-06-09 3:53 ` Huang, FangSheng (Jerry)
2026-06-09 11:14 ` Igor Mammedov
1 sibling, 1 reply; 14+ messages in thread
From: Gregory Price @ 2026-06-05 21:27 UTC (permalink / raw)
To: fanhuang; +Cc: qemu-devel, david, imammedo, philmd, Zhigang.Luo, Lianjie.Shi
On Fri, Jun 05, 2026 at 06:46:07PM +0800, fanhuang 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>
> ---
> hw/i386/acpi-build.c | 98 +++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 92 insertions(+), 6 deletions(-)
>
> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
> index 0d7c83d5e9..76efa83808 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 collect_sp_mem_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));
> + uint64_t cursor, end;
> + int nb_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
> + uint32_t hotplug_pxm = nb_nodes > 0 ? nb_nodes - 1 : 0;
> + guint i;
> +
> + if (!ms->device_memory) {
> + return;
> + }
> +
> + cursor = ms->device_memory->base;
> + end = cursor + memory_region_size(&ms->device_memory->mr);
> +
> + object_child_foreach_recursive(qdev_get_machine(),
> + collect_sp_mem_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 (cursor < r->addr) {
> + build_srat_memory(table_data, cursor, r->addr - cursor,
> + hotplug_pxm,
> + MEM_AFFINITY_HOTPLUGGABLE |
> + MEM_AFFINITY_ENABLED);
> + }
> + build_srat_memory(table_data, r->addr, r->size, r->node,
> + MEM_AFFINITY_ENABLED);
> + cursor = r->addr + r->size;
> + }
> +
Should we expect (cursor == end) here and fire a warning if there wasn't
a full range to cover the region? If not - why set it all as
hotpluggable anyway?
(Maybe just worth a comment here what the last block is actually doing)
> + if (cursor < end) {
> + build_srat_memory(table_data, cursor, end - cursor,
> + hotplug_pxm,
> + MEM_AFFINITY_HOTPLUGGABLE |
> + MEM_AFFINITY_ENABLED);
> + }
> +}
> +
> #define HOLE_640K_START (640 * KiB)
> #define HOLE_640K_END (1 * MiB)
>
> @@ -1481,12 +1572,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
> * Memory devices may override proximity set by this entry,
> * providing _PXM method if necessary.
> */
> - if (machine->device_memory) {
> - build_srat_memory(table_data, machine->device_memory->base,
> - memory_region_size(&machine->device_memory->mr),
> - nb_numa_nodes - 1,
> - MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
> - }
> + build_srat_device_memory(table_data, machine);
>
> acpi_table_end(linker, &table);
> }
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v10 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem
2026-06-05 21:27 ` Gregory Price
@ 2026-06-09 3:53 ` Huang, FangSheng (Jerry)
0 siblings, 0 replies; 14+ messages in thread
From: Huang, FangSheng (Jerry) @ 2026-06-09 3:53 UTC (permalink / raw)
To: Gregory Price
Cc: qemu-devel, david, imammedo, philmd, Zhigang.Luo, Lianjie.Shi
On 6/6/2026 5:27 AM, Gregory Price wrote:
> On Fri, Jun 05, 2026 at 06:46:07PM +0800, fanhuang 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>
>> ---
>> hw/i386/acpi-build.c | 98 +++++++++++++++++++++++++++++++++++++++++---
>> 1 file changed, 92 insertions(+), 6 deletions(-)
>>
>> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
>> index 0d7c83d5e9..76efa83808 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 collect_sp_mem_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));
>> + uint64_t cursor, end;
>> + int nb_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
>> + uint32_t hotplug_pxm = nb_nodes > 0 ? nb_nodes - 1 : 0;
>> + guint i;
>> +
>> + if (!ms->device_memory) {
>> + return;
>> + }
>> +
>> + cursor = ms->device_memory->base;
>> + end = cursor + memory_region_size(&ms->device_memory->mr);
>> +
>> + object_child_foreach_recursive(qdev_get_machine(),
>> + collect_sp_mem_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 (cursor < r->addr) {
>> + build_srat_memory(table_data, cursor, r->addr - cursor,
>> + hotplug_pxm,
>> + MEM_AFFINITY_HOTPLUGGABLE |
>> + MEM_AFFINITY_ENABLED);
>> + }
>> + build_srat_memory(table_data, r->addr, r->size, r->node,
>> + MEM_AFFINITY_ENABLED);
>> + cursor = r->addr + r->size;
>> + }
>> +
>
> Should we expect (cursor == end) here and fire a warning if there wasn't
> a full range to cover the region? If not - why set it all as
> hotpluggable anyway?
>
> (Maybe just worth a comment here what the last block is actually doing)
>
Thanks, Gregory.
(cursor == end) is not an invariant — ms->device_memory is sized
for the maxmem cap, so sp-mem typically leaves the rest of the
window unused. The trailing HOTPLUGGABLE | ENABLED entry covers
that remainder so future pc-dimm / virtio-mem hot-add into the same
window still sees a properly-flagged SRAT range, preserving the
prior umbrella behaviour.
Agreed on the inline comment; will include if/when we respin.
>> + if (cursor < end) {
>> + build_srat_memory(table_data, cursor, end - cursor,
>> + hotplug_pxm,
>> + MEM_AFFINITY_HOTPLUGGABLE |
>> + MEM_AFFINITY_ENABLED);
>> + }
>> +}
>> +
>> #define HOLE_640K_START (640 * KiB)
>> #define HOLE_640K_END (1 * MiB)
>>
>> @@ -1481,12 +1572,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
>> * Memory devices may override proximity set by this entry,
>> * providing _PXM method if necessary.
>> */
>> - if (machine->device_memory) {
>> - build_srat_memory(table_data, machine->device_memory->base,
>> - memory_region_size(&machine->device_memory->mr),
>> - nb_numa_nodes - 1,
>> - MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
>> - }
>> + build_srat_device_memory(table_data, machine);
>>
>> acpi_table_end(linker, &table);
>> }
>> --
>> 2.34.1
>>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v10 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem
2026-06-05 10:46 ` [PATCH v10 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem fanhuang
2026-06-05 21:27 ` Gregory Price
@ 2026-06-09 11:14 ` Igor Mammedov
1 sibling, 0 replies; 14+ messages in thread
From: Igor Mammedov @ 2026-06-09 11:14 UTC (permalink / raw)
To: fanhuang; +Cc: qemu-devel, david, gourry, philmd, Zhigang.Luo, Lianjie.Shi
On Fri, 5 Jun 2026 18:46:07 +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>
> ---
> hw/i386/acpi-build.c | 98 +++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 92 insertions(+), 6 deletions(-)
>
> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
> index 0d7c83d5e9..76efa83808 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 collect_sp_mem_ranges_cb(Object *obj, void *opaque)
sp_mem_collect... maybe
> +{
> + 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));
> + uint64_t cursor, end;
region_start, region_end
> + int nb_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
> + uint32_t hotplug_pxm = nb_nodes > 0 ? nb_nodes - 1 : 0;
when function is called ms->numa_state->num_nodes is always > 0, isn't it?
> + guint i;
> +
> + if (!ms->device_memory) {
> + return;
> + }
> +
> + cursor = ms->device_memory->base;
> + end = cursor + memory_region_size(&ms->device_memory->mr);
> +
> + object_child_foreach_recursive(qdev_get_machine(),
> + collect_sp_mem_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 (cursor < r->addr) {
> + build_srat_memory(table_data, cursor, r->addr - cursor,
> + hotplug_pxm,
> + MEM_AFFINITY_HOTPLUGGABLE |
> + MEM_AFFINITY_ENABLED);
> + }
> + build_srat_memory(table_data, r->addr, r->size, r->node,
> + MEM_AFFINITY_ENABLED);
> + cursor = r->addr + r->size;
> + }
> +
> + if (cursor < end) {
> + build_srat_memory(table_data, cursor, end - cursor,
> + hotplug_pxm,
> + MEM_AFFINITY_HOTPLUGGABLE |
> + MEM_AFFINITY_ENABLED);
> + }
> +}
> +
> #define HOLE_640K_START (640 * KiB)
> #define HOLE_640K_END (1 * MiB)
>
> @@ -1481,12 +1572,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
> * Memory devices may override proximity set by this entry,
> * providing _PXM method if necessary.
> */
> - if (machine->device_memory) {
keep it here, and drop one in build_srat_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] 14+ messages in thread
* [PATCH v10 3/4] hw/i386: hook sp-mem into the pc machine plug path
2026-06-05 10:46 [PATCH v10 0/4] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
2026-06-05 10:46 ` [PATCH v10 1/4] " fanhuang
2026-06-05 10:46 ` [PATCH v10 2/4] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem fanhuang
@ 2026-06-05 10:46 ` fanhuang
2026-06-09 11:52 ` Igor Mammedov
2026-06-05 10:46 ` [PATCH v10 4/4] MAINTAINERS: cover sp-mem under Memory devices, add R: tag fanhuang
3 siblings, 1 reply; 14+ messages in thread
From: fanhuang @ 2026-06-05 10:46 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 | 42 ++++++++++++++++++++++++++++++++++++
hw/i386/Kconfig | 2 ++
3 files changed, 50 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 2ecad3c503..b538f49f23 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,49 @@ 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 (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;
+ }
+ 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;
+
+ host_memory_backend_set_mapped(spm->hostmem, true);
+ 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 +1363,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 +1409,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] 14+ messages in thread* Re: [PATCH v10 3/4] hw/i386: hook sp-mem into the pc machine plug path
2026-06-05 10:46 ` [PATCH v10 3/4] hw/i386: hook sp-mem into the pc machine plug path fanhuang
@ 2026-06-09 11:52 ` Igor Mammedov
0 siblings, 0 replies; 14+ messages in thread
From: Igor Mammedov @ 2026-06-09 11:52 UTC (permalink / raw)
To: fanhuang; +Cc: qemu-devel, david, gourry, philmd, Zhigang.Luo, Lianjie.Shi
On Fri, 5 Jun 2026 18:46:08 +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>
> ---
> hw/i386/e820_memory_layout.h | 11 +++++-----
> hw/i386/pc.c | 42 ++++++++++++++++++++++++++++++++++++
> hw/i386/Kconfig | 2 ++
> 3 files changed, 50 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 2ecad3c503..b538f49f23 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,49 @@ 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 (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;
> + }
this belongs to sp_mem realize
> + 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;
> + }
I think this the right place for this, despite what other mems do.
Perhaps it is worth to consolidate/cleanup 1st.
I'd put it in memory_device_pre_plug():
if has_node_prop:
.....
> + 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;
> +
> + host_memory_backend_set_mapped(spm->hostmem, true);
dup? see above.
> + 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 +1363,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 +1409,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] 14+ messages in thread
* [PATCH v10 4/4] MAINTAINERS: cover sp-mem under Memory devices, add R: tag
2026-06-05 10:46 [PATCH v10 0/4] hw/mem: add sp-mem device for Specific Purpose Memory fanhuang
` (2 preceding siblings ...)
2026-06-05 10:46 ` [PATCH v10 3/4] hw/i386: hook sp-mem into the pc machine plug path fanhuang
@ 2026-06-05 10:46 ` fanhuang
2026-06-09 11:53 ` Igor Mammedov
2026-06-09 12:51 ` David Hildenbrand (Arm)
3 siblings, 2 replies; 14+ messages in thread
From: fanhuang @ 2026-06-05 10:46 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>
---
MAINTAINERS | 3 +++
1 file changed, 3 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 7752917d8c..db34f6cf17 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3361,13 +3361,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] 14+ messages in thread* Re: [PATCH v10 4/4] MAINTAINERS: cover sp-mem under Memory devices, add R: tag
2026-06-05 10:46 ` [PATCH v10 4/4] MAINTAINERS: cover sp-mem under Memory devices, add R: tag fanhuang
@ 2026-06-09 11:53 ` Igor Mammedov
2026-06-09 12:51 ` David Hildenbrand (Arm)
1 sibling, 0 replies; 14+ messages in thread
From: Igor Mammedov @ 2026-06-09 11:53 UTC (permalink / raw)
To: fanhuang; +Cc: qemu-devel, david, gourry, philmd, Zhigang.Luo, Lianjie.Shi
On Fri, 5 Jun 2026 18:46:09 +0800
fanhuang <FangSheng.Huang@amd.com> wrote:
> Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
> ---
> MAINTAINERS | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7752917d8c..db34f6cf17 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3361,13 +3361,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
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v10 4/4] MAINTAINERS: cover sp-mem under Memory devices, add R: tag
2026-06-05 10:46 ` [PATCH v10 4/4] MAINTAINERS: cover sp-mem under Memory devices, add R: tag fanhuang
2026-06-09 11:53 ` Igor Mammedov
@ 2026-06-09 12:51 ` David Hildenbrand (Arm)
1 sibling, 0 replies; 14+ messages in thread
From: David Hildenbrand (Arm) @ 2026-06-09 12:51 UTC (permalink / raw)
To: fanhuang, qemu-devel, imammedo, gourry, philmd; +Cc: Zhigang.Luo, Lianjie.Shi
On 6/5/26 12:46, fanhuang wrote:
/insert text here
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
--
Cheers,
David
^ permalink raw reply [flat|nested] 14+ messages in thread