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