diff options
Diffstat (limited to 'hw/acpi/memory_hotplug.c')
-rw-r--r-- | hw/acpi/memory_hotplug.c | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c new file mode 100644 index 0000000000..de4ddc204f --- /dev/null +++ b/hw/acpi/memory_hotplug.c @@ -0,0 +1,245 @@ +#include "hw/acpi/memory_hotplug.h" +#include "hw/acpi/pc-hotplug.h" +#include "hw/mem/pc-dimm.h" +#include "hw/boards.h" +#include "trace.h" +#include "qapi-visit.h" +#include "monitor/monitor.h" +#include "qapi/dealloc-visitor.h" +#include "qapi/qmp-output-visitor.h" + +static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev) +{ + ACPIOSTInfo *info = g_new0(ACPIOSTInfo, 1); + + info->slot_type = ACPI_SLOT_TYPE_DIMM; + info->slot = g_strdup_printf("%d", slot); + info->source = mdev->ost_event; + info->status = mdev->ost_status; + if (mdev->dimm) { + DeviceState *dev = DEVICE(mdev->dimm); + if (dev->id) { + info->device = g_strdup(dev->id); + info->has_device = true; + } + } + return info; +} + +void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list) +{ + int i; + + for (i = 0; i < mem_st->dev_count; i++) { + ACPIOSTInfoList *elem = g_new0(ACPIOSTInfoList, 1); + elem->value = acpi_memory_device_status(i, &mem_st->devs[i]); + elem->next = NULL; + **list = elem; + *list = &elem->next; + } +} + +static void acpi_memory_ost_mon_event(const MemHotplugState *mem_st) +{ + Visitor *v; + QObject *out_info; + QapiDeallocVisitor *md; + QmpOutputVisitor *mo = qmp_output_visitor_new(); + MemStatus *mdev = &mem_st->devs[mem_st->selector]; + ACPIOSTInfo *info = acpi_memory_device_status(mem_st->selector, mdev); + + v = qmp_output_get_visitor(mo); + visit_type_ACPIOSTInfo(v, &info, "unused", NULL); + + out_info = qmp_output_get_qobject(mo); + monitor_protocol_event(QEVENT_ACPI_OST, out_info); + qobject_decref(out_info); + + qmp_output_visitor_cleanup(mo); + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_ACPIOSTInfo(v, &info, "unused", NULL); + qapi_dealloc_visitor_cleanup(md); +} + +static uint64_t acpi_memory_hotplug_read(void *opaque, hwaddr addr, + unsigned int size) +{ + uint32_t val = 0; + MemHotplugState *mem_st = opaque; + MemStatus *mdev; + Object *o; + + if (mem_st->selector >= mem_st->dev_count) { + trace_mhp_acpi_invalid_slot_selected(mem_st->selector); + return 0; + } + + mdev = &mem_st->devs[mem_st->selector]; + o = OBJECT(mdev->dimm); + switch (addr) { + case 0x0: /* Lo part of phys address where DIMM is mapped */ + val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) : 0; + trace_mhp_acpi_read_addr_lo(mem_st->selector, val); + break; + case 0x4: /* Hi part of phys address where DIMM is mapped */ + val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) >> 32 : 0; + trace_mhp_acpi_read_addr_hi(mem_st->selector, val); + break; + case 0x8: /* Lo part of DIMM size */ + val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) : 0; + trace_mhp_acpi_read_size_lo(mem_st->selector, val); + break; + case 0xc: /* Hi part of DIMM size */ + val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) >> 32 : 0; + trace_mhp_acpi_read_size_hi(mem_st->selector, val); + break; + case 0x10: /* node proximity for _PXM method */ + val = o ? object_property_get_int(o, PC_DIMM_NODE_PROP, NULL) : 0; + trace_mhp_acpi_read_pxm(mem_st->selector, val); + break; + case 0x14: /* pack and return is_* fields */ + val |= mdev->is_enabled ? 1 : 0; + val |= mdev->is_inserting ? 2 : 0; + trace_mhp_acpi_read_flags(mem_st->selector, val); + break; + default: + val = ~0; + break; + } + return val; +} + +static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + MemHotplugState *mem_st = opaque; + MemStatus *mdev; + + if (!mem_st->dev_count) { + return; + } + + if (addr) { + if (mem_st->selector >= mem_st->dev_count) { + trace_mhp_acpi_invalid_slot_selected(mem_st->selector); + return; + } + } + + switch (addr) { + case 0x0: /* DIMM slot selector */ + mem_st->selector = data; + trace_mhp_acpi_write_slot(mem_st->selector); + break; + case 0x4: /* _OST event */ + mdev = &mem_st->devs[mem_st->selector]; + if (data == 1) { + /* TODO: handle device insert OST event */ + } else if (data == 3) { + /* TODO: handle device remove OST event */ + } + mdev->ost_event = data; + trace_mhp_acpi_write_ost_ev(mem_st->selector, mdev->ost_event); + break; + case 0x8: /* _OST status */ + mdev = &mem_st->devs[mem_st->selector]; + mdev->ost_status = data; + trace_mhp_acpi_write_ost_status(mem_st->selector, mdev->ost_status); + /* TODO: implement memory removal on guest signal */ + acpi_memory_ost_mon_event(mem_st); + 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); + } + break; + } + +} +static const MemoryRegionOps acpi_memory_hotplug_ops = { + .read = acpi_memory_hotplug_read, + .write = acpi_memory_hotplug_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, + MemHotplugState *state) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + + state->dev_count = machine->ram_slots; + if (!state->dev_count) { + return; + } + + state->devs = g_malloc0(sizeof(*state->devs) * state->dev_count); + memory_region_init_io(&state->io, owner, &acpi_memory_hotplug_ops, state, + "apci-mem-hotplug", ACPI_MEMORY_HOTPLUG_IO_LEN); + memory_region_add_subregion(as, ACPI_MEMORY_HOTPLUG_BASE, &state->io); +} + +void acpi_memory_plug_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->dimm = dev; + mdev->is_enabled = true; + mdev->is_inserting = 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, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(is_enabled, MemStatus), + VMSTATE_BOOL(is_inserting, MemStatus), + VMSTATE_UINT32(ost_event, MemStatus), + VMSTATE_UINT32(ost_status, MemStatus), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_memory_hotplug = { + .name = "memory hotplug state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(selector, MemHotplugState), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, MemHotplugState, dev_count, + vmstate_memhp_sts, MemStatus), + VMSTATE_END_OF_LIST() + } +}; |