Merge pull request #1985 from CookiePLMonster/fix-rdtsc-overflow

Make RDTSC and ACPI timers stateful to fix overflows
This commit is contained in:
Luke Usher 2020-10-13 19:16:29 +01:00 committed by GitHub
commit efe42f4eba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 47 additions and 26 deletions

View File

@ -324,43 +324,64 @@ void InitDpcThread()
g_DpcData.DpcEvent = CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE, /*bInitialState=*/FALSE, /*lpName=*/nullptr); g_DpcData.DpcEvent = CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE, /*bInitialState=*/FALSE, /*lpName=*/nullptr);
} }
#define XBOX_TSC_FREQUENCY 733333333 // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock) static constexpr uint32_t XBOX_TSC_FREQUENCY = 733333333; // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock)
#define XBOX_ACPI_FREQUENCY 3375000 // Xbox ACPI frequency (3.375 mhz) static constexpr uint32_t XBOX_ACPI_FREQUENCY = 3375000; // Xbox ACPI frequency (3.375 mhz)
ULONGLONG NativeToXbox_FactorForRdtsc; static constexpr uint32_t SEC_TO_NSEC = 1000000000; // For seconds -> nanoseconds conversions
ULONGLONG NativeToXbox_FactorForAcpi; static uint64_t NativeToXbox_FactorForRdtsc = 0, NativeToXbox_FactorForAcpi = 0;
ULONGLONG CxbxGetPerformanceCounter(bool acpi) { // State for CxbxGetPerformanceCounter - concurrent access should be next to non-existent, but secure against it anyway
LARGE_INTEGER tsc; static std::mutex RdtscLock, AcpiLock;
ULARGE_INTEGER scaledTsc; static LARGE_INTEGER LastRdtscQPC, LastAcpiQPC;
static ULONGLONG CurrentRdtsc = 0, CurrentAcpi = 0;
static ULONGLONG CurrentRdtscRemainder = 0, CurrentAcpiRemainder = 0;
QueryPerformanceCounter(&tsc); ULONGLONG CxbxGetPerformanceCounter(bool acpi)
{
if (acpi == false && NativeToXbox_FactorForRdtsc != 0) {
std::lock_guard<std::mutex> lock(RdtscLock);
scaledTsc.QuadPart = 1000000000; LARGE_INTEGER tsc;
scaledTsc.QuadPart *= (ULONGLONG)tsc.QuadPart; QueryPerformanceCounter(&tsc);
if (acpi == false && NativeToXbox_FactorForRdtsc) { LARGE_INTEGER lastTsc = std::exchange(LastRdtscQPC, tsc);
scaledTsc.QuadPart /= NativeToXbox_FactorForRdtsc; tsc.QuadPart -= lastTsc.QuadPart;
return scaledTsc.QuadPart; tsc.QuadPart *= SEC_TO_NSEC;
} else if (acpi == true && NativeToXbox_FactorForRdtsc) { tsc.QuadPart += CurrentRdtscRemainder;
scaledTsc.QuadPart /= NativeToXbox_FactorForAcpi; ULONGLONG quotient = tsc.QuadPart / NativeToXbox_FactorForRdtsc;
return scaledTsc.QuadPart; ULONGLONG remainder = tsc.QuadPart % NativeToXbox_FactorForRdtsc;
CurrentRdtscRemainder = remainder;
return CurrentRdtsc += quotient;
} else if (acpi == true && NativeToXbox_FactorForAcpi != 0) {
std::lock_guard<std::mutex> lock(AcpiLock);
LARGE_INTEGER tsc;
QueryPerformanceCounter(&tsc);
LARGE_INTEGER lastTsc = std::exchange(LastAcpiQPC, tsc);
tsc.QuadPart -= lastTsc.QuadPart;
tsc.QuadPart *= SEC_TO_NSEC;
tsc.QuadPart += CurrentAcpiRemainder;
ULONGLONG quotient = tsc.QuadPart / NativeToXbox_FactorForAcpi;
ULONGLONG remainder = tsc.QuadPart % NativeToXbox_FactorForAcpi;
CurrentAcpiRemainder = remainder;
return CurrentAcpi += quotient;
} }
return (uint64_t)tsc.QuadPart; LARGE_INTEGER tsc;
QueryPerformanceCounter(&tsc);
return static_cast<ULONGLONG>(tsc.QuadPart);
} }
void CxbxInitPerformanceCounters() void CxbxInitPerformanceCounters()
{ {
uint64_t t; NativeToXbox_FactorForRdtsc = Muldiv64(HostClockFrequency, SEC_TO_NSEC, XBOX_TSC_FREQUENCY);
t = 1000000000; NativeToXbox_FactorForAcpi = Muldiv64(HostClockFrequency, SEC_TO_NSEC, XBOX_ACPI_FREQUENCY);
t *= HostClockFrequency;
t /= XBOX_TSC_FREQUENCY;
NativeToXbox_FactorForRdtsc = t;
t = 1000000000; LARGE_INTEGER tsc;
t *= HostClockFrequency; QueryPerformanceCounter(&tsc);
t /= XBOX_ACPI_FREQUENCY; LastRdtscQPC = LastAcpiQPC = tsc;
NativeToXbox_FactorForAcpi = t;
// Let's initialize the Dpc handling thread too, // Let's initialize the Dpc handling thread too,
// here for now (should be called by our caller) // here for now (should be called by our caller)