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