162306a36Sopenharmony_ci#include <linux/kernel.h> 262306a36Sopenharmony_ci#include <linux/bits.h> 362306a36Sopenharmony_ci#include <linux/bitfield.h> 462306a36Sopenharmony_ci#include <stdio.h> 562306a36Sopenharmony_ci#include <stdlib.h> 662306a36Sopenharmony_ci#include <perf/cpumap.h> 762306a36Sopenharmony_ci#include <util/cpumap.h> 862306a36Sopenharmony_ci#include <internal/cpumap.h> 962306a36Sopenharmony_ci#include <api/fs/fs.h> 1062306a36Sopenharmony_ci#include <errno.h> 1162306a36Sopenharmony_ci#include "debug.h" 1262306a36Sopenharmony_ci#include "header.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define MIDR "/regs/identification/midr_el1" 1562306a36Sopenharmony_ci#define MIDR_SIZE 19 1662306a36Sopenharmony_ci#define MIDR_REVISION_MASK GENMASK(3, 0) 1762306a36Sopenharmony_ci#define MIDR_VARIANT_MASK GENMASK(23, 20) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci const char *sysfs = sysfs__mountpoint(); 2262306a36Sopenharmony_ci int cpu; 2362306a36Sopenharmony_ci int ret = EINVAL; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci if (!sysfs || sz < MIDR_SIZE) 2662306a36Sopenharmony_ci return EINVAL; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci cpus = perf_cpu_map__get(cpus); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci for (cpu = 0; cpu < perf_cpu_map__nr(cpus); cpu++) { 3162306a36Sopenharmony_ci char path[PATH_MAX]; 3262306a36Sopenharmony_ci FILE *file; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR, 3562306a36Sopenharmony_ci sysfs, RC_CHK_ACCESS(cpus)->map[cpu].cpu); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci file = fopen(path, "r"); 3862306a36Sopenharmony_ci if (!file) { 3962306a36Sopenharmony_ci pr_debug("fopen failed for file %s\n", path); 4062306a36Sopenharmony_ci continue; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (!fgets(buf, MIDR_SIZE, file)) { 4462306a36Sopenharmony_ci fclose(file); 4562306a36Sopenharmony_ci continue; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci fclose(file); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* got midr break loop */ 5062306a36Sopenharmony_ci ret = 0; 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci perf_cpu_map__put(cpus); 5562306a36Sopenharmony_ci return ret; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ciint get_cpuid(char *buf, size_t sz) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct perf_cpu_map *cpus = perf_cpu_map__new(NULL); 6162306a36Sopenharmony_ci int ret; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (!cpus) 6462306a36Sopenharmony_ci return EINVAL; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci ret = _get_cpuid(buf, sz, cpus); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci perf_cpu_map__put(cpus); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return ret; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cichar *get_cpuid_str(struct perf_pmu *pmu) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci char *buf = NULL; 7662306a36Sopenharmony_ci int res; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!pmu || !pmu->cpus) 7962306a36Sopenharmony_ci return NULL; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci buf = malloc(MIDR_SIZE); 8262306a36Sopenharmony_ci if (!buf) 8362306a36Sopenharmony_ci return NULL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* read midr from list of cpus mapped to this pmu */ 8662306a36Sopenharmony_ci res = _get_cpuid(buf, MIDR_SIZE, pmu->cpus); 8762306a36Sopenharmony_ci if (res) { 8862306a36Sopenharmony_ci pr_err("failed to get cpuid string for PMU %s\n", pmu->name); 8962306a36Sopenharmony_ci free(buf); 9062306a36Sopenharmony_ci buf = NULL; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return buf; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci * Return 0 if idstr is a higher or equal to version of the same part as 9862306a36Sopenharmony_ci * mapcpuid. Therefore, if mapcpuid has 0 for revision and variant then any 9962306a36Sopenharmony_ci * version of idstr will match as long as it's the same CPU type. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Return 1 if the CPU type is different or the version of idstr is lower. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ciint strcmp_cpuid_str(const char *mapcpuid, const char *idstr) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci u64 map_id = strtoull(mapcpuid, NULL, 16); 10662306a36Sopenharmony_ci char map_id_variant = FIELD_GET(MIDR_VARIANT_MASK, map_id); 10762306a36Sopenharmony_ci char map_id_revision = FIELD_GET(MIDR_REVISION_MASK, map_id); 10862306a36Sopenharmony_ci u64 id = strtoull(idstr, NULL, 16); 10962306a36Sopenharmony_ci char id_variant = FIELD_GET(MIDR_VARIANT_MASK, id); 11062306a36Sopenharmony_ci char id_revision = FIELD_GET(MIDR_REVISION_MASK, id); 11162306a36Sopenharmony_ci u64 id_fields = ~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Compare without version first */ 11462306a36Sopenharmony_ci if ((map_id & id_fields) != (id & id_fields)) 11562306a36Sopenharmony_ci return 1; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* 11862306a36Sopenharmony_ci * ID matches, now compare version. 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * Arm revisions (like r0p0) are compared here like two digit semver 12162306a36Sopenharmony_ci * values eg. 1.3 < 2.0 < 2.1 < 2.2. 12262306a36Sopenharmony_ci * 12362306a36Sopenharmony_ci * r = high value = 'Variant' field in MIDR 12462306a36Sopenharmony_ci * p = low value = 'Revision' field in MIDR 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci if (id_variant > map_id_variant) 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (id_variant == map_id_variant && id_revision >= map_id_revision) 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * variant is less than mapfile variant or variants are the same but 13562306a36Sopenharmony_ci * the revision doesn't match. Return no match. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci return 1; 13862306a36Sopenharmony_ci} 139