cpu: Extract shared x86 code from efi & bios_ia32

This issue was initially detected by PVS Studio (issue number V547) and fixed
as part of Google Code-in 2019.

The initial problem was the calculate_cpu_conversion_factor function
which had been copied in the BIOS and EFI versions of the boot code.
Further investigation led to more duplicated or very similar functions
being identified.

Introduce an arch_cpu.h for the x86 boot platform to group these things
in a single place, and adjust the BIOS and EFI code to call into that.
Note that the BIOS and EFI code is still a little platform specific,
ideally there should be a boot_arch_cpu_init() function for each
architecture as already done for openfirmware and u-boot.

Also remove some irrelevant comments from copypasted files for other
architectures, as that was filling my git grep with useless noise.

Change-Id: I16d815f0bf015cec0b4e03cc14f3cc447c7164c5
Reviewed-on: https://review.haiku-os.org/c/haiku/+/1985
Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
This commit is contained in:
Andrej Antunovikj 2019-12-04 15:52:31 +04:00 committed by Adrien Destugues
parent edad811b96
commit 7b4d924f98
12 changed files with 340 additions and 688 deletions

View File

@ -2,8 +2,8 @@
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef CPU_H
#define CPU_H
#ifndef BOOT_ARCH_CPU_H
#define BOOT_ARCH_CPU_H
#include <SupportDefs.h>
@ -15,8 +15,11 @@ extern "C" {
extern void cpu_init(void);
void calculate_cpu_conversion_factor(uint8 channel);
#ifdef __cplusplus
}
#endif
#endif /* CPU_H */
#endif /* BOOT_ARCH_CPU_H */

View File

@ -28,6 +28,7 @@ for platform in [ MultiBootSubDirSetup bios_ia32 efi pxe_ia32 ] {
$(kernelArchSpecificSources)
$(kernelLibArchSpecificSources)
$(librootOsArchSources)
cpu.cpp
: -std=c++11 # additional flags
;

View File

@ -0,0 +1,318 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and
* licensed under the NewOS license.
*/
#include <OS.h>
#include <boot/arch/x86/arch_cpu.h>
#include <boot/kernel_args.h>
#include <boot/platform.h>
#include <boot/stage2.h>
#include <boot/stdio.h>
#include <arch/cpu.h>
#include <arch_kernel.h>
#include <arch_system_info.h>
#include <string.h>
extern "C" uint64 rdtsc();
uint32 gTimeConversionFactor;
// PIT definitions
#define TIMER_CLKNUM_HZ (14318180 / 12)
// PIT IO Ports
#define PIT_CHANNEL_PORT_BASE 0x40
#define PIT_CONTROL 0x43
// Channel selection
#define PIT_SELECT_CHANNEL_SHIFT 6
// Access mode
#define PIT_ACCESS_LATCH_COUNTER (0 << 4)
#define PIT_ACCESS_LOW_BYTE_ONLY (1 << 4)
#define PIT_ACCESS_HIGH_BYTE_ONLY (2 << 4)
#define PIT_ACCESS_LOW_THEN_HIGH_BYTE (3 << 4)
// Operating modes
#define PIT_MODE_INTERRUPT_ON_0 (0 << 1)
#define PIT_MODE_HARDWARE_COUNTDOWN (1 << 1)
#define PIT_MODE_RATE_GENERATOR (2 << 1)
#define PIT_MODE_SQUARE_WAVE_GENERATOR (3 << 1)
#define PIT_MODE_SOFTWARE_STROBE (4 << 1)
#define PIT_MODE_HARDWARE_STROBE (5 << 1)
// BCD/Binary mode
#define PIT_BINARY_MODE 0
#define PIT_BCD_MODE 1
// Channel 2 control (speaker)
#define PIT_CHANNEL_2_CONTROL 0x61
#define PIT_CHANNEL_2_GATE_HIGH 0x01
#define PIT_CHANNEL_2_SPEAKER_OFF_MASK ~0x02
// Maximum values
#define MAX_QUICK_SAMPLES 20
#define MAX_SLOW_SAMPLES 20
// TODO: These are arbitrary. They are here to avoid spinning indefinitely
// if the TSC just isn't stable and we can't get our desired error range.
struct uint128 {
uint128(uint64 low, uint64 high = 0)
:
low(low),
high(high)
{
}
bool operator<(const uint128& other) const
{
return high < other.high || (high == other.high && low < other.low);
}
bool operator<=(const uint128& other) const
{
return !(other < *this);
}
uint128 operator<<(int count) const
{
if (count == 0)
return *this;
if (count >= 128)
return 0;
if (count >= 64)
return uint128(0, low << (count - 64));
return uint128(low << count, (high << count) | (low >> (64 - count)));
}
uint128 operator>>(int count) const
{
if (count == 0)
return *this;
if (count >= 128)
return 0;
if (count >= 64)
return uint128(high >> (count - 64), 0);
return uint128((low >> count) | (high << (64 - count)), high >> count);
}
uint128 operator+(const uint128& other) const
{
uint64 resultLow = low + other.low;
return uint128(resultLow,
high + other.high + (resultLow < low ? 1 : 0));
}
uint128 operator-(const uint128& other) const
{
uint64 resultLow = low - other.low;
return uint128(resultLow,
high - other.high - (resultLow > low ? 1 : 0));
}
uint128 operator*(uint32 other) const
{
uint64 resultMid = (low >> 32) * other;
uint64 resultLow = (low & 0xffffffff) * other + (resultMid << 32);
return uint128(resultLow,
high * other + (resultMid >> 32)
+ (resultLow < resultMid << 32 ? 1 : 0));
}
uint128 operator/(const uint128& other) const
{
int shift = 0;
uint128 shiftedDivider = other;
while (shiftedDivider.high >> 63 == 0 && shiftedDivider < *this) {
shiftedDivider = shiftedDivider << 1;
shift++;
}
uint128 result = 0;
uint128 temp = *this;
for (; shift >= 0; shift--, shiftedDivider = shiftedDivider >> 1) {
if (shiftedDivider <= temp) {
result = result + (uint128(1) << shift);
temp = temp - shiftedDivider;
}
}
return result;
}
operator uint64() const
{
return low;
}
private:
uint64 low;
uint64 high;
};
static inline void
calibration_loop(uint8 desiredHighByte, uint8 channel, uint64& tscDelta,
double& conversionFactor, uint16& expired)
{
uint8 select = channel << PIT_SELECT_CHANNEL_SHIFT;
out8(select | PIT_ACCESS_LOW_THEN_HIGH_BYTE | PIT_MODE_INTERRUPT_ON_0
| PIT_BINARY_MODE, PIT_CONTROL);
// Fill in count of 0xffff, low then high byte
uint8 channelPort = PIT_CHANNEL_PORT_BASE + channel;
out8(0xff, channelPort);
out8(0xff, channelPort);
// Read the count back once to delay the start. This ensures that we've
// waited long enough for the counter to actually start counting down, as
// this only happens on the next clock cycle after reload.
in8(channelPort);
in8(channelPort);
// We're expecting the PIT to be at the starting position (high byte 0xff)
// as we just programmed it, but if it isn't we wait for it to wrap.
uint8 startLow;
uint8 startHigh;
do {
out8(select | PIT_ACCESS_LATCH_COUNTER, PIT_CONTROL);
startLow = in8(channelPort);
startHigh = in8(channelPort);
} while (startHigh != 255);
// Read in the first TSC value
uint64 startTSC = rdtsc();
// Wait for the PIT to count down to our desired value
uint8 endLow;
uint8 endHigh;
do {
out8(select | PIT_ACCESS_LATCH_COUNTER, PIT_CONTROL);
endLow = in8(channelPort);
endHigh = in8(channelPort);
} while (endHigh > desiredHighByte);
// And read the second TSC value
uint64 endTSC = rdtsc();
tscDelta = endTSC - startTSC;
expired = ((startHigh << 8) | startLow) - ((endHigh << 8) | endLow);
conversionFactor = (double)tscDelta / (double)expired;
}
void
calculate_cpu_conversion_factor(uint8 channel)
{
// When using channel 2, enable the input and disable the speaker.
if (channel == 2) {
uint8 control = in8(PIT_CHANNEL_2_CONTROL);
control &= PIT_CHANNEL_2_SPEAKER_OFF_MASK;
control |= PIT_CHANNEL_2_GATE_HIGH;
out8(control, PIT_CHANNEL_2_CONTROL);
}
uint64 tscDeltaQuick, tscDeltaSlower, tscDeltaSlow;
double conversionFactorQuick, conversionFactorSlower, conversionFactorSlow;
uint16 expired;
uint32 quickSampleCount = 1;
uint32 slowSampleCount = 1;
quick_sample:
calibration_loop(224, channel, tscDeltaQuick, conversionFactorQuick,
expired);
slower_sample:
calibration_loop(192, channel, tscDeltaSlower, conversionFactorSlower,
expired);
double deviation = conversionFactorQuick / conversionFactorSlower;
if (deviation < 0.99 || deviation > 1.01) {
// We might have been hit by a SMI or were otherwise stalled
if (quickSampleCount++ < MAX_QUICK_SAMPLES)
goto quick_sample;
}
// Slow sample
calibration_loop(128, channel, tscDeltaSlow, conversionFactorSlow,
expired);
deviation = conversionFactorSlower / conversionFactorSlow;
if (deviation < 0.99 || deviation > 1.01) {
// We might have been hit by a SMI or were otherwise stalled
if (slowSampleCount++ < MAX_SLOW_SAMPLES)
goto slower_sample;
}
// Scale the TSC delta to timer units
tscDeltaSlow *= TIMER_CLKNUM_HZ;
uint64 clockSpeed = tscDeltaSlow / expired;
gTimeConversionFactor = ((uint128(expired) * uint32(1000000)) << 32)
/ uint128(tscDeltaSlow);
#ifdef TRACE_CPU
if (clockSpeed > 1000000000LL) {
dprintf("CPU at %Ld.%03Ld GHz\n", clockSpeed / 1000000000LL,
(clockSpeed % 1000000000LL) / 1000000LL);
} else {
dprintf("CPU at %Ld.%03Ld MHz\n", clockSpeed / 1000000LL,
(clockSpeed % 1000000LL) / 1000LL);
}
#endif
gKernelArgs.arch_args.system_time_cv_factor = gTimeConversionFactor;
gKernelArgs.arch_args.cpu_clock_speed = clockSpeed;
//dprintf("factors: %lu %llu\n", gTimeConversionFactor, clockSpeed);
if (quickSampleCount > 1) {
dprintf("needed %" B_PRIu32 " quick samples for TSC calibration\n",
quickSampleCount);
}
if (slowSampleCount > 1) {
dprintf("needed %" B_PRIu32 " slow samples for TSC calibration\n",
slowSampleCount);
}
if (channel == 2) {
// Set the gate low again
out8(in8(PIT_CHANNEL_2_CONTROL) & ~PIT_CHANNEL_2_GATE_HIGH,
PIT_CHANNEL_2_CONTROL);
}
}
extern "C" bigtime_t
system_time()
{
return rdtsc() * gTimeConversionFactor;
}
extern "C" void
spin(bigtime_t microseconds)
{
bigtime_t time = system_time();
while ((system_time() - time) < microseconds)
asm volatile ("pause;");
}

View File

@ -1,9 +1,6 @@
/*
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and
* licensed under the NewOS license.
*/

View File

@ -2,20 +2,18 @@
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and
* licensed under the NewOS license.
*/
#include "cpu.h"
#include <OS.h>
#include <boot/platform.h>
#include <boot/stdio.h>
#include <boot/arch/x86/arch_cpu.h>
#include <boot/kernel_args.h>
#include <boot/platform.h>
#include <boot/stage2.h>
#include <arch/cpu.h>
#include <boot/stdio.h>
#include <arch/x86/arch_cpu.h>
#include <arch_kernel.h>
#include <arch_system_info.h>
@ -30,291 +28,10 @@
#endif
extern "C" uint64 rdtsc();
uint32 gTimeConversionFactor;
// PIT definitions
#define TIMER_CLKNUM_HZ (14318180 / 12)
// PIT IO Ports
#define PIT_CHANNEL_PORT_BASE 0x40
#define PIT_CONTROL 0x43
// Channel selection
#define PIT_SELECT_CHANNEL_SHIFT 6
// Access mode
#define PIT_ACCESS_LATCH_COUNTER (0 << 4)
#define PIT_ACCESS_LOW_BYTE_ONLY (1 << 4)
#define PIT_ACCESS_HIGH_BYTE_ONLY (2 << 4)
#define PIT_ACCESS_LOW_THEN_HIGH_BYTE (3 << 4)
// Operating modes
#define PIT_MODE_INTERRUPT_ON_0 (0 << 1)
#define PIT_MODE_HARDWARE_COUNTDOWN (1 << 1)
#define PIT_MODE_RATE_GENERATOR (2 << 1)
#define PIT_MODE_SQUARE_WAVE_GENERATOR (3 << 1)
#define PIT_MODE_SOFTWARE_STROBE (4 << 1)
#define PIT_MODE_HARDWARE_STROBE (5 << 1)
// BCD/Binary mode
#define PIT_BINARY_MODE 0
#define PIT_BCD_MODE 1
// Channel 2 control (speaker)
#define PIT_CHANNEL_2_CONTROL 0x61
#define PIT_CHANNEL_2_GATE_HIGH 0x01
#define PIT_CHANNEL_2_SPEAKER_OFF_MASK ~0x02
// Maximum values
#define MAX_QUICK_SAMPLES 20
#define MAX_SLOW_SAMPLES 20
// TODO: These are arbitrary. They are here to avoid spinning indefinitely
// if the TSC just isn't stable and we can't get our desired error range.
#define CPUID_EFLAGS (1UL << 21)
#define RDTSC_FEATURE (1UL << 4)
struct uint128 {
uint128(uint64 low, uint64 high = 0)
:
low(low),
high(high)
{
}
bool operator<(const uint128& other) const
{
return high < other.high || (high == other.high && low < other.low);
}
bool operator<=(const uint128& other) const
{
return !(other < *this);
}
uint128 operator<<(int count) const
{
if (count == 0)
return *this;
if (count >= 128)
return 0;
if (count >= 64)
return uint128(0, low << (count - 64));
return uint128(low << count, (high << count) | (low >> (64 - count)));
}
uint128 operator>>(int count) const
{
if (count == 0)
return *this;
if (count >= 128)
return 0;
if (count >= 64)
return uint128(high >> (count - 64), 0);
return uint128((low >> count) | (high << (64 - count)), high >> count);
}
uint128 operator+(const uint128& other) const
{
uint64 resultLow = low + other.low;
return uint128(resultLow,
high + other.high + (resultLow < low ? 1 : 0));
}
uint128 operator-(const uint128& other) const
{
uint64 resultLow = low - other.low;
return uint128(resultLow,
high - other.high - (resultLow > low ? 1 : 0));
}
uint128 operator*(uint32 other) const
{
uint64 resultMid = (low >> 32) * other;
uint64 resultLow = (low & 0xffffffff) * other + (resultMid << 32);
return uint128(resultLow,
high * other + (resultMid >> 32)
+ (resultLow < resultMid << 32 ? 1 : 0));
}
uint128 operator/(const uint128& other) const
{
int shift = 0;
uint128 shiftedDivider = other;
while (shiftedDivider.high >> 63 == 0 && shiftedDivider < *this) {
shiftedDivider = shiftedDivider << 1;
shift++;
}
uint128 result = 0;
uint128 temp = *this;
for (; shift >= 0; shift--, shiftedDivider = shiftedDivider >> 1) {
if (shiftedDivider <= temp) {
result = result + (uint128(1) << shift);
temp = temp - shiftedDivider;
}
}
return result;
}
operator uint64() const
{
return low;
}
private:
uint64 low;
uint64 high;
};
static inline void
calibration_loop(uint8 desiredHighByte, uint8 channel, uint64& tscDelta,
double& conversionFactor, uint16& expired)
{
uint8 select = channel << PIT_SELECT_CHANNEL_SHIFT;
out8(select | PIT_ACCESS_LOW_THEN_HIGH_BYTE | PIT_MODE_INTERRUPT_ON_0
| PIT_BINARY_MODE, PIT_CONTROL);
// Fill in count of 0xffff, low then high byte
uint8 channelPort = PIT_CHANNEL_PORT_BASE + channel;
out8(0xff, channelPort);
out8(0xff, channelPort);
// Read the count back once to delay the start. This ensures that we've
// waited long enough for the counter to actually start counting down, as
// this only happens on the next clock cycle after reload.
in8(channelPort);
in8(channelPort);
// We're expecting the PIT to be at the starting position (high byte 0xff)
// as we just programmed it, but if it isn't we wait for it to wrap.
uint8 startLow;
uint8 startHigh;
do {
out8(select | PIT_ACCESS_LATCH_COUNTER, PIT_CONTROL);
startLow = in8(channelPort);
startHigh = in8(channelPort);
} while (startHigh != 255);
// Read in the first TSC value
uint64 startTSC = rdtsc();
// Wait for the PIT to count down to our desired value
uint8 endLow;
uint8 endHigh;
do {
out8(select | PIT_ACCESS_LATCH_COUNTER, PIT_CONTROL);
endLow = in8(channelPort);
endHigh = in8(channelPort);
} while (endHigh > desiredHighByte);
// And read the second TSC value
uint64 endTSC = rdtsc();
tscDelta = endTSC - startTSC;
expired = ((startHigh << 8) | startLow) - ((endHigh << 8) | endLow);
conversionFactor = (double)tscDelta / (double)expired;
}
static void
calculate_cpu_conversion_factor()
{
uint8 channel = 0;
// When using channel 2, enable the input and disable the speaker.
if (channel == 2) {
uint8 control = in8(PIT_CHANNEL_2_CONTROL);
control &= PIT_CHANNEL_2_SPEAKER_OFF_MASK;
control |= PIT_CHANNEL_2_GATE_HIGH;
out8(control, PIT_CHANNEL_2_CONTROL);
}
uint64 tscDeltaQuick, tscDeltaSlower, tscDeltaSlow;
double conversionFactorQuick, conversionFactorSlower, conversionFactorSlow;
uint16 expired;
uint32 quickSampleCount = 1;
uint32 slowSampleCount = 1;
quick_sample:
calibration_loop(224, channel, tscDeltaQuick, conversionFactorQuick,
expired);
slower_sample:
calibration_loop(192, channel, tscDeltaSlower, conversionFactorSlower,
expired);
double deviation = conversionFactorQuick / conversionFactorSlower;
if (deviation < 0.99 || deviation > 1.01) {
// We might have been hit by a SMI or were otherwise stalled
if (quickSampleCount++ < MAX_QUICK_SAMPLES)
goto quick_sample;
}
// Slow sample
calibration_loop(128, channel, tscDeltaSlow, conversionFactorSlow,
expired);
deviation = conversionFactorSlower / conversionFactorSlow;
if (deviation < 0.99 || deviation > 1.01) {
// We might have been hit by a SMI or were otherwise stalled
if (slowSampleCount++ < MAX_SLOW_SAMPLES)
goto slower_sample;
}
// Scale the TSC delta to timer units
tscDeltaSlow *= TIMER_CLKNUM_HZ;
uint64 clockSpeed = tscDeltaSlow / expired;
gTimeConversionFactor = ((uint128(expired) * uint32(1000000)) << 32)
/ uint128(tscDeltaSlow);
#ifdef TRACE_CPU
if (clockSpeed > 1000000000LL) {
dprintf("CPU at %Ld.%03Ld GHz\n", clockSpeed / 1000000000LL,
(clockSpeed % 1000000000LL) / 1000000LL);
} else {
dprintf("CPU at %Ld.%03Ld MHz\n", clockSpeed / 1000000LL,
(clockSpeed % 1000000LL) / 1000LL);
}
#endif
gKernelArgs.arch_args.system_time_cv_factor = gTimeConversionFactor;
gKernelArgs.arch_args.cpu_clock_speed = clockSpeed;
//dprintf("factors: %lu %llu\n", gTimeConversionFactor, clockSpeed);
if (quickSampleCount > 1) {
dprintf("needed %lu quick samples for TSC calibration\n",
quickSampleCount);
}
if (slowSampleCount > 1) {
dprintf("needed %lu slow samples for TSC calibration\n",
slowSampleCount);
}
if (channel == 2) {
// Set the gate low again
out8(in8(PIT_CHANNEL_2_CONTROL) & ~PIT_CHANNEL_2_GATE_HIGH,
PIT_CHANNEL_2_CONTROL);
}
}
static status_t
check_cpu_features()
{
@ -342,25 +59,14 @@ check_cpu_features()
// #pragma mark -
extern "C" void
spin(bigtime_t microseconds)
{
bigtime_t time = system_time();
while ((system_time() - time) < microseconds)
asm volatile ("pause;");
}
extern "C" void
cpu_init()
{
if (check_cpu_features() != B_OK)
panic("You need a Pentium or higher in order to boot!\n");
calculate_cpu_conversion_factor();
calculate_cpu_conversion_factor(0);
gKernelArgs.num_cpus = 1;
// this will eventually be corrected later on
}

View File

@ -8,7 +8,9 @@
#include <KernelExport.h>
#include <arch/cpu.h>
#include <arch/x86/arch_cpu.h>
#include <boot/arch/x86/arch_cpu.h>
#include <boot/platform.h>
#include <boot/heap.h>
#include <boot/stage2.h>
@ -17,7 +19,6 @@
#include "apm.h"
#include "bios.h"
#include "console.h"
#include "cpu.h"
#include "debug.h"
#include "hpet.h"
#include "interrupts.h"

View File

@ -35,25 +35,6 @@ FUNCTION(execute_n_instructions):
loop .again
ret
FUNCTION(system_time):
/* load 64-bit factor into %eax (low), %edx (high) */
/* hand-assemble rdtsc -- read time stamp counter */
rdtsc /* time in %edx,%eax */
pushl %ebx
movl gTimeConversionFactor, %ebx
movl %edx, %ecx /* save high half */
mull %ebx /* truncate %eax, but keep %edx */
movl %ecx, %eax
movl %edx, %ecx /* save high half of low */
mull %ebx /*, %eax*/
/* now compute [%edx, %eax] + [%ecx], propagating carry */
subl %ebx, %ebx /* need zero to propagate carry */
addl %ecx, %eax
adc %ebx, %edx
popl %ebx
ret
null_idt_descr:
.word 0
.word 0,0

View File

@ -2,347 +2,19 @@
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and
* licensed under the NewOS license.
*/
#include "cpu.h"
#include "efi_platform.h"
#include <OS.h>
#include <boot/platform.h>
#include <boot/stdio.h>
#include <boot/kernel_args.h>
#include <boot/stage2.h>
#include <arch/cpu.h>
#include <arch_kernel.h>
#include <arch_system_info.h>
#include <string.h>
//#define TRACE_CPU
#ifdef TRACE_CPU
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
extern "C" uint64 rdtsc();
uint32 gTimeConversionFactor;
// PIT definitions
#define TIMER_CLKNUM_HZ (14318180 / 12)
// PIT IO Ports
#define PIT_CHANNEL_PORT_BASE 0x40
#define PIT_CONTROL 0x43
// Channel selection
#define PIT_SELECT_CHANNEL_SHIFT 6
// Access mode
#define PIT_ACCESS_LATCH_COUNTER (0 << 4)
#define PIT_ACCESS_LOW_BYTE_ONLY (1 << 4)
#define PIT_ACCESS_HIGH_BYTE_ONLY (2 << 4)
#define PIT_ACCESS_LOW_THEN_HIGH_BYTE (3 << 4)
// Operating modes
#define PIT_MODE_INTERRUPT_ON_0 (0 << 1)
#define PIT_MODE_HARDWARE_COUNTDOWN (1 << 1)
#define PIT_MODE_RATE_GENERATOR (2 << 1)
#define PIT_MODE_SQUARE_WAVE_GENERATOR (3 << 1)
#define PIT_MODE_SOFTWARE_STROBE (4 << 1)
#define PIT_MODE_HARDWARE_STROBE (5 << 1)
// BCD/Binary mode
#define PIT_BINARY_MODE 0
#define PIT_BCD_MODE 1
// Channel 2 control (speaker)
#define PIT_CHANNEL_2_CONTROL 0x61
#define PIT_CHANNEL_2_GATE_HIGH 0x01
#define PIT_CHANNEL_2_SPEAKER_OFF_MASK ~0x02
// Maximum values
#define MAX_QUICK_SAMPLES 20
#define MAX_SLOW_SAMPLES 20
// TODO: These are arbitrary. They are here to avoid spinning indefinitely
// if the TSC just isn't stable and we can't get our desired error range.
#define CPUID_EFLAGS (1UL << 21)
#define RDTSC_FEATURE (1UL << 4)
struct uint128 {
uint128(uint64 low, uint64 high = 0)
:
low(low),
high(high)
{
}
bool operator<(const uint128& other) const
{
return high < other.high || (high == other.high && low < other.low);
}
bool operator<=(const uint128& other) const
{
return !(other < *this);
}
uint128 operator<<(int count) const
{
if (count == 0)
return *this;
if (count >= 128)
return 0;
if (count >= 64)
return uint128(0, low << (count - 64));
return uint128(low << count, (high << count) | (low >> (64 - count)));
}
uint128 operator>>(int count) const
{
if (count == 0)
return *this;
if (count >= 128)
return 0;
if (count >= 64)
return uint128(high >> (count - 64), 0);
return uint128((low >> count) | (high << (64 - count)), high >> count);
}
uint128 operator+(const uint128& other) const
{
uint64 resultLow = low + other.low;
return uint128(resultLow,
high + other.high + (resultLow < low ? 1 : 0));
}
uint128 operator-(const uint128& other) const
{
uint64 resultLow = low - other.low;
return uint128(resultLow,
high - other.high - (resultLow > low ? 1 : 0));
}
uint128 operator*(uint32 other) const
{
uint64 resultMid = (low >> 32) * other;
uint64 resultLow = (low & 0xffffffff) * other + (resultMid << 32);
return uint128(resultLow,
high * other + (resultMid >> 32)
+ (resultLow < resultMid << 32 ? 1 : 0));
}
uint128 operator/(const uint128& other) const
{
int shift = 0;
uint128 shiftedDivider = other;
while (shiftedDivider.high >> 63 == 0 && shiftedDivider < *this) {
shiftedDivider = shiftedDivider << 1;
shift++;
}
uint128 result = 0;
uint128 temp = *this;
for (; shift >= 0; shift--, shiftedDivider = shiftedDivider >> 1) {
if (shiftedDivider <= temp) {
result = result + (uint128(1) << shift);
temp = temp - shiftedDivider;
}
}
return result;
}
operator uint64() const
{
return low;
}
private:
uint64 low;
uint64 high;
};
static inline void
calibration_loop(uint8 desiredHighByte, uint8 channel, uint64& tscDelta,
double& conversionFactor, uint16& expired)
{
uint8 select = channel << PIT_SELECT_CHANNEL_SHIFT;
out8(select | PIT_ACCESS_LOW_THEN_HIGH_BYTE | PIT_MODE_INTERRUPT_ON_0
| PIT_BINARY_MODE, PIT_CONTROL);
// Fill in count of 0xffff, low then high byte
uint8 channelPort = PIT_CHANNEL_PORT_BASE + channel;
out8(0xff, channelPort);
out8(0xff, channelPort);
// Read the count back once to delay the start. This ensures that we've
// waited long enough for the counter to actually start counting down, as
// this only happens on the next clock cycle after reload.
in8(channelPort);
in8(channelPort);
// We're expecting the PIT to be at the starting position (high byte 0xff)
// as we just programmed it, but if it isn't we wait for it to wrap.
uint8 startLow;
uint8 startHigh;
do {
out8(select | PIT_ACCESS_LATCH_COUNTER, PIT_CONTROL);
startLow = in8(channelPort);
startHigh = in8(channelPort);
} while (startHigh != 255);
// Read in the first TSC value
uint64 startTSC = rdtsc();
// Wait for the PIT to count down to our desired value
uint8 endLow;
uint8 endHigh;
do {
out8(select | PIT_ACCESS_LATCH_COUNTER, PIT_CONTROL);
endLow = in8(channelPort);
endHigh = in8(channelPort);
} while (endHigh > desiredHighByte);
// And read the second TSC value
uint64 endTSC = rdtsc();
tscDelta = endTSC - startTSC;
expired = ((startHigh << 8) | startLow) - ((endHigh << 8) | endLow);
conversionFactor = (double)tscDelta / (double)expired;
}
static void
calculate_cpu_conversion_factor()
{
uint8 channel = 2;
// When using channel 2, enable the input and disable the speaker.
if (channel == 2) {
uint8 control = in8(PIT_CHANNEL_2_CONTROL);
control &= PIT_CHANNEL_2_SPEAKER_OFF_MASK;
control |= PIT_CHANNEL_2_GATE_HIGH;
out8(control, PIT_CHANNEL_2_CONTROL);
}
uint64 tscDeltaQuick, tscDeltaSlower, tscDeltaSlow;
double conversionFactorQuick, conversionFactorSlower, conversionFactorSlow;
uint16 expired;
uint32 quickSampleCount = 1;
uint32 slowSampleCount = 1;
quick_sample:
calibration_loop(224, channel, tscDeltaQuick, conversionFactorQuick,
expired);
slower_sample:
calibration_loop(192, channel, tscDeltaSlower, conversionFactorSlower,
expired);
double deviation = conversionFactorQuick / conversionFactorSlower;
if (deviation < 0.99 || deviation > 1.01) {
// We might have been hit by a SMI or were otherwise stalled
if (quickSampleCount++ < MAX_QUICK_SAMPLES)
goto quick_sample;
}
// Slow sample
calibration_loop(128, channel, tscDeltaSlow, conversionFactorSlow,
expired);
deviation = conversionFactorSlower / conversionFactorSlow;
if (deviation < 0.99 || deviation > 1.01) {
// We might have been hit by a SMI or were otherwise stalled
if (slowSampleCount++ < MAX_SLOW_SAMPLES)
goto slower_sample;
}
// Scale the TSC delta to timer units
tscDeltaSlow *= TIMER_CLKNUM_HZ;
uint64 clockSpeed = tscDeltaSlow / expired;
gTimeConversionFactor = ((uint128(expired) * uint32(1000000)) << 32)
/ uint128(tscDeltaSlow);
#ifdef TRACE_CPU
if (clockSpeed > 1000000000LL) {
dprintf("CPU at %Ld.%03Ld GHz\n", clockSpeed / 1000000000LL,
(clockSpeed % 1000000000LL) / 1000000LL);
} else {
dprintf("CPU at %Ld.%03Ld MHz\n", clockSpeed / 1000000LL,
(clockSpeed % 1000000LL) / 1000LL);
}
#endif
gKernelArgs.arch_args.system_time_cv_factor = gTimeConversionFactor;
gKernelArgs.arch_args.cpu_clock_speed = clockSpeed;
//dprintf("factors: %lu %llu\n", gTimeConversionFactor, clockSpeed);
if (quickSampleCount > 1) {
dprintf("needed %u quick samples for TSC calibration\n",
quickSampleCount);
}
if (slowSampleCount > 1) {
dprintf("needed %u slow samples for TSC calibration\n",
slowSampleCount);
}
if (channel == 2) {
// Set the gate low again
out8(in8(PIT_CHANNEL_2_CONTROL) & ~PIT_CHANNEL_2_GATE_HIGH,
PIT_CHANNEL_2_CONTROL);
}
}
// #pragma mark -
extern "C" bigtime_t
system_time()
{
uint64 lo, hi;
asm("rdtsc": "=a"(lo), "=d"(hi));
return ((lo * gTimeConversionFactor) >> 32) + hi * gTimeConversionFactor;
}
extern "C" void
spin(bigtime_t microseconds)
{
bigtime_t time = system_time();
while ((system_time() - time) < microseconds)
asm volatile ("pause;");
}
#include <arch/x86/arch_cpu.h>
extern "C" void
cpu_init()
{
calculate_cpu_conversion_factor();
calculate_cpu_conversion_factor(2);
gKernelArgs.num_cpus = 1;
// this will eventually be corrected later on

View File

@ -1,22 +0,0 @@
/*
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef CPU_H
#define CPU_H
#include <SupportDefs.h>
#ifdef __cplusplus
extern "C" {
#endif
extern void cpu_init(void);
#ifdef __cplusplus
}
#endif
#endif /* CPU_H */

View File

@ -12,17 +12,18 @@
#include <KernelExport.h>
#include <arch/cpu.h>
#include <kernel.h>
#include <boot/arch/x86/arch_cpu.h>
#include <boot/kernel_args.h>
#include <boot/platform.h>
#include <boot/stage2.h>
#include <boot/stdio.h>
#include <kernel.h>
#include "arch_mmu.h"
#include "acpi.h"
#include "console.h"
#include "efi_platform.h"
#include "cpu.h"
#include "mmu.h"
#include "serial.h"
#include "smp.h"

View File

@ -1,9 +1,6 @@
/*
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and
* licensed under the NewOS license.
*/

View File

@ -1,9 +1,6 @@
/*
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and
* licensed under the NewOS license.
*/