134 lines · c
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 1914#define MIDR_REVISION_MASK GENMASK(3, 0)15#define MIDR_VARIANT_MASK GENMASK(23, 20)16 17static 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 53int 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 68char *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 as93 * mapcpuid. Therefore, if mapcpuid has 0 for revision and variant then any94 * 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 */98int 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 semver116 * values eg. 1.3 < 2.0 < 2.1 < 2.2.117 *118 * r = high value = 'Variant' field in MIDR119 * p = low value = 'Revision' field in MIDR120 *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 but130 * the revision doesn't match. Return no match.131 */132 return 1;133}134