/* perfctr_armv8pmu.c COPYRIGHT FUJITSU LIMITED 2016-2018 */ #include #include #include #include #include #include #include #include #include #include #include #include #define BIT(nr) (1UL << (nr)) //#define DEBUG_PRINT_PMU #ifdef DEBUG_PRINT_PMU #undef DDEBUG_DEFAULT #define DDEBUG_DEFAULT DDEBUG_PRINT #endif /* * read pmevcntr_el0 functions */ #define read_pmevcntrN_el0(N) \ static uint32_t read_pmevcntr##N##_el0(void) \ { \ return read_sysreg(pmevcntr##N##_el0); \ } read_pmevcntrN_el0(0) read_pmevcntrN_el0(1) read_pmevcntrN_el0(2) read_pmevcntrN_el0(3) read_pmevcntrN_el0(4) read_pmevcntrN_el0(5) read_pmevcntrN_el0(6) read_pmevcntrN_el0(7) read_pmevcntrN_el0(8) read_pmevcntrN_el0(9) read_pmevcntrN_el0(10) read_pmevcntrN_el0(11) read_pmevcntrN_el0(12) read_pmevcntrN_el0(13) read_pmevcntrN_el0(14) read_pmevcntrN_el0(15) read_pmevcntrN_el0(16) read_pmevcntrN_el0(17) read_pmevcntrN_el0(18) read_pmevcntrN_el0(19) read_pmevcntrN_el0(20) read_pmevcntrN_el0(21) read_pmevcntrN_el0(22) read_pmevcntrN_el0(23) read_pmevcntrN_el0(24) read_pmevcntrN_el0(25) read_pmevcntrN_el0(26) read_pmevcntrN_el0(27) read_pmevcntrN_el0(28) read_pmevcntrN_el0(29) read_pmevcntrN_el0(30) static uint32_t (* const read_pmevcntr_el0[])(void) = { read_pmevcntr0_el0, read_pmevcntr1_el0, read_pmevcntr2_el0, read_pmevcntr3_el0, read_pmevcntr4_el0, read_pmevcntr5_el0, read_pmevcntr6_el0, read_pmevcntr7_el0, read_pmevcntr8_el0, read_pmevcntr9_el0, read_pmevcntr10_el0, read_pmevcntr11_el0, read_pmevcntr12_el0, read_pmevcntr13_el0, read_pmevcntr14_el0, read_pmevcntr15_el0, read_pmevcntr16_el0, read_pmevcntr17_el0, read_pmevcntr18_el0, read_pmevcntr19_el0, read_pmevcntr20_el0, read_pmevcntr21_el0, read_pmevcntr22_el0, read_pmevcntr23_el0, read_pmevcntr24_el0, read_pmevcntr25_el0, read_pmevcntr26_el0, read_pmevcntr27_el0, read_pmevcntr28_el0, read_pmevcntr29_el0, read_pmevcntr30_el0, }; /* * write pmevcntr_el0 functions */ #define write_pmevcntrN_el0(N) \ static void write_pmevcntr##N##_el0(uint32_t v) \ { \ write_sysreg(v, pmevcntr##N##_el0); \ } write_pmevcntrN_el0(0) write_pmevcntrN_el0(1) write_pmevcntrN_el0(2) write_pmevcntrN_el0(3) write_pmevcntrN_el0(4) write_pmevcntrN_el0(5) write_pmevcntrN_el0(6) write_pmevcntrN_el0(7) write_pmevcntrN_el0(8) write_pmevcntrN_el0(9) write_pmevcntrN_el0(10) write_pmevcntrN_el0(11) write_pmevcntrN_el0(12) write_pmevcntrN_el0(13) write_pmevcntrN_el0(14) write_pmevcntrN_el0(15) write_pmevcntrN_el0(16) write_pmevcntrN_el0(17) write_pmevcntrN_el0(18) write_pmevcntrN_el0(19) write_pmevcntrN_el0(20) write_pmevcntrN_el0(21) write_pmevcntrN_el0(22) write_pmevcntrN_el0(23) write_pmevcntrN_el0(24) write_pmevcntrN_el0(25) write_pmevcntrN_el0(26) write_pmevcntrN_el0(27) write_pmevcntrN_el0(28) write_pmevcntrN_el0(29) write_pmevcntrN_el0(30) static void (* const write_pmevcntr_el0[])(uint32_t) = { write_pmevcntr0_el0, write_pmevcntr1_el0, write_pmevcntr2_el0, write_pmevcntr3_el0, write_pmevcntr4_el0, write_pmevcntr5_el0, write_pmevcntr6_el0, write_pmevcntr7_el0, write_pmevcntr8_el0, write_pmevcntr9_el0, write_pmevcntr10_el0, write_pmevcntr11_el0, write_pmevcntr12_el0, write_pmevcntr13_el0, write_pmevcntr14_el0, write_pmevcntr15_el0, write_pmevcntr16_el0, write_pmevcntr17_el0, write_pmevcntr18_el0, write_pmevcntr19_el0, write_pmevcntr20_el0, write_pmevcntr21_el0, write_pmevcntr22_el0, write_pmevcntr23_el0, write_pmevcntr24_el0, write_pmevcntr25_el0, write_pmevcntr26_el0, write_pmevcntr27_el0, write_pmevcntr28_el0, write_pmevcntr29_el0, write_pmevcntr30_el0, }; /* * write pmevtyper_el0 functions */ #define write_pmevtyperN_el0(N) \ static void write_pmevtyper##N##_el0(uint32_t v) \ { \ write_sysreg(v, pmevtyper##N##_el0); \ } write_pmevtyperN_el0(0) write_pmevtyperN_el0(1) write_pmevtyperN_el0(2) write_pmevtyperN_el0(3) write_pmevtyperN_el0(4) write_pmevtyperN_el0(5) write_pmevtyperN_el0(6) write_pmevtyperN_el0(7) write_pmevtyperN_el0(8) write_pmevtyperN_el0(9) write_pmevtyperN_el0(10) write_pmevtyperN_el0(11) write_pmevtyperN_el0(12) write_pmevtyperN_el0(13) write_pmevtyperN_el0(14) write_pmevtyperN_el0(15) write_pmevtyperN_el0(16) write_pmevtyperN_el0(17) write_pmevtyperN_el0(18) write_pmevtyperN_el0(19) write_pmevtyperN_el0(20) write_pmevtyperN_el0(21) write_pmevtyperN_el0(22) write_pmevtyperN_el0(23) write_pmevtyperN_el0(24) write_pmevtyperN_el0(25) write_pmevtyperN_el0(26) write_pmevtyperN_el0(27) write_pmevtyperN_el0(28) write_pmevtyperN_el0(29) write_pmevtyperN_el0(30) static void (* const write_pmevtyper_el0[])(uint32_t) = { write_pmevtyper0_el0, write_pmevtyper1_el0, write_pmevtyper2_el0, write_pmevtyper3_el0, write_pmevtyper4_el0, write_pmevtyper5_el0, write_pmevtyper6_el0, write_pmevtyper7_el0, write_pmevtyper8_el0, write_pmevtyper9_el0, write_pmevtyper10_el0, write_pmevtyper11_el0, write_pmevtyper12_el0, write_pmevtyper13_el0, write_pmevtyper14_el0, write_pmevtyper15_el0, write_pmevtyper16_el0, write_pmevtyper17_el0, write_pmevtyper18_el0, write_pmevtyper19_el0, write_pmevtyper20_el0, write_pmevtyper21_el0, write_pmevtyper22_el0, write_pmevtyper23_el0, write_pmevtyper24_el0, write_pmevtyper25_el0, write_pmevtyper26_el0, write_pmevtyper27_el0, write_pmevtyper28_el0, write_pmevtyper29_el0, write_pmevtyper30_el0, }; #define ARMV8_IDX_CYCLE_COUNTER 31 #define ARMV8_IDX_COUNTER0 0 /* * @ref.impl linux-v4.15-rc3 arch/arm64/include/asm/perf_event.h * Per-CPU PMCR: config reg */ #define ARMV8_PMU_PMCR_E (1 << 0) /* Enable all counters */ #define ARMV8_PMU_PMCR_P (1 << 1) /* Reset all counters */ #define ARMV8_PMU_PMCR_C (1 << 2) /* Cycle counter reset */ #define ARMV8_PMU_PMCR_D (1 << 3) /* CCNT counts every 64th cpu cycle */ #define ARMV8_PMU_PMCR_X (1 << 4) /* Export to ETM */ #define ARMV8_PMU_PMCR_DP (1 << 5) /* Disable CCNT if non-invasive debug*/ #define ARMV8_PMU_PMCR_LC (1 << 6) /* Overflow on 64 bit cycle counter */ #define ARMV8_PMU_PMCR_N_SHIFT 11 /* Number of counters supported */ #define ARMV8_PMU_PMCR_N_MASK 0x1f #define ARMV8_PMU_PMCR_MASK 0x7f /* Mask for writable bits */ /* * @ref.impl linux-v4.15-rc3 arch/arm64/include/asm/perf_event.h * PMOVSR: counters overflow flag status reg */ #define ARMV8_PMU_OVSR_MASK 0xffffffff /* Mask for writable bits */ #define ARMV8_PMU_OVERFLOWED_MASK ARMV8_PMU_OVSR_MASK /* * @ref.impl linux-v4.15-rc3 arch/arm64/include/asm/perf_event.h * PMXEVTYPER: Event selection reg */ #define ARMV8_PMU_EVTYPE_MASK 0xc800ffff /* Mask for writable bits */ #define ARMV8_PMU_EVTYPE_EVENT 0xffff /* Mask for EVENT bits */ /* * @ref.impl linux-v4.15-rc3 arch/arm64/include/asm/perf_event.h * Event filters for PMUv3 */ #define ARMV8_PMU_EXCLUDE_EL1 (1 << 31) #define ARMV8_PMU_EXCLUDE_EL0 (1 << 30) #define ARMV8_PMU_INCLUDE_EL2 (1 << 27) /* * @ref.impl linux-v4.15-rc3 arch/arm64/include/asm/perf_event.h * PMUSERENR: user enable reg */ #define ARMV8_PMU_USERENR_MASK 0xf /* Mask for writable bits */ #define ARMV8_PMU_USERENR_EN (1 << 0) /* PMU regs can be accessed at EL0 */ #define ARMV8_PMU_USERENR_SW (1 << 1) /* PMSWINC can be written at EL0 */ #define ARMV8_PMU_USERENR_CR (1 << 2) /* Cycle counter can be read at EL0 */ #define ARMV8_PMU_USERENR_ER (1 << 3) /* Event counter can be read at EL0 */ /* * @ref.impl linux-v4.15-rc3 arch/arm64/include/asm/perf_event.h * PMUv3 event types: required events */ #define ARMV8_PMUV3_PERFCTR_SW_INCR 0x00 #define ARMV8_PMUV3_PERFCTR_L1D_CACHE_REFILL 0x03 #define ARMV8_PMUV3_PERFCTR_L1D_CACHE 0x04 #define ARMV8_PMUV3_PERFCTR_BR_MIS_PRED 0x10 #define ARMV8_PMUV3_PERFCTR_CPU_CYCLES 0x11 #define ARMV8_PMUV3_PERFCTR_BR_PRED 0x12 /* * @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c * ARMv8 PMUv3 Performance Events handling code. * Common event types (some are defined in asm/perf_event.h). */ /* At least one of the following is required. */ #define ARMV8_PMUV3_PERFCTR_INST_RETIRED 0x08 #define ARMV8_PMUV3_PERFCTR_INST_SPEC 0x1B /* Common architectural events. */ #define ARMV8_PMUV3_PERFCTR_LD_RETIRED 0x06 #define ARMV8_PMUV3_PERFCTR_ST_RETIRED 0x07 #define ARMV8_PMUV3_PERFCTR_EXC_TAKEN 0x09 #define ARMV8_PMUV3_PERFCTR_EXC_RETURN 0x0A #define ARMV8_PMUV3_PERFCTR_CID_WRITE_RETIRED 0x0B #define ARMV8_PMUV3_PERFCTR_PC_WRITE_RETIRED 0x0C #define ARMV8_PMUV3_PERFCTR_BR_IMMED_RETIRED 0x0D #define ARMV8_PMUV3_PERFCTR_BR_RETURN_RETIRED 0x0E #define ARMV8_PMUV3_PERFCTR_UNALIGNED_LDST_RETIRED 0x0F #define ARMV8_PMUV3_PERFCTR_TTBR_WRITE_RETIRED 0x1C #define ARMV8_PMUV3_PERFCTR_CHAIN 0x1E #define ARMV8_PMUV3_PERFCTR_BR_RETIRED 0x21 /* Common microarchitectural events. */ #define ARMV8_PMUV3_PERFCTR_L1I_CACHE_REFILL 0x01 #define ARMV8_PMUV3_PERFCTR_L1I_TLB_REFILL 0x02 #define ARMV8_PMUV3_PERFCTR_L1D_TLB_REFILL 0x05 #define ARMV8_PMUV3_PERFCTR_MEM_ACCESS 0x13 #define ARMV8_PMUV3_PERFCTR_L1I_CACHE 0x14 #define ARMV8_PMUV3_PERFCTR_L1D_CACHE_WB 0x15 #define ARMV8_PMUV3_PERFCTR_L2D_CACHE 0x16 #define ARMV8_PMUV3_PERFCTR_L2D_CACHE_REFILL 0x17 #define ARMV8_PMUV3_PERFCTR_L2D_CACHE_WB 0x18 #define ARMV8_PMUV3_PERFCTR_BUS_ACCESS 0x19 #define ARMV8_PMUV3_PERFCTR_MEMORY_ERROR 0x1A #define ARMV8_PMUV3_PERFCTR_BUS_CYCLES 0x1D #define ARMV8_PMUV3_PERFCTR_L1D_CACHE_ALLOCATE 0x1F #define ARMV8_PMUV3_PERFCTR_L2D_CACHE_ALLOCATE 0x20 #define ARMV8_PMUV3_PERFCTR_BR_MIS_PRED_RETIRED 0x22 #define ARMV8_PMUV3_PERFCTR_STALL_FRONTEND 0x23 #define ARMV8_PMUV3_PERFCTR_STALL_BACKEND 0x24 #define ARMV8_PMUV3_PERFCTR_L1D_TLB 0x25 #define ARMV8_PMUV3_PERFCTR_L1I_TLB 0x26 #define ARMV8_PMUV3_PERFCTR_L2I_CACHE 0x27 #define ARMV8_PMUV3_PERFCTR_L2I_CACHE_REFILL 0x28 #define ARMV8_PMUV3_PERFCTR_L3D_CACHE_ALLOCATE 0x29 #define ARMV8_PMUV3_PERFCTR_L3D_CACHE_REFILL 0x2A #define ARMV8_PMUV3_PERFCTR_L3D_CACHE 0x2B #define ARMV8_PMUV3_PERFCTR_L3D_CACHE_WB 0x2C #define ARMV8_PMUV3_PERFCTR_L2D_TLB_REFILL 0x2D #define ARMV8_PMUV3_PERFCTR_L2I_TLB_REFILL 0x2E #define ARMV8_PMUV3_PERFCTR_L2D_TLB 0x2F #define ARMV8_PMUV3_PERFCTR_L2I_TLB 0x30 /* @ref.impl linux-v4.15-rc3 include/linux/perf/arm_pmu.h */ #define HW_OP_UNSUPPORTED 0xFFFF #define C(_x) PERF_COUNT_HW_CACHE_##_x #define CACHE_OP_UNSUPPORTED 0xFFFF #define PERF_MAP_ALL_UNSUPPORTED \ [0 ... PERF_COUNT_HW_MAX - 1] = HW_OP_UNSUPPORTED #define PERF_CACHE_MAP_ALL_UNSUPPORTED \ [0 ... C(MAX) - 1] = { \ [0 ... C(OP_MAX) - 1] = { \ [0 ... C(RESULT_MAX) - 1] = CACHE_OP_UNSUPPORTED, \ }, \ } /* PMUv3 HW events mapping. */ /* disable -Woverride-init for the following initializations */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverride-init" /* * @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c * ARMv8 Architectural defined events, not all of these may * be supported on any given implementation. Undefined events will * be disabled at run-time. */ static const unsigned armv8_pmuv3_perf_map[PERF_COUNT_HW_MAX] = { PERF_MAP_ALL_UNSUPPORTED, [PERF_COUNT_HW_CPU_CYCLES] = ARMV8_PMUV3_PERFCTR_CPU_CYCLES, [PERF_COUNT_HW_INSTRUCTIONS] = ARMV8_PMUV3_PERFCTR_INST_RETIRED, [PERF_COUNT_HW_CACHE_REFERENCES] = ARMV8_PMUV3_PERFCTR_L1D_CACHE, [PERF_COUNT_HW_CACHE_MISSES] = ARMV8_PMUV3_PERFCTR_L1D_CACHE_REFILL, [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV8_PMUV3_PERFCTR_PC_WRITE_RETIRED, [PERF_COUNT_HW_BRANCH_MISSES] = ARMV8_PMUV3_PERFCTR_BR_MIS_PRED, [PERF_COUNT_HW_BUS_CYCLES] = ARMV8_PMUV3_PERFCTR_BUS_CYCLES, [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = ARMV8_PMUV3_PERFCTR_STALL_FRONTEND, [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = ARMV8_PMUV3_PERFCTR_STALL_BACKEND, }; /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static const unsigned armv8_pmuv3_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = { PERF_CACHE_MAP_ALL_UNSUPPORTED, [C(L1D)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1D_CACHE, [C(L1D)][C(OP_READ)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_L1D_CACHE_REFILL, [C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1D_CACHE, [C(L1D)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_L1D_CACHE_REFILL, [C(L1I)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1I_CACHE, [C(L1I)][C(OP_READ)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_L1I_CACHE_REFILL, [C(DTLB)][C(OP_READ)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_L1D_TLB_REFILL, [C(DTLB)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1D_TLB, [C(ITLB)][C(OP_READ)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_L1I_TLB_REFILL, [C(ITLB)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1I_TLB, [C(BPU)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_BR_PRED, [C(BPU)][C(OP_READ)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_BR_MIS_PRED, [C(BPU)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_BR_PRED, [C(BPU)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_BR_MIS_PRED, }; /* restore warnings */ #pragma GCC diagnostic pop /* @ref.impl linux-v4.15-rc3 drivers/perf/arm_pmu.c */ static int armpmu_map_cache_event(const unsigned (*cache_map) [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX], uint64_t config) { unsigned int cache_type, cache_op, cache_result, ret; cache_type = (config >> 0) & 0xff; if (cache_type >= PERF_COUNT_HW_CACHE_MAX) return -EINVAL; cache_op = (config >> 8) & 0xff; if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX) return -EINVAL; cache_result = (config >> 16) & 0xff; if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) return -EINVAL; if (!cache_map) return -ENOENT; ret = (int)(*cache_map)[cache_type][cache_op][cache_result]; if (ret == CACHE_OP_UNSUPPORTED) return -ENOENT; return ret; } /* @ref.impl linux-v4.15-rc3 drivers/perf/arm_pmu.c */ static int armpmu_map_hw_event(const unsigned int (*event_map)[PERF_COUNT_HW_MAX], uint64_t config) { int mapping; if (config >= PERF_COUNT_HW_MAX) return -EINVAL; if (!event_map) return -ENOENT; mapping = (*event_map)[config]; return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping; } /* @ref.impl linux-v4.15-rc3 drivers/perf/arm_pmu.c */ static int armpmu_map_raw_event(uint32_t raw_event_mask, uint64_t config) { return (int)(config & raw_event_mask); } /* @ref.impl linux-v4.15-rc3 drivers/perf/arm_pmu.c */ static int armpmu_map_event(uint32_t type, uint64_t config, const unsigned int (*event_map)[PERF_COUNT_HW_MAX], const unsigned int (*cache_map) [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX], uint32_t raw_event_mask) { switch (type) { case PERF_TYPE_HARDWARE: return armpmu_map_hw_event(event_map, config); case PERF_TYPE_HW_CACHE: return armpmu_map_cache_event(cache_map, config); case PERF_TYPE_RAW: return armpmu_map_raw_event(raw_event_mask, config); } return -ENOENT; } static inline int armv8pmu_counter_mask_valid(unsigned long counter_mask) { int num; unsigned long event; unsigned long cycle; unsigned long invalid_mask; num = get_per_cpu_pmu()->num_events; num--; /* Sub the CPU cycles counter */ event = ((1UL << num) - 1) << ARMV8_IDX_COUNTER0; cycle = 1UL << ARMV8_IDX_CYCLE_COUNTER; invalid_mask = ~(event | cycle); return !(counter_mask & invalid_mask); } static inline int armv8pmu_counter_valid(int idx) { return armv8pmu_counter_mask_valid(1UL << idx); } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline uint32_t armv8pmu_getreset_flags(void) { uint32_t value; /* Read */ value = read_sysreg(pmovsclr_el0); /* Write to clear flags */ value &= ARMV8_PMU_OVSR_MASK; write_sysreg(value, pmovsclr_el0); return value; } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline int armv8pmu_has_overflowed(uint32_t pmovsr) { return pmovsr & ARMV8_PMU_OVERFLOWED_MASK; } static inline int armv8pmu_counter_has_overflowed(uint32_t pmnc, int idx) { return pmnc & BIT(idx); } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static int __armv8_pmuv3_map_event(uint32_t type, uint64_t config, const unsigned int (*extra_event_map) [PERF_COUNT_HW_MAX], const unsigned int (*extra_cache_map) [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX]) { int hw_event_id; hw_event_id = armpmu_map_event(type, config, &armv8_pmuv3_perf_map, &armv8_pmuv3_perf_cache_map, ARMV8_PMU_EVTYPE_EVENT); /* Onl expose micro/arch events supported by this PMU */ if ((hw_event_id > 0) && (hw_event_id < ARMV8_PMUV3_MAX_COMMON_EVENTS) && test_bit(hw_event_id, get_per_cpu_pmu()->pmceid_bitmap)) { return hw_event_id; } return armpmu_map_event(type, config, extra_event_map, extra_cache_map, ARMV8_PMU_EVTYPE_EVENT); } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static int armv8_pmuv3_map_event(uint32_t type, uint64_t config) { return __armv8_pmuv3_map_event(type, config, NULL, NULL); } static int armv8_pmuv3_map_hw_event(uint64_t config) { return __armv8_pmuv3_map_event(PERF_TYPE_HARDWARE, config, NULL, NULL); } static int armv8_pmuv3_map_cache_event(uint64_t config) { return __armv8_pmuv3_map_event(PERF_TYPE_HW_CACHE, config, NULL, NULL); } static int armv8_pmuv3_map_raw_event(uint64_t config) { return __armv8_pmuv3_map_event(PERF_TYPE_RAW, config, NULL, NULL); } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline uint32_t armv8pmu_pmcr_read(void) { return read_sysreg(pmcr_el0); } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline void armv8pmu_pmcr_write(uint32_t val) { val &= ARMV8_PMU_PMCR_MASK; isb(); write_sysreg(val, pmcr_el0); } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline uint32_t armv8pmu_read_counter(int idx) { uint32_t value = 0; if (!armv8pmu_counter_valid(idx)) { ekprintf("%s: The count_register#%d is not implemented.\n", __func__, idx); } else if (idx == ARMV8_IDX_CYCLE_COUNTER) { value = read_sysreg(pmccntr_el0); } else { value = read_pmevcntr_el0[idx](); } return value; } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline void armv8pmu_write_counter(int idx, uint32_t value) { if (!armv8pmu_counter_valid(idx)) { ekprintf("%s: The count_register#%d is not implemented.\n", __func__, idx); } else if (idx == ARMV8_IDX_CYCLE_COUNTER) { /* * Set the upper 32bits as this is a 64bit counter but we only * count using the lower 32bits and we want an interrupt when * it overflows. */ uint64_t value64 = (int32_t)value; write_sysreg(value64, pmccntr_el0); } else { write_pmevcntr_el0[idx](value); } } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline int armv8pmu_enable_intens(unsigned long counter_mask) { if (!armv8pmu_counter_mask_valid(counter_mask)) { ekprintf("%s: invalid counter mask(%#lx)\n", __func__, counter_mask); return -EINVAL; } write_sysreg(counter_mask, pmintenset_el1); return 0; } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline int armv8pmu_disable_intens(unsigned long counter_mask) { if (!armv8pmu_counter_mask_valid(counter_mask)) { ekprintf("%s: invalid counter mask(%#lx)\n", __func__, counter_mask); return -EINVAL; } write_sysreg(counter_mask, pmintenclr_el1); isb(); /* Clear the overflow flag in case an interrupt is pending. */ write_sysreg(counter_mask, pmovsclr_el0); isb(); return 0; } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static int armv8pmu_set_event_filter(unsigned long *config_base, int mode) { /* exclude_idle is unused mode, unsupported */ // if (attr->exclude_idle) // return -EPERM; /* * If we're running in hyp mode, then we *are* the hypervisor. * Therefore we ignore exclude_hv in this configuration, since * there's no hypervisor to sample anyway. This is consistent * with other architectures (x86 and Power). */ if (is_kernel_in_hyp_mode()) { if (mode & PERFCTR_KERNEL_MODE) *config_base |= ARMV8_PMU_INCLUDE_EL2; } else { if (!(mode & PERFCTR_KERNEL_MODE)) *config_base |= ARMV8_PMU_EXCLUDE_EL1; /* exclude_hv is unused mode, unsupported */ // if (!attr->exclude_hv) // config_base |= ARMV8_PMU_INCLUDE_EL2; } if (!(mode & PERFCTR_USER_MODE)) *config_base |= ARMV8_PMU_EXCLUDE_EL0; return 0; } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline void armv8pmu_write_evtype(int idx, uint32_t val) { if (!armv8pmu_counter_valid(idx)) { ekprintf("%s: The count_register#%d is not implemented.\n", __func__, idx); return; } else if (idx != ARMV8_IDX_CYCLE_COUNTER) { write_pmevtyper_el0[idx](val); } } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline int armv8pmu_enable_counter(unsigned long counter_mask) { if (!armv8pmu_counter_mask_valid(counter_mask)) { ekprintf("%s: invalid counter mask 0x%lx.\n", __func__, counter_mask); return -EINVAL; } write_sysreg(counter_mask, pmcntenset_el0); return 0; } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static inline int armv8pmu_disable_counter(unsigned long counter_mask) { if (!armv8pmu_counter_mask_valid(counter_mask)) { ekprintf("%s: invalid counter mask 0x%lx.\n", __func__, counter_mask); return -EINVAL; } write_sysreg(counter_mask, pmcntenclr_el0); return 0; } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static ihk_spinlock_t pmu_lock = SPIN_LOCK_UNLOCKED; static int armv8pmu_start(void) { unsigned long flags; flags = ihk_mc_spinlock_lock(&pmu_lock); /* Enable all counters */ armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E); ihk_mc_spinlock_unlock(&pmu_lock, flags); return 0; } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static void armv8pmu_stop(void) { unsigned long flags; flags = ihk_mc_spinlock_lock(&pmu_lock); /* Disable all counters */ armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E); ihk_mc_spinlock_unlock(&pmu_lock, flags); } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static void armv8pmu_reset(void *info) { struct arm_pmu *cpu_pmu = (struct arm_pmu *)info; uint32_t nb_cnt = cpu_pmu->per_cpu[ihk_mc_get_processor_id()].num_events; nb_cnt--; /* Sub the CPU cycles counter */ unsigned long event = ((1UL << nb_cnt) - 1) << ARMV8_IDX_COUNTER0; unsigned long cycle = 1UL << ARMV8_IDX_CYCLE_COUNTER; unsigned long valid_mask = event | cycle; /* The counter and interrupt enable registers are unknown at reset. */ armv8pmu_disable_counter(valid_mask); armv8pmu_disable_intens(valid_mask); /* * Initialize & Reset PMNC. Request overflow interrupt for * 64 bit cycle counter but cheat in armv8pmu_write_counter(). */ armv8pmu_pmcr_write(ARMV8_PMU_PMCR_P | ARMV8_PMU_PMCR_C | ARMV8_PMU_PMCR_LC); } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c */ static int armv8pmu_get_event_idx(int num_events, unsigned long used_mask, unsigned long config) { int idx, end; unsigned long evtype = config & ARMV8_PMU_EVTYPE_EVENT; /* Always prefer to place a cycle counter into the cycle counter. */ if (evtype == ARMV8_PMUV3_PERFCTR_CPU_CYCLES) { if (!(used_mask & (1UL << ARMV8_IDX_CYCLE_COUNTER))) return ARMV8_IDX_CYCLE_COUNTER; } /* * Otherwise use events counters */ end = ARMV8_IDX_COUNTER0 + num_events; end--; /* Sub the CPU cycles counter */ for (idx = ARMV8_IDX_COUNTER0; idx < end; ++idx) { if (!(used_mask & (1UL << idx))) return idx; } /* The counters are all in use. */ return -EAGAIN; } /* @ref.impl linux-v4.15-rc3 arch/arm64/kernel/perf_event.c:__armv8pmu_probe_pmu() */ /* Extract get num_events processing. */ static uint32_t armv8pmu_read_num_pmnc_events(void) { uint32_t num_events = 0; /* Read the nb of CNTx counters supported from PMNC */ num_events = (armv8pmu_pmcr_read() >> ARMV8_PMU_PMCR_N_SHIFT) & ARMV8_PMU_PMCR_N_MASK; /* Add the CPU cycles counter */ num_events += 1; return num_events; } static void armv8pmu_handle_irq(void *priv) { uint32_t pmovsr; struct thread *thread = cpu_local_var(current); struct process *proc = thread->proc; const struct per_cpu_arm_pmu *cpu_pmu = get_per_cpu_pmu(); int idx; /* * Get and reset the IRQ flags */ pmovsr = armv8pmu_getreset_flags(); /* * Did an overflow occur? */ if (!armv8pmu_has_overflowed(pmovsr)) return; if (!proc->monitoring_event) { return; } /* * Handle the counter(s) overflow(s) */ for (idx = 0; idx < cpu_pmu->num_events; idx++) { struct mc_perf_event *event = NULL; struct mc_perf_event *sub; if (!armv8pmu_counter_has_overflowed(pmovsr, idx)) { continue; } if (proc->monitoring_event->counter_id == idx) { event = proc->monitoring_event; } else { list_for_each_entry(sub, &proc->monitoring_event->sibling_list, group_entry) { if (sub->counter_id == idx) { event = sub; break; } } } if (!event) { continue; } ihk_mc_event_update(event); ihk_mc_event_set_period(event); } return; } static void armv8pmu_enable_user_access_pmu_regs(void) { uint32_t value = 0; value = read_sysreg(pmuserenr_el0); write_sysreg(value | (ARMV8_PMU_USERENR_ER | ARMV8_PMU_USERENR_CR), pmuserenr_el0); } static void armv8pmu_disable_user_access_pmu_regs(void) { uint32_t value = 0; value = read_sysreg(pmuserenr_el0); write_sysreg(value & ~(ARMV8_PMU_USERENR_ER | ARMV8_PMU_USERENR_CR), pmuserenr_el0); } static void armv8pmu_create_pmceid_bitmap(unsigned long *bitmap, uint32_t nbits) { uint32_t pmceid[2]; memset(bitmap, 0, BITS_TO_LONGS(nbits) * sizeof(unsigned long)); pmceid[0] = read_sysreg(pmceid0_el0); bitmap[0] = (unsigned long)pmceid[0]; pmceid[1] = read_sysreg(pmceid1_el0); bitmap[0] |= (unsigned long)pmceid[1] << 32; } static struct ihk_mc_interrupt_handler armv8pmu_handler = { .func = armv8pmu_handle_irq, .priv = NULL, }; int armv8pmu_init(struct arm_pmu* cpu_pmu) { cpu_pmu->read_counter = armv8pmu_read_counter; cpu_pmu->write_counter = armv8pmu_write_counter; cpu_pmu->reset = armv8pmu_reset; cpu_pmu->enable_pmu = armv8pmu_start; cpu_pmu->disable_pmu = armv8pmu_stop; cpu_pmu->enable_counter = armv8pmu_enable_counter; cpu_pmu->disable_counter = armv8pmu_disable_counter; cpu_pmu->enable_intens = armv8pmu_enable_intens; cpu_pmu->disable_intens = armv8pmu_disable_intens; cpu_pmu->set_event_filter = armv8pmu_set_event_filter; cpu_pmu->write_evtype = armv8pmu_write_evtype; cpu_pmu->get_event_idx = armv8pmu_get_event_idx; cpu_pmu->map_event = armv8_pmuv3_map_event; cpu_pmu->map_hw_event = armv8_pmuv3_map_hw_event; cpu_pmu->map_cache_event = armv8_pmuv3_map_cache_event; cpu_pmu->map_raw_event = armv8_pmuv3_map_raw_event; cpu_pmu->enable_user_access_pmu_regs = armv8pmu_enable_user_access_pmu_regs; cpu_pmu->disable_user_access_pmu_regs = armv8pmu_disable_user_access_pmu_regs; cpu_pmu->handler = &armv8pmu_handler; cpu_pmu->counter_mask_valid = &armv8pmu_counter_mask_valid; return 0; } void armv8pmu_per_cpu_init(struct per_cpu_arm_pmu *per_cpu) { per_cpu->num_events = armv8pmu_read_num_pmnc_events(); armv8pmu_create_pmceid_bitmap(per_cpu->pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS); }