xref: /kvm-unit-tests/arm/pmu.c (revision d81bb7a30470271a4eca1e372b1c8d7a214b5bdc)
14244065bSChristopher Covington /*
24244065bSChristopher Covington  * Test the ARM Performance Monitors Unit (PMU).
34244065bSChristopher Covington  *
44244065bSChristopher Covington  * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
54244065bSChristopher Covington  * Copyright (C) 2016, Red Hat Inc, Wei Huang <wei@redhat.com>
64244065bSChristopher Covington  *
74244065bSChristopher Covington  * This program is free software; you can redistribute it and/or modify it
84244065bSChristopher Covington  * under the terms of the GNU Lesser General Public License version 2.1 and
94244065bSChristopher Covington  * only version 2.1 as published by the Free Software Foundation.
104244065bSChristopher Covington  *
114244065bSChristopher Covington  * This program is distributed in the hope that it will be useful, but WITHOUT
124244065bSChristopher Covington  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
134244065bSChristopher Covington  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
144244065bSChristopher Covington  * for more details.
154244065bSChristopher Covington  */
164244065bSChristopher Covington #include "libcflat.h"
174244065bSChristopher Covington #include "asm/barrier.h"
184244065bSChristopher Covington #include "asm/sysreg.h"
194244065bSChristopher Covington #include "asm/processor.h"
204244065bSChristopher Covington 
21*d81bb7a3SChristopher Covington #define PMU_PMCR_E         (1 << 0)
22*d81bb7a3SChristopher Covington #define PMU_PMCR_C         (1 << 2)
23*d81bb7a3SChristopher Covington #define PMU_PMCR_LC        (1 << 6)
244244065bSChristopher Covington #define PMU_PMCR_N_SHIFT   11
254244065bSChristopher Covington #define PMU_PMCR_N_MASK    0x1f
264244065bSChristopher Covington #define PMU_PMCR_ID_SHIFT  16
274244065bSChristopher Covington #define PMU_PMCR_ID_MASK   0xff
284244065bSChristopher Covington #define PMU_PMCR_IMP_SHIFT 24
294244065bSChristopher Covington #define PMU_PMCR_IMP_MASK  0xff
304244065bSChristopher Covington 
314244065bSChristopher Covington #define ID_DFR0_PERFMON_SHIFT 24
324244065bSChristopher Covington #define ID_DFR0_PERFMON_MASK  0xf
334244065bSChristopher Covington 
34*d81bb7a3SChristopher Covington #define PMU_CYCLE_IDX         31
35*d81bb7a3SChristopher Covington 
36*d81bb7a3SChristopher Covington #define NR_SAMPLES 10
37*d81bb7a3SChristopher Covington 
384244065bSChristopher Covington static unsigned int pmu_version;
394244065bSChristopher Covington #if defined(__arm__)
404244065bSChristopher Covington #define PMCR         __ACCESS_CP15(c9, 0, c12, 0)
414244065bSChristopher Covington #define ID_DFR0      __ACCESS_CP15(c0, 0, c1, 2)
42*d81bb7a3SChristopher Covington #define PMSELR       __ACCESS_CP15(c9, 0, c12, 5)
43*d81bb7a3SChristopher Covington #define PMXEVTYPER   __ACCESS_CP15(c9, 0, c13, 1)
44*d81bb7a3SChristopher Covington #define PMCNTENSET   __ACCESS_CP15(c9, 0, c12, 1)
45*d81bb7a3SChristopher Covington #define PMCCNTR32    __ACCESS_CP15(c9, 0, c13, 0)
46*d81bb7a3SChristopher Covington #define PMCCNTR64    __ACCESS_CP15_64(0, c9)
474244065bSChristopher Covington 
484244065bSChristopher Covington static inline uint32_t get_id_dfr0(void) { return read_sysreg(ID_DFR0); }
494244065bSChristopher Covington static inline uint32_t get_pmcr(void) { return read_sysreg(PMCR); }
50*d81bb7a3SChristopher Covington static inline void set_pmcr(uint32_t v) { write_sysreg(v, PMCR); }
51*d81bb7a3SChristopher Covington static inline void set_pmcntenset(uint32_t v) { write_sysreg(v, PMCNTENSET); }
52*d81bb7a3SChristopher Covington 
53*d81bb7a3SChristopher Covington static inline uint64_t get_pmccntr(void)
54*d81bb7a3SChristopher Covington {
55*d81bb7a3SChristopher Covington 	if (pmu_version == 0x3)
56*d81bb7a3SChristopher Covington 		return read_sysreg(PMCCNTR64);
57*d81bb7a3SChristopher Covington 	else
58*d81bb7a3SChristopher Covington 		return read_sysreg(PMCCNTR32);
59*d81bb7a3SChristopher Covington }
60*d81bb7a3SChristopher Covington 
61*d81bb7a3SChristopher Covington /* PMCCFILTR is an obsolete name for PMXEVTYPER31 in ARMv7 */
62*d81bb7a3SChristopher Covington static inline void set_pmccfiltr(uint32_t value)
63*d81bb7a3SChristopher Covington {
64*d81bb7a3SChristopher Covington 	write_sysreg(PMU_CYCLE_IDX, PMSELR);
65*d81bb7a3SChristopher Covington 	write_sysreg(value, PMXEVTYPER);
66*d81bb7a3SChristopher Covington 	isb();
67*d81bb7a3SChristopher Covington }
684244065bSChristopher Covington #elif defined(__aarch64__)
694244065bSChristopher Covington static inline uint32_t get_id_dfr0(void) { return read_sysreg(id_dfr0_el1); }
704244065bSChristopher Covington static inline uint32_t get_pmcr(void) { return read_sysreg(pmcr_el0); }
71*d81bb7a3SChristopher Covington static inline void set_pmcr(uint32_t v) { write_sysreg(v, pmcr_el0); }
72*d81bb7a3SChristopher Covington static inline uint64_t get_pmccntr(void) { return read_sysreg(pmccntr_el0); }
73*d81bb7a3SChristopher Covington static inline void set_pmcntenset(uint32_t v) { write_sysreg(v, pmcntenset_el0); }
74*d81bb7a3SChristopher Covington static inline void set_pmccfiltr(uint32_t v) { write_sysreg(v, pmccfiltr_el0); }
754244065bSChristopher Covington #endif
764244065bSChristopher Covington 
774244065bSChristopher Covington /*
784244065bSChristopher Covington  * As a simple sanity check on the PMCR_EL0, ensure the implementer field isn't
794244065bSChristopher Covington  * null. Also print out a couple other interesting fields for diagnostic
804244065bSChristopher Covington  * purposes. For example, as of fall 2016, QEMU TCG mode doesn't implement
814244065bSChristopher Covington  * event counters and therefore reports zero event counters, but hopefully
824244065bSChristopher Covington  * support for at least the instructions event will be added in the future and
834244065bSChristopher Covington  * the reported number of event counters will become nonzero.
844244065bSChristopher Covington  */
854244065bSChristopher Covington static bool check_pmcr(void)
864244065bSChristopher Covington {
874244065bSChristopher Covington 	uint32_t pmcr;
884244065bSChristopher Covington 
894244065bSChristopher Covington 	pmcr = get_pmcr();
904244065bSChristopher Covington 
914244065bSChristopher Covington 	report_info("PMU implementer/ID code/counters: 0x%x(\"%c\")/0x%x/%d",
924244065bSChristopher Covington 		    (pmcr >> PMU_PMCR_IMP_SHIFT) & PMU_PMCR_IMP_MASK,
934244065bSChristopher Covington 		    ((pmcr >> PMU_PMCR_IMP_SHIFT) & PMU_PMCR_IMP_MASK) ? : ' ',
944244065bSChristopher Covington 		    (pmcr >> PMU_PMCR_ID_SHIFT) & PMU_PMCR_ID_MASK,
954244065bSChristopher Covington 		    (pmcr >> PMU_PMCR_N_SHIFT) & PMU_PMCR_N_MASK);
964244065bSChristopher Covington 
974244065bSChristopher Covington 	return ((pmcr >> PMU_PMCR_IMP_SHIFT) & PMU_PMCR_IMP_MASK) != 0;
984244065bSChristopher Covington }
994244065bSChristopher Covington 
100*d81bb7a3SChristopher Covington /*
101*d81bb7a3SChristopher Covington  * Ensure that the cycle counter progresses between back-to-back reads.
102*d81bb7a3SChristopher Covington  */
103*d81bb7a3SChristopher Covington static bool check_cycles_increase(void)
104*d81bb7a3SChristopher Covington {
105*d81bb7a3SChristopher Covington 	bool success = true;
106*d81bb7a3SChristopher Covington 
107*d81bb7a3SChristopher Covington 	/* init before event access, this test only cares about cycle count */
108*d81bb7a3SChristopher Covington 	set_pmcntenset(1 << PMU_CYCLE_IDX);
109*d81bb7a3SChristopher Covington 	set_pmccfiltr(0); /* count cycles in EL0, EL1, but not EL2 */
110*d81bb7a3SChristopher Covington 
111*d81bb7a3SChristopher Covington 	set_pmcr(get_pmcr() | PMU_PMCR_LC | PMU_PMCR_C | PMU_PMCR_E);
112*d81bb7a3SChristopher Covington 
113*d81bb7a3SChristopher Covington 	for (int i = 0; i < NR_SAMPLES; i++) {
114*d81bb7a3SChristopher Covington 		uint64_t a, b;
115*d81bb7a3SChristopher Covington 
116*d81bb7a3SChristopher Covington 		a = get_pmccntr();
117*d81bb7a3SChristopher Covington 		b = get_pmccntr();
118*d81bb7a3SChristopher Covington 
119*d81bb7a3SChristopher Covington 		if (a >= b) {
120*d81bb7a3SChristopher Covington 			printf("Read %"PRId64" then %"PRId64".\n", a, b);
121*d81bb7a3SChristopher Covington 			success = false;
122*d81bb7a3SChristopher Covington 			break;
123*d81bb7a3SChristopher Covington 		}
124*d81bb7a3SChristopher Covington 	}
125*d81bb7a3SChristopher Covington 
126*d81bb7a3SChristopher Covington 	set_pmcr(get_pmcr() & ~PMU_PMCR_E);
127*d81bb7a3SChristopher Covington 
128*d81bb7a3SChristopher Covington 	return success;
129*d81bb7a3SChristopher Covington }
130*d81bb7a3SChristopher Covington 
1314244065bSChristopher Covington /* Return FALSE if no PMU found, otherwise return TRUE */
1324244065bSChristopher Covington bool pmu_probe(void)
1334244065bSChristopher Covington {
1344244065bSChristopher Covington 	uint32_t dfr0;
1354244065bSChristopher Covington 
1364244065bSChristopher Covington 	/* probe pmu version */
1374244065bSChristopher Covington 	dfr0 = get_id_dfr0();
1384244065bSChristopher Covington 	pmu_version = (dfr0 >> ID_DFR0_PERFMON_SHIFT) & ID_DFR0_PERFMON_MASK;
1394244065bSChristopher Covington 
1404244065bSChristopher Covington 	if (pmu_version)
1414244065bSChristopher Covington 		report_info("PMU version: %d", pmu_version);
1424244065bSChristopher Covington 
1434244065bSChristopher Covington 	return pmu_version;
1444244065bSChristopher Covington }
1454244065bSChristopher Covington 
1464244065bSChristopher Covington int main(void)
1474244065bSChristopher Covington {
1484244065bSChristopher Covington 	if (!pmu_probe()) {
1494244065bSChristopher Covington 		printf("No PMU found, test skipped...\n");
1504244065bSChristopher Covington 		return report_summary();
1514244065bSChristopher Covington 	}
1524244065bSChristopher Covington 
1534244065bSChristopher Covington 	report_prefix_push("pmu");
1544244065bSChristopher Covington 
1554244065bSChristopher Covington 	report("Control register", check_pmcr());
156*d81bb7a3SChristopher Covington 	report("Monotonically increasing cycle count", check_cycles_increase());
1574244065bSChristopher Covington 
1584244065bSChristopher Covington 	return report_summary();
1594244065bSChristopher Covington }
160