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.liaskovitis(a)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