1 #include <linux/bits.h> 2 #include <linux/bitfield.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <perf/cpumap.h> 6 #include <api/fs/fs.h> 7 #include <errno.h> 8 #include "debug.h" 9 #include "header.h" 10 11 #define MIDR "/regs/identification/midr_el1" 12 #define MIDR_SIZE 19 13 #define MIDR_REVISION_MASK GENMASK(3, 0) 14 #define MIDR_VARIANT_MASK GENMASK(23, 20) 15 16 static int _get_cpuid(char *buf, size_t sz, struct perf_cpu cpu) 17 { 18 char path[PATH_MAX]; 19 FILE *file; 20 const char *sysfs = sysfs__mountpoint(); 21 22 assert(cpu.cpu != -1); 23 if (!sysfs || sz < MIDR_SIZE) 24 return EINVAL; 25 26 scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR, sysfs, cpu.cpu); 27 28 file = fopen(path, "r"); 29 if (!file) { 30 pr_debug("fopen failed for file %s\n", path); 31 return EINVAL; 32 } 33 34 if (!fgets(buf, MIDR_SIZE, file)) { 35 pr_debug("Failed to read file %s\n", path); 36 fclose(file); 37 return EINVAL; 38 } 39 fclose(file); 40 return 0; 41 } 42 43 int get_cpuid(char *buf, size_t sz, struct perf_cpu cpu) 44 { 45 struct perf_cpu_map *cpus; 46 int idx; 47 48 if (cpu.cpu != -1) 49 return _get_cpuid(buf, sz, cpu); 50 51 cpus = perf_cpu_map__new_online_cpus(); 52 if (!cpus) 53 return EINVAL; 54 55 perf_cpu_map__for_each_cpu(cpu, idx, cpus) { 56 int ret = _get_cpuid(buf, sz, cpu); 57 58 if (ret == 0) 59 return 0; 60 } 61 return EINVAL; 62 } 63 64 char *get_cpuid_str(struct perf_cpu cpu) 65 { 66 char *buf = malloc(MIDR_SIZE); 67 int res; 68 69 if (!buf) 70 return NULL; 71 72 /* read midr from list of cpus mapped to this pmu */ 73 res = get_cpuid(buf, MIDR_SIZE, cpu); 74 if (res) { 75 pr_err("failed to get cpuid string for CPU %d\n", cpu.cpu); 76 free(buf); 77 buf = NULL; 78 } 79 80 return buf; 81 } 82 83 /* 84 * Return 0 if idstr is a higher or equal to version of the same part as 85 * mapcpuid. Therefore, if mapcpuid has 0 for revision and variant then any 86 * version of idstr will match as long as it's the same CPU type. 87 * 88 * Return 1 if the CPU type is different or the version of idstr is lower. 89 */ 90 int strcmp_cpuid_str(const char *mapcpuid, const char *idstr) 91 { 92 u64 map_id = strtoull(mapcpuid, NULL, 16); 93 char map_id_variant = FIELD_GET(MIDR_VARIANT_MASK, map_id); 94 char map_id_revision = FIELD_GET(MIDR_REVISION_MASK, map_id); 95 u64 id = strtoull(idstr, NULL, 16); 96 char id_variant = FIELD_GET(MIDR_VARIANT_MASK, id); 97 char id_revision = FIELD_GET(MIDR_REVISION_MASK, id); 98 u64 id_fields = ~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK); 99 100 /* Compare without version first */ 101 if ((map_id & id_fields) != (id & id_fields)) 102 return 1; 103 104 /* 105 * ID matches, now compare version. 106 * 107 * Arm revisions (like r0p0) are compared here like two digit semver 108 * values eg. 1.3 < 2.0 < 2.1 < 2.2. 109 * 110 * r = high value = 'Variant' field in MIDR 111 * p = low value = 'Revision' field in MIDR 112 * 113 */ 114 if (id_variant > map_id_variant) 115 return 0; 116 117 if (id_variant == map_id_variant && id_revision >= map_id_revision) 118 return 0; 119 120 /* 121 * variant is less than mapfile variant or variants are the same but 122 * the revision doesn't match. Return no match. 123 */ 124 return 1; 125 } 126