diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index 55d7d09ba..1e4b7b599 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -45,6 +45,8 @@ #include "core\kernel\support\EmuFS.h" // For EmuGenerateFS #include "core\kernel\support\NativeHandle.h" +#include + // prevent name collisions namespace NtDll { @@ -58,6 +60,7 @@ typedef struct _PCSTProxyParam { IN xbox::PVOID Ethread; IN xbox::ulong_xt TlsDataSize; + IN OUT std::binary_semaphore* signal; } PCSTProxyParam; @@ -112,6 +115,18 @@ static unsigned int WINAPI PCSTProxy #else EmuGenerateFS(eThread); #endif + + // NOTE: Native KTHREAD-SWITCHING will not need notification as it should be already done within + // PsCreateSystemThreadEx function. For now, it is an experimental to see if xbox thread setup do work + // without reside in host stack. + // Initializing ethread is done, we can allow PsCreateSystemThreadEx process to continue. + params.signal->release(); + params.signal = nullptr; + SuspendThread(GetCurrentThread()); + if (xbox::KeGetCurrentThread()->HasTerminated) { + xbox::PsTerminateSystemThread(0); + } + xbox::PKSTART_FRAME StartFrame = reinterpret_cast(reinterpret_cast(eThread->Tcb.KernelStack) + sizeof(xbox::KSWITCHFRAME)); LOG_PCSTProxy( @@ -309,33 +324,57 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx KernelStackSize = RoundUp(KernelStackSize, PAGE_SIZE); hKernelStackSize = RoundUp(hKernelStackSize, PAGE_SIZE); - // create thread, using our special proxy technique - { - PETHREAD eThread; - ntstatus_xt result = ObCreateObject(&PsThreadObjectType, zeroptr, sizeof(ETHREAD) + ThreadExtensionSize, reinterpret_cast(&eThread)); - if (!X_NT_SUCCESS(result)) { - RETURN(result); - } + 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); + std::memset(eThread, 0, sizeof(ETHREAD) + ThreadExtensionSize); - // Create kernel stack for xbox title to able write on stack instead of host. - PVOID KernelStack = MmCreateKernelStack(KernelStackSize, DebuggerThread); + // Create kernel stack for xbox title to able write on stack instead of host. + PVOID KernelStack = MmCreateKernelStack(KernelStackSize, DebuggerThread); - if (!KernelStack) { - ObfDereferenceObject(eThread); - RETURN(X_STATUS_INSUFFICIENT_RESOURCES); - } + if (!KernelStack) { + ObfDereferenceObject(eThread); + RETURN(X_STATUS_INSUFFICIENT_RESOURCES); + } - // Start thread initialization process here before insert and create thread - KeInitializeThread(&eThread->Tcb, KernelStack, KernelStackSize, TlsDataSize, SystemRoutine, StartRoutine, StartContext, &KiUniqueProcess); + // Start thread initialization process here before insert and create thread + KeInitializeThread(&eThread->Tcb, KernelStack, KernelStackSize, TlsDataSize, SystemRoutine, StartRoutine, StartContext, &KiUniqueProcess); - // 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 binary_semaphore to allow new thread setup non-kthread switching. + std::binary_semaphore signal{0}; + + // PCSTProxy is responsible for cleaning up this pointer + PCSTProxyParam *iPCSTProxyParam = new PCSTProxyParam; + iPCSTProxyParam->Ethread = eThread; + iPCSTProxyParam->TlsDataSize = TlsDataSize; + iPCSTProxyParam->signal = &signal; + + unsigned int hThreadId; + HANDLE handle = reinterpret_cast(_beginthreadex(nullptr, hKernelStackSize, PCSTProxy, iPCSTProxyParam, NULL, &hThreadId)); + if (handle == zeroptr) { + delete iPCSTProxyParam; + MmDeleteKernelStack(eThread->Tcb.StackBase, eThread->Tcb.StackLimit); + ObfDereferenceObject(eThread); + RETURN(X_STATUS_INSUFFICIENT_RESOURCES); + } + + // We are waiting on new thread's non-kthread switching process is done before we can continue on. + signal.acquire(); + + // 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); + + // 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)) { + HANDLE dupHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, hThreadId); + assert(dupHandle); + RegisterXboxHandle(eThread->UniqueThread, dupHandle); if (g_iThreadNotificationCount) { PspCallThreadNotificationRoutines(eThread, TRUE); @@ -343,53 +382,37 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx // 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; - } + KeQuerySystemTime(&eThread->CreateTime); - // PCSTProxy is responsible for cleaning up this pointer - PCSTProxyParam *iPCSTProxyParam = new PCSTProxyParam; - iPCSTProxyParam->Ethread = eThread; - iPCSTProxyParam->TlsDataSize = TlsDataSize; - - unsigned int ThreadId; - HANDLE handle = reinterpret_cast(_beginthreadex(NULL, hKernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, &ThreadId)); - if (handle == zeroptr) { - delete iPCSTProxyParam; - ObpClose(eThread->UniqueThread); - ObfDereferenceObject(eThread); - 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); + if (X_NT_SUCCESS(result)) { RegisterXboxHandle(*ThreadHandle, handle); - HANDLE dupHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId); - assert(dupHandle); - RegisterXboxHandle(eThread->UniqueThread, dupHandle); g_AffinityPolicy->SetAffinityXbox(handle); - // Now that ThreadId is populated and affinity is changed, resume the thread (unless the guest passed CREATE_SUSPENDED) - if (!CreateSuspended) { - ResumeThread(handle); + if (ThreadId != zeroptr) { + *ThreadId = eThread->UniqueThread; } // Log ThreadID identical to how GetCurrentThreadID() is rendered : 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); } + else { + eThread->Tcb.HasTerminated = true; + } - RETURN(X_STATUS_SUCCESS); + // If thread is set to be terminated, allow thread to run and perform termination process from its own thread. + if (eThread->Tcb.HasTerminated) { + ResumeThread(handle); + } + // Now that ThreadId is populated and affinity is changed, resume the thread (unless the guest passed CREATE_SUSPENDED) + else if (!CreateSuspended) { + ResumeThread(handle); + } + + RETURN(result); } // ******************************************************************