]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
LoongArch: KVM: Add DMSINTC device support
authorSong Gao <gaosong@loongson.cn>
Thu, 9 Apr 2026 10:56:37 +0000 (18:56 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Thu, 9 Apr 2026 10:56:37 +0000 (18:56 +0800)
Add device model for DMSINTC interrupt controller, implement basic
create/destroy/set_attr interfaces, and register device model to kvm
device table.

Reviewed-by: Bibo Mao <maobibo@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/include/asm/kvm_dmsintc.h [new file with mode: 0644]
arch/loongarch/include/asm/kvm_host.h
arch/loongarch/include/uapi/asm/kvm.h
arch/loongarch/kvm/Makefile
arch/loongarch/kvm/intc/dmsintc.c [new file with mode: 0644]
arch/loongarch/kvm/main.c
include/uapi/linux/kvm.h

diff --git a/arch/loongarch/include/asm/kvm_dmsintc.h b/arch/loongarch/include/asm/kvm_dmsintc.h
new file mode 100644 (file)
index 0000000..3c5ec98
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_KVM_DMSINTC_H
+#define __ASM_KVM_DMSINTC_H
+
+#include <linux/kvm_types.h>
+
+struct loongarch_dmsintc {
+       struct kvm *kvm;
+       uint64_t msg_addr_base;
+       uint64_t msg_addr_size;
+       uint32_t cpu_mask;
+};
+
+struct dmsintc_state {
+       atomic64_t vector_map[4];
+};
+
+int kvm_loongarch_register_dmsintc_device(void);
+
+#endif
index 19eb5e5c39841a6dfbf3b3fddad020294ea3bda8..130cedbb6b39b3ecffb0c2b107231c14d6f7fc66 100644 (file)
@@ -20,6 +20,7 @@
 #include <asm/inst.h>
 #include <asm/kvm_mmu.h>
 #include <asm/kvm_ipi.h>
+#include <asm/kvm_dmsintc.h>
 #include <asm/kvm_eiointc.h>
 #include <asm/kvm_pch_pic.h>
 #include <asm/loongarch.h>
@@ -133,6 +134,7 @@ struct kvm_arch {
        s64 time_offset;
        struct kvm_context __percpu *vmcs;
        struct loongarch_ipi *ipi;
+       struct loongarch_dmsintc *dmsintc;
        struct loongarch_eiointc *eiointc;
        struct loongarch_pch_pic *pch_pic;
 };
@@ -247,6 +249,7 @@ struct kvm_vcpu_arch {
        struct kvm_mp_state mp_state;
        /* ipi state */
        struct ipi_state ipi_state;
+       struct dmsintc_state dmsintc_state;
        /* cpucfg */
        u32 cpucfg[KVM_MAX_CPUCFG_REGS];
 
index 419647aacdf35a8e7399142ce986ac9e400b2426..cd0b5c11ca9c2b681f8119a82f15912925a28089 100644 (file)
@@ -155,4 +155,8 @@ struct kvm_iocsr_entry {
 #define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL             0x40000006
 #define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT            0
 
+#define KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL             0x40000007
+#define KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_BASE                0x0
+#define KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_SIZE                0x1
+
 #endif /* __UAPI_ASM_LOONGARCH_KVM_H */
index cb41d9265662f4070b1410afa5700a5f19bc9c5b..ae469edec99c166b018943e06886f41b8ee821b0 100644 (file)
@@ -17,6 +17,7 @@ kvm-y += tlb.o
 kvm-y += vcpu.o
 kvm-y += vm.o
 kvm-y += intc/ipi.o
+kvm-y += intc/dmsintc.o
 kvm-y += intc/eiointc.o
 kvm-y += intc/pch_pic.o
 kvm-y += irqfd.o
diff --git a/arch/loongarch/kvm/intc/dmsintc.c b/arch/loongarch/kvm/intc/dmsintc.c
new file mode 100644 (file)
index 0000000..8f0b91e
--- /dev/null
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Loongson Technology Corporation Limited
+ */
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_dmsintc.h>
+#include <asm/kvm_vcpu.h>
+
+static int kvm_dmsintc_ctrl_access(struct kvm_device *dev,
+                                  struct kvm_device_attr *attr, bool is_write)
+{
+       int addr = attr->attr;
+       unsigned long cpu_bit, val;
+       void __user *data = (void __user *)attr->addr;
+       struct loongarch_dmsintc *s = dev->kvm->arch.dmsintc;
+
+       switch (addr) {
+       case KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_BASE:
+               if (is_write) {
+                       if (copy_from_user(&val, data, sizeof(s->msg_addr_base)))
+                               return -EFAULT;
+                       if (s->msg_addr_base)
+                               return -EFAULT; /* Duplicate setting are not allowed. */
+                       if ((val & (BIT(AVEC_CPU_SHIFT) - 1)) != 0)
+                               return -EINVAL;
+                       s->msg_addr_base = val;
+                       cpu_bit = find_first_bit((unsigned long *)&(s->msg_addr_base), 64) - AVEC_CPU_SHIFT;
+                       cpu_bit = min(cpu_bit, AVEC_CPU_BIT);
+                       s->cpu_mask = GENMASK(cpu_bit - 1, 0) & AVEC_CPU_MASK;
+               }
+               break;
+       case KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_SIZE:
+               if (is_write) {
+                       if (copy_from_user(&val, data, sizeof(s->msg_addr_size)))
+                               return -EFAULT;
+                       if (s->msg_addr_size)
+                               return -EFAULT; /*Duplicate setting are not allowed. */
+                       s->msg_addr_size = val;
+               }
+               break;
+       default:
+               kvm_err("%s: unknown dmsintc register, addr = %d\n", __func__, addr);
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static int kvm_dmsintc_set_attr(struct kvm_device *dev,
+                               struct kvm_device_attr *attr)
+{
+       switch (attr->group) {
+       case KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL:
+               return kvm_dmsintc_ctrl_access(dev, attr, true);
+       default:
+               kvm_err("%s: unknown group (%d)\n", __func__, attr->group);
+               return -EINVAL;
+       }
+}
+
+static int kvm_dmsintc_create(struct kvm_device *dev, u32 type)
+{
+       struct kvm *kvm;
+       struct loongarch_dmsintc *s;
+
+       if (!dev) {
+               kvm_err("%s: kvm_device ptr is invalid!\n", __func__);
+               return -EINVAL;
+       }
+
+       kvm = dev->kvm;
+       if (kvm->arch.dmsintc) {
+               kvm_err("%s: LoongArch DMSINTC has already been created!\n", __func__);
+               return -EINVAL;
+       }
+
+       s = kzalloc(sizeof(struct loongarch_dmsintc), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       s->kvm = kvm;
+       kvm->arch.dmsintc = s;
+
+       return 0;
+}
+
+static void kvm_dmsintc_destroy(struct kvm_device *dev)
+{
+
+       if (!dev || !dev->kvm || !dev->kvm->arch.dmsintc)
+               return;
+
+       kfree(dev->kvm->arch.dmsintc);
+       kfree(dev);
+}
+
+static struct kvm_device_ops kvm_dmsintc_dev_ops = {
+       .name = "kvm-loongarch-dmsintc",
+       .create = kvm_dmsintc_create,
+       .destroy = kvm_dmsintc_destroy,
+       .set_attr = kvm_dmsintc_set_attr,
+};
+
+int kvm_loongarch_register_dmsintc_device(void)
+{
+       return kvm_register_device_ops(&kvm_dmsintc_dev_ops, KVM_DEV_TYPE_LOONGARCH_DMSINTC);
+}
index 304c83863e7124fcd5673b0610687ab2dc5ce3e2..76ebff2faeddc97a3732eb373823a90ed7ac3ebc 100644 (file)
@@ -416,6 +416,12 @@ static int kvm_loongarch_env_init(void)
 
        /* Register LoongArch PCH-PIC interrupt controller interface. */
        ret = kvm_loongarch_register_pch_pic_device();
+       if (ret)
+               return ret;
+
+       /* Register LoongArch DMSINTC interrupt contrroller interface */
+       if (cpu_has_msgint)
+               ret = kvm_loongarch_register_dmsintc_device();
 
        return ret;
 }
index 80364d4dbebb0ce9d13f5dea410c36341d157b3d..9e7887230bddcd94ecb3c95c124d825e5224dee3 100644 (file)
@@ -1224,6 +1224,8 @@ enum kvm_device_type {
 #define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC
        KVM_DEV_TYPE_LOONGARCH_PCHPIC,
 #define KVM_DEV_TYPE_LOONGARCH_PCHPIC  KVM_DEV_TYPE_LOONGARCH_PCHPIC
+       KVM_DEV_TYPE_LOONGARCH_DMSINTC,
+#define KVM_DEV_TYPE_LOONGARCH_DMSINTC KVM_DEV_TYPE_LOONGARCH_DMSINTC
 
        KVM_DEV_TYPE_MAX,