Updated KeDelayExecutionThread, KeSetBasePriorityThread and XAPI thread functions to accept ob handles + more bug fixes

This commit is contained in:
ergo720 2022-01-05 17:09:32 +01:00
parent b664488274
commit a791b7609c
10 changed files with 141 additions and 90 deletions

View File

@ -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
// ******************************************************************

View File

@ -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,

View File

@ -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()
// ******************************************************************

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -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;
}
// ******************************************************************

View File

@ -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;

View File

@ -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();

View File

@ -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()) {