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_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 53 int 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 68 char *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 as 93 * mapcpuid. Therefore, if mapcpuid has 0 for revision and variant then any 94 * 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 */ 98 int 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 semver 116 * values eg. 1.3 < 2.0 < 2.1 < 2.2. 117 * 118 * r = high value = 'Variant' field in MIDR 119 * p = low value = 'Revision' field in MIDR 120 * 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 but 130 * the revision doesn't match. Return no match. 131 */ 132 return 1; 133 } 134