Updated KeInterruptTime, KeSystemTime, KeTickCount and timer handling

This commit is contained in:
ergo720 2019-01-08 09:48:17 +01:00
parent 62dfa7b4e2
commit 1ae67fb215
7 changed files with 27 additions and 147 deletions

View File

@ -249,7 +249,7 @@ XBSYSAPI EXPORTNUM(119) BOOLEAN NTAPI KeInsertQueueDpc
// ******************************************************************
// * 0x0078 - KeInterruptTime
// ******************************************************************
XBSYSAPI EXPORTNUM(120) PKSYSTEM_TIME KeInterruptTime;
XBSYSAPI EXPORTNUM(120) KSYSTEM_TIME KeInterruptTime;
// ******************************************************************
// * 0x0079 - KeIsExecutingDpc()
@ -490,7 +490,7 @@ XBSYSAPI EXPORTNUM(153) BOOLEAN NTAPI KeSynchronizeExecution
// ******************************************************************
// * 0x009A - KeSystemTime
// ******************************************************************
XBSYSAPI EXPORTNUM(154) PKSYSTEM_TIME KeSystemTime;
XBSYSAPI EXPORTNUM(154) KSYSTEM_TIME KeSystemTime;
// ******************************************************************
// * 0x009B - KeTestAlertThread()

View File

@ -154,7 +154,7 @@ void RestoreInterruptMode(bool value)
g_bInterruptsEnabled = value;
}
extern DWORD ExecuteDpcQueue();
extern void ExecuteDpcQueue();
void KiUnexpectedInterrupt()
{

View File

@ -71,10 +71,9 @@ typedef struct _DpcData {
CRITICAL_SECTION Lock;
HANDLE DpcEvent;
xboxkrnl::LIST_ENTRY DpcQueue; // TODO : Use KeGetCurrentPrcb()->DpcListHead instead
xboxkrnl::LIST_ENTRY TimerQueue;
} DpcData;
DpcData g_DpcData = { 0 }; // Note : g_DpcData is initialized in InitDpcAndTimerThread()
DpcData g_DpcData = { 0 }; // Note : g_DpcData is initialized in InitDpcThread()
xboxkrnl::ULONGLONG LARGE_INTEGER2ULONGLONG(xboxkrnl::LARGE_INTEGER value)
{
@ -220,23 +219,9 @@ xboxkrnl::KPRCB *KeGetCurrentPrcb()
#define KeRaiseIrql(NewIrql, OldIrql) \
*(OldIrql) = KfRaiseIrql(NewIrql)
ULONGLONG BootTickCount = 0;
// The Xbox GetTickCount is measured in milliseconds, just like the native GetTickCount.
// The only difference we'll take into account here, is that the Xbox will probably reboot
// much more often than Windows, so we correct this with a 'BootTickCount' value :
DWORD CxbxXboxGetTickCount()
{
return (DWORD)(GetTickCount64() - BootTickCount);
}
DWORD ExecuteDpcQueue()
void ExecuteDpcQueue()
{
xboxkrnl::PKDPC pkdpc;
DWORD dwWait;
DWORD dwNow;
LONG lWait;
xboxkrnl::PKTIMER pktimer;
// While we're working with the DpcQueue, we need to be thread-safe :
EnterCriticalSection(&(g_DpcData.Lock));
@ -274,64 +259,18 @@ DWORD ExecuteDpcQueue()
KeGetCurrentPrcb()->DpcRoutineActive = FALSE; // Experimental
}
dwWait = INFINITE;
if (!IsListEmpty(&(g_DpcData.TimerQueue)))
{
while (true)
{
dwNow = CxbxXboxGetTickCount();
dwWait = INFINITE;
pktimer = (xboxkrnl::PKTIMER)g_DpcData.TimerQueue.Flink;
pkdpc = nullptr;
while (pktimer != (xboxkrnl::PKTIMER)&(g_DpcData.TimerQueue))
{
lWait = (LONG)pktimer->DueTime.u.LowPart - dwNow;
if (lWait <= 0)
{
pktimer->DueTime.u.LowPart = pktimer->Period + dwNow;
pkdpc = pktimer->Dpc;
break; // while
}
if (dwWait > (DWORD)lWait)
dwWait = (DWORD)lWait;
pktimer = (xboxkrnl::PKTIMER)pktimer->TimerListEntry.Flink;
}
if (pkdpc == nullptr)
break; // while
DBG_PRINTF("Global TimerQueue, calling DPC at 0x%.8X\n", pkdpc->DeferredRoutine);
__try {
pkdpc->DeferredRoutine(
pkdpc,
pkdpc->DeferredContext,
pkdpc->SystemArgument1,
pkdpc->SystemArgument2);
} __except (EmuException(GetExceptionInformation()))
{
EmuLog(LOG_LEVEL::WARNING, "Problem with ExceptionFilter!");
}
}
}
// Assert(g_DpcData._dwThreadId == GetCurrentThreadId());
// Assert(g_DpcData._dwDpcThreadId == g_DpcData._dwThreadId);
// g_DpcData._dwDpcThreadId = 0;
LeaveCriticalSection(&(g_DpcData.Lock));
return dwWait;
}
void InitDpcAndTimerThread()
void InitDpcThread()
{
DWORD dwThreadId = 0;
InitializeCriticalSection(&(g_DpcData.Lock));
InitializeListHead(&(g_DpcData.DpcQueue));
InitializeListHead(&(g_DpcData.TimerQueue));
DBG_PRINTF_EX(CXBXR_MODULE::INIT, "Creating DPC event\n");
g_DpcData.DpcEvent = CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE, /*bInitialState=*/FALSE, /*lpName=*/nullptr);
@ -342,8 +281,6 @@ void InitDpcAndTimerThread()
ULONGLONG NativeToXbox_FactorForRdtsc;
ULONGLONG NativeToXbox_FactorForAcpi;
void ConnectKeInterruptTimeToThunkTable(); // forward
ULONGLONG CxbxGetPerformanceCounter(bool acpi) {
LARGE_INTEGER tsc;
ULARGE_INTEGER scaledTsc;
@ -377,11 +314,9 @@ void CxbxInitPerformanceCounters()
t /= XBOX_ACPI_FREQUENCY;
NativeToXbox_FactorForAcpi = t;
ConnectKeInterruptTimeToThunkTable();
// Let's initialize the Dpc and timer handling thread too,
// Let's initialize the Dpc handling thread too,
// here for now (should be called by our caller)
InitDpcAndTimerThread();
InitDpcThread();
}
// ******************************************************************
@ -1107,14 +1042,7 @@ XBSYSAPI EXPORTNUM(121) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeIsExecutingDpc
// ******************************************************************
// * 0x0078 - KeInterruptTime
// ******************************************************************
// Dxbx note : This was once a value, but instead we now point to
// the native Windows versions (see ConnectWindowsTimersToThunkTable) :
XBSYSAPI EXPORTNUM(120) xboxkrnl::PKSYSTEM_TIME xboxkrnl::KeInterruptTime = nullptr; // Set by ConnectKeInterruptTimeToThunkTable
void ConnectKeInterruptTimeToThunkTable()
{
xboxkrnl::KeInterruptTime = (xboxkrnl::PKSYSTEM_TIME)CxbxKrnl_KernelThunkTable[120];
}
XBSYSAPI EXPORTNUM(120) xboxkrnl::KSYSTEM_TIME xboxkrnl::KeInterruptTime = { 0, 0, 0 };
// ******************************************************************
// * 0x007A - KeLeaveCriticalRegion()
@ -1204,12 +1132,12 @@ XBSYSAPI EXPORTNUM(125) xboxkrnl::ULONGLONG NTAPI xboxkrnl::KeQueryInterruptTime
{
// Don't use NtDll::QueryInterruptTime, it's too new (Windows 10).
// Instead, read KeInterruptTime from our kernel thunk table.
InterruptTime.u.HighPart = KeInterruptTime->High1Time;
InterruptTime.u.LowPart = KeInterruptTime->LowPart;
InterruptTime.u.HighPart = KeInterruptTime.High1Time;
InterruptTime.u.LowPart = KeInterruptTime.LowPart;
// Read InterruptTime atomically with a spinloop to avoid errors
// when High1Time and High2Time differ (during unprocessed overflow in LowPart).
if (InterruptTime.u.HighPart == KeInterruptTime->High2Time)
if (InterruptTime.u.HighPart == KeInterruptTime.High2Time)
break;
}
@ -1254,12 +1182,12 @@ XBSYSAPI EXPORTNUM(128) xboxkrnl::VOID NTAPI xboxkrnl::KeQuerySystemTime
while (true)
{
SystemTime.u.HighPart = KeSystemTime->High1Time;
SystemTime.u.LowPart = KeSystemTime->LowPart;
SystemTime.u.HighPart = KeSystemTime.High1Time;
SystemTime.u.LowPart = KeSystemTime.LowPart;
// Read InterruptTime atomically with a spinloop to avoid errors
// when High1Time and High2Time differ (during unprocessed overflow in LowPart).
if (SystemTime.u.HighPart == KeSystemTime->High2Time)
if (SystemTime.u.HighPart == KeSystemTime.High2Time)
break;
}
@ -1858,9 +1786,7 @@ XBSYSAPI EXPORTNUM(153) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSynchronizeExecution
// ******************************************************************
// * 0x009A - KeSystemTime
// ******************************************************************
// Dxbx note : This was once a value, but instead we now point to
// the native Windows versions (see ConnectWindowsTimersToThunkTable) :
// XBSYSAPI EXPORTNUM(154) xboxkrnl::PKSYSTEM_TIME xboxkrnl::KeSystemTime; // Used for KernelThunk[154]
// XBSYSAPI EXPORTNUM(154) xboxkrnl::KSYSTEM_TIME xboxkrnl::KeSystemTime; // Used for KernelThunk[154]
// ******************************************************************
// * 0x009B - KeTestAlertThread()

View File

@ -72,26 +72,26 @@ VOID xboxkrnl::KiClockIsr(unsigned int ScalingFactor)
OldIrql = KfRaiseIrql(CLOCK_LEVEL);
// Update the interrupt time
InterruptTime.u.LowPart = KeInterruptTime->LowPart;
InterruptTime.u.HighPart = KeInterruptTime->High1Time;
InterruptTime.u.LowPart = KeInterruptTime.LowPart;
InterruptTime.u.HighPart = KeInterruptTime.High1Time;
InterruptTime.QuadPart += (CLOCK_TIME_INCREMENT * ScalingFactor);
KeInterruptTime->High2Time = InterruptTime.u.HighPart;
KeInterruptTime->LowPart = InterruptTime.u.LowPart;
KeInterruptTime->High1Time = InterruptTime.u.HighPart;
KeInterruptTime.High2Time = InterruptTime.u.HighPart;
KeInterruptTime.LowPart = InterruptTime.u.LowPart;
KeInterruptTime.High1Time = InterruptTime.u.HighPart;
// Update the system time
// NOTE: I'm not sure if we should round down the host system time to the nearest multiple
// of the Xbox clock increment...
GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime);
HostSystemTime.QuadPart += HostSystemTimeDelta.QuadPart;
KeSystemTime->High2Time = HostSystemTime.u.HighPart;
KeSystemTime->LowPart = HostSystemTime.u.LowPart;
KeSystemTime->High1Time = HostSystemTime.u.HighPart;
KeSystemTime.High2Time = HostSystemTime.u.HighPart;
KeSystemTime.LowPart = HostSystemTime.u.LowPart;
KeSystemTime.High1Time = HostSystemTime.u.HighPart;
// Update the tick counter
KeTickCount += ScalingFactor;
// Check if a timer have expired
// Check if a timer has expired
Hand = KeTickCount & (TIMER_TABLE_SIZE - 1);
if (KiTimerTableListHead[Hand].Entry.Flink != &KiTimerTableListHead[Hand].Entry &&
InterruptTime.QuadPart >= KiTimerTableListHead[Hand].Time.QuadPart) {

View File

@ -175,7 +175,7 @@ uint32 CxbxKrnl_KernelThunkTable[379] =
(uint32)FUNC(&xboxkrnl::KeInsertQueue), // 0x0075 (117)
(uint32)FUNC(&xboxkrnl::KeInsertQueueApc), // 0x0076 (118)
(uint32)FUNC(&xboxkrnl::KeInsertQueueDpc), // 0x0077 (119)
(uint32)VARIABLE(0x0078), // 0x0078 (120) KeInterruptTime (Set by ConnectWindowsTimersToThunkTable)
(uint32)VARIABLE(&xboxkrnl::KeInterruptTime), // 0x0078 (120) KeInterruptTime
(uint32)FUNC(&xboxkrnl::KeIsExecutingDpc), // 0x0079 (121)
(uint32)FUNC(&xboxkrnl::KeLeaveCriticalRegion), // 0x007A (122)
(uint32)FUNC(&xboxkrnl::KePulseEvent), // 0x007B (123)
@ -209,7 +209,7 @@ uint32 CxbxKrnl_KernelThunkTable[379] =
(uint32)FUNC(&xboxkrnl::KeStallExecutionProcessor), // 0x0097 (151)
(uint32)FUNC(&xboxkrnl::KeSuspendThread), // 0x0098 (152)
(uint32)FUNC(&xboxkrnl::KeSynchronizeExecution), // 0x0099 (153)
(uint32)VARIABLE(0x009A), // 0x009A (154) KeSystemTime (Set by ConnectWindowsTimersToThunkTable)
(uint32)VARIABLE(&xboxkrnl::KeSystemTime), // 0x009A (154) KeSystemTime
(uint32)FUNC(&xboxkrnl::KeTestAlertThread), // 0x009B (155)
(uint32)VARIABLE(&xboxkrnl::KeTickCount), // 0x009C (156)
(uint32)VARIABLE(&xboxkrnl::KeTimeIncrement), // 0x009D (157)
@ -435,44 +435,3 @@ uint32 CxbxKrnl_KernelThunkTable[379] =
(uint32)FUNC(&xboxkrnl::MmDbgReleaseAddress), // 0x0179 (377) DEVKIT ONLY!
(uint32)FUNC(&xboxkrnl::MmDbgWriteCheck), // 0x017A (378) DEVKIT ONLY!
};
/* prevent name collisions */
namespace NtDll
{
#include "core\kernel\support\EmuNtDll.h"
};
// Virtual memory location of KUSER_SHARED_DATA :
// See http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/base/md/i386/sim/_pertest2.c.htm
// and http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/base/md/i386/sim/_glue.c.htm
// and http://processhacker.sourceforge.net/doc/ntexapi_8h_source.html
// and http://forum.sysinternals.com/0x7ffe0000-what-is-in-it_topic10012.html
#define MM_SHARED_USER_DATA_VA 0x7FFE0000
#define USER_SHARED_DATA ((NtDll::KUSER_SHARED_DATA * const)MM_SHARED_USER_DATA_VA)
// KUSER_SHARED_DATA Offsets
// See http://native-nt-toolkit.googlecode.com/svn/trunk/ndk/asm.h
// Note : KUSER_SHARED_DATA.TickCountLow seems deprecated
const UINT USER_SHARED_DATA_TICK_COUNT = 0x320;
// Here we define the addresses of the native Windows timers :
// Source: Dxbx
const xboxkrnl::PKSYSTEM_TIME CxbxNtTickCount = (xboxkrnl::PKSYSTEM_TIME)(MM_SHARED_USER_DATA_VA + USER_SHARED_DATA_TICK_COUNT);
void ConnectWindowsTimersToThunkTable()
{
// Couple the xbox thunks for xboxkrnl::KeInterruptTime and xboxkrnl::KeSystemTime
// to their actual counterparts on Windows, this way we won't have to spend any
// time on updating them ourselves, and still get highly accurate timers!
// See http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/
// Point Xbox KeInterruptTime to host InterruptTime:
CxbxKrnl_KernelThunkTable[120] = (uint32)&(USER_SHARED_DATA->InterruptTime);
// Point Xbox KeSystemTime to host SystemTime; If read directly (thus skipping
// KeQuerySystemTime), this value is not adjusted with HostSystemTimeDelta!
CxbxKrnl_KernelThunkTable[154] = (uint32)&(USER_SHARED_DATA->SystemTime);
// We can't point Xbox KeTickCount to host TickCount, because it
// updates slower on the xbox. See EmuUpdateTickCount().
}

View File

@ -1248,9 +1248,6 @@ void CxbxKrnlMain(int argc, char* argv[])
RestoreExeImageHeader();
}
// Before readout, make sure our kernel thunk table references the Windows host timer addresses :
ConnectWindowsTimersToThunkTable();
// Decode kernel thunk table address :
uint32_t kt = CxbxKrnl_Xbe->m_Header.dwKernelImageThunkAddr;
kt ^= XOR_KT_KEY[g_XbeType];

View File

@ -266,8 +266,6 @@ void CxbxInitPerformanceCounters(); // Implemented in EmuKrnlKe.cpp
void CxbxInitFilePaths();
void ConnectWindowsTimersToThunkTable();
/*! Generate a standard arg format string */
void CxbxConvertArgToString(std::string &dest, const char* krnlExe, const char* xbeFile, HWND hwndParent, DebugMode krnlDebug, const char* krnlDebugFile);