1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2025 Loongson Technology Corporation Limited 4 */ 5 6 #include <linux/kvm_host.h> 7 #include <asm/kvm_csr.h> 8 #include <asm/kvm_dmsintc.h> 9 #include <asm/kvm_vcpu.h> 10 11 void dmsintc_inject_irq(struct kvm_vcpu *vcpu) 12 { 13 unsigned int i; 14 unsigned long vector[4], old; 15 struct dmsintc_state *ds = &vcpu->arch.dmsintc_state; 16 17 if (!ds) 18 return; 19 20 for (i = 0; i < 4; i++) { 21 old = atomic64_read(&(ds->vector_map[i])); 22 if (old) 23 vector[i] = atomic64_xchg(&(ds->vector_map[i]), 0); 24 } 25 26 if (vector[0]) { 27 old = kvm_read_hw_gcsr(LOONGARCH_CSR_ISR0); 28 kvm_write_hw_gcsr(LOONGARCH_CSR_ISR0, vector[0] | old); 29 } 30 31 if (vector[1]) { 32 old = kvm_read_hw_gcsr(LOONGARCH_CSR_ISR1); 33 kvm_write_hw_gcsr(LOONGARCH_CSR_ISR1, vector[1] | old); 34 } 35 36 if (vector[2]) { 37 old = kvm_read_hw_gcsr(LOONGARCH_CSR_ISR2); 38 kvm_write_hw_gcsr(LOONGARCH_CSR_ISR2, vector[2] | old); 39 } 40 41 if (vector[3]) { 42 old = kvm_read_hw_gcsr(LOONGARCH_CSR_ISR3); 43 kvm_write_hw_gcsr(LOONGARCH_CSR_ISR3, vector[3] | old); 44 } 45 } 46 47 int dmsintc_deliver_msi_to_vcpu(struct kvm *kvm, 48 struct kvm_vcpu *vcpu, u32 vector, int level) 49 { 50 struct kvm_interrupt vcpu_irq; 51 struct dmsintc_state *ds = &vcpu->arch.dmsintc_state; 52 53 if (!level) 54 return 0; 55 if (!vcpu || vector >= 256) 56 return -EINVAL; 57 if (!ds) 58 return -ENODEV; 59 60 vcpu_irq.irq = INT_AVEC; 61 set_bit(vector, (unsigned long *)&ds->vector_map); 62 kvm_vcpu_ioctl_interrupt(vcpu, &vcpu_irq); 63 kvm_vcpu_kick(vcpu); 64 65 return 0; 66 } 67 68 int dmsintc_set_irq(struct kvm *kvm, u64 addr, int data, int level) 69 { 70 unsigned int irq, cpu; 71 struct kvm_vcpu *vcpu; 72 73 irq = (addr >> AVEC_IRQ_SHIFT) & AVEC_IRQ_MASK; 74 cpu = (addr >> AVEC_CPU_SHIFT) & kvm->arch.dmsintc->cpu_mask; 75 if (cpu >= KVM_MAX_VCPUS) 76 return -EINVAL; 77 vcpu = kvm_get_vcpu_by_cpuid(kvm, cpu); 78 if (!vcpu) 79 return -EINVAL; 80 81 return dmsintc_deliver_msi_to_vcpu(kvm, vcpu, irq, level); 82 } 83 84 static int kvm_dmsintc_ctrl_access(struct kvm_device *dev, 85 struct kvm_device_attr *attr, bool is_write) 86 { 87 int addr = attr->attr; 88 unsigned long cpu_bit, val; 89 void __user *data = (void __user *)attr->addr; 90 struct loongarch_dmsintc *s = dev->kvm->arch.dmsintc; 91 92 switch (addr) { 93 case KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_BASE: 94 if (is_write) { 95 if (copy_from_user(&val, data, sizeof(s->msg_addr_base))) 96 return -EFAULT; 97 if (s->msg_addr_base) 98 return -EFAULT; /* Duplicate setting are not allowed. */ 99 if ((val & (BIT(AVEC_CPU_SHIFT) - 1)) != 0) 100 return -EINVAL; 101 s->msg_addr_base = val; 102 cpu_bit = find_first_bit((unsigned long *)&(s->msg_addr_base), 64) - AVEC_CPU_SHIFT; 103 cpu_bit = min(cpu_bit, AVEC_CPU_BIT); 104 s->cpu_mask = GENMASK(cpu_bit - 1, 0) & AVEC_CPU_MASK; 105 } 106 break; 107 case KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_SIZE: 108 if (is_write) { 109 if (copy_from_user(&val, data, sizeof(s->msg_addr_size))) 110 return -EFAULT; 111 if (s->msg_addr_size) 112 return -EFAULT; /*Duplicate setting are not allowed. */ 113 s->msg_addr_size = val; 114 } 115 break; 116 default: 117 kvm_err("%s: unknown dmsintc register, addr = %d\n", __func__, addr); 118 return -ENXIO; 119 } 120 121 return 0; 122 } 123 124 static int kvm_dmsintc_set_attr(struct kvm_device *dev, 125 struct kvm_device_attr *attr) 126 { 127 switch (attr->group) { 128 case KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL: 129 return kvm_dmsintc_ctrl_access(dev, attr, true); 130 default: 131 kvm_err("%s: unknown group (%d)\n", __func__, attr->group); 132 return -EINVAL; 133 } 134 } 135 136 static int kvm_dmsintc_create(struct kvm_device *dev, u32 type) 137 { 138 struct kvm *kvm; 139 struct loongarch_dmsintc *s; 140 141 if (!dev) { 142 kvm_err("%s: kvm_device ptr is invalid!\n", __func__); 143 return -EINVAL; 144 } 145 146 kvm = dev->kvm; 147 if (kvm->arch.dmsintc) { 148 kvm_err("%s: LoongArch DMSINTC has already been created!\n", __func__); 149 return -EINVAL; 150 } 151 152 s = kzalloc(sizeof(struct loongarch_dmsintc), GFP_KERNEL); 153 if (!s) 154 return -ENOMEM; 155 156 s->kvm = kvm; 157 kvm->arch.dmsintc = s; 158 159 return 0; 160 } 161 162 static void kvm_dmsintc_destroy(struct kvm_device *dev) 163 { 164 165 if (!dev || !dev->kvm || !dev->kvm->arch.dmsintc) 166 return; 167 168 kfree(dev->kvm->arch.dmsintc); 169 kfree(dev); 170 } 171 172 static struct kvm_device_ops kvm_dmsintc_dev_ops = { 173 .name = "kvm-loongarch-dmsintc", 174 .create = kvm_dmsintc_create, 175 .destroy = kvm_dmsintc_destroy, 176 .set_attr = kvm_dmsintc_set_attr, 177 }; 178 179 int kvm_loongarch_register_dmsintc_device(void) 180 { 181 return kvm_register_device_ops(&kvm_dmsintc_dev_ops, KVM_DEV_TYPE_LOONGARCH_DMSINTC); 182 } 183