1 /* Copyright (C) 2006 Andi Kleen, SuSE Labs.
2 Decode TSC value into human readable uptime
3
4 mcelog is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public
6 License as published by the Free Software Foundation; version
7 2.
8
9 dmi is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should find a copy of v2 of the GNU General Public License somewhere
15 on your Linux system; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17 #define _GNU_SOURCE 1
18 #ifdef __FreeBSD__
19 #include <sys/types.h>
20 #include <sys/sysctl.h>
21 #include <machine/cpufunc.h>
22 #include <machine/specialreg.h>
23 #endif
24 #include <string.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <time.h>
29 #include "memutil.h"
30 #include "mcelog.h"
31 #include "tsc.h"
32 #include "intel.h"
33
scale(u64 * tsc,unsigned unit,double mhz)34 static unsigned scale(u64 *tsc, unsigned unit, double mhz)
35 {
36 u64 v = (u64)(mhz * 1000000) * unit;
37 unsigned u = *tsc / v;
38 *tsc -= u * v;
39 return u;
40 }
41
fmt_tsc(char ** buf,u64 tsc,double mhz)42 static int fmt_tsc(char **buf, u64 tsc, double mhz)
43 {
44 unsigned days, hours, mins, secs;
45 if (mhz == 0.0)
46 return -1;
47 days = scale(&tsc, 3600 * 24, mhz);
48 hours = scale(&tsc, 3600, mhz);
49 mins = scale(&tsc, 60, mhz);
50 secs = scale(&tsc, 1, mhz);
51 xasprintf(buf, "[at %.0f Mhz %u days %u:%u:%u uptime (unreliable)]",
52 mhz, days, hours, mins, secs);
53 return 0;
54 }
55
56 #ifdef __Linux__
cpufreq_mhz(int cpu,double infomhz)57 static double cpufreq_mhz(int cpu, double infomhz)
58 {
59 double mhz;
60 FILE *f;
61 char *fn;
62 xasprintf(&fn, "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu);
63 f = fopen(fn, "r");
64 free(fn);
65 if (!f) {
66 /* /sys exists, but no cpufreq -- use value from cpuinfo */
67 if (access("/sys/devices", F_OK) == 0)
68 return infomhz;
69 /* /sys not mounted. We don't know if cpufreq is active
70 or not, so must fallback */
71 return 0.0;
72 }
73 if (fscanf(f, "%lf", &mhz) != 1)
74 mhz = 0.0;
75 mhz /= 1000;
76 fclose(f);
77 return mhz;
78 }
79 #endif
80
81 #ifdef __FreeBSD__
cpufreq_mhz(int cpu,double infomhz)82 static double cpufreq_mhz(int cpu, double infomhz)
83 {
84 double mhz;
85 uint64_t freq;
86 size_t len;
87
88 len = sizeof(freq);
89 if (sysctlbyname("machdep.tsc_freq", &freq, &len, NULL, 0) < 0)
90 return infomhz;
91 mhz = freq / 1000000.0;
92 return mhz;
93 }
94 #endif
95
decode_tsc_forced(char ** buf,double mhz,u64 tsc)96 int decode_tsc_forced(char **buf, double mhz, u64 tsc)
97 {
98 return fmt_tsc(buf, tsc, mhz);
99 }
100
101 #ifdef __Linux__
deep_sleep_states(int cpu)102 static int deep_sleep_states(int cpu)
103 {
104 int ret;
105 char *fn;
106 FILE *f;
107 char *line = NULL;
108 size_t linelen = 0;
109
110 /* When cpuidle is active assume there are deep sleep states */
111 xasprintf(&fn, "/sys/devices/system/cpu/cpu%d/cpuidle", cpu);
112 ret = access(fn, X_OK);
113 free(fn);
114 if (ret == 0)
115 return 1;
116
117 xasprintf(&fn, "/proc/acpi/processor/CPU%d/power", cpu);
118 f = fopen(fn, "r");
119 free(fn);
120 if (!f)
121 return 0;
122
123 while ((getline(&line, &linelen, f)) > 0) {
124 int n;
125 if ((sscanf(line, " C%d:", &n)) == 1) {
126 if (n > 1) {
127 char *p = strstr(line, "usage");
128 if (p && sscanf(p, "usage[%d]", &n) == 1 && n > 0)
129 return 1;
130 }
131 }
132 }
133 free(line);
134 fclose(f);
135 return 0;
136 }
137
138 /* Try to figure out if this CPU has a somewhat reliable TSC clock */
tsc_reliable(int cputype,int cpunum)139 static int tsc_reliable(int cputype, int cpunum)
140 {
141 if (!processor_flags)
142 return 0;
143 /* Trust the kernel */
144 if (strstr(processor_flags, "nonstop_tsc"))
145 return 1;
146 /* TSC does not change frequency TBD: really old kernels don't set that */
147 if (!strstr(processor_flags, "constant_tsc"))
148 return 0;
149 /* We don't know the frequency on non Intel CPUs because the
150 kernel doesn't report them (e.g. AMD GH TSC doesn't run at highest
151 P-state). But then the kernel can just report the real time too.
152 Also a lot of AMD and VIA CPUs have unreliable TSC, so would
153 need special rules here too. */
154 if (!is_intel_cpu(cputype))
155 return 0;
156 if (deep_sleep_states(cpunum) && cputype < CPU_NEHALEM)
157 return 0;
158 return 1;
159 }
160 #endif
161
162 #ifdef __FreeBSD__
163 /* Try to figure out if this CPU has a somewhat reliable TSC clock */
tsc_reliable(int cputype,int cpunum)164 static int tsc_reliable(int cputype, int cpunum)
165 {
166 u_int regs[4];
167 u_int cpu_id, amd_pminfo;
168
169 if (cputype != CPU_K8 && !is_intel_cpu(cputype))
170 return 0;
171
172 do_cpuid(0, regs);
173 cpu_id = regs[1];
174 do_cpuid(0x80000000, regs);
175 if (regs[0] >= 0x80000007) {
176 do_cpuid(0x80000007, regs);
177 amd_pminfo = regs[3];
178 } else
179 amd_pminfo = 0;
180
181 if (amd_pminfo & AMDPM_TSC_INVARIANT)
182 return 1;
183 if (is_intel_cpu(cputype)) {
184 if (CPUID_TO_FAMILY(cpu_id) >= 0x10 ||
185 cpu_id == 0x60fb2)
186 return 1;
187 } else if ((CPUID_TO_FAMILY(cpu_id) == 0x6 &&
188 CPUID_TO_MODEL(cpu_id) >= 0xe) ||
189 (CPUID_TO_FAMILY(cpu_id) == 0xf && CPUID_TO_MODEL(cpu_id) >= 0x3))
190 return 1;
191
192 return 0;
193 }
194 #endif
195
decode_tsc_current(char ** buf,int cpunum,enum cputype cputype,double mhz,unsigned long long tsc)196 int decode_tsc_current(char **buf, int cpunum, enum cputype cputype, double mhz,
197 unsigned long long tsc)
198 {
199 double cmhz;
200 if (!tsc_reliable(cputype, cpunum))
201 return -1;
202 cmhz = cpufreq_mhz(cpunum, mhz);
203 if (cmhz != 0.0)
204 mhz = cmhz;
205 return fmt_tsc(buf, tsc, mhz);
206 }
207
208 #ifdef STANDALONE
is_intel_cpu(int cpu)209 int is_intel_cpu(int cpu) { return 1; }
210 /* claim this TSC is reliable always */
211 char *processor_flags = "nonstop_tsc";
212
rdtscll(void)213 static inline u64 rdtscll(void)
214 {
215 unsigned a,b;
216 asm volatile("rdtsc" : "=a" (a), "=d" (b));
217 return (u64)a | (((u64)b) << 32);
218 }
219
main(void)220 int main(void)
221 {
222 char *buf;
223 u64 tsc = rdtscll();
224 printf("%llx tsc\n", tsc);
225 if (decode_tsc_current(&buf, 0, CPU_CORE2, 0.0, tsc) >= 0)
226 printf("%s\n", buf);
227 else
228 printf("failed\n");
229 return 0;
230 }
231 #endif
232