:: commit 9d3e2318344be512693ce9fc6b5d7b684d062863

Mintsuki <mintsuki@protonmail.com> — 2026-02-16 22:28

parents: 1a8caa477e

sys/cpu: Replace cycle-based delay() with calibrated stall()

diff --git a/common/drivers/serial.c b/common/drivers/serial.c
index 664a1c2d..eae75d95 100644
--- a/common/drivers/serial.c
+++ b/common/drivers/serial.c
@@ -2,6 +2,7 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include <lib/misc.h>
 #include <drivers/serial.h>
 #include <sys/cpu.h>
 
diff --git a/common/entry.s2.c b/common/entry.s2.c
index 8cbd37fc..261a40be 100644
--- a/common/entry.s2.c
+++ b/common/entry.s2.c
@@ -87,6 +87,7 @@ noreturn void entry(uint8_t boot_drive, int boot_from) {
         panic(false, "Could not enable A20 line");
     }
 
+    calibrate_tsc();
     uint64_t usec_at_entry = rdtsc_usec();
 
     init_e820();
diff --git a/common/entry.s3.c b/common/entry.s3.c
index 9eb2497d..520232f0 100644
--- a/common/entry.s3.c
+++ b/common/entry.s3.c
@@ -37,6 +37,7 @@ noreturn void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     gRT = SystemTable->RuntimeServices;
     efi_image_handle = ImageHandle;
 
+    calibrate_tsc();
     usec_at_bootloader_entry = rdtsc_usec();
 
     EFI_STATUS status;
diff --git a/common/lib/getchar.c b/common/lib/getchar.c
index 6a21064a..a6c81e44 100644
--- a/common/lib/getchar.c
+++ b/common/lib/getchar.c
@@ -176,7 +176,7 @@ again:
                 case '\r':
                     return '\n';
                 case 0x1b:
-                    delay(10000);
+                    stall(10);
                     ret = serial_in();
                     if (ret == -1) {
                         return GETCHAR_ESCAPE;
diff --git a/common/sys/cpu.h b/common/sys/cpu.h
index 5ab2757e..8c4cf51e 100644
--- a/common/sys/cpu.h
+++ b/common/sys/cpu.h
@@ -4,7 +4,6 @@
 #include <stdint.h>
 #include <stddef.h>
 #include <stdbool.h>
-#include <lib/misc.h>
 
 #if defined(__x86_64__) || defined(__i386__)
 
@@ -232,7 +231,9 @@ static inline uint64_t rdtsc(void) {
 }
 
 static inline uint64_t tsc_freq_arch(void) {
-    return 0; // FIXME
+    uint64_t v;
+    asm volatile ("mrs %0, cntfrq_el0" : "=r" (v));
+    return v;
 }
 
 #define locked_read(var) ({ \
@@ -401,35 +402,26 @@ static inline uint64_t tsc_freq_arch(void) {
 #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
-}
+extern uint64_t tsc_freq;
+void calibrate_tsc(void);
 
 static inline uint64_t rdtsc_usec(void) {
     uint64_t exec_ticks = rdtsc();
-    uint64_t freq = tsc_freq();
-    return freq != 0 ? 1000000ULL * exec_ticks / freq : 0;
+    return tsc_freq != 0 ? 1000000ULL * exec_ticks / tsc_freq : 0;
 }
 
-static inline void delay(uint64_t cycles) {
-    uint64_t next_stop = rdtsc() + cycles;
-
+static inline void stall(uint64_t us) {
+#if defined(BIOS)
+    if (tsc_freq == 0) {
+        // ~1 us per inb on ISA/LPC bus
+        for (uint64_t i = 0; i < us; i++) {
+            inb(0x80);
+        }
+        return;
+    }
+#endif
+    uint64_t ticks = tsc_freq / 1000000 * us;
+    uint64_t next_stop = rdtsc() + ticks;
     while (rdtsc() < next_stop);
 }
 
diff --git a/common/sys/cpu.s2.c b/common/sys/cpu.s2.c
new file mode 100644
index 00000000..d4d6ec89
--- /dev/null
+++ b/common/sys/cpu.s2.c
@@ -0,0 +1,26 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/cpu.h>
+#if defined(UEFI)
+#include <efi.h>
+#include <lib/misc.h>
+#endif
+
+uint64_t tsc_freq = 0;
+
+void calibrate_tsc(void) {
+    tsc_freq = tsc_freq_arch();
+    if (tsc_freq != 0) {
+        return;
+    }
+
+#if defined(UEFI)
+    uint64_t tsc_start = rdtsc();
+    gBS->Stall(1000);
+    uint64_t tsc_end = rdtsc();
+
+    if (tsc_end > tsc_start) {
+        tsc_freq = (tsc_end - tsc_start) * 1000ULL;
+    }
+#endif
+}
diff --git a/common/sys/cpu_riscv.c b/common/sys/cpu_riscv.c
index 93daf154..1491146e 100644
--- a/common/sys/cpu_riscv.c
+++ b/common/sys/cpu_riscv.c
@@ -314,6 +314,10 @@ void init_riscv(const char *config) {
         }
     }
 
+    if (cached_time_base_freq != 0) {
+        tsc_freq = cached_time_base_freq;
+    }
+
     if (bsp_hart == NULL) {
         panic(false, "riscv: missing `struct riscv_hart` for BSP");
     }
diff --git a/common/sys/idt.c b/common/sys/idt.c
index 60391112..4669cf93 100644
--- a/common/sys/idt.c
+++ b/common/sys/idt.c
@@ -61,7 +61,7 @@ void flush_irqs(void) {
     asm volatile ("sti" ::: "memory");
 
     // Delay a while to make sure we catch ALL pending IRQs
-    delay(10000000);
+    stall(10000);
 
     asm volatile ("cli" ::: "memory");
 
diff --git a/common/sys/smp.c b/common/sys/smp.c
index e3519be2..6c021b6c 100644
--- a/common/sys/smp.c
+++ b/common/sys/smp.c
@@ -82,7 +82,7 @@ static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr,
         lapic_write(LAPIC_REG_ICR1, lapic_id << 24);
         lapic_write(LAPIC_REG_ICR0, 0x4500);
     }
-    delay(10000000);
+    stall(10000);
 
     // Send two Startup IPIs per Intel SDM recommendation (Vol 3, 8.4.4.1)
     for (int j = 0; j < 2; j++) {
@@ -93,14 +93,14 @@ static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr,
             lapic_write(LAPIC_REG_ICR1, lapic_id << 24);
             lapic_write(LAPIC_REG_ICR0, ((size_t)trampoline / 4096) | 0x4600);
         }
-        delay(200000); // ~200 us
+        stall(200);
     }
 
     for (int i = 0; i < 100; i++) {
         if (locked_read(&passed_info->smp_tpl_booted_flag) == 1) {
             return true;
         }
-        delay(10000000);
+        stall(10000);
     }
 
     return false;
@@ -423,7 +423,7 @@ static bool try_start_ap(int boot_method, uint64_t method_ptr,
         if (locked_read(&passed_info->smp_tpl_booted_flag) == 1) {
             return true;
         }
-        delay(100000);
+        stall(100);
     }
 
     return false;
@@ -804,7 +804,7 @@ static bool smp_start_ap(size_t hartid, size_t satp, struct limine_mp_info *info
         if (locked_read(&passed_info.smp_tpl_booted_flag) == 1)
             return true;
 
-        delay(100000);
+        stall(100);
     }
 
     return false;
tab: 248 wrap: offon