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(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
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, 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