Updated KeDelayExecutionThread, KeSetBasePriorityThread and XAPI thread functions to accept ob handles + more bug fixes
This commit is contained in:
parent
b664488274
commit
a791b7609c
|
@ -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
|
||||
// ******************************************************************
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h>
|
||||
#include <core\kernel\exports\EmuKrnlKi.h>
|
||||
#include "core\kernel\support\EmuFS.h"
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
#include <cstdio>
|
||||
#include <cctype>
|
||||
#include <clocale>
|
||||
|
@ -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<bool> 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<xbox::PETHREAD>(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()
|
||||
// ******************************************************************
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include <future>
|
||||
|
||||
// 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<bool> WaitUserApc(xbox::boolean_xt Alertable, xbox::char_xt WaitMode, bool *Exit);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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 <chrono>
|
||||
|
@ -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<PETHREAD>(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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -56,17 +56,10 @@ namespace NtDll
|
|||
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
|
||||
// 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<HANDLE> 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;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -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<xbox::HANDLE>(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<HANDLE>(GetCurrentThreadId());
|
||||
EThread->UniqueThread = reinterpret_cast<xbox::HANDLE>(GetCurrentThreadId());
|
||||
}
|
||||
|
||||
EThread->Tcb.TlsData = pNewTLS;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -53,6 +53,12 @@ void RemoveXboxHandle(xbox::HANDLE xhandle)
|
|||
|
||||
std::optional<HANDLE> 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<std::shared_mutex> lck(g_MapMtx);
|
||||
auto &it = g_RegisteredHandles.find(xhandle);
|
||||
if (it == g_RegisteredHandles.end()) {
|
||||
|
|
Loading…
Reference in New Issue