162306a36Sopenharmony_ci#include <linux/kernel.h>
262306a36Sopenharmony_ci#include <linux/bits.h>
362306a36Sopenharmony_ci#include <linux/bitfield.h>
462306a36Sopenharmony_ci#include <stdio.h>
562306a36Sopenharmony_ci#include <stdlib.h>
662306a36Sopenharmony_ci#include <perf/cpumap.h>
762306a36Sopenharmony_ci#include <util/cpumap.h>
862306a36Sopenharmony_ci#include <internal/cpumap.h>
962306a36Sopenharmony_ci#include <api/fs/fs.h>
1062306a36Sopenharmony_ci#include <errno.h>
1162306a36Sopenharmony_ci#include "debug.h"
1262306a36Sopenharmony_ci#include "header.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define MIDR "/regs/identification/midr_el1"
1562306a36Sopenharmony_ci#define MIDR_SIZE 19
1662306a36Sopenharmony_ci#define MIDR_REVISION_MASK      GENMASK(3, 0)
1762306a36Sopenharmony_ci#define MIDR_VARIANT_MASK	GENMASK(23, 20)
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	const char *sysfs = sysfs__mountpoint();
2262306a36Sopenharmony_ci	int cpu;
2362306a36Sopenharmony_ci	int ret = EINVAL;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	if (!sysfs || sz < MIDR_SIZE)
2662306a36Sopenharmony_ci		return EINVAL;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	cpus = perf_cpu_map__get(cpus);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	for (cpu = 0; cpu < perf_cpu_map__nr(cpus); cpu++) {
3162306a36Sopenharmony_ci		char path[PATH_MAX];
3262306a36Sopenharmony_ci		FILE *file;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci		scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR,
3562306a36Sopenharmony_ci			  sysfs, RC_CHK_ACCESS(cpus)->map[cpu].cpu);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci		file = fopen(path, "r");
3862306a36Sopenharmony_ci		if (!file) {
3962306a36Sopenharmony_ci			pr_debug("fopen failed for file %s\n", path);
4062306a36Sopenharmony_ci			continue;
4162306a36Sopenharmony_ci		}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci		if (!fgets(buf, MIDR_SIZE, file)) {
4462306a36Sopenharmony_ci			fclose(file);
4562306a36Sopenharmony_ci			continue;
4662306a36Sopenharmony_ci		}
4762306a36Sopenharmony_ci		fclose(file);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		/* got midr break loop */
5062306a36Sopenharmony_ci		ret = 0;
5162306a36Sopenharmony_ci		break;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	perf_cpu_map__put(cpus);
5562306a36Sopenharmony_ci	return ret;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ciint get_cpuid(char *buf, size_t sz)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct perf_cpu_map *cpus = perf_cpu_map__new(NULL);
6162306a36Sopenharmony_ci	int ret;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (!cpus)
6462306a36Sopenharmony_ci		return EINVAL;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	ret = _get_cpuid(buf, sz, cpus);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	perf_cpu_map__put(cpus);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return ret;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cichar *get_cpuid_str(struct perf_pmu *pmu)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	char *buf = NULL;
7662306a36Sopenharmony_ci	int res;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (!pmu || !pmu->cpus)
7962306a36Sopenharmony_ci		return NULL;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	buf = malloc(MIDR_SIZE);
8262306a36Sopenharmony_ci	if (!buf)
8362306a36Sopenharmony_ci		return NULL;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* read midr from list of cpus mapped to this pmu */
8662306a36Sopenharmony_ci	res = _get_cpuid(buf, MIDR_SIZE, pmu->cpus);
8762306a36Sopenharmony_ci	if (res) {
8862306a36Sopenharmony_ci		pr_err("failed to get cpuid string for PMU %s\n", pmu->name);
8962306a36Sopenharmony_ci		free(buf);
9062306a36Sopenharmony_ci		buf = NULL;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return buf;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/*
9762306a36Sopenharmony_ci * Return 0 if idstr is a higher or equal to version of the same part as
9862306a36Sopenharmony_ci * mapcpuid. Therefore, if mapcpuid has 0 for revision and variant then any
9962306a36Sopenharmony_ci * version of idstr will match as long as it's the same CPU type.
10062306a36Sopenharmony_ci *
10162306a36Sopenharmony_ci * Return 1 if the CPU type is different or the version of idstr is lower.
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_ciint strcmp_cpuid_str(const char *mapcpuid, const char *idstr)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	u64 map_id = strtoull(mapcpuid, NULL, 16);
10662306a36Sopenharmony_ci	char map_id_variant = FIELD_GET(MIDR_VARIANT_MASK, map_id);
10762306a36Sopenharmony_ci	char map_id_revision = FIELD_GET(MIDR_REVISION_MASK, map_id);
10862306a36Sopenharmony_ci	u64 id = strtoull(idstr, NULL, 16);
10962306a36Sopenharmony_ci	char id_variant = FIELD_GET(MIDR_VARIANT_MASK, id);
11062306a36Sopenharmony_ci	char id_revision = FIELD_GET(MIDR_REVISION_MASK, id);
11162306a36Sopenharmony_ci	u64 id_fields = ~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* Compare without version first */
11462306a36Sopenharmony_ci	if ((map_id & id_fields) != (id & id_fields))
11562306a36Sopenharmony_ci		return 1;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/*
11862306a36Sopenharmony_ci	 * ID matches, now compare version.
11962306a36Sopenharmony_ci	 *
12062306a36Sopenharmony_ci	 * Arm revisions (like r0p0) are compared here like two digit semver
12162306a36Sopenharmony_ci	 * values eg. 1.3 < 2.0 < 2.1 < 2.2.
12262306a36Sopenharmony_ci	 *
12362306a36Sopenharmony_ci	 *  r = high value = 'Variant' field in MIDR
12462306a36Sopenharmony_ci	 *  p = low value  = 'Revision' field in MIDR
12562306a36Sopenharmony_ci	 *
12662306a36Sopenharmony_ci	 */
12762306a36Sopenharmony_ci	if (id_variant > map_id_variant)
12862306a36Sopenharmony_ci		return 0;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (id_variant == map_id_variant && id_revision >= map_id_revision)
13162306a36Sopenharmony_ci		return 0;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/*
13462306a36Sopenharmony_ci	 * variant is less than mapfile variant or variants are the same but
13562306a36Sopenharmony_ci	 * the revision doesn't match. Return no match.
13662306a36Sopenharmony_ci	 */
13762306a36Sopenharmony_ci	return 1;
13862306a36Sopenharmony_ci}
139