1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright(C) 2015 Linaro Limited. All rights reserved. 4 * Author: Mathieu Poirier <mathieu.poirier@linaro.org> 5 */ 6 7 #include <dirent.h> 8 #include <stdbool.h> 9 #include <linux/coresight-pmu.h> 10 #include <linux/zalloc.h> 11 #include <api/fs/fs.h> 12 13 #include "../../../util/auxtrace.h" 14 #include "../../../util/debug.h" 15 #include "../../../util/evlist.h" 16 #include "../../../util/pmu.h" 17 #include "cs-etm.h" 18 #include "arm-spe.h" 19 #include "hisi-ptt.h" 20 21 static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err) 22 { 23 struct perf_pmu **arm_spe_pmus = NULL; 24 int ret, i, nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 25 /* arm_spe_xxxxxxxxx\0 */ 26 char arm_spe_pmu_name[sizeof(ARM_SPE_PMU_NAME) + 10]; 27 28 arm_spe_pmus = zalloc(sizeof(struct perf_pmu *) * nr_cpus); 29 if (!arm_spe_pmus) { 30 pr_err("spes alloc failed\n"); 31 *err = -ENOMEM; 32 return NULL; 33 } 34 35 for (i = 0; i < nr_cpus; i++) { 36 ret = sprintf(arm_spe_pmu_name, "%s%d", ARM_SPE_PMU_NAME, i); 37 if (ret < 0) { 38 pr_err("sprintf failed\n"); 39 *err = -ENOMEM; 40 return NULL; 41 } 42 43 arm_spe_pmus[*nr_spes] = perf_pmu__find(arm_spe_pmu_name); 44 if (arm_spe_pmus[*nr_spes]) { 45 pr_debug2("%s %d: arm_spe_pmu %d type %d name %s\n", 46 __func__, __LINE__, *nr_spes, 47 arm_spe_pmus[*nr_spes]->type, 48 arm_spe_pmus[*nr_spes]->name); 49 (*nr_spes)++; 50 } 51 } 52 53 return arm_spe_pmus; 54 } 55 56 static struct perf_pmu **find_all_hisi_ptt_pmus(int *nr_ptts, int *err) 57 { 58 struct perf_pmu **hisi_ptt_pmus = NULL; 59 struct dirent *dent; 60 char path[PATH_MAX]; 61 DIR *dir = NULL; 62 int idx = 0; 63 64 perf_pmu__event_source_devices_scnprintf(path, sizeof(path)); 65 dir = opendir(path); 66 if (!dir) { 67 pr_err("can't read directory '%s'\n", path); 68 *err = -EINVAL; 69 return NULL; 70 } 71 72 while ((dent = readdir(dir))) { 73 if (strstr(dent->d_name, HISI_PTT_PMU_NAME)) 74 (*nr_ptts)++; 75 } 76 77 if (!(*nr_ptts)) 78 goto out; 79 80 hisi_ptt_pmus = zalloc(sizeof(struct perf_pmu *) * (*nr_ptts)); 81 if (!hisi_ptt_pmus) { 82 pr_err("hisi_ptt alloc failed\n"); 83 *err = -ENOMEM; 84 goto out; 85 } 86 87 rewinddir(dir); 88 while ((dent = readdir(dir))) { 89 if (strstr(dent->d_name, HISI_PTT_PMU_NAME) && idx < *nr_ptts) { 90 hisi_ptt_pmus[idx] = perf_pmu__find(dent->d_name); 91 if (hisi_ptt_pmus[idx]) 92 idx++; 93 } 94 } 95 96 out: 97 closedir(dir); 98 return hisi_ptt_pmus; 99 } 100 101 static struct perf_pmu *find_pmu_for_event(struct perf_pmu **pmus, 102 int pmu_nr, struct evsel *evsel) 103 { 104 int i; 105 106 if (!pmus) 107 return NULL; 108 109 for (i = 0; i < pmu_nr; i++) { 110 if (evsel->core.attr.type == pmus[i]->type) 111 return pmus[i]; 112 } 113 114 return NULL; 115 } 116 117 struct auxtrace_record 118 *auxtrace_record__init(struct evlist *evlist, int *err) 119 { 120 struct perf_pmu *cs_etm_pmu = NULL; 121 struct perf_pmu **arm_spe_pmus = NULL; 122 struct perf_pmu **hisi_ptt_pmus = NULL; 123 struct evsel *evsel; 124 struct perf_pmu *found_etm = NULL; 125 struct perf_pmu *found_spe = NULL; 126 struct perf_pmu *found_ptt = NULL; 127 int auxtrace_event_cnt = 0; 128 int nr_spes = 0; 129 int nr_ptts = 0; 130 131 if (!evlist) 132 return NULL; 133 134 cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); 135 arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err); 136 hisi_ptt_pmus = find_all_hisi_ptt_pmus(&nr_ptts, err); 137 138 evlist__for_each_entry(evlist, evsel) { 139 if (cs_etm_pmu && !found_etm) 140 found_etm = find_pmu_for_event(&cs_etm_pmu, 1, evsel); 141 142 if (arm_spe_pmus && !found_spe) 143 found_spe = find_pmu_for_event(arm_spe_pmus, nr_spes, evsel); 144 145 if (hisi_ptt_pmus && !found_ptt) 146 found_ptt = find_pmu_for_event(hisi_ptt_pmus, nr_ptts, evsel); 147 } 148 149 free(arm_spe_pmus); 150 free(hisi_ptt_pmus); 151 152 if (found_etm) 153 auxtrace_event_cnt++; 154 155 if (found_spe) 156 auxtrace_event_cnt++; 157 158 if (found_ptt) 159 auxtrace_event_cnt++; 160 161 if (auxtrace_event_cnt > 1) { 162 pr_err("Concurrent AUX trace operation not currently supported\n"); 163 *err = -EOPNOTSUPP; 164 return NULL; 165 } 166 167 if (found_etm) 168 return cs_etm_record_init(err); 169 170 #if defined(__aarch64__) 171 if (found_spe) 172 return arm_spe_recording_init(err, found_spe); 173 174 if (found_ptt) 175 return hisi_ptt_recording_init(err, found_ptt); 176 #endif 177 178 /* 179 * Clear 'err' even if we haven't found an event - that way perf 180 * record can still be used even if tracers aren't present. The NULL 181 * return value will take care of telling the infrastructure HW tracing 182 * isn't available. 183 */ 184 *err = 0; 185 return NULL; 186 } 187 188 #if defined(__arm__) 189 u64 compat_auxtrace_mmap__read_head(struct auxtrace_mmap *mm) 190 { 191 struct perf_event_mmap_page *pc = mm->userpg; 192 u64 result; 193 194 __asm__ __volatile__( 195 " ldrd %0, %H0, [%1]" 196 : "=&r" (result) 197 : "r" (&pc->aux_head), "Qo" (pc->aux_head) 198 ); 199 200 return result; 201 } 202 203 int compat_auxtrace_mmap__write_tail(struct auxtrace_mmap *mm, u64 tail) 204 { 205 struct perf_event_mmap_page *pc = mm->userpg; 206 207 /* Ensure all reads are done before we write the tail out */ 208 smp_mb(); 209 210 __asm__ __volatile__( 211 " strd %2, %H2, [%1]" 212 : "=Qo" (pc->aux_tail) 213 : "r" (&pc->aux_tail), "r" (tail) 214 ); 215 216 return 0; 217 } 218 #endif 219