| 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: &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(op: 0x15, op2: 0, a: &a, b: &b, c: &c, d: &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 | |