}
} else {
/*
@@ -38,7 +47,14 @@ static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level)
if (val) {
irq = ctz64(val);
s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
- qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_irq = (
+ KVM_LOONGARCH_IRQ_TYPE_IOAPIC << KVM_LOONGARCH_IRQ_TYPE_SHIFT)
+ | (0 << KVM_LOONGARCH_IRQ_VCPU_SHIFT) | s->htmsi_vector[irq];
+ kvm_set_irq(kvm_state, kvm_irq, !!level);
+ } else {
+ qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
+ }
}
}
}
@@ -265,18 +281,18 @@ static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
{
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
uint64_t val = 0;
- uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
+ uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_START;
int64_t offset_tmp;
switch (offset) {
- case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
- offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
+ case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
+ offset_tmp = offset - PCH_PIC_HTMSI_VEC_START;
if (offset_tmp >= 0 && offset_tmp < 64) {
val = s->htmsi_vector[offset_tmp];
}
break;
- case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
- offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
+ case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
+ offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_START;
if (offset_tmp >= 0 && offset_tmp < 64) {
val = s->route_entry[offset_tmp];
}
@@ -294,19 +310,19 @@ static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
{
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
int32_t offset_tmp;
- uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
+ uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_START;
trace_loongarch_pch_pic_writeb(size, addr, data);
switch (offset) {
- case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
- offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
+ case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
+ offset_tmp = offset - PCH_PIC_HTMSI_VEC_START;
if (offset_tmp >= 0 && offset_tmp < 64) {
s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
}
break;
- case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
- offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
+ case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
+ offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_START;
if (offset_tmp >= 0 && offset_tmp < 64) {
s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
}
diff --git a/hw/intc/loongarch_pch_pic_kvm.c b/hw/intc/loongarch_pch_pic_kvm.c
new file mode 100644
index 0000000000..9b6a2f6784
--- /dev/null
+++ b/hw/intc/loongarch_pch_pic_kvm.c
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch kvm pch pic interrupt support
+ *
+ * Copyright (C) 2024 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-properties.h"
+#include "qemu/typedefs.h"
+#include "hw/intc/loongarch_pch_pic.h"
+#include "hw/sysbus.h"
+#include "linux/kvm.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "sysemu/kvm.h"
+#include "hw/loongarch/virt.h"
+#include "hw/pci-host/ls7a.h"
+#include "qemu/error-report.h"
+
+static void kvm_pch_pic_access_regs(int fd, uint64_t addr,
+ void *val, bool is_write)
+{
+ kvm_device_access(fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS,
+ addr, val, is_write, &error_abort);
+}
+
+static void kvm_loongarch_pch_pic_save_load(void *opaque, bool is_write)
+{
+ KVMLoongArchPCHPIC *s = (KVMLoongArchPCHPIC *)opaque;
+ KVMLoongArchPCHPICClass *class = KVM_LOONGARCH_PCH_PIC_GET_CLASS(s);
+ int fd = class->dev_fd;
+ int addr, offset;
+
+ kvm_pch_pic_access_regs(fd, PCH_PIC_MASK_START,
+ (void *)&s->int_mask, is_write);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_HTMSI_EN_START,
+ (void *)&s->htmsi_en, is_write);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_EDGE_START,
+ (void *)&s->intedge, is_write);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_AUTO_CTRL0_START,
+ (void *)&s->auto_crtl0, is_write);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_AUTO_CTRL1_START,
+ (void *)&s->auto_crtl1, is_write);
+
+ for (addr = PCH_PIC_ROUTE_ENTRY_START;
+ addr < PCH_PIC_ROUTE_ENTRY_END; addr++) {
+ offset = addr - PCH_PIC_ROUTE_ENTRY_START;
+ kvm_pch_pic_access_regs(fd, addr,
+ (void *)&s->route_entry[offset], is_write);
+ }
+
+ for (addr = PCH_PIC_HTMSI_VEC_START; addr < PCH_PIC_HTMSI_VEC_END; addr++) {
+ offset = addr - PCH_PIC_HTMSI_VEC_START;
+ kvm_pch_pic_access_regs(fd, addr,
+ (void *)&s->htmsi_vector[offset], is_write);
+ }
+
+ kvm_pch_pic_access_regs(fd, PCH_PIC_INT_IRR_START,
+ (void *)&s->intirr, is_write);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_INT_ISR_START,
+ (void *)&s->intisr, is_write);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_POLARITY_START,
+ (void *)&s->int_polarity, is_write);
+}
+
+static int kvm_loongarch_pch_pic_pre_save(void *opaque)
+{
+ kvm_loongarch_pch_pic_save_load(opaque, false);
+ return 0;
+}
+
+static int kvm_loongarch_pch_pic_post_load(void *opaque, int version_id)
+{
+ kvm_loongarch_pch_pic_save_load(opaque, true);
+ return 0;
+}
+
+static void kvm_pch_pic_handler(void *opaque, int irq, int level)
+{
+ int kvm_irq;
+
+ if (kvm_enabled()) {
+ kvm_irq = \
+ (KVM_LOONGARCH_IRQ_TYPE_IOAPIC << KVM_LOONGARCH_IRQ_TYPE_SHIFT)
+ | (0 << KVM_LOONGARCH_IRQ_VCPU_SHIFT) | irq;
+ kvm_set_irq(kvm_state, kvm_irq, !!level);
+ }
+}
+
+static void kvm_loongarch_pch_pic_realize(DeviceState *dev, Error **errp)
+{
+ KVMLoongArchPCHPICClass *pch_pic_class =
+ KVM_LOONGARCH_PCH_PIC_GET_CLASS(dev);
+ struct kvm_create_device cd = {0};
+ uint64_t pch_pic_base = VIRT_PCH_REG_BASE;
+ Error *err = NULL;
+ int ret;
+
+ pch_pic_class->parent_realize(dev, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ if (!pch_pic_class->is_created) {
+ cd.type = KVM_DEV_TYPE_LOONGARCH_PCH_PIC;
+ ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
+ if (ret < 0) {
+ error_setg_errno(errp, errno,
+ "Creating the KVM pch pic device failed");
+ return;
+ }
+ pch_pic_class->is_created = true;
+ pch_pic_class->dev_fd = cd.fd;
+ fprintf(stdout, "Create LoongArch pch pic irqchip in KVM done!\n");
+
+ ret = kvm_device_access(cd.fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL,
+ KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT,
+ &pch_pic_base, true, NULL);
+ if (ret < 0) {
+ error_report(
+ "KVM PCH_PIC: failed to set the base address of PCH PIC");
+ exit(1);
+ }
+
+ qdev_init_gpio_in(dev, kvm_pch_pic_handler, VIRT_PCH_PIC_IRQ_NUM);
+ }
+}
+
+static const VMStateDescription vmstate_kvm_loongarch_pch_pic = {
+ .name = TYPE_LOONGARCH_PCH_PIC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_save = kvm_loongarch_pch_pic_pre_save,
+ .post_load = kvm_loongarch_pch_pic_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT64(int_mask, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(htmsi_en, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(intedge, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(intclr, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(auto_crtl0, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(auto_crtl1, KVMLoongArchPCHPIC),
+ VMSTATE_UINT8_ARRAY(route_entry, KVMLoongArchPCHPIC, 64),
+ VMSTATE_UINT8_ARRAY(htmsi_vector, KVMLoongArchPCHPIC, 64),
+ VMSTATE_UINT64(last_intirr, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(intirr, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(intisr, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(int_polarity, KVMLoongArchPCHPIC),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+static void kvm_loongarch_pch_pic_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ KVMLoongArchPCHPICClass *pch_pic_class = KVM_LOONGARCH_PCH_PIC_CLASS(oc);
+
+ pch_pic_class->parent_realize = dc->realize;
+ dc->realize = kvm_loongarch_pch_pic_realize;
+ pch_pic_class->is_created = false;
+ dc->vmsd = &vmstate_kvm_loongarch_pch_pic;
+
+}
+
+static const TypeInfo kvm_loongarch_pch_pic_info = {
+ .name = TYPE_KVM_LOONGARCH_PCH_PIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(KVMLoongArchPCHPIC),
+ .class_size = sizeof(KVMLoongArchPCHPICClass),
+ .class_init = kvm_loongarch_pch_pic_class_init,
+};
+
+static void kvm_loongarch_pch_pic_register_types(void)
+{
+ type_register_static(&kvm_loongarch_pch_pic_info);
+}
+
+type_init(kvm_loongarch_pch_pic_register_types)
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 85174d1af1..c20c0a2c05 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -77,3 +77,4 @@ specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_
specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c'))
specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c'))
specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI_KVM', if_true: files('loongarch_extioi_kvm.c'))
+specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC_KVM', if_true: files('loongarch_pch_pic_kvm.c'))
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index 99a523171f..f909f799ad 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -17,6 +17,7 @@ config LOONGARCH_VIRT
select LOONGARCH_PCH_MSI
select LOONGARCH_EXTIOI
select LOONGARCH_IPI_KVM if KVM
+ select LOONGARCH_PCH_PIC_KVM if KVM
select LOONGARCH_EXTIOI_KVM if KVM
select LS7A_RTC
select SMBIOS
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
index 8ca7c09016..db0c08899b 100644
--- a/hw/loongarch/virt.c
+++ b/hw/loongarch/virt.c
@@ -865,40 +865,45 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms)
/* Add Extend I/O Interrupt Controller node */
fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle);
- pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC);
- num = VIRT_PCH_PIC_IRQ_NUM;
- qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num);
- d = SYS_BUS_DEVICE(pch_pic);
- sysbus_realize_and_unref(d, &error_fatal);
- memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE,
- sysbus_mmio_get_region(d, 0));
- memory_region_add_subregion(get_system_memory(),
- VIRT_IOAPIC_REG_BASE + PCH_PIC_ROUTE_ENTRY_OFFSET,
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ pch_pic = qdev_new(TYPE_KVM_LOONGARCH_PCH_PIC);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(pch_pic), &error_fatal);
+ } else {
+ pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC);
+ num = VIRT_PCH_PIC_IRQ_NUM;
+ qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num);
+ d = SYS_BUS_DEVICE(pch_pic);
+ sysbus_realize_and_unref(d, &error_fatal);
+ memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE,
+ sysbus_mmio_get_region(d, 0));
+ memory_region_add_subregion(get_system_memory(),
+ VIRT_IOAPIC_REG_BASE + PCH_PIC_ROUTE_ENTRY_START,
sysbus_mmio_get_region(d, 1));
- memory_region_add_subregion(get_system_memory(),
- VIRT_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO,
- sysbus_mmio_get_region(d, 2));
-
- /* Connect pch_pic irqs to extioi */
- for (i = 0; i < num; i++) {
- qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i));
- }
+ memory_region_add_subregion(get_system_memory(),
+ VIRT_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO,
+ sysbus_mmio_get_region(d, 2));
- /* Add PCH PIC node */
- fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle);
+ /* Connect pch_pic irqs to extioi */
+ for (i = 0; i < num; i++) {
+ qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i));
+ }
- pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI);
- start = num;
- num = EXTIOI_IRQS - start;
- qdev_prop_set_uint32(pch_msi, "msi_irq_base", start);
- qdev_prop_set_uint32(pch_msi, "msi_irq_num", num);
- d = SYS_BUS_DEVICE(pch_msi);
- sysbus_realize_and_unref(d, &error_fatal);
- sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW);
- for (i = 0; i < num; i++) {
- /* Connect pch_msi irqs to extioi */
- qdev_connect_gpio_out(DEVICE(d), i,
- qdev_get_gpio_in(extioi, i + start));
+ /* Add PCH PIC node */
+ fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle);
+
+ pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI);
+ start = num;
+ num = EXTIOI_IRQS - start;
+ qdev_prop_set_uint32(pch_msi, "msi_irq_base", start);
+ qdev_prop_set_uint32(pch_msi, "msi_irq_num", num);
+ d = SYS_BUS_DEVICE(pch_msi);
+ sysbus_realize_and_unref(d, &error_fatal);
+ sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW);
+ for (i = 0; i < num; i++) {
+ /* Connect pch_msi irqs to extioi */
+ qdev_connect_gpio_out(DEVICE(d), i,
+ qdev_get_gpio_in(extioi, i + start));
+ }
}
/* Add PCH MSI node */