@@ -69,6 +69,7 @@ config I440FX
imply E1000_PCI
imply VMPORT
imply VMMOUSE
+ imply HAPVDIMM
select PC_PCI
select PC_ACPI
select ACPI_SMBUS
@@ -96,6 +97,7 @@ config Q35
imply E1000E_PCI_EXPRESS
imply VMPORT
imply VMMOUSE
+ imply HAPVDIMM
select PC_PCI
select PC_ACPI
select PCI_EXPRESS_Q35
@@ -73,6 +73,7 @@
#include "hw/acpi/acpi.h"
#include "hw/acpi/cpu_hotplug.h"
#include "acpi-build.h"
+#include "hw/mem/hapvdimm.h"
#include "hw/mem/pc-dimm.h"
#include "hw/mem/nvdimm.h"
#include "hw/cxl/cxl.h"
@@ -1610,7 +1611,8 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine,
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) ||
- object_dynamic_cast(OBJECT(dev), TYPE_X86_IOMMU_DEVICE)) {
+ object_dynamic_cast(OBJECT(dev), TYPE_X86_IOMMU_DEVICE) ||
+ object_dynamic_cast(OBJECT(dev), TYPE_HAPVDIMM)) {
return HOTPLUG_HANDLER(machine);
}
@@ -16,3 +16,7 @@ config CXL_MEM_DEVICE
bool
default y if CXL
select MEM_DEVICE
+
+config HAPVDIMM
+ bool
+ select MEM_DEVICE
new file mode 100644
@@ -0,0 +1,221 @@
+/*
+ * A memory hot-add protocol vDIMM device
+ *
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * Heavily based on pc-dimm.c:
+ * Copyright ProfitBricks GmbH 2012
+ * Copyright (C) 2014 Red Hat Inc
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "exec/memory.h"
+#include "hw/boards.h"
+#include "hw/mem/hapvdimm.h"
+#include "hw/mem/memory-device.h"
+#include "hw/qdev-core.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qemu/module.h"
+#include "sysemu/hostmem.h"
+#include "trace.h"
+
+typedef struct HAPVDIMMDevice {
+ /* private */
+ DeviceState parent_obj;
+
+ /* public */
+ bool ever_realized;
+ uint64_t addr;
+ uint64_t align;
+ uint32_t node;
+ HostMemoryBackend *hostmem;
+} HAPVDIMMDevice;
+
+typedef struct HAPVDIMMDeviceClass {
+ /* private */
+ DeviceClass parent_class;
+} HAPVDIMMDeviceClass;
+
+static bool hapvdimm_adding_allowed;
+static Property hapvdimm_properties[] = {
+ DEFINE_PROP_UINT64(HAPVDIMM_ADDR_PROP, HAPVDIMMDevice, addr, 0),
+ DEFINE_PROP_UINT64(HAPVDIMM_ALIGN_PROP, HAPVDIMMDevice, align, 0),
+ DEFINE_PROP_LINK(HAPVDIMM_MEMDEV_PROP, HAPVDIMMDevice, hostmem,
+ TYPE_MEMORY_BACKEND, HostMemoryBackend *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+void hapvdimm_allow_adding(void)
+{
+ hapvdimm_adding_allowed = true;
+}
+
+void hapvdimm_disallow_adding(void)
+{
+ hapvdimm_adding_allowed = false;
+}
+
+static void hapvdimm_get_size(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ERRP_GUARD();
+ uint64_t value;
+
+ value = memory_device_get_region_size(MEMORY_DEVICE(obj), errp);
+ if (*errp) {
+ return;
+ }
+
+ visit_type_uint64(v, name, &value, errp);
+}
+
+static void hapvdimm_init(Object *obj)
+{
+ object_property_add(obj, HAPVDIMM_SIZE_PROP, "uint64", hapvdimm_get_size,
+ NULL, NULL, NULL);
+}
+
+static void hapvdimm_realize(DeviceState *dev, Error **errp)
+{
+ ERRP_GUARD();
+ HAPVDIMMDevice *hapvdimm = HAPVDIMM(dev);
+ MachineState *ms = MACHINE(qdev_get_machine());
+
+ if (!hapvdimm->ever_realized) {
+ if (!hapvdimm_adding_allowed) {
+ error_setg(errp, "direct adding not allowed");
+ return;
+ }
+
+ hapvdimm->ever_realized = true;
+ }
+
+ memory_device_pre_plug(MEMORY_DEVICE(hapvdimm), ms,
+ hapvdimm->align ? &hapvdimm->align : NULL,
+ errp);
+ if (*errp) {
+ return;
+ }
+
+ if (!hapvdimm->hostmem) {
+ error_setg(errp, "'" HAPVDIMM_MEMDEV_PROP "' property is not set");
+ return;
+ } else if (host_memory_backend_is_mapped(hapvdimm->hostmem)) {
+ const char *path;
+
+ path = object_get_canonical_path_component(OBJECT(hapvdimm->hostmem));
+ error_setg(errp, "can't use already busy memdev: %s", path);
+ return;
+ }
+
+ host_memory_backend_set_mapped(hapvdimm->hostmem, true);
+
+ memory_device_plug(MEMORY_DEVICE(hapvdimm), ms);
+ vmstate_register_ram(host_memory_backend_get_memory(hapvdimm->hostmem),
+ dev);
+}
+
+static void hapvdimm_unrealize(DeviceState *dev)
+{
+ HAPVDIMMDevice *hapvdimm = HAPVDIMM(dev);
+ MachineState *ms = MACHINE(qdev_get_machine());
+
+ memory_device_unplug(MEMORY_DEVICE(hapvdimm), ms);
+ vmstate_unregister_ram(host_memory_backend_get_memory(hapvdimm->hostmem),
+ dev);
+
+ host_memory_backend_set_mapped(hapvdimm->hostmem, false);
+}
+
+static uint64_t hapvdimm_md_get_addr(const MemoryDeviceState *md)
+{
+ return object_property_get_uint(OBJECT(md), HAPVDIMM_ADDR_PROP,
+ &error_abort);
+}
+
+static void hapvdimm_md_set_addr(MemoryDeviceState *md, uint64_t addr,
+ Error **errp)
+{
+ object_property_set_uint(OBJECT(md), HAPVDIMM_ADDR_PROP, addr, errp);
+}
+
+static MemoryRegion *hapvdimm_md_get_memory_region(MemoryDeviceState *md,
+ Error **errp)
+{
+ HAPVDIMMDevice *hapvdimm = HAPVDIMM(md);
+
+ if (!hapvdimm->hostmem) {
+ error_setg(errp, "'" HAPVDIMM_MEMDEV_PROP "' property must be set");
+ return NULL;
+ }
+
+ return host_memory_backend_get_memory(hapvdimm->hostmem);
+}
+
+static void hapvdimm_md_fill_device_info(const MemoryDeviceState *md,
+ MemoryDeviceInfo *info)
+{
+ PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1);
+ const DeviceClass *dc = DEVICE_GET_CLASS(md);
+ const HAPVDIMMDevice *hapvdimm = HAPVDIMM(md);
+ const DeviceState *dev = DEVICE(md);
+
+ if (dev->id) {
+ di->id = g_strdup(dev->id);
+ }
+ di->hotplugged = dev->hotplugged;
+ di->hotpluggable = dc->hotpluggable;
+ di->addr = hapvdimm->addr;
+ di->slot = -1;
+ di->node = 0; /* FIXME: report proper node */
+ di->size = object_property_get_uint(OBJECT(hapvdimm), HAPVDIMM_SIZE_PROP,
+ NULL);
+ di->memdev = object_get_canonical_path(OBJECT(hapvdimm->hostmem));
+
+ info->u.dimm.data = di;
+ info->type = MEMORY_DEVICE_INFO_KIND_DIMM;
+}
+
+static void hapvdimm_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
+
+ dc->realize = hapvdimm_realize;
+ dc->unrealize = hapvdimm_unrealize;
+ device_class_set_props(dc, hapvdimm_properties);
+ dc->desc = "vDIMM for a hot add protocol";
+
+ mdc->get_addr = hapvdimm_md_get_addr;
+ mdc->set_addr = hapvdimm_md_set_addr;
+ mdc->get_plugged_size = memory_device_get_region_size;
+ mdc->get_memory_region = hapvdimm_md_get_memory_region;
+ mdc->fill_device_info = hapvdimm_md_fill_device_info;
+}
+
+static const TypeInfo hapvdimm_info = {
+ .name = TYPE_HAPVDIMM,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(HAPVDIMMDevice),
+ .instance_init = hapvdimm_init,
+ .class_init = hapvdimm_class_init,
+ .class_size = sizeof(HAPVDIMMDeviceClass),
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_MEMORY_DEVICE },
+ { }
+ },
+};
+
+static void hapvdimm_register_types(void)
+{
+ type_register_static(&hapvdimm_info);
+}
+
+type_init(hapvdimm_register_types)
@@ -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_HAPVDIMM', if_true: files('hapvdimm.c'))
softmmu_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss)
new file mode 100644
@@ -0,0 +1,27 @@
+/*
+ * A memory hot-add protocol vDIMM device
+ *
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_HAPVDIMM_H
+#define QEMU_HAPVDIMM_H
+
+#include "qom/object.h"
+
+#define TYPE_HAPVDIMM "mem-hapvdimm"
+OBJECT_DECLARE_SIMPLE_TYPE(HAPVDIMMDevice, HAPVDIMM)
+
+#define HAPVDIMM_ADDR_PROP "addr"
+#define HAPVDIMM_ALIGN_PROP "align"
+#define HAPVDIMM_SIZE_PROP "size"
+#define HAPVDIMM_MEMDEV_PROP "memdev"
+
+void hapvdimm_allow_adding(void);
+void hapvdimm_disallow_adding(void);
+
+#endif