xref: /haiku/src/system/boot/platform/efi/cpu.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 "efi_platform.h"
14 
15 #include <OS.h>
16 #include <boot/platform.h>
17 #include <boot/stdio.h>
18 #include <boot/kernel_args.h>
19 #include <boot/stage2.h>
20 #include <arch/cpu.h>
21 #include <arch_kernel.h>
22 #include <arch_system_info.h>
23 
24 #include <string.h>
25 
26 
27 //#define TRACE_CPU
28 #ifdef TRACE_CPU
29 #	define TRACE(x) dprintf x
30 #else
31 #	define TRACE(x) ;
32 #endif
33 
34 
35 extern "C" uint64 rdtsc();
36 
37 uint32 gTimeConversionFactor;
38 
39 // PIT definitions
40 #define TIMER_CLKNUM_HZ					(14318180 / 12)
41 
42 // PIT IO Ports
43 #define PIT_CHANNEL_PORT_BASE			0x40
44 #define PIT_CONTROL						0x43
45 
46 // Channel selection
47 #define PIT_SELECT_CHANNEL_SHIFT		6
48 
49 // Access mode
50 #define PIT_ACCESS_LATCH_COUNTER		(0 << 4)
51 #define PIT_ACCESS_LOW_BYTE_ONLY		(1 << 4)
52 #define PIT_ACCESS_HIGH_BYTE_ONLY		(2 << 4)
53 #define PIT_ACCESS_LOW_THEN_HIGH_BYTE	(3 << 4)
54 
55 // Operating modes
56 #define PIT_MODE_INTERRUPT_ON_0			(0 << 1)
57 #define PIT_MODE_HARDWARE_COUNTDOWN		(1 << 1)
58 #define PIT_MODE_RATE_GENERATOR			(2 << 1)
59 #define PIT_MODE_SQUARE_WAVE_GENERATOR	(3 << 1)
60 #define PIT_MODE_SOFTWARE_STROBE		(4 << 1)
61 #define PIT_MODE_HARDWARE_STROBE		(5 << 1)
62 
63 // BCD/Binary mode
64 #define PIT_BINARY_MODE					0
65 #define PIT_BCD_MODE					1
66 
67 // Channel 2 control (speaker)
68 #define PIT_CHANNEL_2_CONTROL			0x61
69 #define PIT_CHANNEL_2_GATE_HIGH			0x01
70 #define PIT_CHANNEL_2_SPEAKER_OFF_MASK	~0x02
71 
72 
73 // Maximum values
74 #define MAX_QUICK_SAMPLES				20
75 #define MAX_SLOW_SAMPLES				20
76 	// TODO: These are arbitrary. They are here to avoid spinning indefinitely
77 	// if the TSC just isn't stable and we can't get our desired error range.
78 
79 
80 #define CPUID_EFLAGS	(1UL << 21)
81 #define RDTSC_FEATURE	(1UL << 4)
82 
83 
84 struct uint128 {
85 	uint128(uint64 low, uint64 high = 0)
86 		:
87 		low(low),
88 		high(high)
89 	{
90 	}
91 
92 	bool operator<(const uint128& other) const
93 	{
94 		return high < other.high || (high == other.high && low < other.low);
95 	}
96 
97 	bool operator<=(const uint128& other) const
98 	{
99 		return !(other < *this);
100 	}
101 
102 	uint128 operator<<(int count) const
103 	{
104 		if (count == 0)
105 			return *this;
106 
107 		if (count >= 128)
108 			return 0;
109 
110 		if (count >= 64)
111 			return uint128(0, low << (count - 64));
112 
113 		return uint128(low << count, (high << count) | (low >> (64 - count)));
114 	}
115 
116 	uint128 operator>>(int count) const
117 	{
118 		if (count == 0)
119 			return *this;
120 
121 		if (count >= 128)
122 			return 0;
123 
124 		if (count >= 64)
125 			return uint128(high >> (count - 64), 0);
126 
127 		return uint128((low >> count) | (high << (64 - count)), high >> count);
128 	}
129 
130 	uint128 operator+(const uint128& other) const
131 	{
132 		uint64 resultLow = low + other.low;
133 		return uint128(resultLow,
134 			high + other.high + (resultLow < low ? 1 : 0));
135 	}
136 
137 	uint128 operator-(const uint128& other) const
138 	{
139 		uint64 resultLow = low - other.low;
140 		return uint128(resultLow,
141 			high - other.high - (resultLow > low ? 1 : 0));
142 	}
143 
144 	uint128 operator*(uint32 other) const
145 	{
146 		uint64 resultMid = (low >> 32) * other;
147 		uint64 resultLow = (low & 0xffffffff) * other + (resultMid << 32);
148 		return uint128(resultLow,
149 			high * other + (resultMid >> 32)
150 				+ (resultLow < resultMid << 32 ? 1 : 0));
151 	}
152 
153 	uint128 operator/(const uint128& other) const
154 	{
155 		int shift = 0;
156 		uint128 shiftedDivider = other;
157 		while (shiftedDivider.high >> 63 == 0 && shiftedDivider < *this) {
158 			shiftedDivider = shiftedDivider << 1;
159 			shift++;
160 		}
161 
162 		uint128 result = 0;
163 		uint128 temp = *this;
164 		for (; shift >= 0; shift--, shiftedDivider = shiftedDivider >> 1) {
165 			if (shiftedDivider <= temp) {
166 				result = result + (uint128(1) << shift);
167 				temp = temp - shiftedDivider;
168 			}
169 		}
170 
171 		return result;
172 	}
173 
174 	operator uint64() const
175 	{
176 		return low;
177 	}
178 
179 private:
180 	uint64	low;
181 	uint64	high;
182 };
183 
184 
185 static inline void
186 calibration_loop(uint8 desiredHighByte, uint8 channel, uint64& tscDelta,
187 	double& conversionFactor, uint16& expired)
188 {
189 	uint8 select = channel << PIT_SELECT_CHANNEL_SHIFT;
190 	out8(select | PIT_ACCESS_LOW_THEN_HIGH_BYTE | PIT_MODE_INTERRUPT_ON_0
191 		| PIT_BINARY_MODE, PIT_CONTROL);
192 
193 	// Fill in count of 0xffff, low then high byte
194 	uint8 channelPort = PIT_CHANNEL_PORT_BASE + channel;
195 	out8(0xff, channelPort);
196 	out8(0xff, channelPort);
197 
198 	// Read the count back once to delay the start. This ensures that we've
199 	// waited long enough for the counter to actually start counting down, as
200 	// this only happens on the next clock cycle after reload.
201 	in8(channelPort);
202 	in8(channelPort);
203 
204 	// We're expecting the PIT to be at the starting position (high byte 0xff)
205 	// as we just programmed it, but if it isn't we wait for it to wrap.
206 	uint8 startLow;
207 	uint8 startHigh;
208 	do {
209 		out8(select | PIT_ACCESS_LATCH_COUNTER, PIT_CONTROL);
210 		startLow = in8(channelPort);
211 		startHigh = in8(channelPort);
212 	} while (startHigh != 255);
213 
214 	// Read in the first TSC value
215 	uint64 startTSC = rdtsc();
216 
217 	// Wait for the PIT to count down to our desired value
218 	uint8 endLow;
219 	uint8 endHigh;
220 	do {
221 		out8(select | PIT_ACCESS_LATCH_COUNTER, PIT_CONTROL);
222 		endLow = in8(channelPort);
223 		endHigh = in8(channelPort);
224 	} while (endHigh > desiredHighByte);
225 
226 	// And read the second TSC value
227 	uint64 endTSC = rdtsc();
228 
229 	tscDelta = endTSC - startTSC;
230 	expired = ((startHigh << 8) | startLow) - ((endHigh << 8) | endLow);
231 	conversionFactor = (double)tscDelta / (double)expired;
232 }
233 
234 
235 static void
236 calculate_cpu_conversion_factor()
237 {
238 	uint8 channel = 2;
239 
240 	// When using channel 2, enable the input and disable the speaker.
241 	if (channel == 2) {
242 		uint8 control = in8(PIT_CHANNEL_2_CONTROL);
243 		control &= PIT_CHANNEL_2_SPEAKER_OFF_MASK;
244 		control |= PIT_CHANNEL_2_GATE_HIGH;
245 		out8(control, PIT_CHANNEL_2_CONTROL);
246 	}
247 
248 	uint64 tscDeltaQuick, tscDeltaSlower, tscDeltaSlow;
249 	double conversionFactorQuick, conversionFactorSlower, conversionFactorSlow;
250 	uint16 expired;
251 
252 	uint32 quickSampleCount = 1;
253 	uint32 slowSampleCount = 1;
254 
255 quick_sample:
256 	calibration_loop(224, channel, tscDeltaQuick, conversionFactorQuick,
257 		expired);
258 
259 slower_sample:
260 	calibration_loop(192, channel, tscDeltaSlower, conversionFactorSlower,
261 		expired);
262 
263 	double deviation = conversionFactorQuick / conversionFactorSlower;
264 	if (deviation < 0.99 || deviation > 1.01) {
265 		// We might have been hit by a SMI or were otherwise stalled
266 		if (quickSampleCount++ < MAX_QUICK_SAMPLES)
267 			goto quick_sample;
268 	}
269 
270 	// Slow sample
271 	calibration_loop(128, channel, tscDeltaSlow, conversionFactorSlow,
272 		expired);
273 
274 	deviation = conversionFactorSlower / conversionFactorSlow;
275 	if (deviation < 0.99 || deviation > 1.01) {
276 		// We might have been hit by a SMI or were otherwise stalled
277 		if (slowSampleCount++ < MAX_SLOW_SAMPLES)
278 			goto slower_sample;
279 	}
280 
281 	// Scale the TSC delta to timer units
282 	tscDeltaSlow *= TIMER_CLKNUM_HZ;
283 
284 	uint64 clockSpeed = tscDeltaSlow / expired;
285 	gTimeConversionFactor = ((uint128(expired) * uint32(1000000)) << 32)
286 		/ uint128(tscDeltaSlow);
287 
288 #ifdef TRACE_CPU
289 	if (clockSpeed > 1000000000LL) {
290 		dprintf("CPU at %Ld.%03Ld GHz\n", clockSpeed / 1000000000LL,
291 			(clockSpeed % 1000000000LL) / 1000000LL);
292 	} else {
293 		dprintf("CPU at %Ld.%03Ld MHz\n", clockSpeed / 1000000LL,
294 			(clockSpeed % 1000000LL) / 1000LL);
295 	}
296 #endif
297 
298 	gKernelArgs.arch_args.system_time_cv_factor = gTimeConversionFactor;
299 	gKernelArgs.arch_args.cpu_clock_speed = clockSpeed;
300 	//dprintf("factors: %lu %llu\n", gTimeConversionFactor, clockSpeed);
301 
302 	if (quickSampleCount > 1) {
303 		dprintf("needed %u quick samples for TSC calibration\n",
304 			quickSampleCount);
305 	}
306 
307 	if (slowSampleCount > 1) {
308 		dprintf("needed %u slow samples for TSC calibration\n",
309 			slowSampleCount);
310 	}
311 
312 	if (channel == 2) {
313 		// Set the gate low again
314 		out8(in8(PIT_CHANNEL_2_CONTROL) & ~PIT_CHANNEL_2_GATE_HIGH,
315 			PIT_CHANNEL_2_CONTROL);
316 	}
317 }
318 
319 
320 //	#pragma mark -
321 
322 
323 extern "C" bigtime_t
324 system_time()
325 {
326 	uint64 lo, hi;
327 	asm("rdtsc": "=a"(lo), "=d"(hi));
328 	return ((lo * gTimeConversionFactor) >> 32) + hi * gTimeConversionFactor;
329 }
330 
331 
332 extern "C" void
333 spin(bigtime_t microseconds)
334 {
335 	bigtime_t time = system_time();
336 
337 	while ((system_time() - time) < microseconds)
338 		asm volatile ("pause;");
339 }
340 
341 
342 extern "C" void
343 cpu_init()
344 {
345 	calculate_cpu_conversion_factor();
346 
347 	gKernelArgs.num_cpus = 1;
348 		// this will eventually be corrected later on
349 }
350