139 lines
3.1 KiB
C
139 lines
3.1 KiB
C
#include <linux/kernel.h>
|
|
#include <linux/bits.h>
|
|
#include <linux/bitfield.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <perf/cpumap.h>
|
|
#include <util/cpumap.h>
|
|
#include <internal/cpumap.h>
|
|
#include <api/fs/fs.h>
|
|
#include <errno.h>
|
|
#include "debug.h"
|
|
#include "header.h"
|
|
|
|
#define MIDR "/regs/identification/midr_el1"
|
|
#define MIDR_SIZE 19
|
|
#define MIDR_REVISION_MASK GENMASK(3, 0)
|
|
#define MIDR_VARIANT_MASK GENMASK(23, 20)
|
|
|
|
static int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus)
|
|
{
|
|
const char *sysfs = sysfs__mountpoint();
|
|
int cpu;
|
|
int ret = EINVAL;
|
|
|
|
if (!sysfs || sz < MIDR_SIZE)
|
|
return EINVAL;
|
|
|
|
cpus = perf_cpu_map__get(cpus);
|
|
|
|
for (cpu = 0; cpu < perf_cpu_map__nr(cpus); cpu++) {
|
|
char path[PATH_MAX];
|
|
FILE *file;
|
|
|
|
scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR,
|
|
sysfs, RC_CHK_ACCESS(cpus)->map[cpu].cpu);
|
|
|
|
file = fopen(path, "r");
|
|
if (!file) {
|
|
pr_debug("fopen failed for file %s\n", path);
|
|
continue;
|
|
}
|
|
|
|
if (!fgets(buf, MIDR_SIZE, file)) {
|
|
fclose(file);
|
|
continue;
|
|
}
|
|
fclose(file);
|
|
|
|
/* got midr break loop */
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
perf_cpu_map__put(cpus);
|
|
return ret;
|
|
}
|
|
|
|
int get_cpuid(char *buf, size_t sz)
|
|
{
|
|
struct perf_cpu_map *cpus = perf_cpu_map__new_online_cpus();
|
|
int ret;
|
|
|
|
if (!cpus)
|
|
return EINVAL;
|
|
|
|
ret = _get_cpuid(buf, sz, cpus);
|
|
|
|
perf_cpu_map__put(cpus);
|
|
|
|
return ret;
|
|
}
|
|
|
|
char *get_cpuid_str(struct perf_pmu *pmu)
|
|
{
|
|
char *buf = NULL;
|
|
int res;
|
|
|
|
if (!pmu || !pmu->cpus)
|
|
return NULL;
|
|
|
|
buf = malloc(MIDR_SIZE);
|
|
if (!buf)
|
|
return NULL;
|
|
|
|
/* read midr from list of cpus mapped to this pmu */
|
|
res = _get_cpuid(buf, MIDR_SIZE, pmu->cpus);
|
|
if (res) {
|
|
pr_err("failed to get cpuid string for PMU %s\n", pmu->name);
|
|
free(buf);
|
|
buf = NULL;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* Return 0 if idstr is a higher or equal to version of the same part as
|
|
* mapcpuid. Therefore, if mapcpuid has 0 for revision and variant then any
|
|
* version of idstr will match as long as it's the same CPU type.
|
|
*
|
|
* Return 1 if the CPU type is different or the version of idstr is lower.
|
|
*/
|
|
int strcmp_cpuid_str(const char *mapcpuid, const char *idstr)
|
|
{
|
|
u64 map_id = strtoull(mapcpuid, NULL, 16);
|
|
char map_id_variant = FIELD_GET(MIDR_VARIANT_MASK, map_id);
|
|
char map_id_revision = FIELD_GET(MIDR_REVISION_MASK, map_id);
|
|
u64 id = strtoull(idstr, NULL, 16);
|
|
char id_variant = FIELD_GET(MIDR_VARIANT_MASK, id);
|
|
char id_revision = FIELD_GET(MIDR_REVISION_MASK, id);
|
|
u64 id_fields = ~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK);
|
|
|
|
/* Compare without version first */
|
|
if ((map_id & id_fields) != (id & id_fields))
|
|
return 1;
|
|
|
|
/*
|
|
* ID matches, now compare version.
|
|
*
|
|
* Arm revisions (like r0p0) are compared here like two digit semver
|
|
* values eg. 1.3 < 2.0 < 2.1 < 2.2.
|
|
*
|
|
* r = high value = 'Variant' field in MIDR
|
|
* p = low value = 'Revision' field in MIDR
|
|
*
|
|
*/
|
|
if (id_variant > map_id_variant)
|
|
return 0;
|
|
|
|
if (id_variant == map_id_variant && id_revision >= map_id_revision)
|
|
return 0;
|
|
|
|
/*
|
|
* variant is less than mapfile variant or variants are the same but
|
|
* the revision doesn't match. Return no match.
|
|
*/
|
|
return 1;
|
|
}
|