aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure13
-rw-r--r--docs/specs/acpi_hw_reduced_hotplug.rst70
-rw-r--r--docs/specs/index.rst1
-rw-r--r--hw/acpi/Kconfig4
-rw-r--r--hw/acpi/Makefile.objs1
-rw-r--r--hw/acpi/generic_event_device.c311
-rw-r--r--hw/acpi/memory_hotplug.c43
-rw-r--r--hw/arm/Kconfig4
-rw-r--r--hw/arm/virt-acpi-build.c35
-rw-r--r--hw/arm/virt.c124
-rw-r--r--hw/i386/acpi-build.c7
-rw-r--r--hw/i386/pc.c3
-rw-r--r--hw/virtio/Makefile.objs2
-rw-r--r--hw/virtio/vhost-user-fs-pci.c85
-rw-r--r--hw/virtio/vhost-user-fs.c299
-rw-r--r--include/hw/acpi/acpi_dev_interface.h1
-rw-r--r--include/hw/acpi/generic_event_device.h103
-rw-r--r--include/hw/acpi/memory_hotplug.h9
-rw-r--r--include/hw/arm/virt.h5
-rw-r--r--include/hw/i386/pc.h3
-rw-r--r--include/hw/virtio/vhost-user-fs.h45
-rw-r--r--net/vhost-user.c4
-rw-r--r--tests/bios-tables-test.c81
-rw-r--r--tests/data/acpi/virt/APIC.memhpbin0 -> 168 bytes
-rw-r--r--tests/data/acpi/virt/APIC.numamembin0 -> 168 bytes
-rw-r--r--tests/data/acpi/virt/DSDTbin18476 -> 18470 bytes
-rw-r--r--tests/data/acpi/virt/DSDT.memhpbin0 -> 19807 bytes
-rw-r--r--tests/data/acpi/virt/DSDT.numamembin0 -> 18470 bytes
-rw-r--r--tests/data/acpi/virt/FACP.memhpbin0 -> 268 bytes
-rw-r--r--tests/data/acpi/virt/FACP.numamembin0 -> 268 bytes
-rw-r--r--tests/data/acpi/virt/GTDT.memhpbin0 -> 96 bytes
-rw-r--r--tests/data/acpi/virt/GTDT.numamembin0 -> 96 bytes
-rw-r--r--tests/data/acpi/virt/MCFG.memhpbin0 -> 60 bytes
-rw-r--r--tests/data/acpi/virt/MCFG.numamembin0 -> 60 bytes
-rw-r--r--tests/data/acpi/virt/SLIT.memhpbin0 -> 48 bytes
-rw-r--r--tests/data/acpi/virt/SPCR.memhpbin0 -> 80 bytes
-rw-r--r--tests/data/acpi/virt/SPCR.numamembin0 -> 80 bytes
-rw-r--r--tests/data/acpi/virt/SRAT.memhpbin0 -> 186 bytes
-rw-r--r--tests/data/acpi/virt/SRAT.numamembin0 -> 106 bytes
39 files changed, 1211 insertions, 42 deletions
diff --git a/configure b/configure
index 08ca4bc..145fcab 100755
--- a/configure
+++ b/configure
@@ -381,6 +381,7 @@ vhost_crypto=""
vhost_scsi=""
vhost_vsock=""
vhost_user=""
+vhost_user_fs=""
kvm="no"
hax="no"
hvf="no"
@@ -1293,6 +1294,10 @@ for opt do
;;
--enable-vhost-vsock) vhost_vsock="yes"
;;
+ --disable-vhost-user-fs) vhost_user_fs="no"
+ ;;
+ --enable-vhost-user-fs) vhost_user_fs="yes"
+ ;;
--disable-opengl) opengl="no"
;;
--enable-opengl) opengl="yes"
@@ -2236,6 +2241,10 @@ test "$vhost_crypto" = "" && vhost_crypto=$vhost_user
if test "$vhost_crypto" = "yes" && test "$vhost_user" = "no"; then
error_exit "--enable-vhost-crypto requires --enable-vhost-user"
fi
+test "$vhost_user_fs" = "" && vhost_user_fs=$vhost_user
+if test "$vhost_user_fs" = "yes" && test "$vhost_user" = "no"; then
+ error_exit "--enable-vhost-user-fs requires --enable-vhost-user"
+fi
# OR the vhost-kernel and vhost-user values for simplicity
if test "$vhost_net" = ""; then
@@ -6374,6 +6383,7 @@ echo "vhost-crypto support $vhost_crypto"
echo "vhost-scsi support $vhost_scsi"
echo "vhost-vsock support $vhost_vsock"
echo "vhost-user support $vhost_user"
+echo "vhost-user-fs support $vhost_user_fs"
echo "Trace backends $trace_backends"
if have_backend "simple"; then
echo "Trace output file $trace_file-<pid>"
@@ -6870,6 +6880,9 @@ fi
if test "$vhost_user" = "yes" ; then
echo "CONFIG_VHOST_USER=y" >> $config_host_mak
fi
+if test "$vhost_user_fs" = "yes" ; then
+ echo "CONFIG_VHOST_USER_FS=y" >> $config_host_mak
+fi
if test "$blobs" = "yes" ; then
echo "INSTALL_BLOBS=yes" >> $config_host_mak
fi
diff --git a/docs/specs/acpi_hw_reduced_hotplug.rst b/docs/specs/acpi_hw_reduced_hotplug.rst
new file mode 100644
index 0000000..911a982
--- /dev/null
+++ b/docs/specs/acpi_hw_reduced_hotplug.rst
@@ -0,0 +1,70 @@
+==================================================
+QEMU and ACPI BIOS Generic Event Device interface
+==================================================
+
+The ACPI *Generic Event Device* (GED) is a HW reduced platform
+specific device introduced in ACPI v6.1 that handles all platform
+events, including the hotplug ones. GED is modelled as a device
+in the namespace with a _HID defined to be ACPI0013. This document
+describes the interface between QEMU and the ACPI BIOS.
+
+GED allows HW reduced platforms to handle interrupts in ACPI ASL
+statements. It follows a very similar approach to the _EVT method
+from GPIO events. All interrupts are listed in _CRS and the handler
+is written in _EVT method. However, the QEMU implementation uses a
+single interrupt for the GED device, relying on an IO memory region
+to communicate the type of device affected by the interrupt. This way,
+we can support up to 32 events with a unique interrupt.
+
+**Here is an example,**
+
+::
+
+ Device (\_SB.GED)
+ {
+ Name (_HID, "ACPI0013")
+ Name (_UID, Zero)
+ Name (_CRS, ResourceTemplate ()
+ {
+ Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
+ {
+ 0x00000029,
+ }
+ })
+ OperationRegion (EREG, SystemMemory, 0x09080000, 0x04)
+ Field (EREG, DWordAcc, NoLock, WriteAsZeros)
+ {
+ ESEL, 32
+ }
+ Method (_EVT, 1, Serialized)
+ {
+ Local0 = ESEL // ESEL = IO memory region which specifies the
+ // device type.
+ If (((Local0 & One) == One))
+ {
+ MethodEvent1()
+ }
+ If ((Local0 & 0x2) == 0x2)
+ {
+ MethodEvent2()
+ }
+ ...
+ }
+ }
+
+GED IO interface (4 byte access)
+--------------------------------
+**read access:**
+
+::
+
+ [0x0-0x3] Event selector bit field (32 bit) set by QEMU.
+
+ bits:
+ 0: Memory hotplug event
+ 1: System power down event
+ 2-31: Reserved
+
+**write_access:**
+
+Nothing is expected to be written into GED IO memory
diff --git a/docs/specs/index.rst b/docs/specs/index.rst
index 40adb97..984ba44 100644
--- a/docs/specs/index.rst
+++ b/docs/specs/index.rst
@@ -12,3 +12,4 @@ Contents:
ppc-xive
ppc-spapr-xive
+ acpi_hw_reduced_hotplug
diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
index 7c59cf9..12e3f1e 100644
--- a/hw/acpi/Kconfig
+++ b/hw/acpi/Kconfig
@@ -31,3 +31,7 @@ config ACPI_VMGENID
bool
default y
depends on PC
+
+config ACPI_HW_REDUCED
+ bool
+ depends on ACPI
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 9bb2101..655a9c1 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
+common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
common-obj-y += acpi_interface.o
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
new file mode 100644
index 0000000..9cee90c
--- /dev/null
+++ b/hw/acpi/generic_event_device.c
@@ -0,0 +1,311 @@
+/*
+ *
+ * Copyright (c) 2018 Intel Corporation
+ * Copyright (c) 2019 Huawei Technologies R & D (UK) Ltd
+ * Written by Samuel Ortiz, Shameer Kolothum
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "exec/address-spaces.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/generic_event_device.h"
+#include "hw/irq.h"
+#include "hw/mem/pc-dimm.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qemu/error-report.h"
+
+static const uint32_t ged_supported_events[] = {
+ ACPI_GED_MEM_HOTPLUG_EVT,
+ ACPI_GED_PWR_DOWN_EVT,
+};
+
+/*
+ * The ACPI Generic Event Device (GED) is a hardware-reduced specific
+ * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
+ * including the hotplug ones. Platforms need to specify their own
+ * GED Event bitmap to describe what kind of events they want to support
+ * through GED. This routine uses a single interrupt for the GED device,
+ * relying on IO memory region to communicate the type of device
+ * affected by the interrupt. This way, we can support up to 32 events
+ * with a unique interrupt.
+ */
+void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
+ uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base)
+{
+ AcpiGedState *s = ACPI_GED(hotplug_dev);
+ Aml *crs = aml_resource_template();
+ Aml *evt, *field;
+ Aml *dev = aml_device("%s", name);
+ Aml *evt_sel = aml_local(0);
+ Aml *esel = aml_name(AML_GED_EVT_SEL);
+
+ /* _CRS interrupt */
+ aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, &ged_irq, 1));
+
+ aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
+ aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ /* Append IO region */
+ aml_append(dev, aml_operation_region(AML_GED_EVT_REG, rs,
+ aml_int(ged_base + ACPI_GED_EVT_SEL_OFFSET),
+ ACPI_GED_EVT_SEL_LEN));
+ field = aml_field(AML_GED_EVT_REG, AML_DWORD_ACC, AML_NOLOCK,
+ AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_named_field(AML_GED_EVT_SEL,
+ ACPI_GED_EVT_SEL_LEN * BITS_PER_BYTE));
+ aml_append(dev, field);
+
+ /*
+ * For each GED event we:
+ * - Add a conditional block for each event, inside a loop.
+ * - Call a method for each supported GED event type.
+ *
+ * The resulting ASL code looks like:
+ *
+ * Local0 = ESEL
+ * If ((Local0 & One) == One)
+ * {
+ * MethodEvent0()
+ * }
+ *
+ * If ((Local0 & 0x2) == 0x2)
+ * {
+ * MethodEvent1()
+ * }
+ * ...
+ */
+ evt = aml_method("_EVT", 1, AML_SERIALIZED);
+ {
+ Aml *if_ctx;
+ uint32_t i;
+ uint32_t ged_events = ctpop32(s->ged_event_bitmap);
+
+ /* Local0 = ESEL */
+ aml_append(evt, aml_store(esel, evt_sel));
+
+ for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) {
+ uint32_t event = s->ged_event_bitmap & ged_supported_events[i];
+
+ if (!event) {
+ continue;
+ }
+
+ if_ctx = aml_if(aml_equal(aml_and(evt_sel, aml_int(event), NULL),
+ aml_int(event)));
+ switch (event) {
+ case ACPI_GED_MEM_HOTPLUG_EVT:
+ aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "."
+ MEMORY_SLOT_SCAN_METHOD));
+ break;
+ case ACPI_GED_PWR_DOWN_EVT:
+ aml_append(if_ctx,
+ aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
+ aml_int(0x80)));
+ break;
+ default:
+ /*
+ * Please make sure all the events in ged_supported_events[]
+ * are handled above.
+ */
+ g_assert_not_reached();
+ }
+
+ aml_append(evt, if_ctx);
+ ged_events--;
+ }
+
+ if (ged_events) {
+ error_report("Unsupported events specified");
+ abort();
+ }
+ }
+
+ /* Append _EVT method */
+ aml_append(dev, evt);
+
+ aml_append(table, dev);
+}
+
+/* Memory read by the GED _EVT AML dynamic method */
+static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
+{
+ uint64_t val = 0;
+ GEDState *ged_st = opaque;
+
+ switch (addr) {
+ case ACPI_GED_EVT_SEL_OFFSET:
+ /* Read the selector value and reset it */
+ val = ged_st->sel;
+ ged_st->sel = 0;
+ break;
+ default:
+ break;
+ }
+
+ return val;
+}
+
+/* Nothing is expected to be written to the GED memory region */
+static void ged_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
+{
+}
+
+static const MemoryRegionOps ged_ops = {
+ .read = ged_read,
+ .write = ged_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ AcpiGedState *s = ACPI_GED(hotplug_dev);
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp);
+ } else {
+ error_setg(errp, "virt: device plug request for unsupported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
+}
+
+static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
+{
+ AcpiGedState *s = ACPI_GED(adev);
+ GEDState *ged_st = &s->ged_state;
+ uint32_t sel;
+
+ if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
+ sel = ACPI_GED_MEM_HOTPLUG_EVT;
+ } else if (ev & ACPI_POWER_DOWN_STATUS) {
+ sel = ACPI_GED_PWR_DOWN_EVT;
+ } else {
+ /* Unknown event. Return without generating interrupt. */
+ warn_report("GED: Unsupported event %d. No irq injected", ev);
+ return;
+ }
+
+ /*
+ * Set the GED selector field to communicate the event type.
+ * This will be read by GED aml code to select the appropriate
+ * event method.
+ */
+ ged_st->sel |= sel;
+
+ /* Trigger the event by sending an interrupt to the guest. */
+ qemu_irq_pulse(s->irq);
+}
+
+static Property acpi_ged_properties[] = {
+ DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_memhp_state = {
+ .name = "acpi-ged/memhp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_MEMORY_HOTPLUG(memhp_state, AcpiGedState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_ged_state = {
+ .name = "acpi-ged-state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(sel, GEDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_acpi_ged = {
+ .name = "acpi-ged",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(ged_state, AcpiGedState, 1, vmstate_ged_state, GEDState),
+ VMSTATE_END_OF_LIST(),
+ },
+ .subsections = (const VMStateDescription * []) {
+ &vmstate_memhp_state,
+ NULL
+ }
+};
+
+static void acpi_ged_initfn(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ AcpiGedState *s = ACPI_GED(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ GEDState *ged_st = &s->ged_state;
+
+ memory_region_init_io(&ged_st->io, obj, &ged_ops, ged_st,
+ TYPE_ACPI_GED, ACPI_GED_EVT_SEL_LEN);
+ sysbus_init_mmio(sbd, &ged_st->io);
+
+ sysbus_init_irq(sbd, &s->irq);
+
+ s->memhp_state.is_enabled = true;
+ /*
+ * GED handles memory hotplug event and acpi-mem-hotplug
+ * memory region gets initialized here. Create an exclusive
+ * container for memory hotplug IO and expose it as GED sysbus
+ * MMIO so that boards can map it separately.
+ */
+ memory_region_init(&s->container_memhp, OBJECT(dev), "memhp container",
+ MEMORY_HOTPLUG_IO_LEN);
+ sysbus_init_mmio(sbd, &s->container_memhp);
+ acpi_memory_hotplug_init(&s->container_memhp, OBJECT(dev),
+ &s->memhp_state, 0);
+}
+
+static void acpi_ged_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
+ AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
+
+ dc->desc = "ACPI Generic Event Device";
+ dc->props = acpi_ged_properties;
+ dc->vmsd = &vmstate_acpi_ged;
+
+ hc->plug = acpi_ged_device_plug_cb;
+
+ adevc->send_event = acpi_ged_send_event;
+}
+
+static const TypeInfo acpi_ged_info = {
+ .name = TYPE_ACPI_GED,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AcpiGedState),
+ .instance_init = acpi_ged_initfn,
+ .class_init = acpi_ged_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { TYPE_ACPI_DEVICE_IF },
+ { }
+ }
+};
+
+static void acpi_ged_register_types(void)
+{
+ type_register_static(&acpi_ged_info);
+}
+
+type_init(acpi_ged_register_types)
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index 9483d66..8d2e822 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -30,12 +30,7 @@
#define MEMORY_SLOT_PROXIMITY_METHOD "MPXM"
#define MEMORY_SLOT_EJECT_METHOD "MEJ0"
#define MEMORY_SLOT_NOTIFY_METHOD "MTFY"
-#define MEMORY_SLOT_SCAN_METHOD "MSCN"
#define MEMORY_HOTPLUG_DEVICE "MHPD"
-#define MEMORY_HOTPLUG_IO_LEN 24
-#define MEMORY_DEVICES_CONTAINER "\\_SB.MHPC"
-
-static uint16_t memhp_io_base;
static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev)
{
@@ -210,7 +205,7 @@ static const MemoryRegionOps acpi_memory_hotplug_ops = {
};
void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
- MemHotplugState *state, uint16_t io_base)
+ MemHotplugState *state, hwaddr io_base)
{
MachineState *machine = MACHINE(qdev_get_machine());
@@ -219,12 +214,10 @@ void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
return;
}
- assert(!memhp_io_base);
- memhp_io_base = io_base;
state->devs = g_malloc0(sizeof(*state->devs) * state->dev_count);
memory_region_init_io(&state->io, owner, &acpi_memory_hotplug_ops, state,
"acpi-mem-hotplug", MEMORY_HOTPLUG_IO_LEN);
- memory_region_add_subregion(as, memhp_io_base, &state->io);
+ memory_region_add_subregion(as, io_base, &state->io);
}
/**
@@ -343,7 +336,8 @@ const VMStateDescription vmstate_memory_hotplug = {
void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
const char *res_root,
- const char *event_handler_method)
+ const char *event_handler_method,
+ AmlRegionSpace rs, hwaddr memhp_io_base)
{
int i;
Aml *ifctx;
@@ -352,10 +346,6 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
Aml *mem_ctrl_dev;
char *mhp_res_path;
- if (!memhp_io_base) {
- return;
- }
-
mhp_res_path = g_strdup_printf("%s." MEMORY_HOTPLUG_DEVICE, res_root);
mem_ctrl_dev = aml_device("%s", mhp_res_path);
{
@@ -366,14 +356,19 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
aml_name_decl("_UID", aml_string("Memory hotplug resources")));
crs = aml_resource_template();
- aml_append(crs,
- aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0,
- MEMORY_HOTPLUG_IO_LEN)
- );
+ if (rs == AML_SYSTEM_IO) {
+ aml_append(crs,
+ aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0,
+ MEMORY_HOTPLUG_IO_LEN)
+ );
+ } else {
+ aml_append(crs, aml_memory32_fixed(memhp_io_base,
+ MEMORY_HOTPLUG_IO_LEN, AML_READ_WRITE));
+ }
aml_append(mem_ctrl_dev, aml_name_decl("_CRS", crs));
aml_append(mem_ctrl_dev, aml_operation_region(
- MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO,
+ MEMORY_HOTPLUG_IO_REGION, rs,
aml_int(memhp_io_base), MEMORY_HOTPLUG_IO_LEN)
);
@@ -717,10 +712,12 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
}
aml_append(table, dev_container);
- method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
- aml_append(method,
- aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD));
- aml_append(table, method);
+ if (event_handler_method) {
+ method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_call0(MEMORY_DEVICES_CONTAINER "."
+ MEMORY_SLOT_SCAN_METHOD));
+ aml_append(table, method);
+ }
g_free(mhp_res_path);
}
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 76a2a6b..c6e7782 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -20,6 +20,10 @@ config ARM_VIRT
select SMBIOS
select VIRTIO_MMIO
select ACPI_PCI
+ select MEM_DEVICE
+ select DIMM
+ select ACPI_MEMORY_HOTPLUG
+ select ACPI_HW_REDUCED
config CHEETAH
bool
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 6cdf156..4cd5017 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -39,6 +39,8 @@
#include "hw/acpi/aml-build.h"
#include "hw/acpi/utils.h"
#include "hw/acpi/pci.h"
+#include "hw/acpi/memory_hotplug.h"
+#include "hw/acpi/generic_event_device.h"
#include "hw/pci/pcie_host.h"
#include "hw/pci/pci.h"
#include "hw/arm/virt.h"
@@ -48,7 +50,6 @@
#include "migration/vmstate.h"
#define ARM_SPI_BASE 32
-#define ACPI_POWER_BUTTON_DEVICE "PWRB"
static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
{
@@ -544,6 +545,14 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
}
}
+ if (ms->device_memory) {
+ numamem = acpi_data_push(table_data, sizeof *numamem);
+ build_srat_memory(numamem, ms->device_memory->base,
+ memory_region_size(&ms->device_memory->mr),
+ ms->numa_state->num_nodes - 1,
+ MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
+ }
+
build_header(linker, table_data, (void *)(table_data->data + srat_start),
"SRAT", table_data->len - srat_start, 3, NULL, NULL);
}
@@ -708,6 +717,7 @@ static void
build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
{
Aml *scope, *dsdt;
+ MachineState *ms = MACHINE(vms);
const MemMapEntry *memmap = vms->memmap;
const int *irqmap = vms->irqmap;
@@ -730,8 +740,27 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
vms->highmem, vms->highmem_ecam);
- acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
- (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
+ if (vms->acpi_dev) {
+ build_ged_aml(scope, "\\_SB."GED_DEVICE,
+ HOTPLUG_HANDLER(vms->acpi_dev),
+ irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE, AML_SYSTEM_MEMORY,
+ memmap[VIRT_ACPI_GED].base);
+ } else {
+ acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
+ (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
+ }
+
+ if (vms->acpi_dev) {
+ uint32_t event = object_property_get_uint(OBJECT(vms->acpi_dev),
+ "ged-event", &error_abort);
+
+ if (event & ACPI_GED_MEM_HOTPLUG_EVT) {
+ build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL,
+ AML_SYSTEM_MEMORY,
+ memmap[VIRT_PCDIMM_ACPI].base);
+ }
+ }
+
acpi_dsdt_add_power_button(scope);
aml_append(dsdt, scope);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index d74538b..d4bedc2 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -68,6 +68,9 @@
#include "hw/arm/smmuv3.h"
#include "hw/acpi/acpi.h"
#include "target/arm/internals.h"
+#include "hw/mem/pc-dimm.h"
+#include "hw/mem/nvdimm.h"
+#include "hw/acpi/generic_event_device.h"
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -138,6 +141,8 @@ static const MemMapEntry base_memmap[] = {
[VIRT_GPIO] = { 0x09030000, 0x00001000 },
[VIRT_SECURE_UART] = { 0x09040000, 0x00001000 },
[VIRT_SMMU] = { 0x09050000, 0x00020000 },
+ [VIRT_PCDIMM_ACPI] = { 0x09070000, MEMORY_HOTPLUG_IO_LEN },
+ [VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
@@ -173,6 +178,7 @@ static const int a15irqmap[] = {
[VIRT_PCIE] = 3, /* ... to 6 */
[VIRT_GPIO] = 7,
[VIRT_SECURE_UART] = 8,
+ [VIRT_ACPI_GED] = 9,
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
[VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
[VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */
@@ -525,6 +531,29 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms)
}
}
+static inline DeviceState *create_acpi_ged(VirtMachineState *vms, qemu_irq *pic)
+{
+ DeviceState *dev;
+ MachineState *ms = MACHINE(vms);
+ int irq = vms->irqmap[VIRT_ACPI_GED];
+ uint32_t event = ACPI_GED_PWR_DOWN_EVT;
+
+ if (ms->ram_slots) {
+ event |= ACPI_GED_MEM_HOTPLUG_EVT;
+ }
+
+ dev = qdev_create(NULL, TYPE_ACPI_GED);
+ qdev_prop_set_uint32(dev, "ged-event", event);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irq]);
+
+ qdev_init_nofail(dev);
+
+ return dev;
+}
+
static void create_its(VirtMachineState *vms, DeviceState *gicdev)
{
const char *itsclass = its_class_name();
@@ -764,13 +793,15 @@ static void create_rtc(const VirtMachineState *vms, qemu_irq *pic)
static DeviceState *gpio_key_dev;
static void virt_powerdown_req(Notifier *n, void *opaque)
{
- /* use gpio Pin 3 for power button event */
- qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1);
-}
+ VirtMachineState *s = container_of(n, VirtMachineState, powerdown_notifier);
-static Notifier virt_system_powerdown_notifier = {
- .notify = virt_powerdown_req
-};
+ if (s->acpi_dev) {
+ acpi_send_event(s->acpi_dev, ACPI_POWER_DOWN_STATUS);
+ } else {
+ /* use gpio Pin 3 for power button event */
+ qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1);
+ }
+}
static void create_gpio(const VirtMachineState *vms, qemu_irq *pic)
{
@@ -812,10 +843,6 @@ static void create_gpio(const VirtMachineState *vms, qemu_irq *pic)
KEY_POWER);
qemu_fdt_setprop_cells(vms->fdt, "/gpio-keys/poweroff",
"gpios", phandle, 3, 0);
-
- /* connect powerdown request */
- qemu_register_powerdown_notifier(&virt_system_powerdown_notifier);
-
g_free(nodename);
}
@@ -1489,6 +1516,7 @@ static void machvirt_init(MachineState *machine)
MemoryRegion *ram = g_new(MemoryRegion, 1);
bool firmware_loaded;
bool aarch64 = true;
+ bool has_ged = !vmc->no_ged;
unsigned int smp_cpus = machine->smp.cpus;
unsigned int max_cpus = machine->smp.max_cpus;
@@ -1701,7 +1729,15 @@ static void machvirt_init(MachineState *machine)
create_pcie(vms, pic);
- create_gpio(vms, pic);
+ if (has_ged && aarch64 && firmware_loaded && acpi_enabled) {
+ vms->acpi_dev = create_acpi_ged(vms, pic);
+ } else {
+ create_gpio(vms, pic);
+ }
+
+ /* connect powerdown request */
+ vms->powerdown_notifier.notify = virt_powerdown_req;
+ qemu_register_powerdown_notifier(&vms->powerdown_notifier);
/* Create mmio transports, so the user can create virtio backends
* (which will be automatically plugged in to the transports). If
@@ -1876,6 +1912,52 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
return ms->possible_cpus;
}
+static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+ const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
+
+ if (is_nvdimm) {
+ error_setg(errp, "nvdimm is not yet supported");
+ return;
+ }
+
+ if (!vms->acpi_dev) {
+ error_setg(errp,
+ "memory hotplug is not enabled: missing acpi-ged device");
+ return;
+ }
+
+ pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
+}
+
+static void virt_memory_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ HotplugHandlerClass *hhc;
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+ Error *local_err = NULL;
+
+ pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
+ hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &error_abort);
+out:
+ error_propagate(errp, local_err);
+}
+
+static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ virt_memory_pre_plug(hotplug_dev, dev, errp);
+ }
+}
+
static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@@ -1887,12 +1969,23 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
SYS_BUS_DEVICE(dev));
}
}
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ virt_memory_plug(hotplug_dev, dev, errp);
+ }
+}
+
+static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ error_setg(errp, "device unplug request for unsupported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
}
static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
- if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+ if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE) ||
+ (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
return HOTPLUG_HANDLER(machine);
}
@@ -1956,8 +2049,11 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
mc->kvm_type = virt_kvm_type;
assert(!mc->get_hotplug_handler);
mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
+ hc->pre_plug = virt_machine_device_pre_plug_cb;
hc->plug = virt_machine_device_plug_cb;
+ hc->unplug_request = virt_machine_device_unplug_request_cb;
mc->numa_mem_supported = true;
+ mc->auto_enable_numa_with_memhp = true;
}
static void virt_instance_init(Object *obj)
@@ -2058,8 +2154,12 @@ DEFINE_VIRT_MACHINE_AS_LATEST(4, 2)
static void virt_machine_4_1_options(MachineClass *mc)
{
+ VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc));
+
virt_machine_4_2_options(mc);
compat_props_add(mc->compat_props, hw_compat_4_1, hw_compat_4_1_len);
+ vmc->no_ged = true;
+ mc->auto_enable_numa_with_memhp = false;
}
DEFINE_VIRT_MACHINE(4, 1)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 4e0f9f4..1d077a7 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -1888,7 +1888,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base,
"\\_SB.PCI0", "\\_GPE._E02");
}
- build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0", "\\_GPE._E03");
+
+ if (pcms->memhp_io_base && nr_mem) {
+ build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0",
+ "\\_GPE._E03", AML_SYSTEM_IO,
+ pcms->memhp_io_base);
+ }
scope = aml_scope("_GPE");
{
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index bcda50e..4b19042 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1766,6 +1766,9 @@ void pc_memory_init(PCMachineState *pcms,
/* Init default IOAPIC address space */
pcms->ioapic_as = &address_space_memory;
+
+ /* Init ACPI memory hotplug IO base address */
+ pcms->memhp_io_base = ACPI_MEMORY_HOTPLUG_BASE;
}
/*
diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs
index 964ce78..e2f70fb 100644
--- a/hw/virtio/Makefile.objs
+++ b/hw/virtio/Makefile.objs
@@ -11,9 +11,11 @@ common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
common-obj-$(CONFIG_VIRTIO_MMIO) += virtio-mmio.o
obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon.o
obj-$(CONFIG_VIRTIO_CRYPTO) += virtio-crypto.o
+obj-$(CONFIG_VHOST_USER_FS) += vhost-user-fs.o
obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-pci.o
obj-$(CONFIG_VIRTIO_PMEM) += virtio-pmem.o
common-obj-$(call land,$(CONFIG_VIRTIO_PMEM),$(CONFIG_VIRTIO_PCI)) += virtio-pmem-pci.o
+obj-$(call land,$(CONFIG_VHOST_USER_FS),$(CONFIG_VIRTIO_PCI)) += vhost-user-fs-pci.o
obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o
ifeq ($(CONFIG_VIRTIO_PCI),y)
diff --git a/hw/virtio/vhost-user-fs-pci.c b/hw/virtio/vhost-user-fs-pci.c
new file mode 100644
index 0000000..933a3f2
--- /dev/null
+++ b/hw/virtio/vhost-user-fs-pci.c
@@ -0,0 +1,85 @@
+/*
+ * Vhost-user filesystem virtio device PCI glue
+ *
+ * Copyright 2018-2019 Red Hat, Inc.
+ *
+ * Authors:
+ * Dr. David Alan Gilbert <dgilbert@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-properties.h"
+#include "hw/virtio/vhost-user-fs.h"
+#include "virtio-pci.h"
+
+struct VHostUserFSPCI {
+ VirtIOPCIProxy parent_obj;
+ VHostUserFS vdev;
+};
+
+typedef struct VHostUserFSPCI VHostUserFSPCI;
+
+#define TYPE_VHOST_USER_FS_PCI "vhost-user-fs-pci-base"
+
+#define VHOST_USER_FS_PCI(obj) \
+ OBJECT_CHECK(VHostUserFSPCI, (obj), TYPE_VHOST_USER_FS_PCI)
+
+static Property vhost_user_fs_pci_properties[] = {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vhost_user_fs_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VHostUserFSPCI *dev = VHOST_USER_FS_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = dev->vdev.conf.num_request_queues + 1;
+ }
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void vhost_user_fs_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+ k->realize = vhost_user_fs_pci_realize;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ dc->props = vhost_user_fs_pci_properties;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */
+ pcidev_k->revision = 0x00;
+ pcidev_k->class_id = PCI_CLASS_STORAGE_OTHER;
+}
+
+static void vhost_user_fs_pci_instance_init(Object *obj)
+{
+ VHostUserFSPCI *dev = VHOST_USER_FS_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VHOST_USER_FS);
+}
+
+static const VirtioPCIDeviceTypeInfo vhost_user_fs_pci_info = {
+ .base_name = TYPE_VHOST_USER_FS_PCI,
+ .non_transitional_name = "vhost-user-fs-pci",
+ .instance_size = sizeof(VHostUserFSPCI),
+ .instance_init = vhost_user_fs_pci_instance_init,
+ .class_init = vhost_user_fs_pci_class_init,
+};
+
+static void vhost_user_fs_pci_register(void)
+{
+ virtio_pci_types_register(&vhost_user_fs_pci_info);
+}
+
+type_init(vhost_user_fs_pci_register);
diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c
new file mode 100644
index 0000000..f0df7f4
--- /dev/null
+++ b/hw/virtio/vhost-user-fs.c
@@ -0,0 +1,299 @@
+/*
+ * Vhost-user filesystem virtio device
+ *
+ * Copyright 2018-2019 Red Hat, Inc.
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include <sys/ioctl.h>
+#include "standard-headers/linux/virtio_fs.h"
+#include "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-access.h"
+#include "qemu/error-report.h"
+#include "hw/virtio/vhost-user-fs.h"
+#include "monitor/monitor.h"
+
+static void vuf_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+ struct virtio_fs_config fscfg = {};
+
+ memcpy((char *)fscfg.tag, fs->conf.tag,
+ MIN(strlen(fs->conf.tag) + 1, sizeof(fscfg.tag)));
+
+ virtio_stl_p(vdev, &fscfg.num_request_queues, fs->conf.num_request_queues);
+
+ memcpy(config, &fscfg, sizeof(fscfg));
+}
+
+static void vuf_start(VirtIODevice *vdev)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ int ret;
+ int i;
+
+ if (!k->set_guest_notifiers) {
+ error_report("binding does not support guest notifiers");
+ return;
+ }
+
+ ret = vhost_dev_enable_notifiers(&fs->vhost_dev, vdev);
+ if (ret < 0) {
+ error_report("Error enabling host notifiers: %d", -ret);
+ return;
+ }
+
+ ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, true);
+ if (ret < 0) {
+ error_report("Error binding guest notifier: %d", -ret);
+ goto err_host_notifiers;
+ }
+
+ fs->vhost_dev.acked_features = vdev->guest_features;
+ ret = vhost_dev_start(&fs->vhost_dev, vdev);
+ if (ret < 0) {
+ error_report("Error starting vhost: %d", -ret);
+ goto err_guest_notifiers;
+ }
+
+ /*
+ * guest_notifier_mask/pending not used yet, so just unmask
+ * everything here. virtio-pci will do the right thing by
+ * enabling/disabling irqfd.
+ */
+ for (i = 0; i < fs->vhost_dev.nvqs; i++) {
+ vhost_virtqueue_mask(&fs->vhost_dev, vdev, i, false);
+ }
+
+ return;
+
+err_guest_notifiers:
+ k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false);
+err_host_notifiers:
+ vhost_dev_disable_notifiers(&fs->vhost_dev, vdev);
+}
+
+static void vuf_stop(VirtIODevice *vdev)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ int ret;
+
+ if (!k->set_guest_notifiers) {
+ return;
+ }
+
+ vhost_dev_stop(&fs->vhost_dev, vdev);
+
+ ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false);
+ if (ret < 0) {
+ error_report("vhost guest notifier cleanup failed: %d", ret);
+ return;
+ }
+
+ vhost_dev_disable_notifiers(&fs->vhost_dev, vdev);
+}
+
+static void vuf_set_status(VirtIODevice *vdev, uint8_t status)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+ bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
+
+ if (!vdev->vm_running) {
+ should_start = false;
+ }
+
+ if (fs->vhost_dev.started == should_start) {
+ return;
+ }
+
+ if (should_start) {
+ vuf_start(vdev);
+ } else {
+ vuf_stop(vdev);
+ }
+}
+
+static uint64_t vuf_get_features(VirtIODevice *vdev,
+ uint64_t requested_features,
+ Error **errp)
+{
+ /* No feature bits used yet */
+ return requested_features;
+}
+
+static void vuf_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ /*
+ * Not normally called; it's the daemon that handles the queue;
+ * however virtio's cleanup path can call this.
+ */
+}
+
+static void vuf_guest_notifier_mask(VirtIODevice *vdev, int idx,
+ bool mask)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+
+ vhost_virtqueue_mask(&fs->vhost_dev, vdev, idx, mask);
+}
+
+static bool vuf_guest_notifier_pending(VirtIODevice *vdev, int idx)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+
+ return vhost_virtqueue_pending(&fs->vhost_dev, idx);
+}
+
+static void vuf_device_realize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostUserFS *fs = VHOST_USER_FS(dev);
+ unsigned int i;
+ size_t len;
+ int ret;
+
+ if (!fs->conf.chardev.chr) {
+ error_setg(errp, "missing chardev");
+ return;
+ }
+
+ if (!fs->conf.tag) {
+ error_setg(errp, "missing tag property");
+ return;
+ }
+ len = strlen(fs->conf.tag);
+ if (len == 0) {
+ error_setg(errp, "tag property cannot be empty");
+ return;
+ }
+ if (len > sizeof_field(struct virtio_fs_config, tag)) {
+ error_setg(errp, "tag property must be %zu bytes or less",
+ sizeof_field(struct virtio_fs_config, tag));
+ return;
+ }
+
+ if (fs->conf.num_request_queues == 0) {
+ error_setg(errp, "num-request-queues property must be larger than 0");
+ return;
+ }
+
+ if (!is_power_of_2(fs->conf.queue_size)) {
+ error_setg(errp, "queue-size property must be a power of 2");
+ return;
+ }
+
+ if (fs->conf.queue_size > VIRTQUEUE_MAX_SIZE) {
+ error_setg(errp, "queue-size property must be %u or smaller",
+ VIRTQUEUE_MAX_SIZE);
+ return;
+ }
+
+ if (!vhost_user_init(&fs->vhost_user, &fs->conf.chardev, errp)) {
+ return;
+ }
+
+ virtio_init(vdev, "vhost-user-fs", VIRTIO_ID_FS,
+ sizeof(struct virtio_fs_config));
+
+ /* Hiprio queue */
+ virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
+
+ /* Request queues */
+ for (i = 0; i < fs->conf.num_request_queues; i++) {
+ virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
+ }
+
+ /* 1 high prio queue, plus the number configured */
+ fs->vhost_dev.nvqs = 1 + fs->conf.num_request_queues;
+ fs->vhost_dev.vqs = g_new0(struct vhost_virtqueue, fs->vhost_dev.nvqs);
+ ret = vhost_dev_init(&fs->vhost_dev, &fs->vhost_user,
+ VHOST_BACKEND_TYPE_USER, 0);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "vhost_dev_init failed");
+ goto err_virtio;
+ }
+
+ return;
+
+err_virtio:
+ vhost_user_cleanup(&fs->vhost_user);
+ virtio_cleanup(vdev);
+ g_free(fs->vhost_dev.vqs);
+ return;
+}
+
+static void vuf_device_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostUserFS *fs = VHOST_USER_FS(dev);
+
+ /* This will stop vhost backend if appropriate. */
+ vuf_set_status(vdev, 0);
+
+ vhost_dev_cleanup(&fs->vhost_dev);
+
+ vhost_user_cleanup(&fs->vhost_user);
+
+ virtio_cleanup(vdev);
+ g_free(fs->vhost_dev.vqs);
+ fs->vhost_dev.vqs = NULL;
+}
+
+static const VMStateDescription vuf_vmstate = {
+ .name = "vhost-user-fs",
+ .unmigratable = 1,
+};
+
+static Property vuf_properties[] = {
+ DEFINE_PROP_CHR("chardev", VHostUserFS, conf.chardev),
+ DEFINE_PROP_STRING("tag", VHostUserFS, conf.tag),
+ DEFINE_PROP_UINT16("num-request-queues", VHostUserFS,
+ conf.num_request_queues, 1),
+ DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128),
+ DEFINE_PROP_STRING("vhostfd", VHostUserFS, conf.vhostfd),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vuf_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+ dc->props = vuf_properties;
+ dc->vmsd = &vuf_vmstate;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ vdc->realize = vuf_device_realize;
+ vdc->unrealize = vuf_device_unrealize;
+ vdc->get_features = vuf_get_features;
+ vdc->get_config = vuf_get_config;
+ vdc->set_status = vuf_set_status;
+ vdc->guest_notifier_mask = vuf_guest_notifier_mask;
+ vdc->guest_notifier_pending = vuf_guest_notifier_pending;
+}
+
+static const TypeInfo vuf_info = {
+ .name = TYPE_VHOST_USER_FS,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VHostUserFS),
+ .class_init = vuf_class_init,
+};
+
+static void vuf_register_types(void)
+{
+ type_register_static(&vuf_info);
+}
+
+type_init(vuf_register_types)
diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
index 0ba90ef..a2a12af 100644
--- a/include/hw/acpi/acpi_dev_interface.h
+++ b/include/hw/acpi/acpi_dev_interface.h
@@ -13,6 +13,7 @@ typedef enum {
ACPI_MEMORY_HOTPLUG_STATUS = 8,
ACPI_NVDIMM_HOTPLUG_STATUS = 16,
ACPI_VMGENID_CHANGE_STATUS = 32,
+ ACPI_POWER_DOWN_STATUS = 64,
} AcpiEventStatusBits;
#define TYPE_ACPI_DEVICE_IF "acpi-device-interface"
diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
new file mode 100644
index 0000000..d157eac
--- /dev/null
+++ b/include/hw/acpi/generic_event_device.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright (c) 2018 Intel Corporation
+ * Copyright (c) 2019 Huawei Technologies R & D (UK) Ltd
+ * Written by Samuel Ortiz, Shameer Kolothum
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * The ACPI Generic Event Device (GED) is a hardware-reduced specific
+ * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
+ * including the hotplug ones. Generic Event Device allows platforms
+ * to handle interrupts in ACPI ASL statements. It follows a very
+ * similar approach like the _EVT method from GPIO events. All
+ * interrupts are listed in _CRS and the handler is written in _EVT
+ * method. Here, we use a single interrupt for the GED device, relying
+ * on IO memory region to communicate the type of device affected by
+ * the interrupt. This way, we can support up to 32 events with a
+ * unique interrupt.
+ *
+ * Here is an example.
+ *
+ * Device (\_SB.GED)
+ * {
+ * Name (_HID, "ACPI0013")
+ * Name (_UID, Zero)
+ * Name (_CRS, ResourceTemplate ()
+ * {
+ * Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
+ * {
+ * 0x00000029,
+ * }
+ * })
+ * OperationRegion (EREG, SystemMemory, 0x09080000, 0x04)
+ * Field (EREG, DWordAcc, NoLock, WriteAsZeros)
+ * {
+ * ESEL, 32
+ * }
+ *
+ * Method (_EVT, 1, Serialized) // _EVT: Event
+ * {
+ * Local0 = ESEL // ESEL = IO memory region which specifies the
+ * // device type.
+ * If (((Local0 & One) == One))
+ * {
+ * MethodEvent1()
+ * }
+ * If ((Local0 & 0x2) == 0x2)
+ * {
+ * MethodEvent2()
+ * }
+ * ...
+ * }
+ * }
+ *
+ */
+
+#ifndef HW_ACPI_GED_H
+#define HW_ACPI_GED_H
+
+#include "hw/sysbus.h"
+#include "hw/acpi/memory_hotplug.h"
+
+#define ACPI_POWER_BUTTON_DEVICE "PWRB"
+
+#define TYPE_ACPI_GED "acpi-ged"
+#define ACPI_GED(obj) \
+ OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
+
+#define ACPI_GED_EVT_SEL_OFFSET 0x0
+#define ACPI_GED_EVT_SEL_LEN 0x4
+
+#define GED_DEVICE "GED"
+#define AML_GED_EVT_REG "EREG"
+#define AML_GED_EVT_SEL "ESEL"
+
+/*
+ * Platforms need to specify the GED event bitmap
+ * to describe what kind of events they want to support
+ * through GED.
+ */
+#define ACPI_GED_MEM_HOTPLUG_EVT 0x1
+#define ACPI_GED_PWR_DOWN_EVT 0x2
+
+typedef struct GEDState {
+ MemoryRegion io;
+ uint32_t sel;
+} GEDState;
+
+typedef struct AcpiGedState {
+ SysBusDevice parent_obj;
+ MemHotplugState memhp_state;
+ MemoryRegion container_memhp;
+ GEDState ged_state;
+ uint32_t ged_event_bitmap;
+ qemu_irq irq;
+} AcpiGedState;
+
+void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
+ uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base);
+
+#endif
diff --git a/include/hw/acpi/memory_hotplug.h b/include/hw/acpi/memory_hotplug.h
index 77c6576..dfe9cf3 100644
--- a/include/hw/acpi/memory_hotplug.h
+++ b/include/hw/acpi/memory_hotplug.h
@@ -5,6 +5,10 @@
#include "hw/acpi/acpi.h"
#include "hw/acpi/aml-build.h"
+#define MEMORY_SLOT_SCAN_METHOD "MSCN"
+#define MEMORY_DEVICES_CONTAINER "\\_SB.MHPC"
+#define MEMORY_HOTPLUG_IO_LEN 24
+
/**
* MemStatus:
* @is_removing: the memory device in slot has been requested to be ejected.
@@ -29,7 +33,7 @@ typedef struct MemHotplugState {
} MemHotplugState;
void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
- MemHotplugState *state, uint16_t io_base);
+ MemHotplugState *state, hwaddr io_base);
void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st,
DeviceState *dev, Error **errp);
@@ -48,5 +52,6 @@ void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list);
void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
const char *res_root,
- const char *event_handler_method);
+ const char *event_handler_method,
+ AmlRegionSpace rs, hwaddr memhp_io_base);
#endif
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index a720942..0b41083 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -77,6 +77,8 @@ enum {
VIRT_GPIO,
VIRT_SECURE_UART,
VIRT_SECURE_MEM,
+ VIRT_PCDIMM_ACPI,
+ VIRT_ACPI_GED,
VIRT_LOWMEMMAP_LAST,
};
@@ -106,6 +108,7 @@ typedef struct {
bool claim_edge_triggered_timers;
bool smbios_old_sys_ver;
bool no_highmem_ecam;
+ bool no_ged; /* Machines < 4.2 has no support for ACPI GED device */
} VirtMachineClass;
typedef struct {
@@ -133,6 +136,8 @@ typedef struct {
uint32_t iommu_phandle;
int psci_conduit;
hwaddr highest_gpa;
+ DeviceState *acpi_dev;
+ Notifier powerdown_notifier;
} VirtMachineState;
#define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 6df4f4b..37bfd95 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -70,6 +70,9 @@ struct PCMachineState {
/* Address space used by IOAPIC device. All IOAPIC interrupts
* will be translated to MSI messages in the address space. */
AddressSpace *ioapic_as;
+
+ /* ACPI Memory hotplug IO base address */
+ hwaddr memhp_io_base;
};
#define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device"
diff --git a/include/hw/virtio/vhost-user-fs.h b/include/hw/virtio/vhost-user-fs.h
new file mode 100644
index 0000000..539885b
--- /dev/null
+++ b/include/hw/virtio/vhost-user-fs.h
@@ -0,0 +1,45 @@
+/*
+ * Vhost-user filesystem virtio device
+ *
+ * Copyright 2018-2019 Red Hat, Inc.
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#ifndef _QEMU_VHOST_USER_FS_H
+#define _QEMU_VHOST_USER_FS_H
+
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/vhost.h"
+#include "hw/virtio/vhost-user.h"
+#include "chardev/char-fe.h"
+
+#define TYPE_VHOST_USER_FS "vhost-user-fs-device"
+#define VHOST_USER_FS(obj) \
+ OBJECT_CHECK(VHostUserFS, (obj), TYPE_VHOST_USER_FS)
+
+typedef struct {
+ CharBackend chardev;
+ char *tag;
+ uint16_t num_request_queues;
+ uint16_t queue_size;
+ char *vhostfd;
+} VHostUserFSConf;
+
+typedef struct {
+ /*< private >*/
+ VirtIODevice parent;
+ VHostUserFSConf conf;
+ struct vhost_virtqueue *vhost_vqs;
+ struct vhost_dev vhost_dev;
+ VhostUserState vhost_user;
+
+ /*< public >*/
+} VHostUserFS;
+
+#endif /* _QEMU_VHOST_USER_FS_H */
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 51921de..014199d 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -235,6 +235,10 @@ static void chr_closed_bh(void *opaque)
s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
+ if (s->vhost_net) {
+ s->acked_features = vhost_net_get_acked_features(s->vhost_net);
+ }
+
qmp_set_link(name, false, &err);
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event,
diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c
index 9b3d8b0..0b33fb2 100644
--- a/tests/bios-tables-test.c
+++ b/tests/bios-tables-test.c
@@ -10,6 +10,33 @@
* See the COPYING file in the top-level directory.
*/
+/*
+ * How to add or update the tests:
+ * Contributor:
+ * 1. add empty files for new tables, if any, under tests/data/acpi
+ * 2. list any changed files in tests/bios-tables-test-allowed-diff.h
+ * 3. commit the above *before* making changes that affect the tables
+ * Maintainer:
+ * After 1-3 above tests will pass but ignore differences with the expected files.
+ * You will also notice that tests/bios-tables-test-allowed-diff.h lists
+ * a bunch of files. This is your hint that you need to do the below:
+ * 4. Run
+ * make check V=1
+ * this will produce a bunch of warnings about differences
+ * beween actual and expected ACPI tables. If you have IASL installed,
+ * they will also be disassembled so you can look at the disassembled
+ * output. If not - disassemble them yourself in any way you like.
+ * Look at the differences - make sure they make sense and match what the
+ * changes you are merging are supposed to do.
+ *
+ * 5. From build directory, run:
+ * $(SRC_PATH)/tests/data/acpi/rebuild-expected-aml.sh
+ * 6. Now commit any changes.
+ * 7. Before doing a pull request, make sure tests/bios-tables-test-allowed-diff.h
+ * is empty - this will ensure following changes to ACPI tables will
+ * be noticed.
+ */
+
#include "qemu/osdep.h"
#include <glib/gstdio.h>
#include "qemu-common.h"
@@ -334,7 +361,10 @@ try_again:
g_assert(ret);
g_assert_no_error(error);
g_assert(exp_sdt.aml);
- g_assert(exp_sdt.aml_len);
+ if (!exp_sdt.aml_len) {
+ fprintf(stderr, "Warning! zero length expected file '%s'\n",
+ aml_file);
+ }
g_array_append_val(exp_tables, exp_sdt);
}
@@ -870,6 +900,53 @@ static void test_acpi_piix4_tcg_dimm_pxm(void)
test_acpi_tcg_dimm_pxm(MACHINE_PC);
}
+static void test_acpi_virt_tcg_memhp(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .accel = "tcg",
+ .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
+ .ram_start = 0x40000000ULL,
+ .scan_len = 256ULL * 1024 * 1024,
+ };
+
+ data.variant = ".memhp";
+ test_acpi_one(" -cpu cortex-a57"
+ " -m 256M,slots=3,maxmem=1G"
+ " -object memory-backend-ram,id=ram0,size=128M"
+ " -object memory-backend-ram,id=ram1,size=128M"
+ " -numa node,memdev=ram0 -numa node,memdev=ram1"
+ " -numa dist,src=0,dst=1,val=21",
+ &data);
+
+ free_test_data(&data);
+
+}
+
+static void test_acpi_virt_tcg_numamem(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .accel = "tcg",
+ .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
+ .ram_start = 0x40000000ULL,
+ .scan_len = 128ULL * 1024 * 1024,
+ };
+
+ data.variant = ".numamem";
+ test_acpi_one(" -cpu cortex-a57"
+ " -object memory-backend-ram,id=ram0,size=128M"
+ " -numa node,memdev=ram0",
+ &data);
+
+ free_test_data(&data);
+
+}
+
static void test_acpi_virt_tcg(void)
{
test_data data = {
@@ -916,6 +993,8 @@ int main(int argc, char *argv[])
qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm);
} else if (strcmp(arch, "aarch64") == 0) {
qtest_add_func("acpi/virt", test_acpi_virt_tcg);
+ qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem);
+ qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp);
}
ret = g_test_run();
boot_sector_cleanup(disk);
diff --git a/tests/data/acpi/virt/APIC.memhp b/tests/data/acpi/virt/APIC.memhp
new file mode 100644
index 0000000..797dfde
--- /dev/null
+++ b/tests/data/acpi/virt/APIC.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/APIC.numamem b/tests/data/acpi/virt/APIC.numamem
new file mode 100644
index 0000000..797dfde
--- /dev/null
+++ b/tests/data/acpi/virt/APIC.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/DSDT b/tests/data/acpi/virt/DSDT
index 20e85c7..bce76e3 100644
--- a/tests/data/acpi/virt/DSDT
+++ b/tests/data/acpi/virt/DSDT
Binary files differ
diff --git a/tests/data/acpi/virt/DSDT.memhp b/tests/data/acpi/virt/DSDT.memhp
new file mode 100644
index 0000000..b4b153f
--- /dev/null
+++ b/tests/data/acpi/virt/DSDT.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/DSDT.numamem b/tests/data/acpi/virt/DSDT.numamem
new file mode 100644
index 0000000..bce76e3
--- /dev/null
+++ b/tests/data/acpi/virt/DSDT.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/FACP.memhp b/tests/data/acpi/virt/FACP.memhp
new file mode 100644
index 0000000..27de99f
--- /dev/null
+++ b/tests/data/acpi/virt/FACP.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/FACP.numamem b/tests/data/acpi/virt/FACP.numamem
new file mode 100644
index 0000000..27de99f
--- /dev/null
+++ b/tests/data/acpi/virt/FACP.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/GTDT.memhp b/tests/data/acpi/virt/GTDT.memhp
new file mode 100644
index 0000000..10107a6
--- /dev/null
+++ b/tests/data/acpi/virt/GTDT.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/GTDT.numamem b/tests/data/acpi/virt/GTDT.numamem
new file mode 100644
index 0000000..10107a6
--- /dev/null
+++ b/tests/data/acpi/virt/GTDT.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/MCFG.memhp b/tests/data/acpi/virt/MCFG.memhp
new file mode 100644
index 0000000..e8987e1
--- /dev/null
+++ b/tests/data/acpi/virt/MCFG.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/MCFG.numamem b/tests/data/acpi/virt/MCFG.numamem
new file mode 100644
index 0000000..e8987e1
--- /dev/null
+++ b/tests/data/acpi/virt/MCFG.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/SLIT.memhp b/tests/data/acpi/virt/SLIT.memhp
new file mode 100644
index 0000000..74ec3b4
--- /dev/null
+++ b/tests/data/acpi/virt/SLIT.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/SPCR.memhp b/tests/data/acpi/virt/SPCR.memhp
new file mode 100644
index 0000000..377271a
--- /dev/null
+++ b/tests/data/acpi/virt/SPCR.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/SPCR.numamem b/tests/data/acpi/virt/SPCR.numamem
new file mode 100644
index 0000000..377271a
--- /dev/null
+++ b/tests/data/acpi/virt/SPCR.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/SRAT.memhp b/tests/data/acpi/virt/SRAT.memhp
new file mode 100644
index 0000000..1b57db2
--- /dev/null
+++ b/tests/data/acpi/virt/SRAT.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/SRAT.numamem b/tests/data/acpi/virt/SRAT.numamem
new file mode 100644
index 0000000..9526e5a
--- /dev/null
+++ b/tests/data/acpi/virt/SRAT.numamem
Binary files differ