KVM: arm64: PMUv3 Event Counter Tests
Subsystems (3)
v3 cover KVM: arm64: PMUv3 Event Counter Tests
This series implements tests exercising the PMUv3 event counters. It tests both the 32-bit and 64-bit versions. Overflow interrupts also are checked. Those tests only are written for arm64.
It allowed to reveal some issues related to SW_INCR implementation (esp. related to 64-bit implementation), some problems related to 32-bit <-> 64-bit transitions and consistency of enabled states of odd and event counters. Fixes now are upstream.
Drew kindly provided "arm64: Provide read/write_sysreg_s".
All PMU tests can be launched with:
./run_tests.sh -g pmu
Tests also can be launched individually. For example:
./arm-run arm/pmu.flat -append 'pmu-chained-sw-incr'
With KVM:
- On TX2, I have some random failures due to MEM_ACCESS event measured with a great disparity. This is not observed on other machines I have access to.
The series can be found at:
https://github.com/eauger/kut/tree/pmu_event_counters_v3
History:
v2 -> v3:
- Took into account numerous comments from Drew
- Applied Andre's R-b when code has not changed too much
- Don't check PMCR.IMP anymore
- better handling of version
- put simple SW_INCR tests for easier TCG testing
v1 -> v2:
- Took into account Andre's comments except I did not use cnbz in the mem_access_loop() and I did not use @loop directly. Those changes had side effects I cannot explain on the tests. Anyway I think this can be improved later on.
- removed [kvm-unit-tests PATCH 09/10] arm/arm64: gic:
Introduce setup_irq() helper
RFC -> v1:
- Use new report() proto
- Style cleanup
- do not warn about ARM spec recommendations
- add a comment about PMCEID0/1 splits
Andrew Jones (1):
arm64: Provide read/write_sysreg_s
Eric Auger (11):
arm: pmu: Let pmu tests take a sub-test parameter
arm: pmu: Don't check PMCR.IMP anymore
arm: pmu: Add a pmu struct
arm: pmu: Introduce defines for PMU versions
arm: pmu: Check Required Event Support
arm: pmu: Basic event counter Tests
arm: pmu: Test SW_INCR event count
arm: pmu: Test chained counters
arm: pmu: test 32-bit <-> 64-bit transitions
arm: gic: Introduce gic_irq_set_clr_enable() helper
arm: pmu: Test overflow interrupts
arm/pmu.c | 839 +++++++++++++++++++++++++++++++++++++++-- arm/unittests.cfg | 61 ++- lib/arm/asm/gic.h | 4 + lib/arm/gic.c | 31 ++ lib/arm64/asm/sysreg.h | 17 + lib/bitops.h | 3 + 6 files changed, 918 insertions(+), 37 deletions(-)
Signature · 1 line
2.20.1
PATCH v3 1/12 arm64: Provide read/write_sysreg_s
From: Andrew Jones <[email protected]>
Sometimes we need to test access to system registers which are missing assembler mnemonics.
--- lib/arm64/asm/sysreg.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
lib/arm64/asm/sysreg.h +11 -0
@@ -38,6 +38,17 @@ asm volatile("msr " xstr(r) ", %x0" : : "rZ" (__val)); \ } while (0) +#define read_sysreg_s (r) ({ \ + u64 __val; \ + asm volatile("mrs_s %0, " xstr(r) : "=r" (__val)); \ + __val; \ +}) + +#define write_sysreg_s (v, r) do { \ + u64 __val = (u64)v; \ + asm volatile("msr_s " xstr(r) ", %x0" : : "rZ" (__val));\ +} while (0) + asm(" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n" " .equ .L__reg_num_x\\num, \\num\n"
Signature · 1 line
2.20.1
PATCH v3 2/12 arm: pmu: Let pmu tests take a sub-test parameter
In reply to Eric AugerAs we intend to introduce more PMU tests, let's add a sub-test parameter that will allow to categorize them. Existing tests are in the cycle-counter category.
---
v2 -> v3:
- added report_prefix_pop()
--- arm/pmu.c | 25 ++++++++++++++++--------- arm/unittests.cfg | 7 ++++--- 2 files changed, 20 insertions(+), 12 deletions(-)
arm/pmu.c +16 -9
@@ -287,22 +287,29 @@ int main(int argc, char *argv[]) {int cpi =0 ; -if (argc >1 ) - cpi =atol (argv[1 ]); -if (!pmu_probe ()) {printf ("No PMU found, test skipped...\n" );return report_summary (); } +if (argc <2 ) +report_abort ("no test specified" ); +report_prefix_push ("pmu" ); -report (check_pmcr (),"Control register" ); -report (check_cycles_increase (), -"Monotonically increasing cycle count" ); -report (check_cpi (cpi),"Cycle/instruction ratio" ); - -pmccntr64_test (); +if (strcmp (argv[1 ],"cycle-counter" ) ==0 ) { +report_prefix_push (argv[1 ]); +if (argc >2 ) + cpi =atol (argv[2 ]); +report (check_pmcr (),"Control register" ); +report (check_cycles_increase (), +"Monotonically increasing cycle count" ); +report (check_cpi (cpi),"Cycle/instruction ratio" ); +pmccntr64_test (); +report_prefix_pop (); + }else { +report_abort ("Unknown sub-test '%s'" , argv[1 ]); + }return report_summary (); }
arm/unittests.cfg +4 -3
@@ -61,21 +61,22 @@ file = pci-test.flat groups = pci # Test PMU support -[pmu] +[pmu-cycle-counter] file = pmu.flat groups = pmu +extra_params = -append 'cycle-counter 0' # Test PMU support (TCG) with -icount IPC=1 #[pmu-tcg-icount-1] #file = pmu.flat -#extra_params = -icount 0 -append '1' +#extra_params = -icount 0 -append 'cycle-counter 1' #groups = pmu #accel = tcg # Test PMU support (TCG) with -icount IPC=256 #[pmu-tcg-icount-256] #file = pmu.flat -#extra_params = -icount 8 -append '256' +#extra_params = -icount 8 -append 'cycle-counter 256' #groups = pmu #accel = tcg
Signature · 1 line
2.20.1
PATCH v3 3/12 arm: pmu: Don't check PMCR.IMP anymore
In reply to Eric Augercheck_pmcr() checks the IMP field is different than 0. However A zero IMP field is permitted by the architecture, meaning the MIDR_EL1 should be looked at instead. This causes TCG to fail this test on '-cpu max' because in that case PMCR.IMP is set equal to MIDR_EL1.Implementer which is 0.
So let's remove the check_pmcr() test and just print PMCR info in the pmu_probe() function.
--- arm/pmu.c | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-)
arm/pmu.c +14 -25
@@ -134,29 +134,6 @@ static inline void precise_instrs_loop(int loop, uint32_t pmcr) }#endif -/* -* As a simple sanity check on the PMCR_EL0, ensure the implementer field isn't -* null. Also print out a couple other interesting fields for diagnostic -* purposes. For example, as of fall 2016, QEMU TCG mode doesn't implement -* event counters and therefore reports zero event counters, but hopefully -* support for at least the instructions event will be added in the future and -* the reported number of event counters will become nonzero. -*/ -static bool check_pmcr (void ) -{ -uint32_t pmcr; - - pmcr =get_pmcr (); - -report_info ("PMU implementer/ID code/counters: %#x(\"%c\")/%#x/%d" , - (pmcr >>PMU_PMCR_IMP_SHIFT ) &PMU_PMCR_IMP_MASK , - ((pmcr >>PMU_PMCR_IMP_SHIFT ) &PMU_PMCR_IMP_MASK ) ? :' ' , - (pmcr >>PMU_PMCR_ID_SHIFT ) &PMU_PMCR_ID_MASK , - (pmcr >>PMU_PMCR_N_SHIFT ) &PMU_PMCR_N_MASK ); - -return ((pmcr >>PMU_PMCR_IMP_SHIFT ) &PMU_PMCR_IMP_MASK ) !=0 ; -} -/* * Ensure that the cycle counter progresses between back-to-back reads. */
@@ -278,9 +255,22 @@ static void pmccntr64_test(void)/* Return FALSE if no PMU found, otherwise return TRUE */ static bool pmu_probe (void ) { +uint32_t pmcr; + pmu_version =get_pmu_version (); +if (pmu_version ==0 || pmu_version ==0xf ) +return false; +report_info ("PMU version: %d" , pmu_version); -return pmu_version !=0 && pmu_version !=0xf ; + + pmcr =get_pmcr (); +report_info ("PMU implementer/ID code/counters: %#x(\"%c\")/%#x/%d" , + (pmcr >>PMU_PMCR_IMP_SHIFT ) &PMU_PMCR_IMP_MASK , + ((pmcr >>PMU_PMCR_IMP_SHIFT ) &PMU_PMCR_IMP_MASK ) ? :' ' , + (pmcr >>PMU_PMCR_ID_SHIFT ) &PMU_PMCR_ID_MASK , + (pmcr >>PMU_PMCR_N_SHIFT ) &PMU_PMCR_N_MASK ); + +return true; }int main (int argc,char *argv[])
@@ -301,7 +291,6 @@ int main(int argc, char *argv[])report_prefix_push (argv[1 ]);if (argc >2 ) cpi =atol (argv[2 ]); -report (check_pmcr (),"Control register" );report (check_cycles_increase (),"Monotonically increasing cycle count" );report (check_cpi (cpi),"Cycle/instruction ratio" );
Signature · 1 line
2.20.1
PATCH v3 4/12 arm: pmu: Add a pmu struct
In reply to Eric AugerThis struct aims at storing information potentially used by all tests such as the pmu version, the read-only part of the PMCR, the number of implemented event counters, ...
---
v2 -> v3:
- Fix pmcr_ro and add a comment
--- arm/pmu.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-)
arm/pmu.c +21 -8
@@ -33,7 +33,14 @@#define NR_SAMPLES 10 -static unsigned int pmu_version; +struct pmu { +unsigned int version; +unsigned int nb_implemented_counters; +uint32_t pmcr_ro; +}; + +static struct pmu pmu; +#if defined(__arm__)#define ID_DFR0_PERFMON_SHIFT 24#define ID_DFR0_PERFMON_MASK 0xf
@@ -242,7 +249,7 @@ static bool check_cpi(int cpi)static void pmccntr64_test (void ) {#ifdef __arm__ -if (pmu_version ==0x3 ) { +if (pmu.version ==0x3 ) {if (ERRATA (9e3f7a296940 )) {write_sysreg (0xdead ,PMCCNTR64 );report (read_sysreg (PMCCNTR64 ) ==0xdead ,"pmccntr64" );
@@ -257,18 +264,24 @@ static bool pmu_probe(void) {uint32_t pmcr; - pmu_version =get_pmu_version (); -if (pmu_version ==0 || pmu_version ==0xf ) + pmu.version =get_pmu_version (); +if (pmu.version ==0 || pmu.version ==0xf )return false; -report_info ("PMU version: %d" , pmu_version); +report_info ("PMU version: %d" , pmu.version); pmcr =get_pmcr (); -report_info ("PMU implementer/ID code/counters: %#x(\"%c\")/%#x/%d" , +report_info ("PMU implementer/ID code: %#x(\"%c\")/%#x" , (pmcr >>PMU_PMCR_IMP_SHIFT ) &PMU_PMCR_IMP_MASK , ((pmcr >>PMU_PMCR_IMP_SHIFT ) &PMU_PMCR_IMP_MASK ) ? :' ' , - (pmcr >>PMU_PMCR_ID_SHIFT ) &PMU_PMCR_ID_MASK , - (pmcr >>PMU_PMCR_N_SHIFT ) &PMU_PMCR_N_MASK ); + (pmcr >>PMU_PMCR_ID_SHIFT ) &PMU_PMCR_ID_MASK ); + +/* store read-only and RES0 fields of the PMCR bottom-half*/ + pmu.pmcr_ro = pmcr &0xFFFFFF00 ; + pmu.nb_implemented_counters = + (pmcr >>PMU_PMCR_N_SHIFT ) &PMU_PMCR_N_MASK ; +report_info ("Implements %d event counters" , + pmu.nb_implemented_counters);return true; }
Signature · 1 line
2.20.1
PATCH v3 5/12 arm: pmu: Introduce defines for PMU versions
In reply to Eric AugerIntroduce some defines encoding the different PMU versions. v3 is encoded differently in 32 and 64 bits.
--- arm/pmu.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-)
arm/pmu.c +21 -5
@@ -45,6 +45,15 @@ static struct pmu pmu;#define ID_DFR0_PERFMON_SHIFT 24#define ID_DFR0_PERFMON_MASK 0xf +#define ID_DFR0_PMU_NOTIMPL 0b0000 +#define ID_DFR0_PMU_V1 0b0001 +#define ID_DFR0_PMU_V2 0b0010 +#define ID_DFR0_PMU_V3 0b0011 +#define ID_DFR0_PMU_V3_8_1 0b0100 +#define ID_DFR0_PMU_V3_8_4 0b0101 +#define ID_DFR0_PMU_V3_8_5 0b0110 +#define ID_DFR0_PMU_IMPDEF 0b1111 +#define PMCR __ACCESS_CP15(c9, 0, c12, 0)#define ID_DFR0 __ACCESS_CP15(c0, 0, c1, 2)#define PMSELR __ACCESS_CP15(c9, 0, c12, 5)
@@ -105,6 +114,13 @@ static inline void precise_instrs_loop(int loop, uint32_t pmcr)#define ID_AA64DFR0_PERFMON_SHIFT 8#define ID_AA64DFR0_PERFMON_MASK 0xf +#define ID_DFR0_PMU_NOTIMPL 0b0000 +#define ID_DFR0_PMU_V3 0b0001 +#define ID_DFR0_PMU_V3_8_1 0b0100 +#define ID_DFR0_PMU_V3_8_4 0b0101 +#define ID_DFR0_PMU_V3_8_5 0b0110 +#define ID_DFR0_PMU_IMPDEF 0b1111 +static inline uint32_t get_id_aa64dfr0 (void ) {return read_sysreg (id_aa64dfr0_el1); }static inline uint32_t get_pmcr (void ) {return read_sysreg (pmcr_el0); }static inline void set_pmcr (uint32_t v) {write_sysreg (v, pmcr_el0); }
@@ -116,7 +132,7 @@ static inline void set_pmccfiltr(uint32_t v) { write_sysreg(v, pmccfiltr_el0); }static inline uint8_t get_pmu_version (void ) {uint8_t ver = (get_id_aa64dfr0 () >>ID_AA64DFR0_PERFMON_SHIFT ) &ID_AA64DFR0_PERFMON_MASK ; -return ver ==1 ?3 : ver; +return ver; }/*
@@ -249,7 +265,7 @@ static bool check_cpi(int cpi)static void pmccntr64_test (void ) {#ifdef __arm__ -if (pmu.version ==0x3 ) { +if (pmu.version ==ID_DFR0_PMU_V3 ) {if (ERRATA (9e3f7a296940 )) {write_sysreg (0xdead ,PMCCNTR64 );report (read_sysreg (PMCCNTR64 ) ==0xdead ,"pmccntr64" );
@@ -262,13 +278,13 @@ static void pmccntr64_test(void)/* Return FALSE if no PMU found, otherwise return TRUE */ static bool pmu_probe (void ) { -uint32_t pmcr; +uint32_t pmcr =get_pmcr (); pmu.version =get_pmu_version (); -if (pmu.version ==0 || pmu.version ==0xf ) +if (pmu.version ==ID_DFR0_PMU_NOTIMPL || pmu.version ==ID_DFR0_PMU_IMPDEF )return false; -report_info ("PMU version: %d" , pmu.version); +report_info ("PMU version: 0x%x" , pmu.version); pmcr =get_pmcr (); report_info("PMU implementer/ID code: %#x(\"%c\")/%#x",
Signature · 1 line
2.20.1
PATCH v3 6/12 arm: pmu: Check Required Event Support
In reply to Eric AugerIf event counters are implemented check the common events required by the PMUv3 are implemented.
Some are unconditionally required (SW_INCR, CPU_CYCLES, either INST_RETIRED or INST_SPEC). Some others only are required if the implementation implements some other features.
Check those wich are unconditionally required.
This test currently fails on TCG as neither INST_RETIRED or INST_SPEC are supported.
---
v2 -> v3:
- use 0x%x instead %d in trace
- pmu.version >= ID_DFR0_PMU_v3_8_1
- added prefix pop
- assert instead of abort, inverse assert and test
- add defines for used events and common events
- given the changes I did not apply Andre's R-b
- introduce and use upper_32_bits()/lower_32_bits()
- added pmu prefix to the test name
v1 -> v2:
- fix is_event_supported()
- fix boolean condition for PMU v4
- fix PMCEID0 definition
RFC ->v1:
- add a comment to explain the PMCEID0/1 splits
--- arm/pmu.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++ arm/unittests.cfg | 6 ++++ lib/bitops.h | 3 ++ 3 files changed, 86 insertions(+)
arm/pmu.c +77 -0
@@ -18,6 +18,7 @@#include "asm/barrier.h" #include "asm/sysreg.h" #include "asm/processor.h" +#include <bitops.h> #define PMU_PMCR_E (1 << 0)#define PMU_PMCR_C (1 << 2)
@@ -33,6 +34,19 @@#define NR_SAMPLES 10 +/* Some PMU events */ +#define SW_INCR 0x0 +#define INST_RETIRED 0x8 +#define CPU_CYCLES 0x11 +#define INST_PREC 0x1B +#define STALL_FRONTEND 0x23 +#define STALL_BACKEND 0x24 + +#define COMMON_EVENTS_LOW 0x0 +#define COMMON_EVENTS_HIGH 0x3F +#define EXT_COMMON_EVENTS_LOW 0x4000 +#define EXT_COMMON_EVENTS_HIGH 0x403F +struct pmu {unsigned int version;unsigned int nb_implemented_counters;
@@ -110,6 +124,10 @@ static inline void precise_instrs_loop(int loop, uint32_t pmcr) : [pmcr] "r" (pmcr), [z] "r" (0) : "cc"); } + +/* event counter tests only implemented for aarch64 */ +static void test_event_introspection (void ) {} +#elif defined(__aarch64__)#define ID_AA64DFR0_PERFMON_SHIFT 8#define ID_AA64DFR0_PERFMON_MASK 0xf
@@ -155,6 +173,61 @@ static inline void precise_instrs_loop(int loop, uint32_t pmcr) : [pmcr] "r" (pmcr) : "cc"); } + +#define PMCEID1_EL0 sys_reg(3, 3, 9, 12, 7) + +static bool is_event_supported (uint32_t n,bool warn) +{ +uint64_t pmceid0 =read_sysreg (pmceid0_el0); +uint64_t pmceid1 =read_sysreg_s (PMCEID1_EL0 ); +bool supported; +uint64_t reg; + +/* +* The low 32-bits of PMCEID0/1 respectively describe +* event support for events 0-31/32-63. Their High +* 32-bits describe support for extended events +* starting at 0x4000, using the same split. +*/ +assert ((n >=COMMON_EVENTS_LOW && n <=COMMON_EVENTS_HIGH ) || + (n >=EXT_COMMON_EVENTS_LOW && n <=EXT_COMMON_EVENTS_HIGH )); + +if (n <=COMMON_EVENTS_HIGH ) + reg =lower_32_bits (pmceid0) | ((u64 )lower_32_bits (pmceid1) <<32 ); +else + reg =upper_32_bits (pmceid0) | ((u64 )upper_32_bits (pmceid1) <<32 ); + + supported = reg & (1UL << (n &0x3F )); + +if (!supported && warn) +report_info ("event 0x%x is not supported" , n); +return supported; +} + +static void test_event_introspection (void ) +{ +bool required_events; + +if (!pmu.nb_implemented_counters) { +report_skip ("No event counter, skip ..." ); +return ; + } + +/* PMUv3 requires an implementation includes some common events */ + required_events =is_event_supported (SW_INCR , true) && +is_event_supported (CPU_CYCLES , true) && + (is_event_supported (INST_RETIRED , true) || +is_event_supported (INST_PREC , true)); + +if (pmu.version >=ID_DFR0_PMU_V3_8_1 ) { + required_events = required_events && +is_event_supported (STALL_FRONTEND , true) && +is_event_supported (STALL_BACKEND , true); + } + +report (required_events,"Check required events are implemented" ); +} +#endif /*
@@ -325,6 +398,10 @@ int main(int argc, char *argv[])report (check_cpi (cpi),"Cycle/instruction ratio" );pmccntr64_test ();report_prefix_pop (); + }else if (strcmp (argv[1 ],"pmu-event-introspection" ) ==0 ) { +report_prefix_push (argv[1 ]); +test_event_introspection (); +report_prefix_pop (); }else {report_abort ("Unknown sub-test '%s'" , argv[1 ]); }
arm/unittests.cfg +6 -0
@@ -66,6 +66,12 @@ file = pmu.flat groups = pmu extra_params = -append 'cycle-counter 0' +[pmu-event-introspection] +file = pmu.flat +groups = pmu +arch = arm64 +extra_params = -append 'pmu-event-introspection' + # Test PMU support (TCG) with -icount IPC=1 #[pmu-tcg-icount-1] #file = pmu.flat
lib/bitops.h +3 -0
@@ -33,6 +33,9 @@#define GENMASK_ULL (h, l) \ (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) +#define upper_32_bits (n) ((u32)(((n) >> 16) >> 16)) +#define lower_32_bits (n) ((u32)(n)) +#ifndef HAVE_BUILTIN_FLS static inline unsigned long fls (unsigned long word) {
Signature · 1 line
2.20.1
PATCH v3 7/12 arm: pmu: Basic event counter Tests
In reply to Eric AugerAdds the following tests:
- event-counter-config: test event counter configuration
- basic-event-count:
- programs counters #0 and #1 to count 2 required events (resp. CPU_CYCLES and INST_RETIRED). Counter #0 is preset to a value close enough to the 32b
overflow limit so that we check the overflow bit is set after the execution of the asm loop.
- mem-access: counts MEM_ACCESS event on counters #0 and #1 with and without 32-bit overflow.
---
v2 -> v3:
- used BIT() for PMEVTYPER_EXCLUDE_EL1/0
- print the event id in hexa (Peter)
- added prefix pop
- s/write_regn/write_regn_el0
- s/read_regn/read_regn_el0
- remove print_pmevtyper
- moved write/read_regn_el0 into lib/arm64/asm/sysreg.h
- added pmu prefix to the test names
- Kept Andre's R-b as functional code has not changed
v1 -> v2:
- fix PMCNTENSET_EL0 and PMCNTENCLR_EL0 op0
- print PMEVTYPER SH
- properly clobber used regs and add "cc"
- simplify mem_access_loop
--- arm/pmu.c | 248 +++++++++++++++++++++++++++++++++++++++++ arm/unittests.cfg | 18 +++ lib/arm64/asm/sysreg.h | 6 + 3 files changed, 272 insertions(+)
arm/pmu.c +248 -0
@@ -19,9 +19,14 @@#include "asm/sysreg.h" #include "asm/processor.h" #include <bitops.h> +#include <asm/gic.h> #define PMU_PMCR_E (1 << 0) +#define PMU_PMCR_P (1 << 1)#define PMU_PMCR_C (1 << 2) +#define PMU_PMCR_D (1 << 3) +#define PMU_PMCR_X (1 << 4) +#define PMU_PMCR_DP (1 << 5)#define PMU_PMCR_LC (1 << 6)#define PMU_PMCR_N_SHIFT 11#define PMU_PMCR_N_MASK 0x1f
@@ -38,6 +43,7 @@#define SW_INCR 0x0#define INST_RETIRED 0x8#define CPU_CYCLES 0x11 +#define MEM_ACCESS 0x13#define INST_PREC 0x1B#define STALL_FRONTEND 0x23#define STALL_BACKEND 0x24
@@ -47,6 +53,10 @@#define EXT_COMMON_EVENTS_LOW 0x4000#define EXT_COMMON_EVENTS_HIGH 0x403F +#define ALL_SET 0xFFFFFFFF +#define ALL_CLEAR 0x0 +#define PRE_OVERFLOW 0xFFFFFFF0 +struct pmu {unsigned int version;unsigned int nb_implemented_counters;
@@ -127,6 +137,9 @@ static inline void precise_instrs_loop(int loop, uint32_t pmcr)/* event counter tests only implemented for aarch64 */ static void test_event_introspection (void ) {} +static void test_event_counter_config (void ) {} +static void test_basic_event_count (void ) {} +static void test_mem_access (void ) {}#elif defined(__aarch64__)#define ID_AA64DFR0_PERFMON_SHIFT 8
@@ -175,6 +188,11 @@ static inline void precise_instrs_loop(int loop, uint32_t pmcr) }#define PMCEID1_EL0 sys_reg(3, 3, 9, 12, 7) +#define PMCNTENSET_EL0 sys_reg(3, 3, 9, 12, 1) +#define PMCNTENCLR_EL0 sys_reg(3, 3, 9, 12, 2) + +#define PMEVTYPER_EXCLUDE_EL1 BIT(31) +#define PMEVTYPER_EXCLUDE_EL0 BIT(30)static bool is_event_supported (uint32_t n,bool warn) {
@@ -228,6 +246,224 @@ static void test_event_introspection(void) report(required_events, "Check required events are implemented"); } +/* +* Extra instructions inserted by the compiler would be difficult to compensate +* for, so hand assemble everything between, and including, the PMCR accesses +* to start and stop counting. isb instructions are inserted to make sure +* pmccntr read after this function returns the exact instructions executed +* in the controlled block. Loads @loop times the data at @address into x9. +*/ +static void mem_access_loop (void *addr,int loop,uint32_t pmcr) +{ +asmvolatile ( +" msr pmcr_el0, %[pmcr]\n" +" isb\n" +" mov x10, %[loop]\n" +"1: sub x10, x10, #1\n" +" ldr x9, [%[addr]]\n" +" cmp x10, #0x0\n" +" b.gt 1b\n" +" msr pmcr_el0, xzr\n" +" isb\n" + : + : [addr]"r" (addr), [pmcr]"r" (pmcr), [loop]"r" (loop) + :"x9" ,"x10" ,"cc" ); +} + +static void pmu_reset (void ) +{ +/* reset all counters, counting disabled at PMCR level*/ +set_pmcr (pmu.pmcr_ro |PMU_PMCR_LC |PMU_PMCR_C |PMU_PMCR_P ); +/* Disable all counters */ +write_sysreg_s (ALL_SET ,PMCNTENCLR_EL0 ); +/* clear overflow reg */ +write_sysreg (ALL_SET , pmovsclr_el0); +/* disable overflow interrupts on all counters */ +write_sysreg (ALL_SET , pmintenclr_el1); +isb (); +} + +static void test_event_counter_config (void ) +{ +int i; + +if (!pmu.nb_implemented_counters) { +report_skip ("No event counter, skip ..." ); +return ; + } + +pmu_reset (); + +/* +* Test setting through PMESELR/PMXEVTYPER and PMEVTYPERn read, +* select counter 0 +*/ +write_sysreg (1 ,PMSELR_EL0 ); +/* program this counter to count unsupported event */ +write_sysreg (0xEA ,PMXEVTYPER_EL0 ); +write_sysreg (0xdeadbeef ,PMXEVCNTR_EL0 ); +report ((read_regn_el0 (pmevtyper,1 ) &0xFFF ) ==0xEA , +"PMESELR/PMXEVTYPER/PMEVTYPERn" ); +report ((read_regn_el0 (pmevcntr,1 ) ==0xdeadbeef ), +"PMESELR/PMXEVCNTR/PMEVCNTRn" ); + +/* try to configure an unsupported event within the range [0x0, 0x3F] */ +for (i =0 ; i <=0x3F ; i++) { +if (!is_event_supported (i, false)) +break ; + } +if (i >0x3F ) { +report_skip ("pmevtyper: all events within [0x0, 0x3F] are supported" ); +return ; + } + +/* select counter 0 */ +write_sysreg (0 ,PMSELR_EL0 ); +/* program this counter to count unsupported event */ +write_sysreg (i,PMXEVCNTR_EL0 ); +/* read the counter value */ +read_sysreg (PMXEVCNTR_EL0 ); +report (read_sysreg (PMXEVCNTR_EL0 ) == i, +"read of a counter programmed with unsupported event" ); + +} + +static bool satisfy_prerequisites (uint32_t *events,unsigned int nb_events) +{ +int i; + +if (pmu.nb_implemented_counters < nb_events) { +report_skip ("Skip test as number of counters is too small (%d)" , + pmu.nb_implemented_counters); +return false; + } + +for (i =0 ; i < nb_events; i++) { +if (!is_event_supported (events[i], false)) { +report_skip ("Skip test as event 0x%x is not supported" , + events[i]); +return false; + } + } +return true; +} + +static void test_basic_event_count (void ) +{ +uint32_t implemented_counter_mask, non_implemented_counter_mask; +uint32_t counter_mask; +uint32_t events[] = {CPU_CYCLES ,INST_RETIRED }; + +if (!satisfy_prerequisites (events,ARRAY_SIZE (events))) +return ; + + implemented_counter_mask =BIT (pmu.nb_implemented_counters) -1 ; + non_implemented_counter_mask = ~(BIT (31 ) | implemented_counter_mask); + counter_mask = implemented_counter_mask | non_implemented_counter_mask; + +write_regn_el0 (pmevtyper,0 ,CPU_CYCLES |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevtyper,1 ,INST_RETIRED |PMEVTYPER_EXCLUDE_EL0 ); + +/* disable all counters */ +write_sysreg_s (ALL_SET ,PMCNTENCLR_EL0 ); +report (!read_sysreg_s (PMCNTENCLR_EL0 ) && !read_sysreg_s (PMCNTENSET_EL0 ), +"pmcntenclr: disable all counters" ); + +/* +* clear cycle and all event counters and allow counter enablement +* through PMCNTENSET. LC is RES1. +*/ +set_pmcr (pmu.pmcr_ro |PMU_PMCR_LC |PMU_PMCR_C |PMU_PMCR_P ); +isb (); +report (get_pmcr () == (pmu.pmcr_ro |PMU_PMCR_LC ),"pmcr: reset counters" ); + +/* Preset counter #0 to pre overflow value to trigger an overflow */ +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +report (read_regn_el0 (pmevcntr,0 ) ==PRE_OVERFLOW , +"counter #0 preset to pre-overflow value" ); +report (!read_regn_el0 (pmevcntr,1 ),"counter #1 is 0" ); + +/* +* Enable all implemented counters and also attempt to enable +* not supported counters. Counting still is disabled by !PMCR.E +*/ +write_sysreg_s (counter_mask,PMCNTENSET_EL0 ); + +/* check only those implemented are enabled */ +report ((read_sysreg_s (PMCNTENSET_EL0 ) ==read_sysreg_s (PMCNTENCLR_EL0 )) && + (read_sysreg_s (PMCNTENSET_EL0 ) == implemented_counter_mask), +"pmcntenset: enabled implemented_counters" ); + +/* Disable all counters but counters #0 and #1 */ +write_sysreg_s (~0x3 ,PMCNTENCLR_EL0 ); +report ((read_sysreg_s (PMCNTENSET_EL0 ) ==read_sysreg_s (PMCNTENCLR_EL0 )) && + (read_sysreg_s (PMCNTENSET_EL0 ) ==0x3 ), +"pmcntenset: just enabled #0 and #1" ); + +/* clear overflow register */ +write_sysreg (ALL_SET , pmovsclr_el0); +report (!read_sysreg (pmovsclr_el0),"check overflow reg is 0" ); + +/* disable overflow interrupts on all counters*/ +write_sysreg (ALL_SET , pmintenclr_el1); +report (!read_sysreg (pmintenclr_el1), +"pmintenclr_el1=0, all interrupts disabled" ); + +/* enable overflow interrupts on all event counters */ +write_sysreg (implemented_counter_mask | non_implemented_counter_mask, + pmintenset_el1); +report (read_sysreg (pmintenset_el1) == implemented_counter_mask, +"overflow interrupts enabled on all implemented counters" ); + +/* Set PMCR.E, execute asm code and unset PMCR.E */ +precise_instrs_loop (20 , pmu.pmcr_ro |PMU_PMCR_E ); + +report_info ("counter #0 is 0x%lx (CPU_CYCLES)" , +read_regn_el0 (pmevcntr,0 )); +report_info ("counter #1 is 0x%lx (INST_RETIRED)" , +read_regn_el0 (pmevcntr,1 )); + +report_info ("overflow reg = 0x%lx" ,read_sysreg (pmovsclr_el0)); +report (read_sysreg (pmovsclr_el0) &0x1 , +"check overflow happened on #0 only" ); +} + +static void test_mem_access (void ) +{ +void *addr =malloc (PAGE_SIZE ); +uint32_t events[] = {MEM_ACCESS ,MEM_ACCESS }; + +if (!satisfy_prerequisites (events,ARRAY_SIZE (events))) +return ; + +pmu_reset (); + +write_regn_el0 (pmevtyper,0 ,MEM_ACCESS |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevtyper,1 ,MEM_ACCESS |PMEVTYPER_EXCLUDE_EL0 ); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); +isb (); +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report_info ("counter #0 is %ld (MEM_ACCESS)" ,read_regn_el0 (pmevcntr,0 )); +report_info ("counter #1 is %ld (MEM_ACCESS)" ,read_regn_el0 (pmevcntr,1 )); +/* We may measure more than 20 mem access depending on the core */ +report ((read_regn_el0 (pmevcntr,0 ) ==read_regn_el0 (pmevcntr,1 )) && + (read_regn_el0 (pmevcntr,0 ) >=20 ) && !read_sysreg (pmovsclr_el0), +"Ran 20 mem accesses" ); + +pmu_reset (); + +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +write_regn_el0 (pmevcntr,1 ,PRE_OVERFLOW ); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); +isb (); +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report (read_sysreg (pmovsclr_el0) ==0x3 , +"Ran 20 mem accesses with expected overflows on both counters" ); +report_info ("cnt#0 = %ld cnt#1=%ld overflow=0x%lx" , +read_regn_el0 (pmevcntr,0 ),read_regn_el0 (pmevcntr,1 ), +read_sysreg (pmovsclr_el0)); +} +#endif /*
@@ -402,6 +638,18 @@ int main(int argc, char *argv[])report_prefix_push (argv[1 ]);test_event_introspection ();report_prefix_pop (); + }else if (strcmp (argv[1 ],"pmu-event-counter-config" ) ==0 ) { +report_prefix_push (argv[1 ]); +test_event_counter_config (); +report_prefix_pop (); + }else if (strcmp (argv[1 ],"pmu-basic-event-count" ) ==0 ) { +report_prefix_push (argv[1 ]); +test_basic_event_count (); +report_prefix_pop (); + }else if (strcmp (argv[1 ],"pmu-mem-access" ) ==0 ) { +report_prefix_push (argv[1 ]); +test_mem_access (); +report_prefix_pop (); }else {report_abort ("Unknown sub-test '%s'" , argv[1 ]); }
arm/unittests.cfg +18 -0
@@ -72,6 +72,24 @@ groups = pmu arch = arm64 extra_params = -append 'pmu-event-introspection' +[pmu-event-counter-config] +file = pmu.flat +groups = pmu +arch = arm64 +extra_params = -append 'pmu-event-counter-config' + +[pmu-basic-event-count] +file = pmu.flat +groups = pmu +arch = arm64 +extra_params = -append 'pmu-basic-event-count' + +[pmu-mem-access] +file = pmu.flat +groups = pmu +arch = arm64 +extra_params = -append 'pmu-mem-access' + # Test PMU support (TCG) with -icount IPC=1 #[pmu-tcg-icount-1] #file = pmu.flat
lib/arm64/asm/sysreg.h +6 -0
@@ -49,6 +49,12 @@ asm volatile("msr_s " xstr(r) ", %x0" : : "rZ" (__val));\ } while (0) +#define write_regn_el0 (__reg, __n, __val) \ + write_sysreg((__val), __reg ## __n ## _el0) + +#define read_regn_el0 (__reg, __n) \ + read_sysreg(__reg ## __n ## _el0) + asm(" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n" " .equ .L__reg_num_x\\num, \\num\n"
Signature · 6 lines
2.20.1 _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
PATCH v3 8/12 arm: pmu: Test SW_INCR event count
In reply to Eric AugerAdd tests dedicated to SW_INCR event counting.
---
v3: new
- Formerly in chained counter tests but as QEMU does not support chained counters, the whole test was failing. Peter split the test.
--- arm/pmu.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ arm/unittests.cfg | 6 ++++++ 2 files changed, 53 insertions(+)
arm/pmu.c +47 -0
@@ -140,6 +140,7 @@ static void test_event_introspection(void) {}static void test_event_counter_config (void ) {}static void test_basic_event_count (void ) {}static void test_mem_access (void ) {} +static void test_sw_incr (void ) {}#elif defined(__aarch64__)#define ID_AA64DFR0_PERFMON_SHIFT 8
@@ -464,6 +465,48 @@ static void test_mem_access(void) read_sysreg(pmovsclr_el0)); } +static void test_sw_incr (void ) +{ +uint32_t events[] = {SW_INCR ,SW_INCR }; +int i; + +if (!satisfy_prerequisites (events,ARRAY_SIZE (events))) +return ; + +pmu_reset (); + +write_regn_el0 (pmevtyper,0 ,SW_INCR |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevtyper,1 ,SW_INCR |PMEVTYPER_EXCLUDE_EL0 ); +/* enable counters #0 and #1 */ +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); + +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); + +for (i =0 ; i <100 ; i++) +write_sysreg (0x1 , pmswinc_el0); + +report_info ("SW_INCR counter #0 has value %ld" ,read_regn_el0 (pmevcntr,0 )); +report (read_regn_el0 (pmevcntr,0 ) ==PRE_OVERFLOW , +"PWSYNC does not increment if PMCR.E is unset" ); + +pmu_reset (); + +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); +set_pmcr (pmu.pmcr_ro |PMU_PMCR_E ); + +for (i =0 ; i <100 ; i++) +write_sysreg (0x3 , pmswinc_el0); + +report (read_regn_el0 (pmevcntr,0 ) ==84 ,"counter #1 after + 100 SW_INCR" ); +report (read_regn_el0 (pmevcntr,1 ) ==100 , +"counter #0 after + 100 SW_INCR" ); +report_info ("counter values after 100 SW_INCR #0=%ld #1=%ld" , +read_regn_el0 (pmevcntr,0 ),read_regn_el0 (pmevcntr,1 )); +report (read_sysreg (pmovsclr_el0) ==0x1 , +"overflow reg after 100 SW_INCR" ); +} +#endif /*
@@ -650,6 +693,10 @@ int main(int argc, char *argv[])report_prefix_push (argv[1 ]);test_mem_access ();report_prefix_pop (); + }else if (strcmp (argv[1 ],"pmu-sw-incr" ) ==0 ) { +report_prefix_push (argv[1 ]); +test_sw_incr (); +report_prefix_pop (); }else {report_abort ("Unknown sub-test '%s'" , argv[1 ]); }
arm/unittests.cfg +6 -0
@@ -90,6 +90,12 @@ groups = pmu arch = arm64 extra_params = -append 'pmu-mem-access' +[pmu-sw-incr] +file = pmu.flat +groups = pmu +arch = arm64 +extra_params = -append 'pmu-sw-incr' + # Test PMU support (TCG) with -icount IPC=1 #[pmu-tcg-icount-1] #file = pmu.flat
Signature · 1 line
2.20.1
PATCH v3 9/12 arm: pmu: Test chained counters
In reply to Eric AugerAdd 2 tests exercising chained counters. The first one uses CPU_CYCLES and the second one uses SW_INCR.
---
v2 -> v3:
- added prefix pop
- added pmu prefix to the test names
- defines, event array ...
--- arm/pmu.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++ arm/unittests.cfg | 12 ++++++ 2 files changed, 109 insertions(+)
arm/pmu.c +97 -0
@@ -47,6 +47,7 @@#define INST_PREC 0x1B#define STALL_FRONTEND 0x23#define STALL_BACKEND 0x24 +#define CHAIN 0x1E#define COMMON_EVENTS_LOW 0x0#define COMMON_EVENTS_HIGH 0x3F
@@ -141,6 +142,8 @@ static void test_event_counter_config(void) {}static void test_basic_event_count (void ) {}static void test_mem_access (void ) {}static void test_sw_incr (void ) {} +static void test_chained_counters (void ) {} +static void test_chained_sw_incr (void ) {}#elif defined(__aarch64__)#define ID_AA64DFR0_PERFMON_SHIFT 8
@@ -507,6 +510,92 @@ static void test_sw_incr(void) "overflow reg after 100 SW_INCR"); } +static void test_chained_counters (void ) +{ +uint32_t events[] = {CPU_CYCLES ,CHAIN }; + +if (!satisfy_prerequisites (events,ARRAY_SIZE (events))) +return ; + +pmu_reset (); + +write_regn_el0 (pmevtyper,0 ,CPU_CYCLES |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevtyper,1 ,CHAIN |PMEVTYPER_EXCLUDE_EL0 ); +/* enable counters #0 and #1 */ +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); + +precise_instrs_loop (22 , pmu.pmcr_ro |PMU_PMCR_E ); + +report (read_regn_el0 (pmevcntr,1 ) ==1 ,"CHAIN counter #1 incremented" ); +report (!read_sysreg (pmovsclr_el0),"check no overflow is recorded" ); + +/* test 64b overflow */ + +pmu_reset (); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); + +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +write_regn_el0 (pmevcntr,1 ,0x1 ); +precise_instrs_loop (22 , pmu.pmcr_ro |PMU_PMCR_E ); +report_info ("overflow reg = 0x%lx" ,read_sysreg (pmovsclr_el0)); +report (read_regn_el0 (pmevcntr,1 ) ==2 ,"CHAIN counter #1 incremented" ); +report (!read_sysreg (pmovsclr_el0),"check no overflow is recorded" ); + +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +write_regn_el0 (pmevcntr,1 ,ALL_SET ); + +precise_instrs_loop (22 , pmu.pmcr_ro |PMU_PMCR_E ); +report_info ("overflow reg = 0x%lx" ,read_sysreg (pmovsclr_el0)); +report (!read_regn_el0 (pmevcntr,1 ),"CHAIN counter #1 wrapped" ); +report (read_sysreg (pmovsclr_el0) ==0x2 , +"check no overflow is recorded" ); +} + +static void test_chained_sw_incr (void ) +{ +uint32_t events[] = {SW_INCR ,CHAIN }; +int i; + +if (!satisfy_prerequisites (events,ARRAY_SIZE (events))) +return ; + +pmu_reset (); + +write_regn_el0 (pmevtyper,0 ,SW_INCR |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevtyper,1 ,CHAIN |PMEVTYPER_EXCLUDE_EL0 ); +/* enable counters #0 and #1 */ +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); + +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +set_pmcr (pmu.pmcr_ro |PMU_PMCR_E ); +for (i =0 ; i <100 ; i++) +write_sysreg (0x1 , pmswinc_el0); + +report (!read_sysreg (pmovsclr_el0) && (read_regn_el0 (pmevcntr,1 ) ==1 ), +"overflow reg after 100 SW_INCR/CHAIN" ); +report_info ("overflow=0x%lx, #0=%ld #1=%ld" ,read_sysreg (pmovsclr_el0), +read_regn_el0 (pmevcntr,0 ),read_regn_el0 (pmevcntr,1 )); + +/* 64b SW_INCR and overflow on CHAIN counter*/ +pmu_reset (); + +write_regn_el0 (pmevtyper,1 , events[1 ] |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +write_regn_el0 (pmevcntr,1 ,ALL_SET ); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); +set_pmcr (pmu.pmcr_ro |PMU_PMCR_E ); +for (i =0 ; i <100 ; i++) +write_sysreg (0x1 , pmswinc_el0); + +report ((read_sysreg (pmovsclr_el0) ==0x2 ) && + (read_regn_el0 (pmevcntr,1 ) ==0 ) && + (read_regn_el0 (pmevcntr,0 ) ==84 ), +"overflow reg after 100 SW_INCR/CHAIN" ); +report_info ("overflow=0x%lx, #0=%ld #1=%ld" ,read_sysreg (pmovsclr_el0), +read_regn_el0 (pmevcntr,0 ),read_regn_el0 (pmevcntr,1 )); +} +#endif /*
@@ -697,6 +786,14 @@ int main(int argc, char *argv[])report_prefix_push (argv[1 ]);test_sw_incr ();report_prefix_pop (); + }else if (strcmp (argv[1 ],"pmu-chained-counters" ) ==0 ) { +report_prefix_push (argv[1 ]); +test_chained_counters (); +report_prefix_pop (); + }else if (strcmp (argv[1 ],"pmu-chained-sw-incr" ) ==0 ) { +report_prefix_push (argv[1 ]); +test_chained_sw_incr (); +report_prefix_pop (); }else {report_abort ("Unknown sub-test '%s'" , argv[1 ]); }
arm/unittests.cfg +12 -0
@@ -96,6 +96,18 @@ groups = pmu arch = arm64 extra_params = -append 'pmu-sw-incr' +[pmu-chained-counters] +file = pmu.flat +groups = pmu +arch = arm64 +extra_params = -append 'pmu-chained-counters' + +[pmu-chained-sw-incr] +file = pmu.flat +groups = pmu +arch = arm64 +extra_params = -append 'pmu-chained-sw-incr' + # Test PMU support (TCG) with -icount IPC=1 #[pmu-tcg-icount-1] #file = pmu.flat
Signature · 1 line
2.20.1
PATCH v3 10/12 arm: pmu: test 32-bit <-> 64-bit transitions
In reply to Eric AugerTest configurations where we transit from 32b to 64b counters and conversely. Also tests configuration where chain counters are configured but only one counter is enabled.
---
v2 -> v3:
- added prefix pop
--- arm/pmu.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++ arm/unittests.cfg | 6 ++ 2 files changed, 144 insertions(+)
arm/pmu.c +138 -0
@@ -57,6 +57,7 @@#define ALL_SET 0xFFFFFFFF#define ALL_CLEAR 0x0#define PRE_OVERFLOW 0xFFFFFFF0 +#define PRE_OVERFLOW2 0xFFFFFFDCstruct pmu {unsigned int version;
@@ -144,6 +145,7 @@ static void test_mem_access(void) {}static void test_sw_incr (void ) {}static void test_chained_counters (void ) {}static void test_chained_sw_incr (void ) {} +static void test_chain_promotion (void ) {}#elif defined(__aarch64__)#define ID_AA64DFR0_PERFMON_SHIFT 8
@@ -596,6 +598,138 @@ static void test_chained_sw_incr(void) read_regn_el0(pmevcntr, 0), read_regn_el0(pmevcntr, 1)); } +static void test_chain_promotion (void ) +{ +uint32_t events[] = {MEM_ACCESS ,CHAIN }; +void *addr =malloc (PAGE_SIZE ); + +if (!satisfy_prerequisites (events,ARRAY_SIZE (events))) +return ; + +/* Only enable CHAIN counter */ +pmu_reset (); +write_regn_el0 (pmevtyper,0 ,MEM_ACCESS |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevtyper,1 ,CHAIN |PMEVTYPER_EXCLUDE_EL0 ); +write_sysreg_s (0x2 ,PMCNTENSET_EL0 ); +isb (); + +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report (!read_regn_el0 (pmevcntr,0 ), +"chain counter not counting if even counter is disabled" ); + +/* Only enable even counter */ +pmu_reset (); +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +write_sysreg_s (0x1 ,PMCNTENSET_EL0 ); +isb (); + +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report (!read_regn_el0 (pmevcntr,1 ) && (read_sysreg (pmovsclr_el0) ==0x1 ), +"odd counter did not increment on overflow if disabled" ); +report_info ("MEM_ACCESS counter #0 has value %ld" , +read_regn_el0 (pmevcntr,0 )); +report_info ("CHAIN counter #1 has value %ld" , +read_regn_el0 (pmevcntr,1 )); +report_info ("overflow counter %ld" ,read_sysreg (pmovsclr_el0)); + +/* start at 0xFFFFFFDC, +20 with CHAIN enabled, +20 with CHAIN disabled */ +pmu_reset (); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW2 ); +isb (); + +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report_info ("MEM_ACCESS counter #0 has value 0x%lx" , +read_regn_el0 (pmevcntr,0 )); + +/* disable the CHAIN event */ +write_sysreg_s (0x2 ,PMCNTENCLR_EL0 ); +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report_info ("MEM_ACCESS counter #0 has value 0x%lx" , +read_regn_el0 (pmevcntr,0 )); +report (read_sysreg (pmovsclr_el0) ==0x1 , +"should have triggered an overflow on #0" ); +report (!read_regn_el0 (pmevcntr,1 ), +"CHAIN counter #1 shouldn't have incremented" ); + +/* start at 0xFFFFFFDC, +20 with CHAIN disabled, +20 with CHAIN enabled */ + +pmu_reset (); +write_sysreg_s (0x1 ,PMCNTENSET_EL0 ); +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW2 ); +isb (); +report_info ("counter #0 = 0x%lx, counter #1 = 0x%lx overflow=0x%lx" , +read_regn_el0 (pmevcntr,0 ),read_regn_el0 (pmevcntr,1 ), +read_sysreg (pmovsclr_el0)); + +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report_info ("MEM_ACCESS counter #0 has value 0x%lx" , +read_regn_el0 (pmevcntr,0 )); + +/* enable the CHAIN event */ +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); +isb (); +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report_info ("MEM_ACCESS counter #0 has value 0x%lx" , +read_regn_el0 (pmevcntr,0 )); + +report ((read_regn_el0 (pmevcntr,1 ) ==1 ) && !read_sysreg (pmovsclr_el0), +"CHAIN counter #1 should have incremented and no overflow expected" ); + +report_info ("CHAIN counter #1 = 0x%lx, overflow=0x%lx" , +read_regn_el0 (pmevcntr,1 ),read_sysreg (pmovsclr_el0)); + +/* start as MEM_ACCESS/CPU_CYCLES and move to CHAIN/MEM_ACCESS */ +pmu_reset (); +write_regn_el0 (pmevtyper,0 ,MEM_ACCESS |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevtyper,1 ,CPU_CYCLES |PMEVTYPER_EXCLUDE_EL0 ); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW2 ); +isb (); + +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report_info ("MEM_ACCESS counter #0 has value 0x%lx" , +read_regn_el0 (pmevcntr,0 )); + +/* 0 becomes CHAINED */ +write_sysreg_s (0x0 ,PMCNTENSET_EL0 ); +write_regn_el0 (pmevtyper,1 ,CHAIN |PMEVTYPER_EXCLUDE_EL0 ); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); +write_regn_el0 (pmevcntr,1 ,0x0 ); + +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report_info ("MEM_ACCESS counter #0 has value 0x%lx" , +read_regn_el0 (pmevcntr,0 )); + +report ((read_regn_el0 (pmevcntr,1 ) ==1 ) && !read_sysreg (pmovsclr_el0), +"CHAIN counter #1 should have incremented and no overflow expected" ); + +report_info ("CHAIN counter #1 = 0x%lx, overflow=0x%lx" , +read_regn_el0 (pmevcntr,1 ),read_sysreg (pmovsclr_el0)); + +/* start as CHAIN/MEM_ACCESS and move to MEM_ACCESS/CPU_CYCLES */ +pmu_reset (); +write_regn_el0 (pmevtyper,0 ,MEM_ACCESS |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevtyper,1 ,CHAIN |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW2 ); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); + +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report_info ("counter #0=0x%lx, counter #1=0x%lx" , +read_regn_el0 (pmevcntr,0 ),read_regn_el0 (pmevcntr,1 )); + +write_sysreg_s (0x0 ,PMCNTENSET_EL0 ); +write_regn_el0 (pmevtyper,1 ,CPU_CYCLES |PMEVTYPER_EXCLUDE_EL0 ); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); + +mem_access_loop (addr,20 , pmu.pmcr_ro |PMU_PMCR_E ); +report (read_sysreg (pmovsclr_el0) ==1 , +"overflow is expected on counter 0" ); +report_info ("counter #0=0x%lx, counter #1=0x%lx overflow=0x%lx" , +read_regn_el0 (pmevcntr,0 ),read_regn_el0 (pmevcntr,1 ), +read_sysreg (pmovsclr_el0)); +} +#endif /*
@@ -794,6 +928,10 @@ int main(int argc, char *argv[])report_prefix_push (argv[1 ]);test_chained_sw_incr ();report_prefix_pop (); + }else if (strcmp (argv[1 ],"pmu-chain-promotion" ) ==0 ) { +report_prefix_push (argv[1 ]); +test_chain_promotion (); +report_prefix_pop (); }else {report_abort ("Unknown sub-test '%s'" , argv[1 ]); }
arm/unittests.cfg +6 -0
@@ -108,6 +108,12 @@ groups = pmu arch = arm64 extra_params = -append 'pmu-chained-sw-incr' +[pmu-chain-promotion] +file = pmu.flat +groups = pmu +arch = arm64 +extra_params = -append 'pmu-chain-promotion' + # Test PMU support (TCG) with -icount IPC=1 #[pmu-tcg-icount-1] #file = pmu.flat
Signature · 1 line
2.20.1
PATCH v3 11/12 arm: gic: Introduce gic_irq_set_clr_enable() helper
In reply to Eric AugerAllows to set or clear the enable state of a PPI/SGI/SPI.
---
--- lib/arm/asm/gic.h | 4 ++++ lib/arm/gic.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+)
lib/arm/asm/gic.h +4 -0
@@ -82,5 +82,9 @@ extern void gic_ipi_send_single(int irq, int cpu);extern void gic_ipi_send_mask (int irq,const cpumask_t *dest);extern enum gic_irq_state gic_irq_state (int irq); +void gic_irq_set_clr_enable (int irq,bool enable); +#define gic_enable_irq (irq) gic_irq_set_clr_enable(irq, true); +#define gic_disable_irq (irq) gic_irq_set_clr_enable(irq, false); +#endif /* !__ASSEMBLY__ */ #endif /* _ASMARM_GIC_H_ */
lib/arm/gic.c +31 -0
@@ -147,6 +147,36 @@ void gic_ipi_send_mask(int irq, const cpumask_t *dest) gic_common_ops->ipi_send_mask (irq, dest); } +void gic_irq_set_clr_enable (int irq,bool enable) +{ +u32 offset, split =32 , shift = (irq %32 ); +u32 reg, mask =BIT (shift); +void *base; + +assert (irq <1020 ); + +switch (gic_version ()) { +case 2 : + offset = enable ?GICD_ISENABLER :GICD_ICENABLER ; + base =gicv2_dist_base (); +break ; +case 3 : +if (irq <32 ) { + offset = enable ?GICR_ISENABLER0 :GICR_ICENABLER0 ; + base =gicv3_sgi_base (); + }else { + offset = enable ?GICD_ISENABLER :GICD_ICENABLER ; + base =gicv3_dist_base (); + } +break ; +default : +assert (0 ); + } + base += offset + (irq / split) *4 ; + reg =readl (base); +writel (reg | mask, base); +} +enum gic_irq_state gic_irq_state (int irq) {enum gic_irq_state state;
@@ -191,3 +221,4 @@ enum gic_irq_state gic_irq_state(int irq)return state; } +
Signature · 1 line
2.20.1
PATCH v3 12/12 arm: pmu: Test overflow interrupts
In reply to Eric AugerTest overflows for MEM_ACCESS and SW_INCR events. Also tests overflows with 64-bit events.
---
v2 -> v3:
- added prefix pop
- added pmu_stats.unexpected
- added pmu- prefix
- remove traces in irq_handler()
v1 -> v2:
- inline setup_irq() code
--- arm/pmu.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++ arm/unittests.cfg | 6 ++ 2 files changed, 145 insertions(+)
arm/pmu.c +139 -0
@@ -59,12 +59,20 @@#define PRE_OVERFLOW 0xFFFFFFF0#define PRE_OVERFLOW2 0xFFFFFFDC +#define PMU_PPI 23 +struct pmu {unsigned int version;unsigned int nb_implemented_counters;uint32_t pmcr_ro; }; +struct pmu_stats { +unsigned long bitmap; +uint32_t interrupts[32 ]; +bool unexpected; +}; +static struct pmu pmu;#if defined(__arm__)
@@ -146,6 +154,7 @@ static void test_sw_incr(void) {}static void test_chained_counters (void ) {}static void test_chained_sw_incr (void ) {}static void test_chain_promotion (void ) {} +static void test_overflow_interrupt (void ) {}#elif defined(__aarch64__)#define ID_AA64DFR0_PERFMON_SHIFT 8
@@ -276,6 +285,43 @@ asm volatile( : "x9", "x10", "cc"); } +static struct pmu_stats pmu_stats; + +static void irq_handler (struct pt_regs *regs) +{ +uint32_t irqstat, irqnr; + + irqstat =gic_read_iar (); + irqnr =gic_iar_irqnr (irqstat); + +if (irqnr ==PMU_PPI ) { +unsigned long overflows =read_sysreg (pmovsclr_el0); +int i; + +for (i =0 ; i <32 ; i++) { +if (test_and_clear_bit (i, &overflows)) { + pmu_stats.interrupts[i]++; + pmu_stats.bitmap |=1 << i; + } + } +write_sysreg (ALL_SET , pmovsclr_el0); + }else { + pmu_stats.unexpected = true; + } +gic_write_eoir (irqstat); +} + +static void pmu_reset_stats (void ) +{ +int i; + +for (i =0 ; i <32 ; i++) + pmu_stats.interrupts[i] =0 ; + + pmu_stats.bitmap =0 ; + pmu_stats.unexpected = false; +} +static void pmu_reset (void ) {/* reset all counters, counting disabled at PMCR level*/
@@ -286,6 +332,7 @@ static void pmu_reset(void)write_sysreg (ALL_SET , pmovsclr_el0);/* disable overflow interrupts on all counters */ write_sysreg (ALL_SET , pmintenclr_el1); +pmu_reset_stats ();isb (); }
@@ -730,6 +777,94 @@ static void test_chain_promotion(void) read_sysreg(pmovsclr_el0)); } +static bool expect_interrupts (uint32_t bitmap) +{ +int i; + +if (pmu_stats.bitmap ^ bitmap || pmu_stats.unexpected) +return false; + +for (i =0 ; i <32 ; i++) { +if (test_and_clear_bit (i, &pmu_stats.bitmap)) +if (pmu_stats.interrupts[i] !=1 ) +return false; + } +return true; +} + +static void test_overflow_interrupt (void ) +{ +uint32_t events[] = {MEM_ACCESS ,SW_INCR }; +void *addr =malloc (PAGE_SIZE ); +int i; + +if (!satisfy_prerequisites (events,ARRAY_SIZE (events))) +return ; + +gic_enable_defaults (); +install_irq_handler (EL1H_IRQ , irq_handler); +local_irq_enable (); +gic_enable_irq (23 ); + +pmu_reset (); + +write_regn_el0 (pmevtyper,0 ,MEM_ACCESS |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevtyper,1 ,SW_INCR |PMEVTYPER_EXCLUDE_EL0 ); +write_sysreg_s (0x3 ,PMCNTENSET_EL0 ); +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +write_regn_el0 (pmevcntr,1 ,PRE_OVERFLOW ); +isb (); + +/* interrupts are disabled */ + +mem_access_loop (addr,200 , pmu.pmcr_ro |PMU_PMCR_E ); +report (expect_interrupts (0 ),"no overflow interrupt received" ); + +set_pmcr (pmu.pmcr_ro |PMU_PMCR_E ); +for (i =0 ; i <100 ; i++) +write_sysreg (0x2 , pmswinc_el0); + +set_pmcr (pmu.pmcr_ro); +report (expect_interrupts (0 ),"no overflow interrupt received" ); + +/* enable interrupts */ + +pmu_reset_stats (); + +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +write_regn_el0 (pmevcntr,1 ,PRE_OVERFLOW ); +write_sysreg (ALL_SET , pmintenset_el1); +isb (); + +mem_access_loop (addr,200 , pmu.pmcr_ro |PMU_PMCR_E ); +for (i =0 ; i <100 ; i++) +write_sysreg (0x3 , pmswinc_el0); + +mem_access_loop (addr,200 , pmu.pmcr_ro); +report_info ("overflow=0x%lx" ,read_sysreg (pmovsclr_el0)); +report (expect_interrupts (0x3 ), +"overflow interrupts expected on #0 and #1" ); + +/* promote to 64-b */ + +pmu_reset_stats (); + +write_regn_el0 (pmevtyper,1 ,CHAIN |PMEVTYPER_EXCLUDE_EL0 ); +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +isb (); +mem_access_loop (addr,200 , pmu.pmcr_ro |PMU_PMCR_E ); +report (expect_interrupts (0 ), +"no overflow interrupt expected on 32b boundary" ); + +/* overflow on odd counter */ +pmu_reset_stats (); +write_regn_el0 (pmevcntr,0 ,PRE_OVERFLOW ); +write_regn_el0 (pmevcntr,1 ,ALL_SET ); +isb (); +mem_access_loop (addr,400 , pmu.pmcr_ro |PMU_PMCR_E ); +report (expect_interrupts (0x2 ), +"expect overflow interrupt on odd counter" ); +}#endif /*
@@ -932,6 +1067,10 @@ int main(int argc, char *argv[])report_prefix_push (argv[1 ]);test_chain_promotion ();report_prefix_pop (); + }else if (strcmp (argv[1 ],"pmu-overflow-interrupt" ) ==0 ) { +report_prefix_push (argv[1 ]); +test_overflow_interrupt (); +report_prefix_pop (); }else {report_abort ("Unknown sub-test '%s'" , argv[1 ]); }
arm/unittests.cfg +6 -0
@@ -114,6 +114,12 @@ groups = pmu arch = arm64 extra_params = -append 'pmu-chain-promotion' +[pmu-overflow-interrupt] +file = pmu.flat +groups = pmu +arch = arm64 +extra_params = -append 'pmu-overflow-interrupt' + # Test PMU support (TCG) with -icount IPC=1 #[pmu-tcg-icount-1] #file = pmu.flat
Signature · 1 line
2.20.1