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