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