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 } 206