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