From 0f254f70d4fcf668b27bd7c5970761afe13589c7 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 30 Mar 2026 13:30:08 +0800 Subject: [PATCH] hw/acpi: Do not save/load cpuhp state unconditionally Commit 7aa563630b6b ("pc: Start with modern CPU hotplug interface by default") removed the .needed callback (vmstate_test_use_cpuhp) from vmstate_cpuhp_state in both piix4.c and ich9.c. However, PIIX4 is also used by non-PC boards - MIPS Malta, which does not select CONFIG_ACPI_CPU_HOTPLUG. For MIPS Malta, the linker resolves vmstate_cpu_hotplug to the stub one in acpi-cpu-hotplug-stub.c, which is a zero-initialized VMStateDescription with .fields == NULL. Before commit 7aa563630b6b, .needed() of PIIX4's vmstate_cpuhp_state returned false for MIPS Malta since PIIX4PMState always initialized the field cpu_hotplug_legacy as true. Malta implicitly relies on this initial value to bypass vmstate_cpuhp_state. However, this is unstable because Malta itself does not support CPU hotplugging, whether via the legacy way or the modern way. Commit 7aa563630b6b removed .needed() check for vmstate_cpuhp_state, this broke the existing dependency that Malta had relied on, forcing Malta to save and load vmstate_cpuhp_state during the save/load process, which in turn caused a segmentation fault due to NULL fields in the stub-compiled code. Fix this by bringing back the .needed = cpuhp_needed callback for vmstate_cpuhp_state of PIIX4, that checks MachineClass::has_hotpluggable_cpus. Boards that do not support CPU hotplug (only MIPS Malta) will skip this subsection entirely, which is both correct and consistent with the previous behavior. At the same time, add a similar .needed() check to ICH9. Although no boards with ICH9 are affected by this issue, this helps avoid potential issues in the future. Reproducer (MIPS Malta): $ qemu-img create -f qcow2 dummy.qcow2 32M $ qemu-system-mipsel -nographic \ -drive if=none,format=qcow2,file=dummy.qcow2 [Type "C-a c" to get the "(qemu)" monitor prompt)] (qemu) savevm foo # segfault Reported-by: Peter Maydell Fixes: 7aa563630b6b ("pc: Start with modern CPU hotplug interface by default") Signed-off-by: Zhao Liu Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3360 Tested-by: Peter Maydell Link: https://lore.kernel.org/r/20260330053008.2721532-1-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- hw/acpi/ich9.c | 8 ++++++++ hw/acpi/piix4.c | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index bbb1bd60a2..5c7dfb2c69 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -184,10 +184,18 @@ static const VMStateDescription vmstate_tco_io_state = { } }; +static bool cpuhp_needed(void *opaque) +{ + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + + return mc->has_hotpluggable_cpus; +} + static const VMStateDescription vmstate_cpuhp_state = { .name = "ich9_pm/cpuhp", .version_id = 1, .minimum_version_id = 1, + .needed = cpuhp_needed, .fields = (const VMStateField[]) { VMSTATE_CPU_HOTPLUG(cpuhp_state, ICH9LPCPMRegs), VMSTATE_END_OF_LIST() diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 43860d1227..9b7f50c7af 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -195,10 +195,18 @@ static const VMStateDescription vmstate_memhp_state = { } }; +static bool cpuhp_needed(void *opaque) +{ + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + + return mc->has_hotpluggable_cpus; +} + static const VMStateDescription vmstate_cpuhp_state = { .name = "piix4_pm/cpuhp", .version_id = 1, .minimum_version_id = 1, + .needed = cpuhp_needed, .fields = (const VMStateField[]) { VMSTATE_CPU_HOTPLUG(cpuhp_state, PIIX4PMState), VMSTATE_END_OF_LIST()