162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(C) 2015 Linaro Limited. All rights reserved. 462306a36Sopenharmony_ci * Author: Mathieu Poirier <mathieu.poirier@linaro.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <api/fs/fs.h> 862306a36Sopenharmony_ci#include <linux/bits.h> 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/compiler.h> 1162306a36Sopenharmony_ci#include <linux/coresight-pmu.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/log2.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/zalloc.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "cs-etm.h" 1962306a36Sopenharmony_ci#include "../../../util/debug.h" 2062306a36Sopenharmony_ci#include "../../../util/record.h" 2162306a36Sopenharmony_ci#include "../../../util/auxtrace.h" 2262306a36Sopenharmony_ci#include "../../../util/cpumap.h" 2362306a36Sopenharmony_ci#include "../../../util/event.h" 2462306a36Sopenharmony_ci#include "../../../util/evlist.h" 2562306a36Sopenharmony_ci#include "../../../util/evsel.h" 2662306a36Sopenharmony_ci#include "../../../util/perf_api_probe.h" 2762306a36Sopenharmony_ci#include "../../../util/evsel_config.h" 2862306a36Sopenharmony_ci#include "../../../util/pmus.h" 2962306a36Sopenharmony_ci#include "../../../util/cs-etm.h" 3062306a36Sopenharmony_ci#include <internal/lib.h> // page_size 3162306a36Sopenharmony_ci#include "../../../util/session.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <errno.h> 3462306a36Sopenharmony_ci#include <stdlib.h> 3562306a36Sopenharmony_ci#include <sys/stat.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct cs_etm_recording { 3862306a36Sopenharmony_ci struct auxtrace_record itr; 3962306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu; 4062306a36Sopenharmony_ci struct evlist *evlist; 4162306a36Sopenharmony_ci bool snapshot_mode; 4262306a36Sopenharmony_ci size_t snapshot_size; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = { 4662306a36Sopenharmony_ci [CS_ETM_ETMCCER] = "mgmt/etmccer", 4762306a36Sopenharmony_ci [CS_ETM_ETMIDR] = "mgmt/etmidr", 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const char * const metadata_etmv4_ro[] = { 5162306a36Sopenharmony_ci [CS_ETMV4_TRCIDR0] = "trcidr/trcidr0", 5262306a36Sopenharmony_ci [CS_ETMV4_TRCIDR1] = "trcidr/trcidr1", 5362306a36Sopenharmony_ci [CS_ETMV4_TRCIDR2] = "trcidr/trcidr2", 5462306a36Sopenharmony_ci [CS_ETMV4_TRCIDR8] = "trcidr/trcidr8", 5562306a36Sopenharmony_ci [CS_ETMV4_TRCAUTHSTATUS] = "mgmt/trcauthstatus", 5662306a36Sopenharmony_ci [CS_ETMV4_TS_SOURCE] = "ts_source", 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic const char * const metadata_ete_ro[] = { 6062306a36Sopenharmony_ci [CS_ETE_TRCIDR0] = "trcidr/trcidr0", 6162306a36Sopenharmony_ci [CS_ETE_TRCIDR1] = "trcidr/trcidr1", 6262306a36Sopenharmony_ci [CS_ETE_TRCIDR2] = "trcidr/trcidr2", 6362306a36Sopenharmony_ci [CS_ETE_TRCIDR8] = "trcidr/trcidr8", 6462306a36Sopenharmony_ci [CS_ETE_TRCAUTHSTATUS] = "mgmt/trcauthstatus", 6562306a36Sopenharmony_ci [CS_ETE_TRCDEVARCH] = "mgmt/trcdevarch", 6662306a36Sopenharmony_ci [CS_ETE_TS_SOURCE] = "ts_source", 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu); 7062306a36Sopenharmony_cistatic bool cs_etm_is_ete(struct auxtrace_record *itr, int cpu); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int cs_etm_validate_context_id(struct auxtrace_record *itr, 7362306a36Sopenharmony_ci struct evsel *evsel, int cpu) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct cs_etm_recording *ptr = 7662306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 7762306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 7862306a36Sopenharmony_ci char path[PATH_MAX]; 7962306a36Sopenharmony_ci int err; 8062306a36Sopenharmony_ci u32 val; 8162306a36Sopenharmony_ci u64 contextid = evsel->core.attr.config & 8262306a36Sopenharmony_ci (perf_pmu__format_bits(cs_etm_pmu, "contextid") | 8362306a36Sopenharmony_ci perf_pmu__format_bits(cs_etm_pmu, "contextid1") | 8462306a36Sopenharmony_ci perf_pmu__format_bits(cs_etm_pmu, "contextid2")); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!contextid) 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Not supported in etmv3 */ 9062306a36Sopenharmony_ci if (!cs_etm_is_etmv4(itr, cpu)) { 9162306a36Sopenharmony_ci pr_err("%s: contextid not supported in ETMv3, disable with %s/contextid=0/\n", 9262306a36Sopenharmony_ci CORESIGHT_ETM_PMU_NAME, CORESIGHT_ETM_PMU_NAME); 9362306a36Sopenharmony_ci return -EINVAL; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Get a handle on TRCIDR2 */ 9762306a36Sopenharmony_ci snprintf(path, PATH_MAX, "cpu%d/%s", 9862306a36Sopenharmony_ci cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR2]); 9962306a36Sopenharmony_ci err = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* There was a problem reading the file, bailing out */ 10262306a36Sopenharmony_ci if (err != 1) { 10362306a36Sopenharmony_ci pr_err("%s: can't read file %s\n", CORESIGHT_ETM_PMU_NAME, 10462306a36Sopenharmony_ci path); 10562306a36Sopenharmony_ci return err; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (contextid & 10962306a36Sopenharmony_ci perf_pmu__format_bits(cs_etm_pmu, "contextid1")) { 11062306a36Sopenharmony_ci /* 11162306a36Sopenharmony_ci * TRCIDR2.CIDSIZE, bit [9-5], indicates whether contextID 11262306a36Sopenharmony_ci * tracing is supported: 11362306a36Sopenharmony_ci * 0b00000 Context ID tracing is not supported. 11462306a36Sopenharmony_ci * 0b00100 Maximum of 32-bit Context ID size. 11562306a36Sopenharmony_ci * All other values are reserved. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci if (BMVAL(val, 5, 9) != 0x4) { 11862306a36Sopenharmony_ci pr_err("%s: CONTEXTIDR_EL1 isn't supported, disable with %s/contextid1=0/\n", 11962306a36Sopenharmony_ci CORESIGHT_ETM_PMU_NAME, CORESIGHT_ETM_PMU_NAME); 12062306a36Sopenharmony_ci return -EINVAL; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (contextid & 12562306a36Sopenharmony_ci perf_pmu__format_bits(cs_etm_pmu, "contextid2")) { 12662306a36Sopenharmony_ci /* 12762306a36Sopenharmony_ci * TRCIDR2.VMIDOPT[30:29] != 0 and 12862306a36Sopenharmony_ci * TRCIDR2.VMIDSIZE[14:10] == 0b00100 (32bit virtual contextid) 12962306a36Sopenharmony_ci * We can't support CONTEXTIDR in VMID if the size of the 13062306a36Sopenharmony_ci * virtual context id is < 32bit. 13162306a36Sopenharmony_ci * Any value of VMIDSIZE >= 4 (i.e, > 32bit) is fine for us. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci if (!BMVAL(val, 29, 30) || BMVAL(val, 10, 14) < 4) { 13462306a36Sopenharmony_ci pr_err("%s: CONTEXTIDR_EL2 isn't supported, disable with %s/contextid2=0/\n", 13562306a36Sopenharmony_ci CORESIGHT_ETM_PMU_NAME, CORESIGHT_ETM_PMU_NAME); 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int cs_etm_validate_timestamp(struct auxtrace_record *itr, 14462306a36Sopenharmony_ci struct evsel *evsel, int cpu) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct cs_etm_recording *ptr = 14762306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 14862306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 14962306a36Sopenharmony_ci char path[PATH_MAX]; 15062306a36Sopenharmony_ci int err; 15162306a36Sopenharmony_ci u32 val; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (!(evsel->core.attr.config & 15462306a36Sopenharmony_ci perf_pmu__format_bits(cs_etm_pmu, "timestamp"))) 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (!cs_etm_is_etmv4(itr, cpu)) { 15862306a36Sopenharmony_ci pr_err("%s: timestamp not supported in ETMv3, disable with %s/timestamp=0/\n", 15962306a36Sopenharmony_ci CORESIGHT_ETM_PMU_NAME, CORESIGHT_ETM_PMU_NAME); 16062306a36Sopenharmony_ci return -EINVAL; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Get a handle on TRCIRD0 */ 16462306a36Sopenharmony_ci snprintf(path, PATH_MAX, "cpu%d/%s", 16562306a36Sopenharmony_ci cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); 16662306a36Sopenharmony_ci err = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* There was a problem reading the file, bailing out */ 16962306a36Sopenharmony_ci if (err != 1) { 17062306a36Sopenharmony_ci pr_err("%s: can't read file %s\n", 17162306a36Sopenharmony_ci CORESIGHT_ETM_PMU_NAME, path); 17262306a36Sopenharmony_ci return err; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* 17662306a36Sopenharmony_ci * TRCIDR0.TSSIZE, bit [28-24], indicates whether global timestamping 17762306a36Sopenharmony_ci * is supported: 17862306a36Sopenharmony_ci * 0b00000 Global timestamping is not implemented 17962306a36Sopenharmony_ci * 0b00110 Implementation supports a maximum timestamp of 48bits. 18062306a36Sopenharmony_ci * 0b01000 Implementation supports a maximum timestamp of 64bits. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci val &= GENMASK(28, 24); 18362306a36Sopenharmony_ci if (!val) { 18462306a36Sopenharmony_ci return -EINVAL; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * Check whether the requested timestamp and contextid options should be 19262306a36Sopenharmony_ci * available on all requested CPUs and if not, tell the user how to override. 19362306a36Sopenharmony_ci * The kernel will silently disable any unavailable options so a warning here 19462306a36Sopenharmony_ci * first is better. In theory the kernel could still disable the option for 19562306a36Sopenharmony_ci * some other reason so this is best effort only. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic int cs_etm_validate_config(struct auxtrace_record *itr, 19862306a36Sopenharmony_ci struct evsel *evsel) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int i, err = -EINVAL; 20162306a36Sopenharmony_ci struct perf_cpu_map *event_cpus = evsel->evlist->core.user_requested_cpus; 20262306a36Sopenharmony_ci struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Set option of each CPU we have */ 20562306a36Sopenharmony_ci for (i = 0; i < cpu__max_cpu().cpu; i++) { 20662306a36Sopenharmony_ci struct perf_cpu cpu = { .cpu = i, }; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (!perf_cpu_map__has(event_cpus, cpu) || 20962306a36Sopenharmony_ci !perf_cpu_map__has(online_cpus, cpu)) 21062306a36Sopenharmony_ci continue; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci err = cs_etm_validate_context_id(itr, evsel, i); 21362306a36Sopenharmony_ci if (err) 21462306a36Sopenharmony_ci goto out; 21562306a36Sopenharmony_ci err = cs_etm_validate_timestamp(itr, evsel, i); 21662306a36Sopenharmony_ci if (err) 21762306a36Sopenharmony_ci goto out; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci err = 0; 22162306a36Sopenharmony_ciout: 22262306a36Sopenharmony_ci perf_cpu_map__put(online_cpus); 22362306a36Sopenharmony_ci return err; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int cs_etm_parse_snapshot_options(struct auxtrace_record *itr, 22762306a36Sopenharmony_ci struct record_opts *opts, 22862306a36Sopenharmony_ci const char *str) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct cs_etm_recording *ptr = 23162306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 23262306a36Sopenharmony_ci unsigned long long snapshot_size = 0; 23362306a36Sopenharmony_ci char *endptr; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (str) { 23662306a36Sopenharmony_ci snapshot_size = strtoull(str, &endptr, 0); 23762306a36Sopenharmony_ci if (*endptr || snapshot_size > SIZE_MAX) 23862306a36Sopenharmony_ci return -1; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci opts->auxtrace_snapshot_mode = true; 24262306a36Sopenharmony_ci opts->auxtrace_snapshot_size = snapshot_size; 24362306a36Sopenharmony_ci ptr->snapshot_size = snapshot_size; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int cs_etm_set_sink_attr(struct perf_pmu *pmu, 24962306a36Sopenharmony_ci struct evsel *evsel) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci char msg[BUFSIZ], path[PATH_MAX], *sink; 25262306a36Sopenharmony_ci struct evsel_config_term *term; 25362306a36Sopenharmony_ci int ret = -EINVAL; 25462306a36Sopenharmony_ci u32 hash; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (evsel->core.attr.config2 & GENMASK(31, 0)) 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci list_for_each_entry(term, &evsel->config_terms, list) { 26062306a36Sopenharmony_ci if (term->type != EVSEL__CONFIG_TERM_DRV_CFG) 26162306a36Sopenharmony_ci continue; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci sink = term->val.str; 26462306a36Sopenharmony_ci snprintf(path, PATH_MAX, "sinks/%s", sink); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ret = perf_pmu__scan_file(pmu, path, "%x", &hash); 26762306a36Sopenharmony_ci if (ret != 1) { 26862306a36Sopenharmony_ci if (errno == ENOENT) 26962306a36Sopenharmony_ci pr_err("Couldn't find sink \"%s\" on event %s\n" 27062306a36Sopenharmony_ci "Missing kernel or device support?\n\n" 27162306a36Sopenharmony_ci "Hint: An appropriate sink will be picked automatically if one isn't specified.\n", 27262306a36Sopenharmony_ci sink, evsel__name(evsel)); 27362306a36Sopenharmony_ci else 27462306a36Sopenharmony_ci pr_err("Failed to set sink \"%s\" on event %s with %d (%s)\n", 27562306a36Sopenharmony_ci sink, evsel__name(evsel), errno, 27662306a36Sopenharmony_ci str_error_r(errno, msg, sizeof(msg))); 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci evsel->core.attr.config2 |= hash; 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * No sink was provided on the command line - allow the CoreSight 28662306a36Sopenharmony_ci * system to look for a default 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic int cs_etm_recording_options(struct auxtrace_record *itr, 29262306a36Sopenharmony_ci struct evlist *evlist, 29362306a36Sopenharmony_ci struct record_opts *opts) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci int ret; 29662306a36Sopenharmony_ci struct cs_etm_recording *ptr = 29762306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 29862306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 29962306a36Sopenharmony_ci struct evsel *evsel, *cs_etm_evsel = NULL; 30062306a36Sopenharmony_ci struct perf_cpu_map *cpus = evlist->core.user_requested_cpus; 30162306a36Sopenharmony_ci bool privileged = perf_event_paranoid_check(-1); 30262306a36Sopenharmony_ci int err = 0; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci evlist__for_each_entry(evlist, evsel) { 30562306a36Sopenharmony_ci if (evsel->core.attr.type == cs_etm_pmu->type) { 30662306a36Sopenharmony_ci if (cs_etm_evsel) { 30762306a36Sopenharmony_ci pr_err("There may be only one %s event\n", 30862306a36Sopenharmony_ci CORESIGHT_ETM_PMU_NAME); 30962306a36Sopenharmony_ci return -EINVAL; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci cs_etm_evsel = evsel; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* no need to continue if at least one event of interest was found */ 31662306a36Sopenharmony_ci if (!cs_etm_evsel) 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ptr->evlist = evlist; 32062306a36Sopenharmony_ci ptr->snapshot_mode = opts->auxtrace_snapshot_mode; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (!record_opts__no_switch_events(opts) && 32362306a36Sopenharmony_ci perf_can_record_switch_events()) 32462306a36Sopenharmony_ci opts->record_switch_events = true; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci cs_etm_evsel->needs_auxtrace_mmap = true; 32762306a36Sopenharmony_ci opts->full_auxtrace = true; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci ret = cs_etm_set_sink_attr(cs_etm_pmu, cs_etm_evsel); 33062306a36Sopenharmony_ci if (ret) 33162306a36Sopenharmony_ci return ret; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (opts->use_clockid) { 33462306a36Sopenharmony_ci pr_err("Cannot use clockid (-k option) with %s\n", 33562306a36Sopenharmony_ci CORESIGHT_ETM_PMU_NAME); 33662306a36Sopenharmony_ci return -EINVAL; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* we are in snapshot mode */ 34062306a36Sopenharmony_ci if (opts->auxtrace_snapshot_mode) { 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * No size were given to '-S' or '-m,', so go with 34362306a36Sopenharmony_ci * the default 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_size && 34662306a36Sopenharmony_ci !opts->auxtrace_mmap_pages) { 34762306a36Sopenharmony_ci if (privileged) { 34862306a36Sopenharmony_ci opts->auxtrace_mmap_pages = MiB(4) / page_size; 34962306a36Sopenharmony_ci } else { 35062306a36Sopenharmony_ci opts->auxtrace_mmap_pages = 35162306a36Sopenharmony_ci KiB(128) / page_size; 35262306a36Sopenharmony_ci if (opts->mmap_pages == UINT_MAX) 35362306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } else if (!opts->auxtrace_mmap_pages && !privileged && 35662306a36Sopenharmony_ci opts->mmap_pages == UINT_MAX) { 35762306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* 36162306a36Sopenharmony_ci * '-m,xyz' was specified but no snapshot size, so make the 36262306a36Sopenharmony_ci * snapshot size as big as the auxtrace mmap area. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_size) { 36562306a36Sopenharmony_ci opts->auxtrace_snapshot_size = 36662306a36Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* 37062306a36Sopenharmony_ci * -Sxyz was specified but no auxtrace mmap area, so make the 37162306a36Sopenharmony_ci * auxtrace mmap area big enough to fit the requested snapshot 37262306a36Sopenharmony_ci * size. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci if (!opts->auxtrace_mmap_pages) { 37562306a36Sopenharmony_ci size_t sz = opts->auxtrace_snapshot_size; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci sz = round_up(sz, page_size) / page_size; 37862306a36Sopenharmony_ci opts->auxtrace_mmap_pages = roundup_pow_of_two(sz); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* Snapshot size can't be bigger than the auxtrace area */ 38262306a36Sopenharmony_ci if (opts->auxtrace_snapshot_size > 38362306a36Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size) { 38462306a36Sopenharmony_ci pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n", 38562306a36Sopenharmony_ci opts->auxtrace_snapshot_size, 38662306a36Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size); 38762306a36Sopenharmony_ci return -EINVAL; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Something went wrong somewhere - this shouldn't happen */ 39162306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_size || 39262306a36Sopenharmony_ci !opts->auxtrace_mmap_pages) { 39362306a36Sopenharmony_ci pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n"); 39462306a36Sopenharmony_ci return -EINVAL; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Buffer sizes weren't specified with '-m,xyz' so give some defaults */ 39962306a36Sopenharmony_ci if (!opts->auxtrace_mmap_pages) { 40062306a36Sopenharmony_ci if (privileged) { 40162306a36Sopenharmony_ci opts->auxtrace_mmap_pages = MiB(4) / page_size; 40262306a36Sopenharmony_ci } else { 40362306a36Sopenharmony_ci opts->auxtrace_mmap_pages = KiB(128) / page_size; 40462306a36Sopenharmony_ci if (opts->mmap_pages == UINT_MAX) 40562306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (opts->auxtrace_snapshot_mode) 41062306a36Sopenharmony_ci pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME, 41162306a36Sopenharmony_ci opts->auxtrace_snapshot_size); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * To obtain the auxtrace buffer file descriptor, the auxtrace 41562306a36Sopenharmony_ci * event must come first. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci evlist__to_front(evlist, cs_etm_evsel); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * get the CPU on the sample - need it to associate trace ID in the 42162306a36Sopenharmony_ci * AUX_OUTPUT_HW_ID event, and the AUX event for per-cpu mmaps. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci evsel__set_sample_bit(cs_etm_evsel, CPU); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * Also the case of per-cpu mmaps, need the contextID in order to be notified 42762306a36Sopenharmony_ci * when a context switch happened. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci if (!perf_cpu_map__empty(cpus)) { 43062306a36Sopenharmony_ci evsel__set_config_if_unset(cs_etm_pmu, cs_etm_evsel, 43162306a36Sopenharmony_ci "timestamp", 1); 43262306a36Sopenharmony_ci evsel__set_config_if_unset(cs_etm_pmu, cs_etm_evsel, 43362306a36Sopenharmony_ci "contextid", 1); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* Add dummy event to keep tracking */ 43762306a36Sopenharmony_ci err = parse_event(evlist, "dummy:u"); 43862306a36Sopenharmony_ci if (err) 43962306a36Sopenharmony_ci goto out; 44062306a36Sopenharmony_ci evsel = evlist__last(evlist); 44162306a36Sopenharmony_ci evlist__set_tracking_event(evlist, evsel); 44262306a36Sopenharmony_ci evsel->core.attr.freq = 0; 44362306a36Sopenharmony_ci evsel->core.attr.sample_period = 1; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* In per-cpu case, always need the time of mmap events etc */ 44662306a36Sopenharmony_ci if (!perf_cpu_map__empty(cpus)) 44762306a36Sopenharmony_ci evsel__set_sample_bit(evsel, TIME); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci err = cs_etm_validate_config(itr, cs_etm_evsel); 45062306a36Sopenharmony_ciout: 45162306a36Sopenharmony_ci return err; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic u64 cs_etm_get_config(struct auxtrace_record *itr) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci u64 config = 0; 45762306a36Sopenharmony_ci struct cs_etm_recording *ptr = 45862306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 45962306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 46062306a36Sopenharmony_ci struct evlist *evlist = ptr->evlist; 46162306a36Sopenharmony_ci struct evsel *evsel; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci evlist__for_each_entry(evlist, evsel) { 46462306a36Sopenharmony_ci if (evsel->core.attr.type == cs_etm_pmu->type) { 46562306a36Sopenharmony_ci /* 46662306a36Sopenharmony_ci * Variable perf_event_attr::config is assigned to 46762306a36Sopenharmony_ci * ETMv3/PTM. The bit fields have been made to match 46862306a36Sopenharmony_ci * the ETMv3.5 ETRMCR register specification. See the 46962306a36Sopenharmony_ci * PMU_FORMAT_ATTR() declarations in 47062306a36Sopenharmony_ci * drivers/hwtracing/coresight/coresight-perf.c for 47162306a36Sopenharmony_ci * details. 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_ci config = evsel->core.attr.config; 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return config; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci#ifndef BIT 48262306a36Sopenharmony_ci#define BIT(N) (1UL << (N)) 48362306a36Sopenharmony_ci#endif 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic u64 cs_etmv4_get_config(struct auxtrace_record *itr) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci u64 config = 0; 48862306a36Sopenharmony_ci u64 config_opts = 0; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * The perf event variable config bits represent both 49262306a36Sopenharmony_ci * the command line options and register programming 49362306a36Sopenharmony_ci * bits in ETMv3/PTM. For ETMv4 we must remap options 49462306a36Sopenharmony_ci * to real bits 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci config_opts = cs_etm_get_config(itr); 49762306a36Sopenharmony_ci if (config_opts & BIT(ETM_OPT_CYCACC)) 49862306a36Sopenharmony_ci config |= BIT(ETM4_CFG_BIT_CYCACC); 49962306a36Sopenharmony_ci if (config_opts & BIT(ETM_OPT_CTXTID)) 50062306a36Sopenharmony_ci config |= BIT(ETM4_CFG_BIT_CTXTID); 50162306a36Sopenharmony_ci if (config_opts & BIT(ETM_OPT_TS)) 50262306a36Sopenharmony_ci config |= BIT(ETM4_CFG_BIT_TS); 50362306a36Sopenharmony_ci if (config_opts & BIT(ETM_OPT_RETSTK)) 50462306a36Sopenharmony_ci config |= BIT(ETM4_CFG_BIT_RETSTK); 50562306a36Sopenharmony_ci if (config_opts & BIT(ETM_OPT_CTXTID2)) 50662306a36Sopenharmony_ci config |= BIT(ETM4_CFG_BIT_VMID) | 50762306a36Sopenharmony_ci BIT(ETM4_CFG_BIT_VMID_OPT); 50862306a36Sopenharmony_ci if (config_opts & BIT(ETM_OPT_BRANCH_BROADCAST)) 50962306a36Sopenharmony_ci config |= BIT(ETM4_CFG_BIT_BB); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return config; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic size_t 51562306a36Sopenharmony_cics_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused, 51662306a36Sopenharmony_ci struct evlist *evlist __maybe_unused) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci int i; 51962306a36Sopenharmony_ci int etmv3 = 0, etmv4 = 0, ete = 0; 52062306a36Sopenharmony_ci struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus; 52162306a36Sopenharmony_ci struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* cpu map is not empty, we have specific CPUs to work with */ 52462306a36Sopenharmony_ci if (!perf_cpu_map__empty(event_cpus)) { 52562306a36Sopenharmony_ci for (i = 0; i < cpu__max_cpu().cpu; i++) { 52662306a36Sopenharmony_ci struct perf_cpu cpu = { .cpu = i, }; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (!perf_cpu_map__has(event_cpus, cpu) || 52962306a36Sopenharmony_ci !perf_cpu_map__has(online_cpus, cpu)) 53062306a36Sopenharmony_ci continue; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (cs_etm_is_ete(itr, i)) 53362306a36Sopenharmony_ci ete++; 53462306a36Sopenharmony_ci else if (cs_etm_is_etmv4(itr, i)) 53562306a36Sopenharmony_ci etmv4++; 53662306a36Sopenharmony_ci else 53762306a36Sopenharmony_ci etmv3++; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci } else { 54062306a36Sopenharmony_ci /* get configuration for all CPUs in the system */ 54162306a36Sopenharmony_ci for (i = 0; i < cpu__max_cpu().cpu; i++) { 54262306a36Sopenharmony_ci struct perf_cpu cpu = { .cpu = i, }; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!perf_cpu_map__has(online_cpus, cpu)) 54562306a36Sopenharmony_ci continue; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (cs_etm_is_ete(itr, i)) 54862306a36Sopenharmony_ci ete++; 54962306a36Sopenharmony_ci else if (cs_etm_is_etmv4(itr, i)) 55062306a36Sopenharmony_ci etmv4++; 55162306a36Sopenharmony_ci else 55262306a36Sopenharmony_ci etmv3++; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci perf_cpu_map__put(online_cpus); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return (CS_ETM_HEADER_SIZE + 55962306a36Sopenharmony_ci (ete * CS_ETE_PRIV_SIZE) + 56062306a36Sopenharmony_ci (etmv4 * CS_ETMV4_PRIV_SIZE) + 56162306a36Sopenharmony_ci (etmv3 * CS_ETMV3_PRIV_SIZE)); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci bool ret = false; 56762306a36Sopenharmony_ci char path[PATH_MAX]; 56862306a36Sopenharmony_ci int scan; 56962306a36Sopenharmony_ci unsigned int val; 57062306a36Sopenharmony_ci struct cs_etm_recording *ptr = 57162306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 57262306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Take any of the RO files for ETMv4 and see if it present */ 57562306a36Sopenharmony_ci snprintf(path, PATH_MAX, "cpu%d/%s", 57662306a36Sopenharmony_ci cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); 57762306a36Sopenharmony_ci scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* The file was read successfully, we have a winner */ 58062306a36Sopenharmony_ci if (scan == 1) 58162306a36Sopenharmony_ci ret = true; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return ret; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int cs_etm_get_ro(struct perf_pmu *pmu, int cpu, const char *path) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci char pmu_path[PATH_MAX]; 58962306a36Sopenharmony_ci int scan; 59062306a36Sopenharmony_ci unsigned int val = 0; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Get RO metadata from sysfs */ 59362306a36Sopenharmony_ci snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci scan = perf_pmu__scan_file(pmu, pmu_path, "%x", &val); 59662306a36Sopenharmony_ci if (scan != 1) 59762306a36Sopenharmony_ci pr_err("%s: error reading: %s\n", __func__, pmu_path); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return val; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int cs_etm_get_ro_signed(struct perf_pmu *pmu, int cpu, const char *path) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci char pmu_path[PATH_MAX]; 60562306a36Sopenharmony_ci int scan; 60662306a36Sopenharmony_ci int val = 0; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Get RO metadata from sysfs */ 60962306a36Sopenharmony_ci snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci scan = perf_pmu__scan_file(pmu, pmu_path, "%d", &val); 61262306a36Sopenharmony_ci if (scan != 1) 61362306a36Sopenharmony_ci pr_err("%s: error reading: %s\n", __func__, pmu_path); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci return val; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic bool cs_etm_pmu_path_exists(struct perf_pmu *pmu, int cpu, const char *path) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci char pmu_path[PATH_MAX]; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* Get RO metadata from sysfs */ 62362306a36Sopenharmony_ci snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return perf_pmu__file_exists(pmu, pmu_path); 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci#define TRCDEVARCH_ARCHPART_SHIFT 0 62962306a36Sopenharmony_ci#define TRCDEVARCH_ARCHPART_MASK GENMASK(11, 0) 63062306a36Sopenharmony_ci#define TRCDEVARCH_ARCHPART(x) (((x) & TRCDEVARCH_ARCHPART_MASK) >> TRCDEVARCH_ARCHPART_SHIFT) 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci#define TRCDEVARCH_ARCHVER_SHIFT 12 63362306a36Sopenharmony_ci#define TRCDEVARCH_ARCHVER_MASK GENMASK(15, 12) 63462306a36Sopenharmony_ci#define TRCDEVARCH_ARCHVER(x) (((x) & TRCDEVARCH_ARCHVER_MASK) >> TRCDEVARCH_ARCHVER_SHIFT) 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic bool cs_etm_is_ete(struct auxtrace_record *itr, int cpu) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); 63962306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 64062306a36Sopenharmony_ci int trcdevarch; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (!cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCDEVARCH])) 64362306a36Sopenharmony_ci return false; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci trcdevarch = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCDEVARCH]); 64662306a36Sopenharmony_ci /* 64762306a36Sopenharmony_ci * ETE if ARCHVER is 5 (ARCHVER is 4 for ETM) and ARCHPART is 0xA13. 64862306a36Sopenharmony_ci * See ETM_DEVARCH_ETE_ARCH in coresight-etm4x.h 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_ci return TRCDEVARCH_ARCHVER(trcdevarch) == 5 && TRCDEVARCH_ARCHPART(trcdevarch) == 0xA13; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic void cs_etm_save_etmv4_header(__u64 data[], struct auxtrace_record *itr, int cpu) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); 65662306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* Get trace configuration register */ 65962306a36Sopenharmony_ci data[CS_ETMV4_TRCCONFIGR] = cs_etmv4_get_config(itr); 66062306a36Sopenharmony_ci /* traceID set to legacy version, in case new perf running on older system */ 66162306a36Sopenharmony_ci data[CS_ETMV4_TRCTRACEIDR] = 66262306a36Sopenharmony_ci CORESIGHT_LEGACY_CPU_TRACE_ID(cpu) | CORESIGHT_TRACE_ID_UNUSED_FLAG; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* Get read-only information from sysFS */ 66562306a36Sopenharmony_ci data[CS_ETMV4_TRCIDR0] = cs_etm_get_ro(cs_etm_pmu, cpu, 66662306a36Sopenharmony_ci metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); 66762306a36Sopenharmony_ci data[CS_ETMV4_TRCIDR1] = cs_etm_get_ro(cs_etm_pmu, cpu, 66862306a36Sopenharmony_ci metadata_etmv4_ro[CS_ETMV4_TRCIDR1]); 66962306a36Sopenharmony_ci data[CS_ETMV4_TRCIDR2] = cs_etm_get_ro(cs_etm_pmu, cpu, 67062306a36Sopenharmony_ci metadata_etmv4_ro[CS_ETMV4_TRCIDR2]); 67162306a36Sopenharmony_ci data[CS_ETMV4_TRCIDR8] = cs_etm_get_ro(cs_etm_pmu, cpu, 67262306a36Sopenharmony_ci metadata_etmv4_ro[CS_ETMV4_TRCIDR8]); 67362306a36Sopenharmony_ci data[CS_ETMV4_TRCAUTHSTATUS] = cs_etm_get_ro(cs_etm_pmu, cpu, 67462306a36Sopenharmony_ci metadata_etmv4_ro[CS_ETMV4_TRCAUTHSTATUS]); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* Kernels older than 5.19 may not expose ts_source */ 67762306a36Sopenharmony_ci if (cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TS_SOURCE])) 67862306a36Sopenharmony_ci data[CS_ETMV4_TS_SOURCE] = (__u64) cs_etm_get_ro_signed(cs_etm_pmu, cpu, 67962306a36Sopenharmony_ci metadata_etmv4_ro[CS_ETMV4_TS_SOURCE]); 68062306a36Sopenharmony_ci else { 68162306a36Sopenharmony_ci pr_debug3("[%03d] pmu file 'ts_source' not found. Fallback to safe value (-1)\n", 68262306a36Sopenharmony_ci cpu); 68362306a36Sopenharmony_ci data[CS_ETMV4_TS_SOURCE] = (__u64) -1; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic void cs_etm_save_ete_header(__u64 data[], struct auxtrace_record *itr, int cpu) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); 69062306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci /* Get trace configuration register */ 69362306a36Sopenharmony_ci data[CS_ETE_TRCCONFIGR] = cs_etmv4_get_config(itr); 69462306a36Sopenharmony_ci /* traceID set to legacy version, in case new perf running on older system */ 69562306a36Sopenharmony_ci data[CS_ETE_TRCTRACEIDR] = 69662306a36Sopenharmony_ci CORESIGHT_LEGACY_CPU_TRACE_ID(cpu) | CORESIGHT_TRACE_ID_UNUSED_FLAG; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* Get read-only information from sysFS */ 69962306a36Sopenharmony_ci data[CS_ETE_TRCIDR0] = cs_etm_get_ro(cs_etm_pmu, cpu, 70062306a36Sopenharmony_ci metadata_ete_ro[CS_ETE_TRCIDR0]); 70162306a36Sopenharmony_ci data[CS_ETE_TRCIDR1] = cs_etm_get_ro(cs_etm_pmu, cpu, 70262306a36Sopenharmony_ci metadata_ete_ro[CS_ETE_TRCIDR1]); 70362306a36Sopenharmony_ci data[CS_ETE_TRCIDR2] = cs_etm_get_ro(cs_etm_pmu, cpu, 70462306a36Sopenharmony_ci metadata_ete_ro[CS_ETE_TRCIDR2]); 70562306a36Sopenharmony_ci data[CS_ETE_TRCIDR8] = cs_etm_get_ro(cs_etm_pmu, cpu, 70662306a36Sopenharmony_ci metadata_ete_ro[CS_ETE_TRCIDR8]); 70762306a36Sopenharmony_ci data[CS_ETE_TRCAUTHSTATUS] = cs_etm_get_ro(cs_etm_pmu, cpu, 70862306a36Sopenharmony_ci metadata_ete_ro[CS_ETE_TRCAUTHSTATUS]); 70962306a36Sopenharmony_ci /* ETE uses the same registers as ETMv4 plus TRCDEVARCH */ 71062306a36Sopenharmony_ci data[CS_ETE_TRCDEVARCH] = cs_etm_get_ro(cs_etm_pmu, cpu, 71162306a36Sopenharmony_ci metadata_ete_ro[CS_ETE_TRCDEVARCH]); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* Kernels older than 5.19 may not expose ts_source */ 71462306a36Sopenharmony_ci if (cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TS_SOURCE])) 71562306a36Sopenharmony_ci data[CS_ETE_TS_SOURCE] = (__u64) cs_etm_get_ro_signed(cs_etm_pmu, cpu, 71662306a36Sopenharmony_ci metadata_ete_ro[CS_ETE_TS_SOURCE]); 71762306a36Sopenharmony_ci else { 71862306a36Sopenharmony_ci pr_debug3("[%03d] pmu file 'ts_source' not found. Fallback to safe value (-1)\n", 71962306a36Sopenharmony_ci cpu); 72062306a36Sopenharmony_ci data[CS_ETE_TS_SOURCE] = (__u64) -1; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic void cs_etm_get_metadata(int cpu, u32 *offset, 72562306a36Sopenharmony_ci struct auxtrace_record *itr, 72662306a36Sopenharmony_ci struct perf_record_auxtrace_info *info) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci u32 increment, nr_trc_params; 72962306a36Sopenharmony_ci u64 magic; 73062306a36Sopenharmony_ci struct cs_etm_recording *ptr = 73162306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 73262306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* first see what kind of tracer this cpu is affined to */ 73562306a36Sopenharmony_ci if (cs_etm_is_ete(itr, cpu)) { 73662306a36Sopenharmony_ci magic = __perf_cs_ete_magic; 73762306a36Sopenharmony_ci cs_etm_save_ete_header(&info->priv[*offset], itr, cpu); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* How much space was used */ 74062306a36Sopenharmony_ci increment = CS_ETE_PRIV_MAX; 74162306a36Sopenharmony_ci nr_trc_params = CS_ETE_PRIV_MAX - CS_ETM_COMMON_BLK_MAX_V1; 74262306a36Sopenharmony_ci } else if (cs_etm_is_etmv4(itr, cpu)) { 74362306a36Sopenharmony_ci magic = __perf_cs_etmv4_magic; 74462306a36Sopenharmony_ci cs_etm_save_etmv4_header(&info->priv[*offset], itr, cpu); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* How much space was used */ 74762306a36Sopenharmony_ci increment = CS_ETMV4_PRIV_MAX; 74862306a36Sopenharmony_ci nr_trc_params = CS_ETMV4_PRIV_MAX - CS_ETMV4_TRCCONFIGR; 74962306a36Sopenharmony_ci } else { 75062306a36Sopenharmony_ci magic = __perf_cs_etmv3_magic; 75162306a36Sopenharmony_ci /* Get configuration register */ 75262306a36Sopenharmony_ci info->priv[*offset + CS_ETM_ETMCR] = cs_etm_get_config(itr); 75362306a36Sopenharmony_ci /* traceID set to legacy value in case new perf running on old system */ 75462306a36Sopenharmony_ci info->priv[*offset + CS_ETM_ETMTRACEIDR] = 75562306a36Sopenharmony_ci CORESIGHT_LEGACY_CPU_TRACE_ID(cpu) | CORESIGHT_TRACE_ID_UNUSED_FLAG; 75662306a36Sopenharmony_ci /* Get read-only information from sysFS */ 75762306a36Sopenharmony_ci info->priv[*offset + CS_ETM_ETMCCER] = 75862306a36Sopenharmony_ci cs_etm_get_ro(cs_etm_pmu, cpu, 75962306a36Sopenharmony_ci metadata_etmv3_ro[CS_ETM_ETMCCER]); 76062306a36Sopenharmony_ci info->priv[*offset + CS_ETM_ETMIDR] = 76162306a36Sopenharmony_ci cs_etm_get_ro(cs_etm_pmu, cpu, 76262306a36Sopenharmony_ci metadata_etmv3_ro[CS_ETM_ETMIDR]); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* How much space was used */ 76562306a36Sopenharmony_ci increment = CS_ETM_PRIV_MAX; 76662306a36Sopenharmony_ci nr_trc_params = CS_ETM_PRIV_MAX - CS_ETM_ETMCR; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Build generic header portion */ 77062306a36Sopenharmony_ci info->priv[*offset + CS_ETM_MAGIC] = magic; 77162306a36Sopenharmony_ci info->priv[*offset + CS_ETM_CPU] = cpu; 77262306a36Sopenharmony_ci info->priv[*offset + CS_ETM_NR_TRC_PARAMS] = nr_trc_params; 77362306a36Sopenharmony_ci /* Where the next CPU entry should start from */ 77462306a36Sopenharmony_ci *offset += increment; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int cs_etm_info_fill(struct auxtrace_record *itr, 77862306a36Sopenharmony_ci struct perf_session *session, 77962306a36Sopenharmony_ci struct perf_record_auxtrace_info *info, 78062306a36Sopenharmony_ci size_t priv_size) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci int i; 78362306a36Sopenharmony_ci u32 offset; 78462306a36Sopenharmony_ci u64 nr_cpu, type; 78562306a36Sopenharmony_ci struct perf_cpu_map *cpu_map; 78662306a36Sopenharmony_ci struct perf_cpu_map *event_cpus = session->evlist->core.user_requested_cpus; 78762306a36Sopenharmony_ci struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL); 78862306a36Sopenharmony_ci struct cs_etm_recording *ptr = 78962306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 79062306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (priv_size != cs_etm_info_priv_size(itr, session->evlist)) 79362306a36Sopenharmony_ci return -EINVAL; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (!session->evlist->core.nr_mmaps) 79662306a36Sopenharmony_ci return -EINVAL; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* If the cpu_map is empty all online CPUs are involved */ 79962306a36Sopenharmony_ci if (perf_cpu_map__empty(event_cpus)) { 80062306a36Sopenharmony_ci cpu_map = online_cpus; 80162306a36Sopenharmony_ci } else { 80262306a36Sopenharmony_ci /* Make sure all specified CPUs are online */ 80362306a36Sopenharmony_ci for (i = 0; i < perf_cpu_map__nr(event_cpus); i++) { 80462306a36Sopenharmony_ci struct perf_cpu cpu = { .cpu = i, }; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (perf_cpu_map__has(event_cpus, cpu) && 80762306a36Sopenharmony_ci !perf_cpu_map__has(online_cpus, cpu)) 80862306a36Sopenharmony_ci return -EINVAL; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci cpu_map = event_cpus; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci nr_cpu = perf_cpu_map__nr(cpu_map); 81562306a36Sopenharmony_ci /* Get PMU type as dynamically assigned by the core */ 81662306a36Sopenharmony_ci type = cs_etm_pmu->type; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* First fill out the session header */ 81962306a36Sopenharmony_ci info->type = PERF_AUXTRACE_CS_ETM; 82062306a36Sopenharmony_ci info->priv[CS_HEADER_VERSION] = CS_HEADER_CURRENT_VERSION; 82162306a36Sopenharmony_ci info->priv[CS_PMU_TYPE_CPUS] = type << 32; 82262306a36Sopenharmony_ci info->priv[CS_PMU_TYPE_CPUS] |= nr_cpu; 82362306a36Sopenharmony_ci info->priv[CS_ETM_SNAPSHOT] = ptr->snapshot_mode; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci offset = CS_ETM_SNAPSHOT + 1; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci for (i = 0; i < cpu__max_cpu().cpu && offset < priv_size; i++) { 82862306a36Sopenharmony_ci struct perf_cpu cpu = { .cpu = i, }; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (perf_cpu_map__has(cpu_map, cpu)) 83162306a36Sopenharmony_ci cs_etm_get_metadata(i, &offset, itr, info); 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci perf_cpu_map__put(online_cpus); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci return 0; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic int cs_etm_snapshot_start(struct auxtrace_record *itr) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci struct cs_etm_recording *ptr = 84262306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 84362306a36Sopenharmony_ci struct evsel *evsel; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci evlist__for_each_entry(ptr->evlist, evsel) { 84662306a36Sopenharmony_ci if (evsel->core.attr.type == ptr->cs_etm_pmu->type) 84762306a36Sopenharmony_ci return evsel__disable(evsel); 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci return -EINVAL; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic int cs_etm_snapshot_finish(struct auxtrace_record *itr) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct cs_etm_recording *ptr = 85562306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 85662306a36Sopenharmony_ci struct evsel *evsel; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci evlist__for_each_entry(ptr->evlist, evsel) { 85962306a36Sopenharmony_ci if (evsel->core.attr.type == ptr->cs_etm_pmu->type) 86062306a36Sopenharmony_ci return evsel__enable(evsel); 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci return -EINVAL; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci return (((u64) rand() << 0) & 0x00000000FFFFFFFFull) | 86862306a36Sopenharmony_ci (((u64) rand() << 32) & 0xFFFFFFFF00000000ull); 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic void cs_etm_recording_free(struct auxtrace_record *itr) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci struct cs_etm_recording *ptr = 87462306a36Sopenharmony_ci container_of(itr, struct cs_etm_recording, itr); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci free(ptr); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistruct auxtrace_record *cs_etm_record_init(int *err) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct perf_pmu *cs_etm_pmu; 88262306a36Sopenharmony_ci struct cs_etm_recording *ptr; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci cs_etm_pmu = perf_pmus__find(CORESIGHT_ETM_PMU_NAME); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (!cs_etm_pmu) { 88762306a36Sopenharmony_ci *err = -EINVAL; 88862306a36Sopenharmony_ci goto out; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci ptr = zalloc(sizeof(struct cs_etm_recording)); 89262306a36Sopenharmony_ci if (!ptr) { 89362306a36Sopenharmony_ci *err = -ENOMEM; 89462306a36Sopenharmony_ci goto out; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci ptr->cs_etm_pmu = cs_etm_pmu; 89862306a36Sopenharmony_ci ptr->itr.pmu = cs_etm_pmu; 89962306a36Sopenharmony_ci ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options; 90062306a36Sopenharmony_ci ptr->itr.recording_options = cs_etm_recording_options; 90162306a36Sopenharmony_ci ptr->itr.info_priv_size = cs_etm_info_priv_size; 90262306a36Sopenharmony_ci ptr->itr.info_fill = cs_etm_info_fill; 90362306a36Sopenharmony_ci ptr->itr.snapshot_start = cs_etm_snapshot_start; 90462306a36Sopenharmony_ci ptr->itr.snapshot_finish = cs_etm_snapshot_finish; 90562306a36Sopenharmony_ci ptr->itr.reference = cs_etm_reference; 90662306a36Sopenharmony_ci ptr->itr.free = cs_etm_recording_free; 90762306a36Sopenharmony_ci ptr->itr.read_finish = auxtrace_record__read_finish; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci *err = 0; 91062306a36Sopenharmony_ci return &ptr->itr; 91162306a36Sopenharmony_ciout: 91262306a36Sopenharmony_ci return NULL; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci/* 91662306a36Sopenharmony_ci * Set a default config to enable the user changed config tracking mechanism 91762306a36Sopenharmony_ci * (CFG_CHG and evsel__set_config_if_unset()). If no default is set then user 91862306a36Sopenharmony_ci * changes aren't tracked. 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_cistruct perf_event_attr * 92162306a36Sopenharmony_cics_etm_get_default_config(struct perf_pmu *pmu __maybe_unused) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci struct perf_event_attr *attr; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci attr = zalloc(sizeof(struct perf_event_attr)); 92662306a36Sopenharmony_ci if (!attr) 92762306a36Sopenharmony_ci return NULL; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci attr->sample_period = 1; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return attr; 93262306a36Sopenharmony_ci} 933