From a791b7609c4bb0d9177de9449009b13337ee2b01 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Wed, 5 Jan 2022 17:09:32 +0100 Subject: [PATCH] Updated KeDelayExecutionThread, KeSetBasePriorityThread and XAPI thread functions to accept ob handles + more bug fixes --- src/core/hle/XAPI/Xapi.cpp | 60 +++++++++++++-------- src/core/kernel/common/types.h | 2 +- src/core/kernel/exports/EmuKrnl.cpp | 51 ++++++++++++++++++ src/core/kernel/exports/EmuKrnl.h | 2 + src/core/kernel/exports/EmuKrnlKe.cpp | 21 ++++++-- src/core/kernel/exports/EmuKrnlKi.cpp | 2 +- src/core/kernel/exports/EmuKrnlNt.cpp | 68 +++++------------------- src/core/kernel/support/EmuFS.cpp | 17 ++++-- src/core/kernel/support/EmuFS.h | 2 +- src/core/kernel/support/NativeHandle.cpp | 6 +++ 10 files changed, 141 insertions(+), 90 deletions(-) diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index f9997f7eb..8fcf0db35 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -39,6 +39,7 @@ #include "core\kernel\support\Emu.h" #include "core\kernel\exports\EmuKrnl.h" // For DefaultLaunchDataPage #include "core\kernel\support\EmuFile.h" +#include "core\kernel\support\NativeHandle.h" #include "EmuShared.h" #include "core\hle\Intercept.hpp" #include "Windef.h" @@ -911,12 +912,16 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriorityBoost) LOG_FUNC_ARG(DisablePriorityBoost) LOG_FUNC_END; - BOOL bRet = SetThreadPriorityBoost(hThread, DisablePriorityBoost); - - if(bRet == FALSE) - EmuLog(LOG_LEVEL::WARNING, "SetThreadPriorityBoost Failed!"); - - RETURN(bRet); + if (const auto &nativeHandle = GetNativeHandle(hThread)) { + BOOL bRet = SetThreadPriorityBoost(*nativeHandle, DisablePriorityBoost); + if (bRet == FALSE) { + EmuLog(LOG_LEVEL::WARNING, "SetThreadPriorityBoost Failed!"); + } + RETURN(bRet); + } + else { + RETURN(0); + } } // ****************************************************************** @@ -935,12 +940,16 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriority) LOG_FUNC_ARG(nPriority) LOG_FUNC_END; - BOOL bRet = SetThreadPriority(hThread, nPriority); - - if(bRet == FALSE) - EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority Failed!"); - - RETURN(bRet); + if (const auto &nativeHandle = GetNativeHandle(hThread)) { + BOOL bRet = SetThreadPriority(*nativeHandle, nPriority); + if (bRet == FALSE) { + EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority Failed!"); + } + RETURN(bRet); + } + else { + RETURN(0); + } } @@ -956,12 +965,16 @@ int WINAPI xbox::EMUPATCH(GetThreadPriority) LOG_FUNC_ONE_ARG(hThread); - int iRet = GetThreadPriority(hThread); - - if(iRet == THREAD_PRIORITY_ERROR_RETURN) - EmuLog(LOG_LEVEL::WARNING, "GetThreadPriority Failed!"); - - RETURN(iRet); + if (const auto &nativeHandle = GetNativeHandle(hThread)) { + int iRet = GetThreadPriority(*nativeHandle); + if (iRet == THREAD_PRIORITY_ERROR_RETURN) { + EmuLog(LOG_LEVEL::WARNING, "GetThreadPriority Failed!"); + } + RETURN(iRet); + } + else { + RETURN(THREAD_PRIORITY_ERROR_RETURN); + } } // ****************************************************************** @@ -980,9 +993,12 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(GetExitCodeThread) LOG_FUNC_ARG(lpExitCode) LOG_FUNC_END; - BOOL bRet = GetExitCodeThread(hThread, (::LPDWORD)lpExitCode); - - RETURN(bRet); + if (const auto &nativeHandle = GetNativeHandle(hThread)) { + RETURN(GetExitCodeThread(*nativeHandle, (::LPDWORD)lpExitCode)); + } + else { + RETURN(0); + } } // ****************************************************************** @@ -1149,6 +1165,7 @@ xbox::LPVOID WINAPI xbox::EMUPATCH(ConvertThreadToFiber) RETURN(pRet); } +#if 0 // Handled by NtQueueApcThread // ****************************************************************** // * patch: QueueUserAPC // ****************************************************************** @@ -1180,6 +1197,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(QueueUserAPC) RETURN(dwRet); } +#endif #if 0 // Handled by WaitForSingleObject // ****************************************************************** diff --git a/src/core/kernel/common/types.h b/src/core/kernel/common/types.h index b4053728c..380041be3 100644 --- a/src/core/kernel/common/types.h +++ b/src/core/kernel/common/types.h @@ -1513,7 +1513,7 @@ KOBJECTS, *PKOBJECTS; // ****************************************************************** // * PKNORMAL_ROUTINE // ****************************************************************** -typedef void_xt (*PKNORMAL_ROUTINE) +typedef void_xt (NTAPI *PKNORMAL_ROUTINE) ( IN PVOID NormalContext, IN PVOID SystemArgument1, diff --git a/src/core/kernel/exports/EmuKrnl.cpp b/src/core/kernel/exports/EmuKrnl.cpp index 2e09cce6d..73007fe24 100644 --- a/src/core/kernel/exports/EmuKrnl.cpp +++ b/src/core/kernel/exports/EmuKrnl.cpp @@ -29,7 +29,9 @@ #include +#include #include "core\kernel\support\EmuFS.h" +#include "core\kernel\support\NativeHandle.h" #include #include #include @@ -208,6 +210,55 @@ const DWORD IrqlMasks[] = { 0x00000000, // IRQL 31 (HIGH_LEVEL) }; +// This helper function is used to signal NtDll waiting functions that the wait has been satisfied by an xbox user APC +static void WINAPI EndWait(ULONG_PTR Parameter) +{ + // Do nothing +} + +std::future WaitUserApc(xbox::boolean_xt Alertable, xbox::char_xt WaitMode, bool *Exit) +{ + // NOTE: kThread->Alerted is currently never set. When the alerted mechanism is implemented, the alerts should + // also interrupt the wait + xbox::PKPCR Kpcr = EmuKeGetPcr(); + DWORD Id = GetCurrentThreadId(); + + // This new thread must execute APCs in the context of the calling thread + return std::async(std::launch::async, [Kpcr, Alertable, WaitMode, Id, Exit]() { + EmuKeSetPcr(Kpcr); + xbox::PETHREAD eThread = reinterpret_cast(Kpcr->Prcb->CurrentThread); + + while (true) { + xbox::KiApcListMtx.lock(); + bool EmptyKernel = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::KernelMode]); + bool EmptyUser = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::UserMode]); + xbox::KiApcListMtx.unlock(); + if (EmptyKernel == false) { + xbox::KiExecuteKernelApc(); + } + if ((EmptyUser == false) && + (Alertable == TRUE) && + (WaitMode == xbox::UserMode)) { + xbox::KiExecuteUserApc(); + // Queue a native APC to the calling thread to forcefully terminate the wait of the NtDll functions, + // in the case it didn't terminate already + HANDLE nativeHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, Id); + assert(nativeHandle); + [[maybe_unused]] BOOL ret = QueueUserAPC(EndWait, nativeHandle, 0); + assert(ret); + CloseHandle(nativeHandle); + EmuKeSetPcr(nullptr); + return true; + } + Sleep(0); + if (*Exit) { + EmuKeSetPcr(nullptr); + return false; + } + } + }); +} + // ****************************************************************** // * 0x0033 - InterlockedCompareExchange() // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnl.h b/src/core/kernel/exports/EmuKrnl.h index 774551f80..6fcd98c63 100644 --- a/src/core/kernel/exports/EmuKrnl.h +++ b/src/core/kernel/exports/EmuKrnl.h @@ -27,6 +27,7 @@ #include "core\kernel\init\CxbxKrnl.h" #include "core\kernel\support\Emu.h" +#include // CONTAINING_RECORD macro // Gets the value of structure member (field - num1),given the type(MYSTRUCT, in this code) and the List_Entry head(temp, in this code) @@ -103,5 +104,6 @@ extern HalSystemInterrupt HalSystemInterrupts[MAX_BUS_INTERRUPT_LEVEL + 1]; bool DisableInterrupts(); void RestoreInterruptMode(bool value); void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql); +std::future WaitUserApc(xbox::boolean_xt Alertable, xbox::char_xt WaitMode, bool *Exit); #endif diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index e38edf244..3ea1967f7 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -81,6 +81,7 @@ namespace NtDll #include "EmuKrnlKi.h" // For KiRemoveTreeTimer(), KiInsertTreeTimer() #include "EmuKrnlKe.h" #include "core\kernel\support\EmuFile.h" // For IsEmuHandle(), NtStatusToString() +#include "core\kernel\support\NativeHandle.h" #include "Timer.h" #include @@ -565,9 +566,17 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread LOG_FUNC_ARG(Interval) LOG_FUNC_END; - NTSTATUS ret = NtDll::NtDelayExecution(Alertable, (NtDll::LARGE_INTEGER*)Interval); + // Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves + // We can't remove NtDll::NtDelayExecution until all APCs queued by Io are implemented by our kernel as well + // Test case: Metal Slug 3 + bool Exit = false; + auto &fut = WaitUserApc(Alertable, WaitMode, &Exit); - RETURN(ret); + NTSTATUS ret = NtDll::NtDelayExecution(Alertable, (NtDll::LARGE_INTEGER *)Interval); + + Exit = true; + bool result = fut.get(); + return result ? X_STATUS_USER_APC : ret; } // ****************************************************************** @@ -1049,8 +1058,8 @@ XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc else { KiApcListMtx.lock(); InsertTailList(&kThread->ApcState.ApcListHead[Apc->ApcMode], &Apc->ApcListEntry); - KiApcListMtx.unlock(); Apc->Inserted = TRUE; + KiApcListMtx.unlock(); // We can only attempt to execute the queued apc right away if it is been inserted in the current thread, because otherwise the KTHREAD // in the fs selector will not be correct @@ -1631,12 +1640,14 @@ XBSYSAPI EXPORTNUM(143) xbox::long_xt NTAPI xbox::KeSetBasePriorityThread LOG_FUNC_ARG_OUT(Priority) LOG_FUNC_END; - LONG ret = GetThreadPriority((HANDLE)Thread); + // It cannot fail because all thread handles are created by ob + const auto &nativeHandle = GetNativeHandle(reinterpret_cast(Thread)->UniqueThread); + LONG ret = GetThreadPriority(*nativeHandle); // This would work normally, but it will slow down the emulation, // don't do that if the priority is higher then normal (so our own)! if((Priority <= THREAD_PRIORITY_NORMAL) && ((HANDLE)Thread != GetCurrentThread())) { - SetThreadPriority((HANDLE)Thread, Priority); + SetThreadPriority(*nativeHandle, Priority); } RETURN(ret); diff --git a/src/core/kernel/exports/EmuKrnlKi.cpp b/src/core/kernel/exports/EmuKrnlKi.cpp index 7cac92984..30dac5a41 100644 --- a/src/core/kernel/exports/EmuKrnlKi.cpp +++ b/src/core/kernel/exports/EmuKrnlKi.cpp @@ -905,8 +905,8 @@ static xbox::void_xt KiExecuteApc() xbox::PLIST_ENTRY Entry = kThread->ApcState.ApcListHead[ApcMode].Flink; xbox::PKAPC Apc = CONTAINING_RECORD(Entry, xbox::KAPC, ApcListEntry); RemoveEntryList(Entry); - xbox::KiApcListMtx.unlock(); Apc->Inserted = FALSE; + xbox::KiApcListMtx.unlock(); // NOTE: we never use KernelRoutine if (Apc->NormalRoutine != xbox::zeroptr) { diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index a61a88d70..d91ceefb1 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -56,17 +56,10 @@ namespace NtDll #include #include -#include // Prevent setting the system time from multiple threads at the same time std::mutex NtSystemTimeMtx; -// This helper function is used to signal NtDll::NtWaitForMultipleObjects that the wait has been satisfied by an xbox user APC -static void WINAPI EndWait(ULONG_PTR Parameter) -{ - // Do nothing -} - // ****************************************************************** // * 0x00B8 - NtAllocateVirtualMemory() // ****************************************************************** @@ -718,17 +711,7 @@ XBSYSAPI EXPORTNUM(197) xbox::ntstatus_xt NTAPI xbox::NtDuplicateObject const ULONG Attributes = 0; const ULONG nativeOptions = (Options | DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS); - // If SourceHandle is -2 = NtCurrentThread, then we need to duplicate the handle of this thread - // Test case: Metal Slug 3 - std::optional nativeHandle; - if (SourceHandle == NtCurrentThread()) { - nativeHandle = GetNativeHandle(PspGetCurrentThread()->UniqueThread); - } - else { - nativeHandle = GetNativeHandle(SourceHandle); - } - - if (nativeHandle) { + if (const auto &nativeHandle = GetNativeHandle(SourceHandle)) { // This was a handle created by Ob PVOID Object; status = ObReferenceObjectByHandle(SourceHandle, zeroptr, &Object); @@ -2215,46 +2198,19 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx } // Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves - if (Alertable && (WaitMode == UserMode)) { - bool Exit = false; - PETHREAD eThread = PspGetCurrentThread(); + bool Exit = false; + auto &fut = WaitUserApc(Alertable, WaitMode, &Exit); - auto &fut = std::async(std::launch::async, [eThread, &Exit]() { - while (true) { - xbox::KiApcListMtx.lock(); - bool Empty = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[UserMode]); - xbox::KiApcListMtx.unlock(); - if (Empty == false) { - KiExecuteUserApc(); - // Queue a native APC to the calling thread to forcefully terminate the wait in NtDll::NtWaitForMultipleObjects, - // in the case it didn't terminate already - BOOL t = QueueUserAPC(EndWait, *GetNativeHandle(eThread->UniqueThread), 0); - return true; - } - Sleep(0); - if (Exit) { return false; } - } - }); + NTSTATUS ret = NtDll::NtWaitForMultipleObjects( + Count, + Handles, + (NtDll::OBJECT_WAIT_TYPE)WaitType, + Alertable, + (NtDll::PLARGE_INTEGER)Timeout); - NTSTATUS ret = NtDll::NtWaitForMultipleObjects( - Count, - Handles, - (NtDll::OBJECT_WAIT_TYPE)WaitType, - Alertable, - (NtDll::PLARGE_INTEGER)Timeout); - - Exit = true; - bool result = fut.get(); - return result ? X_STATUS_USER_APC : ret; - } - else { - return NtDll::NtWaitForMultipleObjects( - Count, - Handles, - (NtDll::OBJECT_WAIT_TYPE)WaitType, - Alertable, - (NtDll::PLARGE_INTEGER)Timeout); - } + Exit = true; + bool result = fut.get(); + return result ? X_STATUS_USER_APC : ret; } // ****************************************************************** diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index f327a4025..8c817053d 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -32,6 +32,7 @@ #include "core\kernel\exports\EmuKrnl.h" // For InitializeListHead(), etc. #include "core\kernel\exports\EmuKrnlKe.h" #include "core\kernel\support\EmuFS.h" // For fs_instruction_t +#include "core\kernel\support\NativeHandle.h" #include "core\kernel\init\CxbxKrnl.h" #include "Logging.h" @@ -185,7 +186,7 @@ void EmuKeSetPcr(xbox::KPCR *Pcr) __writefsdword(TIB_ArbitraryDataSlot, (DWORD)Pcr); } -void EmuKeFreePcr() +void EmuKeFreePcr(xbox::HANDLE UniqueThread) { xbox::PKPCR Pcr = EmuKeGetPcr(); @@ -201,6 +202,12 @@ void EmuKeFreePcr() Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free tls assert(Status == X_STATUS_SUCCESS); } + if (UniqueThread == reinterpret_cast(GetCurrentThreadId())) { + Dummy = Pcr->Prcb->CurrentThread; + Size = xbox::zero; + Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free ethread + assert(Status == X_STATUS_SUCCESS); + } Dummy = Pcr; Size = xbox::zero; Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free pcr @@ -216,12 +223,12 @@ void EmuKeFreeThread() xbox::KeEmptyQueueApc(); xbox::PETHREAD eThread = xbox::PspGetCurrentThread(); - if (eThread->UniqueThread != NULL) { + xbox::HANDLE UniqueThread = eThread->UniqueThread; + if (GetNativeHandle(eThread->UniqueThread)) { xbox::NtClose(eThread->UniqueThread); - eThread->UniqueThread = NULL; } - EmuKeFreePcr(); + EmuKeFreePcr(UniqueThread); } __declspec(naked) void EmuFS_RefreshKPCR() @@ -793,7 +800,7 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread) xbox::NtAllocateVirtualMemory(&base, 0, &size, XBOX_MEM_RESERVE | XBOX_MEM_COMMIT, XBOX_PAGE_READWRITE); EThread = (xbox::ETHREAD *)base; // Clear, to prevent side-effects on random contents xbox::RtlZeroMemory(EThread, sizeof(xbox::ETHREAD)); - EThread->UniqueThread = reinterpret_cast(GetCurrentThreadId()); + EThread->UniqueThread = reinterpret_cast(GetCurrentThreadId()); } EThread->Tcb.TlsData = pNewTLS; diff --git a/src/core/kernel/support/EmuFS.h b/src/core/kernel/support/EmuFS.h index 5dc05332f..8963296c5 100644 --- a/src/core/kernel/support/EmuFS.h +++ b/src/core/kernel/support/EmuFS.h @@ -37,7 +37,7 @@ extern void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread); // free resources allocated for the thread void EmuKeFreeThread(); // free kpcr allocated for the thread -void EmuKeFreePcr(); +void EmuKeFreePcr(xbox::HANDLE UniqueThread); void EmuKeSetPcr(xbox::KPCR *Pcr); xbox::KPCR *_stdcall EmuKeGetPcr(); diff --git a/src/core/kernel/support/NativeHandle.cpp b/src/core/kernel/support/NativeHandle.cpp index 43b744c6f..fc870ef83 100644 --- a/src/core/kernel/support/NativeHandle.cpp +++ b/src/core/kernel/support/NativeHandle.cpp @@ -53,6 +53,12 @@ void RemoveXboxHandle(xbox::HANDLE xhandle) std::optional GetNativeHandle(xbox::HANDLE xhandle) { + // If SourceHandle is -2 = NtCurrentThread, then we are searching the handle of this thread + // Test case: Metal Slug 3 + if (xhandle == NtCurrentThread()) { + xhandle = xbox::PspGetCurrentThread()->UniqueThread; + } + std::shared_lock lck(g_MapMtx); auto &it = g_RegisteredHandles.find(xhandle); if (it == g_RegisteredHandles.end()) {