xref: /linux/tools/perf/arch/arm64/util/header.c (revision 4b660dbd9ee2059850fd30e0df420ca7a38a1856)
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(path, PATH_MAX, "%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, 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