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