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
19static 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
58int 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
73char *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 */
103int 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

source code of linux/tools/perf/arch/arm64/util/header.c