diff --git a/arch/x86/cpu.c b/arch/x86/cpu.c index 55f63dc10..d324ca6de 100644 --- a/arch/x86/cpu.c +++ b/arch/x86/cpu.c @@ -41,8 +41,6 @@ extern uint32_t efi_physical_available_ap_bitmap; #endif -uint64_t tsc_clock_freq = 1000000000; - spinlock_t cpu_secondary_spinlock = { .head = 0, .tail = 0 @@ -494,8 +492,8 @@ void bsp_boot_init(void) /* Make sure rdtsc is enabled */ check_tsc(); - /* Calculate TSC Frequency */ - tsc_clock_freq = tsc_cycles_in_period(1000) / 1000 * 1000000; + /* Calibrate TSC Frequency */ + calibrate_tsc(); /* Enable logging */ init_logmsg(LOG_BUF_SIZE, diff --git a/arch/x86/cpuid.c b/arch/x86/cpuid.c index fb92359de..1167c4465 100644 --- a/arch/x86/cpuid.c +++ b/arch/x86/cpuid.c @@ -170,7 +170,7 @@ static void init_vcpuid_entry(__unused struct vm *vm, * ECX, EDX: RESERVED (reserved fields are set to zero). */ case 0x40000010: - entry->eax = (uint32_t)(tsc_clock_freq / 1000); + entry->eax = (uint32_t)(tsc_hz / 1000); entry->ebx = (128 * 1024 * 1024) / 1000; entry->ecx = 0; entry->edx = 0; diff --git a/arch/x86/timer.c b/arch/x86/timer.c index 0adfeb630..11e1ba18c 100644 --- a/arch/x86/timer.c +++ b/arch/x86/timer.c @@ -34,7 +34,10 @@ #include #include -#define MAX_TIMER_ACTIONS 32 +#define MAX_TIMER_ACTIONS 32 +#define CAL_MS 10 + +uint64_t tsc_hz = 1000000000; struct timer { timer_handle_t func; /* callback if time reached */ @@ -421,21 +424,24 @@ void check_tsc(void) CPU_CR_WRITE(cr4, (temp64 & ~CR4_TSD)); } -uint64_t tsc_cycles_in_period(uint16_t timer_period_in_us) +static uint64_t pit_calibrate_tsc(uint16_t cal_ms) { +#define PIT_TICK_RATE 1193182UL +#define PIT_TARGET 0x3FFF +#define PIT_MAX_COUNT 0xFFFF + uint16_t initial_pit; uint16_t current_pit; - uint32_t current_tsc; -#define PIT_TARGET 0x3FFF + uint16_t max_cal_ms; + uint64_t current_tsc; - if (timer_period_in_us < 1000) - pr_warn("Bad timer_period_in_us: %d\n", - timer_period_in_us); + max_cal_ms = (PIT_MAX_COUNT - PIT_TARGET) * 1000 / PIT_TICK_RATE; + cal_ms = min(cal_ms, max_cal_ms); /* Assume the 8254 delivers 18.2 ticks per second when 16 bits fully * wrap. This is about 1.193MHz or a clock period of 0.8384uSec */ - initial_pit = (uint16_t)(timer_period_in_us*1193000UL/1000000); + initial_pit = (uint16_t)(cal_ms * PIT_TICK_RATE / 1000); initial_pit += PIT_TARGET; /* Port 0x43 ==> Control word write; Data 0x30 ==> Select Counter 0, @@ -444,7 +450,7 @@ uint64_t tsc_cycles_in_period(uint16_t timer_period_in_us) io_write_byte(0x30, 0x43); io_write_byte(initial_pit & 0x00ff, 0x40); /* Write LSB */ - io_write_byte(initial_pit >> 8, 0x40); /* Write MSB */ + io_write_byte(initial_pit >> 8, 0x40); /* Write MSB */ current_tsc = rdtsc(); @@ -461,6 +467,32 @@ uint64_t tsc_cycles_in_period(uint16_t timer_period_in_us) current_tsc = rdtsc() - current_tsc; - return (uint64_t) current_tsc; + return current_tsc / cal_ms * 1000; } +/* + * Determine TSC frequency via CPUID 0x15 + */ +static uint64_t native_calibrate_tsc(void) +{ + if (boot_cpu_data.cpuid_level >= 0x15) { + uint32_t eax_denominator, ebx_numerator, ecx_hz, reserved; + + cpuid(0x15, &eax_denominator, &ebx_numerator, + &ecx_hz, &reserved); + + if (eax_denominator != 0 && ebx_numerator != 0) + return (uint64_t) ecx_hz * + ebx_numerator / eax_denominator; + } + + return 0; +} + +void calibrate_tsc(void) +{ + tsc_hz = native_calibrate_tsc(); + if (!tsc_hz) + tsc_hz = pit_calibrate_tsc(CAL_MS); + printf("%s, tsc_hz=%lu\n", __func__, tsc_hz); +} diff --git a/include/arch/x86/cpu.h b/include/arch/x86/cpu.h index 7bb63587c..97bc49241 100644 --- a/include/arch/x86/cpu.h +++ b/include/arch/x86/cpu.h @@ -258,8 +258,6 @@ extern struct cpuinfo_x86 boot_cpu_data; /* Function prototypes */ void cpu_dead(uint32_t logical_id); -uint64_t cpu_cycles_per_second(void); -uint64_t tsc_cycles_in_period(uint16_t timer_period_in_us); void cpu_secondary_reset(void); int hv_main(int cpu_id); bool is_vapic_supported(void); diff --git a/include/arch/x86/timer.h b/include/arch/x86/timer.h index 2d11e2c14..62eb6d3e9 100644 --- a/include/arch/x86/timer.h +++ b/include/arch/x86/timer.h @@ -42,5 +42,6 @@ int timer_softirq(int pcpu_id); void timer_init(void); void timer_cleanup(void); void check_tsc(void); +void calibrate_tsc(void); #endif /* TIMER_H */ diff --git a/include/lib/rtl.h b/include/lib/rtl.h index c39646352..49ddf0ac1 100644 --- a/include/lib/rtl.h +++ b/include/lib/rtl.h @@ -65,12 +65,12 @@ void *memcpy_s(void *d, size_t dmax, const void *s, size_t slen); int udiv64(uint64_t dividend, uint64_t divisor, struct udiv_result *res); int udiv32(uint32_t dividend, uint32_t divisor, struct udiv_result *res); -extern uint64_t tsc_clock_freq; -#define US_TO_TICKS(x) ((x)*tsc_clock_freq/1000000UL) +extern uint64_t tsc_hz; +#define US_TO_TICKS(x) ((x) * tsc_hz / 1000000UL) #define CYCLES_PER_MS US_TO_TICKS(1000UL) -#define TICKS_TO_US(x) ((((x) * (1000000UL >> 8)) / tsc_clock_freq) << 8) -#define TICKS_TO_MS(x) (((x) * 1000UL) / tsc_clock_freq) +#define TICKS_TO_US(x) ((((x) * (1000000UL >> 8)) / tsc_hz) << 8) +#define TICKS_TO_MS(x) (((x) * 1000UL) / tsc_hz) static inline uint64_t rdtsc(void) {