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@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); }