Merge pull request #2345 from RadWolfie/init-thread-fix

Fix order of thread initialization
This commit is contained in:
ergo720 2022-04-13 15:29:31 +02:00 committed by GitHub
commit 374ba5ec70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 79 additions and 56 deletions

View File

@ -45,6 +45,8 @@
#include "core\kernel\support\EmuFS.h" // For EmuGenerateFS
#include "core\kernel\support\NativeHandle.h"
#include <semaphore>
// 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<xbox::PKSTART_FRAME>(reinterpret_cast<xbox::addr_xt>(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<PVOID *>(&eThread));
if (!X_NT_SUCCESS(result)) {
RETURN(result);
}
PETHREAD eThread;
ntstatus_xt result = ObCreateObject(&PsThreadObjectType, zeroptr, sizeof(ETHREAD) + ThreadExtensionSize, reinterpret_cast<PVOID*>(&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<HANDLE>(_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<HANDLE>(_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);
}
// ******************************************************************