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