2009-03-26 02:32:01 -04:00
|
|
|
/*
|
2025-03-30 14:49:48 -07:00
|
|
|
* Copyright (c) 2025 Travis Geiselbrecht
|
2009-03-26 02:32:01 -04:00
|
|
|
*
|
2019-07-05 17:22:23 -07:00
|
|
|
* Use of this source code is governed by a MIT-style
|
|
|
|
|
* license that can be found in the LICENSE file or at
|
|
|
|
|
* https://opensource.org/licenses/MIT
|
2009-03-26 02:32:01 -04:00
|
|
|
*/
|
|
|
|
|
#include <sys/types.h>
|
2025-03-30 14:49:48 -07:00
|
|
|
#include <lk/debug.h>
|
2019-06-17 18:28:51 -07:00
|
|
|
#include <lk/err.h>
|
2025-03-30 14:49:48 -07:00
|
|
|
#include <lk/init.h>
|
2019-06-17 18:28:51 -07:00
|
|
|
#include <lk/reg.h>
|
2025-03-30 14:49:48 -07:00
|
|
|
#include <lk/trace.h>
|
2009-03-26 02:32:01 -04:00
|
|
|
#include <kernel/thread.h>
|
2025-03-30 21:59:39 -07:00
|
|
|
#include <kernel/vm.h>
|
2009-03-26 02:32:01 -04:00
|
|
|
#include <platform.h>
|
|
|
|
|
#include <platform/timer.h>
|
2009-04-01 17:35:12 -04:00
|
|
|
#include <platform/pc.h>
|
2025-04-01 00:40:50 -07:00
|
|
|
#include <platform/pc/timer.h>
|
2009-03-26 02:32:01 -04:00
|
|
|
#include <arch/x86.h>
|
2025-03-30 21:59:39 -07:00
|
|
|
#include <arch/x86/feature.h>
|
2025-09-24 00:55:17 -07:00
|
|
|
#include <arch/x86/apic.h>
|
2025-04-06 19:28:08 -07:00
|
|
|
#include <arch/x86/pv.h>
|
2025-03-30 21:59:39 -07:00
|
|
|
#include <inttypes.h>
|
|
|
|
|
#include <lib/fixed_point.h>
|
2009-03-26 02:32:01 -04:00
|
|
|
|
2025-04-01 00:40:50 -07:00
|
|
|
#include "platform_p.h"
|
|
|
|
|
|
2025-03-31 00:01:45 -07:00
|
|
|
#define LOCAL_TRACE 0
|
2025-03-30 14:49:48 -07:00
|
|
|
|
|
|
|
|
// Deals with all of the various clock sources and event timers on the PC platform.
|
2025-04-08 23:45:55 -07:00
|
|
|
// TODO:
|
|
|
|
|
// HPET
|
|
|
|
|
// cpuid leaves that describe clock rates
|
2025-03-30 14:49:48 -07:00
|
|
|
|
|
|
|
|
static enum clock_source {
|
|
|
|
|
CLOCK_SOURCE_INITIAL,
|
|
|
|
|
CLOCK_SOURCE_PIT,
|
|
|
|
|
CLOCK_SOURCE_TSC,
|
|
|
|
|
CLOCK_SOURCE_HPET,
|
|
|
|
|
} clock_source = CLOCK_SOURCE_INITIAL;
|
|
|
|
|
|
2025-03-31 00:01:45 -07:00
|
|
|
static struct fp_32_64 tsc_to_timebase;
|
|
|
|
|
static struct fp_32_64 tsc_to_timebase_hires;
|
|
|
|
|
static struct fp_32_64 timebase_to_tsc;
|
|
|
|
|
static bool use_lapic_timer = false;
|
2025-03-30 21:59:39 -07:00
|
|
|
|
2025-03-30 14:49:48 -07:00
|
|
|
static const char *clock_source_name(void) {
|
|
|
|
|
switch (clock_source) {
|
|
|
|
|
case CLOCK_SOURCE_INITIAL:
|
|
|
|
|
return "initial";
|
|
|
|
|
case CLOCK_SOURCE_PIT:
|
2025-03-30 21:59:39 -07:00
|
|
|
return "PIT";
|
2025-03-30 14:49:48 -07:00
|
|
|
case CLOCK_SOURCE_TSC:
|
2025-03-30 21:59:39 -07:00
|
|
|
return "TSC";
|
2025-03-30 14:49:48 -07:00
|
|
|
case CLOCK_SOURCE_HPET:
|
2025-03-30 21:59:39 -07:00
|
|
|
return "HPET";
|
2025-03-30 14:49:48 -07:00
|
|
|
default:
|
|
|
|
|
return "unknown";
|
|
|
|
|
}
|
2009-03-26 02:32:01 -04:00
|
|
|
}
|
|
|
|
|
|
2019-06-19 20:54:28 -07:00
|
|
|
lk_time_t current_time(void) {
|
2025-03-30 14:49:48 -07:00
|
|
|
switch (clock_source) {
|
|
|
|
|
case CLOCK_SOURCE_PIT:
|
|
|
|
|
return pit_current_time();
|
2025-03-30 21:59:39 -07:00
|
|
|
case CLOCK_SOURCE_TSC:
|
|
|
|
|
return u32_mul_u64_fp32_64(__builtin_ia32_rdtsc(), tsc_to_timebase);
|
2025-03-30 14:49:48 -07:00
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2009-03-26 02:32:01 -04:00
|
|
|
}
|
|
|
|
|
|
2019-06-19 20:54:28 -07:00
|
|
|
lk_bigtime_t current_time_hires(void) {
|
2025-03-30 14:49:48 -07:00
|
|
|
switch (clock_source) {
|
|
|
|
|
case CLOCK_SOURCE_PIT:
|
|
|
|
|
return pit_current_time_hires();
|
2025-03-30 21:59:39 -07:00
|
|
|
case CLOCK_SOURCE_TSC:
|
|
|
|
|
return u64_mul_u64_fp32_64(__builtin_ia32_rdtsc(), tsc_to_timebase_hires);
|
2025-03-30 14:49:48 -07:00
|
|
|
default:
|
|
|
|
|
return 0;
|
2015-11-06 16:51:27 -08:00
|
|
|
}
|
2009-03-26 02:32:01 -04:00
|
|
|
}
|
|
|
|
|
|
2025-03-31 00:01:45 -07:00
|
|
|
// Convert lk_time_t to TSC ticks
|
|
|
|
|
uint64_t time_to_tsc_ticks(lk_time_t time) {
|
|
|
|
|
return u64_mul_u32_fp32_64(time, timebase_to_tsc);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-24 00:55:17 -07:00
|
|
|
void platform_init_timer(void) {
|
2025-03-30 21:59:39 -07:00
|
|
|
// Initialize the PIT, it's always present in PC hardware
|
2025-03-30 14:49:48 -07:00
|
|
|
pit_init();
|
|
|
|
|
clock_source = CLOCK_SOURCE_PIT;
|
2025-03-30 21:59:39 -07:00
|
|
|
|
|
|
|
|
#if !X86_LEGACY
|
|
|
|
|
// XXX update note about what invariant TSC means
|
2025-03-31 00:01:45 -07:00
|
|
|
bool use_invariant_tsc = x86_feature_test(X86_FEATURE_INVAR_TSC);
|
|
|
|
|
LTRACEF("invariant TSC %d\n", use_invariant_tsc);
|
2025-03-30 21:59:39 -07:00
|
|
|
|
|
|
|
|
// Test for hypervisor PV clock, which also effectively says if TSC is invariant across
|
|
|
|
|
// all cpus.
|
|
|
|
|
if (pvclock_init() == NO_ERROR) {
|
|
|
|
|
bool pv_clock_stable = pv_clock_is_stable();
|
|
|
|
|
|
2025-03-31 00:01:45 -07:00
|
|
|
use_invariant_tsc |= pv_clock_stable;
|
2025-03-30 21:59:39 -07:00
|
|
|
|
|
|
|
|
printf("pv_clock: Clocksource is %sstable\n", (pv_clock_stable ? "" : "not "));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// XXX test for HPET and use it over PIT if present
|
|
|
|
|
|
2025-03-31 00:01:45 -07:00
|
|
|
if (use_invariant_tsc) {
|
2025-03-30 21:59:39 -07:00
|
|
|
// We're going to try to use the TSC as a time base, obtain the TSC frequency.
|
|
|
|
|
uint64_t tsc_hz = 0;
|
|
|
|
|
|
|
|
|
|
tsc_hz = pvclock_get_tsc_freq();
|
|
|
|
|
if (tsc_hz == 0) {
|
|
|
|
|
// TODO: some x86 cores describe the TSC and lapic clocks in cpuid
|
|
|
|
|
|
|
|
|
|
// Calibrate the TSC against the PIT, which should always be present
|
|
|
|
|
tsc_hz = pit_calibrate_tsc();
|
|
|
|
|
if (tsc_hz == 0) {
|
|
|
|
|
dprintf(CRITICAL, "PC: failed to calibrate TSC frequency\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dprintf(INFO, "PC: TSC frequency %" PRIu64 "Hz\n", tsc_hz);
|
|
|
|
|
|
|
|
|
|
// Compute the ratio of TSC to timebase
|
|
|
|
|
fp_32_64_div_32_32(&tsc_to_timebase, 1000, tsc_hz);
|
|
|
|
|
dprintf(INFO, "PC: TSC to timebase ratio %u.%08u...\n",
|
|
|
|
|
tsc_to_timebase.l0, tsc_to_timebase.l32);
|
|
|
|
|
|
|
|
|
|
fp_32_64_div_32_32(&tsc_to_timebase_hires, 1000*1000, tsc_hz);
|
|
|
|
|
dprintf(INFO, "PC: TSC to hires timebase ratio %u.%08u...\n",
|
|
|
|
|
tsc_to_timebase_hires.l0, tsc_to_timebase_hires.l32);
|
|
|
|
|
|
2025-03-31 00:01:45 -07:00
|
|
|
fp_32_64_div_32_32(&timebase_to_tsc, tsc_hz, 1000);
|
|
|
|
|
dprintf(INFO, "PC: timebase to TSC ratio %u.%08u...\n",
|
|
|
|
|
timebase_to_tsc.l0, timebase_to_tsc.l32);
|
|
|
|
|
|
2025-03-30 21:59:39 -07:00
|
|
|
clock_source = CLOCK_SOURCE_TSC;
|
|
|
|
|
}
|
|
|
|
|
out:
|
2025-03-31 00:01:45 -07:00
|
|
|
|
|
|
|
|
// Set up the local apic for event timer interrupts
|
|
|
|
|
if (lapic_timer_init(use_invariant_tsc) == NO_ERROR) {
|
|
|
|
|
dprintf(INFO, "PC: using LAPIC timer for event timer\n");
|
|
|
|
|
use_lapic_timer = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we're not using the PIT for time base and using the LAPIC timer for events, stop the PIT.
|
|
|
|
|
if (use_lapic_timer && clock_source != CLOCK_SOURCE_PIT) {
|
|
|
|
|
pit_stop_timer();
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-30 21:59:39 -07:00
|
|
|
#endif // !X86_LEGACY
|
|
|
|
|
|
|
|
|
|
dprintf(INFO, "PC: using %s clock source\n", clock_source_name());
|
2009-03-26 02:32:01 -04:00
|
|
|
}
|
|
|
|
|
|
2025-03-30 14:49:48 -07:00
|
|
|
status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval) {
|
2025-03-31 00:01:45 -07:00
|
|
|
if (use_lapic_timer) {
|
|
|
|
|
PANIC_UNIMPLEMENTED;
|
|
|
|
|
}
|
2025-03-30 14:49:48 -07:00
|
|
|
return pit_set_periodic_timer(callback, arg, interval);
|
2009-03-26 02:32:01 -04:00
|
|
|
}
|
|
|
|
|
|
2015-09-11 13:24:10 -07:00
|
|
|
status_t platform_set_oneshot_timer(platform_timer_callback callback,
|
2019-06-19 20:54:28 -07:00
|
|
|
void *arg, lk_time_t interval) {
|
2025-03-31 00:01:45 -07:00
|
|
|
if (use_lapic_timer) {
|
|
|
|
|
return lapic_set_oneshot_timer(callback, arg, interval);
|
|
|
|
|
}
|
2025-03-30 14:49:48 -07:00
|
|
|
return pit_set_oneshot_timer(callback, arg, interval);
|
2015-09-11 13:24:10 -07:00
|
|
|
}
|
|
|
|
|
|
2019-06-19 20:54:28 -07:00
|
|
|
void platform_stop_timer(void) {
|
2025-03-31 00:01:45 -07:00
|
|
|
if (use_lapic_timer) {
|
|
|
|
|
lapic_cancel_timer();
|
|
|
|
|
} else {
|
|
|
|
|
pit_cancel_timer();
|
|
|
|
|
}
|
2015-09-11 13:24:10 -07:00
|
|
|
}
|