@@ -75,12 +75,14 @@ static uint64_t acpi_memory_hotplug_read(void *opaque, hwaddr addr,
case 0x14: /* pack and return is_* fields */
val |= mdev->is_enabled ? 1 : 0;
val |= mdev->is_inserting ? 2 : 0;
+ val |= mdev->is_removing ? 4 : 0;
trace_mhp_acpi_read_flags(mem_st->selector, val);
break;
default:
val = ~0;
break;
}
+
return val;
}
@@ -126,17 +128,57 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data,
info = acpi_memory_device_status(mem_st->selector, mdev);
qapi_event_send_acpi_device_ost(info, &error_abort);
qapi_free_ACPIOSTInfo(info);
+ switch (mdev->ost_event) {
+ case 0x03: /* EJECT */
+ switch (mdev->ost_status) {
+ case 0x0: /* SUCCESS */
+ object_unparent(OBJECT(mdev->dimm));
+ mdev->is_removing = false;
+ mdev->dimm = NULL;
+ break;
+ case 0x1: /* FAILURE */
+ case 0x2: /* UNRECOGNIZED NOTIFY */
+ case 0x80: /* EJECT NOT SUPPORTED */
+ case 0x81: /* DEVICE IN USE */
+ case 0x82: /* DEVICE BUSY */
+ case 0x83: /* EJECT_DEPENDENCY_BUSY */
+ mdev->is_removing = false;
+ mdev->is_enabled = true;
+ break;
+ case 0x84: /* EJECTION IN PROGRESS */
+ break;
+ default:
+ break;
+ }
+ break;
+ case 0x103: /* OSPM EJECT */
+ switch (mdev->ost_status) {
+ case 0x0: /* SUCCESS */
+ object_unparent(OBJECT(mdev->dimm));
+ mdev->is_removing = false;
+ mdev->dimm = NULL;
+ break;
+ case 0x84: /* EJECTION IN PROGRESS */
+ mdev->is_enabled = false;
+ mdev->is_removing = true;
+ break;
+ default:
+ break;
+ }
+ }
break;
case 0x14:
mdev = &mem_st->devs[mem_st->selector];
if (data & 2) { /* clear insert event */
mdev->is_inserting = false;
trace_mhp_acpi_clear_insert_evt(mem_st->selector);
+ } else if (data & 4) { /* MRMV */
+ mdev->is_enabled = false;
}
break;
}
-
}
+
static const MemoryRegionOps acpi_memory_hotplug_ops = {
.read = acpi_memory_hotplug_read,
.write = acpi_memory_hotplug_write,
@@ -195,6 +237,36 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
return;
}
+void acpi_memory_unplug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
+ DeviceState *dev, Error **errp)
+{
+ MemStatus *mdev;
+ Error *local_err = NULL;
+ int slot = object_property_get_int(OBJECT(dev), "slot", &local_err);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (slot >= mem_st->dev_count) {
+ char *dev_path = object_get_canonical_path(OBJECT(dev));
+ error_setg(errp, "acpi_memory_plug_cb: "
+ "device [%s] returned invalid memory slot[%d]",
+ dev_path, slot);
+ g_free(dev_path);
+ return;
+ }
+
+ mdev = &mem_st->devs[slot];
+ mdev->is_removing = true;
+
+ /* do ACPI magic */
+ ar->gpe.sts[0] |= ACPI_MEMORY_HOTPLUG_STATUS;
+ acpi_update_sci(ar, irq);
+ return;
+}
+
static const VMStateDescription vmstate_memhp_sts = {
.name = "memory hotplug device state",
.version_id = 1,
@@ -362,6 +362,8 @@ static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
acpi_pcihp_device_unplug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev,
errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ acpi_memory_unplug_cb(&s->ar, s->irq, &s->acpi_memory_hotplug, dev, errp);
} else {
error_setg(errp, "acpi: device unplug request for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -228,6 +228,15 @@ void qdev_unplug(DeviceState *dev, Error **errp)
if (dev->parent_bus && dev->parent_bus->hotplug_handler) {
hotplug_handler_unplug(dev->parent_bus->hotplug_handler, dev, errp);
+ } else if (*errp == NULL) {
+ HotplugHandler *hotplug_ctrl;
+ MachineState *machine = MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+
+ hotplug_ctrl = mc->get_hotplug_handler(machine ,dev);
+ if (hotplug_ctrl) {
+ hotplug_handler_unplug(hotplug_ctrl, dev, errp);
+ }
} else {
assert(dc->unplug != NULL);
if (dc->unplug(dev) < 0) { /* legacy handler */
@@ -61,6 +61,8 @@
#include "hw/mem/pc-dimm.h"
#include "trace.h"
#include "qapi/visitor.h"
+#include "hw/acpi/piix4.h"
+#include "hw/i386/ich9.h"
/* debug PC/ISA interrupts */
//#define DEBUG_IRQ
@@ -1612,6 +1614,26 @@ out:
error_propagate(errp, local_err);
}
+static void pc_dimm_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ Object *acpi_dev;
+ HotplugHandlerClass *hhc;
+ Error *local_err = NULL;
+
+ acpi_dev = (acpi_dev = piix4_pm_find()) ? acpi_dev : ich9_lpc_find();
+ if (!acpi_dev) {
+ error_setg(&local_err,
+ "memory hotplug is not enabled: missing acpi device");
+ return;
+ }
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(acpi_dev);
+ hhc->unplug(HOTPLUG_HANDLER(acpi_dev), dev, &local_err);
+
+ error_propagate(errp, local_err);
+}
+
static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@@ -1620,6 +1642,14 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
}
}
+static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ pc_dimm_unplug(hotplug_dev, dev, errp);
+ }
+}
+
static HotplugHandler *pc_get_hotpug_handler(MachineState *machine,
DeviceState *dev)
{
@@ -1706,6 +1736,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
pcmc->get_hotplug_handler = mc->get_hotplug_handler;
mc->get_hotplug_handler = pc_get_hotpug_handler;
hc->plug = pc_machine_device_plug_cb;
+ hc->unplug = pc_machine_device_unplug_cb;
}
static const TypeInfo pc_machine_info = {
@@ -43,6 +43,11 @@ DefinitionBlock ("ssdt-mem.aml", "SSDT", 0x02, "BXPC", "CSSDT", 0x1)
External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_STATUS_METHOD, MethodObj)
External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_OST_METHOD, MethodObj)
External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_PROXIMITY_METHOD, MethodObj)
+ External(\_SB.MHPD.MCRS, MethodObj)
+ External(\_SB.MHPD.MRST, MethodObj)
+ External(\_SB.MHPD.MOST, MethodObj)
+ External(\_SB.MHPD.MPXM, MethodObj)
+ External(\_SB.MHPD.MDEJ, MethodObj)
Scope(\_SB) {
/* v------------------ DO NOT EDIT ------------------v */
@@ -72,6 +77,9 @@ DefinitionBlock ("ssdt-mem.aml", "SSDT", 0x02, "BXPC", "CSSDT", 0x1)
Method(_OST, 3) {
\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_OST_METHOD(_UID, Arg0, Arg1, Arg2)
}
+ Method(_EJ0, 1) {
+ \_SB.MHPD.MDEJ(_UID, Arg0)
+ }
}
}
}
@@ -156,6 +156,9 @@ DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1)
Offset(20),
MEMORY_SLOT_ENABLED, 1, // 1 if enabled, read only
MEMORY_SLOT_INSERT_EVENT, 1, // (read) 1 if has a insert event. (write) 1 to clear event
+ //MES, 1, // 1 if DIMM enabled used by _STA, read only
+ //MINS, 1, // (read) 1 if DIMM has a insert event. (write) 1 after MTFY() to clear event
+ MRMV, 1, // 1 if DIMM has a remove request, read only
}
Mutex (MEMORY_SLOT_LOCK, 0)
@@ -178,7 +181,9 @@ DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1)
MEMORY_SLOT_NOTIFY_METHOD(Local0, 1)
Store(1, MEMORY_SLOT_INSERT_EVENT)
}
- // TODO: handle memory eject request
+ If (LEqual(MRMV, One)) { // Ejection request
+ MTFY(Local0, 3)
+ }
Add(Local0, One, Local0) // goto next DIMM
}
Release(MEMORY_SLOT_LOCK)
@@ -278,6 +283,12 @@ DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1)
Store(Arg2, MEMORY_SLOT_OST_STATUS)
Release(MEMORY_SLOT_LOCK)
}
+ Method(MDEJ, 2) {
+ Acquire(MLCK, 0xFFFF)
+ Store(ToInteger(Arg0), MSEL) // select DIMM
+ Store(One, MRMV)
+ Release(MLCK)
+ }
} // Device()
} // Scope()
}
@@ -11,6 +11,7 @@ typedef struct MemStatus {
DeviceState *dimm;
bool is_enabled;
bool is_inserting;
+ bool is_removing;
uint32_t ost_event;
uint32_t ost_status;
} MemStatus;
@@ -28,6 +29,8 @@ void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
DeviceState *dev, Error **errp);
+void acpi_memory_unplug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
+ DeviceState *dev, Error **errp);
extern const VMStateDescription vmstate_memory_hotplug;
#define VMSTATE_MEMORY_HOTPLUG(memhp, state) \
@@ -1300,5 +1300,6 @@ int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque),
*/
Object *container_get(Object *root, const char *path);
+bool object_property_is_child(ObjectProperty *prop);
#endif
@@ -24,6 +24,7 @@
#include "qmp-commands.h"
#include "sysemu/arch_init.h"
#include "qemu/config-file.h"
+#include "qom/object.h"
/*
* Aliases were a bad idea from the start. Let's keep them
@@ -689,12 +690,34 @@ int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
return 0;
}
+static DeviceState *find_peripheral_device(const char *id)
+{
+ Object *peripheral = qdev_get_peripheral();
+ DeviceState *dev = NULL;
+ ObjectProperty *prop;
+
+
+ QTAILQ_FOREACH(prop, &peripheral->properties, node) {
+ if (object_property_is_child(prop)) {
+ dev = DEVICE(prop->opaque);
+ if (!strcmp(dev->id, id)) {
+ return dev;
+ }
+ }
+ }
+
+ return dev;
+}
+
void qmp_device_del(const char *id, Error **errp)
{
DeviceState *dev;
dev = qdev_find_recursive(sysbus_get_default(), id);
- if (NULL == dev) {
+ if (dev == NULL) {
+ dev = find_peripheral_device(id);
+ }
+ if (dev == NULL) {
error_set(errp, QERR_DEVICE_NOT_FOUND, id);
return;
}
@@ -351,7 +351,7 @@ void object_initialize(void *data, size_t size, const char *typename)
object_initialize_with_type(data, size, type);
}
-static inline bool object_property_is_child(ObjectProperty *prop)
+bool object_property_is_child(ObjectProperty *prop)
{
return strstart(prop->type, "child<", NULL);
}