From: Vasilis Liaskovitis <vasilis.liaskovi...@profitbricks.com> 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.liaskovi...@profitbricks.com> Signed-off-by: Hu Tao <hu...@cn.fujitsu.com> --- docs/specs/acpi_mem_hotplug.txt | 14 ++++++++ hw/acpi/piix4.c | 72 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 docs/specs/acpi_mem_hotplug.txt diff --git a/docs/specs/acpi_mem_hotplug.txt b/docs/specs/acpi_mem_hotplug.txt new file mode 100644 index 0000000..8391713 --- /dev/null +++ b/docs/specs/acpi_mem_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/piix4.c b/hw/acpi/piix4.c index 756df3b..2795ab0 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -29,6 +29,7 @@ #include "exec/ioport.h" #include "hw/nvram/fw_cfg.h" #include "exec/address-spaces.h" +#include "hw/mem-hotplug/dimm.h" //#define DEBUG @@ -50,9 +51,12 @@ #define PIIX4_PROC_BASE 0xaf00 #define PIIX4_PROC_LEN 32 +#define PIIX4_MEM_BASE 0xaf80 +#define PIIX4_MEM_LEN 32 #define PIIX4_PCI_HOTPLUG_STATUS 2 #define PIIX4_CPU_HOTPLUG_STATUS 4 +#define PIIX4_MEM_HOTPLUG_STATUS 8 struct pci_status { uint32_t up; /* deprecated, maintained for migration compatibility */ @@ -63,6 +67,10 @@ typedef struct CPUStatus { uint8_t sts[PIIX4_PROC_LEN]; } CPUStatus; +typedef struct MemStatus { + uint8_t mems_sts[PIIX4_MEM_LEN]; +} MemStatus; + typedef struct PIIX4PMState { PCIDevice dev; @@ -70,6 +78,7 @@ typedef struct PIIX4PMState { MemoryRegion io_gpe; MemoryRegion io_pci; MemoryRegion io_cpu; + MemoryRegion io_mem; ACPIREGS ar; APMState apm; @@ -94,6 +103,8 @@ typedef struct PIIX4PMState { CPUStatus gpe_cpu; Notifier cpu_added_notifier; + + MemStatus gpe_mem; } PIIX4PMState; static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, @@ -113,7 +124,8 @@ static void pm_update_sci(PIIX4PMState *s) ACPI_BITMASK_GLOBAL_LOCK_ENABLE | ACPI_BITMASK_TIMER_ENABLE)) != 0) || (((s->ar.gpe.sts[0] & s->ar.gpe.en[0]) & - (PIIX4_PCI_HOTPLUG_STATUS | PIIX4_CPU_HOTPLUG_STATUS)) != 0); + (PIIX4_PCI_HOTPLUG_STATUS | PIIX4_CPU_HOTPLUG_STATUS | + PIIX4_MEM_HOTPLUG_STATUS)) != 0); qemu_set_irq(s->irq, sci_level); /* schedule a timer interruption if needed */ @@ -664,8 +676,40 @@ static void piix4_init_cpu_status(CPUState *cpu, void *data) g->sts[id / 8] |= (1 << (id % 8)); } +static uint32_t mem_status_readb(void *opaque, uint32_t addr) +{ + PIIX4PMState *s = opaque; + uint32_t val = 0; + MemStatus *g = &s->gpe_mem; + if (addr < PIIX4_MEM_LEN) { + val = (uint32_t) g->mems_sts[addr]; + } + PIIX4_DPRINTF("memhp read %" PRIu32 " == %" PRIu32 "\n", addr, val); + return val; +} + +static const MemoryRegionOps mem_hotplug_ops = { + .old_portio = (MemoryRegionPortio[]) { + { + .offset = 0, .len = PIIX4_MEM_LEN, .size = 1, + .read = mem_status_readb, + }, + PORTIO_END_OF_LIST() + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void piix4_init_mem_status(PIIX4PMState *s) +{ + int i; + for (i = 0; i < PIIX4_MEM_LEN; i++) { + s->gpe_mem.mems_sts[i] = 0; + } +} + static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, PCIHotplugState state); +static int piix4_mem_hotplug(DeviceState *qdev, DimmDevice *dev, int add); static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, PCIBus *bus, PIIX4PMState *s) @@ -686,6 +730,13 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, memory_region_add_subregion(parent, PIIX4_PROC_BASE, &s->io_cpu); s->cpu_added_notifier.notify = piix4_cpu_added_req; qemu_register_cpu_added_notifier(&s->cpu_added_notifier); + + piix4_init_mem_status(s); + memory_region_init_io(&s->io_mem, &mem_hotplug_ops, s, "apci-mem-hotplug", + PIIX4_MEM_LEN); + memory_region_add_subregion(parent, PIIX4_MEM_BASE, &s->io_mem); + + dimm_bus_hotplug(piix4_mem_hotplug, &s->dev.qdev); } static void enable_device(PIIX4PMState *s, int slot) @@ -725,3 +776,22 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, return 0; } + +static void enable_mem_device(PIIX4PMState *s, int memdevice) +{ + MemStatus *g = &s->gpe_mem; + s->ar.gpe.sts[0] |= PIIX4_MEM_HOTPLUG_STATUS; + g->mems_sts[memdevice / 8] |= (1 << (memdevice % 8)); +} + +static int piix4_mem_hotplug(DeviceState *dev, DimmDevice *dimm, int add) +{ + PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, dev); + PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, pci_dev); + + if (add) { + enable_mem_device(s, dimm->idx); + } + pm_update_sci(s); + return 0; +} -- 1.8.3.1