Review remarks + use PsCreateSystemThread to start all xbox threads

This commit is contained in:
ergo720 2022-02-01 01:10:55 +01:00
parent 6320dd5539
commit 06f34134ff
14 changed files with 135 additions and 140 deletions

View File

@ -25,6 +25,8 @@
// *
// ******************************************************************
#include <core\kernel\exports\xboxkrnl.h>
#ifdef _WIN32
#include <windows.h>
#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<TimerObject *>(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

View File

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

View File

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

View File

@ -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<bool> 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<xbox::PETHREAD>(Kpcr->Prcb->CurrentThread);
@ -242,15 +241,12 @@ std::future<bool> 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) {

View File

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

View File

@ -882,25 +882,22 @@ xbox::void_xt FASTCALL xbox::KiWaitSatisfyAll
return;
}
template<bool KernelApc>
template<xbox::MODE ApcMode>
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<true>();
KiExecuteApc<KernelMode>();
}
xbox::void_xt xbox::KiExecuteUserApc()
{
KiExecuteApc<false>();
KiExecuteApc<UserMode>();
}

View File

@ -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<PVOID *>(&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);
}
// ******************************************************************

View File

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

View File

@ -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<void(*)()>(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<true>(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<true>();
// 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<NtDll::HANDLE> threads;
std::vector<HANDLE> threads;
threads.reserve(KiUniqueProcess.StackCount);
xbox::KPCR* Pcr = EmuKeGetPcr();
// Don't use EmuKeGetPcr because that asserts kpcr
xbox::KPCR* Pcr = reinterpret_cast<xbox::PKPCR>(__readfsdword(TIB_ArbitraryDataSlot));
// If there's nothing in list entry, skip this step.
if (!ThreadListEntry) {
return;

View File

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

View File

@ -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<true>(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread);
template void EmuGenerateFS<false>(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread);
template void EmuKeFreePcr<true>();
template void EmuKeFreePcr<false>();
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<bool IsHostThread>
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<xbox::PBYTE>(Pcr->NtTib.StackBase) - 12;
Dummy = static_cast<xbox::PBYTE>(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<xbox::HANDLE>(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<bool IsHostThread>
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<xbox::PETHREAD>(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<xbox::HANDLE>(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)
}

View File

@ -33,11 +33,13 @@
extern void EmuInitFS();
// generate fs segment selector
extern void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread);
template<bool IsHostThread = false>
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<bool IsHostThread = false>
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<true>(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread);
extern template void EmuGenerateFS<false>(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread);
extern template void EmuKeFreePcr<true>();
extern template void EmuKeFreePcr<false>();
#endif

View File

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

View File

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