xref: /linux/tools/testing/selftests/kvm/loongarch/pmu_test.c (revision 01f492e1817e858d1712f2489d0afbaa552f417b)
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