sys/cpu: Implement TSC frequency calibration
`tsc_freq_arch` is the architecture-specific function to get the frequency of rdtsc. If that fails (e.g. on an x86_64 machine without CPUID leaf 0x15) then the TSC is calibrated with EFI Boot Services' `Stall`.
diff --git a/common/sys/cpu.h b/common/sys/cpu.h
index 434c436c..0ced9511 100644
--- a/common/sys/cpu.h
+++ b/common/sys/cpu.h
@@ -4,6 +4,7 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
+#include <lib/misc.h>
#if defined(__x86_64__) || defined(__i386__)
@@ -160,6 +161,15 @@ static inline uint64_t rdtsc(void) {
return ((uint64_t)edx << 32) | eax;
}
+static inline uint64_t tsc_freq_arch(void) {
+ uint32_t eax, ebx, ecx, edx;
+ if (!cpuid(0x15, 0, &eax, &ebx, &ecx, &edx))
+ return 0;
+ if (eax == 0 || ebx == 0 || ecx == 0)
+ return 0;
+ return ecx * ebx / eax;
+}
+
#define rdrand(type) ({ \
type rdrand__ret; \
asm volatile ( \
@@ -221,6 +231,10 @@ static inline uint64_t rdtsc(void) {
return v;
}
+static inline uint64_t tsc_freq_arch(void) {
+ return 0; // FIXME
+}
+
#define locked_read(var) ({ \
typeof(*var) locked_read__ret = 0; \
asm volatile ( \
@@ -289,6 +303,10 @@ static inline uint64_t rdtsc(void) {
return v;
}
+static inline uint64_t tsc_freq_arch(void) {
+ return 0; // FIXME
+}
+
#define csr_read(csr) ({\
size_t v;\
asm volatile ("csrr %0, " csr : "=r"(v));\
@@ -348,10 +366,34 @@ static inline uint64_t rdtsc(void) {
return v;
}
+static inline uint64_t tsc_freq_arch(void) {
+ return 0; // FIXME
+}
+
#else
#error Unknown architecture
#endif
+static inline uint64_t tsc_freq(void) {
+ uint64_t freq = tsc_freq_arch();
+ if (freq != 0) {
+ return freq;
+ }
+
+#if defined(UEFI)
+ uint64_t tsc_start = rdtsc();
+ gBS->Stall(1000);
+ uint64_t tsc_end = rdtsc();
+
+ if (tsc_end < tsc_start)
+ return 0;
+
+ return (tsc_end - tsc_start) * 1000ULL;
+#else
+ return 0;
+#endif
+}
+
static inline void delay(uint64_t cycles) {
uint64_t next_stop = rdtsc() + cycles;
