xref: /haiku/src/system/boot/platform/bios_ia32/cpu.cpp (revision 2b97d9a98c890406a66acefd0279c7a4784079fa)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  *
6  * calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and
7  * licensed under the NewOS license.
8  */
9 
10 
11 #include "cpu.h"
12 
13 #include <OS.h>
14 #include <boot/platform.h>
15 #include <boot/stdio.h>
16 #include <boot/kernel_args.h>
17 #include <boot/stage2.h>
18 #include <arch/cpu.h>
19 #include <arch_kernel.h>
20 #include <arch_system_info.h>
21 
22 #include <string.h>
23 
24 
25 //#define TRACE_CPU
26 #ifdef TRACE_CPU
27 #	define TRACE(x) dprintf x
28 #else
29 #	define TRACE(x) ;
30 #endif
31 
32 
33 extern "C" uint64 rdtsc();
34 
35 uint32 gTimeConversionFactor;
36 
37 #define TIMER_CLKNUM_HZ (14318180/12)
38 
39 #define CPUID_EFLAGS	(1UL << 21)
40 #define RDTSC_FEATURE	(1UL << 4)
41 
42 
43 struct uint128 {
44 	uint128(uint64 low, uint64 high = 0)
45 		:
46 		low(low),
47 		high(high)
48 	{
49 	}
50 
51 	bool operator<(const uint128& other) const
52 	{
53 		return high < other.high || (high == other.high && low < other.low);
54 	}
55 
56 	bool operator<=(const uint128& other) const
57 	{
58 		return !(other < *this);
59 	}
60 
61 	uint128 operator<<(int count) const
62 	{
63 		if (count == 0)
64 			return *this;
65 
66 		if (count >= 128)
67 			return 0;
68 
69 		if (count >= 64)
70 			return uint128(0, low << (count - 64));
71 
72 		return uint128(low << count, (high << count) | (low >> (64 - count)));
73 	}
74 
75 	uint128 operator>>(int count) const
76 	{
77 		if (count == 0)
78 			return *this;
79 
80 		if (count >= 128)
81 			return 0;
82 
83 		if (count >= 64)
84 			return uint128(high >> (count - 64), 0);
85 
86 		return uint128((low >> count) | (high << (64 - count)), high >> count);
87 	}
88 
89 	uint128 operator+(const uint128& other) const
90 	{
91 		uint64 resultLow = low + other.low;
92 		return uint128(resultLow,
93 			high + other.high + (resultLow < low ? 1 : 0));
94 	}
95 
96 	uint128 operator-(const uint128& other) const
97 	{
98 		uint64 resultLow = low - other.low;
99 		return uint128(resultLow,
100 			high - other.high - (resultLow > low ? 1 : 0));
101 	}
102 
103 	uint128 operator*(uint32 other) const
104 	{
105 		uint64 resultMid = (low >> 32) * other;
106 		uint64 resultLow = (low & 0xffffffff) * other + (resultMid << 32);
107 		return uint128(resultLow,
108 			high * other + (resultMid >> 32)
109 				+ (resultLow < resultMid << 32 ? 1 : 0));
110 	}
111 
112 	uint128 operator/(const uint128& other) const
113 	{
114 		int shift = 0;
115 		uint128 shiftedDivider = other;
116 		while (shiftedDivider.high >> 63 == 0 && shiftedDivider < *this) {
117 			shiftedDivider = shiftedDivider << 1;
118 			shift++;
119 		}
120 
121 		uint128 result = 0;
122 		uint128 temp = *this;
123 		for (; shift >= 0; shift--, shiftedDivider = shiftedDivider >> 1) {
124 			if (shiftedDivider <= temp) {
125 				result = result + (uint128(1) << shift);
126 				temp = temp - shiftedDivider;
127 			}
128 		}
129 
130 		return result;
131 	}
132 
133 	operator uint64() const
134 	{
135 		return low;
136 	}
137 
138 private:
139 	uint64	low;
140 	uint64	high;
141 };
142 
143 
144 static void
145 calculate_cpu_conversion_factor()
146 {
147 	uint32 s_low, s_high;
148 	uint32 low, high;
149 	uint32 expired;
150 	uint64 t1, t2;
151 	uint64 p1, p2, p3;
152 	double r1, r2, r3;
153 
154 	out8(0x34, 0x43);	/* program the timer to count down mode */
155 	out8(0xff, 0x40);	/* low and then high */
156 	out8(0xff, 0x40);
157 
158 	/* quick sample */
159 quick_sample:
160 	do {
161 		out8(0x00, 0x43); /* latch counter value */
162 		s_low = in8(0x40);
163 		s_high = in8(0x40);
164 	} while (s_high != 255);
165 	t1 = rdtsc();
166 	do {
167 		out8(0x00, 0x43); /* latch counter value */
168 		low = in8(0x40);
169 		high = in8(0x40);
170 	} while (high > 224);
171 	t2 = rdtsc();
172 
173 	p1 = t2-t1;
174 	r1 = (double)(p1) / (double)(((s_high << 8) | s_low) - ((high << 8) | low));
175 
176 	/* not so quick sample */
177 not_so_quick_sample:
178 	do {
179 		out8(0x00, 0x43); /* latch counter value */
180 		s_low = in8(0x40);
181 		s_high = in8(0x40);
182 	} while (s_high != 255);
183 	t1 = rdtsc();
184 	do {
185 		out8(0x00, 0x43); /* latch counter value */
186 		low = in8(0x40);
187 		high = in8(0x40);
188 	} while (high > 192);
189 	t2 = rdtsc();
190 	p2 = t2-t1;
191 	r2 = (double)(p2) / (double)(((s_high << 8) | s_low) - ((high << 8) | low));
192 	if ((r1/r2) > 1.01) {
193 		//dprintf("Tuning loop(1)\n");
194 		goto quick_sample;
195 	}
196 	if ((r1/r2) < 0.99) {
197 		//dprintf("Tuning loop(1)\n");
198 		goto quick_sample;
199 	}
200 
201 	/* slow sample */
202 	do {
203 		out8(0x00, 0x43); /* latch counter value */
204 		s_low = in8(0x40);
205 		s_high = in8(0x40);
206 	} while (s_high != 255);
207 	t1 = rdtsc();
208 	do {
209 		out8(0x00, 0x43); /* latch counter value */
210 		low = in8(0x40);
211 		high = in8(0x40);
212 	} while (high > 128);
213 	t2 = rdtsc();
214 
215 	p3 = t2-t1;
216 	r3 = (double)(p3) / (double)(((s_high << 8) | s_low) - ((high << 8) | low));
217 	if ((r2/r3) > 1.01) {
218 		TRACE(("Tuning loop(2)\n"));
219 		goto not_so_quick_sample;
220 	}
221 	if ((r2/r3) < 0.99) {
222 		TRACE(("Tuning loop(2)\n"));
223 		goto not_so_quick_sample;
224 	}
225 
226 	expired = ((s_high << 8) | s_low) - ((high << 8) | low);
227 	p3 *= TIMER_CLKNUM_HZ;
228 
229 	gTimeConversionFactor = ((uint128(expired) * uint32(1000000)) << 32)
230 		/ uint128(p3);
231 
232 #ifdef TRACE_CPU
233 	if (p3 / expired / 1000000000LL)
234 		dprintf("CPU at %Ld.%03Ld GHz\n", p3/expired/1000000000LL, ((p3/expired)%1000000000LL)/1000000LL);
235 	else
236 		dprintf("CPU at %Ld.%03Ld MHz\n", p3/expired/1000000LL, ((p3/expired)%1000000LL)/1000LL);
237 #endif
238 
239 	gKernelArgs.arch_args.system_time_cv_factor = gTimeConversionFactor;
240 	gKernelArgs.arch_args.cpu_clock_speed = p3 / expired;
241 }
242 
243 
244 static status_t
245 check_cpu_features()
246 {
247 	// check the eflags register to see if the cpuid instruction exists
248 	if ((get_eflags() & CPUID_EFLAGS) == 0) {
249 		// it's not set yet, but maybe we can set it manually
250 		set_eflags(get_eflags() | CPUID_EFLAGS);
251 		if ((get_eflags() & CPUID_EFLAGS) == 0)
252 			return B_ERROR;
253 	}
254 
255 	cpuid_info info;
256 	if (get_current_cpuid(&info, 1) != B_OK)
257 		return B_ERROR;
258 
259 	if ((info.eax_1.features & RDTSC_FEATURE) == 0) {
260 		// we currently require RDTSC
261 		return B_ERROR;
262 	}
263 
264 	return B_OK;
265 }
266 
267 
268 //	#pragma mark -
269 
270 
271 extern "C" void
272 spin(bigtime_t microseconds)
273 {
274 	bigtime_t time = system_time();
275 
276 	while ((system_time() - time) < microseconds)
277 		asm volatile ("pause;");
278 }
279 
280 
281 extern "C" void
282 cpu_init()
283 {
284 	if (check_cpu_features() != B_OK)
285 		panic("You need a Pentium or higher in order to boot!\n");
286 
287 	calculate_cpu_conversion_factor();
288 
289 	gKernelArgs.num_cpus = 1;
290 		// this will eventually be corrected later on
291 }
292 
293