LoongArch: KVM: Add DMSINTC device support

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>
This commit is contained in:
Song Gao
2026-04-09 18:56:37 +08:00
committed by Huacai Chen
parent c43dce6f13
commit 229132c309
7 changed files with 148 additions and 0 deletions

View File

@@ -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

View 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];

View 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 */

View 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

View File

@@ -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);
}

View 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;
}

View 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,