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