diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 401ac0dab241..c13b65c2c911 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -373,6 +373,15 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(field, aml_named_field(CPU_DATA, 32)); aml_append(cpu_ctrl_dev, field); + if (opts.has_legacy_cphp) { + method = aml_method("_INI", 0, AML_SERIALIZED); + /* switch off legacy CPU hotplug HW and use new one, + * on reboot system is in new mode and writing 0 + * in CPU_SELECTOR selects BSP, which is NOP at + * the time _INI is called */ + aml_append(method, aml_store(zero, aml_name(CPU_SELECTOR))); + aml_append(cpu_ctrl_dev, method); + } } aml_append(sb_scope, cpu_ctrl_dev); diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c index fe75bd9ac9ee..e19d90206376 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -34,7 +34,15 @@ static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size) static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { - /* TODO: implement VCPU removal on guest signal that CPU can be removed */ + /* firmware never used to write in CPU present bitmap so use + this fact as means to switch QEMU into modern CPU hotplug + mode by writing 0 at the beginning of legacy CPU bitmap + */ + if (addr == 0 && data == 0) { + AcpiCpuHotplug *cpus = opaque; + object_property_set_bool(cpus->device, false, "cpu-hotplug-legacy", + &error_abort); + } } static const MemoryRegionOps AcpiCpuHotplug_ops = { @@ -83,6 +91,17 @@ void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops, gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN); memory_region_add_subregion(parent, base, &gpe_cpu->io); + gpe_cpu->device = owner; +} + +void acpi_switch_to_modern_cphp(AcpiCpuHotplug *gpe_cpu, + CPUHotplugState *cpuhp_state, + uint16_t io_port) +{ + MemoryRegion *parent = pci_address_space_io(PCI_DEVICE(gpe_cpu->device)); + + memory_region_del_subregion(parent, &gpe_cpu->io); + cpu_hotplug_hw_init(parent, gpe_cpu->device, cpuhp_state, io_port); } void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index d12cc6238137..e5a3c18e52d7 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -189,6 +189,33 @@ static const VMStateDescription vmstate_tco_io_state = { } }; +static bool vmstate_test_use_cpuhp(void *opaque) +{ + ICH9LPCPMRegs *s = opaque; + return !s->cpu_hotplug_legacy; +} + +static int vmstate_cpuhp_pre_load(void *opaque) +{ + ICH9LPCPMRegs *s = opaque; + Object *obj = OBJECT(s->gpe_cpu.device); + object_property_set_bool(obj, false, "cpu-hotplug-legacy", &error_abort); + return 0; +} + +static const VMStateDescription vmstate_cpuhp_state = { + .name = "ich9_pm/cpuhp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .needed = vmstate_test_use_cpuhp, + .pre_load = vmstate_cpuhp_pre_load, + .fields = (VMStateField[]) { + VMSTATE_CPU_HOTPLUG(cpuhp_state, ICH9LPCPMRegs), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_ich9_pm = { .name = "ich9_pm", .version_id = 1, @@ -209,6 +236,7 @@ const VMStateDescription vmstate_ich9_pm = { .subsections = (const VMStateDescription*[]) { &vmstate_memhp_state, &vmstate_tco_io_state, + &vmstate_cpuhp_state, NULL } }; @@ -318,6 +346,11 @@ static void ich9_pm_set_cpu_hotplug_legacy(Object *obj, bool value, { ICH9LPCState *s = ICH9_LPC_DEVICE(obj); + assert(!value); + if (s->pm.cpu_hotplug_legacy && value == false) { + acpi_switch_to_modern_cphp(&s->pm.gpe_cpu, &s->pm.cpuhp_state, + ICH9_CPU_HOTPLUG_IO_BASE); + } s->pm.cpu_hotplug_legacy = value; } diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 858244a6612f..2adc246b0044 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -276,6 +276,32 @@ static const VMStateDescription vmstate_memhp_state = { } }; +static bool vmstate_test_use_cpuhp(void *opaque) +{ + PIIX4PMState *s = opaque; + return !s->cpu_hotplug_legacy; +} + +static int vmstate_cpuhp_pre_load(void *opaque) +{ + Object *obj = OBJECT(opaque); + object_property_set_bool(obj, false, "cpu-hotplug-legacy", &error_abort); + return 0; +} + +static const VMStateDescription vmstate_cpuhp_state = { + .name = "piix4_pm/cpuhp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .needed = vmstate_test_use_cpuhp, + .pre_load = vmstate_cpuhp_pre_load, + .fields = (VMStateField[]) { + VMSTATE_CPU_HOTPLUG(cpuhp_state, PIIX4PMState), + VMSTATE_END_OF_LIST() + } +}; + /* qemu-kvm 1.2 uses version 3 but advertised as 2 * To support incoming qemu-kvm 1.2 migration, change version_id * and minimum_version_id to 2 below (which breaks migration from @@ -310,6 +336,7 @@ static const VMStateDescription vmstate_acpi = { }, .subsections = (const VMStateDescription*[]) { &vmstate_memhp_state, + &vmstate_cpuhp_state, NULL } }; @@ -585,6 +612,11 @@ static void piix4_set_cpu_hotplug_legacy(Object *obj, bool value, Error **errp) { PIIX4PMState *s = PIIX4_PM(obj); + assert(!value); + if (s->cpu_hotplug_legacy && value == false) { + acpi_switch_to_modern_cphp(&s->gpe_cpu, &s->cpuhp_state, + PIIX4_CPU_HOTPLUG_IO_BASE); + } s->cpu_hotplug_legacy = value; } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index e35a446a9d31..20e5b4948ece 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -33,6 +33,7 @@ #include "hw/timer/hpet.h" #include "hw/acpi/acpi-defs.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/cpu.h" #include "hw/nvram/fw_cfg.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/loader.h" @@ -1895,6 +1896,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free); PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine); uint32_t nr_mem = machine->ram_slots; int root_bus_limit = 0xFF; PCIBus *bus = NULL; @@ -1950,7 +1952,15 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, build_q35_pci0_int(dsdt); } - build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base); + if (pcmc->legacy_cpu_hotplug) { + build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base); + } else { + CPUHotplugFeatures opts = { + .apci_1_compatible = true, .has_legacy_cphp = true + }; + build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base, + "\\_SB.PCI0", "\\_GPE._E02"); + } build_memory_hotplug_aml(dsdt, nr_mem, pm->mem_hp_io_base, pm->mem_hp_io_len); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 53bc968bd071..c7d70af2536c 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -445,9 +445,11 @@ DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", NULL, static void pc_i440fx_2_6_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_7_machine_options(m); m->is_default = 0; m->alias = NULL; + pcmc->legacy_cpu_hotplug = true; SET_MACHINE_COMPAT(m, PC_COMPAT_2_6); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index e4b541f7b2ef..97a8835eea22 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -294,8 +294,10 @@ DEFINE_Q35_MACHINE(v2_7, "pc-q35-2.7", NULL, static void pc_q35_2_6_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_2_7_machine_options(m); m->alias = NULL; + pcmc->legacy_cpu_hotplug = true; SET_MACHINE_COMPAT(m, PC_COMPAT_2_6); } diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index 980a83c4b87e..89ce172941b8 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -49,6 +49,7 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, typedef struct CPUHotplugFeatures { bool apci_1_compatible; + bool has_legacy_cphp; } CPUHotplugFeatures; void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, diff --git a/include/hw/acpi/cpu_hotplug.h b/include/hw/acpi/cpu_hotplug.h index 6fef67ec1449..b995ef2ebd16 100644 --- a/include/hw/acpi/cpu_hotplug.h +++ b/include/hw/acpi/cpu_hotplug.h @@ -16,8 +16,10 @@ #include "hw/acpi/pc-hotplug.h" #include "hw/acpi/aml-build.h" #include "hw/hotplug.h" +#include "hw/acpi/cpu.h" typedef struct AcpiCpuHotplug { + Object *device; MemoryRegion io; uint8_t sts[ACPI_GPE_PROC_LEN]; } AcpiCpuHotplug; @@ -28,6 +30,10 @@ void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, AcpiCpuHotplug *gpe_cpu, uint16_t base); +void acpi_switch_to_modern_cphp(AcpiCpuHotplug *gpe_cpu, + CPUHotplugState *cpuhp_state, + uint16_t io_port); + void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, uint16_t io_base); #endif diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 9e239297e862..884224ed1960 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -137,6 +137,8 @@ struct PCMachineClass { /* TSC rate migration: */ bool save_tsc_khz; + /* generate legacy CPU hotplug AML */ + bool legacy_cpu_hotplug; }; #define TYPE_PC_MACHINE "generic-pc-machine"