| #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 cpu) | |
| { | |
| char path[PATH_MAX]; | |
| FILE *file; | |
| const char *sysfs = sysfs__mountpoint(); | |
| assert(cpu.cpu != -1); | |
| if (!sysfs || sz < MIDR_SIZE) | |
| return EINVAL; | |
| 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); | |
| return EINVAL; | |
| } | |
| if (!fgets(buf, MIDR_SIZE, file)) { | |
| pr_debug("Failed to read file %s\n", path); | |
| fclose(file); | |
| return EINVAL; | |
| } | |
| fclose(file); | |
| return 0; | |
| } | |
| int get_cpuid(char *buf, size_t sz, struct perf_cpu cpu) | |
| { | |
| struct perf_cpu_map *cpus; | |
| unsigned int idx; | |
| if (cpu.cpu != -1) | |
| return _get_cpuid(buf, sz, cpu); | |
| cpus = perf_cpu_map__new_online_cpus(); | |
| if (!cpus) | |
| return EINVAL; | |
| perf_cpu_map__for_each_cpu(cpu, idx, cpus) { | |
| int ret = _get_cpuid(buf, sz, cpu); | |
| if (ret == 0) | |
| return 0; | |
| } | |
| return EINVAL; | |
| } | |
| char *get_cpuid_str(struct perf_cpu cpu) | |
| { | |
| char *buf = malloc(MIDR_SIZE); | |
| int res; | |
| if (!buf) | |
| return NULL; | |
| /* read midr from list of cpus mapped to this pmu */ | |
| res = get_cpuid(buf, MIDR_SIZE, cpu); | |
| if (res) { | |
| pr_err("failed to get cpuid string for CPU %d\n", cpu.cpu); | |
| 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; | |
| } |