This implements acpi dimm hot-add capability for q35 (ich9). The logic is the
same as for the pc machine (piix4).

TODO: Fix acpi irq delivery bug. Currently there is a flood of irqs when
delivering an acpi interrupt (should be just one). Guest complains as follows:
"irq 9: nobody cared
[...]
Disabling IRQ #9"
where #9 is the acpi irq

Signed-off-by: Vasilis Liaskovitis <vasilis.liaskovi...@profitbricks.com>
---
 hw/acpi_ich9.c |   61 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 hw/acpi_ich9.h |    7 +++++-
 hw/lpc_ich9.c  |    2 +-
 3 files changed, 65 insertions(+), 5 deletions(-)

diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c
index c5978d3..abafbb5 100644
--- a/hw/acpi_ich9.c
+++ b/hw/acpi_ich9.c
@@ -48,11 +48,14 @@ static void pm_update_sci(ICH9LPCPMRegs *pm)
 
     pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs);
 
-    sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) &
+    sci_level = ((((pm1a_sts & pm->acpi_regs.pm1.evt.en) &
                   (ACPI_BITMASK_RT_CLOCK_ENABLE |
                    ACPI_BITMASK_POWER_BUTTON_ENABLE |
                    ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
-                   ACPI_BITMASK_TIMER_ENABLE)) != 0);
+                   ACPI_BITMASK_TIMER_ENABLE)) != 0) ||
+        (((pm->acpi_regs.gpe.sts[0] & pm->acpi_regs.gpe.en[0]) &
+          (ICH9_MEM_HOTPLUG_STATUS)) != 0));
+
     qemu_set_irq(pm->irq, sci_level);
 
     /* schedule a timer interruption if needed */
@@ -90,6 +93,29 @@ static const MemoryRegionOps ich9_gpe_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
+static uint32_t memhp_readb(void *opaque, uint32_t addr)
+{
+    ICH9LPCPMRegs *s = opaque;
+    uint32_t val = 0;
+    struct gpe_regs *g = &s->gperegs;
+    if (addr < DIMM_BITMAP_BYTES) {
+        val = (uint32_t) g->mems_sts[addr];
+    }
+    ICH9_DEBUG("memhp read %x == %x\n", addr, val);
+    return val;
+}
+
+static const MemoryRegionOps ich9_memhp_ops = {
+    .old_portio = (MemoryRegionPortio[]) {
+        {
+            .offset = 0,   .len = DIMM_BITMAP_BYTES, .size = 1,
+            .read = memhp_readb,
+        },
+        PORTIO_END_OF_LIST()
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
 static uint64_t ich9_smi_readl(void *opaque, hwaddr addr, unsigned width)
 {
     ICH9LPCPMRegs *pm = opaque;
@@ -201,8 +227,31 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
     acpi_pm1_evt_power_down(&pm->acpi_regs);
 }
 
-void ich9_pm_init(ICH9LPCPMRegs *pm, qemu_irq sci_irq, qemu_irq cmos_s3)
+static void enable_mem_device(ICH9LPCState *s, int memdevice)
 {
+    struct gpe_regs *g = &s->pm.gperegs;
+    s->pm.acpi_regs.gpe.sts[0] |= ICH9_MEM_HOTPLUG_STATUS;
+    g->mems_sts[memdevice/8] |= (1 << (memdevice%8));
+}
+
+static int ich9_dimm_hotplug(DeviceState *qdev, DimmDevice *dev, int
+        add)
+{
+    PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, qdev);
+    ICH9LPCState *s = DO_UPCAST(ICH9LPCState, d, pci_dev);
+    DimmDevice *slot = DIMM(dev);
+
+    if (add) {
+        enable_mem_device(s, slot->idx);
+    }
+    pm_update_sci(&s->pm);
+    return 0;
+}
+
+void ich9_pm_init(void *device, qemu_irq sci_irq, qemu_irq cmos_s3)
+{
+    ICH9LPCState *lpc = (ICH9LPCState *)device;
+    ICH9LPCPMRegs *pm = &lpc->pm;
     memory_region_init(&pm->io, "ich9-pm", ICH9_PMIO_SIZE);
     memory_region_set_enabled(&pm->io, false);
     memory_region_add_subregion(get_system_io(), 0, &pm->io);
@@ -220,6 +269,12 @@ void ich9_pm_init(ICH9LPCPMRegs *pm, qemu_irq sci_irq, 
qemu_irq cmos_s3)
                           8);
     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
 
+    memory_region_init_io(&pm->io_memhp, &ich9_memhp_ops, pm, "apci-memhp0",
+                          DIMM_BITMAP_BYTES);
+    memory_region_add_subregion(get_system_io(), ICH9_MEM_BASE, &pm->io_memhp);
+
+    dimm_bus_hotplug(ich9_dimm_hotplug, &lpc->d.qdev);
+
     pm->irq = sci_irq;
     qemu_register_reset(pm_reset, pm);
     pm->powerdown_notifier.notify = pm_powerdown_req;
diff --git a/hw/acpi_ich9.h b/hw/acpi_ich9.h
index bc221d3..4419247 100644
--- a/hw/acpi_ich9.h
+++ b/hw/acpi_ich9.h
@@ -23,6 +23,9 @@
 
 #include "acpi.h"
 
+#define ICH9_MEM_BASE    0xaf80
+#define ICH9_MEM_HOTPLUG_STATUS 8
+
 typedef struct ICH9LPCPMRegs {
     /*
      * In ich9 spec says that pm1_cnt register is 32bit width and
@@ -33,16 +36,18 @@ typedef struct ICH9LPCPMRegs {
     MemoryRegion io;
     MemoryRegion io_gpe;
     MemoryRegion io_smi;
+    MemoryRegion io_memhp;
     uint32_t smi_en;
     uint32_t smi_sts;
 
     qemu_irq irq;      /* SCI */
 
+    struct gpe_regs gperegs;
     uint32_t pm_io_base;
     Notifier powerdown_notifier;
 } ICH9LPCPMRegs;
 
-void ich9_pm_init(ICH9LPCPMRegs *pm,
+void ich9_pm_init(void *lpc,
                   qemu_irq sci_irq, qemu_irq cmos_s3_resume);
 void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base);
 extern const VMStateDescription vmstate_ich9_pm;
diff --git a/hw/lpc_ich9.c b/hw/lpc_ich9.c
index 878a43e..0ef7af6 100644
--- a/hw/lpc_ich9.c
+++ b/hw/lpc_ich9.c
@@ -352,7 +352,7 @@ void ich9_lpc_pm_init(PCIDevice *lpc_pci, qemu_irq cmos_s3)
     qemu_irq *sci_irq;
 
     sci_irq = qemu_allocate_irqs(ich9_set_sci, lpc, 1);
-    ich9_pm_init(&lpc->pm, sci_irq[0], cmos_s3);
+    ich9_pm_init(lpc, sci_irq[0], cmos_s3);
 
     ich9_lpc_reset(&lpc->d.qdev);
 }
-- 
1.7.9


Reply via email to