diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/intc/Kconfig | 3 | ||||
-rw-r--r-- | hw/intc/loongarch_dintc.c | 212 | ||||
-rw-r--r-- | hw/intc/meson.build | 1 | ||||
-rw-r--r-- | hw/loongarch/Kconfig | 1 | ||||
-rw-r--r-- | hw/loongarch/virt.c | 110 |
5 files changed, 326 insertions, 1 deletions
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 7547528..9f456d7 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -109,3 +109,6 @@ config LOONGARCH_PCH_MSI config LOONGARCH_EXTIOI bool + +config LOONGARCH_DINTC + bool diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c new file mode 100644 index 0000000..dc8f7ff --- /dev/null +++ b/hw/intc/loongarch_dintc.c @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch direct interrupt controller. + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "hw/intc/loongarch_pch_msi.h" +#include "hw/intc/loongarch_pch_pic.h" +#include "hw/intc/loongarch_dintc.h" +#include "hw/pci/msi.h" +#include "hw/misc/unimp.h" +#include "migration/vmstate.h" +#include "trace.h" +#include "hw/qdev-properties.h" +#include "target/loongarch/cpu.h" +#include "qemu/error-report.h" +#include "system/hw_accel.h" + +/* msg addr field */ +FIELD(MSG_ADDR, IRQ_NUM, 4, 8) +FIELD(MSG_ADDR, CPU_NUM, 12, 8) +FIELD(MSG_ADDR, FIX, 28, 12) + +static uint64_t loongarch_dintc_mem_read(void *opaque, + hwaddr addr, unsigned size) +{ + return 0; +} + +static void do_set_vcpu_dintc_irq(CPUState *cs, run_on_cpu_data data) +{ + int irq = data.host_int; + CPULoongArchState *env; + + env = &LOONGARCH_CPU(cs)->env; + cpu_synchronize_state(cs); + set_bit(irq, (unsigned long *)&env->CSR_MSGIS); +} + +static void loongarch_dintc_mem_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + int irq_num, cpu_num = 0; + LoongArchDINTCState *s = LOONGARCH_DINTC(opaque); + uint64_t msg_addr = addr + VIRT_DINTC_BASE; + CPUState *cs; + + cpu_num = FIELD_EX64(msg_addr, MSG_ADDR, CPU_NUM); + cs = cpu_by_arch_id(cpu_num); + irq_num = FIELD_EX64(msg_addr, MSG_ADDR, IRQ_NUM); + + async_run_on_cpu(cs, do_set_vcpu_dintc_irq, + RUN_ON_CPU_HOST_INT(irq_num)); + qemu_set_irq(s->cpu[cpu_num].parent_irq, 1); +} + +static const MemoryRegionOps loongarch_dintc_ops = { + .read = loongarch_dintc_mem_read, + .write = loongarch_dintc_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void loongarch_dintc_realize(DeviceState *dev, Error **errp) +{ + LoongArchDINTCState *s = LOONGARCH_DINTC(dev); + LoongArchDINTCClass *lac = LOONGARCH_DINTC_GET_CLASS(dev); + MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); + const CPUArchIdList *id_list; + int i; + + Error *local_err = NULL; + lac->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + assert(mc->possible_cpu_arch_ids); + id_list = mc->possible_cpu_arch_ids(machine); + s->num_cpu = id_list->len; + s->cpu = g_new(DINTCCore, s->num_cpu); + if (s->cpu == NULL) { + error_setg(errp, "Memory allocation for DINTCCore fail"); + return; + } + + 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); + qdev_init_gpio_out(dev, &s->cpu[i].parent_irq, 1); + } + + return; +} + +static void loongarch_dintc_unrealize(DeviceState *dev) +{ + LoongArchDINTCState *s = LOONGARCH_DINTC(dev); + g_free(s->cpu); +} + +static void loongarch_dintc_init(Object *obj) +{ + LoongArchDINTCState *s = LOONGARCH_DINTC(obj); + SysBusDevice *shd = SYS_BUS_DEVICE(obj); + memory_region_init_io(&s->dintc_mmio, OBJECT(s), &loongarch_dintc_ops, + s, TYPE_LOONGARCH_DINTC, VIRT_DINTC_SIZE); + sysbus_init_mmio(shd, &s->dintc_mmio); + msi_nonbroken = true; + return; +} + +static DINTCCore *loongarch_dintc_get_cpu(LoongArchDINTCState *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_dintc_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchDINTCState *s = LOONGARCH_DINTC(hotplug_dev); + Object *obj = OBJECT(dev); + DINTCCore *core; + int index; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch DINTC: Invalid %s device type", + object_get_typename(obj)); + return; + } + core = loongarch_dintc_get_cpu(s, dev); + if (!core) { + return; + } + + core->cpu = CPU(dev); + index = core - s->cpu; + + /* connect dintc msg irq to cpu irq */ + qdev_connect_gpio_out(DEVICE(s), index, qdev_get_gpio_in(dev, INT_DMSI)); + return; +} + +static void loongarch_dintc_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchDINTCState *s = LOONGARCH_DINTC(hotplug_dev); + Object *obj = OBJECT(dev); + DINTCCore *core; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch DINTC: Invalid %s device type", + object_get_typename(obj)); + return; + } + + core = loongarch_dintc_get_cpu(s, dev); + + if (!core) { + return; + } + + core->cpu = NULL; +} + +static void loongarch_dintc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + LoongArchDINTCClass *lac = LOONGARCH_DINTC_CLASS(klass); + + dc->unrealize = loongarch_dintc_unrealize; + device_class_set_parent_realize(dc, loongarch_dintc_realize, + &lac->parent_realize); + hc->plug = loongarch_dintc_cpu_plug; + hc->unplug = loongarch_dintc_cpu_unplug; +} + +static const TypeInfo loongarch_dintc_info = { + .name = TYPE_LOONGARCH_DINTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongArchDINTCState), + .instance_init = loongarch_dintc_init, + .class_init = loongarch_dintc_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, +}; + +static void loongarch_dintc_register_types(void) +{ + type_register_static(&loongarch_dintc_info); +} + +type_init(loongarch_dintc_register_types) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 3efb276..faae20b 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -80,3 +80,4 @@ specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_ specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c', 'loongarch_extioi_common.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_EXTIOI'], if_true: files('loongarch_extioi_kvm.c')) +specific_ss.add(when: 'CONFIG_LOONGARCH_DINTC', if_true: files('loongarch_dintc.c')) diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index bb2838b..8024ddf 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -15,6 +15,7 @@ config LOONGARCH_VIRT select LOONGARCH_PCH_PIC select LOONGARCH_PCH_MSI select LOONGARCH_EXTIOI + select LOONGARCH_DINTC select LS7A_RTC select SMBIOS select ACPI_CPU_HOTPLUG diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index bd5cff1..c176042 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -28,6 +28,7 @@ #include "hw/intc/loongarch_extioi.h" #include "hw/intc/loongarch_pch_pic.h" #include "hw/intc/loongarch_pch_msi.h" +#include "hw/intc/loongarch_dintc.h" #include "hw/pci-host/ls7a.h" #include "hw/pci-host/gpex.h" #include "hw/misc/unimp.h" @@ -48,6 +49,30 @@ #include "qemu/error-report.h" #include "kvm/kvm_loongarch.h" +static void virt_get_dmsi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + OnOffAuto dmsi = lvms->dmsi; + + visit_type_OnOffAuto(v, name, &dmsi, errp); + +} +static void virt_set_dmsi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &lvms->dmsi, errp); + + if (lvms->dmsi == ON_OFF_AUTO_OFF) { + lvms->misc_feature &= ~BIT(IOCSRF_DMSI); + lvms->misc_status &= ~BIT_ULL(IOCSRM_DMSI_EN); + } else if (lvms->dmsi == ON_OFF_AUTO_ON) { + lvms->misc_feature = BIT(IOCSRF_DMSI); + } +} + static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -356,13 +381,17 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) &error_abort); hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), DEVICE(cs), &error_abort); + if (lvms->dintc) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->dintc), DEVICE(cs), + &error_abort); + } } } static void virt_irq_init(LoongArchVirtMachineState *lvms) { DeviceState *pch_pic, *pch_msi; - DeviceState *ipi, *extioi; + DeviceState *ipi, *extioi, *dintc; SysBusDevice *d; int i, start, num; @@ -408,6 +437,33 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) * +--------+ +---------+ +---------+ * | UARTs | | Devices | | Devices | * +--------+ +---------+ +---------+ + * + * + * Advanced Extended IRQ model + * + * +-----+ +---------------------------------+ +-------+ + * | IPI | --> | CPUINTC | <-- | Timer | + * +-----+ +---------------------------------+ +-------+ + * ^ ^ ^ + * | | | + * +-------------+ +----------+ +---------+ +-------+ + * | EIOINTC | | DINTC | | LIOINTC | <-- | UARTs | + * +-------------+ +----------+ +---------+ +-------+ + * ^ ^ ^ + * | | | + * +---------+ +---------+ | + * | PCH-PIC | | PCH-MSI | | + * +---------+ +---------+ | + * ^ ^ ^ | + * | | | | + * +---------+ +---------+ +---------+ + * | Devices | | PCH-LPC | | Devices | + * +---------+ +---------+ +---------+ + * ^ + * | + * +---------+ + * | Devices | + * +---------+ */ /* Create IPI device */ @@ -415,6 +471,14 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) lvms->ipi = ipi; sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); + /* Create DINTC device*/ + if (virt_has_dmsi(lvms)) { + dintc = qdev_new(TYPE_LOONGARCH_DINTC); + lvms->dintc = dintc; + sysbus_realize_and_unref(SYS_BUS_DEVICE(dintc), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dintc), 0, VIRT_DINTC_BASE); + } + /* Create EXTIOI device */ extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); lvms->extioi = extioi; @@ -540,6 +604,10 @@ static MemTxResult virt_iocsr_misc_write(void *opaque, hwaddr addr, return MEMTX_OK; } + if (virt_has_dmsi(lvms) && val & BIT_ULL(IOCSRM_DMSI_EN)) { + lvms->misc_status |= BIT_ULL(IOCSRM_DMSI_EN); + } + features = address_space_ldl(&lvms->as_iocsr, EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, attrs, NULL); @@ -575,6 +643,9 @@ static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, break; case FEATURE_REG: ret = BIT(IOCSRF_MSI) | BIT(IOCSRF_EXTIOI) | BIT(IOCSRF_CSRIPI); + if (virt_has_dmsi(lvms)) { + ret |= BIT(IOCSRF_DMSI); + } if (kvm_enabled()) { ret |= BIT(IOCSRF_VM); } @@ -604,6 +675,10 @@ static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, if (features & BIT(EXTIOI_ENABLE_INT_ENCODE)) { ret |= BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE); } + if (virt_has_dmsi(lvms) && + (lvms->misc_status & BIT_ULL(IOCSRM_DMSI_EN))) { + ret |= BIT_ULL(IOCSRM_DMSI_EN); + } break; default: g_assert_not_reached(); @@ -683,6 +758,25 @@ static void fw_cfg_add_memory(MachineState *ms) } } +static void virt_check_dmsi(MachineState *machine) +{ + LoongArchCPU *cpu; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + + cpu = LOONGARCH_CPU(first_cpu); + if (lvms->dmsi == ON_OFF_AUTO_AUTO) { + if (cpu->msgint != ON_OFF_AUTO_OFF) { + lvms->misc_feature = BIT(IOCSRF_DMSI); + } + } + + if (lvms->dmsi == ON_OFF_AUTO_ON && cpu->msgint == ON_OFF_AUTO_OFF) { + error_report("Fail to enable dmsi , cpu msgint is off " + "pleass add cpu feature mesgint=on."); + exit(EXIT_FAILURE); + } +} + static void virt_init(MachineState *machine) { const char *cpu_model = machine->cpu_type; @@ -717,6 +811,7 @@ static void virt_init(MachineState *machine) } qdev_realize_and_unref(DEVICE(cpuobj), NULL, &error_fatal); } + virt_check_dmsi(machine); fw_cfg_add_memory(machine); /* Node0 memory */ @@ -847,6 +942,8 @@ static void virt_initfn(Object *obj) if (tcg_enabled()) { lvms->veiointc = ON_OFF_AUTO_OFF; } + + lvms->dmsi = ON_OFF_AUTO_AUTO; lvms->acpi = ON_OFF_AUTO_AUTO; lvms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); lvms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); @@ -1010,6 +1107,9 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, /* Notify ipi and extioi irqchip to remove interrupt routing to CPU */ hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->ipi), dev, &error_abort); hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->extioi), dev, &error_abort); + if (lvms->dintc) { + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->dintc), dev, &error_abort); + } /* Notify acpi ged CPU removed */ hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &error_abort); @@ -1034,6 +1134,10 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), dev, &error_abort); } + if (lvms->dintc) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->dintc), dev, &error_abort); + } + if (lvms->acpi_ged) { hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &error_abort); @@ -1241,6 +1345,10 @@ static void virt_class_init(ObjectClass *oc, const void *data) NULL, NULL); object_class_property_set_description(oc, "v-eiointc", "Enable Virt Extend I/O Interrupt Controller."); + object_class_property_add(oc, "dmsi", "OnOffAuto", + virt_get_dmsi, virt_set_dmsi, NULL, NULL); + object_class_property_set_description(oc, "dmsi", + "Enable direct Message-interrupts Controller."); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM |