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 <dirent.h>
862306a36Sopenharmony_ci#include <stdbool.h>
962306a36Sopenharmony_ci#include <linux/coresight-pmu.h>
1062306a36Sopenharmony_ci#include <linux/zalloc.h>
1162306a36Sopenharmony_ci#include <api/fs/fs.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "../../../util/auxtrace.h"
1462306a36Sopenharmony_ci#include "../../../util/debug.h"
1562306a36Sopenharmony_ci#include "../../../util/evlist.h"
1662306a36Sopenharmony_ci#include "../../../util/pmu.h"
1762306a36Sopenharmony_ci#include "../../../util/pmus.h"
1862306a36Sopenharmony_ci#include "cs-etm.h"
1962306a36Sopenharmony_ci#include "arm-spe.h"
2062306a36Sopenharmony_ci#include "hisi-ptt.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct perf_pmu **arm_spe_pmus = NULL;
2562306a36Sopenharmony_ci	int ret, i, nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
2662306a36Sopenharmony_ci	/* arm_spe_xxxxxxxxx\0 */
2762306a36Sopenharmony_ci	char arm_spe_pmu_name[sizeof(ARM_SPE_PMU_NAME) + 10];
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	arm_spe_pmus = zalloc(sizeof(struct perf_pmu *) * nr_cpus);
3062306a36Sopenharmony_ci	if (!arm_spe_pmus) {
3162306a36Sopenharmony_ci		pr_err("spes alloc failed\n");
3262306a36Sopenharmony_ci		*err = -ENOMEM;
3362306a36Sopenharmony_ci		return NULL;
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	for (i = 0; i < nr_cpus; i++) {
3762306a36Sopenharmony_ci		ret = sprintf(arm_spe_pmu_name, "%s%d", ARM_SPE_PMU_NAME, i);
3862306a36Sopenharmony_ci		if (ret < 0) {
3962306a36Sopenharmony_ci			pr_err("sprintf failed\n");
4062306a36Sopenharmony_ci			*err = -ENOMEM;
4162306a36Sopenharmony_ci			return NULL;
4262306a36Sopenharmony_ci		}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci		arm_spe_pmus[*nr_spes] = perf_pmus__find(arm_spe_pmu_name);
4562306a36Sopenharmony_ci		if (arm_spe_pmus[*nr_spes]) {
4662306a36Sopenharmony_ci			pr_debug2("%s %d: arm_spe_pmu %d type %d name %s\n",
4762306a36Sopenharmony_ci				 __func__, __LINE__, *nr_spes,
4862306a36Sopenharmony_ci				 arm_spe_pmus[*nr_spes]->type,
4962306a36Sopenharmony_ci				 arm_spe_pmus[*nr_spes]->name);
5062306a36Sopenharmony_ci			(*nr_spes)++;
5162306a36Sopenharmony_ci		}
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return arm_spe_pmus;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic struct perf_pmu **find_all_hisi_ptt_pmus(int *nr_ptts, int *err)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct perf_pmu **hisi_ptt_pmus = NULL;
6062306a36Sopenharmony_ci	struct dirent *dent;
6162306a36Sopenharmony_ci	char path[PATH_MAX];
6262306a36Sopenharmony_ci	DIR *dir = NULL;
6362306a36Sopenharmony_ci	int idx = 0;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
6662306a36Sopenharmony_ci	dir = opendir(path);
6762306a36Sopenharmony_ci	if (!dir) {
6862306a36Sopenharmony_ci		pr_err("can't read directory '%s'\n", path);
6962306a36Sopenharmony_ci		*err = -EINVAL;
7062306a36Sopenharmony_ci		return NULL;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	while ((dent = readdir(dir))) {
7462306a36Sopenharmony_ci		if (strstr(dent->d_name, HISI_PTT_PMU_NAME))
7562306a36Sopenharmony_ci			(*nr_ptts)++;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (!(*nr_ptts))
7962306a36Sopenharmony_ci		goto out;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	hisi_ptt_pmus = zalloc(sizeof(struct perf_pmu *) * (*nr_ptts));
8262306a36Sopenharmony_ci	if (!hisi_ptt_pmus) {
8362306a36Sopenharmony_ci		pr_err("hisi_ptt alloc failed\n");
8462306a36Sopenharmony_ci		*err = -ENOMEM;
8562306a36Sopenharmony_ci		goto out;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	rewinddir(dir);
8962306a36Sopenharmony_ci	while ((dent = readdir(dir))) {
9062306a36Sopenharmony_ci		if (strstr(dent->d_name, HISI_PTT_PMU_NAME) && idx < *nr_ptts) {
9162306a36Sopenharmony_ci			hisi_ptt_pmus[idx] = perf_pmus__find(dent->d_name);
9262306a36Sopenharmony_ci			if (hisi_ptt_pmus[idx])
9362306a36Sopenharmony_ci				idx++;
9462306a36Sopenharmony_ci		}
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciout:
9862306a36Sopenharmony_ci	closedir(dir);
9962306a36Sopenharmony_ci	return hisi_ptt_pmus;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct perf_pmu *find_pmu_for_event(struct perf_pmu **pmus,
10362306a36Sopenharmony_ci					   int pmu_nr, struct evsel *evsel)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	int i;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (!pmus)
10862306a36Sopenharmony_ci		return NULL;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	for (i = 0; i < pmu_nr; i++) {
11162306a36Sopenharmony_ci		if (evsel->core.attr.type == pmus[i]->type)
11262306a36Sopenharmony_ci			return pmus[i];
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return NULL;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistruct auxtrace_record
11962306a36Sopenharmony_ci*auxtrace_record__init(struct evlist *evlist, int *err)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct perf_pmu	*cs_etm_pmu = NULL;
12262306a36Sopenharmony_ci	struct perf_pmu **arm_spe_pmus = NULL;
12362306a36Sopenharmony_ci	struct perf_pmu **hisi_ptt_pmus = NULL;
12462306a36Sopenharmony_ci	struct evsel *evsel;
12562306a36Sopenharmony_ci	struct perf_pmu *found_etm = NULL;
12662306a36Sopenharmony_ci	struct perf_pmu *found_spe = NULL;
12762306a36Sopenharmony_ci	struct perf_pmu *found_ptt = NULL;
12862306a36Sopenharmony_ci	int auxtrace_event_cnt = 0;
12962306a36Sopenharmony_ci	int nr_spes = 0;
13062306a36Sopenharmony_ci	int nr_ptts = 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (!evlist)
13362306a36Sopenharmony_ci		return NULL;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	cs_etm_pmu = perf_pmus__find(CORESIGHT_ETM_PMU_NAME);
13662306a36Sopenharmony_ci	arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err);
13762306a36Sopenharmony_ci	hisi_ptt_pmus = find_all_hisi_ptt_pmus(&nr_ptts, err);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	evlist__for_each_entry(evlist, evsel) {
14062306a36Sopenharmony_ci		if (cs_etm_pmu && !found_etm)
14162306a36Sopenharmony_ci			found_etm = find_pmu_for_event(&cs_etm_pmu, 1, evsel);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		if (arm_spe_pmus && !found_spe)
14462306a36Sopenharmony_ci			found_spe = find_pmu_for_event(arm_spe_pmus, nr_spes, evsel);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		if (hisi_ptt_pmus && !found_ptt)
14762306a36Sopenharmony_ci			found_ptt = find_pmu_for_event(hisi_ptt_pmus, nr_ptts, evsel);
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	free(arm_spe_pmus);
15162306a36Sopenharmony_ci	free(hisi_ptt_pmus);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (found_etm)
15462306a36Sopenharmony_ci		auxtrace_event_cnt++;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (found_spe)
15762306a36Sopenharmony_ci		auxtrace_event_cnt++;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (found_ptt)
16062306a36Sopenharmony_ci		auxtrace_event_cnt++;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (auxtrace_event_cnt > 1) {
16362306a36Sopenharmony_ci		pr_err("Concurrent AUX trace operation not currently supported\n");
16462306a36Sopenharmony_ci		*err = -EOPNOTSUPP;
16562306a36Sopenharmony_ci		return NULL;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (found_etm)
16962306a36Sopenharmony_ci		return cs_etm_record_init(err);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci#if defined(__aarch64__)
17262306a36Sopenharmony_ci	if (found_spe)
17362306a36Sopenharmony_ci		return arm_spe_recording_init(err, found_spe);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (found_ptt)
17662306a36Sopenharmony_ci		return hisi_ptt_recording_init(err, found_ptt);
17762306a36Sopenharmony_ci#endif
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/*
18062306a36Sopenharmony_ci	 * Clear 'err' even if we haven't found an event - that way perf
18162306a36Sopenharmony_ci	 * record can still be used even if tracers aren't present.  The NULL
18262306a36Sopenharmony_ci	 * return value will take care of telling the infrastructure HW tracing
18362306a36Sopenharmony_ci	 * isn't available.
18462306a36Sopenharmony_ci	 */
18562306a36Sopenharmony_ci	*err = 0;
18662306a36Sopenharmony_ci	return NULL;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci#if defined(__arm__)
19062306a36Sopenharmony_ciu64 compat_auxtrace_mmap__read_head(struct auxtrace_mmap *mm)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct perf_event_mmap_page *pc = mm->userpg;
19362306a36Sopenharmony_ci	u64 result;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	__asm__ __volatile__(
19662306a36Sopenharmony_ci"	ldrd    %0, %H0, [%1]"
19762306a36Sopenharmony_ci	: "=&r" (result)
19862306a36Sopenharmony_ci	: "r" (&pc->aux_head), "Qo" (pc->aux_head)
19962306a36Sopenharmony_ci	);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return result;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ciint compat_auxtrace_mmap__write_tail(struct auxtrace_mmap *mm, u64 tail)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct perf_event_mmap_page *pc = mm->userpg;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* Ensure all reads are done before we write the tail out */
20962306a36Sopenharmony_ci	smp_mb();
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	__asm__ __volatile__(
21262306a36Sopenharmony_ci"	strd    %2, %H2, [%1]"
21362306a36Sopenharmony_ci	: "=Qo" (pc->aux_tail)
21462306a36Sopenharmony_ci	: "r" (&pc->aux_tail), "r" (tail)
21562306a36Sopenharmony_ci	);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return 0;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci#endif
220