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