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;
