From 281ad05dbbcdf7841c2b9855d77c2d45a77de95e Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Tue, 18 Jun 2024 20:05:44 +0600 Subject: [PATCH] Make sure timers don't go completely out of sync upon altering TSC via WRMSR --- src/cpu/cpu.c | 13 +++++++------ src/include/86box/timer.h | 3 +++ src/timer.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 20476eec9ed..0ff54f61c37 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -40,6 +40,7 @@ #include <86box/nmi.h> #include <86box/pic.h> #include <86box/pci.h> +#include <86box/timer.h> #include <86box/gdbstub.h> #include <86box/plat_fallthrough.h> #include <86box/plat_unused.h> @@ -3492,7 +3493,7 @@ cpu_WRMSR(void) break; /* Time Stamp Counter */ case 0x10: - tsc = EAX | ((uint64_t) EDX << 32); + timer_set_new_tsc(EAX | ((uint64_t) EDX << 32)); break; /* Performance Monitor - Control and Event Select */ case 0x11: @@ -3568,7 +3569,7 @@ cpu_WRMSR(void) break; /* Time Stamp Counter */ case 0x10: - tsc = EAX | ((uint64_t) EDX << 32); + timer_set_new_tsc(EAX | ((uint64_t) EDX << 32)); break; /* PERFCTR0 - Performance Counter Register 0 - aliased to TSC */ case 0xc1: @@ -3664,7 +3665,7 @@ cpu_WRMSR(void) break; /* Time Stamp Counter */ case 0x00000010: - tsc = EAX | ((uint64_t) EDX << 32); + timer_set_new_tsc(EAX | ((uint64_t) EDX << 32)); break; /* Array Access Register */ case 0x00000082: @@ -3834,7 +3835,7 @@ cpu_WRMSR(void) /* Time Stamp Counter */ case 0x00000010: case 0x80000010: - tsc = EAX | ((uint64_t) EDX << 32); + timer_set_new_tsc(EAX | ((uint64_t) EDX << 32)); break; /* Performance Monitor - Control and Event Select */ case 0x00000011: @@ -3919,7 +3920,7 @@ cpu_WRMSR(void) msr.tr5 = EAX & 0x008f0f3b; /* Time Stamp Counter */ case 0x10: - tsc = EAX | ((uint64_t) EDX << 32); + timer_set_new_tsc(EAX | ((uint64_t) EDX << 32)); break; /* Performance Monitor - Control and Event Select */ case 0x11: @@ -3952,7 +3953,7 @@ cpu_WRMSR(void) break; /* Time Stamp Counter */ case 0x10: - tsc = EAX | ((uint64_t) EDX << 32); + timer_set_new_tsc(EAX | ((uint64_t) EDX << 32)); break; /* Unknown */ case 0x18: diff --git a/src/include/86box/timer.h b/src/include/86box/timer.h index 91f903a0fb8..25aff6b2fb4 100644 --- a/src/include/86box/timer.h +++ b/src/include/86box/timer.h @@ -185,6 +185,9 @@ timer_set_p(pc_timer_t *timer, void *priv) extern void timer_stop(pc_timer_t *timer); extern void timer_on_auto(pc_timer_t *timer, double period); +/* Change TSC, taking into account the timers. */ +extern void timer_set_new_tsc(uint64_t new_tsc); + #ifdef __cplusplus } #endif diff --git a/src/timer.c b/src/timer.c index d7102ffc3cd..c99880989a1 100644 --- a/src/timer.c +++ b/src/timer.c @@ -253,3 +253,32 @@ timer_on_auto(pc_timer_t *timer, double period) else timer_stop(timer); } + +void +timer_set_new_tsc(uint64_t new_tsc) +{ + pc_timer_t *timer = NULL; + uint32_t offset = 0; + /* Run timers already expired. */ +#ifdef USE_DYNAREC + if (cpu_use_dynarec) + update_tsc(); +#endif + + if (!timer_head) { + tsc = new_tsc; + return; + } + + timer = timer_head; + timer_target = new_tsc + (int32_t)(timer_get_ts_int(timer_head) - (uint32_t)tsc); + + while (timer) { + int32_t offset_from_current_tsc = (int32_t)(timer_get_ts_int(timer) - (uint32_t)tsc); + timer->ts.ts32.integer = new_tsc + offset_from_current_tsc; + + timer = timer->next; + } + + tsc = new_tsc; +}