bochs-emu / Bochs

Bochs - Cross Platform x86 Emulator Project
https://bochs.sourceforge.io/
GNU Lesser General Public License v2.1
782 stars 94 forks source link

QueryPerformanceCounter goes back in time on Windows XP #196

Open Vort opened 6 months ago

Vort commented 6 months ago

Values from QueryPerformanceCounter function should only advance forward, but in Bochs jumps back happen. I made test program which shows such behaviour: qpctest.zip. This is what it outputs in Bochs on Windows XP: image And this is how output should look like (VirtualBox and real hardware): image image

Version: 326114f4e72a59fa8e3c6fb617510c444e3950ac.

stlintel commented 6 months ago

May be you could attach the source code for this program. The Windows Defender doesn't let me download the test, says it is infected by Troyan

Vort commented 6 months ago
#include <windows.h>
#include <iostream>
#include <iomanip>

int main()
{
    LARGE_INTEGER oldTicks = { 0 };
    LARGE_INTEGER ticks = { 0 };
    QueryPerformanceCounter(&oldTicks);
    for (;;)
    {
        Sleep(1000);
        QueryPerformanceCounter(&ticks);
        std::cout << std::setw(20) << ticks.QuadPart <<
            std::setw(20) << ticks.QuadPart - oldTicks.QuadPart << std::endl;
        oldTicks = ticks;
    }

    return 0;
}
stlintel commented 6 months ago

What timer settings do you use ? Realtime ? Slowdown ? I used to clock: sync=none, time0=local With this setting no issue at all:

image

Vort commented 6 months ago

What timer settings do you use ? Realtime ? Slowdown ?

I tried both slowdown and none - effect is the same. Here is config which I use now: bochsrc_xp_tsc_test.zip.

Problem may be specific to Windows XP. This is what image I was using to install it: ru_windows_xp_professional_with_service_pack_3_x86_cd_vl_x14-74146.iso.

Vort commented 6 months ago

I tested 1 more Windows XP disc (problem appears) and 2 Windows 7 discs - no problem (and lower difference between ticks).

Vort commented 6 months ago

Looks like Windows XP uses ACPI Timer with frequency of 3579545 Hz, while Windows 7 uses frequency tied to IPS value (which probably means use of Timestamp Counter). Article about ACPI timer states that its counter may have 24 bit range. When I subtract values, visible on my first screenshot: 3589865 and -13199701, I get 16789566, which is very close to 2^24 (16777216), maybe this property is related to the bug somehow.

Vort commented 6 months ago

After Windows XP starts, bx_acpi_ctrl_c::set_irq_level(true) gets called only once. Looks like in case of correct timer functioning, interrupts should occur periodically.

Vort commented 6 months ago

Windows XP uses IRQ 9 for ACPI, making such change allows timer to tick properly:

diff --git a/bochs/iodev/acpi.cc b/bochs/iodev/acpi.cc
index 2fa0168d8..43f26dd94 100644
--- a/bochs/iodev/acpi.cc
+++ b/bochs/iodev/acpi.cc
@@ -240,7 +240,8 @@ void bx_acpi_ctrl_c::after_restore_state(void)

 void bx_acpi_ctrl_c::set_irq_level(bool level)
 {
-  DEV_pci_set_irq(BX_ACPI_THIS s.devfunc, BX_ACPI_THIS pci_conf[0x3d], level);
+  if (level)
+    DEV_pic_raise_irq(9);
 }

 Bit32u bx_acpi_ctrl_c::get_pmtmr(void) 

But, of course, this is not proper solution. Just confirmation that problems with QueryPerformanceCounter are related to interrupt processing for Power Management Timer.