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