1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/types.h> 3 #include <math.h> 4 #include <string.h> 5 6 #include "../../../util/debug.h" 7 #include "../../../util/tsc.h" 8 #include "cpuid.h" 9 10 u64 rdtsc(void) 11 { 12 unsigned int low, high; 13 14 asm volatile("rdtsc" : "=a" (low), "=d" (high)); 15 16 return low | ((u64)high) << 32; 17 } 18 19 /* 20 * Derive the TSC frequency in Hz from the /proc/cpuinfo, for example: 21 * ... 22 * model name : Intel(R) Xeon(R) Gold 6154 CPU @ 3.00GHz 23 * ... 24 * will return 3000000000. 25 */ 26 static double cpuinfo_tsc_freq(void) 27 { 28 double result = 0; 29 FILE *cpuinfo; 30 char *line = NULL; 31 size_t len = 0; 32 33 cpuinfo = fopen("/proc/cpuinfo", "r"); 34 if (!cpuinfo) { 35 pr_err("Failed to read /proc/cpuinfo for TSC frequency"); 36 return NAN; 37 } 38 while (getline(&line, &len, cpuinfo) > 0) { 39 if (!strncmp(line, "model name", 10)) { 40 char *pos = strstr(line + 11, " @ "); 41 42 if (pos && sscanf(pos, " @ %lfGHz", &result) == 1) { 43 result *= 1000000000; 44 goto out; 45 } 46 } 47 } 48 out: 49 if (fpclassify(result) == FP_ZERO) 50 pr_err("Failed to find TSC frequency in /proc/cpuinfo"); 51 52 free(line); 53 fclose(cpuinfo); 54 return result; 55 } 56 57 double arch_get_tsc_freq(void) 58 { 59 unsigned int a, b, c, d, lvl; 60 static bool cached; 61 static double tsc; 62 char vendor[16]; 63 64 if (cached) 65 return tsc; 66 67 cached = true; 68 get_cpuid_0(vendor, &lvl); 69 if (!strstr(vendor, "Intel")) 70 return 0; 71 72 /* 73 * Don't support Time Stamp Counter and 74 * Nominal Core Crystal Clock Information Leaf. 75 */ 76 if (lvl < 0x15) { 77 tsc = cpuinfo_tsc_freq(); 78 return tsc; 79 } 80 81 cpuid(0x15, 0, &a, &b, &c, &d); 82 /* TSC frequency is not enumerated */ 83 if (!a || !b || !c) { 84 tsc = cpuinfo_tsc_freq(); 85 return tsc; 86 } 87 88 tsc = (double)c * (double)b / (double)a; 89 return tsc; 90 } 91