From: Salil Mehta <salil.mehta@huawei.com>
When vCPUs are administratively enabled or disabled, the guest OSPM must be
notified so it can coordinate the corresponding operational transitions and
preserve system stability.
When a CPU is administratively enabled, GED raises a Device Check event. OSPM
then uses the ACPI _EVT handler to identify the CPU device and evaluates its
_STA, ensuring the CPU is identified, registered with the Linux device model,
enabled in the guest kernel, and made available to the scheduler.
When a CPU is administratively disabled, GED raises an Eject Request event. OSPM
again uses the ACPI _EVT handler to identify the CPU device and evaluates its
_STA, marking the CPU absent. This allows OSPM to invoke the _EJ0 path,
gracefully offload tasks, and shut down state before removal. Without this
coordination, CPUs may be forcefully removed, risking state loss or kernel
instability.
Platform code (e.g. Arm virt machine) calls PowerStateHandler hooks, which in
turn drive the GED callbacks. Those callbacks use ACPI events to reflect the
administrative change and let OSPM orchestrate the operational transition.
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
hw/acpi/generic_event_device.c | 91 ++++++++++++++++++++++++++
hw/arm/virt.c | 9 ++-
include/hw/acpi/acpi_dev_interface.h | 1 +
include/hw/acpi/generic_event_device.h | 6 ++
include/hw/arm/virt.h | 1 +
5 files changed, 107 insertions(+), 1 deletion(-)
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index 95682b79a2..4fbf5aaa20 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -23,11 +23,13 @@
#include "migration/vmstate.h"
#include "qemu/error-report.h"
#include "system/runstate.h"
+#include "hw/powerstate.h"
static const uint32_t ged_supported_events[] = {
ACPI_GED_MEM_HOTPLUG_EVT,
ACPI_GED_PWR_DOWN_EVT,
ACPI_GED_NVDIMM_HOTPLUG_EVT,
+ ACPI_GED_CPU_POWERSTATE_EVT,
ACPI_GED_CPU_HOTPLUG_EVT,
ACPI_GED_PCI_HOTPLUG_EVT,
};
@@ -112,6 +114,9 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "."
MEMORY_SLOT_SCAN_METHOD));
break;
+ case ACPI_GED_CPU_POWERSTATE_EVT:
+ aml_append(if_ctx, aml_call0(AML_GED_EVT_CPUPS_SCAN_METHOD));
+ break;
case ACPI_GED_CPU_HOTPLUG_EVT:
aml_append(if_ctx, aml_call0(AML_GED_EVT_CPU_SCAN_METHOD));
break;
@@ -302,12 +307,57 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev,
}
}
+static void
+acpi_ged_pre_poweron_cb(PowerStateHandler *handler, DeviceState *dev,
+ Error **errp)
+{
+ AcpiGedState *s = ACPI_GED(handler);
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_device_check_cb(&s->cpuospm_state, dev,
+ ACPI_CPU_POWERSTATE_STATUS, errp);
+ } else {
+ error_setg(errp, "acpi: poweron transition on unsupported device"
+ " type %s", object_get_typename(OBJECT(dev)));
+ }
+}
+
+static void
+acpi_ged_request_poweroff_cb(PowerStateHandler *handler, DeviceState *dev,
+ Error **errp)
+{
+ AcpiGedState *s = ACPI_GED(handler);
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_eject_request_cb(&s->cpuospm_state, dev,
+ ACPI_CPU_POWERSTATE_STATUS, errp);
+ } else {
+ error_setg(errp, "acpi: poweroff transition request for unsupported"
+ " device type: %s", object_get_typename(OBJECT(dev)));
+ }
+}
+
+static void
+acpi_ged_post_poweroff_cb(PowerStateHandler *handler, DeviceState *dev,
+ Error **errp)
+{
+ AcpiGedState *s = ACPI_GED(handler);
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_eject_cb(&s->cpuospm_state, dev, errp);
+ } else {
+ error_setg(errp, "acpi: post poweroff handling on unsupported device"
+ " type %s", object_get_typename(OBJECT(dev)));
+ }
+}
+
static void acpi_ged_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
{
AcpiGedState *s = ACPI_GED(adev);
acpi_memory_ospm_status(&s->memhp_state, list);
acpi_cpu_ospm_status(&s->cpuhp_state, list);
+ acpi_cpus_ospm_status(&s->cpuospm_state, list);
}
static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
@@ -322,6 +372,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
sel = ACPI_GED_PWR_DOWN_EVT;
} else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) {
sel = ACPI_GED_NVDIMM_HOTPLUG_EVT;
+ } else if (ev & ACPI_CPU_POWERSTATE_STATUS) {
+ sel = ACPI_GED_CPU_POWERSTATE_EVT;
} else if (ev & ACPI_CPU_HOTPLUG_STATUS) {
sel = ACPI_GED_CPU_HOTPLUG_EVT;
} else if (ev & ACPI_PCI_HOTPLUG_STATUS) {
@@ -379,6 +431,24 @@ static const VMStateDescription vmstate_cpuhp_state = {
}
};
+static bool cpuospm_needed(void *opaque)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+
+ return mc->has_online_capable_cpus;
+}
+
+static const VMStateDescription vmstate_cpuospm_state = {
+ .name = "acpi-ged/cpu-ospm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = cpuospm_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_CPU_OSPM_STATE(cpuospm_state, AcpiGedState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_ged_state = {
.name = "acpi-ged-state",
.version_id = 1,
@@ -447,6 +517,7 @@ static const VMStateDescription vmstate_acpi_ged = {
.subsections = (const VMStateDescription * const []) {
&vmstate_memhp_state,
&vmstate_cpuhp_state,
+ &vmstate_cpuospm_state,
&vmstate_ghes_state,
&vmstate_pcihp_state,
NULL
@@ -461,6 +532,8 @@ static void acpi_ged_realize(DeviceState *dev, Error **errp)
uint32_t ged_events;
int i;
+ s->cpuospm_state.acpi_dev = dev;
+
if (pcihp_state->use_acpi_hotplug_bridge) {
s->ged_event_bitmap |= ACPI_GED_PCI_HOTPLUG_EVT;
}
@@ -474,6 +547,18 @@ static void acpi_ged_realize(DeviceState *dev, Error **errp)
}
switch (event) {
+ case ACPI_GED_CPU_POWERSTATE_EVT:
+ /* initialize regions related to CPU OSPM interface to be used
+ * during notification of the power-on,off events to the OSPM
+ */
+ memory_region_init(&s->container_cpuospm, OBJECT(dev),
+ ACPI_CPUOSPM_REGION_NAME,
+ ACPI_CPU_OSPM_IF_REG_LEN);
+ sysbus_init_mmio(sbd, &s->container_cpuospm);
+ acpi_cpu_ospm_state_interface_init(&s->container_cpuospm,
+ OBJECT(dev),
+ &s->cpuospm_state, 0);
+ break;
case ACPI_GED_CPU_HOTPLUG_EVT:
/* initialize CPU Hotplug related regions */
memory_region_init(&s->container_cpuhp, OBJECT(dev),
@@ -544,6 +629,7 @@ static void acpi_ged_class_init(ObjectClass *class, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(class);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
+ PowerStateHandlerClass *pshc = POWERSTATE_HANDLER_CLASS(class);
AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
ResettableClass *rc = RESETTABLE_CLASS(class);
AcpiGedClass *gedc = ACPI_GED_CLASS(class);
@@ -560,6 +646,10 @@ static void acpi_ged_class_init(ObjectClass *class, const void *data)
resettable_class_set_parent_phases(rc, NULL, ged_reset_hold, NULL,
&gedc->parent_phases);
+ pshc->pre_poweron = acpi_ged_pre_poweron_cb;
+ pshc->request_poweroff = acpi_ged_request_poweroff_cb;
+ pshc->post_poweroff = acpi_ged_post_poweroff_cb;
+
adevc->ospm_status = acpi_ged_ospm_status;
adevc->send_event = acpi_ged_send_event;
}
@@ -573,6 +663,7 @@ static const TypeInfo acpi_ged_info = {
.class_size = sizeof(AcpiGedClass),
.interfaces = (const InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
+ { TYPE_POWERSTATE_HANDLER },
{ TYPE_ACPI_DEVICE_IF },
{ }
}
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 3980f553db..8d498708ab 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -188,6 +188,7 @@ static const MemMapEntry base_memmap[] = {
[VIRT_PVTIME] = { 0x090a0000, 0x00010000 },
[VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 },
[VIRT_ACPI_PCIHP] = { 0x090c0000, ACPI_PCIHP_SIZE },
+ [VIRT_ACPI_CPUPS] = { 0x090d0000, ACPI_CPU_OSPM_IF_REG_LEN },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
@@ -688,9 +689,10 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
{
DeviceState *dev;
MachineState *ms = MACHINE(vms);
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
SysBusDevice *sbdev;
int irq = vms->irqmap[VIRT_ACPI_GED];
- uint32_t event = ACPI_GED_PWR_DOWN_EVT;
+ uint32_t event = ACPI_GED_PWR_DOWN_EVT | ACPI_GED_CPU_POWERSTATE_EVT;
bool acpi_pcihp;
if (ms->ram_slots) {
@@ -711,6 +713,11 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
sysbus_mmio_map_name(sbdev, ACPI_MEMHP_REGION_NAME,
vms->memmap[VIRT_PCDIMM_ACPI].base);
+ if (mc->has_online_capable_cpus) {
+ sysbus_mmio_map_name(sbdev, ACPI_CPUOSPM_REGION_NAME,
+ vms->memmap[VIRT_ACPI_CPUPS].base);
+ }
+
acpi_pcihp = object_property_get_bool(OBJECT(dev),
ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, NULL);
diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
index 68d9d15f50..eea03ca47d 100644
--- a/include/hw/acpi/acpi_dev_interface.h
+++ b/include/hw/acpi/acpi_dev_interface.h
@@ -13,6 +13,7 @@ typedef enum {
ACPI_NVDIMM_HOTPLUG_STATUS = 16,
ACPI_VMGENID_CHANGE_STATUS = 32,
ACPI_POWER_DOWN_STATUS = 64,
+ ACPI_CPU_POWERSTATE_STATUS = 128,
} AcpiEventStatusBits;
#define TYPE_ACPI_DEVICE_IF "acpi-device-interface"
diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
index 2c5b055327..87e4e5e6ce 100644
--- a/include/hw/acpi/generic_event_device.h
+++ b/include/hw/acpi/generic_event_device.h
@@ -64,6 +64,7 @@
#include "hw/acpi/ghes.h"
#include "hw/acpi/cpu.h"
#include "hw/acpi/pcihp.h"
+#include "hw/acpi/cpu_ospm_interface.h"
#include "qom/object.h"
#define ACPI_POWER_BUTTON_DEVICE "PWRB"
@@ -92,6 +93,7 @@ OBJECT_DECLARE_TYPE(AcpiGedState, AcpiGedClass, ACPI_GED)
#define AML_GED_EVT_REG "EREG"
#define AML_GED_EVT_SEL "ESEL"
#define AML_GED_EVT_CPU_SCAN_METHOD "\\_SB.GED.CSCN"
+#define AML_GED_EVT_CPUPS_SCAN_METHOD "\\_SB.GED.PSCN" /* Power State Scan */
/*
* Platforms need to specify the GED event bitmap
@@ -103,6 +105,7 @@ OBJECT_DECLARE_TYPE(AcpiGedState, AcpiGedClass, ACPI_GED)
#define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4
#define ACPI_GED_CPU_HOTPLUG_EVT 0x8
#define ACPI_GED_PCI_HOTPLUG_EVT 0x10
+#define ACPI_GED_CPU_POWERSTATE_EVT 0x20
typedef struct GEDState {
MemoryRegion evt;
@@ -112,6 +115,7 @@ typedef struct GEDState {
#define ACPI_PCIHP_REGION_NAME "pcihp container"
#define ACPI_MEMHP_REGION_NAME "memhp container"
+#define ACPI_CPUOSPM_REGION_NAME "cpuospm container"
struct AcpiGedState {
SysBusDevice parent_obj;
@@ -121,6 +125,8 @@ struct AcpiGedState {
MemoryRegion container_cpuhp;
AcpiPciHpState pcihp_state;
MemoryRegion container_pcihp;
+ AcpiCpuOspmState cpuospm_state;
+ MemoryRegion container_cpuospm;
GEDState ged_state;
uint32_t ged_event_bitmap;
qemu_irq irq;
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 02cc311452..68081b79bb 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -81,6 +81,7 @@ enum {
VIRT_NVDIMM_ACPI,
VIRT_PVTIME,
VIRT_ACPI_PCIHP,
+ VIRT_ACPI_CPUPS,
VIRT_LOWMEMMAP_LAST,
};
--
2.34.1