18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright(C) 2015 Linaro Limited. All rights reserved. 48c2ecf20Sopenharmony_ci * Author: Mathieu Poirier <mathieu.poirier@linaro.org> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <api/fs/fs.h> 88c2ecf20Sopenharmony_ci#include <linux/bits.h> 98c2ecf20Sopenharmony_ci#include <linux/bitops.h> 108c2ecf20Sopenharmony_ci#include <linux/compiler.h> 118c2ecf20Sopenharmony_ci#include <linux/coresight-pmu.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/log2.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "cs-etm.h" 198c2ecf20Sopenharmony_ci#include "../../util/debug.h" 208c2ecf20Sopenharmony_ci#include "../../util/record.h" 218c2ecf20Sopenharmony_ci#include "../../util/auxtrace.h" 228c2ecf20Sopenharmony_ci#include "../../util/cpumap.h" 238c2ecf20Sopenharmony_ci#include "../../util/event.h" 248c2ecf20Sopenharmony_ci#include "../../util/evlist.h" 258c2ecf20Sopenharmony_ci#include "../../util/evsel.h" 268c2ecf20Sopenharmony_ci#include "../../util/perf_api_probe.h" 278c2ecf20Sopenharmony_ci#include "../../util/evsel_config.h" 288c2ecf20Sopenharmony_ci#include "../../util/pmu.h" 298c2ecf20Sopenharmony_ci#include "../../util/cs-etm.h" 308c2ecf20Sopenharmony_ci#include <internal/lib.h> // page_size 318c2ecf20Sopenharmony_ci#include "../../util/session.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <errno.h> 348c2ecf20Sopenharmony_ci#include <stdlib.h> 358c2ecf20Sopenharmony_ci#include <sys/stat.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct cs_etm_recording { 388c2ecf20Sopenharmony_ci struct auxtrace_record itr; 398c2ecf20Sopenharmony_ci struct perf_pmu *cs_etm_pmu; 408c2ecf20Sopenharmony_ci struct evlist *evlist; 418c2ecf20Sopenharmony_ci int wrapped_cnt; 428c2ecf20Sopenharmony_ci bool *wrapped; 438c2ecf20Sopenharmony_ci bool snapshot_mode; 448c2ecf20Sopenharmony_ci size_t snapshot_size; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = { 488c2ecf20Sopenharmony_ci [CS_ETM_ETMCCER] = "mgmt/etmccer", 498c2ecf20Sopenharmony_ci [CS_ETM_ETMIDR] = "mgmt/etmidr", 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic const char *metadata_etmv4_ro[CS_ETMV4_PRIV_MAX] = { 538c2ecf20Sopenharmony_ci [CS_ETMV4_TRCIDR0] = "trcidr/trcidr0", 548c2ecf20Sopenharmony_ci [CS_ETMV4_TRCIDR1] = "trcidr/trcidr1", 558c2ecf20Sopenharmony_ci [CS_ETMV4_TRCIDR2] = "trcidr/trcidr2", 568c2ecf20Sopenharmony_ci [CS_ETMV4_TRCIDR8] = "trcidr/trcidr8", 578c2ecf20Sopenharmony_ci [CS_ETMV4_TRCAUTHSTATUS] = "mgmt/trcauthstatus", 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int cs_etm_set_context_id(struct auxtrace_record *itr, 638c2ecf20Sopenharmony_ci struct evsel *evsel, int cpu) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr; 668c2ecf20Sopenharmony_ci struct perf_pmu *cs_etm_pmu; 678c2ecf20Sopenharmony_ci char path[PATH_MAX]; 688c2ecf20Sopenharmony_ci int err = -EINVAL; 698c2ecf20Sopenharmony_ci u32 val; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ptr = container_of(itr, struct cs_etm_recording, itr); 728c2ecf20Sopenharmony_ci cs_etm_pmu = ptr->cs_etm_pmu; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (!cs_etm_is_etmv4(itr, cpu)) 758c2ecf20Sopenharmony_ci goto out; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* Get a handle on TRCIRD2 */ 788c2ecf20Sopenharmony_ci snprintf(path, PATH_MAX, "cpu%d/%s", 798c2ecf20Sopenharmony_ci cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR2]); 808c2ecf20Sopenharmony_ci err = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* There was a problem reading the file, bailing out */ 838c2ecf20Sopenharmony_ci if (err != 1) { 848c2ecf20Sopenharmony_ci pr_err("%s: can't read file %s\n", 858c2ecf20Sopenharmony_ci CORESIGHT_ETM_PMU_NAME, path); 868c2ecf20Sopenharmony_ci goto out; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* 908c2ecf20Sopenharmony_ci * TRCIDR2.CIDSIZE, bit [9-5], indicates whether contextID tracing 918c2ecf20Sopenharmony_ci * is supported: 928c2ecf20Sopenharmony_ci * 0b00000 Context ID tracing is not supported. 938c2ecf20Sopenharmony_ci * 0b00100 Maximum of 32-bit Context ID size. 948c2ecf20Sopenharmony_ci * All other values are reserved. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci val = BMVAL(val, 5, 9); 978c2ecf20Sopenharmony_ci if (!val || val != 0x4) { 988c2ecf20Sopenharmony_ci err = -EINVAL; 998c2ecf20Sopenharmony_ci goto out; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* All good, let the kernel know */ 1038c2ecf20Sopenharmony_ci evsel->core.attr.config |= (1 << ETM_OPT_CTXTID); 1048c2ecf20Sopenharmony_ci err = 0; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciout: 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return err; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int cs_etm_set_timestamp(struct auxtrace_record *itr, 1128c2ecf20Sopenharmony_ci struct evsel *evsel, int cpu) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr; 1158c2ecf20Sopenharmony_ci struct perf_pmu *cs_etm_pmu; 1168c2ecf20Sopenharmony_ci char path[PATH_MAX]; 1178c2ecf20Sopenharmony_ci int err = -EINVAL; 1188c2ecf20Sopenharmony_ci u32 val; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ptr = container_of(itr, struct cs_etm_recording, itr); 1218c2ecf20Sopenharmony_ci cs_etm_pmu = ptr->cs_etm_pmu; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!cs_etm_is_etmv4(itr, cpu)) 1248c2ecf20Sopenharmony_ci goto out; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Get a handle on TRCIRD0 */ 1278c2ecf20Sopenharmony_ci snprintf(path, PATH_MAX, "cpu%d/%s", 1288c2ecf20Sopenharmony_ci cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); 1298c2ecf20Sopenharmony_ci err = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* There was a problem reading the file, bailing out */ 1328c2ecf20Sopenharmony_ci if (err != 1) { 1338c2ecf20Sopenharmony_ci pr_err("%s: can't read file %s\n", 1348c2ecf20Sopenharmony_ci CORESIGHT_ETM_PMU_NAME, path); 1358c2ecf20Sopenharmony_ci goto out; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* 1398c2ecf20Sopenharmony_ci * TRCIDR0.TSSIZE, bit [28-24], indicates whether global timestamping 1408c2ecf20Sopenharmony_ci * is supported: 1418c2ecf20Sopenharmony_ci * 0b00000 Global timestamping is not implemented 1428c2ecf20Sopenharmony_ci * 0b00110 Implementation supports a maximum timestamp of 48bits. 1438c2ecf20Sopenharmony_ci * 0b01000 Implementation supports a maximum timestamp of 64bits. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci val &= GENMASK(28, 24); 1468c2ecf20Sopenharmony_ci if (!val) { 1478c2ecf20Sopenharmony_ci err = -EINVAL; 1488c2ecf20Sopenharmony_ci goto out; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* All good, let the kernel know */ 1528c2ecf20Sopenharmony_ci evsel->core.attr.config |= (1 << ETM_OPT_TS); 1538c2ecf20Sopenharmony_ci err = 0; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciout: 1568c2ecf20Sopenharmony_ci return err; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int cs_etm_set_option(struct auxtrace_record *itr, 1608c2ecf20Sopenharmony_ci struct evsel *evsel, u32 option) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int i, err = -EINVAL; 1638c2ecf20Sopenharmony_ci struct perf_cpu_map *event_cpus = evsel->evlist->core.cpus; 1648c2ecf20Sopenharmony_ci struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Set option of each CPU we have */ 1678c2ecf20Sopenharmony_ci for (i = 0; i < cpu__max_cpu(); i++) { 1688c2ecf20Sopenharmony_ci if (!cpu_map__has(event_cpus, i) || 1698c2ecf20Sopenharmony_ci !cpu_map__has(online_cpus, i)) 1708c2ecf20Sopenharmony_ci continue; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (option & ETM_OPT_CTXTID) { 1738c2ecf20Sopenharmony_ci err = cs_etm_set_context_id(itr, evsel, i); 1748c2ecf20Sopenharmony_ci if (err) 1758c2ecf20Sopenharmony_ci goto out; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci if (option & ETM_OPT_TS) { 1788c2ecf20Sopenharmony_ci err = cs_etm_set_timestamp(itr, evsel, i); 1798c2ecf20Sopenharmony_ci if (err) 1808c2ecf20Sopenharmony_ci goto out; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci if (option & ~(ETM_OPT_CTXTID | ETM_OPT_TS)) 1838c2ecf20Sopenharmony_ci /* Nothing else is currently supported */ 1848c2ecf20Sopenharmony_ci goto out; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci err = 0; 1888c2ecf20Sopenharmony_ciout: 1898c2ecf20Sopenharmony_ci perf_cpu_map__put(online_cpus); 1908c2ecf20Sopenharmony_ci return err; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int cs_etm_parse_snapshot_options(struct auxtrace_record *itr, 1948c2ecf20Sopenharmony_ci struct record_opts *opts, 1958c2ecf20Sopenharmony_ci const char *str) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr = 1988c2ecf20Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 1998c2ecf20Sopenharmony_ci unsigned long long snapshot_size = 0; 2008c2ecf20Sopenharmony_ci char *endptr; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (str) { 2038c2ecf20Sopenharmony_ci snapshot_size = strtoull(str, &endptr, 0); 2048c2ecf20Sopenharmony_ci if (*endptr || snapshot_size > SIZE_MAX) 2058c2ecf20Sopenharmony_ci return -1; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci opts->auxtrace_snapshot_mode = true; 2098c2ecf20Sopenharmony_ci opts->auxtrace_snapshot_size = snapshot_size; 2108c2ecf20Sopenharmony_ci ptr->snapshot_size = snapshot_size; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int cs_etm_set_sink_attr(struct perf_pmu *pmu, 2168c2ecf20Sopenharmony_ci struct evsel *evsel) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci char msg[BUFSIZ], path[PATH_MAX], *sink; 2198c2ecf20Sopenharmony_ci struct evsel_config_term *term; 2208c2ecf20Sopenharmony_ci int ret = -EINVAL; 2218c2ecf20Sopenharmony_ci u32 hash; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (evsel->core.attr.config2 & GENMASK(31, 0)) 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci list_for_each_entry(term, &evsel->config_terms, list) { 2278c2ecf20Sopenharmony_ci if (term->type != EVSEL__CONFIG_TERM_DRV_CFG) 2288c2ecf20Sopenharmony_ci continue; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci sink = term->val.str; 2318c2ecf20Sopenharmony_ci snprintf(path, PATH_MAX, "sinks/%s", sink); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci ret = perf_pmu__scan_file(pmu, path, "%x", &hash); 2348c2ecf20Sopenharmony_ci if (ret != 1) { 2358c2ecf20Sopenharmony_ci pr_err("failed to set sink \"%s\" on event %s with %d (%s)\n", 2368c2ecf20Sopenharmony_ci sink, evsel__name(evsel), errno, 2378c2ecf20Sopenharmony_ci str_error_r(errno, msg, sizeof(msg))); 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci evsel->core.attr.config2 |= hash; 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * No sink was provided on the command line - allow the CoreSight 2478c2ecf20Sopenharmony_ci * system to look for a default 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int cs_etm_recording_options(struct auxtrace_record *itr, 2538c2ecf20Sopenharmony_ci struct evlist *evlist, 2548c2ecf20Sopenharmony_ci struct record_opts *opts) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci int ret; 2578c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr = 2588c2ecf20Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 2598c2ecf20Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 2608c2ecf20Sopenharmony_ci struct evsel *evsel, *cs_etm_evsel = NULL; 2618c2ecf20Sopenharmony_ci struct perf_cpu_map *cpus = evlist->core.cpus; 2628c2ecf20Sopenharmony_ci bool privileged = perf_event_paranoid_check(-1); 2638c2ecf20Sopenharmony_ci int err = 0; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ptr->evlist = evlist; 2668c2ecf20Sopenharmony_ci ptr->snapshot_mode = opts->auxtrace_snapshot_mode; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (!record_opts__no_switch_events(opts) && 2698c2ecf20Sopenharmony_ci perf_can_record_switch_events()) 2708c2ecf20Sopenharmony_ci opts->record_switch_events = true; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci evlist__for_each_entry(evlist, evsel) { 2738c2ecf20Sopenharmony_ci if (evsel->core.attr.type == cs_etm_pmu->type) { 2748c2ecf20Sopenharmony_ci if (cs_etm_evsel) { 2758c2ecf20Sopenharmony_ci pr_err("There may be only one %s event\n", 2768c2ecf20Sopenharmony_ci CORESIGHT_ETM_PMU_NAME); 2778c2ecf20Sopenharmony_ci return -EINVAL; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci evsel->core.attr.freq = 0; 2808c2ecf20Sopenharmony_ci evsel->core.attr.sample_period = 1; 2818c2ecf20Sopenharmony_ci cs_etm_evsel = evsel; 2828c2ecf20Sopenharmony_ci opts->full_auxtrace = true; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* no need to continue if at least one event of interest was found */ 2878c2ecf20Sopenharmony_ci if (!cs_etm_evsel) 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci ret = cs_etm_set_sink_attr(cs_etm_pmu, cs_etm_evsel); 2918c2ecf20Sopenharmony_ci if (ret) 2928c2ecf20Sopenharmony_ci return ret; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (opts->use_clockid) { 2958c2ecf20Sopenharmony_ci pr_err("Cannot use clockid (-k option) with %s\n", 2968c2ecf20Sopenharmony_ci CORESIGHT_ETM_PMU_NAME); 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* we are in snapshot mode */ 3018c2ecf20Sopenharmony_ci if (opts->auxtrace_snapshot_mode) { 3028c2ecf20Sopenharmony_ci /* 3038c2ecf20Sopenharmony_ci * No size were given to '-S' or '-m,', so go with 3048c2ecf20Sopenharmony_ci * the default 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_ci if (!opts->auxtrace_snapshot_size && 3078c2ecf20Sopenharmony_ci !opts->auxtrace_mmap_pages) { 3088c2ecf20Sopenharmony_ci if (privileged) { 3098c2ecf20Sopenharmony_ci opts->auxtrace_mmap_pages = MiB(4) / page_size; 3108c2ecf20Sopenharmony_ci } else { 3118c2ecf20Sopenharmony_ci opts->auxtrace_mmap_pages = 3128c2ecf20Sopenharmony_ci KiB(128) / page_size; 3138c2ecf20Sopenharmony_ci if (opts->mmap_pages == UINT_MAX) 3148c2ecf20Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } else if (!opts->auxtrace_mmap_pages && !privileged && 3178c2ecf20Sopenharmony_ci opts->mmap_pages == UINT_MAX) { 3188c2ecf20Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* 3228c2ecf20Sopenharmony_ci * '-m,xyz' was specified but no snapshot size, so make the 3238c2ecf20Sopenharmony_ci * snapshot size as big as the auxtrace mmap area. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci if (!opts->auxtrace_snapshot_size) { 3268c2ecf20Sopenharmony_ci opts->auxtrace_snapshot_size = 3278c2ecf20Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* 3318c2ecf20Sopenharmony_ci * -Sxyz was specified but no auxtrace mmap area, so make the 3328c2ecf20Sopenharmony_ci * auxtrace mmap area big enough to fit the requested snapshot 3338c2ecf20Sopenharmony_ci * size. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci if (!opts->auxtrace_mmap_pages) { 3368c2ecf20Sopenharmony_ci size_t sz = opts->auxtrace_snapshot_size; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci sz = round_up(sz, page_size) / page_size; 3398c2ecf20Sopenharmony_ci opts->auxtrace_mmap_pages = roundup_pow_of_two(sz); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Snapshost size can't be bigger than the auxtrace area */ 3438c2ecf20Sopenharmony_ci if (opts->auxtrace_snapshot_size > 3448c2ecf20Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size) { 3458c2ecf20Sopenharmony_ci pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n", 3468c2ecf20Sopenharmony_ci opts->auxtrace_snapshot_size, 3478c2ecf20Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size); 3488c2ecf20Sopenharmony_ci return -EINVAL; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Something went wrong somewhere - this shouldn't happen */ 3528c2ecf20Sopenharmony_ci if (!opts->auxtrace_snapshot_size || 3538c2ecf20Sopenharmony_ci !opts->auxtrace_mmap_pages) { 3548c2ecf20Sopenharmony_ci pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n"); 3558c2ecf20Sopenharmony_ci return -EINVAL; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* We are in full trace mode but '-m,xyz' wasn't specified */ 3608c2ecf20Sopenharmony_ci if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) { 3618c2ecf20Sopenharmony_ci if (privileged) { 3628c2ecf20Sopenharmony_ci opts->auxtrace_mmap_pages = MiB(4) / page_size; 3638c2ecf20Sopenharmony_ci } else { 3648c2ecf20Sopenharmony_ci opts->auxtrace_mmap_pages = KiB(128) / page_size; 3658c2ecf20Sopenharmony_ci if (opts->mmap_pages == UINT_MAX) 3668c2ecf20Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* Validate auxtrace_mmap_pages provided by user */ 3728c2ecf20Sopenharmony_ci if (opts->auxtrace_mmap_pages) { 3738c2ecf20Sopenharmony_ci unsigned int max_page = (KiB(128) / page_size); 3748c2ecf20Sopenharmony_ci size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (!privileged && 3778c2ecf20Sopenharmony_ci opts->auxtrace_mmap_pages > max_page) { 3788c2ecf20Sopenharmony_ci opts->auxtrace_mmap_pages = max_page; 3798c2ecf20Sopenharmony_ci pr_err("auxtrace too big, truncating to %d\n", 3808c2ecf20Sopenharmony_ci max_page); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (!is_power_of_2(sz)) { 3848c2ecf20Sopenharmony_ci pr_err("Invalid mmap size for %s: must be a power of 2\n", 3858c2ecf20Sopenharmony_ci CORESIGHT_ETM_PMU_NAME); 3868c2ecf20Sopenharmony_ci return -EINVAL; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (opts->auxtrace_snapshot_mode) 3918c2ecf20Sopenharmony_ci pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME, 3928c2ecf20Sopenharmony_ci opts->auxtrace_snapshot_size); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* 3958c2ecf20Sopenharmony_ci * To obtain the auxtrace buffer file descriptor, the auxtrace 3968c2ecf20Sopenharmony_ci * event must come first. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci perf_evlist__to_front(evlist, cs_etm_evsel); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* 4018c2ecf20Sopenharmony_ci * In the case of per-cpu mmaps, we need the CPU on the 4028c2ecf20Sopenharmony_ci * AUX event. We also need the contextID in order to be notified 4038c2ecf20Sopenharmony_ci * when a context switch happened. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci if (!perf_cpu_map__empty(cpus)) { 4068c2ecf20Sopenharmony_ci evsel__set_sample_bit(cs_etm_evsel, CPU); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci err = cs_etm_set_option(itr, cs_etm_evsel, 4098c2ecf20Sopenharmony_ci ETM_OPT_CTXTID | ETM_OPT_TS); 4108c2ecf20Sopenharmony_ci if (err) 4118c2ecf20Sopenharmony_ci goto out; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Add dummy event to keep tracking */ 4158c2ecf20Sopenharmony_ci if (opts->full_auxtrace) { 4168c2ecf20Sopenharmony_ci struct evsel *tracking_evsel; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci err = parse_events(evlist, "dummy:u", NULL); 4198c2ecf20Sopenharmony_ci if (err) 4208c2ecf20Sopenharmony_ci goto out; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci tracking_evsel = evlist__last(evlist); 4238c2ecf20Sopenharmony_ci perf_evlist__set_tracking_event(evlist, tracking_evsel); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci tracking_evsel->core.attr.freq = 0; 4268c2ecf20Sopenharmony_ci tracking_evsel->core.attr.sample_period = 1; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* In per-cpu case, always need the time of mmap events etc */ 4298c2ecf20Sopenharmony_ci if (!perf_cpu_map__empty(cpus)) 4308c2ecf20Sopenharmony_ci evsel__set_sample_bit(tracking_evsel, TIME); 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ciout: 4348c2ecf20Sopenharmony_ci return err; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic u64 cs_etm_get_config(struct auxtrace_record *itr) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci u64 config = 0; 4408c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr = 4418c2ecf20Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 4428c2ecf20Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 4438c2ecf20Sopenharmony_ci struct evlist *evlist = ptr->evlist; 4448c2ecf20Sopenharmony_ci struct evsel *evsel; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci evlist__for_each_entry(evlist, evsel) { 4478c2ecf20Sopenharmony_ci if (evsel->core.attr.type == cs_etm_pmu->type) { 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * Variable perf_event_attr::config is assigned to 4508c2ecf20Sopenharmony_ci * ETMv3/PTM. The bit fields have been made to match 4518c2ecf20Sopenharmony_ci * the ETMv3.5 ETRMCR register specification. See the 4528c2ecf20Sopenharmony_ci * PMU_FORMAT_ATTR() declarations in 4538c2ecf20Sopenharmony_ci * drivers/hwtracing/coresight/coresight-perf.c for 4548c2ecf20Sopenharmony_ci * details. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci config = evsel->core.attr.config; 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return config; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci#ifndef BIT 4658c2ecf20Sopenharmony_ci#define BIT(N) (1UL << (N)) 4668c2ecf20Sopenharmony_ci#endif 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic u64 cs_etmv4_get_config(struct auxtrace_record *itr) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci u64 config = 0; 4718c2ecf20Sopenharmony_ci u64 config_opts = 0; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* 4748c2ecf20Sopenharmony_ci * The perf event variable config bits represent both 4758c2ecf20Sopenharmony_ci * the command line options and register programming 4768c2ecf20Sopenharmony_ci * bits in ETMv3/PTM. For ETMv4 we must remap options 4778c2ecf20Sopenharmony_ci * to real bits 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci config_opts = cs_etm_get_config(itr); 4808c2ecf20Sopenharmony_ci if (config_opts & BIT(ETM_OPT_CYCACC)) 4818c2ecf20Sopenharmony_ci config |= BIT(ETM4_CFG_BIT_CYCACC); 4828c2ecf20Sopenharmony_ci if (config_opts & BIT(ETM_OPT_CTXTID)) 4838c2ecf20Sopenharmony_ci config |= BIT(ETM4_CFG_BIT_CTXTID); 4848c2ecf20Sopenharmony_ci if (config_opts & BIT(ETM_OPT_TS)) 4858c2ecf20Sopenharmony_ci config |= BIT(ETM4_CFG_BIT_TS); 4868c2ecf20Sopenharmony_ci if (config_opts & BIT(ETM_OPT_RETSTK)) 4878c2ecf20Sopenharmony_ci config |= BIT(ETM4_CFG_BIT_RETSTK); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return config; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic size_t 4938c2ecf20Sopenharmony_cics_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused, 4948c2ecf20Sopenharmony_ci struct evlist *evlist __maybe_unused) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci int i; 4978c2ecf20Sopenharmony_ci int etmv3 = 0, etmv4 = 0; 4988c2ecf20Sopenharmony_ci struct perf_cpu_map *event_cpus = evlist->core.cpus; 4998c2ecf20Sopenharmony_ci struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* cpu map is not empty, we have specific CPUs to work with */ 5028c2ecf20Sopenharmony_ci if (!perf_cpu_map__empty(event_cpus)) { 5038c2ecf20Sopenharmony_ci for (i = 0; i < cpu__max_cpu(); i++) { 5048c2ecf20Sopenharmony_ci if (!cpu_map__has(event_cpus, i) || 5058c2ecf20Sopenharmony_ci !cpu_map__has(online_cpus, i)) 5068c2ecf20Sopenharmony_ci continue; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (cs_etm_is_etmv4(itr, i)) 5098c2ecf20Sopenharmony_ci etmv4++; 5108c2ecf20Sopenharmony_ci else 5118c2ecf20Sopenharmony_ci etmv3++; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci } else { 5148c2ecf20Sopenharmony_ci /* get configuration for all CPUs in the system */ 5158c2ecf20Sopenharmony_ci for (i = 0; i < cpu__max_cpu(); i++) { 5168c2ecf20Sopenharmony_ci if (!cpu_map__has(online_cpus, i)) 5178c2ecf20Sopenharmony_ci continue; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (cs_etm_is_etmv4(itr, i)) 5208c2ecf20Sopenharmony_ci etmv4++; 5218c2ecf20Sopenharmony_ci else 5228c2ecf20Sopenharmony_ci etmv3++; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci perf_cpu_map__put(online_cpus); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return (CS_ETM_HEADER_SIZE + 5298c2ecf20Sopenharmony_ci (etmv4 * CS_ETMV4_PRIV_SIZE) + 5308c2ecf20Sopenharmony_ci (etmv3 * CS_ETMV3_PRIV_SIZE)); 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci bool ret = false; 5368c2ecf20Sopenharmony_ci char path[PATH_MAX]; 5378c2ecf20Sopenharmony_ci int scan; 5388c2ecf20Sopenharmony_ci unsigned int val; 5398c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr = 5408c2ecf20Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 5418c2ecf20Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Take any of the RO files for ETMv4 and see if it present */ 5448c2ecf20Sopenharmony_ci snprintf(path, PATH_MAX, "cpu%d/%s", 5458c2ecf20Sopenharmony_ci cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); 5468c2ecf20Sopenharmony_ci scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* The file was read successfully, we have a winner */ 5498c2ecf20Sopenharmony_ci if (scan == 1) 5508c2ecf20Sopenharmony_ci ret = true; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return ret; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int cs_etm_get_ro(struct perf_pmu *pmu, int cpu, const char *path) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci char pmu_path[PATH_MAX]; 5588c2ecf20Sopenharmony_ci int scan; 5598c2ecf20Sopenharmony_ci unsigned int val = 0; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Get RO metadata from sysfs */ 5628c2ecf20Sopenharmony_ci snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci scan = perf_pmu__scan_file(pmu, pmu_path, "%x", &val); 5658c2ecf20Sopenharmony_ci if (scan != 1) 5668c2ecf20Sopenharmony_ci pr_err("%s: error reading: %s\n", __func__, pmu_path); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return val; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic void cs_etm_get_metadata(int cpu, u32 *offset, 5728c2ecf20Sopenharmony_ci struct auxtrace_record *itr, 5738c2ecf20Sopenharmony_ci struct perf_record_auxtrace_info *info) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci u32 increment; 5768c2ecf20Sopenharmony_ci u64 magic; 5778c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr = 5788c2ecf20Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 5798c2ecf20Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* first see what kind of tracer this cpu is affined to */ 5828c2ecf20Sopenharmony_ci if (cs_etm_is_etmv4(itr, cpu)) { 5838c2ecf20Sopenharmony_ci magic = __perf_cs_etmv4_magic; 5848c2ecf20Sopenharmony_ci /* Get trace configuration register */ 5858c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETMV4_TRCCONFIGR] = 5868c2ecf20Sopenharmony_ci cs_etmv4_get_config(itr); 5878c2ecf20Sopenharmony_ci /* Get traceID from the framework */ 5888c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETMV4_TRCTRACEIDR] = 5898c2ecf20Sopenharmony_ci coresight_get_trace_id(cpu); 5908c2ecf20Sopenharmony_ci /* Get read-only information from sysFS */ 5918c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETMV4_TRCIDR0] = 5928c2ecf20Sopenharmony_ci cs_etm_get_ro(cs_etm_pmu, cpu, 5938c2ecf20Sopenharmony_ci metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); 5948c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETMV4_TRCIDR1] = 5958c2ecf20Sopenharmony_ci cs_etm_get_ro(cs_etm_pmu, cpu, 5968c2ecf20Sopenharmony_ci metadata_etmv4_ro[CS_ETMV4_TRCIDR1]); 5978c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETMV4_TRCIDR2] = 5988c2ecf20Sopenharmony_ci cs_etm_get_ro(cs_etm_pmu, cpu, 5998c2ecf20Sopenharmony_ci metadata_etmv4_ro[CS_ETMV4_TRCIDR2]); 6008c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETMV4_TRCIDR8] = 6018c2ecf20Sopenharmony_ci cs_etm_get_ro(cs_etm_pmu, cpu, 6028c2ecf20Sopenharmony_ci metadata_etmv4_ro[CS_ETMV4_TRCIDR8]); 6038c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETMV4_TRCAUTHSTATUS] = 6048c2ecf20Sopenharmony_ci cs_etm_get_ro(cs_etm_pmu, cpu, 6058c2ecf20Sopenharmony_ci metadata_etmv4_ro 6068c2ecf20Sopenharmony_ci [CS_ETMV4_TRCAUTHSTATUS]); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* How much space was used */ 6098c2ecf20Sopenharmony_ci increment = CS_ETMV4_PRIV_MAX; 6108c2ecf20Sopenharmony_ci } else { 6118c2ecf20Sopenharmony_ci magic = __perf_cs_etmv3_magic; 6128c2ecf20Sopenharmony_ci /* Get configuration register */ 6138c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETM_ETMCR] = cs_etm_get_config(itr); 6148c2ecf20Sopenharmony_ci /* Get traceID from the framework */ 6158c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETM_ETMTRACEIDR] = 6168c2ecf20Sopenharmony_ci coresight_get_trace_id(cpu); 6178c2ecf20Sopenharmony_ci /* Get read-only information from sysFS */ 6188c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETM_ETMCCER] = 6198c2ecf20Sopenharmony_ci cs_etm_get_ro(cs_etm_pmu, cpu, 6208c2ecf20Sopenharmony_ci metadata_etmv3_ro[CS_ETM_ETMCCER]); 6218c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETM_ETMIDR] = 6228c2ecf20Sopenharmony_ci cs_etm_get_ro(cs_etm_pmu, cpu, 6238c2ecf20Sopenharmony_ci metadata_etmv3_ro[CS_ETM_ETMIDR]); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* How much space was used */ 6268c2ecf20Sopenharmony_ci increment = CS_ETM_PRIV_MAX; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* Build generic header portion */ 6308c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETM_MAGIC] = magic; 6318c2ecf20Sopenharmony_ci info->priv[*offset + CS_ETM_CPU] = cpu; 6328c2ecf20Sopenharmony_ci /* Where the next CPU entry should start from */ 6338c2ecf20Sopenharmony_ci *offset += increment; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic int cs_etm_info_fill(struct auxtrace_record *itr, 6378c2ecf20Sopenharmony_ci struct perf_session *session, 6388c2ecf20Sopenharmony_ci struct perf_record_auxtrace_info *info, 6398c2ecf20Sopenharmony_ci size_t priv_size) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci int i; 6428c2ecf20Sopenharmony_ci u32 offset; 6438c2ecf20Sopenharmony_ci u64 nr_cpu, type; 6448c2ecf20Sopenharmony_ci struct perf_cpu_map *cpu_map; 6458c2ecf20Sopenharmony_ci struct perf_cpu_map *event_cpus = session->evlist->core.cpus; 6468c2ecf20Sopenharmony_ci struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL); 6478c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr = 6488c2ecf20Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 6498c2ecf20Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (priv_size != cs_etm_info_priv_size(itr, session->evlist)) 6528c2ecf20Sopenharmony_ci return -EINVAL; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (!session->evlist->core.nr_mmaps) 6558c2ecf20Sopenharmony_ci return -EINVAL; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* If the cpu_map is empty all online CPUs are involved */ 6588c2ecf20Sopenharmony_ci if (perf_cpu_map__empty(event_cpus)) { 6598c2ecf20Sopenharmony_ci cpu_map = online_cpus; 6608c2ecf20Sopenharmony_ci } else { 6618c2ecf20Sopenharmony_ci /* Make sure all specified CPUs are online */ 6628c2ecf20Sopenharmony_ci for (i = 0; i < perf_cpu_map__nr(event_cpus); i++) { 6638c2ecf20Sopenharmony_ci if (cpu_map__has(event_cpus, i) && 6648c2ecf20Sopenharmony_ci !cpu_map__has(online_cpus, i)) 6658c2ecf20Sopenharmony_ci return -EINVAL; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci cpu_map = event_cpus; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci nr_cpu = perf_cpu_map__nr(cpu_map); 6728c2ecf20Sopenharmony_ci /* Get PMU type as dynamically assigned by the core */ 6738c2ecf20Sopenharmony_ci type = cs_etm_pmu->type; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* First fill out the session header */ 6768c2ecf20Sopenharmony_ci info->type = PERF_AUXTRACE_CS_ETM; 6778c2ecf20Sopenharmony_ci info->priv[CS_HEADER_VERSION_0] = 0; 6788c2ecf20Sopenharmony_ci info->priv[CS_PMU_TYPE_CPUS] = type << 32; 6798c2ecf20Sopenharmony_ci info->priv[CS_PMU_TYPE_CPUS] |= nr_cpu; 6808c2ecf20Sopenharmony_ci info->priv[CS_ETM_SNAPSHOT] = ptr->snapshot_mode; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci offset = CS_ETM_SNAPSHOT + 1; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci for (i = 0; i < cpu__max_cpu() && offset < priv_size; i++) 6858c2ecf20Sopenharmony_ci if (cpu_map__has(cpu_map, i)) 6868c2ecf20Sopenharmony_ci cs_etm_get_metadata(i, &offset, itr, info); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci perf_cpu_map__put(online_cpus); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return 0; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int cs_etm_alloc_wrapped_array(struct cs_etm_recording *ptr, int idx) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci bool *wrapped; 6968c2ecf20Sopenharmony_ci int cnt = ptr->wrapped_cnt; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* Make @ptr->wrapped as big as @idx */ 6998c2ecf20Sopenharmony_ci while (cnt <= idx) 7008c2ecf20Sopenharmony_ci cnt++; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* 7038c2ecf20Sopenharmony_ci * Free'ed in cs_etm_recording_free(). Using realloc() to avoid 7048c2ecf20Sopenharmony_ci * cross compilation problems where the host's system supports 7058c2ecf20Sopenharmony_ci * reallocarray() but not the target. 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci wrapped = realloc(ptr->wrapped, cnt * sizeof(bool)); 7088c2ecf20Sopenharmony_ci if (!wrapped) 7098c2ecf20Sopenharmony_ci return -ENOMEM; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci wrapped[cnt - 1] = false; 7128c2ecf20Sopenharmony_ci ptr->wrapped_cnt = cnt; 7138c2ecf20Sopenharmony_ci ptr->wrapped = wrapped; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci return 0; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic bool cs_etm_buffer_has_wrapped(unsigned char *buffer, 7198c2ecf20Sopenharmony_ci size_t buffer_size, u64 head) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci u64 i, watermark; 7228c2ecf20Sopenharmony_ci u64 *buf = (u64 *)buffer; 7238c2ecf20Sopenharmony_ci size_t buf_size = buffer_size; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* 7268c2ecf20Sopenharmony_ci * We want to look the very last 512 byte (chosen arbitrarily) in 7278c2ecf20Sopenharmony_ci * the ring buffer. 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci watermark = buf_size - 512; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* 7328c2ecf20Sopenharmony_ci * @head is continuously increasing - if its value is equal or greater 7338c2ecf20Sopenharmony_ci * than the size of the ring buffer, it has wrapped around. 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_ci if (head >= buffer_size) 7368c2ecf20Sopenharmony_ci return true; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* 7398c2ecf20Sopenharmony_ci * The value of @head is somewhere within the size of the ring buffer. 7408c2ecf20Sopenharmony_ci * This can be that there hasn't been enough data to fill the ring 7418c2ecf20Sopenharmony_ci * buffer yet or the trace time was so long that @head has numerically 7428c2ecf20Sopenharmony_ci * wrapped around. To find we need to check if we have data at the very 7438c2ecf20Sopenharmony_ci * end of the ring buffer. We can reliably do this because mmap'ed 7448c2ecf20Sopenharmony_ci * pages are zeroed out and there is a fresh mapping with every new 7458c2ecf20Sopenharmony_ci * session. 7468c2ecf20Sopenharmony_ci */ 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* @head is less than 512 byte from the end of the ring buffer */ 7498c2ecf20Sopenharmony_ci if (head > watermark) 7508c2ecf20Sopenharmony_ci watermark = head; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* 7538c2ecf20Sopenharmony_ci * Speed things up by using 64 bit transactions (see "u64 *buf" above) 7548c2ecf20Sopenharmony_ci */ 7558c2ecf20Sopenharmony_ci watermark >>= 3; 7568c2ecf20Sopenharmony_ci buf_size >>= 3; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* 7598c2ecf20Sopenharmony_ci * If we find trace data at the end of the ring buffer, @head has 7608c2ecf20Sopenharmony_ci * been there and has numerically wrapped around at least once. 7618c2ecf20Sopenharmony_ci */ 7628c2ecf20Sopenharmony_ci for (i = watermark; i < buf_size; i++) 7638c2ecf20Sopenharmony_ci if (buf[i]) 7648c2ecf20Sopenharmony_ci return true; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci return false; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic int cs_etm_find_snapshot(struct auxtrace_record *itr, 7708c2ecf20Sopenharmony_ci int idx, struct auxtrace_mmap *mm, 7718c2ecf20Sopenharmony_ci unsigned char *data, 7728c2ecf20Sopenharmony_ci u64 *head, u64 *old) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci int err; 7758c2ecf20Sopenharmony_ci bool wrapped; 7768c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr = 7778c2ecf20Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* 7808c2ecf20Sopenharmony_ci * Allocate memory to keep track of wrapping if this is the first 7818c2ecf20Sopenharmony_ci * time we deal with this *mm. 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_ci if (idx >= ptr->wrapped_cnt) { 7848c2ecf20Sopenharmony_ci err = cs_etm_alloc_wrapped_array(ptr, idx); 7858c2ecf20Sopenharmony_ci if (err) 7868c2ecf20Sopenharmony_ci return err; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* 7908c2ecf20Sopenharmony_ci * Check to see if *head has wrapped around. If it hasn't only the 7918c2ecf20Sopenharmony_ci * amount of data between *head and *old is snapshot'ed to avoid 7928c2ecf20Sopenharmony_ci * bloating the perf.data file with zeros. But as soon as *head has 7938c2ecf20Sopenharmony_ci * wrapped around the entire size of the AUX ring buffer it taken. 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_ci wrapped = ptr->wrapped[idx]; 7968c2ecf20Sopenharmony_ci if (!wrapped && cs_etm_buffer_has_wrapped(data, mm->len, *head)) { 7978c2ecf20Sopenharmony_ci wrapped = true; 7988c2ecf20Sopenharmony_ci ptr->wrapped[idx] = true; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n", 8028c2ecf20Sopenharmony_ci __func__, idx, (size_t)*old, (size_t)*head, mm->len); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* No wrap has occurred, we can just use *head and *old. */ 8058c2ecf20Sopenharmony_ci if (!wrapped) 8068c2ecf20Sopenharmony_ci return 0; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* 8098c2ecf20Sopenharmony_ci * *head has wrapped around - adjust *head and *old to pickup the 8108c2ecf20Sopenharmony_ci * entire content of the AUX buffer. 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_ci if (*head >= mm->len) { 8138c2ecf20Sopenharmony_ci *old = *head - mm->len; 8148c2ecf20Sopenharmony_ci } else { 8158c2ecf20Sopenharmony_ci *head += mm->len; 8168c2ecf20Sopenharmony_ci *old = *head - mm->len; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic int cs_etm_snapshot_start(struct auxtrace_record *itr) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr = 8258c2ecf20Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 8268c2ecf20Sopenharmony_ci struct evsel *evsel; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci evlist__for_each_entry(ptr->evlist, evsel) { 8298c2ecf20Sopenharmony_ci if (evsel->core.attr.type == ptr->cs_etm_pmu->type) 8308c2ecf20Sopenharmony_ci return evsel__disable(evsel); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci return -EINVAL; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic int cs_etm_snapshot_finish(struct auxtrace_record *itr) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr = 8388c2ecf20Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 8398c2ecf20Sopenharmony_ci struct evsel *evsel; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci evlist__for_each_entry(ptr->evlist, evsel) { 8428c2ecf20Sopenharmony_ci if (evsel->core.attr.type == ptr->cs_etm_pmu->type) 8438c2ecf20Sopenharmony_ci return evsel__enable(evsel); 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci return -EINVAL; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci return (((u64) rand() << 0) & 0x00000000FFFFFFFFull) | 8518c2ecf20Sopenharmony_ci (((u64) rand() << 32) & 0xFFFFFFFF00000000ull); 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic void cs_etm_recording_free(struct auxtrace_record *itr) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr = 8578c2ecf20Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci zfree(&ptr->wrapped); 8608c2ecf20Sopenharmony_ci free(ptr); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistruct auxtrace_record *cs_etm_record_init(int *err) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct perf_pmu *cs_etm_pmu; 8668c2ecf20Sopenharmony_ci struct cs_etm_recording *ptr; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (!cs_etm_pmu) { 8718c2ecf20Sopenharmony_ci *err = -EINVAL; 8728c2ecf20Sopenharmony_ci goto out; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci ptr = zalloc(sizeof(struct cs_etm_recording)); 8768c2ecf20Sopenharmony_ci if (!ptr) { 8778c2ecf20Sopenharmony_ci *err = -ENOMEM; 8788c2ecf20Sopenharmony_ci goto out; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci ptr->cs_etm_pmu = cs_etm_pmu; 8828c2ecf20Sopenharmony_ci ptr->itr.pmu = cs_etm_pmu; 8838c2ecf20Sopenharmony_ci ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options; 8848c2ecf20Sopenharmony_ci ptr->itr.recording_options = cs_etm_recording_options; 8858c2ecf20Sopenharmony_ci ptr->itr.info_priv_size = cs_etm_info_priv_size; 8868c2ecf20Sopenharmony_ci ptr->itr.info_fill = cs_etm_info_fill; 8878c2ecf20Sopenharmony_ci ptr->itr.find_snapshot = cs_etm_find_snapshot; 8888c2ecf20Sopenharmony_ci ptr->itr.snapshot_start = cs_etm_snapshot_start; 8898c2ecf20Sopenharmony_ci ptr->itr.snapshot_finish = cs_etm_snapshot_finish; 8908c2ecf20Sopenharmony_ci ptr->itr.reference = cs_etm_reference; 8918c2ecf20Sopenharmony_ci ptr->itr.free = cs_etm_recording_free; 8928c2ecf20Sopenharmony_ci ptr->itr.read_finish = auxtrace_record__read_finish; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci *err = 0; 8958c2ecf20Sopenharmony_ci return &ptr->itr; 8968c2ecf20Sopenharmony_ciout: 8978c2ecf20Sopenharmony_ci return NULL; 8988c2ecf20Sopenharmony_ci} 899