From 9082891903745a4e7095a1ab0f18028d05acb976 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Wed, 29 Dec 2021 21:17:19 +0100 Subject: [PATCH 01/25] Make Ps functions use Ob to create thread handles --- src/common/Timer.cpp | 2 + src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 5 +- src/core/kernel/common/ps.h | 8 +- src/core/kernel/common/types.h | 3 +- src/core/kernel/exports/EmuKrnlKe.cpp | 8 +- src/core/kernel/exports/EmuKrnlKi.cpp | 5 +- src/core/kernel/exports/EmuKrnlKi.h | 1 + src/core/kernel/exports/EmuKrnlPs.cpp | 94 ++++++++++++++++------- src/core/kernel/init/CxbxKrnl.cpp | 74 ++---------------- src/core/kernel/init/CxbxKrnl.h | 11 +-- src/core/kernel/support/EmuFS.cpp | 83 ++++++++++++-------- src/core/kernel/support/EmuFS.h | 4 +- 12 files changed, 142 insertions(+), 156 deletions(-) diff --git a/src/common/Timer.cpp b/src/common/Timer.cpp index 0cbfc8e4e..457b3867f 100644 --- a/src/common/Timer.cpp +++ b/src/common/Timer.cpp @@ -34,6 +34,7 @@ #include "Timer.h" #include "common\util\CxbxUtil.h" #include "core\kernel\init\CxbxKrnl.h" +#include "core\kernel\support\EmuFS.h" #ifdef __linux__ #include #endif @@ -142,6 +143,7 @@ void ClockThread(TimerObject* Timer) if (GetTime_NS(Timer) > NewExpireTime) { if (Timer->Exit.load()) { Timer_Destroy(Timer); + EmuKeFreeThread(); return; } Timer->Callback(Timer->Opaque); diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 86894a588..01d5768c8 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -36,6 +36,7 @@ #include "CxbxVersion.h" #include "core\kernel\init\CxbxKrnl.h" #include "core\kernel\support\Emu.h" +#include "core\kernel\support\EmuFS.h" #include "EmuShared.h" #include "..\FixedFunctionState.h" #include "core\hle\D3D8\ResourceTracker.h" @@ -632,9 +633,6 @@ void CxbxInitWindow(bool bFullInit) // We set the priority of this thread a bit higher, to assure reliable timing : SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL); g_AffinityPolicy->SetAffinityOther(hThread); - - CxbxKrnlRegisterThread(hThread); - CloseHandle(hThread); // CxbxKrnlRegisterThread duplicates the handle so we can close this one } /* TODO : Port this Dxbx code : @@ -2173,6 +2171,7 @@ static DWORD WINAPI EmuUpdateTickCount(LPVOID) // We check for LLE flag as NV2A handles it's own VBLANK if LLE is enabled! if (bLLE_GPU) { + EmuKeFreeThread(); return 0; } diff --git a/src/core/kernel/common/ps.h b/src/core/kernel/common/ps.h index e5ec71aba..db2539342 100644 --- a/src/core/kernel/common/ps.h +++ b/src/core/kernel/common/ps.h @@ -25,7 +25,7 @@ namespace xbox XBSYSAPI EXPORTNUM(254) ntstatus_xt NTAPI PsCreateSystemThread ( OUT PHANDLE ThreadHandle, - OUT PDWORD ThreadId OPTIONAL, + OUT PHANDLE ThreadId OPTIONAL, IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext, IN boolean_xt DebuggerThread @@ -40,7 +40,7 @@ XBSYSAPI EXPORTNUM(255) ntstatus_xt NTAPI PsCreateSystemThreadEx IN ulong_xt ThreadExtensionSize, IN ulong_xt KernelStackSize, IN ulong_xt TlsDataSize, - OUT PDWORD ThreadId OPTIONAL, + OUT PHANDLE ThreadId OPTIONAL, IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext, IN boolean_xt CreateSuspended, @@ -69,7 +69,9 @@ XBSYSAPI EXPORTNUM(257) ntstatus_xt NTAPI PsSetCreateThreadNotifyRoutine // ****************************************************************** XBSYSAPI EXPORTNUM(258) void_xt NTAPI PsTerminateSystemThread(IN ntstatus_xt ExitStatus); -XBSYSAPI EXPORTNUM(259) volatile OBJECT_TYPE PsThreadObjectType; +XBSYSAPI EXPORTNUM(259) OBJECT_TYPE PsThreadObjectType; + +PETHREAD PspGetCurrentThread(); } diff --git a/src/core/kernel/common/types.h b/src/core/kernel/common/types.h index b053d70b9..b4053728c 100644 --- a/src/core/kernel/common/types.h +++ b/src/core/kernel/common/types.h @@ -81,6 +81,7 @@ typedef void* LPSECURITY_ATTRIBUTES; #define X_STATUS_UNRECOGNIZED_MEDIA 0xC0000014L #define X_STATUS_NO_MEMORY 0xC0000017L #define X_STATUS_BUFFER_TOO_SMALL 0xC0000023L +#define X_STATUS_INVALID_PARAMETER 0xC000000DL #define X_STATUS_INVALID_PARAMETER_2 0xC00000F0L #define X_STATUS_ALERTED 0x00000101L #define X_STATUS_USER_APC 0x000000C0L @@ -1916,7 +1917,7 @@ typedef struct _ETHREAD { struct _KTHREAD Tcb; uchar_xt UnknownA[0x1C]; // 0x110 - dword_xt UniqueThread; // 0x12C + HANDLE UniqueThread; // 0x12C } ETHREAD, *PETHREAD; diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 338d27288..d06869692 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -140,11 +140,9 @@ xbox::KPCR* WINAPI KeGetPcr() Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot); if (Pcr == nullptr) { - EmuLog(LOG_LEVEL::WARNING, "KeGetPCR returned nullptr: Was this called from a non-xbox thread?"); - // Attempt to salvage the situation by calling InitXboxThread to setup KPCR in place - InitXboxThread(); - g_AffinityPolicy->SetAffinityXbox(); - Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot); + // If we reach here, it's a bug: it means we are executing xbox code from a host thread, and we have forgotten to intialize + // the xbox thread first + CxbxrKrnlAbort("KeGetPCR returned nullptr: Was this called from a non-xbox thread?"); } return Pcr; diff --git a/src/core/kernel/exports/EmuKrnlKi.cpp b/src/core/kernel/exports/EmuKrnlKi.cpp index 3046cf58c..5f2f66dec 100644 --- a/src/core/kernel/exports/EmuKrnlKi.cpp +++ b/src/core/kernel/exports/EmuKrnlKi.cpp @@ -88,6 +88,7 @@ the said software). #define ASSERT_TIMER_LOCKED assert(KiTimerMtx.Acquired > 0) +xbox::KPROCESS KiUniqueProcess; const xbox::ulong_xt CLOCK_TIME_INCREMENT = 0x2710; xbox::KDPC KiTimerExpireDpc; xbox::KI_TIMER_LOCK KiTimerMtx; @@ -97,13 +98,13 @@ xbox::LIST_ENTRY KiWaitInListHead; xbox::void_xt xbox::KiInitSystem() { - unsigned int i; + KiUniqueProcess.StackCount = 0; InitializeListHead(&KiWaitInListHead); KiTimerMtx.Acquired = 0; KeInitializeDpc(&KiTimerExpireDpc, KiTimerExpiration, NULL); - for (i = 0; i < TIMER_TABLE_SIZE; i++) { + for (unsigned i = 0; i < TIMER_TABLE_SIZE; i++) { InitializeListHead(&KiTimerTableListHead[i].Entry); KiTimerTableListHead[i].Time.u.HighPart = 0xFFFFFFFF; KiTimerTableListHead[i].Time.u.LowPart = 0; diff --git a/src/core/kernel/exports/EmuKrnlKi.h b/src/core/kernel/exports/EmuKrnlKi.h index 159230e49..24b73d6f0 100644 --- a/src/core/kernel/exports/EmuKrnlKi.h +++ b/src/core/kernel/exports/EmuKrnlKi.h @@ -136,6 +136,7 @@ namespace xbox ); }; +extern xbox::KPROCESS KiUniqueProcess; extern const xbox::ulong_xt CLOCK_TIME_INCREMENT; extern xbox::LIST_ENTRY KiWaitInListHead; extern xbox::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE]; diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index 2fa2f9564..291fd0f05 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -31,6 +31,7 @@ #include // For PsCreateSystemThreadEx, etc. +#include "core\kernel\exports\EmuKrnlKi.h" #include // For __beginthreadex(), etc. #include // For _controlfp constants @@ -39,6 +40,7 @@ #include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnl_TLS #include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, ) #include "core\kernel\support\EmuFS.h" // For EmuGenerateFS +#include "core\kernel\support\NativeHandle.h" // prevent name collisions namespace NtDll @@ -51,9 +53,10 @@ namespace NtDll // PsCreateSystemThread proxy parameters typedef struct _PCSTProxyParam { - IN PVOID StartRoutine; - IN PVOID StartContext; - IN PVOID SystemRoutine; + IN xbox::PVOID StartRoutine; + IN xbox::PVOID StartContext; + IN xbox::PVOID SystemRoutine; + IN xbox::PVOID Ethread; } PCSTProxyParam; @@ -64,23 +67,25 @@ extern int g_iThreadNotificationCount = 0; // Separate function for logging, otherwise in PCSTProxy __try wont work (Compiler Error C2712) void LOG_PCSTProxy ( - PVOID StartRoutine, - PVOID StartContext, - PVOID SystemRoutine + xbox::PVOID StartRoutine, + xbox::PVOID StartContext, + xbox::PVOID SystemRoutine, + xbox::PVOID Ethread ) { LOG_FUNC_BEGIN LOG_FUNC_ARG(StartRoutine) LOG_FUNC_ARG(StartContext) LOG_FUNC_ARG(SystemRoutine) + LOG_FUNC_ARG(Ethread) LOG_FUNC_END; } // Overload which doesn't change affinity -void InitXboxThread() +void InitXboxThread(xbox::PVOID Ethread) { // initialize FS segment selector - EmuGenerateFS(CxbxKrnl_TLS, CxbxKrnl_TLSData); + EmuGenerateFS(CxbxKrnl_TLS, CxbxKrnl_TLSData, Ethread); _controlfp(_PC_53, _MCW_PC); // Set Precision control to 53 bits (verified setting) _controlfp(_RC_NEAR, _MCW_RC); // Set Rounding control to near (unsure about this) @@ -104,11 +109,12 @@ static unsigned int WINAPI PCSTProxy LOG_PCSTProxy( params.StartRoutine, params.StartContext, - params.SystemRoutine); + params.SystemRoutine, + params.Ethread); // Do minimal thread initialization - InitXboxThread(); + InitXboxThread(params.Ethread); auto routine = (xbox::PKSYSTEM_ROUTINE)params.SystemRoutine; // Debugging notice : When the below line shows up with an Exception dialog and a @@ -137,13 +143,19 @@ void PspSystemThreadStartup xbox::PsTerminateSystemThread(X_STATUS_SUCCESS); } +xbox::PETHREAD xbox::PspGetCurrentThread() +{ + // This works because we assign ethread to Prcb->CurrentThread + return reinterpret_cast(KeGetCurrentThread()); +} + // ****************************************************************** // * 0x00FE - PsCreateSystemThread() // ****************************************************************** XBSYSAPI EXPORTNUM(254) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThread ( OUT PHANDLE ThreadHandle, - OUT PDWORD ThreadId OPTIONAL, + OUT PHANDLE ThreadId OPTIONAL, IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext, IN boolean_xt DebuggerThread @@ -187,7 +199,7 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx IN ulong_xt ThreadExtensionSize, IN ulong_xt KernelStackSize, IN ulong_xt TlsDataSize, - OUT PDWORD ThreadId OPTIONAL, + OUT PHANDLE ThreadId OPTIONAL, IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext, IN boolean_xt CreateSuspended, @@ -222,7 +234,31 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx // create thread, using our special proxy technique { - DWORD dwThreadId = 0; + PETHREAD eThread; + ntstatus_xt result = ObCreateObject(&PsThreadObjectType, zeroptr, sizeof(ETHREAD) + ThreadExtensionSize, reinterpret_cast(&eThread)); + if (!X_NT_SUCCESS(result)) { + RETURN(result); + } + + std::memset(eThread, 0, sizeof(ETHREAD) + ThreadExtensionSize); + + // The ob handle of the ethread obj is the thread id we return to the title + result = ObInsertObject(eThread, zeroptr, 0, &eThread->UniqueThread); + if (!X_NT_SUCCESS(result)) { + ObfDereferenceObject(eThread); + RETURN(result); + } + + // Create another handle to pass back to the title in the ThreadHandle argument + result = ObOpenObjectByPointer(eThread, &PsThreadObjectType, ThreadHandle); + if (!X_NT_SUCCESS(result)) { + ObfDereferenceObject(eThread); + RETURN(result); + } + + if (ThreadId != zeroptr) { + *ThreadId = eThread->UniqueThread; + } // PCSTProxy is responsible for cleaning up this pointer PCSTProxyParam *iPCSTProxyParam = new PCSTProxyParam; @@ -230,6 +266,7 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx iPCSTProxyParam->StartRoutine = (PVOID)StartRoutine; iPCSTProxyParam->StartContext = StartContext; iPCSTProxyParam->SystemRoutine = (PVOID)SystemRoutine; // NULL, XapiThreadStartup or unknown? + iPCSTProxyParam->Ethread = eThread; /* // call thread notification routine(s) @@ -253,27 +290,27 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx } }*/ - HANDLE handle = reinterpret_cast(_beginthreadex(NULL, KernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, reinterpret_cast(&dwThreadId))); + HANDLE handle = reinterpret_cast(_beginthreadex(NULL, KernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, nullptr)); if (handle == NULL) { delete iPCSTProxyParam; + ObpClose(eThread->UniqueThread); + ObfDereferenceObject(eThread); RETURN(X_STATUS_INSUFFICIENT_RESOURCES); } - *ThreadHandle = handle; - if (ThreadId != NULL) - *ThreadId = dwThreadId; + + KiUniqueProcess.StackCount++; + RegisterXboxHandle(ThreadHandle, handle); + RegisterXboxHandle(eThread->UniqueThread, handle); g_AffinityPolicy->SetAffinityXbox(handle); - CxbxKrnlRegisterThread(handle); // Now that ThreadId is populated and affinity is changed, resume the thread (unless the guest passed CREATE_SUSPENDED) if (!CreateSuspended) { ResumeThread(handle); } - // Note : DO NOT use iPCSTProxyParam anymore, since ownership is transferred to the proxy (which frees it too) - // Log ThreadID identical to how GetCurrentThreadID() is rendered : - EmuLog(LOG_LEVEL::DEBUG, "Created Xbox proxy thread. Handle : 0x%X, ThreadId : [0x%.4X]", handle, dwThreadId); + EmuLog(LOG_LEVEL::DEBUG, "Created Xbox proxy thread. Handle : 0x%X, ThreadId : [0x%.4X]", *ThreadHandle, eThread->UniqueThread); } RETURN(X_STATUS_SUCCESS); @@ -289,14 +326,13 @@ XBSYSAPI EXPORTNUM(256) xbox::ntstatus_xt NTAPI xbox::PsQueryStatistics { LOG_FUNC_ONE_ARG_OUT(ProcessStatistics); - NTSTATUS ret = X_STATUS_SUCCESS; + ntstatus_xt ret = X_STATUS_SUCCESS; if (ProcessStatistics->Length == sizeof(PS_STATISTICS)) { - LOG_INCOMPLETE(); // TODO : Return number of threads and handles that currently exist - ProcessStatistics->ThreadCount = 1; - ProcessStatistics->HandleCount = 1; + ProcessStatistics->ThreadCount = KiUniqueProcess.StackCount; + ProcessStatistics->HandleCount = ObpObjectHandleTable.HandleCount; // This currently doesn't count native handles that we use as xbox handles } else { - ret = STATUS_INVALID_PARAMETER; + ret = X_STATUS_INVALID_PARAMETER; } RETURN(ret); @@ -368,7 +404,9 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread } }*/ - EmuKeFreePcr(); + EmuKeFreeThread(); + KiUniqueProcess.StackCount--; + _endthreadex(ExitStatus); // ExitThread(ExitStatus); // CxbxKrnlTerminateThread(); @@ -377,7 +415,7 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread // ****************************************************************** // * 0x0103 - PsThreadObjectType // ****************************************************************** -XBSYSAPI EXPORTNUM(259) xbox::OBJECT_TYPE VOLATILE xbox::PsThreadObjectType = +XBSYSAPI EXPORTNUM(259) xbox::OBJECT_TYPE xbox::PsThreadObjectType = { xbox::ExAllocatePoolWithTag, xbox::ExFreePool, diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index a5c963a02..c1685dd5f 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -93,9 +93,6 @@ DebugMode CxbxrKrnl_DebugMode = DebugMode::DM_NONE; std::string CxbxrKrnl_DebugFileName = ""; Xbe::Certificate *g_pCertificate = NULL; -/*! thread handles */ -static std::vector g_hThreads; - char szFilePath_CxbxReloaded_Exe[MAX_PATH] = { 0 }; std::string g_DataFilePath; char szFilePath_EEPROM_bin[MAX_PATH] = { 0 }; @@ -364,6 +361,8 @@ static unsigned int WINAPI CxbxKrnlInterruptThread(PVOID param) Sleep(1); } + EmuKeFreeThread(); + return 0; } @@ -1405,8 +1404,6 @@ static void CxbxrKrnlInitHacks() CxbxrLogDumpXbeInfo(pLibraryVersion); - CxbxKrnlRegisterThread(GetCurrentThread()); - // Make sure the Xbox1 code runs on one core (as the box itself has only 1 CPU, // this will better aproximate the environment with regard to multi-threading) : EmuLogInit(LOG_LEVEL::DEBUG, "Determining CPU affinity."); @@ -1511,12 +1508,14 @@ static void CxbxrKrnlInitHacks() DWORD dwThreadId; HANDLE hThread = (HANDLE)_beginthreadex(NULL, NULL, CxbxKrnlInterruptThread, NULL, NULL, (unsigned int*)&dwThreadId); // Start the kernel clock thread - TimerObject* KernelClockThr = Timer_Create(CxbxKrnlClockThread, nullptr, "Kernel clock thread", false); + TimerObject* KernelClockThr = Timer_Create(CxbxKrnlClockThread, nullptr, "Kernel clock thread", true); Timer_Start(KernelClockThr, SCALE_MS_IN_NS); EmuLogInit(LOG_LEVEL::DEBUG, "Calling XBE entry point..."); CxbxLaunchXbe(Entry); + EmuKeFreeThread(); + // FIXME: Wait for Cxbx to exit or error fatally Sleep(INFINITE); @@ -1544,8 +1543,6 @@ static void CxbxrKrnlInitHacks() { g_bEmuException = true; - CxbxKrnlResume(); - // print out error message (if exists) if(szErrorMessage != NULL) { @@ -1575,67 +1572,6 @@ static void CxbxrKrnlInitHacks() CxbxKrnlShutDown(); } -void CxbxKrnlRegisterThread(HANDLE hThread) -{ - // we must duplicate this handle in order to retain Suspend/Resume thread rights from a remote thread - { - HANDLE hDupHandle = NULL; - - if (DuplicateHandle(g_CurrentProcessHandle, hThread, g_CurrentProcessHandle, &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { - hThread = hDupHandle; // Thread handle was duplicated, continue registration with the duplicate - } - else { - auto message = CxbxGetLastErrorString("DuplicateHandle"); - EmuLog(LOG_LEVEL::WARNING, message.c_str()); - } - } - - g_hThreads.push_back(hThread); -} - -void CxbxKrnlSuspend() -{ - if(g_bEmuSuspended || g_bEmuException) - return; - - for (auto it = g_hThreads.begin(); it != g_hThreads.end(); ++it) - { - DWORD dwExitCode; - - if(GetExitCodeThread(*it, &dwExitCode) && dwExitCode == STILL_ACTIVE) { - // suspend thread if it is active - SuspendThread(*it); - } else { - // remove thread from thread list if it is dead - g_hThreads.erase(it); - } - } - - g_bEmuSuspended = true; -} - -void CxbxKrnlResume() -{ - if(!g_bEmuSuspended) - return; - - for (auto it = g_hThreads.begin(); it != g_hThreads.end(); ++it) - { - DWORD dwExitCode; - - if (GetExitCodeThread(*it, &dwExitCode) && dwExitCode == STILL_ACTIVE) { - // resume thread if it is active - ResumeThread(*it); - } - else { - // remove thread from thread list if it is dead - g_hThreads.erase(it); - } - } - - g_bEmuSuspended = false; -} - void CxbxKrnlShutDown(bool is_reboot) { if (!is_reboot) { diff --git a/src/core/kernel/init/CxbxKrnl.h b/src/core/kernel/init/CxbxKrnl.h index f22a61fa5..f59818e5b 100644 --- a/src/core/kernel/init/CxbxKrnl.h +++ b/src/core/kernel/init/CxbxKrnl.h @@ -151,15 +151,6 @@ void CxbxKrnlEmulate(unsigned int system, blocks_reserved_t blocks_reserved); #define CxbxrKrnlAbort(fmt, ...) CxbxrKrnlAbortEx(LOG_PREFIX, fmt, ##__VA_ARGS__) -/*! register a thread handle */ -void CxbxKrnlRegisterThread(HANDLE hThread); - -/*! suspend emulation */ -void CxbxKrnlSuspend(); - -/*! resume emulation */ -void CxbxKrnlResume(); - /*! terminate gracefully the emulation */ void CxbxKrnlShutDown(bool is_reboot = false); @@ -193,7 +184,7 @@ extern ULONG g_CxbxFatalErrorCode; extern size_t g_SystemMaxMemory; -void InitXboxThread(); +void InitXboxThread(xbox::PVOID Ethread = xbox::zeroptr); /*! thread local storage structure */ extern Xbe::TLS *CxbxKrnl_TLS; diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index 8b43d22e6..a5e4a0a3d 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -190,35 +190,39 @@ void EmuKeSetPcr(xbox::KPCR *Pcr) void EmuKeFreePcr() { - // NOTE: don't call KeGetPcr because that one creates a new pcr for the thread when __readfsdword returns nullptr, which we don't want - xbox::PKPCR Pcr = reinterpret_cast(__readfsdword(TIB_ArbitraryDataSlot)); + xbox::PKPCR Pcr = KeGetPcr(); + + xbox::PVOID Dummy; + xbox::ulong_xt Size; + xbox::ntstatus_xt Status; + // tls can be nullptr + if (Pcr->NtTib.StackBase) { + // NOTE: the tls pointer was increased by 12 bytes to enforce the 16 bytes alignment, so adjust it to reach the correct pointer + // that was allocated by xbox::NtAllocateVirtualMemory + Dummy = static_cast(Pcr->NtTib.StackBase) - 12; + Size = xbox::zero; + Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free tls + assert(Status == X_STATUS_SUCCESS); + } + Dummy = Pcr; + Size = xbox::zero; + Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free pcr + assert(Status == X_STATUS_SUCCESS); + __writefsdword(TIB_ArbitraryDataSlot, NULL); +} - if (Pcr) { - // tls can be nullptr - xbox::PVOID Dummy; - xbox::ulong_xt Size; - xbox::ntstatus_xt Status; - if (Pcr->NtTib.StackBase) { - // NOTE: the tls pointer was increased by 12 bytes to enforce the 16 bytes alignment, so adjust it to reach the correct pointer - // that was allocated by xbox::NtAllocateVirtualMemory - Dummy = static_cast(Pcr->NtTib.StackBase) - 12; - Size = xbox::zero; - Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free tls - assert(Status == X_STATUS_SUCCESS); - } - 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 - assert(Status == X_STATUS_SUCCESS); - __writefsdword(TIB_ArbitraryDataSlot, NULL); - } - else { - EmuLog(LOG_LEVEL::WARNING, "__readfsdword in EmuKeFreePcr returned nullptr: was this called from a non-xbox thread?"); +void EmuKeFreeThread() +{ + // This functions is to be used for cxbxr threads that execute xbox code. We can't just call PsTerminateSystemThread because some additional + // xbox state is not created for this kind of threads + + xbox::PETHREAD eThread = xbox::PspGetCurrentThread(); + if (eThread->UniqueThread != NULL) { + xbox::NtClose(eThread->UniqueThread); + eThread->UniqueThread = NULL; } + + EmuKeFreePcr(); } __declspec(naked) void EmuFS_RefreshKPCR() @@ -652,7 +656,7 @@ void EmuInitFS() } // generate fs segment selector -void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData) +void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread) { void *pNewTLS = nullptr; xbox::PVOID base; @@ -781,14 +785,25 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData) // Initialize a fake PrcbData.CurrentThread { - base = xbox::zeroptr; - size = sizeof(xbox::ETHREAD); - xbox::NtAllocateVirtualMemory(&base, 0, &size, XBOX_MEM_RESERVE | XBOX_MEM_COMMIT, XBOX_PAGE_READWRITE); - xbox::ETHREAD *EThread = (xbox::ETHREAD*)base; // Clear, to prevent side-effects on random contents - xbox::RtlZeroMemory(EThread, sizeof(xbox::ETHREAD)); + xbox::PETHREAD EThread = static_cast(Ethread); + if (EThread == xbox::zeroptr) { + // If it is nullptr, it means we are creating this thread from cxbxr, otherwise we were created from PsCreateSystemThreadEx + xbox::PETHREAD eThread; + xbox::ntstatus_xt result = xbox::ObCreateObject(&xbox::PsThreadObjectType, xbox::zeroptr, sizeof(xbox::ETHREAD), reinterpret_cast(&eThread)); + if (!X_NT_SUCCESS(result)) { + // We can't recover from here, abort execution + CxbxrKrnlAbort("ObCreateObject: failed to create new xbox thread!"); + } + std::memset(eThread, 0, sizeof(xbox::ETHREAD)); + EThread = eThread; + result = xbox::ObInsertObject(eThread, xbox::zeroptr, 0, &eThread->UniqueThread); + if (!X_NT_SUCCESS(result)) { + // We can't recover from here, abort execution + CxbxrKrnlAbort("ObInsertObject: failed to create new xbox thread!"); + } + } EThread->Tcb.TlsData = pNewTLS; - EThread->UniqueThread = GetCurrentThreadId(); // Set PrcbData.CurrentThread Prcb->CurrentThread = (xbox::KTHREAD*)EThread; // Initialize the thread header and its wait list diff --git a/src/core/kernel/support/EmuFS.h b/src/core/kernel/support/EmuFS.h index 029b2c75d..402adb568 100644 --- a/src/core/kernel/support/EmuFS.h +++ b/src/core/kernel/support/EmuFS.h @@ -33,8 +33,10 @@ extern void EmuInitFS(); // generate fs segment selector -extern void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData); +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(); typedef struct From 607a48e3eaac6e8c2b49d7aff698371a986cd98f Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Wed, 29 Dec 2021 22:05:00 +0100 Subject: [PATCH 02/25] Update Nt functions that accept thread handles (except for NtQueueApcThread) --- src/core/kernel/exports/EmuKrnlNt.cpp | 36 ++++++++++++++++++--------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index 0fc39687f..a09ef3909 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -1891,15 +1891,15 @@ XBSYSAPI EXPORTNUM(224) xbox::ntstatus_xt NTAPI xbox::NtResumeThread LOG_FUNC_ARG_OUT(PreviousSuspendCount) LOG_FUNC_END; - NTSTATUS ret = NtDll::NtResumeThread( - ThreadHandle, - (::PULONG)PreviousSuspendCount); + if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) { + // Thread handles are created by ob + RETURN(NtDll::NtResumeThread(*nativeHandle, (::PULONG)PreviousSuspendCount)); + } + else { + RETURN(X_STATUS_INVALID_HANDLE); + } // TODO : Once we do our own thread-switching, implement NtResumeThread using KetResumeThread - - //Sleep(10); - - RETURN(ret); } // ****************************************************************** @@ -2114,13 +2114,15 @@ XBSYSAPI EXPORTNUM(231) xbox::ntstatus_xt NTAPI xbox::NtSuspendThread LOG_FUNC_ARG_OUT(PreviousSuspendCount) LOG_FUNC_END; - NTSTATUS ret = NtDll::NtSuspendThread( - ThreadHandle, - (::PULONG)PreviousSuspendCount); + if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) { + // Thread handles are created by ob + RETURN(NtDll::NtSuspendThread(*nativeHandle, (::PULONG)PreviousSuspendCount)); + } + else { + RETURN(X_STATUS_INVALID_HANDLE); + } // TODO : Once we do our own thread-switching, implement NtSuspendThread using KeSuspendThread - - RETURN(ret); } // ****************************************************************** @@ -2223,6 +2225,16 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx LOG_FUNC_ARG(Timeout) LOG_FUNC_END; + // This function can wait on thread handles, which are currently created by ob, + // so we need to check their presence in the handle array + + for (ulong_xt i = 0; i < Count; ++i) { + if (const auto &nativeHandle = GetNativeHandle(Handles[i])) { + // This a ob handle, so replace it with its native counterpart + Handles[i] = *nativeHandle; + } + } + return NtDll::NtWaitForMultipleObjects( Count, Handles, From 114be1b7c905e8c426dc282b3e2f3e676f0b5011 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Sun, 2 Jan 2022 17:10:54 +0100 Subject: [PATCH 03/25] Added APCs support to kernel --- src/core/kernel/common/nt.h | 2 +- src/core/kernel/exports/EmuKrnl.cpp | 30 +++--- src/core/kernel/exports/EmuKrnlHal.cpp | 12 +-- src/core/kernel/exports/EmuKrnlKe.cpp | 65 +++++++++--- src/core/kernel/exports/EmuKrnlKi.cpp | 51 ++++++++++ src/core/kernel/exports/EmuKrnlKi.h | 29 +++--- src/core/kernel/exports/EmuKrnlNt.cpp | 133 +++++++++++++------------ src/core/kernel/exports/EmuKrnlPs.cpp | 17 +++- src/core/kernel/support/EmuFS.cpp | 17 ++-- src/core/kernel/support/EmuFS.h | 3 + 10 files changed, 237 insertions(+), 122 deletions(-) diff --git a/src/core/kernel/common/nt.h b/src/core/kernel/common/nt.h index 446881176..531f20eae 100644 --- a/src/core/kernel/common/nt.h +++ b/src/core/kernel/common/nt.h @@ -261,7 +261,7 @@ XBSYSAPI EXPORTNUM(206) ntstatus_xt NTAPI NtQueueApcThread IN PIO_APC_ROUTINE ApcRoutine, IN PVOID ApcRoutineContext OPTIONAL, IN PIO_STATUS_BLOCK ApcStatusBlock OPTIONAL, - IN ulong_xt ApcReserved OPTIONAL + IN PVOID ApcReserved OPTIONAL ); // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnl.cpp b/src/core/kernel/exports/EmuKrnl.cpp index 3b3342fd9..2e09cce6d 100644 --- a/src/core/kernel/exports/EmuKrnl.cpp +++ b/src/core/kernel/exports/EmuKrnl.cpp @@ -29,6 +29,7 @@ #include +#include "core\kernel\support\EmuFS.h" #include #include #include @@ -119,12 +120,6 @@ xbox::PLIST_ENTRY RemoveTailList(xbox::PLIST_ENTRY pListHead) return Result; } -// ****************************************************************** -// * Declaring this in a header causes errors with xboxkrnl -// * namespace, so we must declare it within any file that uses it -// ****************************************************************** -xbox::KPCR* WINAPI KeGetPcr(); - // Interrupts extern volatile DWORD HalInterruptRequestRegister; @@ -157,8 +152,8 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql) case PASSIVE_LEVEL: KiUnexpectedInterrupt(); break; - case APC_LEVEL: // = 1 // HalpApcInterrupt - EmuLog(LOG_LEVEL::WARNING, "Unimplemented Software Interrupt (APC)"); // TODO : ExecuteApcQueue(); + case APC_LEVEL: // = 1 + xbox::KiExecuteKernelApc(); break; case DISPATCH_LEVEL: // = 2 ExecuteDpcQueue(); @@ -374,7 +369,7 @@ XBSYSAPI EXPORTNUM(160) xbox::KIRQL FASTCALL xbox::KfRaiseIrql LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, NewIrql); // Inlined KeGetCurrentIrql() : - PKPCR Pcr = KeGetPcr(); + PKPCR Pcr = EmuKeGetPcr(); KIRQL OldIrql = (KIRQL)Pcr->Irql; // Set new before check @@ -402,7 +397,7 @@ XBSYSAPI EXPORTNUM(161) xbox::void_xt FASTCALL xbox::KfLowerIrql { LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, NewIrql); - KPCR* Pcr = KeGetPcr(); + KPCR* Pcr = EmuKeGetPcr(); if (g_bIsDebugKernel && NewIrql > Pcr->Irql) { KIRQL OldIrql = Pcr->Irql; @@ -450,12 +445,23 @@ XBSYSAPI EXPORTNUM(163) xbox::void_xt FASTCALL xbox::KiUnlockDispatcherDatabase { LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, OldIrql); - if (!(KeGetCurrentPrcb()->DpcRoutineActive)) // Avoid KeIsExecutingDpc(), as that logs + // Wrong, this should only happen when OldIrql >= DISPATCH_LEVEL + if (!(KeGetCurrentPrcb()->DpcRoutineActive)) { // Avoid KeIsExecutingDpc(), as that logs HalRequestSoftwareInterrupt(DISPATCH_LEVEL); + } - LOG_INCOMPLETE(); // TODO : Thread-switch? + if (OldIrql < DISPATCH_LEVEL) { + // This is wrong: this should perform a thread switch and check the kthread of the new selected thread for pending APCs. + // We can't perform our own threads switching now, so we will just check the current thread + + if (KeGetCurrentThread()->ApcState.KernelApcPending) { + KiExecuteKernelApc(); + } + } KfLowerIrql(OldIrql); + + LOG_INCOMPLETE(); // TODO : Thread-switch? } // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlHal.cpp b/src/core/kernel/exports/EmuKrnlHal.cpp index e20b6bcc3..2ed3494de 100644 --- a/src/core/kernel/exports/EmuKrnlHal.cpp +++ b/src/core/kernel/exports/EmuKrnlHal.cpp @@ -35,6 +35,7 @@ #include "EmuKrnlLogging.h" #include "core\kernel\init\CxbxKrnl.h" // For CxbxrKrnlAbort, and CxbxExec #include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, ) +#include "core\kernel\support\EmuFS.h" #include "EmuKrnl.h" #include "devices\x86\EmuX86.h" // HalReadWritePciSpace needs this #include "EmuShared.h" @@ -59,13 +60,6 @@ uint32_t ResetOrShutdownDataValue = 0; // global list of routines executed during a reboot xbox::LIST_ENTRY ShutdownRoutineList = { &ShutdownRoutineList , &ShutdownRoutineList }; // see InitializeListHead() - -// ****************************************************************** -// * Declaring this in a header causes errors with xboxkrnl -// * namespace, so we must declare it within any file that uses it -// ****************************************************************** -xbox::KPCR* WINAPI KeGetPcr(); - #define TRAY_CLOSED_MEDIA_PRESENT 0x60 #define TRAY_CLOSED_NO_MEDIA 0x40 #define TRAY_OPEN 0x10 @@ -458,10 +452,10 @@ XBSYSAPI EXPORTNUM(48) xbox::void_xt FASTCALL xbox::HalRequestSoftwareInterrupt HalInterruptRequestRegister |= InterruptMask; // Get current IRQL - PKPCR Pcr = KeGetPcr(); + PKPCR Pcr = EmuKeGetPcr(); KIRQL CurrentIrql = (KIRQL)Pcr->Irql; - // Get pending Software Interrupts (by masking of the HW interrupt bits) + // Get pending Software Interrupts (by masking off the HW interrupt bits) uint8_t SoftwareInterrupt = HalInterruptRequestRegister & 3; // Get the highest pending software interrupt level diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index d06869692..3cc58fe3f 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -128,11 +128,11 @@ xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value) // ****************************************************************** -// * KeGetPcr() +// * EmuKeGetPcr() // * NOTE: This is a macro on the Xbox, however we implement it // * as a function so it can suit our emulated KPCR structure // ****************************************************************** -xbox::KPCR* WINAPI KeGetPcr() +xbox::KPCR* WINAPI EmuKeGetPcr() { xbox::PKPCR Pcr; @@ -140,7 +140,7 @@ xbox::KPCR* WINAPI KeGetPcr() Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot); if (Pcr == nullptr) { - // If we reach here, it's a bug: it means we are executing xbox code from a host thread, and we have forgotten to intialize + // If we reach here, it's a bug: it means we are executing xbox code from a host thread, and we have forgotten to initialize // the xbox thread first CxbxrKrnlAbort("KeGetPCR returned nullptr: Was this called from a non-xbox thread?"); } @@ -153,7 +153,7 @@ xbox::KPCR* WINAPI KeGetPcr() // ****************************************************************** xbox::KPRCB *KeGetCurrentPrcb() { - return &(KeGetPcr()->PrcbData); + return &(EmuKeGetPcr()->PrcbData); } // ****************************************************************** @@ -595,7 +595,7 @@ XBSYSAPI EXPORTNUM(103) xbox::KIRQL NTAPI xbox::KeGetCurrentIrql(void) { LOG_FUNC(); // TODO : Remove nested logging on this somehow, so we can call this (instead of inlining) - KPCR* Pcr = KeGetPcr(); + KPCR* Pcr = EmuKeGetPcr(); KIRQL Irql = (KIRQL)Pcr->Irql; RETURN_TYPE(KIRQL_TYPE, Irql); @@ -993,6 +993,9 @@ XBSYSAPI EXPORTNUM(117) xbox::long_xt NTAPI xbox::KeInsertQueue RETURN(0); } +// ****************************************************************** +// * 0x0076 - KeInsertQueueApc() +// ****************************************************************** XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc ( IN PRKAPC Apc, @@ -1008,9 +1011,47 @@ XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc LOG_FUNC_ARG(Increment) LOG_FUNC_END; - LOG_UNIMPLEMENTED(); + KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); - RETURN(TRUE); + PKTHREAD kThread = Apc->Thread; + if (kThread->ApcState.ApcQueueable == FALSE) { + KfLowerIrql(OldIrql); + RETURN(FALSE); + } + else { + Apc->SystemArgument1 = SystemArgument1; + Apc->SystemArgument2 = SystemArgument2; + + if (Apc->Inserted) { + KfLowerIrql(OldIrql); + RETURN(FALSE); + } + else { + g_ApcListMtx.lock(); + InsertTailList(&kThread->ApcState.ApcListHead[Apc->ApcMode], &Apc->ApcListEntry); + g_ApcListMtx.unlock(); + Apc->Inserted = TRUE; + + // 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 + if (kThread == KeGetCurrentThread()) { + if (Apc->ApcMode == KernelMode) { // kernel apc + // NOTE: this is wrong, we should check the thread state instead of just signaling the kernel apc, but we currently + // don't set the appropriate state in kthread + kThread->ApcState.KernelApcPending = TRUE; + KiExecuteKernelApc(); + } + else if ((kThread->WaitMode == UserMode) && (kThread->Alertable)) { // user apc + // NOTE: this should also check the thread state + kThread->ApcState.UserApcPending = TRUE; + KiExecuteUserApc(); + } + } + + KfLowerIrql(OldIrql); + RETURN(TRUE); + } + } } // ****************************************************************** @@ -1087,10 +1128,10 @@ XBSYSAPI EXPORTNUM(122) xbox::void_xt NTAPI xbox::KeLeaveCriticalRegion PKTHREAD thread = KeGetCurrentThread(); thread->KernelApcDisable++; if(thread->KernelApcDisable == 0) { - LIST_ENTRY *apcListHead = &thread->ApcState.ApcListHead[0/*=KernelMode*/]; + LIST_ENTRY *apcListHead = &thread->ApcState.ApcListHead[KernelMode]; if(apcListHead->Flink != apcListHead) { - thread->ApcState.KernelApcPending = 1; // TRUE - HalRequestSoftwareInterrupt(1); // APC_LEVEL + thread->ApcState.KernelApcPending = TRUE; + HalRequestSoftwareInterrupt(APC_LEVEL); } } } @@ -2168,7 +2209,7 @@ NoWait: KiUnlockDispatcherDatabase(Thread->WaitIrql); if (WaitStatus == X_STATUS_USER_APC) { - // TODO: KiDeliverUserApc(); + KiExecuteUserApc(); } RETURN(WaitStatus); @@ -2352,7 +2393,7 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject // So unlock the dispatcher database, lower the IRQ and return the status KiUnlockDispatcherDatabase(Thread->WaitIrql); if (WaitStatus == X_STATUS_USER_APC) { - //TODO: KiDeliverUserApc(); + KiExecuteUserApc(); } RETURN(WaitStatus); diff --git a/src/core/kernel/exports/EmuKrnlKi.cpp b/src/core/kernel/exports/EmuKrnlKi.cpp index 5f2f66dec..b67c95440 100644 --- a/src/core/kernel/exports/EmuKrnlKi.cpp +++ b/src/core/kernel/exports/EmuKrnlKi.cpp @@ -80,6 +80,7 @@ the said software). #include // For KeBugCheck, etc. +#include "core\kernel\support\EmuFS.h" #include "Logging.h" // For LOG_FUNC() #include "EmuKrnl.h" // for the list support functions #include "EmuKrnlKi.h" @@ -877,3 +878,53 @@ xbox::void_xt FASTCALL xbox::KiWaitSatisfyAll return; } + +template +static xbox::void_xt KiExecuteApc() +{ + xbox::PKTHREAD kThread = xbox::KeGetCurrentThread(); + + int ApcMode; + if constexpr (KernelApc) { + kThread->ApcState.KernelApcPending = FALSE; + ApcMode = xbox::KernelMode; + } + else { + kThread->ApcState.UserApcPending = FALSE; + ApcMode = xbox::UserMode; + } + + // Even though the apc list is per-thread, it's still possible that another thread will access it while we are processing it below + xbox::g_ApcListMtx.lock(); + while (!IsListEmpty(&kThread->ApcState.ApcListHead[ApcMode])) { + if (KernelApc && (kThread->KernelApcDisable != 0)) { + xbox::g_ApcListMtx.unlock(); + return; + } + xbox::PLIST_ENTRY Entry = kThread->ApcState.ApcListHead[ApcMode].Flink; + xbox::PKAPC Apc = CONTAINING_RECORD(Entry, xbox::KAPC, ApcListEntry); + RemoveEntryList(Entry); + xbox::g_ApcListMtx.unlock(); + Apc->Inserted = FALSE; + + // NOTE: we never use KernelRoutine + if (Apc->NormalRoutine != xbox::zeroptr) { + (Apc->NormalRoutine)(Apc->NormalContext, Apc->SystemArgument1, Apc->SystemArgument2); + } + + xbox::ExFreePool(Apc); + xbox::g_ApcListMtx.lock(); + } + + xbox::g_ApcListMtx.unlock(); +} + +xbox::void_xt xbox::KiExecuteKernelApc() +{ + KiExecuteApc(); +} + +xbox::void_xt xbox::KiExecuteUserApc() +{ + KiExecuteApc(); +} diff --git a/src/core/kernel/exports/EmuKrnlKi.h b/src/core/kernel/exports/EmuKrnlKi.h index 24b73d6f0..b4286447b 100644 --- a/src/core/kernel/exports/EmuKrnlKi.h +++ b/src/core/kernel/exports/EmuKrnlKi.h @@ -47,14 +47,16 @@ namespace xbox int Acquired; } KI_TIMER_LOCK; + // NOTE: since the apc list is per-thread, we could also create a different mutex for each kthread + std::mutex g_ApcListMtx; - xbox::void_xt KiInitSystem(); + void_xt KiInitSystem(); - xbox::void_xt KiTimerLock(); + void_xt KiTimerLock(); - xbox::void_xt KiTimerUnlock(); + void_xt KiTimerUnlock(); - xbox::void_xt KiClockIsr + void_xt KiClockIsr ( IN unsigned int ScalingFactor ); @@ -64,25 +66,25 @@ namespace xbox IN ULARGE_INTEGER CurrentTime ); - xbox::void_xt KxInsertTimer + void_xt KxInsertTimer ( IN PKTIMER Timer, IN ulong_xt Hand ); - xbox::void_xt FASTCALL KiCompleteTimer + void_xt FASTCALL KiCompleteTimer ( IN PKTIMER Timer, IN ulong_xt Hand ); - xbox::void_xt KiRemoveEntryTimer + void_xt KiRemoveEntryTimer ( IN PKTIMER Timer, IN ulong_xt Hand ); - xbox::void_xt KxRemoveTreeTimer + void_xt KxRemoveTreeTimer ( IN PKTIMER Timer ); @@ -99,7 +101,7 @@ namespace xbox IN LARGE_INTEGER Interval ); - xbox::ulong_xt KiComputeTimerTableIndex + ulong_xt KiComputeTimerTableIndex ( IN ulonglong_xt Interval ); @@ -116,7 +118,7 @@ namespace xbox IN PKTIMER Timer ); - xbox::void_xt NTAPI KiTimerExpiration + void_xt NTAPI KiTimerExpiration ( IN PKDPC Dpc, IN PVOID DeferredContext, @@ -124,16 +126,19 @@ namespace xbox IN PVOID SystemArgument2 ); - xbox::void_xt FASTCALL KiTimerListExpire + void_xt FASTCALL KiTimerListExpire ( IN PLIST_ENTRY ExpiredListHead, IN KIRQL OldIrql ); - xbox::void_xt FASTCALL KiWaitSatisfyAll + void_xt FASTCALL KiWaitSatisfyAll ( IN PKWAIT_BLOCK WaitBlock ); + + void_xt KiExecuteKernelApc(); + void_xt KiExecuteUserApc(); }; extern xbox::KPROCESS KiUniqueProcess; diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index a09ef3909..2dc8aa8f8 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -30,6 +30,7 @@ #include // For NtAllocateVirtualMemory, etc. +#include "EmuKrnl.h" #include "Logging.h" // For LOG_FUNC() #include "EmuKrnlLogging.h" @@ -41,6 +42,7 @@ namespace NtDll #include "core\kernel\init\CxbxKrnl.h" // For CxbxrKrnlAbort #include "core\kernel\exports\EmuKrnlKe.h" +#include "EmuKrnlKi.h" #include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, ) #include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc. #include "core\kernel\memory-manager\VMManager.h" // For g_VMManager @@ -54,12 +56,16 @@ namespace NtDll #include #include +#include -// Used to keep track of duplicate handles created by NtQueueApcThread() -std::unordered_map g_DuplicateHandles; // 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() @@ -173,15 +179,6 @@ XBSYSAPI EXPORTNUM(187) xbox::ntstatus_xt NTAPI xbox::NtClose if (GetHandleInformation(Handle, &flags) != 0) { // This was a native handle, call NtDll::NtClose ret = NtDll::NtClose(Handle); - - // Delete duplicate threads created by our implementation of NtQueueApcThread() - if (GetHandleInformation(g_DuplicateHandles[Handle], &flags) != 0) - { - EmuLog(LOG_LEVEL::DEBUG, "Closing duplicate handle..."); - - CloseHandle(g_DuplicateHandles[Handle]); - g_DuplicateHandles.erase(Handle); - } } } @@ -1028,7 +1025,7 @@ XBSYSAPI EXPORTNUM(206) xbox::ntstatus_xt NTAPI xbox::NtQueueApcThread IN PIO_APC_ROUTINE ApcRoutine, IN PVOID ApcRoutineContext OPTIONAL, IN PIO_STATUS_BLOCK ApcStatusBlock OPTIONAL, - IN ulong_xt ApcReserved OPTIONAL + IN PVOID ApcReserved OPTIONAL ) { LOG_FUNC_BEGIN @@ -1039,54 +1036,27 @@ XBSYSAPI EXPORTNUM(206) xbox::ntstatus_xt NTAPI xbox::NtQueueApcThread LOG_FUNC_ARG(ApcReserved) LOG_FUNC_END; - // In order for NtQueueApcThread or QueueUserAPC to work, you must... I repeat... - // YOU MUST duplicate the handle with the appropriate permissions first! So far, - // the only game that I know of using this is Metal Slug 3, and it won't launch - // without it. Other SNK games might use it also, beware. + PETHREAD Thread; + ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast(&Thread)); + if (!X_NT_SUCCESS(result)) { + RETURN(result); + } - // TODO: Use our implementation of NtDuplicateObject instead? - - HANDLE hApcThread = NULL; - - // Just to be safe, let's see if the appropriate permissions are even set for the - // target thread first... - - NTSTATUS ret = NtDll::NtQueueApcThread( - (NtDll::HANDLE)ThreadHandle, - (NtDll::PIO_APC_ROUTINE)ApcRoutine, - ApcRoutineContext, - (NtDll::PIO_STATUS_BLOCK)ApcStatusBlock, - ApcReserved); - - if( FAILED( ret ) ) - { - EmuLog(LOG_LEVEL::WARNING, "Duplicating handle with THREAD_SET_CONTEXT..." ); - - // If we get here, then attempt to duplicate the thread. - if(!DuplicateHandle(g_CurrentProcessHandle, ThreadHandle, g_CurrentProcessHandle, &hApcThread, THREAD_SET_CONTEXT,FALSE,0)) - EmuLog(LOG_LEVEL::WARNING, "DuplicateHandle failed!"); - else - { - g_DuplicateHandles[ThreadHandle] = hApcThread; // Save this thread because we'll need to de-reference it later - EmuLog(LOG_LEVEL::DEBUG, "DuplicateHandle returned 0x%X (ThreadId 0x%.4X)", hApcThread, GetThreadId( hApcThread ) ); + PKAPC Apc = static_cast(ExAllocatePoolWithTag(sizeof(KAPC), 'pasP')); + if (Apc != zeroptr) { + KeInitializeApc(Apc, &Thread->Tcb, zeroptr, zeroptr, reinterpret_cast(ApcRoutine), UserMode, ApcRoutineContext); + if (!KeInsertQueueApc(Apc, ApcStatusBlock, ApcReserved, 0)) { + ExFreePool(Apc); + result = X_STATUS_UNSUCCESSFUL; } - - - ret = NtDll::NtQueueApcThread( - (NtDll::HANDLE)hApcThread, - (NtDll::PIO_APC_ROUTINE)ApcRoutine, - ApcRoutineContext, - (NtDll::PIO_STATUS_BLOCK)ApcStatusBlock, - ApcReserved); } - if (FAILED(ret)) - { - EmuLog(LOG_LEVEL::WARNING, "NtQueueApcThread failed!"); - CloseHandle( g_DuplicateHandles[ThreadHandle] ); - g_DuplicateHandles.erase( ThreadHandle ); + else { + result = X_STATUS_NO_MEMORY; } - RETURN(ret); + ObfDereferenceObject(Thread); + + RETURN(result); } // ****************************************************************** @@ -1159,8 +1129,8 @@ XBSYSAPI EXPORTNUM(207) xbox::ntstatus_xt NTAPI xbox::NtQueryDirectoryFile ret = NtDll::NtQueryDirectoryFile( FileHandle, Event, - (NtDll::PIO_APC_ROUTINE)ApcRoutine, - ApcContext, + (NtDll::PIO_APC_ROUTINE)ApcRoutine, + ApcContext, (NtDll::IO_STATUS_BLOCK*)IoStatusBlock, /*FileInformation=*/NtFileDirInfo, NtFileDirectoryInformationSize + NtPathBufferSize, @@ -2227,20 +2197,53 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx // This function can wait on thread handles, which are currently created by ob, // so we need to check their presence in the handle array - for (ulong_xt i = 0; i < Count; ++i) { if (const auto &nativeHandle = GetNativeHandle(Handles[i])) { - // This a ob handle, so replace it with its native counterpart + // This is a ob handle, so replace it with its native counterpart Handles[i] = *nativeHandle; } } - return NtDll::NtWaitForMultipleObjects( - Count, - Handles, - (NtDll::OBJECT_WAIT_TYPE)WaitType, - Alertable, - (NtDll::PLARGE_INTEGER)Timeout); + // 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(); + auto &fut = std::async(std::launch::async, [eThread, &Exit]() { + while (true) { + xbox::g_ApcListMtx.lock(); + bool Empty = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[UserMode]); + xbox::g_ApcListMtx.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 + 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); + + 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); + } } // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index 291fd0f05..0e1b0f34e 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -38,6 +38,7 @@ #include "Logging.h" // For LOG_FUNC() #include "EmuKrnlLogging.h" #include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnl_TLS +#include "EmuKrnl.h" #include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, ) #include "core\kernel\support\EmuFS.h" // For EmuGenerateFS #include "core\kernel\support\NativeHandle.h" @@ -404,12 +405,24 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread } }*/ + PKTHREAD kThread = KeGetCurrentThread(); + kThread->ApcState.ApcQueueable = FALSE; + + g_ApcListMtx.lock(); + for (int Mode = KernelMode; Mode < MaximumMode; ++Mode) { + while (!IsListEmpty(&kThread->ApcState.ApcListHead[Mode])) { + PLIST_ENTRY Entry = kThread->ApcState.ApcListHead[Mode].Flink; + PKAPC Apc = CONTAINING_RECORD(Entry, KAPC, ApcListEntry); + RemoveEntryList(Entry); + ExFreePool(Apc); + } + } + g_ApcListMtx.unlock(); + EmuKeFreeThread(); KiUniqueProcess.StackCount--; _endthreadex(ExitStatus); - // ExitThread(ExitStatus); - // CxbxKrnlTerminateThread(); } // ****************************************************************** diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index a5e4a0a3d..a25f9d710 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -116,9 +116,6 @@ NT_TIB *GetNtTib() return (NT_TIB *)__readfsdword(TIB_LinearSelfAddress); } - -xbox::KPCR* WINAPI KeGetPcr(); - uint32_t fs_lock = 0; __declspec(naked) void LockFS() @@ -160,7 +157,7 @@ __declspec(naked) void UnlockFS() void EmuKeSetPcr(xbox::KPCR *Pcr) { - // Store the Xbox KPCR pointer in FS (See KeGetPcr()) + // Store the Xbox KPCR pointer in FS (See EmuKeGetPcr()) // // Note : Cxbx currently doesn't do preemptive thread switching, // which implies that thread-state management is done by Windows. @@ -190,7 +187,7 @@ void EmuKeSetPcr(xbox::KPCR *Pcr) void EmuKeFreePcr() { - xbox::PKPCR Pcr = KeGetPcr(); + xbox::PKPCR Pcr = EmuKeGetPcr(); xbox::PVOID Dummy; xbox::ulong_xt Size; @@ -227,8 +224,8 @@ void EmuKeFreeThread() __declspec(naked) void EmuFS_RefreshKPCR() { - // Backup all registers, call KeGetPcr and then restore all registers - // KeGetPcr makes sure a valid KPCR exists for the current thread + // Backup all registers, call EmuKeGetPcr and then restore all registers + // EmuKeGetPcr makes sure a valid KPCR exists for the current thread // and creates it if missing, we backup and restore all registers // to keep it safe to call in our patches // This function can be later expanded to do nice things @@ -236,7 +233,7 @@ __declspec(naked) void EmuFS_RefreshKPCR() __asm { pushfd pushad - call KeGetPcr + call EmuKeGetPcr popad popfd ret @@ -806,6 +803,8 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread) EThread->Tcb.TlsData = pNewTLS; // Set PrcbData.CurrentThread Prcb->CurrentThread = (xbox::KTHREAD*)EThread; + Prcb->CurrentThread->KernelApcDisable = 0; + Prcb->CurrentThread->ApcState.ApcQueueable = TRUE; // Initialize the thread header and its wait list Prcb->CurrentThread->Header.Type = xbox::ThreadObject; Prcb->CurrentThread->Header.Size = sizeof(xbox::KTHREAD) / sizeof(xbox::long_xt); @@ -821,7 +820,7 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread) WaitBlock->WaitListEntry.Blink = &Prcb->CurrentThread->Timer.Header.WaitListHead; } - // Make the KPCR struct available to KeGetPcr() + // Make the KPCR struct available to EmuKeGetPcr() EmuKeSetPcr(NewPcr); EmuLog(LOG_LEVEL::DEBUG, "Installed KPCR in TIB_ArbitraryDataSlot (with pTLS = 0x%.8X)", pTLS); diff --git a/src/core/kernel/support/EmuFS.h b/src/core/kernel/support/EmuFS.h index 402adb568..5dc05332f 100644 --- a/src/core/kernel/support/EmuFS.h +++ b/src/core/kernel/support/EmuFS.h @@ -39,6 +39,9 @@ void EmuKeFreeThread(); // free kpcr allocated for the thread void EmuKeFreePcr(); +void EmuKeSetPcr(xbox::KPCR *Pcr); +xbox::KPCR *_stdcall EmuKeGetPcr(); + typedef struct { std::vector data; From b664488274a2a353f3897f68f8005e58e231c439 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Mon, 3 Jan 2022 11:12:45 +0100 Subject: [PATCH 04/25] Bug fixes --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 19 ---------------- src/core/kernel/common/ps.h | 3 --- src/core/kernel/exports/EmuKrnlKe.cpp | 24 ++++++++++++++++++-- src/core/kernel/exports/EmuKrnlKe.h | 6 +++-- src/core/kernel/exports/EmuKrnlKi.cpp | 11 ++++----- src/core/kernel/exports/EmuKrnlKi.h | 2 +- src/core/kernel/exports/EmuKrnlNt.cpp | 25 +++++++++++++++------ src/core/kernel/exports/EmuKrnlOb.cpp | 2 +- src/core/kernel/exports/EmuKrnlPs.cpp | 23 +++++-------------- src/core/kernel/init/CxbxKrnl.cpp | 1 + src/core/kernel/support/Emu.cpp | 1 - src/core/kernel/support/Emu.h | 1 - src/core/kernel/support/EmuFS.cpp | 27 +++++++++++------------ 13 files changed, 72 insertions(+), 73 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 01d5768c8..0840adb94 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -1867,8 +1867,6 @@ extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg // rendering window message procedure static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - static bool bAutoPaused = false; - const LRESULT imguiResult = ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam); if (imguiResult != 0) return imguiResult; @@ -2035,27 +2033,10 @@ static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPar { switch(wParam) { - case SIZE_RESTORED: - case SIZE_MAXIMIZED: - { - if(bAutoPaused) - { - bAutoPaused = false; - CxbxKrnlResume(); - } - } - break; - case SIZE_MINIMIZED: { if(g_XBVideo.bFullScreen) CxbxrKrnlAbort(nullptr); - - if(!g_bEmuSuspended) - { - bAutoPaused = true; - CxbxKrnlSuspend(); - } } break; } diff --git a/src/core/kernel/common/ps.h b/src/core/kernel/common/ps.h index db2539342..564f96e54 100644 --- a/src/core/kernel/common/ps.h +++ b/src/core/kernel/common/ps.h @@ -16,9 +16,6 @@ namespace xbox { - -#define PsGetCurrentThread() (CONTAINING_RECORD((KeGetCurrentThread()),ETHREAD,Tcb)) - // ****************************************************************** // * PsCreateSystemThread // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 3cc58fe3f..e38edf244 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -263,6 +263,26 @@ xbox::void_xt NTAPI xbox::KeInitializeTimer KeInitializeTimerEx(Timer, NotificationTimer); } +// ****************************************************************** +// * KeEmptyQueueApc() +// ****************************************************************** +xbox::void_xt xbox::KeEmptyQueueApc() +{ + PKTHREAD kThread = KeGetCurrentThread(); + kThread->ApcState.ApcQueueable = FALSE; + + KiApcListMtx.lock(); + for (int Mode = KernelMode; Mode < MaximumMode; ++Mode) { + while (!IsListEmpty(&kThread->ApcState.ApcListHead[Mode])) { + PLIST_ENTRY Entry = kThread->ApcState.ApcListHead[Mode].Flink; + PKAPC Apc = CONTAINING_RECORD(Entry, KAPC, ApcListEntry); + RemoveEntryList(Entry); + ExFreePool(Apc); + } + } + KiApcListMtx.unlock(); +} + // Forward KeLowerIrql() to KfLowerIrql() #define KeLowerIrql(NewIrql) \ KfLowerIrql(NewIrql) @@ -1027,9 +1047,9 @@ XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc RETURN(FALSE); } else { - g_ApcListMtx.lock(); + KiApcListMtx.lock(); InsertTailList(&kThread->ApcState.ApcListHead[Apc->ApcMode], &Apc->ApcListEntry); - g_ApcListMtx.unlock(); + KiApcListMtx.unlock(); Apc->Inserted = TRUE; // We can only attempt to execute the queued apc right away if it is been inserted in the current thread, because otherwise the KTHREAD diff --git a/src/core/kernel/exports/EmuKrnlKe.h b/src/core/kernel/exports/EmuKrnlKe.h index cca37ebd9..d92b1cb96 100644 --- a/src/core/kernel/exports/EmuKrnlKe.h +++ b/src/core/kernel/exports/EmuKrnlKe.h @@ -27,14 +27,16 @@ namespace xbox { - xbox::void_xt NTAPI KeSetSystemTime + void_xt NTAPI KeSetSystemTime ( IN PLARGE_INTEGER NewTime, OUT PLARGE_INTEGER OldTime ); - xbox::void_xt NTAPI KeInitializeTimer + void_xt NTAPI KeInitializeTimer ( IN PKTIMER Timer ); + + void_xt KeEmptyQueueApc(); } diff --git a/src/core/kernel/exports/EmuKrnlKi.cpp b/src/core/kernel/exports/EmuKrnlKi.cpp index b67c95440..7cac92984 100644 --- a/src/core/kernel/exports/EmuKrnlKi.cpp +++ b/src/core/kernel/exports/EmuKrnlKi.cpp @@ -95,6 +95,7 @@ xbox::KDPC KiTimerExpireDpc; xbox::KI_TIMER_LOCK KiTimerMtx; xbox::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE]; xbox::LIST_ENTRY KiWaitInListHead; +std::mutex xbox::KiApcListMtx; xbox::void_xt xbox::KiInitSystem() @@ -895,16 +896,16 @@ static xbox::void_xt KiExecuteApc() } // Even though the apc list is per-thread, it's still possible that another thread will access it while we are processing it below - xbox::g_ApcListMtx.lock(); + xbox::KiApcListMtx.lock(); while (!IsListEmpty(&kThread->ApcState.ApcListHead[ApcMode])) { if (KernelApc && (kThread->KernelApcDisable != 0)) { - xbox::g_ApcListMtx.unlock(); + xbox::KiApcListMtx.unlock(); return; } xbox::PLIST_ENTRY Entry = kThread->ApcState.ApcListHead[ApcMode].Flink; xbox::PKAPC Apc = CONTAINING_RECORD(Entry, xbox::KAPC, ApcListEntry); RemoveEntryList(Entry); - xbox::g_ApcListMtx.unlock(); + xbox::KiApcListMtx.unlock(); Apc->Inserted = FALSE; // NOTE: we never use KernelRoutine @@ -913,10 +914,10 @@ static xbox::void_xt KiExecuteApc() } xbox::ExFreePool(Apc); - xbox::g_ApcListMtx.lock(); + xbox::KiApcListMtx.lock(); } - xbox::g_ApcListMtx.unlock(); + xbox::KiApcListMtx.unlock(); } xbox::void_xt xbox::KiExecuteKernelApc() diff --git a/src/core/kernel/exports/EmuKrnlKi.h b/src/core/kernel/exports/EmuKrnlKi.h index b4286447b..501c53b72 100644 --- a/src/core/kernel/exports/EmuKrnlKi.h +++ b/src/core/kernel/exports/EmuKrnlKi.h @@ -48,7 +48,7 @@ namespace xbox } KI_TIMER_LOCK; // NOTE: since the apc list is per-thread, we could also create a different mutex for each kthread - std::mutex g_ApcListMtx; + extern std::mutex KiApcListMtx; void_xt KiInitSystem(); diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index 2dc8aa8f8..a61a88d70 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -716,18 +716,28 @@ XBSYSAPI EXPORTNUM(197) xbox::ntstatus_xt NTAPI xbox::NtDuplicateObject // On the xbox, the duplicated handle always has the same access rigths of the source handle const ACCESS_MASK DesiredAccess = 0; const ULONG Attributes = 0; - dword_xt nativeOptions = (Options | DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS); + const ULONG nativeOptions = (Options | DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS); - if (const auto &nativeHandle = GetNativeHandle(SourceHandle)) { + // 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) { // This was a handle created by Ob PVOID Object; - status = ObReferenceObjectByHandle(SourceHandle, /*ObjectType=*/nullptr, &Object); + status = ObReferenceObjectByHandle(SourceHandle, zeroptr, &Object); if (X_NT_SUCCESS(status)) { if (ObpIsFlagSet(Options, DUPLICATE_CLOSE_SOURCE)) { NtClose(SourceHandle); } - status = ObOpenObjectByPointer(Object, OBJECT_TO_OBJECT_HEADER(Object)->Type, /*OUT*/TargetHandle); + status = ObOpenObjectByPointer(Object, OBJECT_TO_OBJECT_HEADER(Object)->Type, TargetHandle); if (!X_NT_SUCCESS(status)) { *TargetHandle = NULL; RETURN(status); @@ -2208,16 +2218,17 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx if (Alertable && (WaitMode == UserMode)) { bool Exit = false; PETHREAD eThread = PspGetCurrentThread(); + auto &fut = std::async(std::launch::async, [eThread, &Exit]() { while (true) { - xbox::g_ApcListMtx.lock(); + xbox::KiApcListMtx.lock(); bool Empty = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[UserMode]); - xbox::g_ApcListMtx.unlock(); + 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 - QueueUserAPC(EndWait, *GetNativeHandle(eThread->UniqueThread), 0); + BOOL t = QueueUserAPC(EndWait, *GetNativeHandle(eThread->UniqueThread), 0); return true; } Sleep(0); diff --git a/src/core/kernel/exports/EmuKrnlOb.cpp b/src/core/kernel/exports/EmuKrnlOb.cpp index 159d1fbe6..647bb07fa 100644 --- a/src/core/kernel/exports/EmuKrnlOb.cpp +++ b/src/core/kernel/exports/EmuKrnlOb.cpp @@ -1019,7 +1019,7 @@ XBSYSAPI EXPORTNUM(246) xbox::ntstatus_xt NTAPI xbox::ObReferenceObjectByHandle if (Handle == NtCurrentThread()) { if ((ObjectType == &PsThreadObjectType) || (ObjectType == NULL)) { - Object = PsGetCurrentThread(); + Object = PspGetCurrentThread(); ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); InterlockedIncrement((::PLONG)(&ObjectHeader->PointerCount)); *ReturnedObject = Object; diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index 0e1b0f34e..c2138a9af 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -32,6 +32,7 @@ #include // For PsCreateSystemThreadEx, etc. #include "core\kernel\exports\EmuKrnlKi.h" +#include "core\kernel\exports\EmuKrnlKe.h" #include // For __beginthreadex(), etc. #include // For _controlfp constants @@ -291,7 +292,8 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx } }*/ - HANDLE handle = reinterpret_cast(_beginthreadex(NULL, KernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, nullptr)); + unsigned int ThreadId; + HANDLE handle = reinterpret_cast(_beginthreadex(NULL, KernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, &ThreadId)); if (handle == NULL) { delete iPCSTProxyParam; ObpClose(eThread->UniqueThread); @@ -300,7 +302,7 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx } KiUniqueProcess.StackCount++; - RegisterXboxHandle(ThreadHandle, handle); + RegisterXboxHandle(*ThreadHandle, handle); RegisterXboxHandle(eThread->UniqueThread, handle); g_AffinityPolicy->SetAffinityXbox(handle); @@ -311,7 +313,8 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx } // Log ThreadID identical to how GetCurrentThreadID() is rendered : - EmuLog(LOG_LEVEL::DEBUG, "Created Xbox proxy thread. Handle : 0x%X, ThreadId : [0x%.4X]", *ThreadHandle, eThread->UniqueThread); + EmuLog(LOG_LEVEL::DEBUG, "Created Xbox proxy thread. Handle : 0x%X, ThreadId : [0x%.4X], Native ThreadId : [0x%.4X]", + *ThreadHandle, eThread->UniqueThread, ThreadId); } RETURN(X_STATUS_SUCCESS); @@ -405,20 +408,6 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread } }*/ - PKTHREAD kThread = KeGetCurrentThread(); - kThread->ApcState.ApcQueueable = FALSE; - - g_ApcListMtx.lock(); - for (int Mode = KernelMode; Mode < MaximumMode; ++Mode) { - while (!IsListEmpty(&kThread->ApcState.ApcListHead[Mode])) { - PLIST_ENTRY Entry = kThread->ApcState.ApcListHead[Mode].Flink; - PKAPC Apc = CONTAINING_RECORD(Entry, KAPC, ApcListEntry); - RemoveEntryList(Entry); - ExFreePool(Apc); - } - } - g_ApcListMtx.unlock(); - EmuKeFreeThread(); KiUniqueProcess.StackCount--; diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index c1685dd5f..4098ae7d0 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -42,6 +42,7 @@ #include "EmuEEPROM.h" // For CxbxRestoreEEPROM, EEPROM, XboxFactoryGameRegion #include "core\kernel\exports\EmuKrnl.h" #include "core\kernel\exports\EmuKrnlKi.h" +#include "core\kernel\exports\EmuKrnlKe.h" #include "EmuShared.h" #include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For CxbxInitWindow, EmuD3DInit #include "core\hle\DSOUND\DirectSound\DirectSound.hpp" // For CxbxInitAudio diff --git a/src/core/kernel/support/Emu.cpp b/src/core/kernel/support/Emu.cpp index 5eec5646e..01b919af2 100644 --- a/src/core/kernel/support/Emu.cpp +++ b/src/core/kernel/support/Emu.cpp @@ -45,7 +45,6 @@ CRITICAL_SECTION dbgCritical; // Global Variable(s) volatile thread_local bool g_bEmuException = false; static thread_local bool bOverrideEmuException; -volatile bool g_bEmuSuspended = false; volatile bool g_bPrintfOn = true; bool g_DisablePixelShaders = false; bool g_UseAllCores = false; diff --git a/src/core/kernel/support/Emu.h b/src/core/kernel/support/Emu.h index 328b993fd..0ee0ec773 100644 --- a/src/core/kernel/support/Emu.h +++ b/src/core/kernel/support/Emu.h @@ -58,7 +58,6 @@ void EmuPrintStackTrace(PCONTEXT ContextRecord); // global flags specifying current emulation state extern volatile thread_local bool g_bEmuException; -extern volatile bool g_bEmuSuspended; // global exception patching address extern void * funcExclude[2048]; diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index a25f9d710..f327a4025 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -213,6 +213,8 @@ void EmuKeFreeThread() // This functions is to be used for cxbxr threads that execute xbox code. We can't just call PsTerminateSystemThread because some additional // xbox state is not created for this kind of threads + xbox::KeEmptyQueueApc(); + xbox::PETHREAD eThread = xbox::PspGetCurrentThread(); if (eThread->UniqueThread != NULL) { xbox::NtClose(eThread->UniqueThread); @@ -782,27 +784,24 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread) // Initialize a fake PrcbData.CurrentThread { + // If it is nullptr, it means we are creating this thread from cxbxr, otherwise we were created from PsCreateSystemThreadEx xbox::PETHREAD EThread = static_cast(Ethread); if (EThread == xbox::zeroptr) { - // If it is nullptr, it means we are creating this thread from cxbxr, otherwise we were created from PsCreateSystemThreadEx - xbox::PETHREAD eThread; - xbox::ntstatus_xt result = xbox::ObCreateObject(&xbox::PsThreadObjectType, xbox::zeroptr, sizeof(xbox::ETHREAD), reinterpret_cast(&eThread)); - if (!X_NT_SUCCESS(result)) { - // We can't recover from here, abort execution - CxbxrKrnlAbort("ObCreateObject: failed to create new xbox thread!"); - } - std::memset(eThread, 0, sizeof(xbox::ETHREAD)); - EThread = eThread; - result = xbox::ObInsertObject(eThread, xbox::zeroptr, 0, &eThread->UniqueThread); - if (!X_NT_SUCCESS(result)) { - // We can't recover from here, abort execution - CxbxrKrnlAbort("ObInsertObject: failed to create new xbox thread!"); - } + // Since this a host thread that we use to execute xbox code, we do not need to create ob handles for this thread + base = xbox::zeroptr; + size = sizeof(xbox::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->Tcb.TlsData = pNewTLS; // Set PrcbData.CurrentThread Prcb->CurrentThread = (xbox::KTHREAD*)EThread; + // Initialize APC stuff + InitializeListHead(&Prcb->CurrentThread->ApcState.ApcListHead[xbox::KernelMode]); + InitializeListHead(&Prcb->CurrentThread->ApcState.ApcListHead[xbox::UserMode]); Prcb->CurrentThread->KernelApcDisable = 0; Prcb->CurrentThread->ApcState.ApcQueueable = TRUE; // Initialize the thread header and its wait list 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 05/25] 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()) { From b39801df11d83f6fc06e56174692834a74ddec7c Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Sat, 15 Jan 2022 03:15:55 -0600 Subject: [PATCH 06/25] unpatch SetThreadPriority, GetThreadPriority, SetThreadPriorityBoost --- src/core/hle/Patches.cpp | 6 ++-- src/core/hle/XAPI/Xapi.cpp | 6 ++++ src/core/hle/XAPI/Xapi.h | 6 ++++ src/core/kernel/common/kernel.h | 4 +-- src/core/kernel/exports/EmuKrnlKe.cpp | 49 ++++++++++++++++++++++----- 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/core/hle/Patches.cpp b/src/core/hle/Patches.cpp index c341fe3e6..175f4399d 100644 --- a/src/core/hle/Patches.cpp +++ b/src/core/hle/Patches.cpp @@ -355,11 +355,11 @@ std::map g_PatchTable = { PATCH_ENTRY("CreateFiber", xbox::EMUPATCH(CreateFiber), PATCH_IS_FIBER), PATCH_ENTRY("DeleteFiber", xbox::EMUPATCH(DeleteFiber), PATCH_IS_FIBER), PATCH_ENTRY("GetExitCodeThread", xbox::EMUPATCH(GetExitCodeThread), PATCH_ALWAYS), - PATCH_ENTRY("GetThreadPriority", xbox::EMUPATCH(GetThreadPriority), PATCH_ALWAYS), + //PATCH_ENTRY("GetThreadPriority", xbox::EMUPATCH(GetThreadPriority), PATCH_ALWAYS), PATCH_ENTRY("OutputDebugStringA", xbox::EMUPATCH(OutputDebugStringA), PATCH_ALWAYS), //PATCH_ENTRY("RaiseException", xbox::EMUPATCH(RaiseException), PATCH_ALWAYS), - PATCH_ENTRY("SetThreadPriority", xbox::EMUPATCH(SetThreadPriority), PATCH_ALWAYS), - PATCH_ENTRY("SetThreadPriorityBoost", xbox::EMUPATCH(SetThreadPriorityBoost), PATCH_ALWAYS), + //PATCH_ENTRY("SetThreadPriority", xbox::EMUPATCH(SetThreadPriority), PATCH_ALWAYS), + //PATCH_ENTRY("SetThreadPriorityBoost", xbox::EMUPATCH(SetThreadPriorityBoost), PATCH_ALWAYS), PATCH_ENTRY("SignalObjectAndWait", xbox::EMUPATCH(SignalObjectAndWait), PATCH_ALWAYS), PATCH_ENTRY("SwitchToFiber", xbox::EMUPATCH(SwitchToFiber), PATCH_IS_FIBER), PATCH_ENTRY("XMountMUA", xbox::EMUPATCH(XMountMUA), PATCH_ALWAYS), diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 8fcf0db35..9a5033ee4 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -896,6 +896,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetLightgunCalibration) RETURN(ret); } +#if 0 // ****************************************************************** // * patch: SetThreadPriorityBoost // ****************************************************************** @@ -923,7 +924,9 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriorityBoost) RETURN(0); } } +#endif +#if 0 // ****************************************************************** // * patch: SetThreadPriority // ****************************************************************** @@ -951,8 +954,10 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriority) RETURN(0); } } +#endif +#if 0 // ****************************************************************** // * patch: GetThreadPriority // ****************************************************************** @@ -976,6 +981,7 @@ int WINAPI xbox::EMUPATCH(GetThreadPriority) RETURN(THREAD_PRIORITY_ERROR_RETURN); } } +#endif // ****************************************************************** // * patch: GetExitCodeThread diff --git a/src/core/hle/XAPI/Xapi.h b/src/core/hle/XAPI/Xapi.h index 09d12856a..014744068 100644 --- a/src/core/hle/XAPI/Xapi.h +++ b/src/core/hle/XAPI/Xapi.h @@ -509,6 +509,7 @@ xbox::bool_xt WINAPI EMUPATCH(CloseHandle) HANDLE hObject ); +#if 0 // Handled by KeSetBasePriorityThread // ****************************************************************** // * patch: SetThreadPriority // ****************************************************************** @@ -517,7 +518,9 @@ xbox::bool_xt WINAPI EMUPATCH(SetThreadPriority) HANDLE hThread, int nPriority ); +#endif +#if 0 // Handled by KeQueryBasePriorityThread // ****************************************************************** // * patch: GetThreadPriority // ****************************************************************** @@ -525,7 +528,9 @@ int WINAPI EMUPATCH(GetThreadPriority) ( HANDLE hThread ); +#endif +#if 0 // Handled by KeSetDisableBoostThread // ****************************************************************** // * patch: SetThreadPriorityBoost // ****************************************************************** @@ -534,6 +539,7 @@ xbox::bool_xt WINAPI EMUPATCH(SetThreadPriorityBoost) HANDLE hThread, bool_xt DisablePriorityBoost ); +#endif // ****************************************************************** // * patch: GetExitCodeThread diff --git a/src/core/kernel/common/kernel.h b/src/core/kernel/common/kernel.h index 2c1fcece4..8ba1597c0 100644 --- a/src/core/kernel/common/kernel.h +++ b/src/core/kernel/common/kernel.h @@ -408,10 +408,10 @@ XBSYSAPI EXPORTNUM(143) long_xt NTAPI KeSetBasePriorityThread IN long_xt Priority ); -XBSYSAPI EXPORTNUM(144) ulong_xt NTAPI KeSetDisableBoostThread +XBSYSAPI EXPORTNUM(144) boolean_xt NTAPI KeSetDisableBoostThread ( IN PKTHREAD Thread, - IN ulong_xt Disable + IN boolean_xt Disable ); // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 3ea1967f7..de5421d59 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -83,6 +83,7 @@ namespace NtDll #include "core\kernel\support\EmuFile.h" // For IsEmuHandle(), NtStatusToString() #include "core\kernel\support\NativeHandle.h" #include "Timer.h" +#include "Util.h" #include #include @@ -1220,9 +1221,16 @@ XBSYSAPI EXPORTNUM(124) xbox::long_xt NTAPI xbox::KeQueryBasePriorityThread { LOG_FUNC_ONE_ARG(Thread); - LOG_UNIMPLEMENTED(); + KIRQL OldIrql; + KiLockDispatcherDatabase(&OldIrql); - RETURN(0); + // It cannot fail because all thread handles are created by ob + const auto& nativeHandle = GetNativeHandle(PspGetCurrentThread()->UniqueThread); + long_xt ret = GetThreadPriority(*nativeHandle); + + KiUnlockDispatcherDatabase(OldIrql); + + RETURN(ret); } // ****************************************************************** @@ -1640,23 +1648,40 @@ XBSYSAPI EXPORTNUM(143) xbox::long_xt NTAPI xbox::KeSetBasePriorityThread LOG_FUNC_ARG_OUT(Priority) LOG_FUNC_END; + KIRQL oldIRQL; + KiLockDispatcherDatabase(&oldIRQL); + // It cannot fail because all thread handles are created by ob - const auto &nativeHandle = GetNativeHandle(reinterpret_cast(Thread)->UniqueThread); + const auto &nativeHandle = GetNativeHandle(PspGetCurrentThread()->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(*nativeHandle, Priority); + if(Priority <= THREAD_PRIORITY_NORMAL) { + HANDLE nhandle; + // Verify if the thread is the same as current thread. + // Then use special handle to correct the problem for Windows' call usage. + if (Thread == KeGetCurrentPrcb()->CurrentThread) { + nhandle = NtCurrentThread(); + } + else { + nhandle = *nativeHandle; + } + BOOL result = SetThreadPriority(nhandle, Priority); + if (!result) { + EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority failed: %s", WinError2Str().c_str()); + } } + KiUnlockDispatcherDatabase(oldIRQL); + RETURN(ret); } -XBSYSAPI EXPORTNUM(144) xbox::ulong_xt NTAPI xbox::KeSetDisableBoostThread +XBSYSAPI EXPORTNUM(144) xbox::boolean_xt NTAPI xbox::KeSetDisableBoostThread ( IN PKTHREAD Thread, - IN ulong_xt Disable + IN boolean_xt Disable ) { LOG_FUNC_BEGIN @@ -1667,9 +1692,17 @@ XBSYSAPI EXPORTNUM(144) xbox::ulong_xt NTAPI xbox::KeSetDisableBoostThread KIRQL oldIRQL; KiLockDispatcherDatabase(&oldIRQL); - ULONG prevDisableBoost = Thread->DisableBoost; + // It cannot fail because all thread handles are created by ob + const auto &nativeHandle = GetNativeHandle(PspGetCurrentThread()->UniqueThread); + + boolean_xt prevDisableBoost = Thread->DisableBoost; Thread->DisableBoost = (CHAR)Disable; + BOOL bRet = SetThreadPriorityBoost(*nativeHandle, Disable); + if (!bRet) { + EmuLog(LOG_LEVEL::WARNING, "SetThreadPriorityBoost failed: %s", WinError2Str().c_str()); + } + KiUnlockDispatcherDatabase(oldIRQL); RETURN(prevDisableBoost); From 7e5f9a7cb7b47450d764dd0e0c0d1107cc2c5284 Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Sat, 15 Jan 2022 16:44:44 -0600 Subject: [PATCH 07/25] unpatch GetExitCodeThread --- src/core/hle/Patches.cpp | 2 +- src/core/hle/XAPI/Xapi.cpp | 2 ++ src/core/hle/XAPI/Xapi.h | 2 ++ src/core/kernel/common/types.h | 5 ++++- src/core/kernel/exports/EmuKrnlPs.cpp | 2 +- src/core/kernel/support/EmuFS.cpp | 10 +++++++++- src/core/kernel/support/EmuFS.h | 2 +- 7 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/core/hle/Patches.cpp b/src/core/hle/Patches.cpp index 175f4399d..2703bcaee 100644 --- a/src/core/hle/Patches.cpp +++ b/src/core/hle/Patches.cpp @@ -354,7 +354,7 @@ std::map g_PatchTable = { PATCH_ENTRY("ConvertThreadToFiber", xbox::EMUPATCH(ConvertThreadToFiber), PATCH_IS_FIBER), PATCH_ENTRY("CreateFiber", xbox::EMUPATCH(CreateFiber), PATCH_IS_FIBER), PATCH_ENTRY("DeleteFiber", xbox::EMUPATCH(DeleteFiber), PATCH_IS_FIBER), - PATCH_ENTRY("GetExitCodeThread", xbox::EMUPATCH(GetExitCodeThread), PATCH_ALWAYS), + //PATCH_ENTRY("GetExitCodeThread", xbox::EMUPATCH(GetExitCodeThread), PATCH_ALWAYS), //PATCH_ENTRY("GetThreadPriority", xbox::EMUPATCH(GetThreadPriority), PATCH_ALWAYS), PATCH_ENTRY("OutputDebugStringA", xbox::EMUPATCH(OutputDebugStringA), PATCH_ALWAYS), //PATCH_ENTRY("RaiseException", xbox::EMUPATCH(RaiseException), PATCH_ALWAYS), diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 9a5033ee4..4b4aa8c27 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -983,6 +983,7 @@ int WINAPI xbox::EMUPATCH(GetThreadPriority) } #endif +#if 0 // ****************************************************************** // * patch: GetExitCodeThread // ****************************************************************** @@ -1006,6 +1007,7 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(GetExitCodeThread) RETURN(0); } } +#endif // ****************************************************************** // * patch: XapiThreadStartup diff --git a/src/core/hle/XAPI/Xapi.h b/src/core/hle/XAPI/Xapi.h index 014744068..b0a5c1ce4 100644 --- a/src/core/hle/XAPI/Xapi.h +++ b/src/core/hle/XAPI/Xapi.h @@ -541,6 +541,7 @@ xbox::bool_xt WINAPI EMUPATCH(SetThreadPriorityBoost) ); #endif +#if 0 // ****************************************************************** // * patch: GetExitCodeThread // ****************************************************************** @@ -549,6 +550,7 @@ xbox::bool_xt WINAPI EMUPATCH(GetExitCodeThread) HANDLE hThread, LPDWORD lpExitCode ); +#endif // ****************************************************************** // * patch: XapiThreadStartup diff --git a/src/core/kernel/common/types.h b/src/core/kernel/common/types.h index 380041be3..8557ee202 100644 --- a/src/core/kernel/common/types.h +++ b/src/core/kernel/common/types.h @@ -1916,10 +1916,13 @@ KTHREAD, *PKTHREAD, *RESTRICTED_POINTER PRKTHREAD; typedef struct _ETHREAD { struct _KTHREAD Tcb; - uchar_xt UnknownA[0x1C]; // 0x110 + uchar_xt UnknownA[0x10]; // 0x110 + ntstatus_xt ExitStatus; // 0x120 + uchar_xt UnknownB[0x8]; // 0x124 HANDLE UniqueThread; // 0x12C } ETHREAD, *PETHREAD; +static_assert(sizeof(ETHREAD) == 0x130); // ****************************************************************** // * PCREATE_THREAD_NOTIFY_ROUTINE diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index c2138a9af..f4c69f332 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -408,7 +408,7 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread } }*/ - EmuKeFreeThread(); + EmuKeFreeThread(ExitStatus); KiUniqueProcess.StackCount--; _endthreadex(ExitStatus); diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index 8c817053d..9b2664c1d 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -215,7 +215,7 @@ void EmuKeFreePcr(xbox::HANDLE UniqueThread) __writefsdword(TIB_ArbitraryDataSlot, NULL); } -void EmuKeFreeThread() +void EmuKeFreeThread(xbox::ntstatus_xt ExitStatus) { // This functions is to be used for cxbxr threads that execute xbox code. We can't just call PsTerminateSystemThread because some additional // xbox state is not created for this kind of threads @@ -223,6 +223,14 @@ void EmuKeFreeThread() xbox::KeEmptyQueueApc(); xbox::PETHREAD eThread = xbox::PspGetCurrentThread(); + + eThread->Tcb.HasTerminated = 1; + + // Emulate our exit strategy for GetExitCodeThread + eThread->ExitStatus = ExitStatus; + eThread->Tcb.Header.SignalState = 1; + + xbox::HANDLE UniqueThread = eThread->UniqueThread; if (GetNativeHandle(eThread->UniqueThread)) { xbox::NtClose(eThread->UniqueThread); diff --git a/src/core/kernel/support/EmuFS.h b/src/core/kernel/support/EmuFS.h index 8963296c5..c1b342459 100644 --- a/src/core/kernel/support/EmuFS.h +++ b/src/core/kernel/support/EmuFS.h @@ -35,7 +35,7 @@ extern void EmuInitFS(); // generate fs segment selector extern void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread); // free resources allocated for the thread -void EmuKeFreeThread(); +void EmuKeFreeThread(xbox::ntstatus_xt ExitStatus = X_STATUS_ABANDONED); // free kpcr allocated for the thread void EmuKeFreePcr(xbox::HANDLE UniqueThread); From e208c73586c8e4b10992cf58ecd777e319dfa83d Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Sun, 16 Jan 2022 16:12:56 -0600 Subject: [PATCH 08/25] fix thread calls issue from GetNativeHandle to keep special handle return --- src/core/kernel/exports/EmuKrnlKe.cpp | 6 +++--- src/core/kernel/support/NativeHandle.cpp | 19 ++++++++++++++++++- src/core/kernel/support/NativeHandle.h | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index de5421d59..9d3f22384 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -1225,7 +1225,7 @@ XBSYSAPI EXPORTNUM(124) xbox::long_xt NTAPI xbox::KeQueryBasePriorityThread KiLockDispatcherDatabase(&OldIrql); // It cannot fail because all thread handles are created by ob - const auto& nativeHandle = GetNativeHandle(PspGetCurrentThread()->UniqueThread); + const auto& nativeHandle = GetNativeHandle(PspGetCurrentThread()->UniqueThread); long_xt ret = GetThreadPriority(*nativeHandle); KiUnlockDispatcherDatabase(OldIrql); @@ -1652,7 +1652,7 @@ XBSYSAPI EXPORTNUM(143) xbox::long_xt NTAPI xbox::KeSetBasePriorityThread KiLockDispatcherDatabase(&oldIRQL); // It cannot fail because all thread handles are created by ob - const auto &nativeHandle = GetNativeHandle(PspGetCurrentThread()->UniqueThread); + const auto &nativeHandle = GetNativeHandle(PspGetCurrentThread()->UniqueThread); LONG ret = GetThreadPriority(*nativeHandle); // This would work normally, but it will slow down the emulation, @@ -1693,7 +1693,7 @@ XBSYSAPI EXPORTNUM(144) xbox::boolean_xt NTAPI xbox::KeSetDisableBoostThread KiLockDispatcherDatabase(&oldIRQL); // It cannot fail because all thread handles are created by ob - const auto &nativeHandle = GetNativeHandle(PspGetCurrentThread()->UniqueThread); + const auto &nativeHandle = GetNativeHandle(PspGetCurrentThread()->UniqueThread); boolean_xt prevDisableBoost = Thread->DisableBoost; Thread->DisableBoost = (CHAR)Disable; diff --git a/src/core/kernel/support/NativeHandle.cpp b/src/core/kernel/support/NativeHandle.cpp index fc870ef83..459e6cdfe 100644 --- a/src/core/kernel/support/NativeHandle.cpp +++ b/src/core/kernel/support/NativeHandle.cpp @@ -51,12 +51,27 @@ void RemoveXboxHandle(xbox::HANDLE xhandle) assert(ret == 1); } +template 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; + // Only used for threads as Windows doesn't allow non-special handle for same thread. + if (NoConversion) { + return xhandle; + } + else { + xhandle = xbox::PspGetCurrentThread()->UniqueThread; + } + } + // If xhandle is not special handle, check if it's the same as current thread. + // Only used for threads as Windows doesn't allow non-special handle for same thread. + // This will only triggered within kernel functions i.e. KeSetDisableBoostThread and KeSetBasePriorityThread. + else if (NoConversion) { + if (xhandle == xbox::PspGetCurrentThread()->UniqueThread) { + return NtCurrentThread(); + } } std::shared_lock lck(g_MapMtx); @@ -68,3 +83,5 @@ std::optional GetNativeHandle(xbox::HANDLE xhandle) return it->second; } } +template std::optional GetNativeHandle(xbox::HANDLE xhandle); +template std::optional GetNativeHandle(xbox::HANDLE xhandle); diff --git a/src/core/kernel/support/NativeHandle.h b/src/core/kernel/support/NativeHandle.h index 3ba46f9b8..b0ec16dcd 100644 --- a/src/core/kernel/support/NativeHandle.h +++ b/src/core/kernel/support/NativeHandle.h @@ -31,4 +31,4 @@ void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle); void RemoveXboxHandle(xbox::HANDLE xhandle); -std::optional GetNativeHandle(xbox::HANDLE xhandle); +template std::optional GetNativeHandle(xbox::HANDLE xhandle); From e9cc351bba9529a7545b99b109b3400485d0c472 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Tue, 18 Jan 2022 19:41:05 +0100 Subject: [PATCH 09/25] Unpatch XSetProcessQuantumLength + moved unused xapi patched to standalone file + added code to handle xbox user APCs in SignalObjectAndWait --- src/core/hle/Patches.cpp | 2 +- src/core/hle/XAPI/Xapi.cpp | 415 +--------------------- src/core/hle/XAPI/Xapi.cpp.unused-patches | 355 ++++++++++++++++++ src/core/kernel/common/ps.h | 2 + src/core/kernel/exports/EmuKrnl.cpp | 6 +- src/core/kernel/exports/EmuKrnl.h | 2 +- src/core/kernel/exports/EmuKrnlKe.cpp | 33 +- src/core/kernel/exports/EmuKrnlKi.cpp | 1 + src/core/kernel/exports/EmuKrnlNt.cpp | 2 +- src/core/kernel/exports/EmuKrnlPs.cpp | 4 +- src/core/kernel/support/EmuFS.cpp | 3 + src/core/kernel/support/NativeHandle.cpp | 1 + 12 files changed, 390 insertions(+), 436 deletions(-) create mode 100644 src/core/hle/XAPI/Xapi.cpp.unused-patches diff --git a/src/core/hle/Patches.cpp b/src/core/hle/Patches.cpp index 2703bcaee..85d6eb41f 100644 --- a/src/core/hle/Patches.cpp +++ b/src/core/hle/Patches.cpp @@ -364,7 +364,7 @@ std::map g_PatchTable = { PATCH_ENTRY("SwitchToFiber", xbox::EMUPATCH(SwitchToFiber), PATCH_IS_FIBER), PATCH_ENTRY("XMountMUA", xbox::EMUPATCH(XMountMUA), PATCH_ALWAYS), PATCH_ENTRY("XMountMURootA", xbox::EMUPATCH(XMountMURootA), PATCH_ALWAYS), - PATCH_ENTRY("XSetProcessQuantumLength", xbox::EMUPATCH(XSetProcessQuantumLength), PATCH_ALWAYS), + //PATCH_ENTRY("XSetProcessQuantumLength", xbox::EMUPATCH(XSetProcessQuantumLength), PATCH_ALWAYS), PATCH_ENTRY("timeKillEvent", xbox::EMUPATCH(timeKillEvent), PATCH_ALWAYS), PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS), PATCH_ENTRY("XReadMUMetaData", xbox::EMUPATCH(XReadMUMetaData), PATCH_ALWAYS), diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 4b4aa8c27..93eeb1f86 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -896,200 +896,6 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetLightgunCalibration) RETURN(ret); } -#if 0 -// ****************************************************************** -// * patch: SetThreadPriorityBoost -// ****************************************************************** -xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriorityBoost) -( - HANDLE hThread, - bool_xt DisablePriorityBoost -) -{ - - - LOG_FUNC_BEGIN - LOG_FUNC_ARG(hThread) - LOG_FUNC_ARG(DisablePriorityBoost) - LOG_FUNC_END; - - 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); - } -} -#endif - -#if 0 -// ****************************************************************** -// * patch: SetThreadPriority -// ****************************************************************** -xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriority) -( - HANDLE hThread, - int nPriority -) -{ - - - LOG_FUNC_BEGIN - LOG_FUNC_ARG(hThread) - LOG_FUNC_ARG(nPriority) - LOG_FUNC_END; - - 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); - } -} -#endif - - -#if 0 -// ****************************************************************** -// * patch: GetThreadPriority -// ****************************************************************** -int WINAPI xbox::EMUPATCH(GetThreadPriority) -( - HANDLE hThread -) -{ - - - LOG_FUNC_ONE_ARG(hThread); - - 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); - } -} -#endif - -#if 0 -// ****************************************************************** -// * patch: GetExitCodeThread -// ****************************************************************** -xbox::bool_xt WINAPI xbox::EMUPATCH(GetExitCodeThread) -( - HANDLE hThread, - LPDWORD lpExitCode -) -{ - - - LOG_FUNC_BEGIN - LOG_FUNC_ARG(hThread) - LOG_FUNC_ARG(lpExitCode) - LOG_FUNC_END; - - if (const auto &nativeHandle = GetNativeHandle(hThread)) { - RETURN(GetExitCodeThread(*nativeHandle, (::LPDWORD)lpExitCode)); - } - else { - RETURN(0); - } -} -#endif - -// ****************************************************************** -// * patch: XapiThreadStartup -// ****************************************************************** -xbox::void_xt WINAPI xbox::EMUPATCH(XapiThreadStartup) -( - dword_xt dwDummy1, - dword_xt dwDummy2 -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(dwDummy1) - LOG_FUNC_ARG(dwDummy2) - LOG_FUNC_END; - - typedef int (__stdcall *pfDummyFunc)(dword_xt dwDummy); - - pfDummyFunc func = (pfDummyFunc)dwDummy1; - - func(dwDummy2); - - // TODO: Call thread notify routines ? - - /* - __asm - { - push dwDummy2 - call dwDummy1 - } - */ - - //_asm int 3; -} - -// ****************************************************************** -// * patch: XRegisterThreadNotifyRoutine -// ****************************************************************** -xbox::void_xt WINAPI xbox::EMUPATCH(XRegisterThreadNotifyRoutine) -( - PXTHREAD_NOTIFICATION pThreadNotification, - bool_xt fRegister -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pThreadNotification) - LOG_FUNC_ARG(fRegister) - LOG_FUNC_END; - - if(fRegister) - { - // I honestly don't expect this to happen, but if it does... - if(g_iThreadNotificationCount >= 16) - CxbxrKrnlAbort("Too many thread notification routines installed\n"); - - // Find an empty spot in the thread notification array - for(int i = 0; i < 16; i++) - { - // If we find one, then add it to the array, and break the loop so - // that we don't accidently register the same routine twice! - if(g_pfnThreadNotification[i] == NULL) - { - g_pfnThreadNotification[i] = (PVOID)pThreadNotification->pfnNotifyRoutine; - g_iThreadNotificationCount++; - break; - } - } - } - else - { - // Go through each routine and nullify the routine passed in. - for(int i = 0; i < 16; i++) - { - if(pThreadNotification->pfnNotifyRoutine == g_pfnThreadNotification[i]) - { - g_pfnThreadNotification[i] = NULL; - g_iThreadNotificationCount--; - break; - } - } - } -} - typedef struct { LPFIBER_START_ROUTINE lpStartRoutine; LPVOID lpParameter; @@ -1172,216 +978,6 @@ xbox::LPVOID WINAPI xbox::EMUPATCH(ConvertThreadToFiber) RETURN(pRet); } - -#if 0 // Handled by NtQueueApcThread -// ****************************************************************** -// * patch: QueueUserAPC -// ****************************************************************** -xbox::dword_xt WINAPI xbox::EMUPATCH(QueueUserAPC) -( - PAPCFUNC pfnAPC, - HANDLE hThread, - dword_xt dwData -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG_TYPE(PVOID, pfnAPC) - LOG_FUNC_ARG(hThread) - LOG_FUNC_ARG(dwData) - LOG_FUNC_END; - - dword_xt dwRet = 0; - - // If necessary, we can just continue to emulate NtQueueApcThread (0xCE). - // I added this because NtQueueApcThread fails in Metal Slug 3. - - HANDLE hApcThread = NULL; - if(!DuplicateHandle(g_CurrentProcessHandle, hThread, g_CurrentProcessHandle, &hApcThread, THREAD_SET_CONTEXT,FALSE,0)) - EmuLog(LOG_LEVEL::WARNING, "DuplicateHandle failed!"); - - dwRet = QueueUserAPC(pfnAPC, hApcThread, dwData); - if(!dwRet) - EmuLog(LOG_LEVEL::WARNING, "QueueUserAPC failed!"); - - RETURN(dwRet); -} -#endif - -#if 0 // Handled by WaitForSingleObject -// ****************************************************************** -// * patch: GetOverlappedResult -// ****************************************************************** -xbox::bool_xt WINAPI xbox::EMUPATCH(GetOverlappedResult) -( - HANDLE hFile, - LPOVERLAPPED lpOverlapped, - LPDWORD lpNumberOfBytesTransferred, - bool_xt bWait -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(hFile) - LOG_FUNC_ARG(lpOverlapped) - LOG_FUNC_ARG(lpNumberOfBytesTransferred) - LOG_FUNC_ARG(bWait) - LOG_FUNC_END; - - BOOL bRet = GetOverlappedResult( hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait ); - -// if(bWait) -// bRet = TRUE; // Sucker... - - RETURN(bRet); -} -#endif - -// ****************************************************************** -// * patch: XLaunchNewImageA -// ****************************************************************** -xbox::dword_xt WINAPI xbox::EMUPATCH(XLaunchNewImageA) -( - LPCSTR lpTitlePath, - PLAUNCH_DATA pLaunchData -) -{ - // Note : This can be tested using "Innocent tears", - // which relaunches different xbes between scenes; - // One for menus, one for fmvs, etc. - // - // Other titles do this too (like "DOA2 Ultimate", - // and probably "Panzer Dragoon Orta"), but these - // titles don't come this far as-of yet. - - LOG_FUNC_BEGIN - LOG_FUNC_ARG(lpTitlePath) - LOG_FUNC_ARG(pLaunchData) - LOG_FUNC_END; - - // TODO : This patch can be removed once NtOpenSymbolicLinkObject() - // and NtQuerySymbolicLinkObject() work together correctly. - // Also, XLaunchNewImageA() depends on XeImageHeader() and uses - // XWriteTitleInfoAndReboot() and indirectly XWriteTitleInfoNoReboot() - - // Update the kernel's LaunchDataPage : - { - if (xbox::LaunchDataPage == xbox::zeroptr) - { - PVOID LaunchDataVAddr = xbox::MmAllocateContiguousMemory(sizeof(xbox::LAUNCH_DATA_PAGE)); - if (!LaunchDataVAddr) - { - RETURN(X_STATUS_NO_MEMORY); - } - xbox::LaunchDataPage = (xbox::LAUNCH_DATA_PAGE*)LaunchDataVAddr; - } - - xbox::LaunchDataPage->Header.dwTitleId = g_pCertificate->dwTitleId; - xbox::LaunchDataPage->Header.dwFlags = 0; // TODO : What to put in here? - xbox::LaunchDataPage->Header.dwLaunchDataType = LDT_TITLE; - - xbox::MmPersistContiguousMemory((PVOID)xbox::LaunchDataPage, PAGE_SIZE, TRUE); - - if (pLaunchData != xbox::zeroptr) - // Save the launch data - memcpy(&(xbox::LaunchDataPage->LaunchData[0]), pLaunchData, sizeof(LAUNCH_DATA)); - - if (lpTitlePath == xbox::zeroptr) - { - // If no path is specified, then the xbe is rebooting to dashboard - char szDashboardPath[xbox::max_path] = { 0 }; - XboxDevice* rootDevice = CxbxDeviceByDevicePath(DeviceHarddisk0Partition2); - if (rootDevice != nullptr) - sprintf(szDashboardPath, "%s\\xboxdash.xbe", rootDevice->HostDevicePath.c_str()); - - if (PathFileExists(szDashboardPath)) - { - PopupInfo(nullptr, "The title is rebooting to dashboard"); - lpTitlePath = "C:\\xboxdash.xbe"; - xbox::LaunchDataPage->Header.dwLaunchDataType = LDT_FROM_DASHBOARD; - // Other options include LDT_NONE, LDT_FROM_DEBUGGER_CMDLINE and LDT_FROM_UPDATE - } - else - CxbxrKrnlAbort("The xbe rebooted to Dashboard and xboxdash.xbe could not be found"); - } - - strncpy(&(xbox::LaunchDataPage->Header.szLaunchPath[0]), lpTitlePath, 520); - } - - // Note : While this patch exists, HalReturnToFirmware() calls - // MmPersistContiguousMemory on LaunchDataPage. When this - // patch on XLaunchNewImageA is removed, remove the call to - // MmPersistContiguousMemory from HalReturnToFirmware() too!! - - xbox::HalReturnToFirmware(xbox::ReturnFirmwareQuickReboot); - - // If this function succeeds, it doesn't get a chance to return anything. - RETURN(ERROR_GEN_FAILURE); -} - -#if 0 // patch disabled -// ****************************************************************** -// * patch: XGetLaunchInfo -// ****************************************************************** -xbox::dword_xt WINAPI xbox::EMUPATCH(XGetLaunchInfo) -( - PDWORD pdwLaunchDataType, - PLAUNCH_DATA pLaunchData -) -{ - - - // TODO : This patch can be removed once we're sure all XAPI library - // functions indirectly reference our xbox::LaunchDataPage variable. - // For this, we need a test-case that hits this function, and run that - // with and without this patch enabled. Behavior should be identical. - // When this is verified, this patch can be removed. - LOG_TEST_CASE("Unpatching test needed"); - - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pdwLaunchDataType) - LOG_FUNC_ARG(pLaunchData) - LOG_FUNC_END; - - dword_xt ret = ERROR_NOT_FOUND; - - if (xbox::LaunchDataPage != NULL) - { - // Note : Here, CxbxRestoreLaunchDataPage() was already called, - // which has loaded LaunchDataPage from a binary file (if present). - - // A title can pass data only to itself, not another title (unless started from the dashboard, of course) : - if ( (xbox::LaunchDataPage->Header.dwTitleId == g_pCertificate->dwTitleId) - || (xbox::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DASHBOARD) - || (xbox::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DEBUGGER_CMDLINE)) - { - *pdwLaunchDataType = xbox::LaunchDataPage->Header.dwLaunchDataType; - memcpy(pLaunchData, &(xbox::LaunchDataPage->LaunchData[0]), sizeof(LAUNCH_DATA)); - - // Now that LaunchDataPage is retrieved by the emulated software, free it : - MmFreeContiguousMemory(xbox::LaunchDataPage); - xbox::LaunchDataPage = NULL; - - ret = ERROR_SUCCESS; - } - } - - RETURN(ret); -} -#endif - -// ****************************************************************** -// * patch: XSetProcessQuantumLength -// ****************************************************************** -xbox::void_xt WINAPI xbox::EMUPATCH(XSetProcessQuantumLength) -( - dword_xt dwMilliseconds -) -{ - - LOG_FUNC_ONE_ARG(dwMilliseconds); - - // TODO: Implement? - LOG_IGNORED(); -} // ****************************************************************** // * patch: SignalObjectAndWait @@ -1394,7 +990,6 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait) bool_xt bAlertable ) { - LOG_FUNC_BEGIN LOG_FUNC_ARG(hObjectToSignal) LOG_FUNC_ARG(hObjectToWaitOn) @@ -1402,7 +997,15 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait) LOG_FUNC_ARG(bAlertable) LOG_FUNC_END; - dword_xt dwRet = SignalObjectAndWait( hObjectToSignal, hObjectToWaitOn, dwMilliseconds, bAlertable ); + // Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves + bool Exit = false; + auto fut = WaitApc(bAlertable, UserMode, &Exit); + + dword_xt dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, dwMilliseconds, bAlertable); + + Exit = true; + bool result = fut.get(); + return result ? X_STATUS_USER_APC : dwRet; RETURN(dwRet); } diff --git a/src/core/hle/XAPI/Xapi.cpp.unused-patches b/src/core/hle/XAPI/Xapi.cpp.unused-patches new file mode 100644 index 000000000..976a261b0 --- /dev/null +++ b/src/core/hle/XAPI/Xapi.cpp.unused-patches @@ -0,0 +1,355 @@ +xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriorityBoost) +( + HANDLE hThread, + bool_xt DisablePriorityBoost +) +{ + + + LOG_FUNC_BEGIN + LOG_FUNC_ARG(hThread) + LOG_FUNC_ARG(DisablePriorityBoost) + LOG_FUNC_END; + + 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); + } +} + +xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriority) +( + HANDLE hThread, + int nPriority +) +{ + + + LOG_FUNC_BEGIN + LOG_FUNC_ARG(hThread) + LOG_FUNC_ARG(nPriority) + LOG_FUNC_END; + + 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); + } +} + +int WINAPI xbox::EMUPATCH(GetThreadPriority) +( + HANDLE hThread +) +{ + + + LOG_FUNC_ONE_ARG(hThread); + + 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); + } +} + +xbox::bool_xt WINAPI xbox::EMUPATCH(GetExitCodeThread) +( + HANDLE hThread, + LPDWORD lpExitCode +) +{ + + + LOG_FUNC_BEGIN + LOG_FUNC_ARG(hThread) + LOG_FUNC_ARG(lpExitCode) + LOG_FUNC_END; + + if (const auto &nativeHandle = GetNativeHandle(hThread)) { + RETURN(GetExitCodeThread(*nativeHandle, (::LPDWORD)lpExitCode)); + } + else { + RETURN(0); + } +} + +xbox::void_xt WINAPI xbox::EMUPATCH(XapiThreadStartup) +( + dword_xt dwDummy1, + dword_xt dwDummy2 +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(dwDummy1) + LOG_FUNC_ARG(dwDummy2) + LOG_FUNC_END; + + typedef int (__stdcall *pfDummyFunc)(dword_xt dwDummy); + + pfDummyFunc func = (pfDummyFunc)dwDummy1; + + func(dwDummy2); + + // TODO: Call thread notify routines ? + + /* + __asm + { + push dwDummy2 + call dwDummy1 + } + */ + + //_asm int 3; +} + +xbox::void_xt WINAPI xbox::EMUPATCH(XRegisterThreadNotifyRoutine) +( + PXTHREAD_NOTIFICATION pThreadNotification, + bool_xt fRegister +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pThreadNotification) + LOG_FUNC_ARG(fRegister) + LOG_FUNC_END; + + if(fRegister) + { + // I honestly don't expect this to happen, but if it does... + if(g_iThreadNotificationCount >= 16) + CxbxrKrnlAbort("Too many thread notification routines installed\n"); + + // Find an empty spot in the thread notification array + for(int i = 0; i < 16; i++) + { + // If we find one, then add it to the array, and break the loop so + // that we don't accidently register the same routine twice! + if(g_pfnThreadNotification[i] == NULL) + { + g_pfnThreadNotification[i] = (PVOID)pThreadNotification->pfnNotifyRoutine; + g_iThreadNotificationCount++; + break; + } + } + } + else + { + // Go through each routine and nullify the routine passed in. + for(int i = 0; i < 16; i++) + { + if(pThreadNotification->pfnNotifyRoutine == g_pfnThreadNotification[i]) + { + g_pfnThreadNotification[i] = NULL; + g_iThreadNotificationCount--; + break; + } + } + } +} + +xbox::dword_xt WINAPI xbox::EMUPATCH(QueueUserAPC) +( + PAPCFUNC pfnAPC, + HANDLE hThread, + dword_xt dwData +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG_TYPE(PVOID, pfnAPC) + LOG_FUNC_ARG(hThread) + LOG_FUNC_ARG(dwData) + LOG_FUNC_END; + + dword_xt dwRet = 0; + + // If necessary, we can just continue to emulate NtQueueApcThread (0xCE). + // I added this because NtQueueApcThread fails in Metal Slug 3. + + HANDLE hApcThread = NULL; + if(!DuplicateHandle(g_CurrentProcessHandle, hThread, g_CurrentProcessHandle, &hApcThread, THREAD_SET_CONTEXT,FALSE,0)) + EmuLog(LOG_LEVEL::WARNING, "DuplicateHandle failed!"); + + dwRet = QueueUserAPC(pfnAPC, hApcThread, dwData); + if(!dwRet) + EmuLog(LOG_LEVEL::WARNING, "QueueUserAPC failed!"); + + RETURN(dwRet); +} + +xbox::bool_xt WINAPI xbox::EMUPATCH(GetOverlappedResult) +( + HANDLE hFile, + LPOVERLAPPED lpOverlapped, + LPDWORD lpNumberOfBytesTransferred, + bool_xt bWait +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(hFile) + LOG_FUNC_ARG(lpOverlapped) + LOG_FUNC_ARG(lpNumberOfBytesTransferred) + LOG_FUNC_ARG(bWait) + LOG_FUNC_END; + + BOOL bRet = GetOverlappedResult( hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait ); + +// if(bWait) +// bRet = TRUE; // Sucker... + + RETURN(bRet); +} + +xbox::dword_xt WINAPI xbox::EMUPATCH(XLaunchNewImageA) +( + LPCSTR lpTitlePath, + PLAUNCH_DATA pLaunchData +) +{ + // Note : This can be tested using "Innocent tears", + // which relaunches different xbes between scenes; + // One for menus, one for fmvs, etc. + // + // Other titles do this too (like "DOA2 Ultimate", + // and probably "Panzer Dragoon Orta"), but these + // titles don't come this far as-of yet. + + LOG_FUNC_BEGIN + LOG_FUNC_ARG(lpTitlePath) + LOG_FUNC_ARG(pLaunchData) + LOG_FUNC_END; + + // TODO : This patch can be removed once NtOpenSymbolicLinkObject() + // and NtQuerySymbolicLinkObject() work together correctly. + // Also, XLaunchNewImageA() depends on XeImageHeader() and uses + // XWriteTitleInfoAndReboot() and indirectly XWriteTitleInfoNoReboot() + + // Update the kernel's LaunchDataPage : + { + if (xbox::LaunchDataPage == xbox::zeroptr) + { + PVOID LaunchDataVAddr = xbox::MmAllocateContiguousMemory(sizeof(xbox::LAUNCH_DATA_PAGE)); + if (!LaunchDataVAddr) + { + RETURN(X_STATUS_NO_MEMORY); + } + xbox::LaunchDataPage = (xbox::LAUNCH_DATA_PAGE*)LaunchDataVAddr; + } + + xbox::LaunchDataPage->Header.dwTitleId = g_pCertificate->dwTitleId; + xbox::LaunchDataPage->Header.dwFlags = 0; // TODO : What to put in here? + xbox::LaunchDataPage->Header.dwLaunchDataType = LDT_TITLE; + + xbox::MmPersistContiguousMemory((PVOID)xbox::LaunchDataPage, PAGE_SIZE, TRUE); + + if (pLaunchData != xbox::zeroptr) + // Save the launch data + memcpy(&(xbox::LaunchDataPage->LaunchData[0]), pLaunchData, sizeof(LAUNCH_DATA)); + + if (lpTitlePath == xbox::zeroptr) + { + // If no path is specified, then the xbe is rebooting to dashboard + char szDashboardPath[xbox::max_path] = { 0 }; + XboxDevice* rootDevice = CxbxDeviceByDevicePath(DeviceHarddisk0Partition2); + if (rootDevice != nullptr) + sprintf(szDashboardPath, "%s\\xboxdash.xbe", rootDevice->HostDevicePath.c_str()); + + if (PathFileExists(szDashboardPath)) + { + PopupInfo(nullptr, "The title is rebooting to dashboard"); + lpTitlePath = "C:\\xboxdash.xbe"; + xbox::LaunchDataPage->Header.dwLaunchDataType = LDT_FROM_DASHBOARD; + // Other options include LDT_NONE, LDT_FROM_DEBUGGER_CMDLINE and LDT_FROM_UPDATE + } + else + CxbxrKrnlAbort("The xbe rebooted to Dashboard and xboxdash.xbe could not be found"); + } + + strncpy(&(xbox::LaunchDataPage->Header.szLaunchPath[0]), lpTitlePath, 520); + } + + // Note : While this patch exists, HalReturnToFirmware() calls + // MmPersistContiguousMemory on LaunchDataPage. When this + // patch on XLaunchNewImageA is removed, remove the call to + // MmPersistContiguousMemory from HalReturnToFirmware() too!! + + xbox::HalReturnToFirmware(xbox::ReturnFirmwareQuickReboot); + + // If this function succeeds, it doesn't get a chance to return anything. + RETURN(ERROR_GEN_FAILURE); +} + +xbox::dword_xt WINAPI xbox::EMUPATCH(XGetLaunchInfo) +( + PDWORD pdwLaunchDataType, + PLAUNCH_DATA pLaunchData +) +{ + + + // TODO : This patch can be removed once we're sure all XAPI library + // functions indirectly reference our xbox::LaunchDataPage variable. + // For this, we need a test-case that hits this function, and run that + // with and without this patch enabled. Behavior should be identical. + // When this is verified, this patch can be removed. + LOG_TEST_CASE("Unpatching test needed"); + + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pdwLaunchDataType) + LOG_FUNC_ARG(pLaunchData) + LOG_FUNC_END; + + dword_xt ret = ERROR_NOT_FOUND; + + if (xbox::LaunchDataPage != NULL) + { + // Note : Here, CxbxRestoreLaunchDataPage() was already called, + // which has loaded LaunchDataPage from a binary file (if present). + + // A title can pass data only to itself, not another title (unless started from the dashboard, of course) : + if ( (xbox::LaunchDataPage->Header.dwTitleId == g_pCertificate->dwTitleId) + || (xbox::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DASHBOARD) + || (xbox::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DEBUGGER_CMDLINE)) + { + *pdwLaunchDataType = xbox::LaunchDataPage->Header.dwLaunchDataType; + memcpy(pLaunchData, &(xbox::LaunchDataPage->LaunchData[0]), sizeof(LAUNCH_DATA)); + + // Now that LaunchDataPage is retrieved by the emulated software, free it : + MmFreeContiguousMemory(xbox::LaunchDataPage); + xbox::LaunchDataPage = NULL; + + ret = ERROR_SUCCESS; + } + } + + RETURN(ret); +} + +xbox::void_xt WINAPI xbox::EMUPATCH(XSetProcessQuantumLength) +( + dword_xt dwMilliseconds +) +{ + + LOG_FUNC_ONE_ARG(dwMilliseconds); + + // TODO: Implement? + LOG_IGNORED(); +} diff --git a/src/core/kernel/common/ps.h b/src/core/kernel/common/ps.h index 564f96e54..0e4ce266c 100644 --- a/src/core/kernel/common/ps.h +++ b/src/core/kernel/common/ps.h @@ -14,6 +14,8 @@ #include "types.h" +#define X_THREAD_QUANTUM 60 + namespace xbox { // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnl.cpp b/src/core/kernel/exports/EmuKrnl.cpp index 73007fe24..2cc6fafdc 100644 --- a/src/core/kernel/exports/EmuKrnl.cpp +++ b/src/core/kernel/exports/EmuKrnl.cpp @@ -210,13 +210,13 @@ 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 +// This helper function is used to signal WinApi 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) +std::future WaitApc(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 @@ -240,7 +240,7 @@ std::future WaitUserApc(xbox::boolean_xt Alertable, xbox::char_xt WaitMode (Alertable == TRUE) && (WaitMode == xbox::UserMode)) { xbox::KiExecuteUserApc(); - // Queue a native APC to the calling thread to forcefully terminate the wait of the NtDll functions, + // Queue a native APC to the calling thread to forcefully terminate the wait of the WinApi functions, // in the case it didn't terminate already HANDLE nativeHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, Id); assert(nativeHandle); diff --git a/src/core/kernel/exports/EmuKrnl.h b/src/core/kernel/exports/EmuKrnl.h index 6fcd98c63..3e83d5330 100644 --- a/src/core/kernel/exports/EmuKrnl.h +++ b/src/core/kernel/exports/EmuKrnl.h @@ -104,6 +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); +std::future WaitApc(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 9d3f22384..5edf67c9f 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -140,12 +140,10 @@ xbox::KPCR* WINAPI EmuKeGetPcr() // See EmuKeSetPcr() Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot); - - if (Pcr == nullptr) { - // If we reach here, it's a bug: it means we are executing xbox code from a host thread, and we have forgotten to initialize - // the xbox thread first - CxbxrKrnlAbort("KeGetPCR returned nullptr: Was this called from a non-xbox thread?"); - } + + // If this fails, it's a bug: it means we are executing xbox code from a host thread, and we have forgotten to initialize + // the xbox thread first + assert(Pcr); return Pcr; } @@ -571,7 +569,7 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread // 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); + auto fut = WaitApc(Alertable, WaitMode, &Exit); NTSTATUS ret = NtDll::NtDelayExecution(Alertable, (NtDll::LARGE_INTEGER *)Interval); @@ -1657,17 +1655,8 @@ XBSYSAPI EXPORTNUM(143) xbox::long_xt NTAPI xbox::KeSetBasePriorityThread // 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 nhandle; - // Verify if the thread is the same as current thread. - // Then use special handle to correct the problem for Windows' call usage. - if (Thread == KeGetCurrentPrcb()->CurrentThread) { - nhandle = NtCurrentThread(); - } - else { - nhandle = *nativeHandle; - } - BOOL result = SetThreadPriority(nhandle, Priority); + if (Priority <= THREAD_PRIORITY_NORMAL) { + BOOL result = SetThreadPriority(*nativeHandle, Priority); if (!result) { EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority failed: %s", WinError2Str().c_str()); } @@ -2221,7 +2210,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects //WaitStatus = (NTSTATUS)KiSwapThread(); //if (WaitStatus == X_STATUS_USER_APC) { - // TODO: KiDeliverUserApc(); + // KiExecuteUserApc(); //} // If the thread was not awakened for an APC, return the Wait Status @@ -2252,7 +2241,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects // So unlock the dispatcher database, lower the IRQ and return the status KiUnlockDispatcherDatabase(Thread->WaitIrql); if (WaitStatus == X_STATUS_USER_APC) { - //TODO: KiDeliverUserApc(); + KiExecuteUserApc(); } RETURN(WaitStatus); @@ -2426,7 +2415,7 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject WaitStatus = (NTSTATUS)KiSwapThread(); if (WaitStatus == X_STATUS_USER_APC) { - // TODO: KiDeliverUserApc(); + KiExecuteUserApc(); } // If the thread was not awakened for an APC, return the Wait Status @@ -2478,7 +2467,7 @@ NoWait: KiUnlockDispatcherDatabase(Thread->WaitIrql); if (WaitStatus == X_STATUS_USER_APC) { - // TODO: KiDeliverUserApc(); + KiExecuteUserApc(); } RETURN(WaitStatus); diff --git a/src/core/kernel/exports/EmuKrnlKi.cpp b/src/core/kernel/exports/EmuKrnlKi.cpp index 30dac5a41..f06976f2d 100644 --- a/src/core/kernel/exports/EmuKrnlKi.cpp +++ b/src/core/kernel/exports/EmuKrnlKi.cpp @@ -101,6 +101,7 @@ std::mutex xbox::KiApcListMtx; xbox::void_xt xbox::KiInitSystem() { KiUniqueProcess.StackCount = 0; + KiUniqueProcess.ThreadQuantum = X_THREAD_QUANTUM; InitializeListHead(&KiWaitInListHead); diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index d91ceefb1..e0718f9a5 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -2199,7 +2199,7 @@ 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 bool Exit = false; - auto &fut = WaitUserApc(Alertable, WaitMode, &Exit); + auto fut = WaitApc(Alertable, WaitMode, &Exit); NTSTATUS ret = NtDll::NtWaitForMultipleObjects( Count, diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index f4c69f332..e9a00c814 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -313,8 +313,8 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx } // Log ThreadID identical to how GetCurrentThreadID() is rendered : - EmuLog(LOG_LEVEL::DEBUG, "Created Xbox proxy thread. Handle : 0x%X, ThreadId : [0x%.4X], Native ThreadId : [0x%.4X]", - *ThreadHandle, eThread->UniqueThread, ThreadId); + EmuLog(LOG_LEVEL::DEBUG, "Created Xbox proxy thread. Handle : 0x%X, ThreadId : [0x%.4X], Native Handle : 0x%X, Native ThreadId : [0x%.4X]", + *ThreadHandle, eThread->UniqueThread, handle, ThreadId); } RETURN(X_STATUS_SUCCESS); diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index 9b2664c1d..d4223feba 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -31,6 +31,7 @@ #include #include "core\kernel\exports\EmuKrnl.h" // For InitializeListHead(), etc. #include "core\kernel\exports\EmuKrnlKe.h" +#include "core\kernel\exports\EmuKrnlKi.h" #include "core\kernel\support\EmuFS.h" // For fs_instruction_t #include "core\kernel\support\NativeHandle.h" #include "core\kernel\init\CxbxKrnl.h" @@ -819,6 +820,8 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread) InitializeListHead(&Prcb->CurrentThread->ApcState.ApcListHead[xbox::UserMode]); Prcb->CurrentThread->KernelApcDisable = 0; Prcb->CurrentThread->ApcState.ApcQueueable = TRUE; + Prcb->CurrentThread->ApcState.Process = &KiUniqueProcess; + Prcb->CurrentThread->ApcState.Process->ThreadQuantum = KiUniqueProcess.ThreadQuantum; // Initialize the thread header and its wait list Prcb->CurrentThread->Header.Type = xbox::ThreadObject; Prcb->CurrentThread->Header.Size = sizeof(xbox::KTHREAD) / sizeof(xbox::long_xt); diff --git a/src/core/kernel/support/NativeHandle.cpp b/src/core/kernel/support/NativeHandle.cpp index 459e6cdfe..4b24e3bff 100644 --- a/src/core/kernel/support/NativeHandle.cpp +++ b/src/core/kernel/support/NativeHandle.cpp @@ -83,5 +83,6 @@ std::optional GetNativeHandle(xbox::HANDLE xhandle) return it->second; } } + template std::optional GetNativeHandle(xbox::HANDLE xhandle); template std::optional GetNativeHandle(xbox::HANDLE xhandle); From ec6b16c68a921cdaeec238601a95f7cc751f9f03 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Thu, 27 Jan 2022 14:59:17 +0100 Subject: [PATCH 10/25] Added support to Ps notification routines --- src/core/kernel/exports/EmuKrnlKe.cpp | 4 +- src/core/kernel/exports/EmuKrnlPs.cpp | 88 ++++++++------------------- src/core/kernel/support/Emu.h | 4 -- 3 files changed, 28 insertions(+), 68 deletions(-) diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 5edf67c9f..06449ab94 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -136,10 +136,8 @@ xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value) // ****************************************************************** xbox::KPCR* WINAPI EmuKeGetPcr() { - xbox::PKPCR Pcr; - // See EmuKeSetPcr() - Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot); + xbox::PKPCR Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot); // If this fails, it's a bug: it means we are executing xbox code from a host thread, and we have forgotten to initialize // the xbox thread first diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index e9a00c814..a5b5529ce 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -50,7 +50,7 @@ namespace NtDll #include "core\kernel\support\EmuNtDll.h" }; -#define PSP_MAX_CREATE_THREAD_NOTIFY 16 /* TODO : Should be 8 */ +#define PSP_MAX_CREATE_THREAD_NOTIFY 8 // PsCreateSystemThread proxy parameters typedef struct _PCSTProxyParam @@ -62,9 +62,8 @@ typedef struct _PCSTProxyParam } PCSTProxyParam; -// Global Variable(s) -extern PVOID g_pfnThreadNotification[PSP_MAX_CREATE_THREAD_NOTIFY] = { NULL }; -extern int g_iThreadNotificationCount = 0; +xbox::PCREATE_THREAD_NOTIFY_ROUTINE g_pfnThreadNotification[PSP_MAX_CREATE_THREAD_NOTIFY] = { xbox::zeroptr }; +int g_iThreadNotificationCount = 0; // Separate function for logging, otherwise in PCSTProxy __try wont work (Compiler Error C2712) void LOG_PCSTProxy @@ -251,6 +250,16 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx RETURN(result); } + // Call thread notification routine(s) + if (g_iThreadNotificationCount) { + for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) { + if (g_pfnThreadNotification[i]) { + EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", i, g_pfnThreadNotification[i]); + (*g_pfnThreadNotification[i])(eThread, eThread->UniqueThread, TRUE); + } + } + } + // Create another handle to pass back to the title in the ThreadHandle argument result = ObOpenObjectByPointer(eThread, &PsThreadObjectType, ThreadHandle); if (!X_NT_SUCCESS(result)) { @@ -270,28 +279,6 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx iPCSTProxyParam->SystemRoutine = (PVOID)SystemRoutine; // NULL, XapiThreadStartup or unknown? iPCSTProxyParam->Ethread = eThread; - /* - // call thread notification routine(s) - if (g_iThreadNotificationCount != 0) - { - for (int i = 0; i < 16; i++) - { - // TODO: This is *very* wrong, ps notification routines are NOT the same as XApi notification routines - // TODO: XAPI notification routines are already handeld by XapiThreadStartup and don't need to be called by us - // TODO: This type of notification routine is PCREATE_THREAD_NOTIFY_ROUTINE, which takes an ETHREAD pointer as well as Thread ID as input - // TODO: This is impossible to support currently, as we do not create or register Xbox ETHREAD objects, so we're better to skip it entirely! - xbox::XTHREAD_NOTIFY_PROC pfnNotificationRoutine = (xbox::XTHREAD_NOTIFY_PROC)g_pfnThreadNotification[i]; - - // If the routine doesn't exist, don't execute it! - if (pfnNotificationRoutine == NULL) - continue; - - EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", g_iThreadNotificationCount, pfnNotificationRoutine); - - pfnNotificationRoutine(TRUE); - } - }*/ - unsigned int ThreadId; HANDLE handle = reinterpret_cast(_beginthreadex(NULL, KernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, &ThreadId)); if (handle == NULL) { @@ -352,29 +339,15 @@ XBSYSAPI EXPORTNUM(257) xbox::ntstatus_xt NTAPI xbox::PsSetCreateThreadNotifyRou { LOG_FUNC_ONE_ARG(NotifyRoutine); - NTSTATUS ret = X_STATUS_INSUFFICIENT_RESOURCES; - - // Taken from xbox::EmuXRegisterThreadNotifyRoutine (perhaps that can be removed now) : - - // I honestly don't expect this to happen, but if it does... - if (g_iThreadNotificationCount >= PSP_MAX_CREATE_THREAD_NOTIFY) - CxbxrKrnlAbort("Too many thread notification routines installed\n"); - - // Find an empty spot in the thread notification array - for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) - { - // If we find one, then add it to the array, and break the loop so - // that we don't accidently register the same routine twice! - if (g_pfnThreadNotification[i] == NULL) - { - g_pfnThreadNotification[i] = (PVOID)NotifyRoutine; + for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) { + if (g_pfnThreadNotification[i] == zeroptr) { + g_pfnThreadNotification[i] = NotifyRoutine; g_iThreadNotificationCount++; - ret = X_STATUS_SUCCESS; - break; + RETURN(X_STATUS_SUCCESS); } } - RETURN(ret); + RETURN(X_STATUS_INSUFFICIENT_RESOURCES); } // ****************************************************************** @@ -390,23 +363,16 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread { LOG_FUNC_ONE_ARG(ExitStatus); - /* - // call thread notification routine(s) - if (g_iThreadNotificationCount != 0) - { - for (int i = 0; i < 16; i++) - { - xbox::XTHREAD_NOTIFY_PROC pfnNotificationRoutine = (xbox::XTHREAD_NOTIFY_PROC)g_pfnThreadNotification[i]; - - // If the routine doesn't exist, don't execute it! - if (pfnNotificationRoutine == NULL) - continue; - - EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", g_iThreadNotificationCount, pfnNotificationRoutine); - - pfnNotificationRoutine(FALSE); + // Call thread notification routine(s) + xbox::PETHREAD eThread = xbox::PspGetCurrentThread(); + if (eThread->UniqueThread && g_iThreadNotificationCount) { + for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) { + if (g_pfnThreadNotification[i]) { + EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", i, g_pfnThreadNotification[i]); + (*g_pfnThreadNotification[i])(eThread, eThread->UniqueThread, FALSE); + } } - }*/ + } EmuKeFreeThread(ExitStatus); KiUniqueProcess.StackCount--; diff --git a/src/core/kernel/support/Emu.h b/src/core/kernel/support/Emu.h index 0ee0ec773..d12f493ab 100644 --- a/src/core/kernel/support/Emu.h +++ b/src/core/kernel/support/Emu.h @@ -66,10 +66,6 @@ extern HWND g_hEmuWindow; #define GET_FRONT_WINDOW_HANDLE ((CxbxKrnl_hEmuParent != nullptr) ? CxbxKrnl_hEmuParent : g_hEmuWindow) -// thread notification routine -extern PVOID g_pfnThreadNotification[16]; -extern int g_iThreadNotificationCount; - extern HANDLE g_CurrentProcessHandle; // Set in CxbxKrnlMain // Delta added to host SystemTime, used in KiClockIsr and KeSetSystemTime From 79ac0c30196d630b4a69115348ad09e98c8724c9 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Thu, 27 Jan 2022 20:51:36 +0100 Subject: [PATCH 11/25] Updated thread timings in ethread + null id upon thread termination --- src/core/kernel/common/types.h | 5 +++-- src/core/kernel/exports/EmuKrnlPs.cpp | 1 + src/core/kernel/support/EmuFS.cpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/core/kernel/common/types.h b/src/core/kernel/common/types.h index 8557ee202..efca34d62 100644 --- a/src/core/kernel/common/types.h +++ b/src/core/kernel/common/types.h @@ -1915,8 +1915,9 @@ KTHREAD, *PKTHREAD, *RESTRICTED_POINTER PRKTHREAD; // ****************************************************************** typedef struct _ETHREAD { - struct _KTHREAD Tcb; - uchar_xt UnknownA[0x10]; // 0x110 + struct _KTHREAD Tcb; + LARGE_INTEGER CreateTime; + LARGE_INTEGER ExitTime; ntstatus_xt ExitStatus; // 0x120 uchar_xt UnknownB[0x8]; // 0x124 HANDLE UniqueThread; // 0x12C diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index a5b5529ce..d626a8057 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -288,6 +288,7 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx RETURN(X_STATUS_INSUFFICIENT_RESOURCES); } + KeQuerySystemTime(&eThread->CreateTime); KiUniqueProcess.StackCount++; RegisterXboxHandle(*ThreadHandle, handle); RegisterXboxHandle(eThread->UniqueThread, handle); diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index d4223feba..68a45c40d 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -225,16 +225,17 @@ void EmuKeFreeThread(xbox::ntstatus_xt ExitStatus) xbox::PETHREAD eThread = xbox::PspGetCurrentThread(); + xbox::KeQuerySystemTime(&eThread->ExitTime); eThread->Tcb.HasTerminated = 1; // Emulate our exit strategy for GetExitCodeThread eThread->ExitStatus = ExitStatus; eThread->Tcb.Header.SignalState = 1; - xbox::HANDLE UniqueThread = eThread->UniqueThread; if (GetNativeHandle(eThread->UniqueThread)) { xbox::NtClose(eThread->UniqueThread); + eThread->UniqueThread = NULL; } EmuKeFreePcr(UniqueThread); From 733670c7f8485d134d26472c89359128e16a20d1 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Thu, 27 Jan 2022 22:07:34 +0100 Subject: [PATCH 12/25] Fixed an issue with xbox handle registration --- src/core/kernel/support/NativeHandle.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/core/kernel/support/NativeHandle.cpp b/src/core/kernel/support/NativeHandle.cpp index 4b24e3bff..123483374 100644 --- a/src/core/kernel/support/NativeHandle.cpp +++ b/src/core/kernel/support/NativeHandle.cpp @@ -31,6 +31,7 @@ #include "Windows.h" #include "assert.h" #include "NativeHandle.h" +#include "core\kernel\init\CxbxKrnl.h" std::unordered_map g_RegisteredHandles; @@ -39,9 +40,26 @@ std::shared_mutex g_MapMtx; void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle) { std::unique_lock lck(g_MapMtx); - [[maybe_unused]] const auto &ret = g_RegisteredHandles.emplace(xhandle, nhandle); - // Even when duplicating xbox handles with NtDuplicateObject, the duplicate will still be different then the source handle - assert(ret.second == true); + const auto &ret = g_RegisteredHandles.try_emplace(xhandle, nhandle); + if (ret.second == false) { + // This can happen when an ob handle has been destroyed, but then a thread switch happens before the first thread + // got a chance to remove the old handle from g_RegisteredHandles with RemoveXboxHandle + auto now = std::chrono::system_clock::now(); + auto timeout = now + std::chrono::milliseconds(2000); + while (now <= timeout) { + lck.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + lck.lock(); + g_RegisteredHandles.try_emplace(xhandle, nhandle); + if (ret.second) { + return; + } + } + + // If we reach here, it means that we could not insert the handle after more than two seconds of trying. This probably means + // that we have forgotten to call RemoveXboxHandle on the old handle, or the other thread is waiting/deadlocked, so this is a bug + CxbxrKrnlAbortEx(CXBXR_MODULE::CXBXR, "Failed to register new xbox handle after more than two seconds!"); + } } void RemoveXboxHandle(xbox::HANDLE xhandle) From e85af190d506ef67124911d9a27598e0d1ba8d6a Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Sat, 29 Jan 2022 20:03:05 +0100 Subject: [PATCH 13/25] Properly set the ref count of ethread, fixes Amped --- src/core/kernel/common/types.h | 6 +++--- src/core/kernel/exports/EmuKrnlPs.cpp | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/core/kernel/common/types.h b/src/core/kernel/common/types.h index efca34d62..d55b8f838 100644 --- a/src/core/kernel/common/types.h +++ b/src/core/kernel/common/types.h @@ -1916,10 +1916,10 @@ KTHREAD, *PKTHREAD, *RESTRICTED_POINTER PRKTHREAD; typedef struct _ETHREAD { struct _KTHREAD Tcb; - LARGE_INTEGER CreateTime; - LARGE_INTEGER ExitTime; + LARGE_INTEGER CreateTime; // 0x110 + LARGE_INTEGER ExitTime; // 0x118 ntstatus_xt ExitStatus; // 0x120 - uchar_xt UnknownB[0x8]; // 0x124 + uchar_xt Unknown[0x8]; // 0x124 HANDLE UniqueThread; // 0x12C } ETHREAD, *PETHREAD; diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index d626a8057..7f67643f3 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -288,6 +288,11 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx RETURN(X_STATUS_INSUFFICIENT_RESOURCES); } + // Increment the ref count of the thread once more. This is to guard against the case the title closes the thread handle + // before this thread terminates with PsTerminateSystemThread + // Test case: Amped + ObfReferenceObject(eThread); + KeQuerySystemTime(&eThread->CreateTime); KiUniqueProcess.StackCount++; RegisterXboxHandle(*ThreadHandle, handle); @@ -376,6 +381,8 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread } EmuKeFreeThread(ExitStatus); + // Don't do this in EmuKeFreeThread because we only increment the thread ref count in PsCreateSystemThreadEx + ObfDereferenceObject(eThread); KiUniqueProcess.StackCount--; _endthreadex(ExitStatus); From 0b90a4843418597725585066f576ea1096641406 Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Sat, 29 Jan 2022 17:16:41 -0600 Subject: [PATCH 14/25] register duplicated xbox handle require duplicated handle from host --- src/core/kernel/exports/EmuKrnlPs.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index 7f67643f3..913614f0c 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -296,7 +296,8 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx KeQuerySystemTime(&eThread->CreateTime); KiUniqueProcess.StackCount++; RegisterXboxHandle(*ThreadHandle, handle); - RegisterXboxHandle(eThread->UniqueThread, handle); + HANDLE dupHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId); + RegisterXboxHandle(eThread->UniqueThread, dupHandle); g_AffinityPolicy->SetAffinityXbox(handle); From 8c7247abf53e3a6762765d3f6c6ade4dc282cd90 Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Sat, 29 Jan 2022 13:09:28 -0600 Subject: [PATCH 15/25] reimplement suspend xbox threads so we can shutdown emulation properly --- src/core/kernel/exports/EmuKrnlKi.cpp | 1 + src/core/kernel/exports/EmuKrnlPs.cpp | 1 + src/core/kernel/init/CxbxKrnl.cpp | 50 ++++++++++++++++++++++++ src/core/kernel/support/EmuFS.cpp | 2 + src/core/kernel/support/NativeHandle.cpp | 1 + 5 files changed, 55 insertions(+) diff --git a/src/core/kernel/exports/EmuKrnlKi.cpp b/src/core/kernel/exports/EmuKrnlKi.cpp index f06976f2d..1e7c10f97 100644 --- a/src/core/kernel/exports/EmuKrnlKi.cpp +++ b/src/core/kernel/exports/EmuKrnlKi.cpp @@ -102,6 +102,7 @@ xbox::void_xt xbox::KiInitSystem() { KiUniqueProcess.StackCount = 0; KiUniqueProcess.ThreadQuantum = X_THREAD_QUANTUM; + InitializeListHead(&KiUniqueProcess.ThreadListHead); InitializeListHead(&KiWaitInListHead); diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index 913614f0c..84a96bcbf 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -294,6 +294,7 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx ObfReferenceObject(eThread); KeQuerySystemTime(&eThread->CreateTime); + InsertTailList(&KiUniqueProcess.ThreadListHead, &eThread->Tcb.ThreadListEntry); KiUniqueProcess.StackCount++; RegisterXboxHandle(*ThreadHandle, handle); HANDLE dupHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId); diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 4098ae7d0..c6a74603d 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -72,6 +72,8 @@ #include "common\crypto\EmuSha.h" // For the SHA1 functions #include "Timer.h" // For Timer_Init #include "common\input\InputManager.h" // For the InputDeviceManager +#include "core/kernel/support/NativeHandle.h" +#include "common/win32/Util.h" // for WinError2Str #include "common/FilePaths.hpp" @@ -1573,6 +1575,51 @@ static void CxbxrKrnlInitHacks() CxbxKrnlShutDown(); } +void CxbxrKrnlSuspendThreads() +{ + xbox::PLIST_ENTRY ThreadListEntry = KiUniqueProcess.ThreadListHead.Flink; + std::vector threads; + threads.reserve(KiUniqueProcess.StackCount); + + xbox::KPCR* Pcr = EmuKeGetPcr(); + + // If there's nothing in list entry, skip this step. + if (!ThreadListEntry) { + return; + } + + while (ThreadListEntry != &KiUniqueProcess.ThreadListHead) { + xbox::HANDLE UniqueThread = CONTAINING_RECORD(ThreadListEntry, xbox::ETHREAD, Tcb.ThreadListEntry)->UniqueThread; + if (UniqueThread) { + // Current thread is an xbox thread + if (Pcr) { + const auto& nHandle = GetNativeHandle(UniqueThread); + if (nHandle) { + // We do not want to suspend current thread, so we let it skip this one. + if (*nHandle != NtCurrentThread()) { + threads.push_back(*nHandle); + } + } + } + // Otherwise, convert all UniqueThread to host thead handles. + else { + const auto& nHandle = GetNativeHandle(UniqueThread); + if (nHandle) { + threads.push_back(*nHandle); + } + } + } + ThreadListEntry = ThreadListEntry->Flink; + } + + for (const auto& thread : threads) { + DWORD PrevCount = SuspendThread(thread); + if (PrevCount == -1) { + EmuLog(LOG_LEVEL::ERROR2, "Unable to suspend thread 0x%X for: %s", thread, WinError2Str().c_str()); + } + } +} + void CxbxKrnlShutDown(bool is_reboot) { if (!is_reboot) { @@ -1581,6 +1628,9 @@ void CxbxKrnlShutDown(bool is_reboot) g_EmuShared->SetBootFlags(&BootFlags); } + // This is very important process to prevent false positive report and allow IDEs to continue debug multiple reboots. + CxbxrKrnlSuspendThreads(); + // NOTE: This causes a hang when exiting while NV2A is processing // This is okay for now: It won't leak memory or resources since TerminateProcess will free everything // delete g_NV2A; // TODO : g_pXbox diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index 68a45c40d..b46b9b9e7 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -228,6 +228,8 @@ void EmuKeFreeThread(xbox::ntstatus_xt ExitStatus) xbox::KeQuerySystemTime(&eThread->ExitTime); eThread->Tcb.HasTerminated = 1; + RemoveEntryList(&eThread->Tcb.ThreadListEntry); + // Emulate our exit strategy for GetExitCodeThread eThread->ExitStatus = ExitStatus; eThread->Tcb.Header.SignalState = 1; diff --git a/src/core/kernel/support/NativeHandle.cpp b/src/core/kernel/support/NativeHandle.cpp index 123483374..6edd55e83 100644 --- a/src/core/kernel/support/NativeHandle.cpp +++ b/src/core/kernel/support/NativeHandle.cpp @@ -32,6 +32,7 @@ #include "assert.h" #include "NativeHandle.h" #include "core\kernel\init\CxbxKrnl.h" +#include "core/kernel/support/EmuFS.h" std::unordered_map g_RegisteredHandles; From 6320dd553915704d2c00ec280c0aa1029797161c Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Sat, 29 Jan 2022 17:16:01 -0600 Subject: [PATCH 16/25] fix shutdown process crashed on shutdown and reboots --- src/common/Timer.cpp | 26 ++++++++++++++++++++++++++ src/common/Timer.h | 1 + src/core/kernel/init/CxbxKrnl.cpp | 17 +++++++++++------ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/common/Timer.cpp b/src/common/Timer.cpp index 457b3867f..947cd471e 100644 --- a/src/common/Timer.cpp +++ b/src/common/Timer.cpp @@ -122,6 +122,32 @@ void Timer_Destroy(TimerObject* Timer) TimerList.erase(TimerList.begin() + index); } +void Timer_Shutdown() +{ + unsigned int index, i; + TimerMtx.lock(); + + index = TimerList.size(); + for (i = 0; i < index; i++) { + TimerObject* Timer = TimerList[i]; + Timer_Exit(Timer); + } + + int counter = 0; + while (TimerList.size()) { + if (counter >= 8) { + break; + } + TimerMtx.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + TimerMtx.lock(); + counter++; + + } + TimerList.clear(); + TimerMtx.unlock(); +} + // Thread that runs the timer void ClockThread(TimerObject* Timer) { diff --git a/src/common/Timer.h b/src/common/Timer.h index b99bb93d9..007c75536 100644 --- a/src/common/Timer.h +++ b/src/common/Timer.h @@ -63,6 +63,7 @@ void Timer_Exit(TimerObject* Timer); void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms); uint64_t GetTime_NS(TimerObject* Timer); void Timer_Init(); +void Timer_Shutdown(); int64_t Timer_GetScaledPerformanceCounter(int64_t Period); diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index c6a74603d..0e863c303 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -1628,9 +1628,6 @@ void CxbxKrnlShutDown(bool is_reboot) g_EmuShared->SetBootFlags(&BootFlags); } - // This is very important process to prevent false positive report and allow IDEs to continue debug multiple reboots. - CxbxrKrnlSuspendThreads(); - // NOTE: This causes a hang when exiting while NV2A is processing // This is okay for now: It won't leak memory or resources since TerminateProcess will free everything // delete g_NV2A; // TODO : g_pXbox @@ -1643,15 +1640,23 @@ void CxbxKrnlShutDown(bool is_reboot) g_io_mu_metadata = nullptr; } - // Shutdown the memory manager - g_VMManager.Shutdown(); - // Shutdown the render manager if (g_renderbase != nullptr) { g_renderbase->Shutdown(); g_renderbase = nullptr; } + // This is very important process to prevent false positive report and allow IDEs to continue debug multiple reboots. + CxbxrKrnlSuspendThreads(); + + // NOTE: Require to be after g_renderbase's shutdown process. + // Next thing we need to do is shutdown our timer threads. + Timer_Shutdown(); + + // NOTE: Must be last step of shutdown process and before CxbxUnlockFilePath call! + // Shutdown the memory manager + g_VMManager.Shutdown(); + CxbxUnlockFilePath(); if (CxbxKrnl_hEmuParent != NULL && !is_reboot) { From 06f34134ff0ca19f438958d479359ec256cbb186 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Tue, 1 Feb 2022 01:10:55 +0100 Subject: [PATCH 17/25] Review remarks + use PsCreateSystemThread to start all xbox threads --- src/common/Timer.cpp | 24 ++++----- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 25 ++++------ src/core/hle/XAPI/Xapi.cpp | 7 ++- src/core/kernel/exports/EmuKrnl.cpp | 14 ++---- src/core/kernel/exports/EmuKrnlKe.cpp | 6 +-- src/core/kernel/exports/EmuKrnlKi.cpp | 15 +++--- src/core/kernel/exports/EmuKrnlNt.cpp | 8 +-- src/core/kernel/exports/EmuKrnlPs.cpp | 44 +++++++---------- src/core/kernel/init/CxbxKrnl.cpp | 46 ++++++++---------- src/core/kernel/init/CxbxKrnl.h | 2 - src/core/kernel/support/EmuFS.cpp | 59 +++++++++++++---------- src/core/kernel/support/EmuFS.h | 11 ++++- src/core/kernel/support/NativeHandle.cpp | 1 + src/devices/network/NVNetDevice.cpp | 13 ++--- 14 files changed, 135 insertions(+), 140 deletions(-) diff --git a/src/common/Timer.cpp b/src/common/Timer.cpp index 947cd471e..6bcb73057 100644 --- a/src/common/Timer.cpp +++ b/src/common/Timer.cpp @@ -25,6 +25,8 @@ // * // ****************************************************************** +#include + #ifdef _WIN32 #include #endif @@ -142,34 +144,28 @@ void Timer_Shutdown() std::this_thread::sleep_for(std::chrono::milliseconds(500)); TimerMtx.lock(); counter++; - } TimerList.clear(); TimerMtx.unlock(); } // Thread that runs the timer -void ClockThread(TimerObject* Timer) +void NTAPI ClockThread(void *TimerArg) { - uint64_t NewExpireTime; - + TimerObject *Timer = static_cast(TimerArg); if (!Timer->Name.empty()) { CxbxSetThreadName(Timer->Name.c_str()); } - if (Timer->IsXboxTimer) { - InitXboxThread(); - g_AffinityPolicy->SetAffinityXbox(); - } else { + if (!Timer->IsXboxTimer) { g_AffinityPolicy->SetAffinityOther(); } - NewExpireTime = GetNextExpireTime(Timer); + uint64_t NewExpireTime = GetNextExpireTime(Timer); while (true) { if (GetTime_NS(Timer) > NewExpireTime) { if (Timer->Exit.load()) { Timer_Destroy(Timer); - EmuKeFreeThread(); return; } Timer->Callback(Timer->Opaque); @@ -213,7 +209,13 @@ TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool Is void Timer_Start(TimerObject* Timer, uint64_t Expire_MS) { Timer->ExpireTime_MS.store(Expire_MS); - std::thread(ClockThread, Timer).detach(); + if (Timer->IsXboxTimer) { + xbox::HANDLE hThread; + xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, ClockThread, Timer, FALSE); + } + else { + std::thread(ClockThread, Timer).detach(); + } } // Retrives the frequency of the high resolution clock of the host diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 0840adb94..128bc1723 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -37,6 +37,7 @@ #include "core\kernel\init\CxbxKrnl.h" #include "core\kernel\support\Emu.h" #include "core\kernel\support\EmuFS.h" +#include "core\kernel\support\NativeHandle.h" #include "EmuShared.h" #include "..\FixedFunctionState.h" #include "core\hle\D3D8\ResourceTracker.h" @@ -221,7 +222,7 @@ static xbox::dword_xt *g_Xbox_D3DDevice; // TODO: This should b // Static Function(s) static DWORD WINAPI EmuRenderWindow(LPVOID); static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -static DWORD WINAPI EmuUpdateTickCount(LPVOID); +static xbox::void_xt NTAPI EmuUpdateTickCount(xbox::PVOID Arg); static inline void EmuVerifyResourceIsRegistered(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize); static void UpdateCurrentMSpFAndFPS(); // Used for benchmarking/fps count static void CxbxImpl_SetRenderTarget(xbox::X_D3DSurface *pRenderTarget, xbox::X_D3DSurface *pNewZStencil); @@ -627,12 +628,15 @@ void CxbxInitWindow(bool bFullInit) CxbxKrnl_hEmuParent = NULL; // create timing thread - if (bFullInit) + if (bFullInit && !bLLE_GPU) { - HANDLE hThread = CreateThread(nullptr, 0, EmuUpdateTickCount, nullptr, 0, nullptr); + xbox::HANDLE hThread; + xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, EmuUpdateTickCount, xbox::zeroptr, FALSE); // We set the priority of this thread a bit higher, to assure reliable timing : - SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL); - g_AffinityPolicy->SetAffinityOther(hThread); + auto nativeHandle = GetNativeHandle(hThread); + assert(nativeHandle); + SetThreadPriority(*nativeHandle, THREAD_PRIORITY_ABOVE_NORMAL); + g_AffinityPolicy->SetAffinityOther(*nativeHandle); } /* TODO : Port this Dxbx code : @@ -2141,21 +2145,12 @@ std::chrono::steady_clock::time_point GetNextVBlankTime() } // timing thread procedure -static DWORD WINAPI EmuUpdateTickCount(LPVOID) +static xbox::void_xt NTAPI EmuUpdateTickCount(xbox::PVOID Arg) { CxbxSetThreadName("Cxbx Timing Thread"); - // since callbacks come from here - InitXboxThread(); - EmuLog(LOG_LEVEL::DEBUG, "Timing thread is running."); - // We check for LLE flag as NV2A handles it's own VBLANK if LLE is enabled! - if (bLLE_GPU) { - EmuKeFreeThread(); - return 0; - } - auto nextVBlankTime = GetNextVBlankTime(); while(true) diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 93eeb1f86..7b309168e 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -999,15 +999,14 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait) // Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves bool Exit = false; - auto fut = WaitApc(bAlertable, UserMode, &Exit); + auto async_bool = WaitApc(bAlertable, UserMode, &Exit); dword_xt dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, dwMilliseconds, bAlertable); Exit = true; - bool result = fut.get(); - return result ? X_STATUS_USER_APC : dwRet; + bool result = async_bool.get(); - RETURN(dwRet); + RETURN(result ? X_STATUS_USER_APC : dwRet); } // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnl.cpp b/src/core/kernel/exports/EmuKrnl.cpp index 2cc6fafdc..bf8fce718 100644 --- a/src/core/kernel/exports/EmuKrnl.cpp +++ b/src/core/kernel/exports/EmuKrnl.cpp @@ -154,7 +154,7 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql) case PASSIVE_LEVEL: KiUnexpectedInterrupt(); break; - case APC_LEVEL: // = 1 + case APC_LEVEL: // = 1 HalpApcInterrupt xbox::KiExecuteKernelApc(); break; case DISPATCH_LEVEL: // = 2 @@ -221,10 +221,9 @@ std::future WaitApc(xbox::boolean_xt Alertable, xbox::char_xt WaitMode, bo // 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]() { + return std::async(std::launch::async, [Kpcr, Alertable, WaitMode, Exit]() { EmuKeSetPcr(Kpcr); xbox::PETHREAD eThread = reinterpret_cast(Kpcr->Prcb->CurrentThread); @@ -242,15 +241,12 @@ std::future WaitApc(xbox::boolean_xt Alertable, xbox::char_xt WaitMode, bo xbox::KiExecuteUserApc(); // Queue a native APC to the calling thread to forcefully terminate the wait of the WinApi 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); + [[maybe_unused]] BOOL ret = QueueUserAPC(EndWait, *GetNativeHandle(eThread->UniqueThread), 0); assert(ret); - CloseHandle(nativeHandle); EmuKeSetPcr(nullptr); return true; } - Sleep(0); + SwitchToThread(); if (*Exit) { EmuKeSetPcr(nullptr); return false; @@ -502,7 +498,7 @@ XBSYSAPI EXPORTNUM(163) xbox::void_xt FASTCALL xbox::KiUnlockDispatcherDatabase } if (OldIrql < DISPATCH_LEVEL) { - // This is wrong: this should perform a thread switch and check the kthread of the new selected thread for pending APCs. + // FIXME: this is wrong, it should perform a thread switch and check the kthread of the new selected thread for pending APCs. // We can't perform our own threads switching now, so we will just check the current thread if (KeGetCurrentThread()->ApcState.KernelApcPending) { diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 06449ab94..3ea3433f8 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -567,13 +567,13 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread // 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 = WaitApc(Alertable, WaitMode, &Exit); + auto async_bool = WaitApc(Alertable, WaitMode, &Exit); NTSTATUS ret = NtDll::NtDelayExecution(Alertable, (NtDll::LARGE_INTEGER *)Interval); Exit = true; - bool result = fut.get(); - return result ? X_STATUS_USER_APC : ret; + bool result = async_bool.get(); + RETURN(result ? X_STATUS_USER_APC : ret); } // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlKi.cpp b/src/core/kernel/exports/EmuKrnlKi.cpp index 1e7c10f97..e8cf7549c 100644 --- a/src/core/kernel/exports/EmuKrnlKi.cpp +++ b/src/core/kernel/exports/EmuKrnlKi.cpp @@ -882,25 +882,22 @@ xbox::void_xt FASTCALL xbox::KiWaitSatisfyAll return; } -template +template static xbox::void_xt KiExecuteApc() { xbox::PKTHREAD kThread = xbox::KeGetCurrentThread(); - int ApcMode; - if constexpr (KernelApc) { + if constexpr (ApcMode == xbox::KernelMode) { kThread->ApcState.KernelApcPending = FALSE; - ApcMode = xbox::KernelMode; } else { kThread->ApcState.UserApcPending = FALSE; - ApcMode = xbox::UserMode; } // Even though the apc list is per-thread, it's still possible that another thread will access it while we are processing it below xbox::KiApcListMtx.lock(); while (!IsListEmpty(&kThread->ApcState.ApcListHead[ApcMode])) { - if (KernelApc && (kThread->KernelApcDisable != 0)) { + if ((ApcMode == xbox::KernelMode) && (kThread->KernelApcDisable != 0)) { xbox::KiApcListMtx.unlock(); return; } @@ -910,7 +907,7 @@ static xbox::void_xt KiExecuteApc() Apc->Inserted = FALSE; xbox::KiApcListMtx.unlock(); - // NOTE: we never use KernelRoutine + // NOTE: we never use KernelRoutine because that is only used for kernel APCs, which we currently don't use if (Apc->NormalRoutine != xbox::zeroptr) { (Apc->NormalRoutine)(Apc->NormalContext, Apc->SystemArgument1, Apc->SystemArgument2); } @@ -924,10 +921,10 @@ static xbox::void_xt KiExecuteApc() xbox::void_xt xbox::KiExecuteKernelApc() { - KiExecuteApc(); + KiExecuteApc(); } xbox::void_xt xbox::KiExecuteUserApc() { - KiExecuteApc(); + KiExecuteApc(); } diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index e0718f9a5..54f3d2009 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -1029,6 +1029,8 @@ XBSYSAPI EXPORTNUM(206) xbox::ntstatus_xt NTAPI xbox::NtQueueApcThread LOG_FUNC_ARG(ApcReserved) LOG_FUNC_END; + // Test case: Metal Slug 3, possibly other SNK games too + PETHREAD Thread; ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast(&Thread)); if (!X_NT_SUCCESS(result)) { @@ -2199,7 +2201,7 @@ 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 bool Exit = false; - auto fut = WaitApc(Alertable, WaitMode, &Exit); + auto async_bool = WaitApc(Alertable, WaitMode, &Exit); NTSTATUS ret = NtDll::NtWaitForMultipleObjects( Count, @@ -2209,8 +2211,8 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx (NtDll::PLARGE_INTEGER)Timeout); Exit = true; - bool result = fut.get(); - return result ? X_STATUS_USER_APC : ret; + bool result = async_bool.get(); + RETURN(result ? X_STATUS_USER_APC : ret); } // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index 84a96bcbf..ecf57fc0c 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -64,6 +64,7 @@ PCSTProxyParam; xbox::PCREATE_THREAD_NOTIFY_ROUTINE g_pfnThreadNotification[PSP_MAX_CREATE_THREAD_NOTIFY] = { xbox::zeroptr }; int g_iThreadNotificationCount = 0; +static std::mutex PspThreadNotificationMtx; // Separate function for logging, otherwise in PCSTProxy __try wont work (Compiler Error C2712) void LOG_PCSTProxy @@ -82,16 +83,6 @@ void LOG_PCSTProxy LOG_FUNC_END; } -// Overload which doesn't change affinity -void InitXboxThread(xbox::PVOID Ethread) -{ - // initialize FS segment selector - EmuGenerateFS(CxbxKrnl_TLS, CxbxKrnl_TLSData, Ethread); - - _controlfp(_PC_53, _MCW_PC); // Set Precision control to 53 bits (verified setting) - _controlfp(_RC_NEAR, _MCW_RC); // Set Rounding control to near (unsure about this) -} - // PsCreateSystemThread proxy procedure // Dxbx Note : The signature of PCSTProxy should conform to System.TThreadFunc ! static unsigned int WINAPI PCSTProxy @@ -113,9 +104,8 @@ static unsigned int WINAPI PCSTProxy params.SystemRoutine, params.Ethread); - // Do minimal thread initialization - InitXboxThread(params.Ethread); + EmuGenerateFS(CxbxKrnl_TLS, CxbxKrnl_TLSData, static_cast(params.Ethread)); auto routine = (xbox::PKSYSTEM_ROUTINE)params.SystemRoutine; // Debugging notice : When the below line shows up with an Exception dialog and a @@ -150,6 +140,17 @@ xbox::PETHREAD xbox::PspGetCurrentThread() return reinterpret_cast(KeGetCurrentThread()); } +static xbox::void_xt PspCallThreadNotificationRoutines(xbox::PETHREAD eThread, xbox::boolean_xt Create) +{ + std::unique_lock lck(PspThreadNotificationMtx); + for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) { + if (g_pfnThreadNotification[i]) { + EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", i, g_pfnThreadNotification[i]); + (*g_pfnThreadNotification[i])(eThread, eThread->UniqueThread, Create); + } + } +} + // ****************************************************************** // * 0x00FE - PsCreateSystemThread() // ****************************************************************** @@ -250,14 +251,9 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx RETURN(result); } - // Call thread notification routine(s) + // Call the thread notification routine(s) if (g_iThreadNotificationCount) { - for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) { - if (g_pfnThreadNotification[i]) { - EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", i, g_pfnThreadNotification[i]); - (*g_pfnThreadNotification[i])(eThread, eThread->UniqueThread, TRUE); - } - } + PspCallThreadNotificationRoutines(eThread, TRUE); } // Create another handle to pass back to the title in the ThreadHandle argument @@ -298,6 +294,7 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx KiUniqueProcess.StackCount++; RegisterXboxHandle(*ThreadHandle, handle); HANDLE dupHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId); + assert(dupHandle); RegisterXboxHandle(eThread->UniqueThread, dupHandle); g_AffinityPolicy->SetAffinityXbox(handle); @@ -371,15 +368,10 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread { LOG_FUNC_ONE_ARG(ExitStatus); - // Call thread notification routine(s) + // Call the thread notification routine(s) xbox::PETHREAD eThread = xbox::PspGetCurrentThread(); if (eThread->UniqueThread && g_iThreadNotificationCount) { - for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) { - if (g_pfnThreadNotification[i]) { - EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", i, g_pfnThreadNotification[i]); - (*g_pfnThreadNotification[i])(eThread, eThread->UniqueThread, FALSE); - } - } + PspCallThreadNotificationRoutines(eThread, FALSE); } EmuKeFreeThread(ExitStatus); diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 0e863c303..3eff4753f 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -141,9 +141,10 @@ void SetupPerTitleKeys() } -void CxbxLaunchXbe(void(*Entry)()) +xbox::void_xt NTAPI CxbxLaunchXbe(xbox::PVOID Entry) { - Entry(); + EmuLogInit(LOG_LEVEL::DEBUG, "Calling XBE entry point..."); + static_cast(Entry)(); } // Entry point address XOR keys per Xbe type (Retail, Debug or Chihiro) : @@ -345,14 +346,10 @@ void TriggerPendingConnectedInterrupts() } } -static unsigned int WINAPI CxbxKrnlInterruptThread(PVOID param) +static xbox::void_xt NTAPI CxbxKrnlInterruptThread(xbox::PVOID param) { CxbxSetThreadName("CxbxKrnl Interrupts"); - // Make sure Xbox1 code runs on one core : - InitXboxThread(); - g_AffinityPolicy->SetAffinityXbox(); - #if 0 InitSoftwareInterrupts(); #endif @@ -364,9 +361,7 @@ static unsigned int WINAPI CxbxKrnlInterruptThread(PVOID param) Sleep(1); } - EmuKeFreeThread(); - - return 0; + assert(0); } static void CxbxKrnlClockThread(void* pVoid) @@ -1412,6 +1407,14 @@ static void CxbxrKrnlInitHacks() EmuLogInit(LOG_LEVEL::DEBUG, "Determining CPU affinity."); g_AffinityPolicy = AffinityPolicy::InitPolicy(); + // Create a kpcr for this thread. This is necessary because ObInitSystem needs to access the irql. This must also be done before + // CxbxInitWindow because that function creates the xbox EmuUpdateTickCount thread + EmuGenerateFS(nullptr, nullptr, xbox::zeroptr); + if (!xbox::ObInitSystem()) { + CxbxrKrnlAbortEx(LOG_PREFIX_INIT, "Unable to intialize ObInitSystem."); + } + xbox::KiInitSystem(); + // initialize graphics EmuLogInit(LOG_LEVEL::DEBUG, "Initializing render window."); CxbxInitWindow(true); @@ -1492,13 +1495,6 @@ static void CxbxrKrnlInitHacks() EmuInitFS(); - InitXboxThread(); - g_AffinityPolicy->SetAffinityXbox(); - if (!xbox::ObInitSystem()) { - CxbxrKrnlAbortEx(LOG_PREFIX_INIT, "Unable to intialize ObInitSystem."); - } - xbox::KiInitSystem(); - #ifdef CHIHIRO_WORK // If this title is Chihiro, Setup JVS if (g_bIsChihiro) { @@ -1508,16 +1504,15 @@ static void CxbxrKrnlInitHacks() EmuX86_Init(); // Create the interrupt processing thread - DWORD dwThreadId; - HANDLE hThread = (HANDLE)_beginthreadex(NULL, NULL, CxbxKrnlInterruptThread, NULL, NULL, (unsigned int*)&dwThreadId); + xbox::HANDLE hThread; + xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxKrnlInterruptThread, xbox::zeroptr, FALSE); // Start the kernel clock thread TimerObject* KernelClockThr = Timer_Create(CxbxKrnlClockThread, nullptr, "Kernel clock thread", true); Timer_Start(KernelClockThr, SCALE_MS_IN_NS); - EmuLogInit(LOG_LEVEL::DEBUG, "Calling XBE entry point..."); - CxbxLaunchXbe(Entry); + xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxLaunchXbe, Entry, FALSE); - EmuKeFreeThread(); + EmuKeFreePcr(); // FIXME: Wait for Cxbx to exit or error fatally Sleep(INFINITE); @@ -1578,11 +1573,12 @@ static void CxbxrKrnlInitHacks() void CxbxrKrnlSuspendThreads() { xbox::PLIST_ENTRY ThreadListEntry = KiUniqueProcess.ThreadListHead.Flink; - std::vector threads; + std::vector threads; threads.reserve(KiUniqueProcess.StackCount); - xbox::KPCR* Pcr = EmuKeGetPcr(); - + // Don't use EmuKeGetPcr because that asserts kpcr + xbox::KPCR* Pcr = reinterpret_cast(__readfsdword(TIB_ArbitraryDataSlot)); + // If there's nothing in list entry, skip this step. if (!ThreadListEntry) { return; diff --git a/src/core/kernel/init/CxbxKrnl.h b/src/core/kernel/init/CxbxKrnl.h index f59818e5b..38e27daec 100644 --- a/src/core/kernel/init/CxbxKrnl.h +++ b/src/core/kernel/init/CxbxKrnl.h @@ -184,8 +184,6 @@ extern ULONG g_CxbxFatalErrorCode; extern size_t g_SystemMaxMemory; -void InitXboxThread(xbox::PVOID Ethread = xbox::zeroptr); - /*! thread local storage structure */ extern Xbe::TLS *CxbxKrnl_TLS; diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index b46b9b9e7..2e2c23116 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -45,6 +45,8 @@ #undef RtlZeroMemory #endif +#define TLS_ALIGNMENT_OFFSET 12 + // NT_TIB (Thread Information Block) offsets - see https://www.microsoft.com/msj/archive/S2CE.aspx #define TIB_ExceptionList offsetof(NT_TIB, ExceptionList) // = 0x00/0 #define TIB_StackBase offsetof(NT_TIB, StackBase) // = 0x04/4 @@ -113,6 +115,11 @@ // = 0x104/260 */ LIST_ENTRY ThreadListEntry; // = 0x10C/268 */ UCHAR _padding[4]; +template void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread); +template void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread); +template void EmuKeFreePcr(); +template void EmuKeFreePcr(); + NT_TIB *GetNtTib() { return (NT_TIB *)__readfsdword(TIB_LinearSelfAddress); @@ -187,10 +194,10 @@ void EmuKeSetPcr(xbox::KPCR *Pcr) __writefsdword(TIB_ArbitraryDataSlot, (DWORD)Pcr); } -void EmuKeFreePcr(xbox::HANDLE UniqueThread) +template +void EmuKeFreePcr() { xbox::PKPCR Pcr = EmuKeGetPcr(); - xbox::PVOID Dummy; xbox::ulong_xt Size; xbox::ntstatus_xt Status; @@ -198,12 +205,13 @@ void EmuKeFreePcr(xbox::HANDLE UniqueThread) if (Pcr->NtTib.StackBase) { // NOTE: the tls pointer was increased by 12 bytes to enforce the 16 bytes alignment, so adjust it to reach the correct pointer // that was allocated by xbox::NtAllocateVirtualMemory - Dummy = static_cast(Pcr->NtTib.StackBase) - 12; + Dummy = static_cast(Pcr->NtTib.StackBase) - TLS_ALIGNMENT_OFFSET; Size = xbox::zero; Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free tls assert(Status == X_STATUS_SUCCESS); } - if (UniqueThread == reinterpret_cast(GetCurrentThreadId())) { + if constexpr (IsHostThread) { + // This only happens for the kernel initialization thread of cxbxr Dummy = Pcr->Prcb->CurrentThread; Size = xbox::zero; Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free ethread @@ -218,8 +226,7 @@ void EmuKeFreePcr(xbox::HANDLE UniqueThread) void EmuKeFreeThread(xbox::ntstatus_xt ExitStatus) { - // This functions is to be used for cxbxr threads that execute xbox code. We can't just call PsTerminateSystemThread because some additional - // xbox state is not created for this kind of threads + // Free all kernel resources that were allocated fo this thread xbox::KeEmptyQueueApc(); @@ -234,13 +241,12 @@ void EmuKeFreeThread(xbox::ntstatus_xt ExitStatus) eThread->ExitStatus = ExitStatus; eThread->Tcb.Header.SignalState = 1; - xbox::HANDLE UniqueThread = eThread->UniqueThread; if (GetNativeHandle(eThread->UniqueThread)) { xbox::NtClose(eThread->UniqueThread); - eThread->UniqueThread = NULL; + eThread->UniqueThread = xbox::zero; } - EmuKeFreePcr(UniqueThread); + EmuKeFreePcr(); } __declspec(naked) void EmuFS_RefreshKPCR() @@ -674,7 +680,8 @@ void EmuInitFS() } // generate fs segment selector -void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread) +template +void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread) { void *pNewTLS = nullptr; xbox::PVOID base; @@ -704,7 +711,7 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread) pNewTLS = (void*)base; xbox::RtlZeroMemory(pNewTLS, dwCopySize + dwZeroSize + 0x100 + 0xC); /* Skip the first 12 bytes so that TLSData will be 16 byte aligned (addr returned by NtAllocateVirtualMemory is 4K aligned) */ - pNewTLS = (uint8_t*)pNewTLS + 12; + pNewTLS = (uint8_t*)pNewTLS + TLS_ALIGNMENT_OFFSET; if (dwCopySize > 0) { memcpy((uint8_t*)pNewTLS + 4, pTLSData, dwCopySize); @@ -801,23 +808,22 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread) NewPcr->Irql = PASSIVE_LEVEL; // See KeLowerIrql; } + if constexpr (IsHostThread) { + // This only happens for the kernel initialization thread of cxbxr + assert(Ethread == xbox::zeroptr); + + base = xbox::zeroptr; + size = sizeof(xbox::ETHREAD); + xbox::NtAllocateVirtualMemory(&base, 0, &size, XBOX_MEM_RESERVE | XBOX_MEM_COMMIT, XBOX_PAGE_READWRITE); + Ethread = (xbox::PETHREAD)base; + xbox::RtlZeroMemory(Ethread, sizeof(xbox::ETHREAD)); // Clear, to prevent side-effects on random contents + } + // Initialize a fake PrcbData.CurrentThread { - // If it is nullptr, it means we are creating this thread from cxbxr, otherwise we were created from PsCreateSystemThreadEx - xbox::PETHREAD EThread = static_cast(Ethread); - if (EThread == xbox::zeroptr) { - // Since this a host thread that we use to execute xbox code, we do not need to create ob handles for this thread - base = xbox::zeroptr; - size = sizeof(xbox::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->Tcb.TlsData = pNewTLS; // Set PrcbData.CurrentThread - Prcb->CurrentThread = (xbox::KTHREAD*)EThread; + Prcb->CurrentThread = (xbox::PKTHREAD)Ethread; + Prcb->CurrentThread->TlsData = pNewTLS; // Initialize APC stuff InitializeListHead(&Prcb->CurrentThread->ApcState.ApcListHead[xbox::KernelMode]); InitializeListHead(&Prcb->CurrentThread->ApcState.ApcListHead[xbox::UserMode]); @@ -844,4 +850,7 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread) EmuKeSetPcr(NewPcr); EmuLog(LOG_LEVEL::DEBUG, "Installed KPCR in TIB_ArbitraryDataSlot (with pTLS = 0x%.8X)", pTLS); + + _controlfp(_PC_53, _MCW_PC); // Set Precision control to 53 bits (verified setting) + _controlfp(_RC_NEAR, _MCW_RC); // Set Rounding control to near (unsure about this) } diff --git a/src/core/kernel/support/EmuFS.h b/src/core/kernel/support/EmuFS.h index c1b342459..27da33144 100644 --- a/src/core/kernel/support/EmuFS.h +++ b/src/core/kernel/support/EmuFS.h @@ -33,11 +33,13 @@ extern void EmuInitFS(); // generate fs segment selector -extern void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread); +template +void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread); // free resources allocated for the thread void EmuKeFreeThread(xbox::ntstatus_xt ExitStatus = X_STATUS_ABANDONED); // free kpcr allocated for the thread -void EmuKeFreePcr(xbox::HANDLE UniqueThread); +template +void EmuKeFreePcr(); void EmuKeSetPcr(xbox::KPCR *Pcr); xbox::KPCR *_stdcall EmuKeGetPcr(); @@ -48,4 +50,9 @@ typedef struct void* functionPtr; }fs_instruction_t; +extern template void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread); +extern template void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread); +extern template void EmuKeFreePcr(); +extern template void EmuKeFreePcr(); + #endif diff --git a/src/core/kernel/support/NativeHandle.cpp b/src/core/kernel/support/NativeHandle.cpp index 6edd55e83..67f3816c5 100644 --- a/src/core/kernel/support/NativeHandle.cpp +++ b/src/core/kernel/support/NativeHandle.cpp @@ -45,6 +45,7 @@ void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle) if (ret.second == false) { // This can happen when an ob handle has been destroyed, but then a thread switch happens before the first thread // got a chance to remove the old handle from g_RegisteredHandles with RemoveXboxHandle + // Test case: dashboard auto now = std::chrono::system_clock::now(); auto timeout = now + std::chrono::milliseconds(2000); while (now <= timeout) { diff --git a/src/devices/network/NVNetDevice.cpp b/src/devices/network/NVNetDevice.cpp index 0bceed388..7b47ddac0 100644 --- a/src/devices/network/NVNetDevice.cpp +++ b/src/devices/network/NVNetDevice.cpp @@ -536,21 +536,21 @@ void NVNetDevice::Reset() bool NVNetDevice::GetMacAddress(std::string adapterName, void* pMAC) { - IP_ADAPTER_INFO AdapterInfo[128]; - PIP_ADAPTER_INFO pAdapterInfo; - ULONG dwBufferLength = sizeof(AdapterInfo); + // AdapterInfo is too large to be allocated on the stack, and will cause a crash in debug builds when _chkstk detects it + PIP_ADAPTER_INFO pAdapterInfo = new IP_ADAPTER_INFO[128]; + ULONG dwBufferLength = sizeof(IP_ADAPTER_INFO) * 128; - DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufferLength); + DWORD dwStatus = GetAdaptersInfo(pAdapterInfo, &dwBufferLength); if (dwStatus != ERROR_SUCCESS) { + delete[] pAdapterInfo; return false; } - pAdapterInfo = AdapterInfo; - // Find the specified adapter do { if (strcmp(pAdapterInfo->AdapterName, adapterName.c_str()) == 0) { memcpy(pMAC, pAdapterInfo->Address, 6); + delete[] pAdapterInfo; return true; } @@ -558,6 +558,7 @@ bool NVNetDevice::GetMacAddress(std::string adapterName, void* pMAC) } while (pAdapterInfo); + delete[] pAdapterInfo; return false; } From 44ed2ee3aa28e056324326a028efb477f17284a2 Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Wed, 2 Feb 2022 17:49:54 -0600 Subject: [PATCH 18/25] update Timer_Shutdown to reduce wait time for shutdown if threads are all xbox --- src/common/Timer.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/common/Timer.cpp b/src/common/Timer.cpp index 6bcb73057..af569c1fd 100644 --- a/src/common/Timer.cpp +++ b/src/common/Timer.cpp @@ -126,17 +126,25 @@ void Timer_Destroy(TimerObject* Timer) void Timer_Shutdown() { - unsigned int index, i; + unsigned i, iXboxThreads = 0; TimerMtx.lock(); - index = TimerList.size(); - for (i = 0; i < index; i++) { + for (i = 0; i < TimerList.size(); i++) { TimerObject* Timer = TimerList[i]; - Timer_Exit(Timer); + // We only need to terminate host threads. + if (!Timer->IsXboxTimer) { + Timer_Exit(Timer); + } + // If the thread is xbox, we need to increment for while statement check + else { + iXboxThreads++; + } } + // Only perform wait for host threads, otherwise xbox threads are + // already handled within xbox kernel for shutdown process. See CxbxrKrnlSuspendThreads function. int counter = 0; - while (TimerList.size()) { + while (iXboxThreads != TimerList.size()) { if (counter >= 8) { break; } From a769e896c6a261c8e5264d6475e1b10df66241f9 Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Wed, 2 Feb 2022 17:51:10 -0600 Subject: [PATCH 19/25] hidden bug while debugging on xbox kernel thread's issue --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 128bc1723..cf502d96c 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -759,7 +759,7 @@ static void CxbxUpdateCursor(bool forceShow = false) { return; } - if (g_renderbase->IsImGuiFocus() || forceShow) { + if (g_renderbase && g_renderbase->IsImGuiFocus() || forceShow) { if (cursorInfo.flags == 0) { ShowCursor(TRUE); } From 7589f0a94c562b463b44e6b66cefab5b7c4ec9cf Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Thu, 17 Feb 2022 02:33:44 +0100 Subject: [PATCH 20/25] Avoid using std::async in WaitApc --- src/core/hle/XAPI/Xapi.cpp | 20 ++++++----- src/core/kernel/common/types.h | 1 + src/core/kernel/exports/EmuKrnl.cpp | 44 ------------------------ src/core/kernel/exports/EmuKrnl.h | 37 +++++++++++++++++++- src/core/kernel/exports/EmuKrnlKe.cpp | 43 ++++++++++------------- src/core/kernel/exports/EmuKrnlKi.cpp | 17 +++++++++ src/core/kernel/exports/EmuKrnlKi.h | 7 ++++ src/core/kernel/exports/EmuKrnlNt.cpp | 35 +++++++++++++------ src/core/kernel/support/NativeHandle.cpp | 1 + 9 files changed, 117 insertions(+), 88 deletions(-) diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 7b309168e..4d51e8ba7 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -38,6 +38,7 @@ #include "Logging.h" #include "core\kernel\support\Emu.h" #include "core\kernel\exports\EmuKrnl.h" // For DefaultLaunchDataPage +#include "core\kernel\exports\EmuKrnlKi.h" #include "core\kernel\support\EmuFile.h" #include "core\kernel\support\NativeHandle.h" #include "EmuShared.h" @@ -997,16 +998,19 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait) LOG_FUNC_ARG(bAlertable) LOG_FUNC_END; - // Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves - bool Exit = false; - auto async_bool = WaitApc(bAlertable, UserMode, &Exit); + LARGE_INTEGER ExpireTime, DueTime, NewTime; + ExpireTime.QuadPart = DueTime.QuadPart = (dwMilliseconds == INFINITE) ? ~0ull : -dwMilliseconds; + KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime); - dword_xt dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, dwMilliseconds, bAlertable); + xbox::dword_xt ret = WaitApc([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional { + DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable); + if (dwRet == WAIT_TIMEOUT) { + return std::nullopt; + } + return std::make_optional(dwRet); + }, &NewTime, bAlertable, UserMode); - Exit = true; - bool result = async_bool.get(); - - RETURN(result ? X_STATUS_USER_APC : dwRet); + RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret); } // ****************************************************************** diff --git a/src/core/kernel/common/types.h b/src/core/kernel/common/types.h index d55b8f838..ddbaaa3b1 100644 --- a/src/core/kernel/common/types.h +++ b/src/core/kernel/common/types.h @@ -105,6 +105,7 @@ typedef void* LPSECURITY_ATTRIBUTES; #define X_STATUS_NOT_COMMITTED 0xC000002DL #define X_STATUS_UNRECOGNIZED_VOLUME 0xC000014FL #define X_STATUS_OBJECT_PATH_NOT_FOUND 0xC000003AL +#define X_STATUS_TIMEOUT 0x00000102L // ****************************************************************** // * Registry value types diff --git a/src/core/kernel/exports/EmuKrnl.cpp b/src/core/kernel/exports/EmuKrnl.cpp index bf8fce718..361d06f7f 100644 --- a/src/core/kernel/exports/EmuKrnl.cpp +++ b/src/core/kernel/exports/EmuKrnl.cpp @@ -210,50 +210,6 @@ const DWORD IrqlMasks[] = { 0x00000000, // IRQL 31 (HIGH_LEVEL) }; -// This helper function is used to signal WinApi waiting functions that the wait has been satisfied by an xbox user APC -static void WINAPI EndWait(ULONG_PTR Parameter) -{ - // Do nothing -} - -std::future WaitApc(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(); - - // This new thread must execute APCs in the context of the calling thread - return std::async(std::launch::async, [Kpcr, Alertable, WaitMode, 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 WinApi functions, - // in the case it didn't terminate already - [[maybe_unused]] BOOL ret = QueueUserAPC(EndWait, *GetNativeHandle(eThread->UniqueThread), 0); - assert(ret); - EmuKeSetPcr(nullptr); - return true; - } - SwitchToThread(); - 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 3e83d5330..5414174b8 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 "core\kernel\support\EmuFS.h" #include // CONTAINING_RECORD macro @@ -104,6 +105,40 @@ extern HalSystemInterrupt HalSystemInterrupts[MAX_BUS_INTERRUPT_LEVEL + 1]; bool DisableInterrupts(); void RestoreInterruptMode(bool value); void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql); -std::future WaitApc(xbox::boolean_xt Alertable, xbox::char_xt WaitMode, bool *Exit); + +template +xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER AbsoluteExpireTime, xbox::boolean_xt Alertable, xbox::char_xt WaitMode) +{ + // NOTE: kThread->Alerted is currently never set. When the alerted mechanism is implemented, the alerts should + // also interrupt the wait + + xbox::ulonglong_xt now = xbox::KeQueryInterruptTime(); + xbox::PETHREAD eThread = reinterpret_cast(EmuKeGetPcr()->Prcb->CurrentThread); + + while (now <= static_cast(AbsoluteExpireTime->QuadPart)) { + if (const auto ret = Lambda()) { + return *ret; + } + + 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(); + return X_STATUS_USER_APC; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + now = xbox::KeQueryInterruptTime(); + } + + return X_STATUS_TIMEOUT; +} #endif diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 3ea3433f8..82c9d306a 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -566,14 +566,27 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread // 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 async_bool = WaitApc(Alertable, WaitMode, &Exit); + LARGE_INTEGER NewTime; + if (Interval) { + LARGE_INTEGER ExpireTime, DueTime; + ExpireTime.QuadPart = DueTime.QuadPart = Interval->QuadPart; + KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime); + } + else { + NewTime.QuadPart = ~0ull; + } - NTSTATUS ret = NtDll::NtDelayExecution(Alertable, (NtDll::LARGE_INTEGER *)Interval); + xbox::ntstatus_xt ret = WaitApc([Alertable]() -> std::optional { + NtDll::LARGE_INTEGER ExpireTime; + ExpireTime.QuadPart = 0; + NTSTATUS Status = NtDll::NtDelayExecution(Alertable, &ExpireTime); + if (Status == 0) { // STATUS_SUCCESS + return std::nullopt; + } + return std::make_optional(Status); + }, &NewTime, Alertable, WaitMode); - Exit = true; - bool result = async_bool.get(); - RETURN(result ? X_STATUS_USER_APC : ret); + RETURN(ret); } // ****************************************************************** @@ -2000,24 +2013,6 @@ XBSYSAPI EXPORTNUM(156) xbox::dword_xt VOLATILE xbox::KeTickCount = 0; // ****************************************************************** XBSYSAPI EXPORTNUM(157) xbox::ulong_xt xbox::KeTimeIncrement = CLOCK_TIME_INCREMENT; - -xbox::PLARGE_INTEGER FASTCALL KiComputeWaitInterval( - IN xbox::PLARGE_INTEGER OriginalTime, - IN xbox::PLARGE_INTEGER DueTime, - IN OUT xbox::PLARGE_INTEGER NewTime -) -{ - if (OriginalTime->QuadPart >= 0) { - return OriginalTime; - - } - else { - NewTime->QuadPart = xbox::KeQueryInterruptTime(); - NewTime->QuadPart -= DueTime->QuadPart; - return NewTime; - } -} - // ****************************************************************** // * 0x009E - KeWaitForMultipleObjects() // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlKi.cpp b/src/core/kernel/exports/EmuKrnlKi.cpp index e8cf7549c..2d8b5c395 100644 --- a/src/core/kernel/exports/EmuKrnlKi.cpp +++ b/src/core/kernel/exports/EmuKrnlKi.cpp @@ -928,3 +928,20 @@ xbox::void_xt xbox::KiExecuteUserApc() { KiExecuteApc(); } + +xbox::PLARGE_INTEGER FASTCALL xbox::KiComputeWaitInterval +( + IN xbox::PLARGE_INTEGER OriginalTime, + IN xbox::PLARGE_INTEGER DueTime, + IN OUT xbox::PLARGE_INTEGER NewTime +) +{ + if (OriginalTime->QuadPart >= 0) { + return OriginalTime; + } + else { + NewTime->QuadPart = xbox::KeQueryInterruptTime(); + NewTime->QuadPart -= DueTime->QuadPart; + return NewTime; + } +} diff --git a/src/core/kernel/exports/EmuKrnlKi.h b/src/core/kernel/exports/EmuKrnlKi.h index 501c53b72..0e9dc3481 100644 --- a/src/core/kernel/exports/EmuKrnlKi.h +++ b/src/core/kernel/exports/EmuKrnlKi.h @@ -139,6 +139,13 @@ namespace xbox void_xt KiExecuteKernelApc(); void_xt KiExecuteUserApc(); + + PLARGE_INTEGER FASTCALL KiComputeWaitInterval + ( + IN PLARGE_INTEGER OriginalTime, + IN PLARGE_INTEGER DueTime, + IN OUT PLARGE_INTEGER NewTime + ); }; extern xbox::KPROCESS KiUniqueProcess; diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index 54f3d2009..e62ef2ad5 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -2200,19 +2200,32 @@ 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 - bool Exit = false; - auto async_bool = WaitApc(Alertable, WaitMode, &Exit); + LARGE_INTEGER NewTime; + if (Timeout) { + LARGE_INTEGER ExpireTime, DueTime; + ExpireTime.QuadPart = DueTime.QuadPart = Timeout->QuadPart; + KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime); + } + else { + NewTime.QuadPart = ~0ull; + } - NTSTATUS ret = NtDll::NtWaitForMultipleObjects( - Count, - Handles, - (NtDll::OBJECT_WAIT_TYPE)WaitType, - Alertable, - (NtDll::PLARGE_INTEGER)Timeout); + xbox::ntstatus_xt ret = WaitApc([Count, Handles, WaitType, Alertable]() -> std::optional { + NtDll::LARGE_INTEGER ExpireTime; + ExpireTime.QuadPart = 0; + NTSTATUS Status = NtDll::NtWaitForMultipleObjects( + Count, + Handles, + (NtDll::OBJECT_WAIT_TYPE)WaitType, + Alertable, + &ExpireTime); + if (Status == STATUS_TIMEOUT) { + return std::nullopt; + } + return std::make_optional(Status); + }, &NewTime, Alertable, WaitMode); - Exit = true; - bool result = async_bool.get(); - RETURN(result ? X_STATUS_USER_APC : ret); + RETURN(ret); } // ****************************************************************** diff --git a/src/core/kernel/support/NativeHandle.cpp b/src/core/kernel/support/NativeHandle.cpp index 67f3816c5..74f95141f 100644 --- a/src/core/kernel/support/NativeHandle.cpp +++ b/src/core/kernel/support/NativeHandle.cpp @@ -56,6 +56,7 @@ void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle) if (ret.second) { return; } + now += std::chrono::milliseconds(5); } // If we reach here, it means that we could not insert the handle after more than two seconds of trying. This probably means From 6867907a3c11f1bd7b9ab12a79b48c7f97c9696d Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Thu, 17 Feb 2022 11:43:06 +0100 Subject: [PATCH 21/25] Fixed a bug in NtWaitForMultipleObjectsEx that caused the dashboard to deadlock + more review remarks --- src/core/hle/XAPI/Xapi.cpp | 17 +++++++++++++---- src/core/kernel/exports/EmuKrnl.h | 5 +++++ src/core/kernel/exports/EmuKrnlKe.cpp | 6 ++++-- src/core/kernel/exports/EmuKrnlNt.cpp | 6 ++++-- src/core/kernel/exports/EmuKrnlPs.cpp | 12 +++++------- src/core/kernel/support/NativeHandle.cpp | 4 ++-- 6 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 4d51e8ba7..f63635e2a 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -998,9 +998,18 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait) LOG_FUNC_ARG(bAlertable) LOG_FUNC_END; - LARGE_INTEGER ExpireTime, DueTime, NewTime; - ExpireTime.QuadPart = DueTime.QuadPart = (dwMilliseconds == INFINITE) ? ~0ull : -dwMilliseconds; - KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime); + // Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves + LARGE_INTEGER NewTime; + PLARGE_INTEGER pNewTime; + if (dwMilliseconds == INFINITE) { + NewTime.QuadPart = ~0ull; + pNewTime = &NewTime; + } + else { + LARGE_INTEGER ExpireTime, DueTime; + ExpireTime.QuadPart = DueTime.QuadPart = -dwMilliseconds; + pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime); + } xbox::dword_xt ret = WaitApc([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional { DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable); @@ -1008,7 +1017,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait) return std::nullopt; } return std::make_optional(dwRet); - }, &NewTime, bAlertable, UserMode); + }, pNewTime, bAlertable, UserMode); RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret); } diff --git a/src/core/kernel/exports/EmuKrnl.h b/src/core/kernel/exports/EmuKrnl.h index 5414174b8..5b3247eeb 100644 --- a/src/core/kernel/exports/EmuKrnl.h +++ b/src/core/kernel/exports/EmuKrnl.h @@ -115,6 +115,11 @@ xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER AbsoluteExpireTime, x xbox::ulonglong_xt now = xbox::KeQueryInterruptTime(); xbox::PETHREAD eThread = reinterpret_cast(EmuKeGetPcr()->Prcb->CurrentThread); + if (AbsoluteExpireTime->QuadPart == 0) { + // This will only happen when the title specifies a zero timeout + AbsoluteExpireTime->QuadPart = now; + } + while (now <= static_cast(AbsoluteExpireTime->QuadPart)) { if (const auto ret = Lambda()) { return *ret; diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 82c9d306a..1d27e5051 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -567,13 +567,15 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread // We can't remove NtDll::NtDelayExecution until all APCs queued by Io are implemented by our kernel as well // Test case: Metal Slug 3 LARGE_INTEGER NewTime; + PLARGE_INTEGER pNewTime; if (Interval) { LARGE_INTEGER ExpireTime, DueTime; ExpireTime.QuadPart = DueTime.QuadPart = Interval->QuadPart; - KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime); + pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime); } else { NewTime.QuadPart = ~0ull; + pNewTime = &NewTime; } xbox::ntstatus_xt ret = WaitApc([Alertable]() -> std::optional { @@ -584,7 +586,7 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread return std::nullopt; } return std::make_optional(Status); - }, &NewTime, Alertable, WaitMode); + }, pNewTime, Alertable, WaitMode); RETURN(ret); } diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index e62ef2ad5..02935c80f 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -2201,13 +2201,15 @@ 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 LARGE_INTEGER NewTime; + PLARGE_INTEGER pNewTime; if (Timeout) { LARGE_INTEGER ExpireTime, DueTime; ExpireTime.QuadPart = DueTime.QuadPart = Timeout->QuadPart; - KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime); + pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime); } else { NewTime.QuadPart = ~0ull; + pNewTime = &NewTime; } xbox::ntstatus_xt ret = WaitApc([Count, Handles, WaitType, Alertable]() -> std::optional { @@ -2223,7 +2225,7 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx return std::nullopt; } return std::make_optional(Status); - }, &NewTime, Alertable, WaitMode); + }, pNewTime, Alertable, WaitMode); RETURN(ret); } diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index ecf57fc0c..0b569a2f2 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -62,9 +62,9 @@ typedef struct _PCSTProxyParam } PCSTProxyParam; -xbox::PCREATE_THREAD_NOTIFY_ROUTINE g_pfnThreadNotification[PSP_MAX_CREATE_THREAD_NOTIFY] = { xbox::zeroptr }; -int g_iThreadNotificationCount = 0; -static std::mutex PspThreadNotificationMtx; +static xbox::PCREATE_THREAD_NOTIFY_ROUTINE g_pfnThreadNotification[PSP_MAX_CREATE_THREAD_NOTIFY] = { xbox::zeroptr }; +static std::atomic_int g_iThreadNotificationCount = 0; +static std::mutex g_ThreadNotificationMtx; // Separate function for logging, otherwise in PCSTProxy __try wont work (Compiler Error C2712) void LOG_PCSTProxy @@ -142,7 +142,7 @@ xbox::PETHREAD xbox::PspGetCurrentThread() static xbox::void_xt PspCallThreadNotificationRoutines(xbox::PETHREAD eThread, xbox::boolean_xt Create) { - std::unique_lock lck(PspThreadNotificationMtx); + std::unique_lock lck(g_ThreadNotificationMtx); for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) { if (g_pfnThreadNotification[i]) { EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", i, g_pfnThreadNotification[i]); @@ -251,7 +251,6 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx RETURN(result); } - // Call the thread notification routine(s) if (g_iThreadNotificationCount) { PspCallThreadNotificationRoutines(eThread, TRUE); } @@ -269,7 +268,6 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx // PCSTProxy is responsible for cleaning up this pointer PCSTProxyParam *iPCSTProxyParam = new PCSTProxyParam; - iPCSTProxyParam->StartRoutine = (PVOID)StartRoutine; iPCSTProxyParam->StartContext = StartContext; iPCSTProxyParam->SystemRoutine = (PVOID)SystemRoutine; // NULL, XapiThreadStartup or unknown? @@ -344,6 +342,7 @@ XBSYSAPI EXPORTNUM(257) xbox::ntstatus_xt NTAPI xbox::PsSetCreateThreadNotifyRou { LOG_FUNC_ONE_ARG(NotifyRoutine); + std::unique_lock lck(g_ThreadNotificationMtx); for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) { if (g_pfnThreadNotification[i] == zeroptr) { g_pfnThreadNotification[i] = NotifyRoutine; @@ -368,7 +367,6 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread { LOG_FUNC_ONE_ARG(ExitStatus); - // Call the thread notification routine(s) xbox::PETHREAD eThread = xbox::PspGetCurrentThread(); if (eThread->UniqueThread && g_iThreadNotificationCount) { PspCallThreadNotificationRoutines(eThread, FALSE); diff --git a/src/core/kernel/support/NativeHandle.cpp b/src/core/kernel/support/NativeHandle.cpp index 74f95141f..9b745d818 100644 --- a/src/core/kernel/support/NativeHandle.cpp +++ b/src/core/kernel/support/NativeHandle.cpp @@ -41,7 +41,7 @@ std::shared_mutex g_MapMtx; void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle) { std::unique_lock lck(g_MapMtx); - const auto &ret = g_RegisteredHandles.try_emplace(xhandle, nhandle); + auto ret = g_RegisteredHandles.try_emplace(xhandle, nhandle); if (ret.second == false) { // This can happen when an ob handle has been destroyed, but then a thread switch happens before the first thread // got a chance to remove the old handle from g_RegisteredHandles with RemoveXboxHandle @@ -52,7 +52,7 @@ void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle) lck.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(5)); lck.lock(); - g_RegisteredHandles.try_emplace(xhandle, nhandle); + ret = g_RegisteredHandles.try_emplace(xhandle, nhandle); if (ret.second) { return; } From bc98e164b2eaaf0687c9263b89c460d0aa3d6e05 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Fri, 18 Feb 2022 15:30:18 +0100 Subject: [PATCH 22/25] Removed CxbxKrnlTerminateThread and some unnecessary calls to TerminateProcess --- src/core/kernel/exports/EmuKrnlHal.cpp | 1 - src/core/kernel/init/CxbxKrnl.cpp | 22 ++++++---------------- src/core/kernel/init/CxbxKrnl.h | 5 +---- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/core/kernel/exports/EmuKrnlHal.cpp b/src/core/kernel/exports/EmuKrnlHal.cpp index 2ed3494de..d22d0552a 100644 --- a/src/core/kernel/exports/EmuKrnlHal.cpp +++ b/src/core/kernel/exports/EmuKrnlHal.cpp @@ -599,7 +599,6 @@ XBSYSAPI EXPORTNUM(49) xbox::void_xt DECLSPEC_NORETURN NTAPI xbox::HalReturnToFi } CxbxKrnlShutDown(is_reboot); - TerminateProcess(GetCurrentProcess(), EXIT_SUCCESS); } // ****************************************************************** diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 3eff4753f..80a30ed0f 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -145,6 +145,7 @@ xbox::void_xt NTAPI CxbxLaunchXbe(xbox::PVOID Entry) { EmuLogInit(LOG_LEVEL::DEBUG, "Calling XBE entry point..."); static_cast(Entry)(); + EmuLogInit(LOG_LEVEL::DEBUG, "XBE entry point returned"); } // Entry point address XOR keys per Xbe type (Retail, Debug or Chihiro) : @@ -703,7 +704,6 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems // Launch Segaboot CxbxLaunchNewXbe(chihiroSegaBootNew); CxbxKrnlShutDown(true); - TerminateProcess(GetCurrentProcess(), EXIT_SUCCESS); } #endif // Chihiro wip block @@ -1514,16 +1514,11 @@ static void CxbxrKrnlInitHacks() EmuKeFreePcr(); - // FIXME: Wait for Cxbx to exit or error fatally - Sleep(INFINITE); - - EmuLogInit(LOG_LEVEL::DEBUG, "XBE entry point returned"); - fflush(stdout); - - CxbxUnlockFilePath(); - - // EmuShared::Cleanup(); FIXME: commenting this line is a bad workaround for issue #617 (https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/617) - CxbxKrnlTerminateThread(); + // This will wait forever + std::condition_variable cv; + std::mutex m; + std::unique_lock lock(m); + cv.wait(lock, [] { return false; }); } // REMARK: the following is useless, but PatrickvL has asked to keep it for documentation purposes @@ -1754,11 +1749,6 @@ void CxbxPrintUEMInfo(ULONG ErrorCode) } } -[[noreturn]] void CxbxKrnlTerminateThread() -{ - TerminateThread(GetCurrentThread(), 0); -} - void CxbxKrnlPanic() { CxbxrKrnlAbort("Kernel Panic!"); diff --git a/src/core/kernel/init/CxbxKrnl.h b/src/core/kernel/init/CxbxKrnl.h index 38e27daec..2de3a0e27 100644 --- a/src/core/kernel/init/CxbxKrnl.h +++ b/src/core/kernel/init/CxbxKrnl.h @@ -152,7 +152,7 @@ void CxbxKrnlEmulate(unsigned int system, blocks_reserved_t blocks_reserved); #define CxbxrKrnlAbort(fmt, ...) CxbxrKrnlAbortEx(LOG_PREFIX, fmt, ##__VA_ARGS__) /*! terminate gracefully the emulation */ -void CxbxKrnlShutDown(bool is_reboot = false); +[[noreturn]] void CxbxKrnlShutDown(bool is_reboot = false); /*! display the fatal error message*/ void CxbxKrnlPrintUEM(ULONG ErrorCode); @@ -160,9 +160,6 @@ void CxbxKrnlPrintUEM(ULONG ErrorCode); /*! display the cause of the fatal error message*/ void CxbxPrintUEMInfo(ULONG ErrorCode); -/*! terminate the calling thread */ -[[noreturn]] void CxbxKrnlTerminateThread(); - /*! kernel panic (trap for unimplemented kernel functions) */ void CxbxKrnlPanic(); From c6ea72dcf4646bc24434629ce2937e4cd9dea279 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Fri, 18 Feb 2022 19:06:39 +0100 Subject: [PATCH 23/25] Use a condition variable to notify interrupts, fixes stuttering in the dashboard --- src/core/kernel/exports/EmuKrnl.h | 4 ++++ src/core/kernel/init/CxbxKrnl.cpp | 23 +++++++++-------------- src/devices/video/nv2a.cpp | 4 +--- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/core/kernel/exports/EmuKrnl.h b/src/core/kernel/exports/EmuKrnl.h index 5b3247eeb..89f1fbf80 100644 --- a/src/core/kernel/exports/EmuKrnl.h +++ b/src/core/kernel/exports/EmuKrnl.h @@ -51,6 +51,8 @@ xbox::PLIST_ENTRY RemoveTailList(xbox::PLIST_ENTRY pListHead); extern xbox::LAUNCH_DATA_PAGE DefaultLaunchDataPage; extern xbox::PKINTERRUPT EmuInterruptList[MAX_BUS_INTERRUPT_LEVEL + 1]; +inline std::condition_variable g_InterruptSignal; +inline std::atomic_bool g_AnyInterruptAsserted = false; class HalSystemInterrupt { public: @@ -61,6 +63,8 @@ public: } m_Asserted = state; + g_AnyInterruptAsserted = true; + g_InterruptSignal.notify_one(); }; void Enable() { diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 80a30ed0f..23cbaea73 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -336,17 +336,6 @@ void InitSoftwareInterrupts() } #endif -void TriggerPendingConnectedInterrupts() -{ - for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) { - // If the interrupt is pending and connected, process it - if (HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) { - HalSystemInterrupts[i].Trigger(EmuInterruptList[i]); - } - SwitchToThread(); - } -} - static xbox::void_xt NTAPI CxbxKrnlInterruptThread(xbox::PVOID param) { CxbxSetThreadName("CxbxKrnl Interrupts"); @@ -355,11 +344,17 @@ static xbox::void_xt NTAPI CxbxKrnlInterruptThread(xbox::PVOID param) InitSoftwareInterrupts(); #endif + std::mutex m; + std::unique_lock lock(m); while (true) { - if (g_bEnableAllInterrupts) { - TriggerPendingConnectedInterrupts(); + for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) { + // If the interrupt is pending and connected, process it + if (HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) { + HalSystemInterrupts[i].Trigger(EmuInterruptList[i]); + } } - Sleep(1); + g_InterruptSignal.wait(lock, []() { return g_AnyInterruptAsserted.load() && g_bEnableAllInterrupts.load(); }); + g_AnyInterruptAsserted = false; } assert(0); diff --git a/src/devices/video/nv2a.cpp b/src/devices/video/nv2a.cpp index 73214a7b1..661495d56 100644 --- a/src/devices/video/nv2a.cpp +++ b/src/devices/video/nv2a.cpp @@ -150,8 +150,6 @@ static void update_irq(NV2AState *d) else { HalSystemInterrupts[3].Assert(false); } - - SwitchToThread(); } @@ -1117,7 +1115,7 @@ static void nv2a_vblank_thread(NV2AState *d) //NV2ADevice::UpdateHostDisplay(d); } - Sleep(1); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } From f1748727023c3e04307c84fbe49d4e18eabfd59b Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Sun, 20 Feb 2022 00:35:29 +0100 Subject: [PATCH 24/25] Unpatch timeSetEvent and timeKillEvent + fixes a crash in chihiro games + missing audio effects in virtua cop 3 + fixes a bug in SignalObjectAndWait --- src/core/hle/Patches.cpp | 4 +- src/core/hle/XAPI/Xapi.cpp | 53 ++--------------------- src/core/hle/XAPI/Xapi.cpp.unused-patches | 38 ++++++++++++++++ src/core/kernel/common/types.h | 5 +++ src/core/kernel/exports/EmuKrnlKe.cpp | 4 +- 5 files changed, 50 insertions(+), 54 deletions(-) diff --git a/src/core/hle/Patches.cpp b/src/core/hle/Patches.cpp index 85d6eb41f..3236914b2 100644 --- a/src/core/hle/Patches.cpp +++ b/src/core/hle/Patches.cpp @@ -365,8 +365,8 @@ std::map g_PatchTable = { PATCH_ENTRY("XMountMUA", xbox::EMUPATCH(XMountMUA), PATCH_ALWAYS), PATCH_ENTRY("XMountMURootA", xbox::EMUPATCH(XMountMURootA), PATCH_ALWAYS), //PATCH_ENTRY("XSetProcessQuantumLength", xbox::EMUPATCH(XSetProcessQuantumLength), PATCH_ALWAYS), - PATCH_ENTRY("timeKillEvent", xbox::EMUPATCH(timeKillEvent), PATCH_ALWAYS), - PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS), + //PATCH_ENTRY("timeKillEvent", xbox::EMUPATCH(timeKillEvent), PATCH_ALWAYS), + //PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS), PATCH_ENTRY("XReadMUMetaData", xbox::EMUPATCH(XReadMUMetaData), PATCH_ALWAYS), PATCH_ENTRY("XUnmountMU", xbox::EMUPATCH(XUnmountMU), PATCH_ALWAYS), }; diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index f63635e2a..82a360e97 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -1000,15 +1000,12 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait) // Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves LARGE_INTEGER NewTime; - PLARGE_INTEGER pNewTime; if (dwMilliseconds == INFINITE) { NewTime.QuadPart = ~0ull; - pNewTime = &NewTime; } else { - LARGE_INTEGER ExpireTime, DueTime; - ExpireTime.QuadPart = DueTime.QuadPart = -dwMilliseconds; - pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime); + NewTime.QuadPart = xbox::KeQueryInterruptTime(); + NewTime.QuadPart += (static_cast(dwMilliseconds) * CLOCK_TIME_INCREMENT); } xbox::dword_xt ret = WaitApc([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional { @@ -1017,55 +1014,11 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait) return std::nullopt; } return std::make_optional(dwRet); - }, pNewTime, bAlertable, UserMode); + }, &NewTime, bAlertable, UserMode); RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret); } -// ****************************************************************** -// * patch: timeSetEvent -// ****************************************************************** -MMRESULT WINAPI xbox::EMUPATCH(timeSetEvent) -( - uint_xt uDelay, - uint_xt uResolution, - LPTIMECALLBACK fptc, - dword_xt dwUser, - uint_xt fuEvent -) -{ - - - LOG_FUNC_BEGIN - LOG_FUNC_ARG(uDelay) - LOG_FUNC_ARG(uResolution) - LOG_FUNC_ARG_TYPE(PVOID, fptc) - LOG_FUNC_ARG(dwUser) - LOG_FUNC_ARG(fuEvent) - LOG_FUNC_END; - - MMRESULT Ret = timeSetEvent( uDelay, uResolution, fptc, (DWORD_PTR) dwUser, fuEvent ); - - RETURN(Ret); -} - -// ****************************************************************** -// * patch: timeKillEvent -// ****************************************************************** -MMRESULT WINAPI xbox::EMUPATCH(timeKillEvent) -( - uint_xt uTimerID -) -{ - - - LOG_FUNC_ONE_ARG(uTimerID); - - MMRESULT Ret = timeKillEvent( uTimerID ); - - RETURN(Ret); -} - // ****************************************************************** // * patch: RaiseException // ****************************************************************** diff --git a/src/core/hle/XAPI/Xapi.cpp.unused-patches b/src/core/hle/XAPI/Xapi.cpp.unused-patches index 976a261b0..11c317c73 100644 --- a/src/core/hle/XAPI/Xapi.cpp.unused-patches +++ b/src/core/hle/XAPI/Xapi.cpp.unused-patches @@ -353,3 +353,41 @@ xbox::void_xt WINAPI xbox::EMUPATCH(XSetProcessQuantumLength) // TODO: Implement? LOG_IGNORED(); } + +MMRESULT WINAPI xbox::EMUPATCH(timeSetEvent) +( + uint_xt uDelay, + uint_xt uResolution, + LPTIMECALLBACK fptc, + dword_xt dwUser, + uint_xt fuEvent +) +{ + + + LOG_FUNC_BEGIN + LOG_FUNC_ARG(uDelay) + LOG_FUNC_ARG(uResolution) + LOG_FUNC_ARG_TYPE(PVOID, fptc) + LOG_FUNC_ARG(dwUser) + LOG_FUNC_ARG(fuEvent) + LOG_FUNC_END; + + MMRESULT Ret = timeSetEvent( uDelay, uResolution, fptc, (DWORD_PTR) dwUser, fuEvent ); + + RETURN(Ret); +} + +MMRESULT WINAPI xbox::EMUPATCH(timeKillEvent) +( + uint_xt uTimerID +) +{ + + + LOG_FUNC_ONE_ARG(uTimerID); + + MMRESULT Ret = timeKillEvent( uTimerID ); + + RETURN(Ret); +} diff --git a/src/core/kernel/common/types.h b/src/core/kernel/common/types.h index ddbaaa3b1..bf5774d94 100644 --- a/src/core/kernel/common/types.h +++ b/src/core/kernel/common/types.h @@ -1498,8 +1498,10 @@ KFLOATING_SAVE, *PKFLOATING_SAVE; // ****************************************************************** typedef enum _KOBJECTS { + EventNotificationObject = 0, EventSynchronizationObject = 1, MutantObject = 2, + ProcessObject = 3, QueueObject = 4, SemaphoreObject = 5, ThreadObject = 6, @@ -1508,6 +1510,9 @@ typedef enum _KOBJECTS ApcObject = 0x12, DpcObject = 0x13, DeviceQueueObject = 0x14, + EventPairObject = 0x15, + InterruptObject = 0x16, + ProfileObject = 0x17, } KOBJECTS, *PKOBJECTS; diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 1d27e5051..bd811eee1 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -2079,7 +2079,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects if ((ObjectMutant->Header.SignalState > 0) || (Thread == ObjectMutant->OwnerThread)) { if (ObjectMutant->Header.SignalState != MINLONG) { KiWaitSatisfyMutant(ObjectMutant, Thread); - WaitStatus = (NTSTATUS)(Thread->WaitStatus); + WaitStatus = (NTSTATUS)(Index | Thread->WaitStatus); goto NoWait; } else { @@ -2091,7 +2091,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects else if (ObjectMutant->Header.SignalState) { // Otherwise, if the signal state is > 0, we can still just satisfy the wait KiWaitSatisfyOther(ObjectMutant); - WaitStatus = X_STATUS_SUCCESS; + WaitStatus = Index; goto NoWait; } } else { From d3b2554b20da00bed8c2994c4f3a4303f21e5f1e Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Sun, 20 Feb 2022 16:07:42 +0100 Subject: [PATCH 25/25] Use a separate array for the native handles in NtWaitForMultipleObjectsEx --- src/core/kernel/common/nt.h | 2 ++ src/core/kernel/exports/EmuKrnlNt.cpp | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/core/kernel/common/nt.h b/src/core/kernel/common/nt.h index 531f20eae..01095adc9 100644 --- a/src/core/kernel/common/nt.h +++ b/src/core/kernel/common/nt.h @@ -19,6 +19,8 @@ namespace xbox #define NtCurrentThread() ((HANDLE)-2) +#define X_MAXIMUM_WAIT_OBJECTS 64 + // ****************************************************************** // * NtAllocateVirtualMemory // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index 02935c80f..0c9e78e1b 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -2190,12 +2190,20 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx LOG_FUNC_ARG(Timeout) LOG_FUNC_END; + if (!Count || (Count > X_MAXIMUM_WAIT_OBJECTS)) { + RETURN(X_STATUS_INVALID_PARAMETER); + } + // This function can wait on thread handles, which are currently created by ob, // so we need to check their presence in the handle array + ::HANDLE nativeHandles[X_MAXIMUM_WAIT_OBJECTS]; for (ulong_xt i = 0; i < Count; ++i) { if (const auto &nativeHandle = GetNativeHandle(Handles[i])) { // This is a ob handle, so replace it with its native counterpart - Handles[i] = *nativeHandle; + nativeHandles[i] = *nativeHandle; + } + else { + nativeHandles[i] = Handles[i]; } } @@ -2212,12 +2220,12 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx pNewTime = &NewTime; } - xbox::ntstatus_xt ret = WaitApc([Count, Handles, WaitType, Alertable]() -> std::optional { + xbox::ntstatus_xt ret = WaitApc([Count, &nativeHandles, WaitType, Alertable]() -> std::optional { NtDll::LARGE_INTEGER ExpireTime; ExpireTime.QuadPart = 0; NTSTATUS Status = NtDll::NtWaitForMultipleObjects( Count, - Handles, + nativeHandles, (NtDll::OBJECT_WAIT_TYPE)WaitType, Alertable, &ExpireTime);