Fixed dpc recursion bug

This commit is contained in:
ergo720 2023-03-02 14:59:10 +01:00
parent 58041c95b4
commit 99ab34ac82
3 changed files with 25 additions and 4 deletions

View File

@ -158,7 +158,10 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql)
xbox::KiExecuteKernelApc();
break;
case DISPATCH_LEVEL: // = 2
ExecuteDpcQueue();
// This can be recursively called by KiUnlockDispatcherDatabase and KfLowerIrql, so avoid calling DPCs again if the current one has queued yet another one
if (!IsDpcActive()) { // Avoid KeIsExecutingDpc(), as that logs
ExecuteDpcQueue();
}
break;
case APC_LEVEL | DISPATCH_LEVEL: // = 3
KiUnexpectedInterrupt();
@ -450,7 +453,8 @@ XBSYSAPI EXPORTNUM(163) xbox::void_xt FASTCALL xbox::KiUnlockDispatcherDatabase
LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, OldIrql);
// Wrong, this should only happen when OldIrql >= DISPATCH_LEVEL
if (!(KeGetCurrentPrcb()->DpcRoutineActive)) { // Avoid KeIsExecutingDpc(), as that logs
// Checking DpcRoutineActive doesn't work because our Prcb is per-thread instead of being per-processor
if (!IsDpcActive()) { // Avoid KeIsExecutingDpc(), as that logs
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}

View File

@ -96,6 +96,7 @@ namespace NtDll
// TODO : Move towards thread-simulation based Dpc emulation
typedef struct _DpcData {
CRITICAL_SECTION Lock;
std::atomic_flag IsDpcActive;
xbox::LIST_ENTRY DpcQueue; // TODO : Use KeGetCurrentPrcb()->DpcListHead instead
} DpcData;
@ -459,8 +460,10 @@ void ExecuteDpcQueue()
// Mark it as no longer linked into the DpcQueue
pkdpc->Inserted = FALSE;
// Set DpcRoutineActive to support KeIsExecutingDpc:
g_DpcData.IsDpcActive.test_and_set();
KeGetCurrentPrcb()->DpcRoutineActive = TRUE; // Experimental
EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC at 0x%.8X", pkdpc->DeferredRoutine);
LeaveCriticalSection(&(g_DpcData.Lock));
EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC object 0x%.8X at 0x%.8X", pkdpc, pkdpc->DeferredRoutine);
// Call the Deferred Procedure :
pkdpc->DeferredRoutine(
@ -469,7 +472,9 @@ void ExecuteDpcQueue()
pkdpc->SystemArgument1,
pkdpc->SystemArgument2);
EnterCriticalSection(&(g_DpcData.Lock));
KeGetCurrentPrcb()->DpcRoutineActive = FALSE; // Experimental
g_DpcData.IsDpcActive.clear();
}
// Assert(g_DpcData._dwThreadId == GetCurrentThreadId());
@ -486,6 +491,11 @@ void InitDpcData()
InitializeListHead(&(g_DpcData.DpcQueue));
}
bool IsDpcActive()
{
return g_DpcData.IsDpcActive.test();
}
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)
@ -1258,7 +1268,9 @@ XBSYSAPI EXPORTNUM(119) xbox::boolean_xt NTAPI xbox::KeInsertQueueDpc
InsertTailList(&(g_DpcData.DpcQueue), &(Dpc->DpcListEntry));
// TODO : Instead of DpcQueue, add the DPC to KeGetCurrentPrcb()->DpcListHead
// Signal the Dpc handling code there's work to do
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
if (g_DpcData.IsDpcActive.test() == false) {
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
// OpenXbox has this instead:
// if (!pKPRCB->DpcRoutineActive && !pKPRCB->DpcInterruptRequested) {
// pKPRCB->DpcInterruptRequested = TRUE;
@ -1279,7 +1291,11 @@ XBSYSAPI EXPORTNUM(121) xbox::boolean_xt NTAPI xbox::KeIsExecutingDpc
{
LOG_FUNC();
// This is the correct implementation, but it doesn't work because our Prcb is per-thread instead of being per-processor
#if 0
BOOLEAN ret = (BOOLEAN)KeGetCurrentPrcb()->DpcRoutineActive;
#endif
BOOLEAN ret = (BOOLEAN)IsDpcActive();
RETURN(ret);
}

View File

@ -156,6 +156,7 @@ void CxbxKrnlPanic();
void CxbxKrnlNoFunc();
void InitDpcData(); // Implemented in EmuKrnlKe.cpp
bool IsDpcActive();
/*! kernel thunk table */
extern uint32_t CxbxKrnl_KernelThunkTable[379];