A 32-byte register is used to present up to 256 hotplug-able memory devices to BIOS and OSPM. Hot-add and hot-remove functions trigger an ACPI hotplug event through these. Only reads are allowed from these registers (from BIOS/OSPM perspective). "memslot id add" will immediately populate the new memslot (a new MemoryRegion is created and attached to system memory), and then trigger the ACPI hot-add event. "memslot id delete" triggers the ACPI hot-remove event but needs to wait for OSPM to eject the device. We use a second set of eject registers to know when OSPM has called the _EJ function for a particular memslot. A write to these will depopulate the corresponding memslot i.e. detach and free the MemoryRegion. Only writes to the eject registers are allowed.
A new property mem_acpi_hotplug should enable these memory hotplug registers for future machine types (not yet implemented in this version).
Signed-off-by: Vasilis Liaskovitis vasilis.liaskovitis@profitbricks.com --- hw/acpi_piix4.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 89 insertions(+), 4 deletions(-)
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 797ed24..a14dd3c 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -27,6 +27,8 @@ #include "sysemu.h" #include "range.h" #include "ioport.h" +#include "sysbus.h" +#include "memslot.h"
//#define DEBUG
@@ -43,9 +45,16 @@ #define PCI_BASE 0xae00 #define PCI_EJ_BASE 0xae08 #define PCI_RMV_BASE 0xae0c +#define MEM_BASE 0xaf20 +#define MEM_EJ_BASE 0xaf40
+#define PIIX4_MEM_HOTPLUG_STATUS 8 #define PIIX4_PCI_HOTPLUG_STATUS 2
+struct gpe_regs { + uint8_t mems_sts[32]; +}; + struct pci_status { uint32_t up; uint32_t down; @@ -66,6 +75,7 @@ typedef struct PIIX4PMState { int kvm_enabled; Notifier machine_ready;
+ struct gpe_regs gpe; /* for pci hotplug */ struct pci_status pci0_status; uint32_t pci0_hotplug_enable; @@ -86,8 +96,8 @@ static void pm_update_sci(PIIX4PMState *s) ACPI_BITMASK_POWER_BUTTON_ENABLE | ACPI_BITMASK_GLOBAL_LOCK_ENABLE | ACPI_BITMASK_TIMER_ENABLE)) != 0) || - (((s->ar.gpe.sts[0] & s->ar.gpe.en[0]) - & PIIX4_PCI_HOTPLUG_STATUS) != 0); + (((s->ar.gpe.sts[0] & s->ar.gpe.en[0]) & + (PIIX4_PCI_HOTPLUG_STATUS | PIIX4_MEM_HOTPLUG_STATUS)) != 0);
qemu_set_irq(s->irq, sci_level); /* schedule a timer interruption if needed */ @@ -432,17 +442,34 @@ type_init(piix4_pm_register_types) static uint32_t gpe_readb(void *opaque, uint32_t addr) { PIIX4PMState *s = opaque; - uint32_t val = acpi_gpe_ioport_readb(&s->ar, addr); + uint32_t val = 0; + struct gpe_regs *g = &s->gpe; + + switch (addr) { + case MEM_BASE ... MEM_BASE+31: + val = g->mems_sts[addr - MEM_BASE]; + break; + default: + val = acpi_gpe_ioport_readb(&s->ar, addr); + }
PIIX4_DPRINTF("gpe read %x == %x\n", addr, val); return val; }
+static void piix4_memslot_eject(uint32_t addr, uint32_t val); + static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val) { PIIX4PMState *s = opaque;
- acpi_gpe_ioport_writeb(&s->ar, addr, val); + switch (addr) { + case MEM_EJ_BASE ... MEM_EJ_BASE+31: + piix4_memslot_eject(addr, val); + break; + default: + acpi_gpe_ioport_writeb(&s->ar, addr, val); + } pm_update_sci(s);
PIIX4_DPRINTF("gpe write %x <== %d\n", addr, val); @@ -521,9 +548,12 @@ static void pcirmv_write(void *opaque, uint32_t addr, uint32_t val) static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, PCIHotplugState state);
+static int piix4_memslot_hotplug(DeviceState *qdev, SysBusDevice *dev, int add); + static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s) { struct pci_status *pci0_status = &s->pci0_status; + int i = 0;
register_ioport_write(GPE_BASE, GPE_LEN, 1, gpe_writeb, s); register_ioport_read(GPE_BASE, GPE_LEN, 1, gpe_readb, s); @@ -538,6 +568,13 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s) register_ioport_write(PCI_RMV_BASE, 4, 4, pcirmv_write, s); register_ioport_read(PCI_RMV_BASE, 4, 4, pcirmv_read, s);
+ register_ioport_read(MEM_BASE, 32, 1, gpe_readb, s); + register_ioport_write(MEM_EJ_BASE, 32, 1, gpe_writeb, s); + for(i = 0; i < 32; i++) { + s->gpe.mems_sts[i] = 0; + } + memslot_register_hotplug(piix4_memslot_hotplug, &s->dev.qdev); + pci_bus_hotplug(bus, piix4_device_hotplug, &s->dev.qdev); }
@@ -553,6 +590,54 @@ static void disable_device(PIIX4PMState *s, int slot) s->pci0_status.down |= (1 << slot); }
+static void enable_mem_device(PIIX4PMState *s, int memdevice) +{ + struct gpe_regs *g = &s->gpe; + s->ar.gpe.sts[0] |= PIIX4_MEM_HOTPLUG_STATUS; + g->mems_sts[memdevice/8] |= (1 << (memdevice%8)); +} + +static void disable_mem_device(PIIX4PMState *s, int memdevice) +{ + struct gpe_regs *g = &s->gpe; + s->ar.gpe.sts[0] |= PIIX4_MEM_HOTPLUG_STATUS; + g->mems_sts[memdevice/8] &= ~(1 << (memdevice%8)); +} + +static void piix4_memslot_eject(uint32_t addr, uint32_t val) +{ + uint32_t start = 8 * (addr - MEM_EJ_BASE); + uint32_t idx = 0; + MemSlotState *s; + PIIX4_DPRINTF("memej write %x <= %d\n", addr, val); + while (val) { + if (val & 1) { + s = memslot_find_from_idx(start + idx); + assert(s != NULL); + memslot_depopulate(s); + } + val = val >> 1; + idx++; + } +} + +static int piix4_memslot_hotplug(DeviceState *qdev, SysBusDevice *dev, int + add) +{ + PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, + PCI_DEVICE(qdev)); + MemSlotState *slot = MEMSLOT(dev); + + if (add) { + enable_mem_device(s, slot->idx); + } + else { + disable_mem_device(s, slot->idx); + } + pm_update_sci(s); + return 0; +} + static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, PCIHotplugState state) {