91 lines
1.7 KiB
C
91 lines
1.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/types.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include "../../../util/debug.h"
|
|
#include "../../../util/tsc.h"
|
|
#include "cpuid.h"
|
|
|
|
u64 rdtsc(void)
|
|
{
|
|
unsigned int low, high;
|
|
|
|
asm volatile("rdtsc" : "=a" (low), "=d" (high));
|
|
|
|
return low | ((u64)high) << 32;
|
|
}
|
|
|
|
/*
|
|
* Derive the TSC frequency in Hz from the /proc/cpuinfo, for example:
|
|
* ...
|
|
* model name : Intel(R) Xeon(R) Gold 6154 CPU @ 3.00GHz
|
|
* ...
|
|
* will return 3000000000.
|
|
*/
|
|
static double cpuinfo_tsc_freq(void)
|
|
{
|
|
double result = 0;
|
|
FILE *cpuinfo;
|
|
char *line = NULL;
|
|
size_t len = 0;
|
|
|
|
cpuinfo = fopen("/proc/cpuinfo", "r");
|
|
if (!cpuinfo) {
|
|
pr_err("Failed to read /proc/cpuinfo for TSC frequency");
|
|
return NAN;
|
|
}
|
|
while (getline(&line, &len, cpuinfo) > 0) {
|
|
if (!strncmp(line, "model name", 10)) {
|
|
char *pos = strstr(line + 11, " @ ");
|
|
|
|
if (pos && sscanf(pos, " @ %lfGHz", &result) == 1) {
|
|
result *= 1000000000;
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
out:
|
|
if (fpclassify(result) == FP_ZERO)
|
|
pr_err("Failed to find TSC frequency in /proc/cpuinfo");
|
|
|
|
free(line);
|
|
fclose(cpuinfo);
|
|
return result;
|
|
}
|
|
|
|
double arch_get_tsc_freq(void)
|
|
{
|
|
unsigned int a, b, c, d, lvl;
|
|
static bool cached;
|
|
static double tsc;
|
|
char vendor[16];
|
|
|
|
if (cached)
|
|
return tsc;
|
|
|
|
cached = true;
|
|
get_cpuid_0(vendor, &lvl);
|
|
if (!strstr(vendor, "Intel"))
|
|
return 0;
|
|
|
|
/*
|
|
* Don't support Time Stamp Counter and
|
|
* Nominal Core Crystal Clock Information Leaf.
|
|
*/
|
|
if (lvl < 0x15) {
|
|
tsc = cpuinfo_tsc_freq();
|
|
return tsc;
|
|
}
|
|
|
|
cpuid(0x15, 0, &a, &b, &c, &d);
|
|
/* TSC frequency is not enumerated */
|
|
if (!a || !b || !c) {
|
|
tsc = cpuinfo_tsc_freq();
|
|
return tsc;
|
|
}
|
|
|
|
tsc = (double)c * (double)b / (double)a;
|
|
return tsc;
|
|
}
|