Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'loongarch-kvm-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson into HEAD

LoongArch KVM changes for v7.1

1. Use CSR_CRMD_PLV in kvm_arch_vcpu_in_kernel().
2. Let vcpu_is_preempted() a macro & some enhanments.
3. Add DMSINTC irqchip in kernel support.
4. Add KVM PMU test cases for tools/selftests.

+593 -45
+27
arch/loongarch/include/asm/kvm_dmsintc.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2025 Loongson Technology Corporation Limited 4 + */ 5 + 6 + #ifndef __ASM_KVM_DMSINTC_H 7 + #define __ASM_KVM_DMSINTC_H 8 + 9 + #include <linux/kvm_types.h> 10 + 11 + struct loongarch_dmsintc { 12 + struct kvm *kvm; 13 + uint64_t msg_addr_base; 14 + uint64_t msg_addr_size; 15 + uint32_t cpu_mask; 16 + }; 17 + 18 + struct dmsintc_state { 19 + atomic64_t vector_map[4]; 20 + }; 21 + 22 + int kvm_loongarch_register_dmsintc_device(void); 23 + void dmsintc_inject_irq(struct kvm_vcpu *vcpu); 24 + int dmsintc_set_irq(struct kvm *kvm, u64 addr, int data, int level); 25 + int dmsintc_deliver_msi_to_vcpu(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 vector, int level); 26 + 27 + #endif
+3
arch/loongarch/include/asm/kvm_host.h
··· 20 20 #include <asm/inst.h> 21 21 #include <asm/kvm_mmu.h> 22 22 #include <asm/kvm_ipi.h> 23 + #include <asm/kvm_dmsintc.h> 23 24 #include <asm/kvm_eiointc.h> 24 25 #include <asm/kvm_pch_pic.h> 25 26 #include <asm/loongarch.h> ··· 134 133 s64 time_offset; 135 134 struct kvm_context __percpu *vmcs; 136 135 struct loongarch_ipi *ipi; 136 + struct loongarch_dmsintc *dmsintc; 137 137 struct loongarch_eiointc *eiointc; 138 138 struct loongarch_pch_pic *pch_pic; 139 139 }; ··· 249 247 struct kvm_mp_state mp_state; 250 248 /* ipi state */ 251 249 struct ipi_state ipi_state; 250 + struct dmsintc_state dmsintc_state; 252 251 /* cpucfg */ 253 252 u32 cpucfg[KVM_MAX_CPUCFG_REGS]; 254 253
+2 -1
arch/loongarch/include/asm/kvm_pch_pic.h
··· 68 68 uint64_t pch_pic_base; 69 69 }; 70 70 71 + struct kvm_kernel_irq_routing_entry; 71 72 int kvm_loongarch_register_pch_pic_device(void); 72 73 void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level); 73 - void pch_msi_set_irq(struct kvm *kvm, int irq, int level); 74 + int pch_msi_set_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, int level); 74 75 75 76 #endif /* __ASM_KVM_PCH_PIC_H */
+22 -4
arch/loongarch/include/asm/qspinlock.h
··· 2 2 #ifndef _ASM_LOONGARCH_QSPINLOCK_H 3 3 #define _ASM_LOONGARCH_QSPINLOCK_H 4 4 5 + #include <asm/kvm_para.h> 5 6 #include <linux/jump_label.h> 6 7 7 8 #ifdef CONFIG_PARAVIRT 8 - 9 + DECLARE_STATIC_KEY_FALSE(virt_preempt_key); 9 10 DECLARE_STATIC_KEY_FALSE(virt_spin_lock_key); 11 + DECLARE_PER_CPU(struct kvm_steal_time, steal_time); 10 12 11 13 #define virt_spin_lock virt_spin_lock 12 14 ··· 36 34 return true; 37 35 } 38 36 39 - #define vcpu_is_preempted vcpu_is_preempted 40 - 41 - bool vcpu_is_preempted(int cpu); 37 + /* 38 + * Macro is better than inline function here 39 + * With macro, parameter cpu is parsed only when it is used. 40 + * With inline function, parameter cpu is parsed even though it is not used. 41 + * This may cause cache line thrashing across NUMA nodes. 42 + */ 43 + #define vcpu_is_preempted(cpu) \ 44 + ({ \ 45 + bool __val; \ 46 + \ 47 + if (!static_branch_unlikely(&virt_preempt_key)) \ 48 + __val = false; \ 49 + else { \ 50 + struct kvm_steal_time *src; \ 51 + src = &per_cpu(steal_time, cpu); \ 52 + __val = !!(READ_ONCE(src->preempted) & KVM_VCPU_PREEMPTED); \ 53 + } \ 54 + __val; \ 55 + }) 42 56 43 57 #endif /* CONFIG_PARAVIRT */ 44 58
+4
arch/loongarch/include/uapi/asm/kvm.h
··· 155 155 #define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000006 156 156 #define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0 157 157 158 + #define KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL 0x40000007 159 + #define KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_BASE 0x0 160 + #define KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_SIZE 0x1 161 + 158 162 #endif /* __UAPI_ASM_LOONGARCH_KVM_H */
+2 -14
arch/loongarch/kernel/paravirt.c
··· 10 10 #include <asm/paravirt.h> 11 11 12 12 static int has_steal_clock; 13 - static DEFINE_PER_CPU(struct kvm_steal_time, steal_time) __aligned(64); 14 - static DEFINE_STATIC_KEY_FALSE(virt_preempt_key); 13 + DEFINE_STATIC_KEY_FALSE(virt_preempt_key); 15 14 DEFINE_STATIC_KEY_FALSE(virt_spin_lock_key); 15 + DEFINE_PER_CPU(struct kvm_steal_time, steal_time) __aligned(64); 16 16 17 17 static bool steal_acc = true; 18 18 ··· 260 260 261 261 return 0; 262 262 } 263 - 264 - bool vcpu_is_preempted(int cpu) 265 - { 266 - struct kvm_steal_time *src; 267 - 268 - if (!static_branch_unlikely(&virt_preempt_key)) 269 - return false; 270 - 271 - src = &per_cpu(steal_time, cpu); 272 - return !!(src->preempted & KVM_VCPU_PREEMPTED); 273 - } 274 - EXPORT_SYMBOL(vcpu_is_preempted); 275 263 #endif 276 264 277 265 static void pv_cpu_reboot(void *unused)
+1
arch/loongarch/kvm/Makefile
··· 17 17 kvm-y += vcpu.o 18 18 kvm-y += vm.o 19 19 kvm-y += intc/ipi.o 20 + kvm-y += intc/dmsintc.o 20 21 kvm-y += intc/eiointc.o 21 22 kvm-y += intc/pch_pic.o 22 23 kvm-y += irqfd.o
+182
arch/loongarch/kvm/intc/dmsintc.c
··· 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 + }
+13 -2
arch/loongarch/kvm/intc/pch_pic.c
··· 3 3 * Copyright (C) 2024 Loongson Technology Corporation Limited 4 4 */ 5 5 6 + #include <asm/kvm_dmsintc.h> 6 7 #include <asm/kvm_eiointc.h> 7 8 #include <asm/kvm_pch_pic.h> 8 9 #include <asm/kvm_vcpu.h> ··· 68 67 } 69 68 70 69 /* msi irq handler */ 71 - void pch_msi_set_irq(struct kvm *kvm, int irq, int level) 70 + int pch_msi_set_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, int level) 72 71 { 73 - eiointc_set_irq(kvm->arch.eiointc, irq, level); 72 + u64 msg_addr = (((u64)e->msi.address_hi) << 32) | e->msi.address_lo; 73 + 74 + if (cpu_has_msgint && kvm->arch.dmsintc && 75 + msg_addr >= kvm->arch.dmsintc->msg_addr_base && 76 + msg_addr < (kvm->arch.dmsintc->msg_addr_base + kvm->arch.dmsintc->msg_addr_size)) { 77 + return dmsintc_set_irq(kvm, msg_addr, e->msi.data, level); 78 + } 79 + 80 + eiointc_set_irq(kvm->arch.eiointc, e->msi.data, level); 81 + 82 + return 0; 74 83 } 75 84 76 85 static int loongarch_pch_pic_read(struct loongarch_pch_pic *s, gpa_t addr, int len, void *val)
+2
arch/loongarch/kvm/interrupt.c
··· 7 7 #include <linux/errno.h> 8 8 #include <asm/kvm_csr.h> 9 9 #include <asm/kvm_vcpu.h> 10 + #include <asm/kvm_dmsintc.h> 10 11 11 12 static unsigned int priority_to_irq[EXCCODE_INT_NUM] = { 12 13 [INT_TI] = CPU_TIMER, ··· 34 33 irq = priority_to_irq[priority]; 35 34 36 35 if (kvm_guest_has_msgint(&vcpu->arch) && (priority == INT_AVEC)) { 36 + dmsintc_inject_irq(vcpu); 37 37 set_gcsr_estat(irq); 38 38 return 1; 39 39 }
+5 -5
arch/loongarch/kvm/irqfd.c
··· 29 29 if (!level) 30 30 return -1; 31 31 32 - pch_msi_set_irq(kvm, e->msi.data, level); 33 - 34 - return 0; 32 + return pch_msi_set_irq(kvm, e, level); 35 33 } 36 34 37 35 /* ··· 69 71 int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, 70 72 struct kvm *kvm, int irq_source_id, int level, bool line_status) 71 73 { 74 + if (!level) 75 + return -EWOULDBLOCK; 76 + 72 77 switch (e->type) { 73 78 case KVM_IRQ_ROUTING_IRQCHIP: 74 79 pch_pic_set_irq(kvm->arch.pch_pic, e->irqchip.pin, level); 75 80 return 0; 76 81 case KVM_IRQ_ROUTING_MSI: 77 - pch_msi_set_irq(kvm, e->msi.data, level); 78 - return 0; 82 + return pch_msi_set_irq(kvm, e, level); 79 83 default: 80 84 return -EWOULDBLOCK; 81 85 }
+10 -4
arch/loongarch/kvm/main.c
··· 271 271 * memory with new address is changed on other VCPUs. 272 272 */ 273 273 set_gcsr_llbctl(CSR_LLBCTL_WCLLB); 274 - } 275 274 276 - /* Restore GSTAT(0x50).vpid */ 277 - vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; 278 - change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); 275 + /* Restore GSTAT(0x50).vpid */ 276 + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; 277 + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); 278 + } 279 279 } 280 280 281 281 void kvm_init_vmcs(struct kvm *kvm) ··· 416 416 417 417 /* Register LoongArch PCH-PIC interrupt controller interface. */ 418 418 ret = kvm_loongarch_register_pch_pic_device(); 419 + if (ret) 420 + return ret; 421 + 422 + /* Register LoongArch DMSINTC interrupt contrroller interface */ 423 + if (cpu_has_msgint) 424 + ret = kvm_loongarch_register_dmsintc_device(); 419 425 420 426 return ret; 421 427 }
+16 -13
arch/loongarch/kvm/vcpu.c
··· 149 149 kvm_restore_host_pmu(vcpu); 150 150 } 151 151 152 - static void kvm_check_pmu(struct kvm_vcpu *vcpu) 153 - { 154 - if (kvm_check_request(KVM_REQ_PMU, vcpu)) { 155 - kvm_own_pmu(vcpu); 156 - vcpu->arch.aux_inuse |= KVM_LARCH_PMU; 157 - } 158 - } 159 - 160 152 static void kvm_update_stolen_time(struct kvm_vcpu *vcpu) 161 153 { 162 154 u32 version; ··· 224 232 static void kvm_late_check_requests(struct kvm_vcpu *vcpu) 225 233 { 226 234 lockdep_assert_irqs_disabled(); 235 + 236 + if (!kvm_request_pending(vcpu)) 237 + return; 238 + 239 + if (kvm_check_request(KVM_REQ_PMU, vcpu)) { 240 + kvm_own_pmu(vcpu); 241 + vcpu->arch.aux_inuse |= KVM_LARCH_PMU; 242 + } 243 + 227 244 if (kvm_check_request(KVM_REQ_TLB_FLUSH_GPA, vcpu)) 228 245 if (vcpu->arch.flush_gpa != INVALID_GPA) { 229 246 kvm_flush_tlb_gpa(vcpu, vcpu->arch.flush_gpa); ··· 313 312 /* Make sure the vcpu mode has been written */ 314 313 smp_store_mb(vcpu->mode, IN_GUEST_MODE); 315 314 kvm_check_vpid(vcpu); 316 - kvm_check_pmu(vcpu); 317 315 318 316 /* 319 317 * Called after function kvm_check_vpid() ··· 320 320 * and it may also clear KVM_REQ_TLB_FLUSH_GPA pending bit 321 321 */ 322 322 kvm_late_check_requests(vcpu); 323 - vcpu->arch.host_eentry = csr_read64(LOONGARCH_CSR_EENTRY); 324 323 /* Clear KVM_LARCH_SWCSR_LATEST as CSR will change when enter guest */ 325 324 vcpu->arch.aux_inuse &= ~KVM_LARCH_SWCSR_LATEST; 326 325 ··· 401 402 val = gcsr_read(LOONGARCH_CSR_CRMD); 402 403 preempt_enable(); 403 404 404 - return (val & CSR_PRMD_PPLV) == PLV_KERN; 405 + return (val & CSR_CRMD_PLV) == PLV_KERN; 405 406 } 406 407 407 408 #ifdef CONFIG_GUEST_PERF_EVENTS ··· 1627 1628 * If not, any old guest state from this vCPU will have been clobbered. 1628 1629 */ 1629 1630 context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu); 1630 - if (migrated || (context->last_vcpu != vcpu)) 1631 + if (migrated || (context->last_vcpu != vcpu)) { 1632 + context->last_vcpu = vcpu; 1631 1633 vcpu->arch.aux_inuse &= ~KVM_LARCH_HWCSR_USABLE; 1632 - context->last_vcpu = vcpu; 1634 + vcpu->arch.host_eentry = csr_read64(LOONGARCH_CSR_EENTRY); 1635 + } 1633 1636 1634 1637 /* Restore timer state regardless */ 1635 1638 kvm_restore_timer(vcpu); ··· 1699 1698 1700 1699 /* Restore Root.GINTC from unused Guest.GINTC register */ 1701 1700 write_csr_gintc(csr->csrs[LOONGARCH_CSR_GINTC]); 1701 + write_csr_gstat(csr->csrs[LOONGARCH_CSR_GSTAT]); 1702 1702 1703 1703 /* 1704 1704 * We should clear linked load bit to break interrupted atomics. This ··· 1795 1793 kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ISR3); 1796 1794 } 1797 1795 1796 + csr->csrs[LOONGARCH_CSR_GSTAT] = read_csr_gstat(); 1798 1797 vcpu->arch.aux_inuse |= KVM_LARCH_SWCSR_LATEST; 1799 1798 1800 1799 out:
+2
include/uapi/linux/kvm.h
··· 1225 1225 #define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC 1226 1226 KVM_DEV_TYPE_LOONGARCH_PCHPIC, 1227 1227 #define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC 1228 + KVM_DEV_TYPE_LOONGARCH_DMSINTC, 1229 + #define KVM_DEV_TYPE_LOONGARCH_DMSINTC KVM_DEV_TYPE_LOONGARCH_DMSINTC 1228 1230 1229 1231 KVM_DEV_TYPE_MAX, 1230 1232
+2 -1
tools/testing/selftests/kvm/Makefile.kvm
··· 222 222 TEST_GEN_PROGS_riscv += rseq_test 223 223 TEST_GEN_PROGS_riscv += steal_time 224 224 225 - TEST_GEN_PROGS_loongarch = arch_timer 225 + TEST_GEN_PROGS_loongarch = loongarch/pmu_test 226 + TEST_GEN_PROGS_loongarch += arch_timer 226 227 TEST_GEN_PROGS_loongarch += coalesced_io_test 227 228 TEST_GEN_PROGS_loongarch += demand_paging_test 228 229 TEST_GEN_PROGS_loongarch += dirty_log_perf_test
+66
tools/testing/selftests/kvm/include/loongarch/pmu.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * LoongArch PMU specific interface 4 + */ 5 + #ifndef SELFTEST_KVM_PMU_H 6 + #define SELFTEST_KVM_PMU_H 7 + 8 + #include "processor.h" 9 + 10 + #define LOONGARCH_CPUCFG6 0x6 11 + #define CPUCFG6_PMP BIT(0) 12 + #define CPUCFG6_PAMVER GENMASK(3, 1) 13 + #define CPUCFG6_PMNUM GENMASK(7, 4) 14 + #define CPUCFG6_PMNUM_SHIFT 4 15 + #define CPUCFG6_PMBITS GENMASK(13, 8) 16 + #define CPUCFG6_PMBITS_SHIFT 8 17 + #define CPUCFG6_UPM BIT(14) 18 + 19 + /* Performance Counter registers */ 20 + #define LOONGARCH_CSR_PERFCTRL0 0x200 /* perf event 0 config */ 21 + #define LOONGARCH_CSR_PERFCNTR0 0x201 /* perf event 0 count value */ 22 + #define LOONGARCH_CSR_PERFCTRL1 0x202 /* perf event 1 config */ 23 + #define LOONGARCH_CSR_PERFCNTR1 0x203 /* perf event 1 count value */ 24 + #define LOONGARCH_CSR_PERFCTRL2 0x204 /* perf event 2 config */ 25 + #define LOONGARCH_CSR_PERFCNTR2 0x205 /* perf event 2 count value */ 26 + #define LOONGARCH_CSR_PERFCTRL3 0x206 /* perf event 3 config */ 27 + #define LOONGARCH_CSR_PERFCNTR3 0x207 /* perf event 3 count value */ 28 + #define CSR_PERFCTRL_PLV0 BIT(16) 29 + #define CSR_PERFCTRL_PLV1 BIT(17) 30 + #define CSR_PERFCTRL_PLV2 BIT(18) 31 + #define CSR_PERFCTRL_PLV3 BIT(19) 32 + #define CSR_PERFCTRL_PMIE BIT(20) 33 + #define PMU_ENVENT_ENABLED (CSR_PERFCTRL_PLV0 | CSR_PERFCTRL_PLV1 | CSR_PERFCTRL_PLV2 | CSR_PERFCTRL_PLV3) 34 + 35 + /* Hardware event codes (from LoongArch perf_event.c */ 36 + #define LOONGARCH_PMU_EVENT_CYCLES 0x00 /* CPU cycles */ 37 + #define LOONGARCH_PMU_EVENT_INSTR_RETIRED 0x01 /* Instructions retired */ 38 + #define PERF_COUNT_HW_BRANCH_INSTRUCTIONS 0x02 /* Branch instructions */ 39 + #define PERF_COUNT_HW_BRANCH_MISSES 0x03 /* Branch misses */ 40 + 41 + #define NUM_LOOPS 1000 42 + #define EXPECTED_INSTR_MIN (NUM_LOOPS + 10) /* Loop + overhead */ 43 + #define EXPECTED_CYCLES_MIN NUM_LOOPS /* At least 1 cycle per iteration */ 44 + #define UPPER_BOUND (10 * NUM_LOOPS) 45 + 46 + #define PMU_OVERFLOW (1ULL << 63) 47 + 48 + static inline void pmu_irq_enable(void) 49 + { 50 + unsigned long val; 51 + 52 + val = csr_read(LOONGARCH_CSR_ECFG); 53 + val |= ECFGF_PMU; 54 + csr_write(val, LOONGARCH_CSR_ECFG); 55 + } 56 + 57 + static inline void pmu_irq_disable(void) 58 + { 59 + unsigned long val; 60 + 61 + val = csr_read(LOONGARCH_CSR_ECFG); 62 + val &= ~ECFGF_PMU; 63 + csr_write(val, LOONGARCH_CSR_ECFG); 64 + } 65 + 66 + #endif
+15
tools/testing/selftests/kvm/include/loongarch/processor.h
··· 83 83 #define LOONGARCH_CSR_PRMD 0x1 84 84 #define LOONGARCH_CSR_EUEN 0x2 85 85 #define LOONGARCH_CSR_ECFG 0x4 86 + #define ECFGB_PMU 10 87 + #define ECFGF_PMU (BIT_ULL(ECFGB_PMU)) 86 88 #define ECFGB_TIMER 11 87 89 #define ECFGF_TIMER (BIT_ULL(ECFGB_TIMER)) 88 90 #define LOONGARCH_CSR_ESTAT 0x5 /* Exception status */ ··· 92 90 #define CSR_ESTAT_EXC_WIDTH 6 93 91 #define CSR_ESTAT_EXC (0x3f << CSR_ESTAT_EXC_SHIFT) 94 92 #define EXCCODE_INT 0 /* Interrupt */ 93 + #define INT_PMI 10 /* PMU interrupt */ 95 94 #define INT_TI 11 /* Timer interrupt*/ 96 95 #define LOONGARCH_CSR_ERA 0x6 /* ERA */ 97 96 #define LOONGARCH_CSR_BADV 0x7 /* Bad virtual address */ ··· 130 127 #define LOONGARCH_CSR_TLBREHI 0x8e 131 128 #define CSR_TLBREHI_PS_SHIFT 0 132 129 #define CSR_TLBREHI_PS (0x3fUL << CSR_TLBREHI_PS_SHIFT) 130 + 131 + #define read_cpucfg(reg) \ 132 + ({ \ 133 + register unsigned long __v; \ 134 + __asm__ __volatile__( \ 135 + "cpucfg %0, %1\n\t" \ 136 + : "=r" (__v) \ 137 + : "r" (reg) \ 138 + : "memory"); \ 139 + __v; \ 140 + }) 133 141 134 142 #define csr_read(csr) \ 135 143 ({ \ ··· 192 178 handler_fn exception_handlers[VECTOR_NUM]; 193 179 }; 194 180 181 + void loongarch_vcpu_setup(struct kvm_vcpu *vcpu); 195 182 void vm_init_descriptor_tables(struct kvm_vm *vm); 196 183 void vm_install_exception_handler(struct kvm_vm *vm, int vector, handler_fn handler); 197 184
+14 -1
tools/testing/selftests/kvm/lib/loongarch/processor.c
··· 5 5 6 6 #include <asm/kvm.h> 7 7 #include "kvm_util.h" 8 + #include "pmu.h" 8 9 #include "processor.h" 9 10 #include "ucall_common.h" 10 11 ··· 252 251 __vcpu_set_reg(vcpu, id, val); 253 252 } 254 253 254 + static void loongarch_set_cpucfg(struct kvm_vcpu *vcpu, uint64_t id, uint64_t val) 255 + { 256 + uint64_t cfgid; 257 + 258 + cfgid = KVM_REG_LOONGARCH_CPUCFG | KVM_REG_SIZE_U64 | 8 * id; 259 + __vcpu_set_reg(vcpu, cfgid, val); 260 + } 261 + 255 262 static void loongarch_get_csr(struct kvm_vcpu *vcpu, uint64_t id, void *addr) 256 263 { 257 264 uint64_t csrid; ··· 276 267 __vcpu_set_reg(vcpu, csrid, val); 277 268 } 278 269 279 - static void loongarch_vcpu_setup(struct kvm_vcpu *vcpu) 270 + void loongarch_vcpu_setup(struct kvm_vcpu *vcpu) 280 271 { 281 272 int width; 273 + unsigned int cfg; 282 274 unsigned long val; 283 275 struct kvm_vm *vm = vcpu->vm; 284 276 ··· 291 281 default: 292 282 TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); 293 283 } 284 + 285 + cfg = read_cpucfg(LOONGARCH_CPUCFG6); 286 + loongarch_set_cpucfg(vcpu, LOONGARCH_CPUCFG6, cfg); 294 287 295 288 /* kernel mode and page enable mode */ 296 289 val = PLV_KERN | CSR_CRMD_PG;
+205
tools/testing/selftests/kvm/loongarch/pmu_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * LoongArch KVM PMU event counting test 4 + * 5 + * Test hardware event counting: CPU_CYCLES, INSTR_RETIRED, 6 + * BRANCH_INSTRUCTIONS and BRANCH_MISSES. 7 + */ 8 + #include <linux/bitops.h> 9 + #include "kvm_util.h" 10 + #include "pmu.h" 11 + #include "loongarch/processor.h" 12 + 13 + static int pmu_irq_count; 14 + 15 + /* Check PMU support */ 16 + static bool has_pmu_support(void) 17 + { 18 + uint32_t cfg6; 19 + 20 + /* Read CPUCFG6 to check PMU */ 21 + cfg6 = read_cpucfg(LOONGARCH_CPUCFG6); 22 + 23 + /* Check PMU present bit */ 24 + if (!(cfg6 & CPUCFG6_PMP)) 25 + return false; 26 + 27 + /* Check that at least one counter exists */ 28 + if (((cfg6 & CPUCFG6_PMNUM) >> CPUCFG6_PMNUM_SHIFT) == 0) 29 + return false; 30 + 31 + return true; 32 + } 33 + 34 + /* Dump PMU capabilities */ 35 + static void dump_pmu_caps(void) 36 + { 37 + uint32_t cfg6; 38 + int nr_counters, counter_bits; 39 + 40 + cfg6 = read_cpucfg(LOONGARCH_CPUCFG6); 41 + nr_counters = ((cfg6 & CPUCFG6_PMNUM) >> CPUCFG6_PMNUM_SHIFT) + 1; 42 + counter_bits = ((cfg6 & CPUCFG6_PMBITS) >> CPUCFG6_PMBITS_SHIFT) + 1; 43 + 44 + pr_info("PMU capabilities:\n"); 45 + pr_info(" Counters present: %s\n", cfg6 & CPUCFG6_PMP ? "yes" : "no"); 46 + pr_info(" Number of counters: %d\n", nr_counters); 47 + pr_info(" Counter width: %d bits\n", counter_bits); 48 + } 49 + 50 + /* Guest test code - runs inside VM */ 51 + static void guest_pmu_base_test(void) 52 + { 53 + int i; 54 + uint32_t cfg6, pmnum; 55 + uint64_t cnt[4]; 56 + 57 + cfg6 = read_cpucfg(LOONGARCH_CPUCFG6); 58 + pmnum = (cfg6 >> 4) & 0xf; 59 + GUEST_PRINTF("CPUCFG6 = 0x%x\n", cfg6); 60 + GUEST_PRINTF("PMP enabled: %s\n", (cfg6 & 0x1) ? "YES" : "NO"); 61 + GUEST_PRINTF("Number of counters (PMNUM): %x\n", pmnum + 1); 62 + GUEST_ASSERT(pmnum == 3); 63 + 64 + GUEST_PRINTF("Clean csr_perfcntr0-3\n"); 65 + csr_write(0, LOONGARCH_CSR_PERFCNTR0); 66 + csr_write(0, LOONGARCH_CSR_PERFCNTR1); 67 + csr_write(0, LOONGARCH_CSR_PERFCNTR2); 68 + csr_write(0, LOONGARCH_CSR_PERFCNTR3); 69 + GUEST_PRINTF("Set csr_perfctrl0 for cycles event\n"); 70 + csr_write(PMU_ENVENT_ENABLED | 71 + LOONGARCH_PMU_EVENT_CYCLES, LOONGARCH_CSR_PERFCTRL0); 72 + GUEST_PRINTF("Set csr_perfctrl1 for instr_retired event\n"); 73 + csr_write(PMU_ENVENT_ENABLED | 74 + LOONGARCH_PMU_EVENT_INSTR_RETIRED, LOONGARCH_CSR_PERFCTRL1); 75 + GUEST_PRINTF("Set csr_perfctrl2 for branch_instructions event\n"); 76 + csr_write(PMU_ENVENT_ENABLED | 77 + PERF_COUNT_HW_BRANCH_INSTRUCTIONS, LOONGARCH_CSR_PERFCTRL2); 78 + GUEST_PRINTF("Set csr_perfctrl3 for branch_misses event\n"); 79 + csr_write(PMU_ENVENT_ENABLED | 80 + PERF_COUNT_HW_BRANCH_MISSES, LOONGARCH_CSR_PERFCTRL3); 81 + 82 + for (i = 0; i < NUM_LOOPS; i++) 83 + cpu_relax(); 84 + 85 + cnt[0] = csr_read(LOONGARCH_CSR_PERFCNTR0); 86 + GUEST_PRINTF("csr_perfcntr0 is %lx\n", cnt[0]); 87 + cnt[1] = csr_read(LOONGARCH_CSR_PERFCNTR1); 88 + GUEST_PRINTF("csr_perfcntr1 is %lx\n", cnt[1]); 89 + cnt[2] = csr_read(LOONGARCH_CSR_PERFCNTR2); 90 + GUEST_PRINTF("csr_perfcntr2 is %lx\n", cnt[2]); 91 + cnt[3] = csr_read(LOONGARCH_CSR_PERFCNTR3); 92 + GUEST_PRINTF("csr_perfcntr3 is %lx\n", cnt[3]); 93 + 94 + GUEST_PRINTF("assert csr_perfcntr0 >EXPECTED_CYCLES_MIN && csr_perfcntr0 < UPPER_BOUND\n"); 95 + GUEST_ASSERT(cnt[0] > EXPECTED_CYCLES_MIN && cnt[0] < UPPER_BOUND); 96 + GUEST_PRINTF("assert csr_perfcntr1 > EXPECTED_INSTR_MIN && csr_perfcntr1 < UPPER_BOUND\n"); 97 + GUEST_ASSERT(cnt[1] > EXPECTED_INSTR_MIN && cnt[1] < UPPER_BOUND); 98 + GUEST_PRINTF("assert csr_perfcntr2 > 0 && csr_perfcntr2 < UPPER_BOUND\n"); 99 + GUEST_ASSERT(cnt[2] > 0 && cnt[2] < UPPER_BOUND); 100 + GUEST_PRINTF("assert csr_perfcntr3 > 0 && csr_perfcntr3 < UPPER_BOUND\n"); 101 + GUEST_ASSERT(cnt[3] > 0 && cnt[3] < UPPER_BOUND); 102 + } 103 + 104 + static void guest_irq_handler(struct ex_regs *regs) 105 + { 106 + unsigned int intid; 107 + 108 + pmu_irq_disable(); 109 + intid = !!(regs->estat & BIT(INT_PMI)); 110 + GUEST_ASSERT_EQ(intid, 1); 111 + GUEST_PRINTF("Get PMU interrupt\n"); 112 + WRITE_ONCE(pmu_irq_count, pmu_irq_count + 1); 113 + } 114 + 115 + static void guest_pmu_interrupt_test(void) 116 + { 117 + uint64_t cnt; 118 + 119 + csr_write(PMU_OVERFLOW - 1, LOONGARCH_CSR_PERFCNTR0); 120 + csr_write(PMU_ENVENT_ENABLED | CSR_PERFCTRL_PMIE | LOONGARCH_PMU_EVENT_CYCLES, LOONGARCH_CSR_PERFCTRL0); 121 + 122 + cpu_relax(); 123 + 124 + GUEST_ASSERT_EQ(pmu_irq_count, 1); 125 + cnt = csr_read(LOONGARCH_CSR_PERFCNTR0); 126 + GUEST_PRINTF("csr_perfcntr0 is %lx\n", cnt); 127 + GUEST_PRINTF("PMU interrupt test success\n"); 128 + 129 + } 130 + 131 + static void guest_code(void) 132 + { 133 + guest_pmu_base_test(); 134 + 135 + pmu_irq_enable(); 136 + local_irq_enable(); 137 + guest_pmu_interrupt_test(); 138 + 139 + GUEST_DONE(); 140 + } 141 + 142 + int main(int argc, char *argv[]) 143 + { 144 + int ret = 0; 145 + struct kvm_device_attr attr; 146 + struct kvm_vcpu *vcpu; 147 + struct kvm_vm *vm; 148 + struct ucall uc; 149 + 150 + /* Check host KVM PMU support */ 151 + if (!has_pmu_support()) { 152 + print_skip("PMU not supported by host hardware\n"); 153 + dump_pmu_caps(); 154 + return KSFT_SKIP; 155 + } 156 + pr_info("Host support PMU\n"); 157 + 158 + /* Dump PMU capabilities */ 159 + dump_pmu_caps(); 160 + 161 + vm = vm_create(VM_MODE_P47V47_16K); 162 + vcpu = vm_vcpu_add(vm, 0, guest_code); 163 + 164 + pmu_irq_count = 0; 165 + vm_init_descriptor_tables(vm); 166 + loongarch_vcpu_setup(vcpu); 167 + vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler); 168 + sync_global_to_guest(vm, pmu_irq_count); 169 + 170 + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL, 171 + attr.attr = KVM_LOONGARCH_VM_FEAT_PMU, 172 + 173 + ret = ioctl(vm->fd, KVM_HAS_DEVICE_ATTR, &attr); 174 + 175 + if (ret == 0) { 176 + pr_info("PMU is enabled in VM\n"); 177 + } else { 178 + print_skip("PMU not enabled by VM config\n"); 179 + return KSFT_SKIP; 180 + } 181 + 182 + while (1) { 183 + vcpu_run(vcpu); 184 + switch (get_ucall(vcpu, &uc)) { 185 + case UCALL_PRINTF: 186 + printf("%s", (const char *)uc.buffer); 187 + break; 188 + case UCALL_DONE: 189 + printf("PMU test PASSED\n"); 190 + goto done; 191 + case UCALL_ABORT: 192 + printf("PMU test FAILED\n"); 193 + ret = -1; 194 + goto done; 195 + default: 196 + printf("Unexpected exit\n"); 197 + ret = -1; 198 + goto done; 199 + } 200 + } 201 + 202 + done: 203 + kvm_vm_free(vm); 204 + return ret; 205 + }