diff mbox

[13/16] acpi/piix4: introduce memory hot-plug interface QEMU<->ACPI BIOS

Message ID 1374596592-7027-14-git-send-email-imammedo@redhat.com
State New
Headers show

Commit Message

Igor Mammedov July 23, 2013, 4:23 p.m. UTC
- implements QEMU part of memory hotplug protocol described at
  docs/specs/acpi_mem_hotplug.txt
- handles memory add notification event.

Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
 docs/specs/acpi_mem_hotplug.txt |   38 +++++++++
 hw/acpi/piix4.c                 |  170 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 207 insertions(+), 1 deletions(-)
 create mode 100644 docs/specs/acpi_mem_hotplug.txt
diff mbox

Patch

diff --git a/docs/specs/acpi_mem_hotplug.txt b/docs/specs/acpi_mem_hotplug.txt
new file mode 100644
index 0000000..33b7aa0
--- /dev/null
+++ b/docs/specs/acpi_mem_hotplug.txt
@@ -0,0 +1,38 @@ 
+QEMU<->ACPI BIOS memory hotplug interface
+--------------------------------------
+
+ACPI BIOS GPE.3 handler is dedicated for notifying OS of memory hot-add
+or hot-remove events. Read-only.
+
+Memory Dimm hot-plug interface (IO port 0xaf80-0xaf97, 1-4 byte access):
+---------------------------------------------------------------
+0xaf80:
+  read access:
+      [0x0-0x3] Lo part of memory device phys address
+      [0x4-0x7] Hi part of memory device phys address
+      [0x8-0xb] Lo part of memory device size in bytes
+      [0xc-0xf] Hi part of memory device size in bytes
+      [0x14] Memory hot-plug interface version
+      [0x15] Memory device status fields
+          bits:
+              1: device is enabled, end may be used by guest code
+              2: device insert event, used by ACPI BIOS to distinguish
+                 device for which no device check event to OSPM was issued
+      [0x16-0x17] reserved
+
+  write access:
+      [0x0-0x3] Memory device slot selector, selects active memory device.
+                All following accesses to other registers in 0xaf80-0xaf97
+                region will read/store data from/to selected memory device.
+      [0x4-0x7] OST event code reported by OSPM
+      [0x8-0xb] OST status code reported by OSPM
+      [0x15] Memory device status fields
+          bits:
+              2: if set to 1 clears device insert event, set by ACPI BIOS
+                 after it has sent device check event to OSPM for
+                 seleted memory device
+
+Selecting memory device slot beyond present range has no effect on platform:
+   - following write access to memory hot-plug registers except of
+     [0x0-0x3] is ignored;
+   - following read access to memory hot-plug registers returns 0
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index c077a7a..f58a5bf 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -29,6 +29,8 @@ 
 #include "exec/ioport.h"
 #include "hw/nvram/fw_cfg.h"
 #include "exec/address-spaces.h"
+#include "hw/mem-hotplug/dimm.h"
+#include "qemu/config-file.h"
 
 //#define DEBUG
 
@@ -50,9 +52,12 @@ 
 
 #define PIIX4_PROC_BASE 0xaf00
 #define PIIX4_PROC_LEN 32
+#define PIIX4_MEM_BASE 0xaf80
+#define PIIX4_MEM_LEN 24
 
 #define PIIX4_PCI_HOTPLUG_STATUS 2
 #define PIIX4_CPU_HOTPLUG_STATUS 4
+#define PIIX4_MEM_HOTPLUG_STATUS 8
 
 struct pci_status {
     uint32_t up; /* deprecated, maintained for migration compatibility */
@@ -63,6 +68,20 @@  typedef struct CPUStatus {
     uint8_t sts[PIIX4_PROC_LEN];
 } CPUStatus;
 
+typedef struct MemStatus {
+    DimmDevice *dimm;
+    bool is_enabled;
+    bool is_inserting;
+    uint32_t ost_event;
+    uint32_t ost_status;
+} MemStatus;
+
+typedef struct mem_hotplug_state {
+    uint32_t selector;
+    uint32_t dev_count;
+    MemStatus *devs;
+} mem_hotplug_state;
+
 typedef struct PIIX4PMState {
     PCIDevice dev;
 
@@ -70,6 +89,7 @@  typedef struct PIIX4PMState {
     MemoryRegion io_gpe;
     MemoryRegion io_pci;
     MemoryRegion io_cpu;
+    MemoryRegion io_mem;
     ACPIREGS ar;
 
     APMState apm;
@@ -96,6 +116,9 @@  typedef struct PIIX4PMState {
     Notifier cpu_added_notifier;
 
     PcGuestInfo *guest_info;
+
+    Notifier mem_added_notifier;
+    mem_hotplug_state  gpe_mem;
 } PIIX4PMState;
 
 static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
@@ -115,7 +138,8 @@  static void pm_update_sci(PIIX4PMState *s)
                    ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
                    ACPI_BITMASK_TIMER_ENABLE)) != 0) ||
         (((s->ar.gpe.sts[0] & s->ar.gpe.en[0]) &
-          (PIIX4_PCI_HOTPLUG_STATUS | PIIX4_CPU_HOTPLUG_STATUS)) != 0);
+          (PIIX4_PCI_HOTPLUG_STATUS | PIIX4_CPU_HOTPLUG_STATUS |
+          PIIX4_MEM_HOTPLUG_STATUS)) != 0);
 
     qemu_set_irq(s->irq, sci_level);
     /* schedule a timer interruption if needed */
@@ -706,6 +730,143 @@  static void piix4_init_cpu_status(CPUState *cpu, void *data)
     g->sts[id / 8] |= (1 << (id % 8));
 }
 
+static uint64_t piix4_hp_mem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    PIIX4PMState *s = opaque;
+    uint32_t val = 0;
+    mem_hotplug_state *mem_st = &s->gpe_mem;
+    MemStatus *mdev;
+
+    if (mem_st->selector >= mem_st->dev_count) {
+        return 0;
+    }
+
+    mdev = &mem_st->devs[mem_st->selector];
+    switch (addr) {
+    case 0x0: /* Lo part of phys address where DIMM is mapped */
+        val = mdev->dimm->start;
+        break;
+    case 0x4: /* Hi part of phys address where DIMM is mapped */
+        val = mdev->dimm->start >> 32;
+        break;
+    case 0x8: /* Lo part of DIMM size */
+        val = mdev->dimm->size;
+        break;
+    case 0xc: /* Hi part of DIMM size */
+        val = mdev->dimm->size >> 32;
+        break;
+    case 0x10: /* node proximity for _PXM method */
+        val = mdev->dimm->node;
+        break;
+    case 0x14: /* intf version */
+        val = 1;
+        break;
+    case 0x15: /* pack and return is_* fields */
+        val |= mdev->is_enabled   ? 1 : 0;
+        val |= mdev->is_inserting ? 2 : 0;
+        break;
+    }
+    return val;
+}
+
+static void piix4_hp_mem_write(void *opaque, hwaddr addr, uint64_t data,
+                             unsigned int size)
+{
+    PIIX4PMState *s = opaque;
+    mem_hotplug_state *mem_st = &s->gpe_mem;
+    MemStatus *mdev;
+
+    if (!mem_st->dev_count) {
+        return;
+    }
+
+    if (addr) {
+        if (mem_st->selector >= mem_st->dev_count) {
+            return;
+        }
+    }
+
+    switch (addr) {
+    case 0x0: /* DIMM slot selector */
+        mem_st->selector = data;
+        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;
+        break;
+    case 0x8: /* _OST status */
+        mdev = &mem_st->devs[mem_st->selector];
+        mdev->ost_status = data;
+        /* TODO: report async error */
+        /* TODO: implement memory removal on guest signal */
+        break;
+    case 0x15:
+        mdev = &mem_st->devs[mem_st->selector];
+        if (data & 2) { /* clear insert event */
+            mdev->is_inserting  = false;
+        }
+        break;
+    }
+
+}
+static const MemoryRegionOps mem_hotplug_ops = {
+    .read = piix4_hp_mem_read,
+    .write = piix4_hp_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static void piix4_init_mem_status(PIIX4PMState *s)
+{
+    mem_hotplug_state *mem_st = &s->gpe_mem;
+    QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory-opts"), NULL);
+
+    if (!opts) { /* no -m x,... was passed to cmd line so no men hotplug */
+        return;
+    }
+
+    mem_st->dev_count = qemu_opt_get_number(opts, "slots", 0);
+
+    if (!mem_st->dev_count) {
+        return;
+    }
+
+    mem_st->devs = g_malloc0(sizeof(MemStatus) * mem_st->dev_count);
+}
+
+static void piix4_mem_added_req(Notifier *n, void *opaque)
+{
+    PIIX4PMState *s = container_of(n, PIIX4PMState, mem_added_notifier);
+    DeviceState *dev = DEVICE(opaque);
+    DimmDevice *dimm = DIMM(dev);
+    mem_hotplug_state *mem_st = &s->gpe_mem;
+    MemStatus *mdev;
+
+    if (dimm->slot >= mem_st->dev_count) {
+        return;
+    }
+
+    mdev = &mem_st->devs[dimm->slot];
+    object_property_add_link(OBJECT(s), dev->id, TYPE_DIMM,
+                             (Object **) &mdev->dimm, NULL);
+    object_property_set_link(OBJECT(s), OBJECT(dimm), dev->id, NULL);
+
+    mdev->is_enabled = true;
+    mdev->is_inserting = true;
+
+    /* do ACPI magic */
+    s->ar.gpe.sts[0] |= PIIX4_MEM_HOTPLUG_STATUS;
+    pm_update_sci(s);
+}
+
 static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
                                 PCIHotplugState state);
 
@@ -728,6 +889,13 @@  static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
     memory_region_add_subregion(parent, PIIX4_PROC_BASE, &s->io_cpu);
     s->cpu_added_notifier.notify = piix4_cpu_added_req;
     qemu_register_cpu_added_notifier(&s->cpu_added_notifier);
+
+    piix4_init_mem_status(s);
+    memory_region_init_io(&s->io_mem, &mem_hotplug_ops, s,
+                          "apci-mem-hotplug", PIIX4_MEM_LEN);
+    memory_region_add_subregion(parent, PIIX4_MEM_BASE, &s->io_mem);
+    s->mem_added_notifier.notify = piix4_mem_added_req;
+    qemu_register_mem_added_notifier(&s->mem_added_notifier);
 }
 
 static void enable_device(PIIX4PMState *s, int slot)