diff options
author | Stefan Hajnoczi <stefanha@redhat.com> | 2025-03-05 21:57:15 +0800 |
---|---|---|
committer | Stefan Hajnoczi <stefanha@redhat.com> | 2025-03-05 21:57:15 +0800 |
commit | cf2f8cf3b7b2d4eadb4023eb18f4ae45b294fe82 (patch) | |
tree | a0f53429519942f2cbfa55e52f4fcde7d0d7c1af | |
parent | 9ee727802012ddb32e193d84052a44e382088277 (diff) | |
parent | 0a629c827300d514cc1f61806414d214fcf75051 (diff) | |
download | qemu-cf2f8cf3b7b2d4eadb4023eb18f4ae45b294fe82.zip qemu-cf2f8cf3b7b2d4eadb4023eb18f4ae45b294fe82.tar.gz qemu-cf2f8cf3b7b2d4eadb4023eb18f4ae45b294fe82.tar.bz2 |
Merge tag 'pull-loongarch-20250305' of https://gitlab.com/bibo-mao/qemu into staging
loongarch queue
# -----BEGIN PGP SIGNATURE-----
#
# iHUEABYKAB0WIQQNhkKjomWfgLCz0aQfewwSUazn0QUCZ8ezJgAKCRAfewwSUazn
# 0T9pAQCKb+C69kbf9cEVg7PU/z5I0ALFtCNWCKxSkWZPDPik4gEA3IYwdrJ+csuX
# 8nWL0fzyk+8+LDzEwEgCYoNcMnttRQw=
# =P8mi
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 05 Mar 2025 10:12:54 HKT
# gpg: using EDDSA key 0D8642A3A2659F80B0B3D1A41F7B0C1251ACE7D1
# gpg: Good signature from "bibo mao <maobibo@loongson.cn>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 7044 3A00 19C0 E97A 31C7 13C4 8E86 8FB7 A176 9D4C
# Subkey fingerprint: 0D86 42A3 A265 9F80 B0B3 D1A4 1F7B 0C12 51AC E7D1
* tag 'pull-loongarch-20250305' of https://gitlab.com/bibo-mao/qemu:
target/loongarch: Adjust the cpu reset action to a proper position
hw/loongarch/virt: Enable cpu hotplug feature on virt machine
hw/loongarch/virt: Update the ACPI table for hotplug cpu
hw/loongarch/virt: Implement cpu plug interface
hw/loongarch/virt: Implement cpu unplug interface
hw/loongarch/virt: Add basic cpu plug interface framework
hw/loongarch/virt: Add topo properties on CPU object
hw/loongarch/virt: Add CPU topology support
hw/intc/loongarch_extioi: Use cpu plug notification
hw/intc/loongarch_extioi: Implment cpu hotplug interface
hw/intc/loongarch_extioi: Add basic hotplug framework
hw/intc/loongarch_extioi: Move gpio irq initial to common code
hw/intc/loongarch_ipi: Notify ipi object when cpu is plugged
hw/intc/loongarch_ipi: Implment cpu hotplug interface
hw/intc/loongarch_ipi: Add basic hotplug framework
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r-- | hw/intc/loongarch_extioi.c | 8 | ||||
-rw-r--r-- | hw/intc/loongarch_extioi_common.c | 84 | ||||
-rw-r--r-- | hw/intc/loongarch_ipi.c | 71 | ||||
-rw-r--r-- | hw/loongarch/Kconfig | 1 | ||||
-rw-r--r-- | hw/loongarch/virt-acpi-build.c | 35 | ||||
-rw-r--r-- | hw/loongarch/virt.c | 301 | ||||
-rw-r--r-- | include/hw/loongarch/virt.h | 1 | ||||
-rw-r--r-- | target/loongarch/cpu.c | 25 | ||||
-rw-r--r-- | target/loongarch/cpu.h | 11 |
9 files changed, 493 insertions, 44 deletions
diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index f3055ec..a51a215 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -343,7 +343,7 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_GET_CLASS(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); Error *local_err = NULL; - int i, pin; + int i; lec->parent_realize(dev, &local_err); if (local_err) { @@ -368,12 +368,6 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) } else { s->status |= BIT(EXTIOI_ENABLE); } - - for (i = 0; i < s->num_cpu; i++) { - for (pin = 0; pin < LS3A_INTC_IP; pin++) { - qdev_init_gpio_out(dev, &s->cpu[i].parent_irq[pin], 1); - } - } } static void loongarch_extioi_unrealize(DeviceState *dev) diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index fd56253..ff3974f 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -4,11 +4,82 @@ * Copyright (C) 2024 Loongson Technology Corporation Limited */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/error.h" #include "hw/qdev-properties.h" #include "hw/intc/loongarch_extioi_common.h" #include "migration/vmstate.h" +#include "target/loongarch/cpu.h" + +static ExtIOICore *loongarch_extioi_get_cpu(LoongArchExtIOICommonState *s, + DeviceState *dev) +{ + CPUClass *k = CPU_GET_CLASS(dev); + uint64_t arch_id = k->get_arch_id(CPU(dev)); + int i; + + for (i = 0; i < s->num_cpu; i++) { + if (s->cpu[i].arch_id == arch_id) { + return &s->cpu[i]; + } + } + + return NULL; +} + +static void loongarch_extioi_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev); + Object *obj = OBJECT(dev); + ExtIOICore *core; + int pin, index; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } + + core = loongarch_extioi_get_cpu(s, dev); + if (!core) { + return; + } + + core->cpu = CPU(dev); + index = core - s->cpu; + + /* + * connect extioi irq to the cpu irq + * cpu_pin[LS3A_INTC_IP + 2 : 2] <= intc_pin[LS3A_INTC_IP : 0] + */ + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_connect_gpio_out(DEVICE(s), index * LS3A_INTC_IP + pin, + qdev_get_gpio_in(dev, pin + 2)); + } +} + +static void loongarch_extioi_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev); + Object *obj = OBJECT(dev); + ExtIOICore *core; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } + + core = loongarch_extioi_get_cpu(s, dev); + if (!core) { + return; + } + + core->cpu = NULL; +} static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) { @@ -16,7 +87,7 @@ static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) MachineState *machine = MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(machine); const CPUArchIdList *id_list; - int i; + int i, pin; assert(mc->possible_cpu_arch_ids); id_list = mc->possible_cpu_arch_ids(machine); @@ -30,6 +101,10 @@ static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_cpu; i++) { s->cpu[i].arch_id = id_list->cpus[i].arch_id; s->cpu[i].cpu = CPU(id_list->cpus[i].cpu); + + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_init_gpio_out(dev, &s->cpu[i].parent_irq[pin], 1); + } } } @@ -103,11 +178,14 @@ static void loongarch_extioi_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); device_class_set_parent_realize(dc, loongarch_extioi_common_realize, &lecc->parent_realize); device_class_set_props(dc, extioi_properties); dc->vmsd = &vmstate_loongarch_extioi; + hc->plug = loongarch_extioi_cpu_plug; + hc->unplug = loongarch_extioi_cpu_unplug; } static const TypeInfo loongarch_extioi_common_types[] = { @@ -117,6 +195,10 @@ static const TypeInfo loongarch_extioi_common_types[] = { .instance_size = sizeof(LoongArchExtIOICommonState), .class_size = sizeof(LoongArchExtIOICommonClass), .class_init = loongarch_extioi_common_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, .abstract = true, } }; diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 5376f1e..b10641d 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -6,6 +6,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/boards.h" #include "qapi/error.h" #include "hw/intc/loongarch_ipi.h" @@ -48,6 +49,22 @@ static int loongarch_cpu_by_arch_id(LoongsonIPICommonState *lics, return MEMTX_ERROR; } +static IPICore *loongarch_ipi_get_cpu(LoongsonIPICommonState *lics, + DeviceState *dev) +{ + CPUClass *k = CPU_GET_CLASS(dev); + uint64_t arch_id = k->get_arch_id(CPU(dev)); + int i; + + for (i = 0; i < lics->num_cpu; i++) { + if (lics->cpu[i].arch_id == arch_id) { + return &lics->cpu[i]; + } + } + + return NULL; +} + static void loongarch_ipi_realize(DeviceState *dev, Error **errp) { LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(dev); @@ -76,9 +93,57 @@ static void loongarch_ipi_realize(DeviceState *dev, Error **errp) } } +static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev); + Object *obj = OBJECT(dev); + IPICore *core; + int index; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } + + core = loongarch_ipi_get_cpu(lics, dev); + if (!core) { + return; + } + + core->cpu = CPU(dev); + index = core - lics->cpu; + + /* connect ipi irq to cpu irq */ + qdev_connect_gpio_out(DEVICE(lics), index, qdev_get_gpio_in(dev, IRQ_IPI)); +} + +static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev); + Object *obj = OBJECT(dev); + IPICore *core; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } + + core = loongarch_ipi_get_cpu(lics, dev); + if (!core) { + return; + } + + core->cpu = NULL; +} + static void loongarch_ipi_class_init(ObjectClass *klass, void *data) { LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -86,6 +151,8 @@ static void loongarch_ipi_class_init(ObjectClass *klass, void *data) &lic->parent_realize); licc->get_iocsr_as = get_iocsr_as; licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; + hc->plug = loongarch_ipi_cpu_plug; + hc->unplug = loongarch_ipi_cpu_unplug; } static const TypeInfo loongarch_ipi_types[] = { @@ -95,6 +162,10 @@ static const TypeInfo loongarch_ipi_types[] = { .instance_size = sizeof(LoongarchIPIState), .class_size = sizeof(LoongarchIPIClass), .class_init = loongarch_ipi_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, } }; diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index fe1c6fe..bb2838b 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -17,6 +17,7 @@ config LOONGARCH_VIRT select LOONGARCH_EXTIOI select LS7A_RTC select SMBIOS + select ACPI_CPU_HOTPLUG select ACPI_PCI select ACPI_HW_REDUCED select FW_CFG_DMA diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c index 9ca88d6..fced6c4 100644 --- a/hw/loongarch/virt-acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -47,6 +47,22 @@ #define ACPI_BUILD_DPRINTF(fmt, ...) #endif +static void virt_madt_cpu_entry(int uid, + const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled) +{ + uint32_t flags, apic_id = apic_ids->cpus[uid].arch_id; + + flags = apic_ids->cpus[uid].cpu || force_enabled ? 1 /* Enabled */ : 0; + + /* Rev 1.0b, Table 5-13 Processor Local APIC Structure */ + build_append_int_noprefix(entry, 0, 1); /* Type */ + build_append_int_noprefix(entry, 8, 1); /* Length */ + build_append_int_noprefix(entry, uid, 1); /* ACPI Processor ID */ + build_append_int_noprefix(entry, apic_id, 1); /* APIC ID */ + build_append_int_noprefix(entry, flags, 4); /* Flags */ +} + /* build FADT */ static void init_common_fadt_data(AcpiFadtData *data) { @@ -112,7 +128,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, MachineState *ms = MACHINE(lvms); MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); - int i, arch_id; + int i, arch_id, flags; AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id, .oem_table_id = lvms->oem_table_id }; @@ -125,13 +141,13 @@ build_madt(GArray *table_data, BIOSLinker *linker, for (i = 0; i < arch_ids->len; i++) { /* Processor Core Interrupt Controller Structure */ arch_id = arch_ids->cpus[i].arch_id; - + flags = arch_ids->cpus[i].cpu ? 1 : 0; build_append_int_noprefix(table_data, 17, 1); /* Type */ build_append_int_noprefix(table_data, 15, 1); /* Length */ build_append_int_noprefix(table_data, 1, 1); /* Version */ build_append_int_noprefix(table_data, i, 4); /* ACPI Processor ID */ build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */ - build_append_int_noprefix(table_data, 1, 4); /* Flags */ + build_append_int_noprefix(table_data, flags, 4); /* Flags */ } /* Extend I/O Interrupt Controller Structure */ @@ -338,6 +354,7 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine) { uint32_t event; LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + CPUHotplugFeatures opts; build_ged_aml(dsdt, "\\_SB."GED_DEVICE, HOTPLUG_HANDLER(lvms->acpi_ged), @@ -350,6 +367,18 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine) AML_SYSTEM_MEMORY, VIRT_GED_MEM_ADDR); } + + if (event & ACPI_GED_CPU_HOTPLUG_EVT) { + opts.acpi_1_compatible = false; + opts.has_legacy_cphp = false; + opts.fw_unplugs_cpu = false; + opts.smi_path = NULL; + + build_cpus_aml(dsdt, machine, opts, virt_madt_cpu_entry, + VIRT_GED_CPUHP_ADDR, "\\_SB", + AML_GED_EVT_CPU_SCAN_METHOD, AML_SYSTEM_MEMORY); + } + acpi_dsdt_add_power_button(dsdt); } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 59533b0..a5840ff 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -187,11 +187,17 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, { DeviceState *dev; MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(lvms); uint32_t event = ACPI_GED_PWR_DOWN_EVT; if (ms->ram_slots) { event |= ACPI_GED_MEM_HOTPLUG_EVT; } + + if (mc->has_hotpluggable_cpus) { + event |= ACPI_GED_CPU_HOTPLUG_EVT; + } + dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -203,6 +209,10 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, /* ged regs used for reset and power down */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, VIRT_GED_REG_ADDR); + if (mc->has_hotpluggable_cpus) { + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 3, VIRT_GED_CPUHP_ADDR); + } + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(pch_pic, VIRT_SCI_IRQ - VIRT_GSI_BASE)); return dev; @@ -312,11 +322,12 @@ static void virt_devices_init(DeviceState *pch_pic, static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) { - int num, pin; + int num; MachineState *ms = MACHINE(lvms); MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *possible_cpus; CPUState *cs; + Error *err = NULL; /* cpu nodes */ possible_cpus = mc->possible_cpu_arch_ids(ms); @@ -326,18 +337,8 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) continue; } - /* connect ipi irq to cpu irq */ - qdev_connect_gpio_out(lvms->ipi, num, - qdev_get_gpio_in(DEVICE(cs), IRQ_IPI)); - - /* - * connect ext irq to the cpu irq - * cpu_pin[9:2] <= intc_pin[7:0] - */ - for (pin = 0; pin < LS3A_INTC_IP; pin++) { - qdev_connect_gpio_out(lvms->extioi, (num * LS3A_INTC_IP + pin), - qdev_get_gpio_in(DEVICE(cs), pin + 2)); - } + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), DEVICE(cs), &err); + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), DEVICE(cs), &err); } } @@ -656,15 +657,13 @@ static void fw_cfg_add_memory(MachineState *ms) static void virt_init(MachineState *machine) { - LoongArchCPU *lacpu; const char *cpu_model = machine->cpu_type; MemoryRegion *address_space_mem = get_system_memory(); LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); int i; hwaddr base, size, ram_size = machine->ram_size; - const CPUArchIdList *possible_cpus; MachineClass *mc = MACHINE_GET_CLASS(machine); - CPUState *cpu; + Object *cpuobj; if (!cpu_model) { cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); @@ -680,14 +679,15 @@ static void virt_init(MachineState *machine) memory_region_add_subregion(&lvms->system_iocsr, 0, &lvms->iocsr_mem); /* Init CPUs */ - possible_cpus = mc->possible_cpu_arch_ids(machine); - for (i = 0; i < possible_cpus->len; i++) { - cpu = cpu_create(machine->cpu_type); - cpu->cpu_index = i; - machine->possible_cpus->cpus[i].cpu = cpu; - lacpu = LOONGARCH_CPU(cpu); - lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; - lacpu->env.address_space_iocsr = &lvms->as_iocsr; + mc->possible_cpu_arch_ids(machine); + for (i = 0; i < machine->smp.cpus; i++) { + cpuobj = object_new(machine->cpu_type); + if (cpuobj == NULL) { + error_report("Fail to create object with type %s ", + machine->cpu_type); + exit(EXIT_FAILURE); + } + qdev_realize_and_unref(DEVICE(cpuobj), NULL, &error_fatal); } fw_cfg_add_memory(machine); @@ -783,6 +783,232 @@ static void virt_initfn(Object *obj) virt_flash_create(lvms); } +static void virt_get_topo_from_index(MachineState *ms, + LoongArchCPUTopo *topo, int index) +{ + topo->socket_id = index / (ms->smp.cores * ms->smp.threads); + topo->core_id = index / ms->smp.threads % ms->smp.cores; + topo->thread_id = index % ms->smp.threads; +} + +static unsigned int topo_align_up(unsigned int count) +{ + g_assert(count >= 1); + count -= 1; + return BIT(count ? 32 - clz32(count) : 0); +} + +/* + * LoongArch Reference Manual Vol1, Chapter 7.4.12 CPU Identity + * For CPU architecture, bit0 .. bit8 is valid for CPU id, max cpuid is 512 + * However for IPI/Eiointc interrupt controller, max supported cpu id for + * irq routingis 256 + * + * Here max cpu id is 256 for virt machine + */ +static int virt_get_arch_id_from_topo(MachineState *ms, LoongArchCPUTopo *topo) +{ + int arch_id, threads, cores, sockets; + + threads = topo_align_up(ms->smp.threads); + cores = topo_align_up(ms->smp.cores); + sockets = topo_align_up(ms->smp.sockets); + if ((threads * cores * sockets) > 256) { + error_report("Exceeding max cpuid 256 with sockets[%d] cores[%d]" + " threads[%d]", ms->smp.sockets, ms->smp.cores, + ms->smp.threads); + exit(1); + } + + arch_id = topo->thread_id + topo->core_id * threads; + arch_id += topo->socket_id * threads * cores; + return arch_id; +} + +/* Find cpu slot in machine->possible_cpus by arch_id */ +static CPUArchId *virt_find_cpu_slot(MachineState *ms, int arch_id) +{ + int n; + for (n = 0; n < ms->possible_cpus->len; n++) { + if (ms->possible_cpus->cpus[n].arch_id == arch_id) { + return &ms->possible_cpus->cpus[n]; + } + } + + return NULL; +} + +/* Find cpu slot for cold-plut CPU object where cpu is NULL */ +static CPUArchId *virt_find_empty_cpu_slot(MachineState *ms) +{ + int n; + for (n = 0; n < ms->possible_cpus->len; n++) { + if (ms->possible_cpus->cpus[n].cpu == NULL) { + return &ms->possible_cpus->cpus[n]; + } + } + + return NULL; +} + +static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + MachineState *ms = MACHINE(OBJECT(hotplug_dev)); + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + CPUState *cs = CPU(dev); + CPUArchId *cpu_slot; + Error *err = NULL; + LoongArchCPUTopo topo; + int arch_id; + + if (lvms->acpi_ged) { + if ((cpu->thread_id < 0) || (cpu->thread_id >= ms->smp.threads)) { + error_setg(&err, + "Invalid thread-id %u specified, must be in range 1:%u", + cpu->thread_id, ms->smp.threads - 1); + goto out; + } + + if ((cpu->core_id < 0) || (cpu->core_id >= ms->smp.cores)) { + error_setg(&err, + "Invalid core-id %u specified, must be in range 1:%u", + cpu->core_id, ms->smp.cores - 1); + goto out; + } + + if ((cpu->socket_id < 0) || (cpu->socket_id >= ms->smp.sockets)) { + error_setg(&err, + "Invalid socket-id %u specified, must be in range 1:%u", + cpu->socket_id, ms->smp.sockets - 1); + goto out; + } + + topo.socket_id = cpu->socket_id; + topo.core_id = cpu->core_id; + topo.thread_id = cpu->thread_id; + arch_id = virt_get_arch_id_from_topo(ms, &topo); + cpu_slot = virt_find_cpu_slot(ms, arch_id); + if (CPU(cpu_slot->cpu)) { + error_setg(&err, + "cpu(id%d=%d:%d:%d) with arch-id %" PRIu64 " exists", + cs->cpu_index, cpu->socket_id, cpu->core_id, + cpu->thread_id, cpu_slot->arch_id); + goto out; + } + } else { + /* For cold-add cpu, find empty cpu slot */ + cpu_slot = virt_find_empty_cpu_slot(ms); + topo.socket_id = cpu_slot->props.socket_id; + topo.core_id = cpu_slot->props.core_id; + topo.thread_id = cpu_slot->props.thread_id; + object_property_set_int(OBJECT(dev), "socket-id", topo.socket_id, NULL); + object_property_set_int(OBJECT(dev), "core-id", topo.core_id, NULL); + object_property_set_int(OBJECT(dev), "thread-id", topo.thread_id, NULL); + } + + cpu->env.address_space_iocsr = &lvms->as_iocsr; + cpu->phy_id = cpu_slot->arch_id; + cs->cpu_index = cpu_slot - ms->possible_cpus->cpus; + numa_cpu_pre_plug(cpu_slot, dev, &err); +out: + if (err) { + error_propagate(errp, err); + } +} + +static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + Error *err = NULL; + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + CPUState *cs = CPU(dev); + + if (cs->cpu_index == 0) { + error_setg(&err, "hot-unplug of boot cpu(id%d=%d:%d:%d) not supported", + cs->cpu_index, cpu->socket_id, + cpu->core_id, cpu->thread_id); + error_propagate(errp, err); + return; + } + + hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &err); + if (err) { + error_propagate(errp, err); + } +} + +static void virt_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *cpu_slot; + Error *err = NULL; + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + + /* Notify ipi and extioi irqchip to remove interrupt routing to CPU */ + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->ipi), dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->extioi), dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* Notify acpi ged CPU removed */ + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + + cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); + cpu_slot->cpu = NULL; + return; +} + +static void virt_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *cpu_slot; + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + Error *err = NULL; + + cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); + cpu_slot->cpu = CPU(dev); + if (lvms->ipi) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + if (lvms->extioi) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + if (lvms->acpi_ged) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &err); + if (err) { + error_propagate(errp, err); + } + } + + return; +} + static bool memhp_type_supported(DeviceState *dev) { /* we only support pc dimm now */ @@ -801,6 +1027,8 @@ static void virt_device_pre_plug(HotplugHandler *hotplug_dev, { if (memhp_type_supported(dev)) { virt_mem_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_pre_plug(hotplug_dev, dev, errp); } } @@ -819,6 +1047,8 @@ static void virt_device_unplug_request(HotplugHandler *hotplug_dev, { if (memhp_type_supported(dev)) { virt_mem_unplug_request(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_unplug_request(hotplug_dev, dev, errp); } } @@ -837,6 +1067,8 @@ static void virt_device_unplug(HotplugHandler *hotplug_dev, { if (memhp_type_supported(dev)) { virt_mem_unplug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_unplug(hotplug_dev, dev, errp); } } @@ -864,6 +1096,8 @@ static void virt_device_plug_cb(HotplugHandler *hotplug_dev, } } else if (memhp_type_supported(dev)) { virt_mem_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_plug(hotplug_dev, dev, errp); } } @@ -873,6 +1107,7 @@ static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, MachineClass *mc = MACHINE_GET_CLASS(machine); if (device_is_dynamic_sysbus(mc, dev) || + object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || memhp_type_supported(dev)) { return HOTPLUG_HANDLER(machine); @@ -882,8 +1117,9 @@ static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) { - int n; + int n, arch_id; unsigned int max_cpus = ms->smp.max_cpus; + LoongArchCPUTopo topo; if (ms->possible_cpus) { assert(ms->possible_cpus->len == max_cpus); @@ -894,17 +1130,17 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) sizeof(CPUArchId) * max_cpus); ms->possible_cpus->len = max_cpus; for (n = 0; n < ms->possible_cpus->len; n++) { + virt_get_topo_from_index(ms, &topo, n); + arch_id = virt_get_arch_id_from_topo(ms, &topo); ms->possible_cpus->cpus[n].type = ms->cpu_type; - ms->possible_cpus->cpus[n].arch_id = n; - + ms->possible_cpus->cpus[n].arch_id = arch_id; + ms->possible_cpus->cpus[n].vcpus_count = 1; ms->possible_cpus->cpus[n].props.has_socket_id = true; - ms->possible_cpus->cpus[n].props.socket_id = - n / (ms->smp.cores * ms->smp.threads); + ms->possible_cpus->cpus[n].props.socket_id = topo.socket_id; ms->possible_cpus->cpus[n].props.has_core_id = true; - ms->possible_cpus->cpus[n].props.core_id = - n / ms->smp.threads % ms->smp.cores; + 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->smp.threads; + ms->possible_cpus->cpus[n].props.thread_id = topo.thread_id; } return ms->possible_cpus; } @@ -952,6 +1188,7 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->numa_mem_supported = true; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; + mc->has_hotpluggable_cpus = true; mc->get_hotplug_handler = virt_get_hotplug_handler; mc->default_nic = "virtio-net-pci"; hc->plug = virt_device_plug_cb; diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 2e7cdfa..2b7d199 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -30,6 +30,7 @@ #define VIRT_GED_EVT_ADDR 0x100e0000 #define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN) #define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN) +#define VIRT_GED_CPUHP_ADDR (VIRT_GED_REG_ADDR + ACPI_GED_REG_COUNT) #define COMMAND_LINE_SIZE 512 diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 3788f89..ac514a1 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -14,6 +14,7 @@ #include "system/tcg.h" #include "system/kvm.h" #include "kvm/kvm_loongarch.h" +#include "hw/qdev-properties.h" #include "exec/exec-all.h" #include "exec/translation-block.h" #include "cpu.h" @@ -640,12 +641,23 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) loongarch_cpu_register_gdb_regs_for_features(cs); - cpu_reset(cs); qemu_init_vcpu(cs); + cpu_reset(cs); lacc->parent_realize(dev, errp); } +static void loongarch_cpu_unrealizefn(DeviceState *dev) +{ + LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); + +#ifndef CONFIG_USER_ONLY + cpu_remove_sync(CPU(dev)); +#endif + + lacc->parent_unrealize(dev); +} + static bool loongarch_get_lsx(Object *obj, Error **errp) { return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; @@ -879,6 +891,13 @@ static int64_t loongarch_cpu_get_arch_id(CPUState *cs) } #endif +static const Property loongarch_cpu_properties[] = { + DEFINE_PROP_INT32("socket-id", LoongArchCPU, socket_id, 0), + DEFINE_PROP_INT32("core-id", LoongArchCPU, core_id, 0), + DEFINE_PROP_INT32("thread-id", LoongArchCPU, thread_id, 0), + DEFINE_PROP_INT32("node-id", LoongArchCPU, node_id, CPU_UNSET_NUMA_NODE_ID), +}; + static void loongarch_cpu_class_init(ObjectClass *c, void *data) { LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c); @@ -886,8 +905,11 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) DeviceClass *dc = DEVICE_CLASS(c); ResettableClass *rc = RESETTABLE_CLASS(c); + device_class_set_props(dc, loongarch_cpu_properties); device_class_set_parent_realize(dc, loongarch_cpu_realizefn, &lacc->parent_realize); + device_class_set_parent_unrealize(dc, loongarch_cpu_unrealizefn, + &lacc->parent_unrealize); resettable_class_set_parent_phases(rc, NULL, loongarch_cpu_reset_hold, NULL, &lacc->parent_phases); @@ -910,6 +932,7 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) #ifdef CONFIG_TCG cc->tcg_ops = &loongarch_tcg_ops; #endif + dc->user_creatable = true; } static const gchar *loongarch32_gdb_arch_name(CPUState *cs) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 83183a3..eae874c 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -393,6 +393,12 @@ typedef struct CPUArchState { #endif } CPULoongArchState; +typedef struct LoongArchCPUTopo { + int32_t socket_id; /* socket-id of this VCPU */ + int32_t core_id; /* core-id of this VCPU */ + int32_t thread_id; /* thread-id of this VCPU */ +} LoongArchCPUTopo; + /** * LoongArchCPU: * @env: #CPULoongArchState @@ -411,6 +417,10 @@ struct ArchCPU { OnOffAuto lasx; OnOffAuto kvm_pv_ipi; OnOffAuto kvm_steal_time; + int32_t socket_id; /* socket-id of this CPU */ + int32_t core_id; /* core-id of this CPU */ + int32_t thread_id; /* thread-id of this CPU */ + int32_t node_id; /* NUMA node of this CPU */ /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; @@ -429,6 +439,7 @@ struct LoongArchCPUClass { CPUClass parent_class; DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; ResettablePhases parent_phases; }; |