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