From c3eef02d50263a535b1cf2d54649b77f17cad754 Mon Sep 17 00:00:00 2001 From: Matheus Date: Sun, 29 Sep 2024 16:30:08 -0300 Subject: [PATCH] tsc: initial implementation of timekeeper source --- kernel-src/arch/x86-64/hpet.c | 1 + kernel-src/arch/x86-64/kvmclock.c | 1 + kernel-src/arch/x86-64/tsc.c | 76 ++++++++++++++++++++++++++ kernel-src/include/time.h | 2 +- kernel-src/include/x86-64/arch/cpuid.h | 7 ++- 5 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 kernel-src/arch/x86-64/tsc.c diff --git a/kernel-src/arch/x86-64/hpet.c b/kernel-src/arch/x86-64/hpet.c index 05db9fc1..7ceb7929 100644 --- a/kernel-src/arch/x86-64/hpet.c +++ b/kernel-src/arch/x86-64/hpet.c @@ -152,6 +152,7 @@ static timekeeper_source_info_t *hpet_init(void) { } +// ran in at least IPL_DPC time_t hpet_ticks(timekeeper_source_info_t *) { if (hpet_private.type == TYPE_64BIT) { return read64(HPET_REG_COUNTER); diff --git a/kernel-src/arch/x86-64/kvmclock.c b/kernel-src/arch/x86-64/kvmclock.c index 5e4c9553..079f170b 100644 --- a/kernel-src/arch/x86-64/kvmclock.c +++ b/kernel-src/arch/x86-64/kvmclock.c @@ -60,6 +60,7 @@ static timekeeper_source_info_t *kvm_timer_init(void) { return timekeeper_source_info; } +// ran in at least IPL_DPC static time_t kvm_timer_ticks(timekeeper_source_info_t *timekeeper_source_info) { volatile kvm_timer_info_t *kvm_timer_info = MAKE_HHDM(timekeeper_source_info->private); diff --git a/kernel-src/arch/x86-64/tsc.c b/kernel-src/arch/x86-64/tsc.c new file mode 100644 index 00000000..92955429 --- /dev/null +++ b/kernel-src/arch/x86-64/tsc.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include + +static bool tsc_probe(void) { + cpuid_results_t cpuid_results; + + uint32_t max_extended_leaf = cpuid_extended_max_leaf(); + if (max_extended_leaf < 0x80000007) + return false; + + cpuid(0x80000007, &cpuid_results); + return cpuid_results.edx & (1 << 8); +} + +static timekeeper_source_info_t *tsc_init(void) { + __assert(tsc_probe()); + + timekeeper_source_info_t *timekeeper_source_info = alloc(sizeof(timekeeper_source_info_t)); + __assert(timekeeper_source_info); + + timekeeper_source_info->private = current_cpu(); + + bool need_calibration = false; + if (cpuid_base_max_leaf() >= 0x15) { + cpuid_results_t cpuid_results; + cpuid(0x15, &cpuid_results); + + if (cpuid_results.eax == 0 || cpuid_results.ebx == 0 || cpuid_results.ecx == 0) { + need_calibration = true; + goto calibrate; + } + + uint64_t core_frequency = cpuid_results.ecx; + uint64_t core_numerator = cpuid_results.ebx; + uint64_t core_denominator = cpuid_results.eax; + + uint64_t tsc_frequency = core_frequency * core_numerator / core_denominator; + timekeeper_source_info->ticks_per_us = tsc_frequency / 1000000; + } + + calibrate: + if (need_calibration) { + for (int i = 0; i < 10; ++i) { + long old_ipl = interrupt_raiseipl(IPL_TIMER - 1); + time_t ticks_start = rdtsc_serialized(); + + timekeeper_wait_us(50000); + time_t ticks_end = rdtsc_serialized(); + + interrupt_loweripl(old_ipl); + + timekeeper_source_info->ticks_per_us = (ticks_end - ticks_start) / 50000; + } + } + + return timekeeper_source_info; +} + +// ran in at least IPL_DPC +static time_t tsc_ticks(timekeeper_source_info_t *timekeeper_source_info) { + __assert(timekeeper_source_info->private == current_cpu()); + + return rdtsc_serialized(); +} + +TIMEKEEPER_SOURCE(kvmtimer_source, + .name = "Invariant TSC", + .priority = 990, + .probe = tsc_probe, + .init = tsc_init, + .ticks = tsc_ticks, + .flags = 0 +); diff --git a/kernel-src/include/time.h b/kernel-src/include/time.h index 4527e7fe..f5ed505c 100644 --- a/kernel-src/include/time.h +++ b/kernel-src/include/time.h @@ -47,7 +47,7 @@ static inline time_t timespec_us(timespec_t a) { static inline timespec_t timespec_from_us(time_t us) { timespec_t ts = { .s = us / 1000000, - .ns = (us % 1000000) * 100 + .ns = (us * 1000) % 1000000000 }; return ts; diff --git a/kernel-src/include/x86-64/arch/cpuid.h b/kernel-src/include/x86-64/arch/cpuid.h index 4b47b47a..beb457ad 100644 --- a/kernel-src/include/x86-64/arch/cpuid.h +++ b/kernel-src/include/x86-64/arch/cpuid.h @@ -18,6 +18,7 @@ typedef struct { #define CPUID_VENDOR_INTEL "GenuineIntel" #define CPUID_LEAF_0x80000001_EDX_SYSCALL (1 << 11) +#define CPUID_LEAF_1_EDX_TSC (1 << 4) #define CPUID_LEAF_1_EDX_HTT (1 << 28) static inline void cpuid_with_ecx(uint32_t leaf, uint32_t ecx, cpuid_results_t *cpuid_results) { @@ -90,9 +91,9 @@ static inline bool cpuid_leaf_0x4_available(void) { if (cpuid_base_max_leaf() < 0x4) return false; - unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; - __get_cpuid_count(0x4, 0, &eax, &ebx, &ecx, &edx); - return eax != 0; + cpuid_results_t cpuid_results; + cpuid_with_ecx(0x4, 0, &cpuid_results); + return cpuid_results.eax != 0; } #endif