]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
LoongArch: KVM: Add EIOINTC device support
authorXianglai Li <lixianglai@loongson.cn>
Wed, 13 Nov 2024 08:18:27 +0000 (16:18 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Wed, 13 Nov 2024 08:18:27 +0000 (16:18 +0800)
Add device model for EIOINTC interrupt controller, implement basic
create & destroy interfaces, and register device model to kvm device
table.

Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn>
Signed-off-by: Xianglai Li <lixianglai@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/include/asm/kvm_eiointc.h [new file with mode: 0644]
arch/loongarch/include/asm/kvm_host.h
arch/loongarch/kvm/Makefile
arch/loongarch/kvm/intc/eiointc.c [new file with mode: 0644]
arch/loongarch/kvm/main.c
include/uapi/linux/kvm.h

diff --git a/arch/loongarch/include/asm/kvm_eiointc.h b/arch/loongarch/include/asm/kvm_eiointc.h
new file mode 100644 (file)
index 0000000..ed65de5
--- /dev/null
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2024 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_KVM_EIOINTC_H
+#define __ASM_KVM_EIOINTC_H
+
+#include <kvm/iodev.h>
+
+#define EIOINTC_IRQS                   256
+#define EIOINTC_ROUTE_MAX_VCPUS                256
+#define EIOINTC_IRQS_U8_NUMS           (EIOINTC_IRQS / 8)
+#define EIOINTC_IRQS_U16_NUMS          (EIOINTC_IRQS_U8_NUMS / 2)
+#define EIOINTC_IRQS_U32_NUMS          (EIOINTC_IRQS_U8_NUMS / 4)
+#define EIOINTC_IRQS_U64_NUMS          (EIOINTC_IRQS_U8_NUMS / 8)
+/* map to ipnum per 32 irqs */
+#define EIOINTC_IRQS_NODETYPE_COUNT    16
+
+#define EIOINTC_BASE                   0x1400
+#define EIOINTC_SIZE                   0x900
+
+#define EIOINTC_VIRT_BASE              (0x40000000)
+#define EIOINTC_VIRT_SIZE              (0x1000)
+
+#define LOONGSON_IP_NUM                        8
+
+struct loongarch_eiointc {
+       spinlock_t lock;
+       struct kvm *kvm;
+       struct kvm_io_device device;
+       struct kvm_io_device device_vext;
+       uint32_t num_cpu;
+       uint32_t features;
+       uint32_t status;
+
+       /* hardware state */
+       union nodetype {
+               u64 reg_u64[EIOINTC_IRQS_NODETYPE_COUNT / 4];
+               u32 reg_u32[EIOINTC_IRQS_NODETYPE_COUNT / 2];
+               u16 reg_u16[EIOINTC_IRQS_NODETYPE_COUNT];
+               u8 reg_u8[EIOINTC_IRQS_NODETYPE_COUNT * 2];
+       } nodetype;
+
+       /* one bit shows the state of one irq */
+       union bounce {
+               u64 reg_u64[EIOINTC_IRQS_U64_NUMS];
+               u32 reg_u32[EIOINTC_IRQS_U32_NUMS];
+               u16 reg_u16[EIOINTC_IRQS_U16_NUMS];
+               u8 reg_u8[EIOINTC_IRQS_U8_NUMS];
+       } bounce;
+
+       union isr {
+               u64 reg_u64[EIOINTC_IRQS_U64_NUMS];
+               u32 reg_u32[EIOINTC_IRQS_U32_NUMS];
+               u16 reg_u16[EIOINTC_IRQS_U16_NUMS];
+               u8 reg_u8[EIOINTC_IRQS_U8_NUMS];
+       } isr;
+       union coreisr {
+               u64 reg_u64[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U64_NUMS];
+               u32 reg_u32[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U32_NUMS];
+               u16 reg_u16[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U16_NUMS];
+               u8 reg_u8[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U8_NUMS];
+       } coreisr;
+       union enable {
+               u64 reg_u64[EIOINTC_IRQS_U64_NUMS];
+               u32 reg_u32[EIOINTC_IRQS_U32_NUMS];
+               u16 reg_u16[EIOINTC_IRQS_U16_NUMS];
+               u8 reg_u8[EIOINTC_IRQS_U8_NUMS];
+       } enable;
+
+       /* use one byte to config ipmap for 32 irqs at once */
+       union ipmap {
+               u64 reg_u64;
+               u32 reg_u32[EIOINTC_IRQS_U32_NUMS / 4];
+               u16 reg_u16[EIOINTC_IRQS_U16_NUMS / 4];
+               u8 reg_u8[EIOINTC_IRQS_U8_NUMS / 4];
+       } ipmap;
+       /* use one byte to config coremap for one irq */
+       union coremap {
+               u64 reg_u64[EIOINTC_IRQS / 8];
+               u32 reg_u32[EIOINTC_IRQS / 4];
+               u16 reg_u16[EIOINTC_IRQS / 2];
+               u8 reg_u8[EIOINTC_IRQS];
+       } coremap;
+
+       DECLARE_BITMAP(sw_coreisr[EIOINTC_ROUTE_MAX_VCPUS][LOONGSON_IP_NUM], EIOINTC_IRQS);
+       uint8_t  sw_coremap[EIOINTC_IRQS];
+};
+
+int kvm_loongarch_register_eiointc_device(void);
+
+#endif /* __ASM_KVM_EIOINTC_H */
index a1de884ebb44fad0f58015cb253175a19390c20d..2d0476f05148af319a7dac88924cb2061f2f4503 100644 (file)
@@ -19,6 +19,7 @@
 #include <asm/inst.h>
 #include <asm/kvm_mmu.h>
 #include <asm/kvm_ipi.h>
+#include <asm/kvm_eiointc.h>
 #include <asm/loongarch.h>
 
 /* Loongarch KVM register ids */
@@ -87,7 +88,7 @@ struct kvm_world_switch {
  *
  *  For LOONGARCH_CSR_CPUID register, max CPUID size if 512
  *  For IPI hardware, max destination CPUID size 1024
- *  For extioi interrupt controller, max destination CPUID size is 256
+ *  For eiointc interrupt controller, max destination CPUID size is 256
  *  For msgint interrupt controller, max supported CPUID size is 65536
  *
  * Currently max CPUID is defined as 256 for KVM hypervisor, in future
@@ -121,6 +122,7 @@ struct kvm_arch {
        s64 time_offset;
        struct kvm_context __percpu *vmcs;
        struct loongarch_ipi *ipi;
+       struct loongarch_eiointc *eiointc;
 };
 
 #define CSR_MAX_NUMS           0x800
index 36c3009fe89c9f64dd23a8b09048982163a573c5..bb50fc799c290dc2a68d5aaf2df4bc4171f55f9a 100644 (file)
@@ -19,5 +19,6 @@ kvm-y += tlb.o
 kvm-y += vcpu.o
 kvm-y += vm.o
 kvm-y += intc/ipi.o
+kvm-y += intc/eiointc.o
 
 CFLAGS_exit.o  += $(call cc-option,-Wno-override-init,)
diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c
new file mode 100644 (file)
index 0000000..10afa61
--- /dev/null
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Loongson Technology Corporation Limited
+ */
+
+#include <asm/kvm_eiointc.h>
+#include <asm/kvm_vcpu.h>
+#include <linux/count_zeros.h>
+
+static int kvm_eiointc_read(struct kvm_vcpu *vcpu,
+                       struct kvm_io_device *dev,
+                       gpa_t addr, int len, void *val)
+{
+       return 0;
+}
+
+static int kvm_eiointc_write(struct kvm_vcpu *vcpu,
+                       struct kvm_io_device *dev,
+                       gpa_t addr, int len, const void *val)
+{
+       return 0;
+}
+
+static const struct kvm_io_device_ops kvm_eiointc_ops = {
+       .read   = kvm_eiointc_read,
+       .write  = kvm_eiointc_write,
+};
+
+static int kvm_eiointc_virt_read(struct kvm_vcpu *vcpu,
+                               struct kvm_io_device *dev,
+                               gpa_t addr, int len, void *val)
+{
+       return 0;
+}
+
+static int kvm_eiointc_virt_write(struct kvm_vcpu *vcpu,
+                               struct kvm_io_device *dev,
+                               gpa_t addr, int len, const void *val)
+{
+       return 0;
+}
+
+static const struct kvm_io_device_ops kvm_eiointc_virt_ops = {
+       .read   = kvm_eiointc_virt_read,
+       .write  = kvm_eiointc_virt_write,
+};
+
+static int kvm_eiointc_get_attr(struct kvm_device *dev,
+                               struct kvm_device_attr *attr)
+{
+       return 0;
+}
+
+static int kvm_eiointc_set_attr(struct kvm_device *dev,
+                               struct kvm_device_attr *attr)
+{
+       return 0;
+}
+
+static int kvm_eiointc_create(struct kvm_device *dev, u32 type)
+{
+       int ret;
+       struct loongarch_eiointc *s;
+       struct kvm_io_device *device, *device1;
+       struct kvm *kvm = dev->kvm;
+
+       /* eiointc has been created */
+       if (kvm->arch.eiointc)
+               return -EINVAL;
+
+       s = kzalloc(sizeof(struct loongarch_eiointc), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       spin_lock_init(&s->lock);
+       s->kvm = kvm;
+
+       /*
+        * Initialize IOCSR device
+        */
+       device = &s->device;
+       kvm_iodevice_init(device, &kvm_eiointc_ops);
+       mutex_lock(&kvm->slots_lock);
+       ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS,
+                       EIOINTC_BASE, EIOINTC_SIZE, device);
+       mutex_unlock(&kvm->slots_lock);
+       if (ret < 0) {
+               kfree(s);
+               return ret;
+       }
+
+       device1 = &s->device_vext;
+       kvm_iodevice_init(device1, &kvm_eiointc_virt_ops);
+       ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS,
+                       EIOINTC_VIRT_BASE, EIOINTC_VIRT_SIZE, device1);
+       if (ret < 0) {
+               kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device);
+               kfree(s);
+               return ret;
+       }
+       kvm->arch.eiointc = s;
+
+       return 0;
+}
+
+static void kvm_eiointc_destroy(struct kvm_device *dev)
+{
+       struct kvm *kvm;
+       struct loongarch_eiointc *eiointc;
+
+       if (!dev || !dev->kvm || !dev->kvm->arch.eiointc)
+               return;
+
+       kvm = dev->kvm;
+       eiointc = kvm->arch.eiointc;
+       kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device);
+       kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device_vext);
+       kfree(eiointc);
+}
+
+static struct kvm_device_ops kvm_eiointc_dev_ops = {
+       .name = "kvm-loongarch-eiointc",
+       .create = kvm_eiointc_create,
+       .destroy = kvm_eiointc_destroy,
+       .set_attr = kvm_eiointc_set_attr,
+       .get_attr = kvm_eiointc_get_attr,
+};
+
+int kvm_loongarch_register_eiointc_device(void)
+{
+       return kvm_register_device_ops(&kvm_eiointc_dev_ops, KVM_DEV_TYPE_LOONGARCH_EIOINTC);
+}
index 14f3f69c5bb916b643095722ca47f5b0135d624b..8de366ade99cca0d21e7b472b8c8542e05ef3913 100644 (file)
@@ -9,6 +9,7 @@
 #include <asm/cacheflush.h>
 #include <asm/cpufeature.h>
 #include <asm/kvm_csr.h>
+#include <asm/kvm_eiointc.h>
 #include "trace.h"
 
 unsigned long vpid_mask;
@@ -370,6 +371,11 @@ static int kvm_loongarch_env_init(void)
 
        /* Register LoongArch IPI interrupt controller interface. */
        ret = kvm_loongarch_register_ipi_device();
+       if (ret)
+               return ret;
+
+       /* Register LoongArch EIOINTC interrupt controller interface. */
+       ret = kvm_loongarch_register_eiointc_device();
 
        return ret;
 }
index 9fff439c30ea72ed2e75e8d5962e7bf7143e9293..0ec5c631d9e9285cf33ea0a90df6c39e5e644153 100644 (file)
@@ -1160,6 +1160,8 @@ enum kvm_device_type {
 #define KVM_DEV_TYPE_RISCV_AIA         KVM_DEV_TYPE_RISCV_AIA
        KVM_DEV_TYPE_LOONGARCH_IPI,
 #define KVM_DEV_TYPE_LOONGARCH_IPI     KVM_DEV_TYPE_LOONGARCH_IPI
+       KVM_DEV_TYPE_LOONGARCH_EIOINTC,
+#define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC
 
        KVM_DEV_TYPE_MAX,