Patchwork [RFC,v4,11/30] acpi_piix4 : Implement memory device hotplug registers

login
register
mail settings
Submitter Vasilis Liaskovitis
Date Dec. 18, 2012, 12:41 p.m.
Message ID <1355834518-17989-12-git-send-email-vasilis.liaskovitis@profitbricks.com>
Download mbox | patch
Permalink /patch/207136/
State New
Headers show

Comments

Vasilis Liaskovitis - Dec. 18, 2012, 12:41 p.m.
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.

An ACPI hot-remove event but needs to wait for OSPM to eject the device.
We use a single-byte register to know when OSPM has called the _EJ function
for a particular dimm. A write to this byte will depopulate the respective dimm.
Only writes are allowed to this byte.

v1->v2:
mems_sts address moved from 0xaf20 to 0xaf80 (to accomodate more space for
cpu-hotplugging in the future).
_EJ array is reduced to a single byte.
Add documentation in docs/specs/acpi_hotplug.txt

v3->v4: Removed hot-remove functions, will be added separately. Updated for
memory API.

Signed-off-by: Vasilis Liaskovitis <vasilis.liaskovitis@profitbricks.com>
---
 docs/specs/acpi_hotplug.txt |   14 +++++++++
 hw/acpi.h                   |    5 +++
 hw/acpi_piix4.c             |   65 +++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 82 insertions(+), 2 deletions(-)
 create mode 100644 docs/specs/acpi_hotplug.txt

Patch

diff --git a/docs/specs/acpi_hotplug.txt b/docs/specs/acpi_hotplug.txt
new file mode 100644
index 0000000..8391713
--- /dev/null
+++ b/docs/specs/acpi_hotplug.txt
@@ -0,0 +1,14 @@ 
+QEMU<->ACPI BIOS hotplug interface
+--------------------------------------
+This document describes the interface between QEMU and the ACPI BIOS for non-PCI
+space. For the PCI interface please look at docs/specs/acpi_pci_hotplug.txt
+
+QEMU<->ACPI BIOS memory hotplug interface
+--------------------------------------
+
+Memory Dimm status array (IO port 0xaf80-0xaf9f, 1-byte access):
+---------------------------------------------------------------
+Dimm hot-plug notification pending. One bit per slot.
+
+Read by ACPI BIOS GPE.3 handler to notify OS of memory hot-add or hot-remove
+events.  Read-only.
diff --git a/hw/acpi.h b/hw/acpi.h
index afda153..dc617d3 100644
--- a/hw/acpi.h
+++ b/hw/acpi.h
@@ -120,6 +120,11 @@  struct ACPIREGS {
     Notifier wakeup;
 };
 
+#include "dimm.h"
+struct gpe_regs {
+    uint8_t mems_sts[DIMM_BITMAP_BYTES];
+};
+
 /* PM_TMR */
 void acpi_pm_tmr_update(ACPIREGS *ar, bool enable);
 void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar);
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 0b5b0d3..879d8a0 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -29,6 +29,8 @@ 
 #include "ioport.h"
 #include "fw_cfg.h"
 #include "exec-memory.h"
+#include "sysbus.h"
+#include "dimm.h"
 
 //#define DEBUG
 
@@ -47,7 +49,9 @@ 
 #define PCI_DOWN_BASE 0xae04
 #define PCI_EJ_BASE 0xae08
 #define PCI_RMV_BASE 0xae0c
+#define MEM_BASE 0xaf80
 
+#define PIIX4_MEM_HOTPLUG_STATUS 8
 #define PIIX4_PCI_HOTPLUG_STATUS 2
 
 struct pci_status {
@@ -60,6 +64,7 @@  typedef struct PIIX4PMState {
     MemoryRegion io;
     MemoryRegion io_gpe;
     MemoryRegion io_pci;
+    MemoryRegion io_memhp;
     ACPIREGS ar;
 
     APMState apm;
@@ -74,6 +79,7 @@  typedef struct PIIX4PMState {
     Notifier powerdown_notifier;
 
     /* for pci hotplug */
+    struct gpe_regs gperegs;
     struct pci_status pci0_status;
     uint32_t pci0_hotplug_enable;
     uint32_t pci0_slot_device_present;
@@ -98,8 +104,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 */
@@ -526,6 +532,29 @@  static const MemoryRegionOps piix4_gpe_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
+static uint32_t memhp_readb(void *opaque, uint32_t addr)
+{
+    PIIX4PMState *s = opaque;
+    uint32_t val = 0;
+    struct gpe_regs *g = &s->gperegs;
+    if (addr < DIMM_BITMAP_BYTES) {
+        val = (uint32_t) g->mems_sts[addr];
+    }
+    PIIX4_DPRINTF(stderr, "memhp read %x == %x\n", addr, val);
+    return val;
+}
+
+static const MemoryRegionOps piix4_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 uint32_t pci_up_read(void *opaque, uint32_t addr)
 {
     PIIX4PMState *s = opaque;
@@ -592,9 +621,11 @@  static const MemoryRegionOps piix4_pci_ops = {
 
 static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
                                 PCIHotplugState state);
+static int piix4_dimm_hotplug(DeviceState *qdev, DimmDevice *dev, int add);
 
 static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
 {
+    int i = 0;
     memory_region_init_io(&s->io_gpe, &piix4_gpe_ops, s, "apci-gpe0",
                           GPE_LEN);
     memory_region_add_subregion(get_system_io(), GPE_BASE, &s->io_gpe);
@@ -603,7 +634,16 @@  static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
                           PCI_HOTPLUG_SIZE);
     memory_region_add_subregion(get_system_io(), PCI_HOTPLUG_ADDR,
                                 &s->io_pci);
+    memory_region_init_io(&s->io_memhp, &piix4_memhp_ops, s, "apci-memhp0",
+                          DIMM_BITMAP_BYTES);
+    memory_region_add_subregion(get_system_io(), MEM_BASE, &s->io_memhp);
+
+    for (i = 0; i < DIMM_BITMAP_BYTES; i++) {
+        s->gperegs.mems_sts[i] = 0;
+    }
+
     pci_bus_hotplug(bus, piix4_device_hotplug, &s->dev.qdev);
+    dimm_bus_hotplug(piix4_dimm_hotplug, &s->dev.qdev);
 }
 
 static void enable_device(PIIX4PMState *s, int slot)
@@ -618,6 +658,27 @@  static void disable_device(PIIX4PMState *s, int slot)
     s->pci0_status.down |= (1U << slot);
 }
 
+static void enable_mem_device(PIIX4PMState *s, int memdevice)
+{
+    struct gpe_regs *g = &s->gperegs;
+    s->ar.gpe.sts[0] |= PIIX4_MEM_HOTPLUG_STATUS;
+    g->mems_sts[memdevice/8] |= (1 << (memdevice%8));
+}
+
+static int piix4_dimm_hotplug(DeviceState *qdev, DimmDevice *dev, int
+        add)
+{
+    PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, qdev);
+    PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, pci_dev);
+    DimmDevice *slot = DIMM(dev);
+
+    if (add) {
+        enable_mem_device(s, slot->idx);
+    }
+    pm_update_sci(s);
+    return 0;
+}
+
 static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
 				PCIHotplugState state)
 {