| #include <linux/kernel.h> |
| #include <linux/bits.h> |
| #include <linux/bitfield.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <perf/cpumap.h> |
| #include <api/fs/fs.h> |
| #include <errno.h> |
| #include "debug.h" |
| #include "header.h" |
| |
| #define MIDR "/regs/identification/midr_el1" |
| #define MIDR_SIZE 19 |
| #define MIDR_REVISION_MASK GENMASK(3, 0) |
| #define MIDR_VARIANT_MASK GENMASK(23, 20) |
| |
| static int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus) |
| { |
| const char *sysfs = sysfs__mountpoint(); |
| struct perf_cpu cpu; |
| int idx, ret = EINVAL; |
| |
| if (!sysfs || sz < MIDR_SIZE) |
| return EINVAL; |
| |
| perf_cpu_map__for_each_cpu(cpu, idx, cpus) { |
| char path[PATH_MAX]; |
| FILE *file; |
| |
| scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR, |
| sysfs, cpu.cpu); |
| |
| file = fopen(path, "r"); |
| if (!file) { |
| pr_debug("fopen failed for file %s\n", path); |
| continue; |
| } |
| |
| if (!fgets(buf, MIDR_SIZE, file)) { |
| fclose(file); |
| continue; |
| } |
| fclose(file); |
| |
| /* got midr break loop */ |
| ret = 0; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| int get_cpuid(char *buf, size_t sz) |
| { |
| struct perf_cpu_map *cpus = perf_cpu_map__new_online_cpus(); |
| int ret; |
| |
| if (!cpus) |
| return EINVAL; |
| |
| ret = _get_cpuid(buf, sz, cpus); |
| |
| perf_cpu_map__put(cpus); |
| |
| return ret; |
| } |
| |
| char *get_cpuid_str(struct perf_pmu *pmu) |
| { |
| char *buf = NULL; |
| int res; |
| |
| if (!pmu || !pmu->cpus) |
| return NULL; |
| |
| buf = malloc(MIDR_SIZE); |
| if (!buf) |
| return NULL; |
| |
| /* read midr from list of cpus mapped to this pmu */ |
| res = _get_cpuid(buf, MIDR_SIZE, pmu->cpus); |
| if (res) { |
| pr_err("failed to get cpuid string for PMU %s\n", pmu->name); |
| free(buf); |
| buf = NULL; |
| } |
| |
| return buf; |
| } |
| |
| /* |
| * Return 0 if idstr is a higher or equal to version of the same part as |
| * mapcpuid. Therefore, if mapcpuid has 0 for revision and variant then any |
| * version of idstr will match as long as it's the same CPU type. |
| * |
| * Return 1 if the CPU type is different or the version of idstr is lower. |
| */ |
| int strcmp_cpuid_str(const char *mapcpuid, const char *idstr) |
| { |
| u64 map_id = strtoull(mapcpuid, NULL, 16); |
| char map_id_variant = FIELD_GET(MIDR_VARIANT_MASK, map_id); |
| char map_id_revision = FIELD_GET(MIDR_REVISION_MASK, map_id); |
| u64 id = strtoull(idstr, NULL, 16); |
| char id_variant = FIELD_GET(MIDR_VARIANT_MASK, id); |
| char id_revision = FIELD_GET(MIDR_REVISION_MASK, id); |
| u64 id_fields = ~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK); |
| |
| /* Compare without version first */ |
| if ((map_id & id_fields) != (id & id_fields)) |
| return 1; |
| |
| /* |
| * ID matches, now compare version. |
| * |
| * Arm revisions (like r0p0) are compared here like two digit semver |
| * values eg. 1.3 < 2.0 < 2.1 < 2.2. |
| * |
| * r = high value = 'Variant' field in MIDR |
| * p = low value = 'Revision' field in MIDR |
| * |
| */ |
| if (id_variant > map_id_variant) |
| return 0; |
| |
| if (id_variant == map_id_variant && id_revision >= map_id_revision) |
| return 0; |
| |
| /* |
| * variant is less than mapfile variant or variants are the same but |
| * the revision doesn't match. Return no match. |
| */ |
| return 1; |
| } |