diff --git a/src/core/kernel/exports/EmuKrnl.cpp b/src/core/kernel/exports/EmuKrnl.cpp index a53220c83..6934cd1b5 100644 --- a/src/core/kernel/exports/EmuKrnl.cpp +++ b/src/core/kernel/exports/EmuKrnl.cpp @@ -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); } diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 45a3a521a..3300b8587 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -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); } diff --git a/src/core/kernel/init/CxbxKrnl.h b/src/core/kernel/init/CxbxKrnl.h index 5f89fb5b0..c11af39cc 100644 --- a/src/core/kernel/init/CxbxKrnl.h +++ b/src/core/kernel/init/CxbxKrnl.h @@ -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];