xref: /linux/tools/perf/arch/arm64/util/header.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
1c3e1e8cfSJames Clark #include <linux/kernel.h>
2c3e1e8cfSJames Clark #include <linux/bits.h>
3c3e1e8cfSJames Clark #include <linux/bitfield.h>
4b57df288SGanapatrao Kulkarni #include <stdio.h>
5b57df288SGanapatrao Kulkarni #include <stdlib.h>
687ffb6c6SArnaldo Carvalho de Melo #include <perf/cpumap.h>
7b57df288SGanapatrao Kulkarni #include <api/fs/fs.h>
8df5a5f3cSJohn Garry #include <errno.h>
945a2c0ccSArnaldo Carvalho de Melo #include "debug.h"
10b57df288SGanapatrao Kulkarni #include "header.h"
11b57df288SGanapatrao Kulkarni 
12b57df288SGanapatrao Kulkarni #define MIDR "/regs/identification/midr_el1"
13b57df288SGanapatrao Kulkarni #define MIDR_SIZE 19
14c3e1e8cfSJames Clark #define MIDR_REVISION_MASK      GENMASK(3, 0)
15c3e1e8cfSJames Clark #define MIDR_VARIANT_MASK	GENMASK(23, 20)
16b57df288SGanapatrao Kulkarni 
17df5a5f3cSJohn Garry static int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus)
18b57df288SGanapatrao Kulkarni {
19b57df288SGanapatrao Kulkarni 	const char *sysfs = sysfs__mountpoint();
20*71bc3ac8SIan Rogers 	struct perf_cpu cpu;
21*71bc3ac8SIan Rogers 	int idx, ret = EINVAL;
22df5a5f3cSJohn Garry 
23df5a5f3cSJohn Garry 	if (!sysfs || sz < MIDR_SIZE)
24df5a5f3cSJohn Garry 		return EINVAL;
25df5a5f3cSJohn Garry 
26*71bc3ac8SIan Rogers 	perf_cpu_map__for_each_cpu(cpu, idx, cpus) {
27df5a5f3cSJohn Garry 		char path[PATH_MAX];
28b57df288SGanapatrao Kulkarni 		FILE *file;
29b57df288SGanapatrao Kulkarni 
30b57df288SGanapatrao Kulkarni 		scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR,
31*71bc3ac8SIan Rogers 			  sysfs, cpu.cpu);
32b57df288SGanapatrao Kulkarni 
33b57df288SGanapatrao Kulkarni 		file = fopen(path, "r");
34b57df288SGanapatrao Kulkarni 		if (!file) {
35b57df288SGanapatrao Kulkarni 			pr_debug("fopen failed for file %s\n", path);
36b57df288SGanapatrao Kulkarni 			continue;
37b57df288SGanapatrao Kulkarni 		}
38b57df288SGanapatrao Kulkarni 
39b57df288SGanapatrao Kulkarni 		if (!fgets(buf, MIDR_SIZE, file)) {
40b57df288SGanapatrao Kulkarni 			fclose(file);
41b57df288SGanapatrao Kulkarni 			continue;
42b57df288SGanapatrao Kulkarni 		}
43b57df288SGanapatrao Kulkarni 		fclose(file);
44b57df288SGanapatrao Kulkarni 
45b57df288SGanapatrao Kulkarni 		/* got midr break loop */
46c3e1e8cfSJames Clark 		ret = 0;
47b57df288SGanapatrao Kulkarni 		break;
48b57df288SGanapatrao Kulkarni 	}
49b57df288SGanapatrao Kulkarni 
50c3e1e8cfSJames Clark 	return ret;
51df5a5f3cSJohn Garry }
52df5a5f3cSJohn Garry 
53df5a5f3cSJohn Garry int get_cpuid(char *buf, size_t sz)
54df5a5f3cSJohn Garry {
55effe957cSIan Rogers 	struct perf_cpu_map *cpus = perf_cpu_map__new_online_cpus();
56df5a5f3cSJohn Garry 	int ret;
57df5a5f3cSJohn Garry 
58df5a5f3cSJohn Garry 	if (!cpus)
59df5a5f3cSJohn Garry 		return EINVAL;
60df5a5f3cSJohn Garry 
61df5a5f3cSJohn Garry 	ret = _get_cpuid(buf, sz, cpus);
62df5a5f3cSJohn Garry 
63df5a5f3cSJohn Garry 	perf_cpu_map__put(cpus);
64df5a5f3cSJohn Garry 
65df5a5f3cSJohn Garry 	return ret;
66df5a5f3cSJohn Garry }
67df5a5f3cSJohn Garry 
68df5a5f3cSJohn Garry char *get_cpuid_str(struct perf_pmu *pmu)
69df5a5f3cSJohn Garry {
70df5a5f3cSJohn Garry 	char *buf = NULL;
71df5a5f3cSJohn Garry 	int res;
72df5a5f3cSJohn Garry 
73df5a5f3cSJohn Garry 	if (!pmu || !pmu->cpus)
74df5a5f3cSJohn Garry 		return NULL;
75df5a5f3cSJohn Garry 
76df5a5f3cSJohn Garry 	buf = malloc(MIDR_SIZE);
77df5a5f3cSJohn Garry 	if (!buf)
78df5a5f3cSJohn Garry 		return NULL;
79df5a5f3cSJohn Garry 
80df5a5f3cSJohn Garry 	/* read midr from list of cpus mapped to this pmu */
81df5a5f3cSJohn Garry 	res = _get_cpuid(buf, MIDR_SIZE, pmu->cpus);
82df5a5f3cSJohn Garry 	if (res) {
83b57df288SGanapatrao Kulkarni 		pr_err("failed to get cpuid string for PMU %s\n", pmu->name);
84b57df288SGanapatrao Kulkarni 		free(buf);
85b57df288SGanapatrao Kulkarni 		buf = NULL;
86b57df288SGanapatrao Kulkarni 	}
87b57df288SGanapatrao Kulkarni 
88b57df288SGanapatrao Kulkarni 	return buf;
89b57df288SGanapatrao Kulkarni }
90c3e1e8cfSJames Clark 
91c3e1e8cfSJames Clark /*
92c3e1e8cfSJames Clark  * Return 0 if idstr is a higher or equal to version of the same part as
93c3e1e8cfSJames Clark  * mapcpuid. Therefore, if mapcpuid has 0 for revision and variant then any
94c3e1e8cfSJames Clark  * version of idstr will match as long as it's the same CPU type.
95c3e1e8cfSJames Clark  *
96c3e1e8cfSJames Clark  * Return 1 if the CPU type is different or the version of idstr is lower.
97c3e1e8cfSJames Clark  */
98c3e1e8cfSJames Clark int strcmp_cpuid_str(const char *mapcpuid, const char *idstr)
99c3e1e8cfSJames Clark {
100c3e1e8cfSJames Clark 	u64 map_id = strtoull(mapcpuid, NULL, 16);
101c3e1e8cfSJames Clark 	char map_id_variant = FIELD_GET(MIDR_VARIANT_MASK, map_id);
102c3e1e8cfSJames Clark 	char map_id_revision = FIELD_GET(MIDR_REVISION_MASK, map_id);
103c3e1e8cfSJames Clark 	u64 id = strtoull(idstr, NULL, 16);
104c3e1e8cfSJames Clark 	char id_variant = FIELD_GET(MIDR_VARIANT_MASK, id);
105c3e1e8cfSJames Clark 	char id_revision = FIELD_GET(MIDR_REVISION_MASK, id);
106c3e1e8cfSJames Clark 	u64 id_fields = ~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK);
107c3e1e8cfSJames Clark 
108c3e1e8cfSJames Clark 	/* Compare without version first */
109c3e1e8cfSJames Clark 	if ((map_id & id_fields) != (id & id_fields))
110c3e1e8cfSJames Clark 		return 1;
111c3e1e8cfSJames Clark 
112c3e1e8cfSJames Clark 	/*
113c3e1e8cfSJames Clark 	 * ID matches, now compare version.
114c3e1e8cfSJames Clark 	 *
115c3e1e8cfSJames Clark 	 * Arm revisions (like r0p0) are compared here like two digit semver
116c3e1e8cfSJames Clark 	 * values eg. 1.3 < 2.0 < 2.1 < 2.2.
117c3e1e8cfSJames Clark 	 *
118c3e1e8cfSJames Clark 	 *  r = high value = 'Variant' field in MIDR
119c3e1e8cfSJames Clark 	 *  p = low value  = 'Revision' field in MIDR
120c3e1e8cfSJames Clark 	 *
121c3e1e8cfSJames Clark 	 */
122c3e1e8cfSJames Clark 	if (id_variant > map_id_variant)
123c3e1e8cfSJames Clark 		return 0;
124c3e1e8cfSJames Clark 
125c3e1e8cfSJames Clark 	if (id_variant == map_id_variant && id_revision >= map_id_revision)
126c3e1e8cfSJames Clark 		return 0;
127c3e1e8cfSJames Clark 
128c3e1e8cfSJames Clark 	/*
129c3e1e8cfSJames Clark 	 * variant is less than mapfile variant or variants are the same but
130c3e1e8cfSJames Clark 	 * the revision doesn't match. Return no match.
131c3e1e8cfSJames Clark 	 */
132c3e1e8cfSJames Clark 	return 1;
133c3e1e8cfSJames Clark }
134