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 19 static 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 58 int 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 73 char *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 */ 103 int 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