2. Define macro MISC_BASE as LOONGARCH_IOCSR_MISC_FUNC rather than
hard coded 0x420
---
arch/loongarch/include/asm/kvm_eiointc.h | 2 +
arch/loongarch/include/asm/kvm_host.h | 2 +
arch/loongarch/include/asm/kvm_misc.h | 17 +++
arch/loongarch/include/asm/loongarch.h | 1 +
arch/loongarch/kvm/Makefile | 1 +
arch/loongarch/kvm/intc/eiointc.c | 61 +++++++++++
arch/loongarch/kvm/intc/misc.c | 125 +++++++++++++++++++++++
7 files changed, 209 insertions(+)
create mode 100644 arch/loongarch/include/asm/kvm_misc.h
create mode 100644 arch/loongarch/kvm/intc/misc.c
diff --git a/arch/loongarch/include/asm/kvm_eiointc.h b/arch/loongarch/include/asm/kvm_eiointc.h
index a3a40aba8acf..2d1c183f2b1b 100644
--- a/arch/loongarch/include/asm/kvm_eiointc.h
+++ b/arch/loongarch/include/asm/kvm_eiointc.h
@@ -119,5 +119,7 @@ struct loongarch_eiointc {
int kvm_loongarch_register_eiointc_device(void);
void eiointc_set_irq(struct loongarch_eiointc *s, int irq, int level);
+int kvm_eiointc_get_status(struct kvm_vcpu *vcpu, unsigned long *value);
+int kvm_eiointc_update_status(struct kvm_vcpu *vcpu, unsigned long value, unsigned long mask);
#endif /* __ASM_KVM_EIOINTC_H */
diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h
index a3c4cc46c892..f463ec52d86c 100644
--- a/arch/loongarch/include/asm/kvm_host.h
+++ b/arch/loongarch/include/asm/kvm_host.h
@@ -132,6 +132,8 @@ struct kvm_arch {
struct loongarch_ipi *ipi;
struct loongarch_eiointc *eiointc;
struct loongarch_pch_pic *pch_pic;
+ struct kvm_io_device misc;
+ bool misc_created;
};
#define CSR_MAX_NUMS 0x800
diff --git a/arch/loongarch/include/asm/kvm_misc.h b/arch/loongarch/include/asm/kvm_misc.h
new file mode 100644
index 000000000000..621e4228dea2
--- /dev/null
+++ b/arch/loongarch/include/asm/kvm_misc.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_KVM_MISC_H
+#define __ASM_KVM_MISC_H
+
+#include <asm/loongarch.h>
+
+#define MISC_BASE LOONGARCH_IOCSR_MISC_FUNC
+#define MISC_SIZE 0x8
+
+int kvm_loongarch_create_misc(struct kvm *kvm);
+void kvm_loongarch_destroy_misc(struct kvm *kvm);
+
+#endif /* __ASM_KVM_MISC_H */
diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
index d84dac88a584..e30d330d497e 100644
--- a/arch/loongarch/include/asm/loongarch.h
+++ b/arch/loongarch/include/asm/loongarch.h
@@ -1141,6 +1141,7 @@
#define IOCSR_MISC_FUNC_SOFT_INT BIT_ULL(10)
#define IOCSR_MISC_FUNC_TIMER_RESET BIT_ULL(21)
#define IOCSR_MISC_FUNC_EXT_IOI_EN BIT_ULL(48)
+#define IOCSR_MISC_FUNC_INT_ENCODE BIT_ULL(49)
#define IOCSR_MISC_FUNC_AVEC_EN BIT_ULL(51)
#define LOONGARCH_IOCSR_CPUTEMP 0x428
diff --git a/arch/loongarch/kvm/Makefile b/arch/loongarch/kvm/Makefile
index cb41d9265662..25fa3866613d 100644
--- a/arch/loongarch/kvm/Makefile
+++ b/arch/loongarch/kvm/Makefile
@@ -18,6 +18,7 @@ kvm-y += vcpu.o
kvm-y += vm.o
kvm-y += intc/ipi.o
kvm-y += intc/eiointc.o
+kvm-y += intc/misc.o
kvm-y += intc/pch_pic.o
kvm-y += irqfd.o
diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c
index f39929d7bf8a..87d01521e92f 100644
--- a/arch/loongarch/kvm/intc/eiointc.c
+++ b/arch/loongarch/kvm/intc/eiointc.c
@@ -4,6 +4,7 @@
*/
#include <asm/kvm_eiointc.h>
+#include <asm/kvm_misc.h>
#include <asm/kvm_vcpu.h>
#include <linux/count_zeros.h>
@@ -708,6 +709,56 @@ static const struct kvm_io_device_ops kvm_eiointc_ops = {
.write = kvm_eiointc_write,
};
+int kvm_eiointc_get_status(struct kvm_vcpu *vcpu, unsigned long *value)
+{
+ unsigned long data, flags;
+ struct loongarch_eiointc *eiointc = vcpu->kvm->arch.eiointc;
+
+ if (!eiointc) {
+ kvm_err("%s: eiointc irqchip not valid!\n", __func__);
+ return -EINVAL;
+ }
+
+ data = 0;
+ spin_lock_irqsave(&eiointc->lock, flags);
+ if (eiointc->status & BIT(EIOINTC_ENABLE))
+ data |= IOCSR_MISC_FUNC_EXT_IOI_EN;
+
+ if (eiointc->status & BIT(EIOINTC_ENABLE_INT_ENCODE))
+ data |= IOCSR_MISC_FUNC_INT_ENCODE;
+ spin_unlock_irqrestore(&eiointc->lock, flags);
+
+ *value = data;
+ return 0;
+}
+
+int kvm_eiointc_update_status(struct kvm_vcpu *vcpu, unsigned long value, unsigned long mask)
+{
+ struct loongarch_eiointc *eiointc = vcpu->kvm->arch.eiointc;
+ unsigned long old, flags;
+
+ if (!eiointc) {
+ kvm_err("%s: eiointc irqchip not valid!\n", __func__);
+ return -EINVAL;
+ }
+
+ old = 0;
+ spin_lock_irqsave(&eiointc->lock, flags);
+ if (eiointc->status & BIT(EIOINTC_ENABLE))
+ old |= IOCSR_MISC_FUNC_EXT_IOI_EN;
+ if (eiointc->status & BIT(EIOINTC_ENABLE_INT_ENCODE))
+ old |= IOCSR_MISC_FUNC_INT_ENCODE;
+
+ value |= (old & ~mask);
+ eiointc->status &= ~(BIT(EIOINTC_ENABLE_INT_ENCODE) | BIT(EIOINTC_ENABLE));
+ if (value & IOCSR_MISC_FUNC_INT_ENCODE)
+ eiointc->status |= BIT(EIOINTC_ENABLE_INT_ENCODE);
+ if (value & IOCSR_MISC_FUNC_EXT_IOI_EN)
+ eiointc->status |= BIT(EIOINTC_ENABLE);
+ spin_unlock_irqrestore(&eiointc->lock, flags);
+ return 0;
+}
+
static int kvm_eiointc_virt_read(struct kvm_vcpu *vcpu,
struct kvm_io_device *dev,
gpa_t addr, int len, void *val)
@@ -993,6 +1044,15 @@ static int kvm_eiointc_create(struct kvm_device *dev, u32 type)
kfree(s);
return ret;
}
+
+ ret = kvm_loongarch_create_misc(kvm);
+ if (ret < 0) {
+ kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device);
+ kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device_vext);
+ kfree(s);
+ return ret;
+ }
+
kvm->arch.eiointc = s;
return 0;
@@ -1010,6 +1070,7 @@ static void kvm_eiointc_destroy(struct kvm_device *dev)
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);
+ kvm_loongarch_destroy_misc(kvm);
kfree(eiointc);
}
diff --git a/arch/loongarch/kvm/intc/misc.c b/arch/loongarch/kvm/intc/misc.c
new file mode 100644
index 000000000000..edee66afa36e
--- /dev/null
+++ b/arch/loongarch/kvm/intc/misc.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Loongson Technology Corporation Limited
+ */
+#include <asm/kvm_vcpu.h>
+#include <asm/kvm_eiointc.h>
+#include <asm/kvm_misc.h>
+
+static int kvm_misc_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
+ gpa_t addr, int len, void *val)
+{
+ unsigned long data;
+ unsigned int ret;
+
+ addr -= MISC_BASE;
+ if (addr & (len - 1)) {
+ kvm_err("%s: eiointc not aligned addr %llx len %d\n", __func__, addr, len);
+ return -EINVAL;
+ }
+
+ ret = kvm_eiointc_get_status(vcpu, &data);
+ if (ret)
+ return ret;
+
+ data = data >> ((addr & 7) * 8);
+ switch (len) {
+ case 1:
+ *(unsigned char *)val = (unsigned char)data;
+ break;
+
+ case 2:
+ *(unsigned short *)val = (unsigned short)data;
+ break;
+
+ case 4:
+ *(unsigned int *)val = (unsigned int)data;
+ break;
+
+ default:
+ *(unsigned long *)val = data;
+ break;
+ }
+
+ return 0;
+}
+
+static int kvm_misc_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
+ gpa_t addr, int len, const void *val)
+{
+ unsigned long data, mask;
+ unsigned int shift;
+
+ addr -= MISC_BASE;
+ if (addr & (len - 1)) {
+ kvm_err("%s: eiointc not aligned addr %llx len %d\n", __func__, addr, len);
+ return -EINVAL;
+ }
+
+ shift = (addr & 7) * 8;
+ switch (len) {
+ case 1:
+ data = *(unsigned char *)val;
+ mask = 0xFF;
+ mask = mask << shift;
+ data = data << shift;
+ break;
+
+ case 2:
+ data = *(unsigned short *)val;
+ mask = 0xFFFF;
+ mask = mask << shift;
+ data = data << shift;
+ break;
+
+ case 4:
+ data = *(unsigned int *)val;
+ mask = UINT_MAX;
+ mask = mask << shift;
+ data = data << shift;
+ break;
+
+ default:
+ data = *(unsigned long *)val;
+ mask = ULONG_MAX;
+ mask = mask << shift;
+ data = data << shift;
+ break;
+ }
+
+ return kvm_eiointc_update_status(vcpu, data, mask);
+}
+
+static const struct kvm_io_device_ops kvm_misc_ops = {
+ .read = kvm_misc_read,
+ .write = kvm_misc_write,
+};
+
+int kvm_loongarch_create_misc(struct kvm *kvm)
+{
+ struct kvm_io_device *device;
+ int ret;
+
+ if (kvm->arch.misc_created)
+ return 0;
+
+ device = &kvm->arch.misc;
+ kvm_iodevice_init(device, &kvm_misc_ops);
+ ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, MISC_BASE, MISC_SIZE, device);
+ if (ret < 0)
+ return ret;
+
+ kvm->arch.misc_created = true;
+ return 0;
+}
+
+void kvm_loongarch_destroy_misc(struct kvm *kvm)
+{
+ struct kvm_io_device *device;
+
+ if (kvm->arch.misc_created) {
+ device = &kvm->arch.misc;
+ kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, device);
+ kvm->arch.misc_created = false;
+ }
+}
base-commit: 52da431bf03b5506203bca27fe14a97895c80faf
--
2.39.3