diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 8a98446b7c74131dcaeb14bad67b30b8378c1c23..f2ce5cd45a2ca984473cb40c26ada71e7591bb04 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -433,6 +433,29 @@ void kvm_destroy_vcpu(CPUState *cpu) } } +int kvm_create_parked_vcpu(unsigned long vcpu_id) +{ + KVMState *s = kvm_state; + struct KVMParkedVcpu *vcpu = NULL; + int ret; + + DPRINTF("kvm_create_parked_vcpu\n"); + + ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id); + if (ret < 0) { + DPRINTF("kvm_create_vcpu failed\n"); + goto err; + } + + vcpu = g_malloc0(sizeof(*vcpu)); + vcpu->vcpu_id = vcpu_id; + vcpu->kvm_fd = ret; + QLIST_INSERT_HEAD(&s->kvm_parked_vcpus, vcpu, node); + +err: + return ret; +} + static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id) { struct KVMParkedVcpu *cpu; diff --git a/docs/specs/acpi_hw_reduced_hotplug.rst b/docs/specs/acpi_hw_reduced_hotplug.rst index 0bd3f9399fee04bc7829f6f5936a398720dc7eeb..3acd6fcd8b8fd84492391f702643b29af935b2c7 100644 --- a/docs/specs/acpi_hw_reduced_hotplug.rst +++ b/docs/specs/acpi_hw_reduced_hotplug.rst @@ -64,7 +64,8 @@ GED IO interface (4 byte access) 0: Memory hotplug event 1: System power down event 2: NVDIMM hotplug event - 3-31: Reserved + 3: CPU hotplug event + 4-31: Reserved **write_access:** diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index b20903ea303f27130fb3d570278ddf1322ae5a2c..f9ce0a7f41861aae1f6ac02f759ceaf49b643ff7 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -6,7 +6,6 @@ #include "trace.h" #include "sysemu/numa.h" -#define ACPI_CPU_HOTPLUG_REG_LEN 12 #define ACPI_CPU_SELECTOR_OFFSET_WR 0 #define ACPI_CPU_FLAGS_OFFSET_RW 4 #define ACPI_CPU_CMD_OFFSET_WR 5 @@ -343,7 +342,8 @@ const VMStateDescription vmstate_cpu_hotplug = { void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, hwaddr io_base, const char *res_root, - const char *event_handler_method) + const char *event_handler_method, + AmlRegionSpace rs) { Aml *ifctx; Aml *field; @@ -371,13 +371,18 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0)); crs = aml_resource_template(); - aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1, - ACPI_CPU_HOTPLUG_REG_LEN)); + if (rs == AML_SYSTEM_IO) { + aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1, + ACPI_CPU_HOTPLUG_REG_LEN)); + } else { + aml_append(crs, aml_memory32_fixed(io_base, + ACPI_CPU_HOTPLUG_REG_LEN, AML_READ_WRITE)); + } aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs)); /* declare CPU hotplug MMIO region with related access fields */ aml_append(cpu_ctrl_dev, - aml_operation_region("PRST", AML_SYSTEM_IO, aml_int(io_base), + aml_operation_region("PRST", rs, aml_int(io_base), ACPI_CPU_HOTPLUG_REG_LEN)); field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, @@ -663,6 +668,11 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(dev, aml_name_decl("_UID", uid)); } + assert(adevc); + if (adevc->cpu_cppc) { + adevc->cpu_cppc(adev, i, arch_ids->len, dev); + } + method = aml_method("_STA", 0, AML_SERIALIZED); aml_append(method, aml_return(aml_call1(CPU_STS_METHOD, uid))); aml_append(dev, method); @@ -703,9 +713,11 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(sb_scope, cpus_dev); aml_append(table, sb_scope); - method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED); - aml_append(method, aml_call0("\\_SB.CPUS." CPU_SCAN_METHOD)); - aml_append(table, method); + if (event_handler_method) { + method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED); + aml_append(method, aml_call0("\\_SB.CPUS." CPU_SCAN_METHOD)); + aml_append(table, method); + } g_free(cphp_res_path); } diff --git a/hw/acpi/cpufreq.c b/hw/acpi/cpufreq.c index a84db490b35715929c345e0c58ec0a83fdda90ca..a76f7b8fa2c86e790f6e66d73093e388e9bd8a21 100644 --- a/hw/acpi/cpufreq.c +++ b/hw/acpi/cpufreq.c @@ -83,6 +83,7 @@ typedef struct CpuhzState { uint32_t PerformanceLimited; uint32_t LowestFreq; uint32_t NominalFreq; + uint32_t num_cpu; uint32_t reg_size; } CpuhzState; @@ -93,10 +94,7 @@ static uint64_t cpufreq_read(void *opaque, hwaddr offset, unsigned size) uint64_t r; uint64_t n; - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int smp_cpus = ms->smp.cpus; - - if (offset >= smp_cpus * CPPC_REG_PER_CPU_STRIDE) { + if (offset >= s->num_cpu * CPPC_REG_PER_CPU_STRIDE) { warn_report("cpufreq_read: offset 0x%lx out of range", offset); return 0; } @@ -163,11 +161,10 @@ static uint64_t cpufreq_read(void *opaque, hwaddr offset, unsigned size) static void cpufreq_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { + CpuhzState *s = CPUFREQ(opaque); uint64_t n; - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int smp_cpus = ms->smp.cpus; - if (offset >= smp_cpus * CPPC_REG_PER_CPU_STRIDE) { + if (offset >= s->num_cpu * CPPC_REG_PER_CPU_STRIDE) { error_printf("cpufreq_write: offset 0x%lx out of range", offset); return; } @@ -248,9 +245,9 @@ static void cpufreq_init(Object *obj) CpuhzState *s = CPUFREQ(obj); MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int smp_cpus = ms->smp.cpus; + s->num_cpu = ms->smp.max_cpus; - s->reg_size = smp_cpus * CPPC_REG_PER_CPU_STRIDE; + s->reg_size = s->num_cpu * CPPC_REG_PER_CPU_STRIDE; if (s->reg_size > MAX_SUPPORT_SPACE) { error_report("Required space 0x%x excesses the max support 0x%x", s->reg_size, MAX_SUPPORT_SPACE); diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index e28457a7d103f58ce8f12b38b55af2a266fb1205..042a8ef8a5668de998eef575fa3bdab9028fb2a9 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -25,6 +25,7 @@ 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_HOTPLUG_EVT, }; /* @@ -117,6 +118,9 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, aml_notify(aml_name("\\_SB.NVDR"), aml_int(0x80))); break; + case ACPI_GED_CPU_HOTPLUG_EVT: + aml_append(if_ctx, aml_call0("\\_SB.CPUS.CSCN")); + break; default: /* * Please make sure all the events in ged_supported_events[] @@ -234,6 +238,8 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, } else { acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp); } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp); } else { error_setg(errp, "virt: device plug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -279,6 +285,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_HOTPLUG_STATUS) { + sel = ACPI_GED_CPU_HOTPLUG_EVT; } else { /* Unknown event. Return without generating interrupt. */ warn_report("GED: Unsupported event %d. No irq injected", ev); @@ -311,6 +319,16 @@ static const VMStateDescription vmstate_memhp_state = { } }; +static const VMStateDescription vmstate_cpuhp_state = { + .name = "acpi-ged/cpuhp", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_CPU_HOTPLUG(cpuhp_state, AcpiGedState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_ged_state = { .name = "acpi-ged-state", .version_id = 1, @@ -360,6 +378,7 @@ static const VMStateDescription vmstate_acpi_ged = { .subsections = (const VMStateDescription * []) { &vmstate_memhp_state, &vmstate_ghes_state, + &vmstate_cpuhp_state, NULL } }; @@ -370,6 +389,7 @@ static void acpi_ged_initfn(Object *obj) AcpiGedState *s = ACPI_GED(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); GEDState *ged_st = &s->ged_state; + MachineClass *mc; memory_region_init_io(&ged_st->evt, obj, &ged_evt_ops, ged_st, TYPE_ACPI_GED, ACPI_GED_EVT_SEL_LEN); @@ -393,6 +413,21 @@ static void acpi_ged_initfn(Object *obj) memory_region_init_io(&ged_st->regs, obj, &ged_regs_ops, ged_st, TYPE_ACPI_GED "-regs", ACPI_GED_REG_COUNT); sysbus_init_mmio(sbd, &ged_st->regs); + + mc = MACHINE_GET_CLASS(qdev_get_machine()); + if (!mc->possible_cpu_arch_ids) { + /* + * MachineClass should support possible_cpu_arch_ids in + * cpu_hotplug_hw_init below. + */ + return; + } + + memory_region_init(&s->container_cpuhp, OBJECT(dev), "cpuhp container", + ACPI_CPU_HOTPLUG_REG_LEN); + sysbus_init_mmio(sbd, &s->container_cpuhp); + cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev), + &s->cpuhp_state, 0); } static void acpi_ged_class_init(ObjectClass *class, void *data) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 2d37d29f02b409c00f27b8ec9da7ad6b9894fc5f..006a4b4c4b709a3ff2b984a7b531c749b2ceb53e 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -27,6 +27,7 @@ config ARM_VIRT select DIMM select ACPI_HW_REDUCED select ACPI_APEI + select ACPI_CPU_HOTPLUG config CHEETAH bool diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 21024f79991bf1b35ea8f6788485b469568f006f..3d45de1772ac2fc5f48971c1d520b3bf81b2944b 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -814,6 +814,24 @@ static void do_cpu_reset(void *opaque) } } +void cpu_hotplug_register_reset(int ncpu) +{ + CPUState *cpu_0 = qemu_get_cpu(0); + CPUState *cpu = qemu_get_cpu(ncpu); + QEMUResetEntry *entry = qemu_get_reset_entry(do_cpu_reset, cpu_0); + + assert(entry); + /* Gather the reset handlers of all CPUs */ + qemu_register_reset_after(entry, do_cpu_reset, cpu); +} + +void cpu_hotplug_reset_manually(int ncpu) +{ + CPUState *cpu = qemu_get_cpu(ncpu); + + do_cpu_reset(cpu); +} + /** * load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified * by key. diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 1ca705654bd6d6fcca47e761105683501490bb09..1101161d70f86025ec1d32c57b7f38b358cdf0ab 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -120,8 +120,24 @@ static void acpi_dsdt_add_cppc(Aml *dev, uint64_t cpu_base, int *regs_offset) aml_append(dev, aml_name_decl("_CPC", cpc)); } -static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms, - const MemMapEntry *cppc_memmap) +void virt_acpi_dsdt_cpu_cppc(AcpiDeviceIf *adev, int ncpu, int num_cpu, Aml *dev) +{ + VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); + const MemMapEntry *cppc_memmap = &vms->memmap[VIRT_CPUFREQ]; + + /* + * Append _CPC and _PSD to support CPU frequence show + * Check CPPC available by DESIRED_PERF register + */ + if (cppc_regs_offset[DESIRED_PERF] != -1) { + acpi_dsdt_add_cppc(dev, + cppc_memmap->base + ncpu * CPPC_REG_PER_CPU_STRIDE, + cppc_regs_offset); + acpi_dsdt_add_psd(dev, num_cpu); + } +} + +static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms) { MachineState *ms = MACHINE(vms); uint16_t i; @@ -131,16 +147,7 @@ static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms, aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007"))); aml_append(dev, aml_name_decl("_UID", aml_int(i))); - /* - * Append _CPC and _PSD to support CPU frequence show - * Check CPPC available by DESIRED_PERF register - */ - if (cppc_regs_offset[DESIRED_PERF] != -1) { - acpi_dsdt_add_cppc(dev, - cppc_memmap->base + i * CPPC_REG_PER_CPU_STRIDE, - cppc_regs_offset); - acpi_dsdt_add_psd(dev, ms->smp.cpus); - } + virt_acpi_dsdt_cpu_cppc(NULL, i, ms->smp.cpus, dev); aml_append(scope, dev); } @@ -771,14 +778,69 @@ static void build_append_gicr(GArray *table_data, uint64_t base, uint32_t size) build_append_int_noprefix(table_data, size, 4); /* Discovery Range Length */ } +void virt_madt_cpu_entry(AcpiDeviceIf *adev, int i, + const CPUArchIdList *possible_cpus, GArray *table_data, + bool force_enabled) +{ + VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); + const MemMapEntry *memmap = vms->memmap; + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); + uint64_t physical_base_address = 0, gich = 0, gicv = 0; + uint32_t vgic_interrupt = vms->virt ? PPI(ARCH_GIC_MAINT_IRQ) : 0; + uint32_t pmu_interrupt, enabled; + static bool pmu; + + if (i == 0) { + pmu = arm_feature(&armcpu->env, ARM_FEATURE_PMU); + } + /* FEATURE_PMU should be all enabled or disabled for CPUs */ + assert(!armcpu || arm_feature(&armcpu->env, ARM_FEATURE_PMU) == pmu); + pmu_interrupt = pmu ? PPI(VIRTUAL_PMU_IRQ) : 0; + enabled = armcpu || force_enabled ? 1 /* Enabled */ : 0 /* Disabled */; + + if (vms->gic_version == 2) { + physical_base_address = memmap[VIRT_GIC_CPU].base; + gicv = memmap[VIRT_GIC_VCPU].base; + gich = memmap[VIRT_GIC_HYP].base; + } + + /* 5.2.12.14 GIC Structure */ + build_append_int_noprefix(table_data, 0xB, 1); /* Type */ + build_append_int_noprefix(table_data, 76, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, i, 4); /* GIC ID */ + build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ + /* Flags */ + build_append_int_noprefix(table_data, enabled, 4); /* Enabled */ + /* Parking Protocol Version */ + build_append_int_noprefix(table_data, 0, 4); + /* Performance Interrupt GSIV */ + build_append_int_noprefix(table_data, pmu_interrupt, 4); + build_append_int_noprefix(table_data, 0, 8); /* Parked Address */ + /* Physical Base Address */ + build_append_int_noprefix(table_data, physical_base_address, 8); + build_append_int_noprefix(table_data, gicv, 8); /* GICV */ + build_append_int_noprefix(table_data, gich, 8); /* GICH */ + /* VGIC Maintenance interrupt */ + build_append_int_noprefix(table_data, vgic_interrupt, 4); + build_append_int_noprefix(table_data, 0, 8); /* GICR Base Address*/ + /* MPIDR */ + build_append_int_noprefix(table_data, possible_cpus->cpus[i].arch_id, 8); +} + static void build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { int i; VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); + MachineClass *mc = MACHINE_GET_CLASS(vms); + MachineState *ms = MACHINE(vms); + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); const MemMapEntry *memmap = vms->memmap; AcpiTable table = { .sig = "APIC", .rev = 3, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; + /* The MADT GICC numbers */ + int num_cpu = ms->smp.cpus; acpi_table_begin(&table, table_data); /* Local Interrupt Controller Address */ @@ -797,41 +859,11 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, vms->gic_version, 1); build_append_int_noprefix(table_data, 0, 3); /* Reserved */ - for (i = 0; i < MACHINE(vms)->smp.cpus; i++) { - ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); - uint64_t physical_base_address = 0, gich = 0, gicv = 0; - uint32_t vgic_interrupt = vms->virt ? PPI(ARCH_GIC_MAINT_IRQ) : 0; - uint32_t pmu_interrupt = arm_feature(&armcpu->env, ARM_FEATURE_PMU) ? - PPI(VIRTUAL_PMU_IRQ) : 0; - - if (vms->gic_version == 2) { - physical_base_address = memmap[VIRT_GIC_CPU].base; - gicv = memmap[VIRT_GIC_VCPU].base; - gich = memmap[VIRT_GIC_HYP].base; - } - - /* 5.2.12.14 GIC Structure */ - build_append_int_noprefix(table_data, 0xB, 1); /* Type */ - build_append_int_noprefix(table_data, 76, 1); /* Length */ - build_append_int_noprefix(table_data, 0, 2); /* Reserved */ - build_append_int_noprefix(table_data, i, 4); /* GIC ID */ - build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ - /* Flags */ - build_append_int_noprefix(table_data, 1, 4); /* Enabled */ - /* Parking Protocol Version */ - build_append_int_noprefix(table_data, 0, 4); - /* Performance Interrupt GSIV */ - build_append_int_noprefix(table_data, pmu_interrupt, 4); - build_append_int_noprefix(table_data, 0, 8); /* Parked Address */ - /* Physical Base Address */ - build_append_int_noprefix(table_data, physical_base_address, 8); - build_append_int_noprefix(table_data, gicv, 8); /* GICV */ - build_append_int_noprefix(table_data, gich, 8); /* GICH */ - /* VGIC Maintenance interrupt */ - build_append_int_noprefix(table_data, vgic_interrupt, 4); - build_append_int_noprefix(table_data, 0, 8); /* GICR Base Address*/ - /* MPIDR */ - build_append_int_noprefix(table_data, armcpu->mp_affinity, 8); + if (vms->cpu_hotplug_enabled) { + num_cpu = ms->smp.max_cpus; + } + for (i = 0; i < num_cpu; i++) { + virt_madt_cpu_entry(NULL, i, possible_cpus, table_data, false); } if (vms->gic_version == 3) { @@ -921,6 +953,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) const int *irqmap = vms->irqmap; AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; + bool cpu_aml_built = false; acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); @@ -931,7 +964,6 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) * the RTC ACPI device at all when using UEFI. */ scope = aml_scope("\\_SB"); - acpi_dsdt_add_cpus(scope, vms, &memmap[VIRT_CPUFREQ]); acpi_dsdt_add_uart(scope, &memmap[VIRT_UART], (irqmap[VIRT_UART] + ARM_SPI_BASE)); if (vmc->acpi_expose_flash) { @@ -961,6 +993,19 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) AML_SYSTEM_MEMORY, memmap[VIRT_PCDIMM_ACPI].base); } + + if (event & ACPI_GED_CPU_HOTPLUG_EVT) { + CPUHotplugFeatures opts = { + .acpi_1_compatible = false, .has_legacy_cphp = false + }; + build_cpus_aml(dsdt, ms, opts, memmap[VIRT_CPU_ACPI].base, + "\\_SB", NULL, AML_SYSTEM_MEMORY); + cpu_aml_built = true; + } + } + + if (!cpu_aml_built) { + acpi_dsdt_add_cpus(scope, vms); } acpi_dsdt_add_power_button(scope); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 47e98f09e83026b8bb313d06bafd439bdd4dfd55..b81d22d68f7e67b01eb92cb83bae8a605eb5189f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -39,6 +39,7 @@ #include "hw/sysbus.h" #include "hw/arm/boot.h" #include "hw/arm/primecell.h" +#include "hw/arm/topology.h" #include "hw/arm/virt.h" #include "hw/block/flash.h" #include "hw/vfio/vfio-calxeda-xgmac.h" @@ -51,6 +52,8 @@ #include "sysemu/tpm.h" #include "sysemu/kvm.h" #include "sysemu/hvf.h" +#include "sysemu/cpus.h" +#include "sysemu/hw_accel.h" #include "hw/loader.h" #include "qapi/error.h" #include "qemu/bitops.h" @@ -154,6 +157,7 @@ static const MemMapEntry base_memmap[] = { [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, + [VIRT_CPU_ACPI] = { 0x090c0000, ACPI_CPU_HOTPLUG_REG_LEN }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, [VIRT_CPUFREQ] = { 0x0b000000, 0x00010000 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ @@ -211,6 +215,10 @@ static const char *valid_cpus[] = { ARM_CPU_TYPE_NAME("max"), }; +static MemoryRegion *secure_sysmem; +static MemoryRegion *tag_sysmem; +static MemoryRegion *secure_tag_sysmem; + static bool cpu_type_valid(const char *cpu) { int i; @@ -684,6 +692,7 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms) static inline DeviceState *create_acpi_ged(VirtMachineState *vms) { DeviceState *dev; + AcpiDeviceIfClass *adevc; MachineState *ms = MACHINE(vms); int irq = vms->irqmap[VIRT_ACPI_GED]; uint32_t event = ACPI_GED_PWR_DOWN_EVT; @@ -696,11 +705,20 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) event |= ACPI_GED_NVDIMM_HOTPLUG_EVT; } + if (vms->cpu_hotplug_enabled) { + event |= ACPI_GED_CPU_HOTPLUG_EVT; + } + dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); + adevc = ACPI_DEVICE_IF_GET_CLASS(dev); + adevc->madt_cpu = virt_madt_cpu_entry; + adevc->cpu_cppc = virt_acpi_dsdt_cpu_cppc; + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 3, vms->memmap[VIRT_CPU_ACPI].base); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(vms->gic, irq)); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -756,6 +774,54 @@ static void create_v2m(VirtMachineState *vms) vms->msi_controller = VIRT_MSI_CTRL_GICV2M; } +static void connect_gic_cpu_irqs(VirtMachineState *vms, int i) +{ + DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); + SysBusDevice *gicbusdev = SYS_BUS_DEVICE(vms->gic); + int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; + int num_cpus = object_property_get_uint(OBJECT(vms->gic), "num-cpu", NULL); + int gic_type = vms->gic_version; + int irq; + /* Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs we use for the virt board. + */ + const int timer_irq[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + }; + + for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(vms->gic, + ppibase + timer_irq[irq])); + } + + if (gic_type == 3) { + qemu_irq irq = qdev_get_gpio_in(vms->gic, + ppibase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, irq); + } else if (vms->virt) { + qemu_irq irq = qdev_get_gpio_in(vms->gic, + ppibase + ARCH_GIC_MAINT_IRQ); + sysbus_connect_irq(gicbusdev, i + 4 * num_cpus, irq); + } + + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, + qdev_get_gpio_in(vms->gic, ppibase + + VIRTUAL_PMU_IRQ)); + + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicbusdev, i + num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicbusdev, i + 2 * num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicbusdev, i + 3 * num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); +} + static void create_gic(VirtMachineState *vms, MemoryRegion *mem) { MachineState *ms = MACHINE(vms); @@ -763,14 +829,22 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) SysBusDevice *gicbusdev; const char *gictype; int type = vms->gic_version, i; + /* The max number of CPUs suppored by GIC */ + unsigned int num_cpus = ms->smp.cpus; + /* The number of CPUs present before boot */ unsigned int smp_cpus = ms->smp.cpus; uint32_t nb_redist_regions = 0; + if (vms->cpu_hotplug_enabled) { + num_cpus = ms->smp.max_cpus; + } + assert(num_cpus >= smp_cpus); + gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); vms->gic = qdev_new(gictype); qdev_prop_set_uint32(vms->gic, "revision", type); - qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus); + qdev_prop_set_uint32(vms->gic, "num-cpu", num_cpus); /* Note that the num-irq property counts both internal and external * interrupts; there are always 32 of the former (mandated by GIC spec). */ @@ -782,7 +856,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) if (type == 3) { uint32_t redist0_capacity = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; - uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); + uint32_t redist0_count = MIN(num_cpus, redist0_capacity); nb_redist_regions = virt_gicv3_redist_region_count(vms); @@ -803,7 +877,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE; qdev_prop_set_uint32(vms->gic, "redist-region-count[1]", - MIN(smp_cpus - redist0_count, redist1_capacity)); + MIN(num_cpus - redist0_count, redist1_capacity)); } } else { if (!kvm_irqchip_in_kernel()) { @@ -830,50 +904,14 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) /* Wire the outputs from each CPU's generic timer and the GICv3 * maintenance interrupt signal to the appropriate GIC PPI inputs, - * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's + * inputs. + * + * The irqs of remaining CPUs (if we has) will be connected during + * hotplugging. */ for (i = 0; i < smp_cpus; i++) { - DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); - int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; - int irq; - /* Mapping from the output timer irq lines from the CPU to the - * GIC PPI inputs we use for the virt board. - */ - const int timer_irq[] = { - [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, - [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, - [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, - [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, - }; - - for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { - qdev_connect_gpio_out(cpudev, irq, - qdev_get_gpio_in(vms->gic, - ppibase + timer_irq[irq])); - } - - if (type == 3) { - qemu_irq irq = qdev_get_gpio_in(vms->gic, - ppibase + ARCH_GIC_MAINT_IRQ); - qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", - 0, irq); - } else if (vms->virt) { - qemu_irq irq = qdev_get_gpio_in(vms->gic, - ppibase + ARCH_GIC_MAINT_IRQ); - sysbus_connect_irq(gicbusdev, i + 4 * smp_cpus, irq); - } - - qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, - qdev_get_gpio_in(vms->gic, ppibase - + VIRTUAL_PMU_IRQ)); - - sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); - sysbus_connect_irq(gicbusdev, i + smp_cpus, - qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); - sysbus_connect_irq(gicbusdev, i + 2 * smp_cpus, - qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); - sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus, - qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + connect_gic_cpu_irqs(vms, i); } fdt_add_gic_node(vms); @@ -1975,18 +2013,17 @@ static void machvirt_init(MachineState *machine) { VirtMachineState *vms = VIRT_MACHINE(machine); VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine); + MachineState *ms = MACHINE(machine); MachineClass *mc = MACHINE_GET_CLASS(machine); const CPUArchIdList *possible_cpus; MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *secure_sysmem = NULL; - MemoryRegion *tag_sysmem = NULL; - MemoryRegion *secure_tag_sysmem = NULL; int n, virt_max_cpus; bool firmware_loaded; bool aarch64 = true; bool has_ged = !vmc->no_ged; unsigned int smp_cpus = machine->smp.cpus; unsigned int max_cpus = machine->smp.max_cpus; + ObjectClass *cpu_class; /* * In accelerated mode, the memory map is computed earlier in kvm_type() @@ -2076,111 +2113,34 @@ static void machvirt_init(MachineState *machine) create_fdt(vms); qemu_log("cpu init start\n"); + cpu_class = object_class_by_name(ms->cpu_type); + vms->cpu_hotplug_enabled = has_ged && firmware_loaded && + virt_is_acpi_enabled(vms) && vms->gic_version == 3 && + !!object_class_dynamic_cast(cpu_class, TYPE_AARCH64_CPU); + possible_cpus = mc->possible_cpu_arch_ids(machine); assert(possible_cpus->len == max_cpus); for (n = 0; n < possible_cpus->len; n++) { Object *cpuobj; CPUState *cs; + if (kvm_enabled() && vms->cpu_hotplug_enabled) { + if (kvm_create_parked_vcpu(n) < 0) { + error_report("mach-virt: Create KVM parked vCPU failed"); + exit(1); + } + } + if (n >= smp_cpus) { - break; + continue; } cpuobj = object_new(possible_cpus->cpus[n].type); - object_property_set_int(cpuobj, "mp-affinity", - possible_cpus->cpus[n].arch_id, NULL); + aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL); cs = CPU(cpuobj); cs->cpu_index = n; - numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj), - &error_fatal); - - aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL); - - if (!vms->secure) { - object_property_set_bool(cpuobj, "has_el3", false, NULL); - } - - if (!vms->virt && object_property_find(cpuobj, "has_el2")) { - object_property_set_bool(cpuobj, "has_el2", false, NULL); - } - - if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { - object_property_set_int(cpuobj, "psci-conduit", vms->psci_conduit, - NULL); - - /* Secondary CPUs start in PSCI powered-down state */ - if (n > 0) { - object_property_set_bool(cpuobj, "start-powered-off", true, - NULL); - } - } - - if (vmc->kvm_no_adjvtime && - object_property_find(cpuobj, "kvm-no-adjvtime")) { - object_property_set_bool(cpuobj, "kvm-no-adjvtime", true, NULL); - } - - if (vmc->no_kvm_steal_time && - object_property_find(cpuobj, "kvm-steal-time")) { - object_property_set_bool(cpuobj, "kvm-steal-time", false, NULL); - } - - if (vmc->no_pmu && object_property_find(cpuobj, "pmu")) { - object_property_set_bool(cpuobj, "pmu", false, NULL); - } - - if (object_property_find(cpuobj, "reset-cbar")) { - object_property_set_int(cpuobj, "reset-cbar", - vms->memmap[VIRT_CPUPERIPHS].base, - &error_abort); - } - - object_property_set_link(cpuobj, "memory", OBJECT(sysmem), - &error_abort); - if (vms->secure) { - object_property_set_link(cpuobj, "secure-memory", - OBJECT(secure_sysmem), &error_abort); - } - - if (vms->mte) { - /* Create the memory region only once, but link to all cpus. */ - if (!tag_sysmem) { - /* - * The property exists only if MemTag is supported. - * If it is, we must allocate the ram to back that up. - */ - if (!object_property_find(cpuobj, "tag-memory")) { - error_report("MTE requested, but not supported " - "by the guest CPU"); - exit(1); - } - - tag_sysmem = g_new(MemoryRegion, 1); - memory_region_init(tag_sysmem, OBJECT(machine), - "tag-memory", UINT64_MAX / 32); - - if (vms->secure) { - secure_tag_sysmem = g_new(MemoryRegion, 1); - memory_region_init(secure_tag_sysmem, OBJECT(machine), - "secure-tag-memory", UINT64_MAX / 32); - - /* As with ram, secure-tag takes precedence over tag. */ - memory_region_add_subregion_overlap(secure_tag_sysmem, 0, - tag_sysmem, -1); - } - } - - object_property_set_link(cpuobj, "tag-memory", OBJECT(tag_sysmem), - &error_abort); - if (vms->secure) { - object_property_set_link(cpuobj, "secure-tag-memory", - OBJECT(secure_tag_sysmem), - &error_abort); - } - } - qdev_realize(DEVICE(cpuobj), NULL, &error_fatal); object_unref(cpuobj); } @@ -2260,7 +2220,7 @@ static void machvirt_init(MachineState *machine) } vms->bootinfo.ram_size = machine->ram_size; - vms->bootinfo.nb_cpus = smp_cpus; + vms->bootinfo.nb_cpus = vms->cpu_hotplug_enabled ? max_cpus : smp_cpus; vms->bootinfo.board_id = -1; vms->bootinfo.loader_start = vms->memmap[VIRT_MEM].base; vms->bootinfo.get_dtb = machvirt_dtb; @@ -2513,6 +2473,7 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) int n; unsigned int max_cpus = ms->smp.max_cpus; VirtMachineState *vms = VIRT_MACHINE(ms); + ARMCPUTopoInfo topo; if (ms->possible_cpus) { assert(ms->possible_cpus->len == max_cpus); @@ -2524,10 +2485,19 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) ms->possible_cpus->len = max_cpus; for (n = 0; n < ms->possible_cpus->len; n++) { ms->possible_cpus->cpus[n].type = ms->cpu_type; + ms->possible_cpus->cpus[n].vcpus_count = 1; ms->possible_cpus->cpus[n].arch_id = virt_cpu_mp_affinity(vms, n); + + topo_ids_from_idx(n, ms->smp.clusters, ms->smp.cores, ms->smp.threads, &topo); + ms->possible_cpus->cpus[n].props.has_socket_id = true; + ms->possible_cpus->cpus[n].props.socket_id = topo.pkg_id; + ms->possible_cpus->cpus[n].props.has_cluster_id = true; + ms->possible_cpus->cpus[n].props.cluster_id = topo.cluster_id; + ms->possible_cpus->cpus[n].props.has_core_id = true; + ms->possible_cpus->cpus[n].props.core_id = topo.core_id; ms->possible_cpus->cpus[n].props.has_thread_id = true; - ms->possible_cpus->cpus[n].props.thread_id = n; + ms->possible_cpus->cpus[n].props.thread_id = topo.smt_id; } return ms->possible_cpus; } @@ -2575,6 +2545,286 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, dev, &error_abort); } +static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + ARMCPUTopoInfo topo; + Object *cpuobj = OBJECT(dev); + CPUState *cs = CPU(dev); + ARMCPU *cpu = ARM_CPU(dev); + MachineState *ms = MACHINE(hotplug_dev); + MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev); + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(hotplug_dev); + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); + const CPUArchId *cpu_slot = NULL; + MemoryRegion *sysmem = get_system_memory(); + int smp_clusters = ms->smp.clusters; + int smp_cores = ms->smp.cores; + int smp_threads = ms->smp.threads; + + if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { + error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", + ms->cpu_type); + return; + } + + /* if cpu idx is not set, set it based on socket/cluster/core/thread + * properties + */ + if (cs->cpu_index == UNASSIGNED_CPU_INDEX) { + int max_socket = ms->smp.max_cpus / smp_threads / smp_cores / smp_clusters; + if (cpu->socket_id < 0 || cpu->socket_id >= max_socket) { + error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", + cpu->socket_id, max_socket - 1); + return; + } + if (cpu->cluster_id < 0 || cpu->cluster_id >= smp_clusters) { + error_setg(errp, "Invalid CPU cluster-id: %u must be in range 0:%u", + cpu->cluster_id, smp_clusters - 1); + return; + } + if (cpu->core_id < 0 || cpu->core_id >= smp_cores) { + error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", + cpu->core_id, smp_cores - 1); + return; + } + if (cpu->thread_id < 0 || cpu->thread_id >= smp_threads) { + error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", + cpu->thread_id, smp_threads - 1); + return; + } + + topo.pkg_id = cpu->socket_id; + topo.cluster_id = cpu->cluster_id; + topo.core_id = cpu->core_id; + topo.smt_id = cpu->thread_id; + cs->cpu_index = idx_from_topo_ids(smp_clusters, smp_cores, smp_threads, &topo); + } + + /* Some hotplug capability checks */ + if (cs->cpu_index >= ms->smp.cpus) { + if (!vms->acpi_dev) { + error_setg(errp, "CPU cold/hot plug is disabled: " + "missing acpi device."); + return; + } + if (!vms->cpu_hotplug_enabled) { + error_setg(errp, "CPU cold/hot plug is disabled: " + "should use AArch64 CPU and GICv3."); + return; + } + } + + /* if 'address' properties socket-id/cluster-id/core-id/thread-id are not + * set, set them so that machine_query_hotpluggable_cpus would show correct + * values + */ + topo_ids_from_idx(cs->cpu_index, smp_clusters, smp_cores, smp_threads, &topo); + if (cpu->socket_id != -1 && cpu->socket_id != topo.pkg_id) { + error_setg(errp, "property socket-id: %u doesn't match set idx:" + " 0x%x (socket-id: %u)", cpu->socket_id, cs->cpu_index, topo.pkg_id); + return; + } + cpu->socket_id = topo.pkg_id; + + if (cpu->cluster_id != -1 && cpu->cluster_id != topo.cluster_id) { + error_setg(errp, "property cluster-id: %u doesn't match set idx:" + " 0x%x (cluster-id: %u)", cpu->cluster_id, cs->cpu_index, topo.cluster_id); + return; + } + cpu->cluster_id = topo.cluster_id; + + if (cpu->core_id != -1 && cpu->core_id != topo.core_id) { + error_setg(errp, "property core-id: %u doesn't match set idx:" + " 0x%x (core-id: %u)", cpu->core_id, cs->cpu_index, topo.core_id); + return; + } + cpu->core_id = topo.core_id; + + if (cpu->thread_id != -1 && cpu->thread_id != topo.smt_id) { + error_setg(errp, "property thread-id: %u doesn't match set idx:" + " 0x%x (thread-id: %u)", cpu->thread_id, cs->cpu_index, topo.smt_id); + return; + } + cpu->thread_id = topo.smt_id; + + /* Init some properties */ + + object_property_set_int(cpuobj, "mp-affinity", + possible_cpus->cpus[cs->cpu_index].arch_id, NULL); + + cpu_slot = &possible_cpus->cpus[cs->cpu_index]; + if (cpu_slot->cpu) { + error_setg(errp, "CPU[%d] with mp_affinity %" PRIu64 " exists", + cs->cpu_index, cpu->mp_affinity); + return; + } + + numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj), + &error_fatal); + + if (!vms->secure) { + object_property_set_bool(cpuobj, "has_el3", false, NULL); + } + + if (!vms->virt && object_property_find(cpuobj, "has_el2")) { + object_property_set_bool(cpuobj, "has_el2", false, NULL); + } + + if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { + object_property_set_int(cpuobj, "psci-conduit", vms->psci_conduit, + NULL); + + /* Secondary CPUs start in PSCI powered-down state */ + if (cs->cpu_index > 0) { + object_property_set_bool(cpuobj, "start-powered-off", true, + NULL); + } + } + + if (vmc->kvm_no_adjvtime && + object_property_find(cpuobj, "kvm-no-adjvtime")) { + object_property_set_bool(cpuobj, "kvm-no-adjvtime", true, NULL); + } + + if (vmc->no_kvm_steal_time && + object_property_find(cpuobj, "kvm-steal-time")) { + object_property_set_bool(cpuobj, "kvm-steal-time", false, NULL); + } + + if (vmc->no_pmu && object_property_find(cpuobj, "pmu")) { + object_property_set_bool(cpuobj, "pmu", false, NULL); + } + + if (object_property_find(cpuobj, "reset-cbar")) { + object_property_set_int(cpuobj, "reset-cbar", + vms->memmap[VIRT_CPUPERIPHS].base, + &error_abort); + } + + object_property_set_link(cpuobj, "memory", OBJECT(sysmem), + &error_abort); + if (vms->secure) { + object_property_set_link(cpuobj, "secure-memory", + OBJECT(secure_sysmem), &error_abort); + } + + if (vms->mte) { + /* Create the memory region only once, but link to all cpus. */ + if (!tag_sysmem) { + /* + * The property exists only if MemTag is supported. + * If it is, we must allocate the ram to back that up. + */ + if (!object_property_find(cpuobj, "tag-memory")) { + error_report("MTE requested, but not supported " + "by the guest CPU"); + exit(1); + } + + tag_sysmem = g_new(MemoryRegion, 1); + memory_region_init(tag_sysmem, OBJECT(ms), + "tag-memory", UINT64_MAX / 32); + + if (vms->secure) { + secure_tag_sysmem = g_new(MemoryRegion, 1); + memory_region_init(secure_tag_sysmem, OBJECT(ms), + "secure-tag-memory", UINT64_MAX / 32); + + /* As with ram, secure-tag takes precedence over tag. */ + memory_region_add_subregion_overlap(secure_tag_sysmem, 0, + tag_sysmem, -1); + } + } + + object_property_set_link(cpuobj, "tag-memory", OBJECT(tag_sysmem), + &error_abort); + if (vms->secure) { + object_property_set_link(cpuobj, "secure-tag-memory", + OBJECT(secure_tag_sysmem), + &error_abort); + } + } + + /* If we use KVM accel, we should pause all vcpus to + * allow hot access of vcpu registers. + */ + if (dev->hotplugged && kvm_enabled()) { + pause_all_vcpus(); + } +} + +static void virt_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *cpu_slot; + CPUState *cs = CPU(dev); + int ncpu = cs->cpu_index; + MachineState *ms = MACHINE(hotplug_dev); + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + bool pmu = object_property_get_bool(OBJECT(first_cpu), "pmu", NULL); + bool steal_time = object_property_get_bool(OBJECT(first_cpu), + "kvm-steal-time", NULL); + GICv3State *gicv3; + ARMGICv3CommonClass *agcc; + Error *local_err = NULL; + + /* For CPU that is cold/hot plugged */ + if (ncpu >= ms->smp.cpus) { + /* Realize GIC related parts of CPU */ + assert(vms->gic_version == 3); + gicv3 = ARM_GICV3_COMMON(vms->gic); + agcc = ARM_GICV3_COMMON_GET_CLASS(gicv3); + agcc->cpu_hotplug_realize(gicv3, ncpu); + connect_gic_cpu_irqs(vms, ncpu); + + /* Init PMU and steal_time part */ + if (kvm_enabled()) { + hwaddr pvtime_reg_base = vms->memmap[VIRT_PVTIME].base; + + if (pmu) { + assert(arm_feature(&ARM_CPU(cs)->env, ARM_FEATURE_PMU)); + if (kvm_irqchip_in_kernel()) { + kvm_arm_pmu_set_irq(cs, PPI(VIRTUAL_PMU_IRQ)); + } + kvm_arm_pmu_init(cs); + } + if (steal_time) { + kvm_arm_pvtime_init(cs, pvtime_reg_base + + ncpu * PVTIME_SIZE_PER_CPU); + } + } + + /* Register CPU reset and trigger it manually */ + cpu_synchronize_state(cs); + cpu_hotplug_register_reset(ncpu); + cpu_hotplug_reset_manually(ncpu); + cpu_synchronize_post_reset(cs); + } + + if (dev->hotplugged && kvm_enabled()) { + resume_all_vcpus(); + } + + if (vms->acpi_dev) { + hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &local_err); + if (local_err) { + goto out; + } + } + + vms->boot_cpus++; + if (vms->fw_cfg) { + fw_cfg_modify_i16(vms->fw_cfg, FW_CFG_NB_CPUS, vms->boot_cpus); + } + + cpu_slot = &ms->possible_cpus->cpus[ncpu]; + cpu_slot->cpu = OBJECT(dev); +out: + error_propagate(errp, local_err); +} + static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -2608,6 +2858,8 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, qdev_prop_set_uint32(dev, "len-reserved-regions", 1); qdev_prop_set_string(dev, "reserved-regions[0]", resv_prop_str); g_free(resv_prop_str); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + virt_cpu_pre_plug(hotplug_dev, dev, errp); } } @@ -2626,6 +2878,8 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, } if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { virt_memory_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + virt_cpu_plug(hotplug_dev, dev, errp); } if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { PCIDevice *pdev = PCI_DEVICE(dev); @@ -2706,7 +2960,8 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, MachineClass *mc = MACHINE_GET_CLASS(machine); if (device_is_dynamic_sysbus(mc, dev) || - (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) { + (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) || + (object_dynamic_cast(OBJECT(dev), TYPE_CPU))) { return HOTPLUG_HANDLER(machine); } if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { @@ -2786,6 +3041,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"); mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; mc->kvm_type = virt_kvm_type; + mc->has_hotpluggable_cpus = true; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = virt_machine_get_hotplug_handler; hc->pre_plug = virt_machine_device_pre_plug_cb; diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 9e3241b43085bd8e44f9b189d0ef36c726abdcba..b8d1d820cb4dc26f13248c0944fd87600b25a17c 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -208,6 +208,10 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) if (dev->hotplugged) { cpu_synchronize_post_init(cpu); + +#ifdef __aarch64__ + if (!kvm_enabled()) +#endif cpu_resume(cpu); } diff --git a/hw/core/reset.c b/hw/core/reset.c index e923723d38fa45ae39a7da631f1adf53be8adba4..314d33211172948fb6d0f00b9f68c6a1ead70f1d 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -48,6 +48,31 @@ void qemu_register_reset(QEMUResetHandler *func, void *opaque) QTAILQ_INSERT_TAIL(&reset_handlers, re, entry); } +QEMUResetEntry *qemu_get_reset_entry(QEMUResetHandler *func, + void *opaque) +{ + QEMUResetEntry *re; + + QTAILQ_FOREACH(re, &reset_handlers, entry) { + if (re->func == func && re->opaque == opaque) { + return re; + } + } + + return NULL; +} + +void qemu_register_reset_after(QEMUResetEntry *entry, + QEMUResetHandler *func, + void *opaque) +{ + QEMUResetEntry *re = g_malloc0(sizeof(QEMUResetEntry)); + + re->func = func; + re->opaque = opaque; + QTAILQ_INSERT_AFTER(&reset_handlers, entry, re, entry); +} + void qemu_unregister_reset(QEMUResetHandler *func, void *opaque) { QEMUResetEntry *re; diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index a99c6e4fe3fad88da568c9f738d7bd8f5900f126..1ce2d67c2ef711616486b48f89eb93ef4f035d6e 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1513,7 +1513,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, .fw_unplugs_cpu = pm->smi_on_cpu_unplug, }; build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base, - "\\_SB.PCI0", "\\_GPE._E02"); + "\\_SB.PCI0", "\\_GPE._E02", AML_SYSTEM_IO); } if (pcms->memhp_io_base && nr_mem) { diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 9f5f815db9bc599f5d45c463a5ad4c6eeb0f0d57..864d4e40347bf76e82492542af8d6b4e7c904f79 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -19,6 +19,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "hw/intc/arm_gicv3.h" +#include "hw/core/cpu.h" #include "gicv3_internal.h" static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio) @@ -217,7 +218,9 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len) assert(len > 0); for (i = 0; i < s->num_cpu; i++) { - s->cpu[i].seenbetter = false; + if (qemu_get_cpu(i)) { + s->cpu[i].seenbetter = false; + } } /* Find the highest priority pending interrupt in this range. */ @@ -259,16 +262,18 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len) * now be the new best one). */ for (i = 0; i < s->num_cpu; i++) { - GICv3CPUState *cs = &s->cpu[i]; + if (qemu_get_cpu(i)) { + GICv3CPUState *cs = &s->cpu[i]; - if (cs->seenbetter) { - cs->hppi.grp = gicv3_irq_group(cs->gic, cs, cs->hppi.irq); - } + if (cs->seenbetter) { + cs->hppi.grp = gicv3_irq_group(cs->gic, cs, cs->hppi.irq); + } - if (!cs->seenbetter && cs->hppi.prio != 0xff && - cs->hppi.irq >= start && cs->hppi.irq < start + len) { - gicv3_full_update_noirqset(s); - break; + if (!cs->seenbetter && cs->hppi.prio != 0xff && + cs->hppi.irq >= start && cs->hppi.irq < start + len) { + gicv3_full_update_noirqset(s); + break; + } } } } @@ -279,7 +284,9 @@ void gicv3_update(GICv3State *s, int start, int len) gicv3_update_noirqset(s, start, len); for (i = 0; i < s->num_cpu; i++) { - gicv3_cpuif_update(&s->cpu[i]); + if (qemu_get_cpu(i)) { + gicv3_cpuif_update(&s->cpu[i]); + } } } @@ -291,7 +298,9 @@ void gicv3_full_update_noirqset(GICv3State *s) int i; for (i = 0; i < s->num_cpu; i++) { - s->cpu[i].hppi.prio = 0xff; + if (qemu_get_cpu(i)) { + s->cpu[i].hppi.prio = 0xff; + } } /* Note that we can guarantee that these functions will not @@ -302,7 +311,9 @@ void gicv3_full_update_noirqset(GICv3State *s) gicv3_update_noirqset(s, GIC_INTERNAL, s->num_irq - GIC_INTERNAL); for (i = 0; i < s->num_cpu; i++) { - gicv3_redist_update_noirqset(&s->cpu[i]); + if (qemu_get_cpu(i)) { + gicv3_redist_update_noirqset(&s->cpu[i]); + } } } @@ -315,7 +326,9 @@ void gicv3_full_update(GICv3State *s) gicv3_full_update_noirqset(s); for (i = 0; i < s->num_cpu; i++) { - gicv3_cpuif_update(&s->cpu[i]); + if (qemu_get_cpu(i)) { + gicv3_cpuif_update(&s->cpu[i]); + } } } @@ -376,12 +389,26 @@ static const MemoryRegionOps gic_ops[] = { } }; +static void gicv3_cpu_realize(GICv3State *s, int i) +{ + gicv3_init_one_cpuif(s, i); +} + +static void arm_gicv3_cpu_hotplug_realize(GICv3State *s, int ncpu) +{ + ARMGICv3Class *agc = ARM_GICV3_GET_CLASS(s); + + agc->parent_cpu_hotplug_realize(s, ncpu); + gicv3_cpu_realize(s, ncpu); +} + static void arm_gic_realize(DeviceState *dev, Error **errp) { /* Device instance realize function for the GIC sysbus device */ GICv3State *s = ARM_GICV3(dev); ARMGICv3Class *agc = ARM_GICV3_GET_CLASS(s); Error *local_err = NULL; + int i; agc->parent_realize(dev, &local_err); if (local_err) { @@ -391,7 +418,11 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops); - gicv3_init_cpuif(s); + for (i = 0; i < s->num_cpu; i++) { + if (qemu_get_cpu(i)) { + gicv3_cpu_realize(s, i); + } + } } static void arm_gicv3_class_init(ObjectClass *klass, void *data) @@ -400,6 +431,8 @@ static void arm_gicv3_class_init(ObjectClass *klass, void *data) ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); ARMGICv3Class *agc = ARM_GICV3_CLASS(klass); + agc->parent_cpu_hotplug_realize = agcc->cpu_hotplug_realize; + agcc->cpu_hotplug_realize = arm_gicv3_cpu_hotplug_realize; agcc->post_load = arm_gicv3_post_load; device_class_set_parent_realize(dc, arm_gic_realize, &agc->parent_realize); } diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 9884d2e39b9ab938f78e26219054cd04755691df..a4976b2ba023d5bf04b0ec7fe328c7c6668c73b1 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -24,12 +24,14 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qemu/error-report.h" #include "hw/core/cpu.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "gicv3_internal.h" #include "hw/arm/linux-boot-if.h" +#include "hw/boards.h" #include "sysemu/kvm.h" @@ -301,6 +303,21 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, } } +static void arm_gicv3_common_cpu_realize(GICv3State *s, int ncpu) +{ + CPUState *cpu = qemu_get_cpu(ncpu); + + s->cpu[ncpu].cpu = cpu; + s->cpu[ncpu].gic = s; + /* Store GICv3CPUState in CPUARMState gicv3state pointer */ + gicv3_set_gicv3state(cpu, &s->cpu[ncpu]); +} + +static void arm_gicv3_common_cpu_hotplug_realize(GICv3State *s, int ncpu) +{ + arm_gicv3_common_cpu_realize(s, ncpu); +} + static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) { GICv3State *s = ARM_GICV3_COMMON(dev); @@ -361,12 +378,15 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_cpu; i++) { CPUState *cpu = qemu_get_cpu(i); + + MachineState *ms = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus = NULL; uint64_t cpu_affid; - s->cpu[i].cpu = cpu; - s->cpu[i].gic = s; - /* Store GICv3CPUState in CPUARMState gicv3state pointer */ - gicv3_set_gicv3state(cpu, &s->cpu[i]); + if (cpu) { + arm_gicv3_common_cpu_realize(s, i); + } /* Pre-construct the GICR_TYPER: * For our implementation: @@ -380,7 +400,18 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) * VLPIS == 0 (virtual LPIs not supported) * PLPIS == 0 (physical LPIs not supported) */ - cpu_affid = object_property_get_uint(OBJECT(cpu), "mp-affinity", NULL); + if (cpu) { + cpu_affid = object_property_get_uint(OBJECT(cpu), "mp-affinity", NULL); + } else { + if (!mc->possible_cpu_arch_ids) { + error_report("MachineClass must implement possible_cpu_arch_ids " + "hook to support pre-sizing GICv3"); + exit(1); + } + + possible_cpus = mc->possible_cpu_arch_ids(ms); + cpu_affid = possible_cpus->cpus[i].arch_id; + } /* The CPU mp-affinity property is in MPIDR register format; squash * the affinity bytes into 32 bits as the GICR_TYPER has them. @@ -530,12 +561,14 @@ static Property arm_gicv3_common_properties[] = { static void arm_gicv3_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass); dc->reset = arm_gicv3_common_reset; dc->realize = arm_gicv3_common_realize; device_class_set_props(dc, arm_gicv3_common_properties); dc->vmsd = &vmstate_gicv3; + agcc->cpu_hotplug_realize = arm_gicv3_common_cpu_hotplug_realize; albifc->arm_linux_init = arm_gic_common_linux_init; } diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 85fc369e55019bc332af7a974f5b60ebfcf33d59..274a40a40c0a33887ecb6cf21b49d94e520c1e53 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -1676,6 +1676,10 @@ static void icc_generate_sgi(CPUARMState *env, GICv3CPUState *cs, aff, targetlist); for (i = 0; i < s->num_cpu; i++) { + if (!qemu_get_cpu(i)) { + continue; + } + GICv3CPUState *ocs = &s->cpu[i]; if (irm) { @@ -2625,76 +2629,72 @@ static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque) gicv3_cpuif_update(cs); } -void gicv3_init_cpuif(GICv3State *s) +void gicv3_init_one_cpuif(GICv3State *s, int ncpu) { /* Called from the GICv3 realize function; register our system * registers with the CPU */ - int i; - - for (i = 0; i < s->num_cpu; i++) { - ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i)); - GICv3CPUState *cs = &s->cpu[i]; - - /* Note that we can't just use the GICv3CPUState as an opaque pointer - * in define_arm_cp_regs_with_opaque(), because when we're called back - * it might be with code translated by CPU 0 but run by CPU 1, in - * which case we'd get the wrong value. - * So instead we define the regs with no ri->opaque info, and - * get back to the GICv3CPUState from the CPUARMState. + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(ncpu)); + GICv3CPUState *cs = &s->cpu[ncpu]; + + /* Note that we can't just use the GICv3CPUState as an opaque pointer + * in define_arm_cp_regs_with_opaque(), because when we're called back + * it might be with code translated by CPU 0 but run by CPU 1, in + * which case we'd get the wrong value. + * So instead we define the regs with no ri->opaque info, and + * get back to the GICv3CPUState from the CPUARMState. + */ + define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); + if (arm_feature(&cpu->env, ARM_FEATURE_EL2) + && cpu->gic_num_lrs) { + int j; + + cs->num_list_regs = cpu->gic_num_lrs; + cs->vpribits = cpu->gic_vpribits; + cs->vprebits = cpu->gic_vprebits; + + /* Check against architectural constraints: getting these + * wrong would be a bug in the CPU code defining these, + * and the implementation relies on them holding. */ - define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); - if (arm_feature(&cpu->env, ARM_FEATURE_EL2) - && cpu->gic_num_lrs) { - int j; - - cs->num_list_regs = cpu->gic_num_lrs; - cs->vpribits = cpu->gic_vpribits; - cs->vprebits = cpu->gic_vprebits; - - /* Check against architectural constraints: getting these - * wrong would be a bug in the CPU code defining these, - * and the implementation relies on them holding. - */ - g_assert(cs->vprebits <= cs->vpribits); - g_assert(cs->vprebits >= 5 && cs->vprebits <= 7); - g_assert(cs->vpribits >= 5 && cs->vpribits <= 8); + g_assert(cs->vprebits <= cs->vpribits); + g_assert(cs->vprebits >= 5 && cs->vprebits <= 7); + g_assert(cs->vpribits >= 5 && cs->vpribits <= 8); - define_arm_cp_regs(cpu, gicv3_cpuif_hcr_reginfo); + define_arm_cp_regs(cpu, gicv3_cpuif_hcr_reginfo); - for (j = 0; j < cs->num_list_regs; j++) { - /* Note that the AArch64 LRs are 64-bit; the AArch32 LRs - * are split into two cp15 regs, LR (the low part, with the - * same encoding as the AArch64 LR) and LRC (the high part). - */ - ARMCPRegInfo lr_regset[] = { - { .name = "ICH_LRn_EL2", .state = ARM_CP_STATE_BOTH, - .opc0 = 3, .opc1 = 4, .crn = 12, - .crm = 12 + (j >> 3), .opc2 = j & 7, - .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL2_RW, - .readfn = ich_lr_read, - .writefn = ich_lr_write, - }, - { .name = "ICH_LRCn_EL2", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 4, .crn = 12, - .crm = 14 + (j >> 3), .opc2 = j & 7, - .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL2_RW, - .readfn = ich_lr_read, - .writefn = ich_lr_write, - }, - REGINFO_SENTINEL - }; - define_arm_cp_regs(cpu, lr_regset); - } - if (cs->vprebits >= 6) { - define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr1_reginfo); - } - if (cs->vprebits == 7) { - define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr23_reginfo); - } + for (j = 0; j < cs->num_list_regs; j++) { + /* Note that the AArch64 LRs are 64-bit; the AArch32 LRs + * are split into two cp15 regs, LR (the low part, with the + * same encoding as the AArch64 LR) and LRC (the high part). + */ + ARMCPRegInfo lr_regset[] = { + { .name = "ICH_LRn_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, + .crm = 12 + (j >> 3), .opc2 = j & 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_lr_read, + .writefn = ich_lr_write, + }, + { .name = "ICH_LRCn_EL2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 12, + .crm = 14 + (j >> 3), .opc2 = j & 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_lr_read, + .writefn = ich_lr_write, + }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, lr_regset); + } + if (cs->vprebits >= 6) { + define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr1_reginfo); + } + if (cs->vprebits == 7) { + define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr23_reginfo); } - arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs); } + arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs); } diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 5ec5ff9ef6e8deecb91c5207d5b5aa1718919faf..2e2b08e31f7ac3dccd95f0009c3472880e901a9f 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -76,6 +76,7 @@ struct KVMARMGICv3Class { ARMGICv3CommonClass parent_class; DeviceRealize parent_realize; void (*parent_reset)(DeviceState *dev); + CPUHotplugRealize parent_cpu_hotplug_realize; }; static void kvm_arm_gicv3_set_irq(void *opaque, int irq, int level) @@ -341,6 +342,10 @@ static void kvm_arm_gicv3_put(GICv3State *s) for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { GICv3CPUState *c = &s->cpu[ncpu]; + if (!qemu_get_cpu(ncpu)) { + continue; + } + reg64 = c->gicr_propbaser; regl = (uint32_t)reg64; kvm_gicr_access(s, GICR_PROPBASER, ncpu, ®l, true); @@ -360,6 +365,10 @@ static void kvm_arm_gicv3_put(GICv3State *s) for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { GICv3CPUState *c = &s->cpu[ncpu]; + if (!qemu_get_cpu(ncpu)) { + continue; + } + reg = c->gicr_ctlr; kvm_gicr_access(s, GICR_CTLR, ncpu, ®, true); @@ -456,6 +465,10 @@ static void kvm_arm_gicv3_put(GICv3State *s) GICv3CPUState *c = &s->cpu[ncpu]; int num_pri_bits; + if (!qemu_get_cpu(ncpu)) { + continue; + } + kvm_gicc_access(s, ICC_SRE_EL1, ncpu, &c->icc_sre_el1, true); kvm_gicc_access(s, ICC_CTLR_EL1, ncpu, &c->icc_ctlr_el1[GICV3_NS], true); @@ -523,6 +536,10 @@ static void kvm_arm_gicv3_get(GICv3State *s) /* Redistributor state (one per CPU) */ for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { + if (!qemu_get_cpu(ncpu)) { + continue; + } + GICv3CPUState *c = &s->cpu[ncpu]; kvm_gicr_access(s, GICR_CTLR, ncpu, ®, false); @@ -558,6 +575,10 @@ static void kvm_arm_gicv3_get(GICv3State *s) if (redist_typer & GICR_TYPER_PLPIS) { for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { + if (!qemu_get_cpu(ncpu)) { + continue; + } + GICv3CPUState *c = &s->cpu[ncpu]; kvm_gicr_access(s, GICR_PROPBASER, ncpu, ®l, false); @@ -611,6 +632,10 @@ static void kvm_arm_gicv3_get(GICv3State *s) */ for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { + if (!qemu_get_cpu(ncpu)) { + continue; + } + GICv3CPUState *c = &s->cpu[ncpu]; int num_pri_bits; @@ -764,6 +789,20 @@ static void vm_change_state_handler(void *opaque, bool running, } } +static void kvm_arm_gicv3_cpu_realize(GICv3State *s, int ncpu) +{ + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(ncpu)); + + define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); +} + +static void kvm_arm_gicv3_cpu_hotplug_realize(GICv3State *s, int ncpu) +{ + KVMARMGICv3Class *kagcc = KVM_ARM_GICV3_GET_CLASS(s); + + kagcc->parent_cpu_hotplug_realize(s, ncpu); + kvm_arm_gicv3_cpu_realize(s, ncpu); +} static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) { @@ -790,9 +829,9 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); for (i = 0; i < s->num_cpu; i++) { - ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i)); - - define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); + if (qemu_get_cpu(i)) { + kvm_arm_gicv3_cpu_realize(s, i); + } } /* Try to create the device via the device control API */ @@ -877,6 +916,8 @@ static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data) ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); KVMARMGICv3Class *kgc = KVM_ARM_GICV3_CLASS(klass); + kgc->parent_cpu_hotplug_realize = agcc->cpu_hotplug_realize; + agcc->cpu_hotplug_realize = kvm_arm_gicv3_cpu_hotplug_realize; agcc->pre_save = kvm_arm_gicv3_get; agcc->post_load = kvm_arm_gicv3_put; device_class_set_parent_realize(dc, kvm_arm_gicv3_realize, diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index b9c37453b0426a03f45124817eab5ae86dc4a34d..65db0126005269755b864ea4270b2da5f91a70a6 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -495,7 +495,7 @@ void gicv3_redist_update_lpi(GICv3CPUState *cs); */ void gicv3_redist_update_lpi_only(GICv3CPUState *cs); void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns); -void gicv3_init_cpuif(GICv3State *s); +void gicv3_init_one_cpuif(GICv3State *s, int ncpu); /** * gicv3_cpuif_update: diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index ea6056ab926ffaf30c327032f2361d804729029e..601931433aaaf5e4540c304bec2d6a9cc956f72b 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -5,6 +5,7 @@ #include "qom/object.h" #include "hw/boards.h" #include "hw/qdev-core.h" +#include "hw/acpi/aml-build.h" /* These values are part of guest ABI, and can not be changed */ typedef enum { @@ -55,5 +56,6 @@ struct AcpiDeviceIfClass { void (*madt_cpu)(AcpiDeviceIf *adev, int uid, const CPUArchIdList *apic_ids, GArray *entry, bool force_enabled); + void (*cpu_cppc)(AcpiDeviceIf *adev, int uid, int num_cpu, Aml *dev); }; #endif diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index 999caaf510600dda467b91dfa77c8ab9c0450068..d521025830904b1e48433ad627f43b484f4b6760 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -17,6 +17,8 @@ #include "hw/acpi/aml-build.h" #include "hw/hotplug.h" +#define ACPI_CPU_HOTPLUG_REG_LEN 12 + typedef struct AcpiCpuStatus { struct CPUState *cpu; uint64_t arch_id; @@ -58,7 +60,8 @@ typedef struct CPUHotplugFeatures { void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, hwaddr io_base, const char *res_root, - const char *event_handler_method); + const char *event_handler_method, + AmlRegionSpace rs); void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list); diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index d49217c445fbcc04664db41a87439386f71e190f..6bb2ade385a464eedd07319c31719e2fc32fe961 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -63,6 +63,7 @@ #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/ghes.h" #include "qom/object.h" +#include "hw/acpi/cpu.h" #define ACPI_POWER_BUTTON_DEVICE "PWRB" @@ -97,6 +98,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) #define ACPI_GED_MEM_HOTPLUG_EVT 0x1 #define ACPI_GED_PWR_DOWN_EVT 0x2 #define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4 +#define ACPI_GED_CPU_HOTPLUG_EVT 0x8 typedef struct GEDState { MemoryRegion evt; @@ -108,6 +110,8 @@ struct AcpiGedState { SysBusDevice parent_obj; MemHotplugState memhp_state; MemoryRegion container_memhp; + CPUHotplugState cpuhp_state; + MemoryRegion container_cpuhp; GEDState ged_state; uint32_t ged_event_bitmap; qemu_irq irq; diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index ce2b48b88bca591e3747c25bba7330aa17304485..c3c4d3ea79e2b84ea01aa46991f701307f9d116d 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -119,6 +119,9 @@ struct arm_boot_info { arm_endianness endianness; }; +void cpu_hotplug_register_reset(int ncpu); +void cpu_hotplug_reset_manually(int ncpu); + /** * arm_load_kernel - Loads memory with everything needed to boot * diff --git a/include/hw/arm/topology.h b/include/hw/arm/topology.h new file mode 100644 index 0000000000000000000000000000000000000000..d0dad1a9a3ad552eed085df328ef939d6638de0b --- /dev/null +++ b/include/hw/arm/topology.h @@ -0,0 +1,68 @@ +/* + * ARM CPU topology data structures and functions + * + * Copyright (c) 2020 HUAWEI TECHNOLOGIES CO.,LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef HW_ARM_TOPOLOGY_H +#define HW_ARM_TOPOLOGY_H + +typedef struct ARMCPUTopoInfo { + unsigned pkg_id; + unsigned cluster_id; + unsigned core_id; + unsigned smt_id; +} ARMCPUTopoInfo; + +/* Calculate (contiguous) CPU index based on topology */ +static inline unsigned idx_from_topo_ids(unsigned nr_clusters, + unsigned nr_cores, + unsigned nr_threads, + const ARMCPUTopoInfo *topo) +{ + assert(nr_clusters > 0); + assert(nr_cores > 0); + assert(nr_threads > 0); + assert(topo != NULL); + + return topo->pkg_id * nr_clusters * nr_cores * nr_threads + + topo->cluster_id * nr_cores + + topo->core_id * nr_threads + + topo->smt_id; +} + +/* Calculate thread/core/cluster/package topology + * based on (contiguous) CPU index + */ +static inline void topo_ids_from_idx(unsigned cpu_index, + unsigned nr_clusters, + unsigned nr_cores, + unsigned nr_threads, + ARMCPUTopoInfo *topo) +{ + assert(nr_clusters > 0); + assert(nr_cores > 0); + assert(nr_threads > 0); + assert(topo != NULL); + + topo->smt_id = cpu_index % nr_threads; + topo->core_id = cpu_index / nr_threads % nr_cores; + topo->cluster_id = cpu_index / nr_threads / nr_cores % nr_clusters; + topo->pkg_id = cpu_index / nr_threads / nr_cores / nr_clusters; +} + +#endif /* HW_ARM_TOPOLOGY_H */ + diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index a4356cf736fcf11747141292e20aef400826461f..4ddee19b184a1c2b2c4c4de54b29894dbc643ade 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -38,6 +38,7 @@ #include "sysemu/kvm.h" #include "hw/intc/arm_gicv3_common.h" #include "qom/object.h" +#include "hw/acpi/acpi_dev_interface.h" #define NUM_GICV2M_SPIS 64 #define NUM_VIRTIO_TRANSPORTS 32 @@ -87,6 +88,7 @@ enum { VIRT_ACPI_GED, VIRT_NVDIMM_ACPI, VIRT_PVTIME, + VIRT_CPU_ACPI, VIRT_LOWMEMMAP_LAST, }; @@ -147,6 +149,7 @@ struct VirtMachineState { bool its; bool tcg_its; bool virt; + bool cpu_hotplug_enabled; bool ras; bool mte; OnOffAuto acpi; @@ -165,6 +168,7 @@ struct VirtMachineState { uint32_t msi_phandle; uint32_t iommu_phandle; int psci_conduit; + uint32_t boot_cpus; hwaddr highest_gpa; DeviceState *gic; DeviceState *acpi_dev; @@ -181,6 +185,11 @@ OBJECT_DECLARE_TYPE(VirtMachineState, VirtMachineClass, VIRT_MACHINE) void virt_acpi_setup(VirtMachineState *vms); bool virt_is_acpi_enabled(VirtMachineState *vms); +void virt_madt_cpu_entry(AcpiDeviceIf *adev, int uid, + const CPUArchIdList *cpu_list, GArray *entry, + bool force_enabled); +void virt_acpi_dsdt_cpu_cppc(AcpiDeviceIf *adev, int uid, + int num_cpu, Aml *dev); /* Return the number of used redistributor regions */ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) @@ -189,8 +198,9 @@ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; assert(vms->gic_version == VIRT_GIC_VERSION_3); + GICv3State *s = ARM_GICV3_COMMON(vms->gic); - return MACHINE(vms)->smp.cpus > redist0_capacity ? 2 : 1; + return s->num_cpu > redist0_capacity ? 2 : 1; } #endif /* QEMU_ARM_VIRT_H */ diff --git a/include/hw/intc/arm_gicv3.h b/include/hw/intc/arm_gicv3.h index a81a6ae7ecad6d1251738842e0ea67e94495eb27..e360556bd5031ee77ab7b1ff23e9041d4ce579a8 100644 --- a/include/hw/intc/arm_gicv3.h +++ b/include/hw/intc/arm_gicv3.h @@ -26,6 +26,8 @@ struct ARMGICv3Class { ARMGICv3CommonClass parent_class; /*< public >*/ + CPUHotplugRealize parent_cpu_hotplug_realize; + DeviceRealize parent_realize; }; diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index fc38e4b7dca4ac7f4a5e0377a5cc300e9a13feaf..c208a191ff26a94e312cf633007b5275ed5a3e14 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -306,11 +306,15 @@ typedef struct ARMGICv3CommonClass ARMGICv3CommonClass; DECLARE_OBJ_CHECKERS(GICv3State, ARMGICv3CommonClass, ARM_GICV3_COMMON, TYPE_ARM_GICV3_COMMON) +typedef void (*CPUHotplugRealize)(GICv3State *s, int ncpu); + struct ARMGICv3CommonClass { /*< private >*/ SysBusDeviceClass parent_class; /*< public >*/ + CPUHotplugRealize cpu_hotplug_realize; + void (*pre_save)(GICv3State *s); void (*post_load)(GICv3State *s); }; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 7b22aeb6ae1a111c158370933260ead5059c35c9..2623775c27e436619b287191ba2d7c1ee8e17a02 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -221,6 +221,7 @@ int kvm_has_pit_state2(void); int kvm_has_many_ioeventfds(void); int kvm_has_gsi_routing(void); int kvm_has_intx_set_mask(void); +int kvm_create_parked_vcpu(unsigned long vcpu_id); /** * kvm_arm_supports_user_irq diff --git a/include/sysemu/reset.h b/include/sysemu/reset.h index 0b0d6d7598c9566c11cab829c6d15fe22b57634a..f3ff26c6371a88c011dc32164dc6226f8397ec0f 100644 --- a/include/sysemu/reset.h +++ b/include/sysemu/reset.h @@ -2,7 +2,11 @@ #define QEMU_SYSEMU_RESET_H typedef void QEMUResetHandler(void *opaque); +typedef struct QEMUResetEntry QEMUResetEntry; +QEMUResetEntry *qemu_get_reset_entry(QEMUResetHandler *func, void *opaque); +void qemu_register_reset_after(QEMUResetEntry *entry, + QEMUResetHandler *func, void *opaque); void qemu_register_reset(QEMUResetHandler *func, void *opaque); void qemu_unregister_reset(QEMUResetHandler *func, void *opaque); void qemu_devices_reset(void); diff --git a/qapi/machine.json b/qapi/machine.json index 8faa51074ed47a651c07980ce69c732d5e7d16c4..6822cafe2e07ad7678451b6636d2694bba011e38 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -868,6 +868,7 @@ # @node-id: NUMA node ID the CPU belongs to # @socket-id: socket number within node/board the CPU belongs to # @die-id: die number within socket the CPU belongs to (since 4.1) +# @cluster-id: cluster number within die the CPU belongs to (since 6.2) # @core-id: core number within die the CPU belongs to # @thread-id: thread number within core the CPU belongs to # @@ -883,6 +884,7 @@ 'data': { '*node-id': 'int', '*socket-id': 'int', '*die-id': 'int', + '*cluster-id': 'int', '*core-id': 'int', '*thread-id': 'int' } diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 65163f51354351ab7abccf6bbdca2721ac888ada..d550022f18eb3d256ae6185c8d85b37a3044e50d 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2507,6 +2507,10 @@ static Property arm_cpu_properties[] = { DEFINE_PROP_UINT64("mp-affinity", ARMCPU, mp_affinity, ARM64_AFFINITY_INVALID), DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID), + DEFINE_PROP_INT32("socket-id", ARMCPU, socket_id, -1), + DEFINE_PROP_INT32("cluster-id", ARMCPU, cluster_id, -1), + DEFINE_PROP_INT32("core-id", ARMCPU, core_id, -1), + DEFINE_PROP_INT32("thread-id", ARMCPU, thread_id, -1), DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1), DEFINE_PROP_END_OF_LIST() }; @@ -2557,6 +2561,13 @@ static const struct TCGCPUOps arm_tcg_ops = { }; #endif /* CONFIG_TCG */ +static int64_t arm_cpu_get_arch_id(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + + return cpu->mp_affinity; +} + static void arm_cpu_class_init(ObjectClass *oc, void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); @@ -2569,12 +2580,15 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) device_class_set_props(dc, arm_cpu_properties); device_class_set_parent_reset(dc, arm_cpu_reset, &acc->parent_reset); + dc->user_creatable = true; + cc->class_by_name = arm_cpu_class_by_name; cc->has_work = arm_cpu_has_work; cc->dump_state = arm_cpu_dump_state; cc->set_pc = arm_cpu_set_pc; cc->gdb_read_register = arm_cpu_gdb_read_register; cc->gdb_write_register = arm_cpu_gdb_write_register; + cc->get_arch_id = arm_cpu_get_arch_id; #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &arm_sysemu_ops; #endif diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 947897d5acc144867591b72cbaf0c80a36770f43..eb804dffaa65e4f6badf69fa0bd7a74c109b7d04 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1006,6 +1006,10 @@ struct ARMCPU { QLIST_HEAD(, ARMELChangeHook) el_change_hooks; int32_t node_id; /* NUMA node this CPU belongs to */ + int32_t socket_id; + int32_t cluster_id; + int32_t core_id; + int32_t thread_id; /* Used to synchronize KVM and QEMU in-kernel device levels */ uint8_t device_irq_level; diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 59d556724fd6cdb9b7bac4f503de5600c55b0efa..29ac3f40e0d8decc11291bf194a244d767c31a31 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -262,9 +262,9 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); - if (ms->smp.cpus > 256 && + if (ms->smp.max_cpus > 256 && !kvm_check_extension(s, KVM_CAP_ARM_IRQ_LINE_LAYOUT_2)) { - error_report("Using more than 256 vcpus requires a host kernel " + error_report("Using more than max 256 vcpus requires a host kernel " "with KVM_CAP_ARM_IRQ_LINE_LAYOUT_2"); ret = -EINVAL; } diff --git a/tests/data/acpi/virt/DSDT b/tests/data/acpi/virt/DSDT index dd8573f0312918ea1ba17496e9f3a275e98ab214..4643f6fa4510fade07c1187d230b92b49cd28cba 100644 Binary files a/tests/data/acpi/virt/DSDT and b/tests/data/acpi/virt/DSDT differ diff --git a/tests/data/acpi/virt/DSDT.memhp b/tests/data/acpi/virt/DSDT.memhp index d764481440adea59d928a1a48afe0ba1ce135597..f03b87702e3ec04b8308a1843aa373174c2ed7bb 100644 Binary files a/tests/data/acpi/virt/DSDT.memhp and b/tests/data/acpi/virt/DSDT.memhp differ diff --git a/tests/data/acpi/virt/DSDT.numamem b/tests/data/acpi/virt/DSDT.numamem index dd8573f0312918ea1ba17496e9f3a275e98ab214..4643f6fa4510fade07c1187d230b92b49cd28cba 100644 Binary files a/tests/data/acpi/virt/DSDT.numamem and b/tests/data/acpi/virt/DSDT.numamem differ diff --git a/tests/data/acpi/virt/DSDT.pxb b/tests/data/acpi/virt/DSDT.pxb index 9ff22b5ea465d2f678beb2ce9d905861d69a5b87..8dd662f4bacfa6cb73d454932710f1a3d1d7a33f 100644 Binary files a/tests/data/acpi/virt/DSDT.pxb and b/tests/data/acpi/virt/DSDT.pxb differ diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index 90bf68a5b33c1f0098eb8bc482b5029cf1d469cc..08f28012c84378ae08d1c53e54636a459b0eb536 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -223,17 +223,17 @@ static void aarch64_numa_cpu(const void *data) QTestState *qts; g_autofree char *cli = NULL; - cli = make_cli(data, "-machine smp.cpus=2 " + cli = make_cli(data, "-machine smp.cpus=2,smp.cores=2 " "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " - "-numa cpu,node-id=1,thread-id=0 " - "-numa cpu,node-id=0,thread-id=1"); + "-numa cpu,node-id=1,core-id=0 " + "-numa cpu,node-id=0,core-id=1"); qts = qtest_init(cli); cpus = get_cpus(qts, &resp); g_assert(cpus); while ((e = qlist_pop(cpus))) { QDict *cpu, *props; - int64_t thread, node; + int64_t core, node; cpu = qobject_to(QDict, e); g_assert(qdict_haskey(cpu, "props")); @@ -241,12 +241,12 @@ static void aarch64_numa_cpu(const void *data) g_assert(qdict_haskey(props, "node-id")); node = qdict_get_int(props, "node-id"); - g_assert(qdict_haskey(props, "thread-id")); - thread = qdict_get_int(props, "thread-id"); + g_assert(qdict_haskey(props, "core-id")); + core = qdict_get_int(props, "core-id"); - if (thread == 0) { + if (core == 0) { g_assert_cmpint(node, ==, 1); - } else if (thread == 1) { + } else if (core == 1) { g_assert_cmpint(node, ==, 0); } else { g_assert(false);