Merge pull request #2339 from RadWolfie/improve-thread-setup

Improve Xbox Thread Setup
This commit is contained in:
ergo720 2022-04-04 00:54:23 +02:00 committed by GitHub
commit 5f7b9417b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 653 additions and 230 deletions

View File

@ -177,6 +177,7 @@ file (GLOB CXBXR_HEADER_EMU
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKe.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKi.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlPs.hpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/init/CxbxKrnl.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/init/KrnlPatches.hpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PhysicalMemory.h"

View File

@ -1401,7 +1401,7 @@ typedef struct _KTIMER
ULARGE_INTEGER DueTime; // 0x10
LIST_ENTRY TimerListEntry; // 0x18
struct _KDPC *Dpc; // 0x20
long_xt Period; // 0x24
long_xt Period; // 0x24
}
KTIMER, *PKTIMER;
@ -1424,7 +1424,7 @@ typedef void_xt (NTAPI *PKSTART_ROUTINE)
// * opposed to 1.
// *
// ******************************************************************
typedef void_xt (*PKSYSTEM_ROUTINE)
typedef void_xt (NTAPI *PKSYSTEM_ROUTINE)
(
IN PKSTART_ROUTINE StartRoutine OPTIONAL,
IN PVOID StartContext OPTIONAL
@ -1435,7 +1435,7 @@ struct _KDPC;
// ******************************************************************
// * PKDEFERRED_ROUTINE
// ******************************************************************
typedef void_xt (__stdcall *PKDEFERRED_ROUTINE)
typedef void_xt (NTAPI *PKDEFERRED_ROUTINE)
(
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
@ -1474,6 +1474,14 @@ typedef struct _DPC_QUEUE_ENTRY
}
DPC_QUEUE_ENTRY, *PDPC_QUEUE_ENTRY;
// ******************************************************************
// * NPX_STATE flags
// ******************************************************************
// Source: ReactOS
#define NPX_STATE_NOT_LOADED 0xA
#define NPX_STATE_LOADED 0x0
// ******************************************************************
// * KFLOATING_SAVE
// ******************************************************************
@ -1492,6 +1500,21 @@ typedef struct _KFLOATING_SAVE
}
KFLOATING_SAVE, *PKFLOATING_SAVE;
// ******************************************************************
// * KTHREAD_STATE
// ******************************************************************
// Source: ReactOS
typedef enum _KTHREAD_STATE
{
Initialized,
Ready,
Running,
Standby,
Terminated,
Waiting,
Transition
} KTHREAD_STATE, * PKTHREAD_STATE;
#define DISPATCHER_OBJECT_TYPE_MASK 0x7
// ******************************************************************
// * KOBJECTS
@ -1529,7 +1552,7 @@ typedef void_xt (NTAPI *PKNORMAL_ROUTINE)
// ******************************************************************
// * PKKERNEL_ROUTINE
// ******************************************************************
typedef void_xt (*PKKERNEL_ROUTINE)
typedef void_xt (NTAPI *PKKERNEL_ROUTINE)
(
IN struct _KAPC *Apc,
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
@ -1541,7 +1564,7 @@ typedef void_xt (*PKKERNEL_ROUTINE)
// ******************************************************************
// * PKRUNDOWN_ROUTINE
// ******************************************************************
typedef void_xt (*PKRUNDOWN_ROUTINE)
typedef void_xt (NTAPI *PKRUNDOWN_ROUTINE)
(
IN struct _KAPC *Apc
);
@ -1549,7 +1572,7 @@ typedef void_xt (*PKRUNDOWN_ROUTINE)
// ******************************************************************
// * PKSYNCHRONIZE_ROUTINE
// ******************************************************************
typedef boolean_xt (*PKSYNCHRONIZE_ROUTINE)
typedef boolean_xt (NTAPI *PKSYNCHRONIZE_ROUTINE)
(
IN PVOID SynchronizeContext
);
@ -1557,7 +1580,7 @@ typedef boolean_xt (*PKSYNCHRONIZE_ROUTINE)
// ******************************************************************
// * PKSERVICE_ROUTINE
// ******************************************************************
typedef boolean_xt (*PKSERVICE_ROUTINE)
typedef boolean_xt (NTAPI *PKSERVICE_ROUTINE)
(
IN struct _KINTERRUPT *Interrupt,
IN PVOID ServiceContext
@ -1636,8 +1659,8 @@ PS_STATISTICS, *PPS_STATISTICS;
typedef struct _RTL_CRITICAL_SECTION
{
DISPATCHER_HEADER Event; // 0x00
long_xt LockCount; // 0x10
long_xt RecursionCount; // 0x14
long_xt LockCount; // 0x10
long_xt RecursionCount; // 0x14
HANDLE OwningThread; // 0x18
}
RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
@ -1654,7 +1677,7 @@ typedef struct _NT_TIB
union
{
PVOID FiberData; // 0x10 for TIB
ulong_xt Version; // 0x10 for TEB (?)
ulong_xt Version; // 0x10 for TEB (?)
}
u_a;
PVOID ArbitraryUserPointer; // 0x14
@ -1753,6 +1776,40 @@ typedef struct _KQUEUE
}
KQUEUE, *PKQUEUE, *RESTRICTED_POINTER PRKQUEUE;
// ******************************************************************
// * KSTART_FRAME
// ******************************************************************
typedef struct _KSTART_FRAME
{
PKSYSTEM_ROUTINE SystemRoutine;
PKSTART_ROUTINE StartRoutine;
PVOID StartContext;
} KSTART_FRAME, *PKSTART_FRAME;
// ******************************************************************
// * KSWITCHFRAME
// ******************************************************************
typedef struct _KSWITCHFRAME
{
PVOID ExceptionList;
dword_xt Unknown;
PVOID RetAddr;
} KSWITCHFRAME, *PKSWITCHFRAME;
// Exception record flags
// Source: ReactOS
// NOTE: Do not exclude X_ prefix, they will conflict with the macros provided by Windows
#define X_EXCEPTION_NONCONTINUABLE 0x01
#define X_EXCEPTION_UNWINDING 0x02
#define X_EXCEPTION_EXIT_UNWIND 0x04
#define X_EXCEPTION_STACK_INVALID 0x08
#define X_EXCEPTION_NESTED_CALL 0x10
#define X_EXCEPTION_TARGET_UNWIND 0x20
#define X_EXCEPTION_COLLIDED_UNWIND 0x40
#define X_EXCEPTION_UNWIND (X_EXCEPTION_UNWINDING | X_EXCEPTION_EXIT_UNWIND | X_EXCEPTION_TARGET_UNWIND | X_EXCEPTION_COLLIDED_UNWIND)
#define X_EXCEPTION_CHAIN_END 0xFFFFFFFF
// ******************************************************************
// * EXCEPTION_DISPOSITION
// ******************************************************************
@ -1925,11 +1982,14 @@ typedef struct _ETHREAD
LARGE_INTEGER CreateTime; // 0x110
LARGE_INTEGER ExitTime; // 0x118
ntstatus_xt ExitStatus; // 0x120
uchar_xt Unknown[0x8]; // 0x124
LIST_ENTRY ReaperLink; // 0x124
HANDLE UniqueThread; // 0x12C
PVOID StartAddress; // 0x130
LIST_ENTRY IrpList; // 0x134
PVOID DebugData; // 0x13C
}
ETHREAD, *PETHREAD;
static_assert(sizeof(ETHREAD) == 0x130);
static_assert(sizeof(ETHREAD) == 0x140);
// ******************************************************************
// * PCREATE_THREAD_NOTIFY_ROUTINE
@ -1954,13 +2014,13 @@ typedef struct _KPRCB
struct _KTHREAD* NextThread; // 0x04, KPCR : 0x2C
struct _KTHREAD* IdleThread; // 0x08, KPCR : 0x30
ulong_xt Unknown1[7]; // 0x0C, KPCR : 0x34
ulong_xt Unknown1[7]; // 0x0C, KPCR : 0x34
LIST_ENTRY DpcListHead; // 0x28, KPCR : 0x50
ulong_xt DpcRoutineActive; // 0x30, KPCR : 0x58
ulong_xt DpcRoutineActive; // 0x30, KPCR : 0x58
// This completes the total size of the structure (presumably)
uchar_xt Unknown[0x224];
uchar_xt Unknown[0x224];
}
KPRCB, *PKPRCB;
@ -1977,7 +2037,7 @@ typedef struct _KPCR
struct _NT_TIB NtTib; // 0x00
struct _KPCR *SelfPcr; // 0x1C
struct _KPRCB *Prcb; // 0x20
uchar_xt Irql; // 0x24
uchar_xt Irql; // 0x24
struct _KPRCB PrcbData; // 0x28
}
KPCR, *PKPCR;
@ -2532,6 +2592,13 @@ typedef struct _FLOATING_SAVE_AREA
dword_xt Cr0NpxState;
} FLOATING_SAVE_AREA, *PFLOATING_SAVE_AREA;
#pragma pack(pop)
static_assert(sizeof(_FLOATING_SAVE_AREA) == 0x204);
typedef struct _FX_SAVE_AREA {
FLOATING_SAVE_AREA FloatSave;
ulong_xt Unknown[3];
} FX_SAVE_AREA, *PFX_SAVE_AREA;
static_assert(sizeof(_FX_SAVE_AREA) == 0x210);
typedef struct _CONTEXT
{

View File

@ -266,7 +266,9 @@ xbox::void_xt NTAPI xbox::KeInitializeTimer
xbox::void_xt xbox::KeEmptyQueueApc()
{
PKTHREAD kThread = KeGetCurrentThread();
KeEnterCriticalRegion();
kThread->ApcState.ApcQueueable = FALSE;
KeLeaveCriticalRegion();
KiApcListMtx.lock();
for (int Mode = KernelMode; Mode < MaximumMode; ++Mode) {
@ -280,6 +282,150 @@ xbox::void_xt xbox::KeEmptyQueueApc()
KiApcListMtx.unlock();
}
// Source: ReactOS (modified to fit in xbox compatibility layer)
template<bool IsHostThread>
xbox::void_xt xbox::KeInitializeThread(
IN OUT PKTHREAD Thread,
IN PVOID KernelStack,
IN ulong_xt KernelStackSize,
IN ulong_xt TlsDataSize,
IN PKSYSTEM_ROUTINE SystemRoutine,
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext,
IN PKPROCESS Process
)
{
/* ReactOS's KeInitThread inline code begin */
/* Initialize the Dispatcher Header */
Thread->Header.Type = xbox::ThreadObject;
Thread->Header.Size = sizeof(xbox::KTHREAD) / sizeof(xbox::long_xt);
// ThreadControlFlags
// DebugActive
Thread->Header.SignalState = 0;
InitializeListHead(&Thread->Header.WaitListHead);
/* Initialize the Mutant List */
InitializeListHead(&Thread->MutantListHead);
#if 0 // Not used or not yet reverse engineered
/* Set swap settings */
Thread->EnableStackSwap = TRUE;
Thread->IdealProcessor = 1;
Thread->SwapBusy = FALSE;
Thread->KernelStackResident = TRUE;
Thread->AdjustReason = AdjustNone;
#endif
#if 0 // Not used or not yet reverse engineered
/* Initialize the lock */
KeInitializeSpinLock(&Thread->ThreadLock);
#endif
#if 0 // Not used or not yet reverse engineered
/* Setup the Service Descriptor Table for Native Calls */
Thread->ServiceTable = KeServiceDescriptorTable;
#endif
/* Setup APC Fields */
InitializeListHead(&Thread->ApcState.ApcListHead[xbox::KernelMode]);
InitializeListHead(&Thread->ApcState.ApcListHead[xbox::UserMode]);
Thread->KernelApcDisable = 0;
Thread->ApcState.Process = &KiUniqueProcess;
Thread->ApcState.ApcQueueable = TRUE;
Thread->ApcState.Process->ThreadQuantum = KiUniqueProcess.ThreadQuantum;
/* Initialize the Suspend APC */
KeInitializeApc(
&Thread->SuspendApc,
Thread,
KiSuspendNop,
zeroptr,
KiSuspendThread,
KernelMode,
zeroptr);
/* Initialize the Suspend Semaphore */
KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 2);
/* Setup the timer */
xbox::KeInitializeTimer(&Thread->Timer);
xbox::PKWAIT_BLOCK TimerWaitBlock = &Thread->TimerWaitBlock;
TimerWaitBlock->Object = &Thread->Timer;
TimerWaitBlock->WaitKey = (xbox::cshort_xt)X_STATUS_TIMEOUT;
TimerWaitBlock->WaitType = xbox::WaitAny;
TimerWaitBlock->Thread = Thread;
TimerWaitBlock->NextWaitBlock = zeroptr;
/* Link the two wait lists together */
TimerWaitBlock->WaitListEntry.Flink = &Thread->Timer.Header.WaitListHead;
TimerWaitBlock->WaitListEntry.Blink = &Thread->Timer.Header.WaitListHead;
#if 0 // Not used or not yet reverse engineered
/* Set the TEB and process */
Thread->Teb = Teb;
Thread->Process = Process;
#endif
/* Set the Thread Stacks */
Thread->StackBase = KernelStack;
Thread->StackLimit = reinterpret_cast<PVOID>(reinterpret_cast<ulong_ptr_xt>(KernelStack) - KernelStackSize);
/* Initialize the Thread Context */
KiInitializeContextThread(Thread, TlsDataSize, SystemRoutine, StartRoutine, StartContext);
/* Set the Thread to initialized */
Thread->State = Initialized;
/* ReactOS's KeInitThread inline code end */
/* ReactOS's KeStartThread inline code begin */
// NOTE: The cxbxr's kernel initialization will not be insert into ThreadListHead of Process.
if constexpr (!IsHostThread) {
/* Setup static fields from parent */
Thread->DisableBoost = Process->DisableBoost;
Thread->Quantum = Process->ThreadQuantum;
/* Setup volatile data */
Thread->Priority = Process->BasePriority;
Thread->BasePriority = Process->BasePriority;
/* Lock the Dispatcher Database */
UCHAR orig_irql = KeRaiseIrqlToDpcLevel();
/* Insert the thread into the process list */
InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
/* Increase the stack count */
Process->StackCount++;
/* Release lock and return */
KfLowerIrql(orig_irql);
}
/* ReactOS's KeStartThread inline code end */
}
template
xbox::void_xt xbox::KeInitializeThread<true>(
IN OUT PKTHREAD Thread,
IN PVOID KernelStack,
IN ulong_xt KernelStackSize,
IN ulong_xt TlsDataSize,
IN PKSYSTEM_ROUTINE SystemRoutine,
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext,
IN PKPROCESS Process
);
template
xbox::void_xt xbox::KeInitializeThread<false>(
IN OUT PKTHREAD Thread,
IN PVOID KernelStack,
IN ulong_xt KernelStackSize,
IN ulong_xt TlsDataSize,
IN PKSYSTEM_ROUTINE SystemRoutine,
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext,
IN PKPROCESS Process
);
// Forward KeLowerIrql() to KfLowerIrql()
#define KeLowerIrql(NewIrql) \
KfLowerIrql(NewIrql)

View File

@ -38,5 +38,17 @@ namespace xbox
IN PKTIMER Timer
);
template<bool IsHostThread = false>
void_xt KeInitializeThread(
IN OUT PKTHREAD Thread,
IN PVOID KernelStack,
IN ulong_xt KernelStackSize,
IN ulong_xt TlsDataSize,
IN PKSYSTEM_ROUTINE SystemRoutine,
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext,
IN PKPROCESS Process
);
void_xt KeEmptyQueueApc();
}

View File

@ -945,3 +945,115 @@ xbox::PLARGE_INTEGER FASTCALL xbox::KiComputeWaitInterval
return NewTime;
}
}
// Source: ReactOS
xbox::void_xt NTAPI xbox::KiSuspendNop(
IN PKAPC Apc,
IN PKNORMAL_ROUTINE* NormalRoutine,
IN PVOID* NormalContext,
IN PVOID* SystemArgument1,
IN PVOID* SystemArgument2
)
{
/* Does nothing */
UNREFERENCED_PARAMETER(Apc);
UNREFERENCED_PARAMETER(NormalRoutine);
UNREFERENCED_PARAMETER(NormalContext);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
}
// Source: ReactOS
xbox::void_xt NTAPI xbox::KiSuspendThread(
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
/* Non-alertable kernel-mode suspended wait */
KeWaitForSingleObject(
&KeGetCurrentThread()->SuspendSemaphore,
Suspended,
KernelMode,
FALSE,
zeroptr);
}
xbox::void_xt NTAPI xbox::KiThreadStartup(void_xt)
{
PKSTART_FRAME StartFrame;
PKSWITCHFRAME SwitchFrame;
/* Get the start and trap frames */
SwitchFrame = reinterpret_cast<PKSWITCHFRAME>(KeGetCurrentThread()->KernelStack);
StartFrame = reinterpret_cast<PKSTART_FRAME>(SwitchFrame + 1);
/* Lower to Passive level */
KfLowerIrql(PASSIVE_LEVEL);
// NOTE: if assert is triggered, then thread-switching may have been processed.
// If it does, then verify xbox thread's StackBase is from MmCreateKernelStack instead of host's stack and was not deleted by MmDeleteKernelStack.
// Otherwise, feel free to clear this reminder message.
assert(0);
/* Call the system routine */
StartFrame->SystemRoutine(StartFrame->StartRoutine, StartFrame->StartContext);
/* We do not return as it is a top function */
PsTerminateSystemThread(X_STATUS_NO_MEMORY);
}
// Source: ReactOS (modified to fit in xbox compatibility layer)
xbox::void_xt xbox::KiInitializeContextThread(
IN PKTHREAD Thread,
IN ulong_xt TlsDataSize,
IN PKSYSTEM_ROUTINE SystemRoutine,
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext
)
{
addr_xt StackAddress = reinterpret_cast<addr_xt>(Thread->StackBase);
/* Setup the Fx Area */
StackAddress -= sizeof(FX_SAVE_AREA);
PFX_SAVE_AREA FxSaveArea = reinterpret_cast<PFX_SAVE_AREA>(StackAddress);
std::memset(FxSaveArea, 0, sizeof(FX_SAVE_AREA));
/* Set the stub FX area */
FxSaveArea->FloatSave.ControlWord = 0x27F;
FxSaveArea->FloatSave.MXCsr = 0x1F80;
/* No NPX State */
Thread->NpxState = NPX_STATE_NOT_LOADED;
/* Setup the Stack for TlsData dynamic sized array */
TlsDataSize = ALIGN_UP(TlsDataSize, ulong_xt);
StackAddress -= TlsDataSize; // TlsData section (optional)
if (TlsDataSize) {
Thread->TlsData = reinterpret_cast<PVOID>(StackAddress);
// Title will process which section of TlsData will be fill with data and zero'd.
// So, we leave this untouched.
}
else {
Thread->TlsData = zeroptr;
}
/* Setup the Stack for KiThreadStartup and Context Switching */
StackAddress -= sizeof(KSTART_FRAME);
PKSTART_FRAME StartFrame = reinterpret_cast<PKSTART_FRAME>(StackAddress);
StackAddress -= sizeof(KSWITCHFRAME);
PKSWITCHFRAME CtxSwitchFrame = reinterpret_cast<PKSWITCHFRAME>(StackAddress);
/* Now setup the remaining data for KiThreadStartup */
StartFrame->StartContext = StartContext;
StartFrame->StartRoutine = StartRoutine;
StartFrame->SystemRoutine = SystemRoutine;
/* And set up the Context Switch Frame */
CtxSwitchFrame->RetAddr = KiThreadStartup;
CtxSwitchFrame->Unknown = 0x200; // TODO: Find out what this field is.
CtxSwitchFrame->ExceptionList = reinterpret_cast<PVOID>(X_EXCEPTION_CHAIN_END);
/* Save back the new value of the kernel stack. */
Thread->KernelStack = reinterpret_cast<PVOID>(CtxSwitchFrame);
}

View File

@ -146,6 +146,32 @@ namespace xbox
IN PLARGE_INTEGER DueTime,
IN OUT PLARGE_INTEGER NewTime
);
// Source: ReactOS
void_xt NTAPI KiSuspendNop(
IN PKAPC Apc,
IN PKNORMAL_ROUTINE* NormalRoutine,
IN PVOID* NormalContext,
IN PVOID* SystemArgument1,
IN PVOID* SystemArgument2
);
// Source: ReactOS
void_xt NTAPI KiSuspendThread(
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
void_xt NTAPI KiThreadStartup(void_xt);
xbox::void_xt KiInitializeContextThread(
IN PKTHREAD Thread,
IN ulong_xt TlsDataSize,
IN PKSYSTEM_ROUTINE SystemRoutine,
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext
);
};
extern xbox::KPROCESS KiUniqueProcess;

View File

@ -31,6 +31,7 @@
#include <core\kernel\exports\xboxkrnl.h> // For PsCreateSystemThreadEx, etc.
#include "EmuKrnlPs.hpp"
#include "core\kernel\exports\EmuKrnlKi.h"
#include "core\kernel\exports\EmuKrnlKe.h"
#include <process.h> // For __beginthreadex(), etc.
@ -55,10 +56,8 @@ namespace NtDll
// PsCreateSystemThread proxy parameters
typedef struct _PCSTProxyParam
{
IN xbox::PVOID StartRoutine;
IN xbox::PVOID StartContext;
IN xbox::PVOID SystemRoutine;
IN xbox::PVOID Ethread;
IN xbox::ulong_xt TlsDataSize;
}
PCSTProxyParam;
@ -72,7 +71,8 @@ void LOG_PCSTProxy
xbox::PVOID StartRoutine,
xbox::PVOID StartContext,
xbox::PVOID SystemRoutine,
xbox::PVOID Ethread
xbox::PVOID Ethread,
xbox::ulong_xt TlsDataSize
)
{
LOG_FUNC_BEGIN
@ -80,6 +80,7 @@ void LOG_PCSTProxy
LOG_FUNC_ARG(StartContext)
LOG_FUNC_ARG(SystemRoutine)
LOG_FUNC_ARG(Ethread)
LOG_FUNC_ARG(TlsDataSize)
LOG_FUNC_END;
}
@ -97,32 +98,49 @@ static unsigned int WINAPI PCSTProxy
// Copy params to the stack so they can be freed
PCSTProxyParam params = *iPCSTProxyParam;
delete iPCSTProxyParam;
LOG_PCSTProxy(
params.StartRoutine,
params.StartContext,
params.SystemRoutine,
params.Ethread);
#ifndef ENABLE_KTHREAD_SWITCHING
unsigned Host2XbStackBaseReserved = 0;
__asm mov Host2XbStackBaseReserved, esp;
unsigned Host2XbStackSizeReserved = EmuGenerateStackSize(Host2XbStackBaseReserved, params.TlsDataSize);
__asm sub esp, Host2XbStackSizeReserved;
#endif
// Do minimal thread initialization
EmuGenerateFS(CxbxKrnl_TLS, CxbxKrnl_TLSData, static_cast<xbox::PETHREAD>(params.Ethread));
xbox::PETHREAD eThread = static_cast<xbox::PETHREAD>(params.Ethread);
#ifndef ENABLE_KTHREAD_SWITCHING
EmuGenerateFS(eThread, Host2XbStackBaseReserved, Host2XbStackSizeReserved);
#else
EmuGenerateFS(eThread);
#endif
xbox::PKSTART_FRAME StartFrame = reinterpret_cast<xbox::PKSTART_FRAME>(reinterpret_cast<xbox::addr_xt>(eThread->Tcb.KernelStack) + sizeof(xbox::KSWITCHFRAME));
auto routine = (xbox::PKSYSTEM_ROUTINE)params.SystemRoutine;
LOG_PCSTProxy(
StartFrame->StartRoutine,
StartFrame->StartContext,
StartFrame->SystemRoutine,
params.Ethread,
params.TlsDataSize);
auto routine = (xbox::PKSYSTEM_ROUTINE)StartFrame->SystemRoutine;
// Debugging notice : When the below line shows up with an Exception dialog and a
// message like: "Exception thrown at 0x00026190 in cxbx.exe: 0xC0000005: Access
// violation reading location 0xFD001804.", then this is AS-DESIGNED behaviour!
// (To avoid repetitions, uncheck "Break when this exception type is thrown").
routine(xbox::PKSTART_ROUTINE(params.StartRoutine), params.StartContext);
routine(StartFrame->StartRoutine, StartFrame->StartContext);
// This will also handle thread notification :
LOG_TEST_CASE("Thread returned from SystemRoutine");
xbox::PsTerminateSystemThread(X_STATUS_SUCCESS);
#ifndef ENABLE_KTHREAD_SWITCHING
__asm add esp, Host2XbStackSizeReserved;
#endif
return 0; // will never be reached
}
// Placeholder system function, instead of XapiThreadStartup
void PspSystemThreadStartup
xbox::void_xt NTAPI PspSystemThreadStartup
(
IN xbox::PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext
@ -151,6 +169,64 @@ static xbox::void_xt PspCallThreadNotificationRoutines(xbox::PETHREAD eThread, x
}
}
// Source: ReactOS
xbox::LIST_ENTRY PspReaperListHead;
xbox::void_xt NTAPI PspReaperRoutine(
IN xbox::PKDPC Dpc,
IN xbox::PVOID DeferredContext,
IN xbox::PVOID SystemArgument1,
IN xbox::PVOID SystemArgument2
)
{
using namespace xbox;
xbox::PLIST_ENTRY NextEntry;
PETHREAD Thread;
//PSTRACE(PS_KILL_DEBUG, "Context: %p\n", Context);
/* Write magic value and return the next entry to process */
NextEntry = PspReaperListHead.Flink;
/* Start loop */
while (NextEntry != &PspReaperListHead) {
/* Get the first Thread Entry */
Thread = CONTAINING_RECORD(NextEntry, ETHREAD, ReaperLink);
RemoveEntryList(NextEntry);
// Currently, only kernel's stack portion reside on the host's stack.
// Once we have our own kernel thread switching implement or in virtual environment.
// Then enable ENABLE_KTHREAD_SWITCHING macro for any further implement needed.
#ifdef ENABLE_KTHREAD_SWITCHING
/* Delete this entry's kernel stack */
MmDeleteKernelStack(Thread->Tcb.StackBase, Thread->Tcb.StackLimit);
#else
// Backup plan in case if certain titles did not let new thread start.
// And therefore still have the xbox's kernel stack allocated.
if (MmIsAddressValid(Thread->Tcb.StackBase)) {
MmDeleteKernelStack(Thread->Tcb.StackBase, Thread->Tcb.StackLimit);
}
#endif
Thread->Tcb.StackBase = zeroptr;
/* Move to the next entry */
NextEntry = NextEntry->Flink;
/* Dereference this thread */
ObfDereferenceObject(Thread);
}
}
static xbox::KDPC PsReaperDpc;
xbox::void_xt xbox::PsInitSystem()
{
#ifdef ENABLE_KTHREAD_SWITCHING
assert(0); // NOTE: Verify all defined ENABLE_KTHREAD_SWITCHING check are implemented
#endif
/* Setup the reaper */
InitializeListHead(&PspReaperListHead);
KeInitializeDpc(&PsReaperDpc, PspReaperRoutine, zeroptr);
}
// ******************************************************************
// * 0x00FE - PsCreateSystemThread()
// ******************************************************************
@ -222,20 +298,19 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
LOG_FUNC_ARG(SystemRoutine)
LOG_FUNC_END;
// TODO : Arguments to use : TlsDataSize, DebuggerThread
// use default kernel stack size if lesser specified
if (KernelStackSize < KERNEL_STACK_SIZE)
KernelStackSize = KERNEL_STACK_SIZE;
// Double the stack size, this is to account for the overhead HLE patching adds to the stack
KernelStackSize *= 2;
uint32_t hKernelStackSize = KernelStackSize * 2;
// round up to the next page boundary if un-aligned
KernelStackSize = RoundUp(KernelStackSize, PAGE_SIZE);
hKernelStackSize = RoundUp(hKernelStackSize, PAGE_SIZE);
// create thread, using our special proxy technique
{
// 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)) {
@ -244,6 +319,17 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
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);
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);
// 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)) {
@ -266,16 +352,14 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
*ThreadId = eThread->UniqueThread;
}
// PCSTProxy is responsible for cleaning up this pointer
// PCSTProxy is responsible for cleaning up this pointer
PCSTProxyParam *iPCSTProxyParam = new PCSTProxyParam;
iPCSTProxyParam->StartRoutine = (PVOID)StartRoutine;
iPCSTProxyParam->StartContext = StartContext;
iPCSTProxyParam->SystemRoutine = (PVOID)SystemRoutine; // NULL, XapiThreadStartup or unknown?
iPCSTProxyParam->Ethread = eThread;
iPCSTProxyParam->TlsDataSize = TlsDataSize;
unsigned int ThreadId;
HANDLE handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL, KernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, &ThreadId));
if (handle == NULL) {
HANDLE handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL, hKernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, &ThreadId));
if (handle == zeroptr) {
delete iPCSTProxyParam;
ObpClose(eThread->UniqueThread);
ObfDereferenceObject(eThread);
@ -288,8 +372,6 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
ObfReferenceObject(eThread);
KeQuerySystemTime(&eThread->CreateTime);
InsertTailList(&KiUniqueProcess.ThreadListHead, &eThread->Tcb.ThreadListEntry);
KiUniqueProcess.StackCount++;
RegisterXboxHandle(*ThreadHandle, handle);
HANDLE dupHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId);
assert(dupHandle);
@ -368,15 +450,43 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread
LOG_FUNC_ONE_ARG(ExitStatus);
xbox::PETHREAD eThread = xbox::PspGetCurrentThread();
eThread->Tcb.HasTerminated = 1;
KfLowerIrql(PASSIVE_LEVEL);
if (eThread->UniqueThread && g_iThreadNotificationCount) {
PspCallThreadNotificationRoutines(eThread, FALSE);
}
EmuKeFreeThread(ExitStatus);
// Don't do this in EmuKeFreeThread because we only increment the thread ref count in PsCreateSystemThreadEx
ObfDereferenceObject(eThread);
KeEmptyQueueApc();
// Emulate our exit strategy for GetExitCodeThread
KeQuerySystemTime(&eThread->ExitTime);
eThread->ExitStatus = ExitStatus;
eThread->Tcb.Header.SignalState = 1;
if (!IsListEmpty(&eThread->Tcb.Header.WaitListHead)) {
// TODO: Implement KiWaitTest's relative objects usage
//KiWaitTest()
assert(0);
}
if (GetNativeHandle(eThread->UniqueThread)) {
NtClose(eThread->UniqueThread);
eThread->UniqueThread = xbox::zeroptr;
}
// Remove thread from the process
RemoveEntryList(&eThread->Tcb.ThreadListEntry);
eThread->Tcb.State = Terminated;
KiUniqueProcess.StackCount--;
// PspReaperRoutine technically free'd the memory allocation from MmCreateKernelStack function.
// Therefore is run from another thread.
InsertTailList(&PspReaperListHead, &((PETHREAD)eThread)->ReaperLink);
KeInsertQueueDpc(&PsReaperDpc, NULL, NULL);
EmuKeFreePcr();
_endthreadex(ExitStatus);
}

View File

@ -0,0 +1,28 @@
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
namespace xbox
{
void_xt PsInitSystem();
};

View File

@ -50,29 +50,9 @@ namespace NtDll
#undef RtlFillMemory
#undef RtlMoveMemory
#undef RtlZeroMemory
#undef EXCEPTION_NONCONTINUABLE
#undef EXCEPTION_UNWINDING
#undef EXCEPTION_EXIT_UNWIND
#undef EXCEPTION_STACK_INVALID
#undef EXCEPTION_NESTED_CALL
#undef EXCEPTION_TARGET_UNWIND
#undef EXCEPTION_COLLIDED_UNWIND
#undef EXCEPTION_UNWIND
#endif // _WIN32
// Exception record flags
// Source: ReactOS
// NOTE: don't put these in xboxkrnl.h, they will conflict with the macros provided by Windows
#define EXCEPTION_NONCONTINUABLE 0x01
#define EXCEPTION_UNWINDING 0x02
#define EXCEPTION_EXIT_UNWIND 0x04
#define EXCEPTION_STACK_INVALID 0x08
#define EXCEPTION_NESTED_CALL 0x10
#define EXCEPTION_TARGET_UNWIND 0x20
#define EXCEPTION_COLLIDED_UNWIND 0x40
#define EXCEPTION_UNWIND (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND | EXCEPTION_TARGET_UNWIND | EXCEPTION_COLLIDED_UNWIND)
xbox::dword_xt WINAPI RtlAnsiStringToUnicodeSize(const xbox::STRING *str)
{
return (str->Length + sizeof(ANSI_NULL)) * sizeof(WCHAR);
@ -1503,7 +1483,7 @@ XBSYSAPI EXPORTNUM(303) xbox::void_xt NTAPI xbox::RtlRaiseStatus
EXCEPTION_RECORD record;
record.ExceptionCode = Status;
record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
record.ExceptionFlags = X_EXCEPTION_NONCONTINUABLE;
record.ExceptionRecord = NULL;
record.NumberParameters = 0;

View File

@ -43,6 +43,7 @@
#include "core\kernel\exports\EmuKrnl.h"
#include "core\kernel\exports\EmuKrnlKi.h"
#include "core\kernel\exports\EmuKrnlKe.h"
#include "core\kernel\exports\EmuKrnlPs.hpp"
#include "EmuShared.h"
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For CxbxInitWindow, EmuD3DInit
#include "core\hle\DSOUND\DirectSound\DirectSound.hpp" // For CxbxInitAudio
@ -112,7 +113,7 @@ std::atomic_bool g_bEnableAllInterrupts = true;
// Set by the VMManager during initialization. Exported because it's needed in other parts of the emu
size_t g_SystemMaxMemory = 0;
HANDLE g_CurrentProcessHandle = 0; // Set in CxbxKrnlMain
HANDLE g_CurrentProcessHandle = 0; // Set in CxbxKrnlEmulate
bool g_CxbxPrintUEM = false;
ULONG g_CxbxFatalErrorCode = FATAL_ERROR_NONE;
@ -1015,6 +1016,9 @@ void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_res
// and capture any crash from this point and beyond. Useful for capture live crash and generate crash report.
g_ExceptionManager = new ExceptionManager();
// Set current process handle in order for CxbxKrnlShutDown to work properly.
g_CurrentProcessHandle = GetCurrentProcess(); // OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
// First of all, check if the EmuShared version matches the emu version and abort otherwise
char GitVersionEmuShared[GitVersionMaxLength];
g_EmuShared->GetGitVersion(GitVersionEmuShared);
@ -1068,8 +1072,6 @@ void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_res
int BootFlags;
g_EmuShared->GetBootFlags(&BootFlags);
g_CurrentProcessHandle = GetCurrentProcess(); // OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
// Set up the logging variables for the kernel process during initialization.
log_sync_config();
@ -1250,6 +1252,10 @@ static void CxbxrKrnlInitHacks()
void(*Entry)(),
int BootFlags)
{
unsigned Host2XbStackBaseReserved = 0;
__asm mov Host2XbStackBaseReserved, esp;
unsigned Host2XbStackSizeReserved = EmuGenerateStackSize(Host2XbStackBaseReserved, 0);
__asm sub esp, Host2XbStackSizeReserved;
// Set windows timer period to 1ms
// Windows will automatically restore this value back to original on program exit
// But with this, we can replace some busy loops with sleeps.
@ -1404,10 +1410,11 @@ static void CxbxrKrnlInitHacks()
// 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);
EmuGenerateFS<true>(xbox::zeroptr, Host2XbStackBaseReserved, Host2XbStackSizeReserved);
if (!xbox::ObInitSystem()) {
CxbxrKrnlAbortEx(LOG_PREFIX_INIT, "Unable to intialize ObInitSystem.");
}
xbox::PsInitSystem();
xbox::KiInitSystem();
// initialize graphics
@ -1507,7 +1514,8 @@ static void CxbxrKrnlInitHacks()
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxLaunchXbe, Entry, FALSE);
EmuKeFreePcr<true>();
EmuKeFreePcr();
__asm add esp, Host2XbStackSizeReserved;
// This will wait forever
std::condition_variable cv;

View File

@ -115,10 +115,8 @@
// = 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>();
template void EmuGenerateFS<true>(xbox::PETHREAD Ethread, unsigned XboxStackBaseReserved, unsigned XboxStackSizeReserved);
template void EmuGenerateFS<false>(xbox::PETHREAD Ethread, unsigned XboxStackBaseReserved, unsigned XboxStackSizeReserved);
NT_TIB *GetNtTib()
{
@ -194,61 +192,16 @@ void EmuKeSetPcr(xbox::KPCR *Pcr)
__writefsdword(TIB_ArbitraryDataSlot, (DWORD)Pcr);
}
template<bool IsHostThread>
void EmuKeFreePcr()
{
xbox::PKPCR Pcr = EmuKeGetPcr();
xbox::PVOID Dummy;
xbox::ulong_xt Size;
xbox::ntstatus_xt Status;
// tls can be nullptr
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) - TLS_ALIGNMENT_OFFSET;
Size = xbox::zero;
Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free tls
assert(Status == X_STATUS_SUCCESS);
}
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
assert(Status == X_STATUS_SUCCESS);
}
Dummy = Pcr;
Size = xbox::zero;
Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free pcr
using namespace xbox;
PVOID Pcr = EmuKeGetPcr();
ulong_xt Size = zero;
ntstatus_xt Status = NtFreeVirtualMemory(&Pcr, &Size, XBOX_MEM_RELEASE); // free pcr
assert(Status == X_STATUS_SUCCESS);
__writefsdword(TIB_ArbitraryDataSlot, NULL);
}
void EmuKeFreeThread(xbox::ntstatus_xt ExitStatus)
{
// Free all kernel resources that were allocated fo this thread
xbox::KeEmptyQueueApc();
xbox::PETHREAD eThread = xbox::PspGetCurrentThread();
xbox::KeQuerySystemTime(&eThread->ExitTime);
eThread->Tcb.HasTerminated = 1;
RemoveEntryList(&eThread->Tcb.ThreadListEntry);
// Emulate our exit strategy for GetExitCodeThread
eThread->ExitStatus = ExitStatus;
eThread->Tcb.Header.SignalState = 1;
if (GetNativeHandle(eThread->UniqueThread)) {
xbox::NtClose(eThread->UniqueThread);
eThread->UniqueThread = xbox::zeroptr;
}
EmuKeFreePcr();
}
__declspec(naked) void EmuFS_RefreshKPCR()
{
// Backup all registers, call EmuKeGetPcr and then restore all registers
@ -679,81 +632,33 @@ void EmuInitFS()
EmuLogEx(CXBXR_MODULE::INIT, LOG_LEVEL::DEBUG, "Done patching FS Register Accesses\n");
}
// Get Xbox's TIB StackBase address from thread's StackBase.
xbox::PVOID EmuGetTIBStackBase(xbox::PVOID ThreadStackBase) {
xbox::addr_xt StackBaseAddr = reinterpret_cast<xbox::addr_xt>(ThreadStackBase);
StackBaseAddr -= sizeof(xbox::FX_SAVE_AREA);
return reinterpret_cast<xbox::PVOID>(StackBaseAddr);
}
// generate stack size reserved for xbox threads to write on.
xbox::dword_xt EmuGenerateStackSize(xbox::addr_xt& espBaseAddress, xbox::ulong_xt TlsDataSize) {
using namespace xbox;
dword_xt StackSize = espBaseAddress & 15; // Fix 16 byte alignment
espBaseAddress -= StackSize;
StackSize += sizeof(FX_SAVE_AREA);
TlsDataSize = ALIGN_UP(TlsDataSize, ulong_xt);
StackSize += TlsDataSize; // (optional)
StackSize += sizeof(KSTART_FRAME);
StackSize += sizeof(KSWITCHFRAME);
return StackSize;
}
// generate fs segment selector
template<bool IsHostThread>
void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread)
void EmuGenerateFS(xbox::PETHREAD Ethread, unsigned Host2XbStackBaseReserved, unsigned Host2XbStackSizeReserved)
{
void *pNewTLS = nullptr;
xbox::PVOID base;
xbox::ulong_xt size;
// Be aware that TLS might be absent (for example in homebrew "Wolf3d-xbox")
if (pTLS != nullptr) {
// copy global TLS to the current thread
{
uint32_t dwCopySize = 0;
uint32_t dwZeroSize = pTLS->dwSizeofZeroFill;
if (pTLSData != NULL) {
// Make sure the TLS Start and End addresses are within Xbox virtual memory
if (pTLS->dwDataStartAddr >= XBE_MAX_VA || pTLS->dwDataEndAddr >= XBE_MAX_VA) {
// ignore
}
else {
dwCopySize = pTLS->dwDataEndAddr - pTLS->dwDataStartAddr;
}
}
/* + HACK: extra safety padding 0x100 */
base = xbox::zeroptr;
size = dwCopySize + dwZeroSize + 0x100 + 0xC;
xbox::NtAllocateVirtualMemory(&base, 0, &size, XBOX_MEM_RESERVE | XBOX_MEM_COMMIT, XBOX_PAGE_READWRITE);
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 + TLS_ALIGNMENT_OFFSET;
if (dwCopySize > 0) {
memcpy((uint8_t*)pNewTLS + 4, pTLSData, dwCopySize);
}
#ifdef _DEBUG_TRACE
// dump raw TLS data
if (pNewTLS == nullptr) {
EmuLog(LOG_LEVEL::DEBUG, "TLS Non-Existant (OK)");
} else {
EmuLog(LOG_LEVEL::DEBUG, "TLS Data Dump...");
if (g_bPrintfOn) {
for (uint32_t v = 4; v < dwCopySize + 4; v++) {// Note : Don't dump dwZeroSize
uint8_t *bByte = (uint8_t*)pNewTLS + v;
if (v % 0x10 == 0) {
EmuLog(LOG_LEVEL::DEBUG, "0x%.8X:", (xbox::addr_xt)bByte);
}
// Note : Use printf instead of EmuLog here, which prefixes with GetCurrentThreadId() :
printf(" %.2X", *bByte);
}
printf("\n");
}
}
#endif
}
// prepare TLS
{
if (pTLS->dwTLSIndexAddr != 0) {
*(xbox::addr_xt*)pTLS->dwTLSIndexAddr = xbox::zero;
}
// dword @ pTLSData := pTLSData
if (pNewTLS != nullptr)
*(void**)pNewTLS = pNewTLS;
}
}
// Allocate the xbox KPCR structure
base = xbox::zeroptr;
size = sizeof(xbox::KPCR);
@ -769,21 +674,26 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread)
//
// Once we simulate thread switching ourselves, we can update PrcbData.CurrentThread
// and simplify this initialization, by using only one KPCR for the single Xbox processor.
//
//
// One way to do our own (preemprive) thread-switching would be to use this technique :
// http://www.eran.io/implementing-a-preemptive-kernel-within-a-single-windows-thread/
// See https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/146 for more info.
// Copy the Nt TIB over to the emulated TIB :
NT_TIB* hTib = GetNtTib();
{
memcpy(XbTib, GetNtTib(), sizeof(NT_TIB));
memcpy(XbTib, hTib, sizeof(NT_TIB));
// Fixup the TIB self pointer :
NewPcr->NtTib.Self = XbTib;
// Set the stack base - TODO : Verify this, doesn't look right?
NewPcr->NtTib.StackBase = pNewTLS;
// NOTE: The actual issue was TlsData was not within Host's stack which is now implemented.
// But instead of direct Host's stack, (which should not be tampered from Host's kernel stack block!)
// we allocated through inline asm to reserve xbox stack dynamically in order to have xbox's kernel stack reside in
// host's stack (in permitted function's stack usage).
// Write the Xbox stack base to the Host, allows ConvertThreadToFiber to work correctly
// Test case: DOA3
// Test case:
// * DoA2
// * DoA3
// NOTE: This is disabled due to cause of corruption to host's TIB and
// silent crash for xbox threads creation.
// Test case:
@ -810,6 +720,8 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread)
if constexpr (IsHostThread) {
// This only happens for the kernel initialization thread of cxbxr
// Another thing to note, we do not insert into xbox's system as it will not be used in running xbox environment.
// Instead it will be sleeping until title/user make a decision what to do next.
assert(Ethread == xbox::zeroptr);
base = xbox::zeroptr;
@ -817,39 +729,61 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD 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
// Emulate kernel stack size as we can't use exact size.
xbox::ulong_xt KernelStackSize = Host2XbStackBaseReserved - reinterpret_cast<xbox::ulong_xt>(hTib->StackLimit);
// Since the cxbxr's kernel initialization occur there, we do not create a new thread
// and therefore doesn't need to set any additional System/Start details set in the xbox's kernel stack.
xbox::KeInitializeThread<IsHostThread>(
&Ethread->Tcb,
(xbox::PVOID)Host2XbStackBaseReserved,
KernelStackSize,
xbox::zero,
xbox::zeroptr, // Unused (SystemRoutine)
xbox::zeroptr, // Unused (StartRoutine)
xbox::zeroptr, // Unused (StartContext)
xbox::zeroptr); // Unused (&KiUniqueProcess)
}
#ifndef ENABLE_KTHREAD_SWITCHING
else {
// Otherwise, xbox::PsCreateSystemThreadEx is called and xbox::KeInitializeThread is already called from it.
// But we need to carry the reserved part onto host's stack to able align with xbox and host sharing the same stack in a new thread.
// Since we are using direct execution than in virtualization environment.
// Tcb.StackBase always point at the beginning of kernel stack (DOWN).
xbox::addr_xt xStackBase = reinterpret_cast<xbox::addr_xt>(Ethread->Tcb.StackBase);
xbox::addr_xt xStackLimit = reinterpret_cast<xbox::addr_xt>(Ethread->Tcb.StackLimit);
xbox::addr_xt xTlsData = reinterpret_cast<xbox::addr_xt>(Ethread->Tcb.TlsData);
xbox::addr_xt xKernelStack = reinterpret_cast<xbox::addr_xt>(Ethread->Tcb.KernelStack);
xbox::dword_xt xKernelStackSize = xStackBase - xKernelStack;
assert(xKernelStackSize <= Host2XbStackSizeReserved);
PVOID hKernelStack = reinterpret_cast<PVOID>(Host2XbStackBaseReserved - xKernelStackSize);
std::memcpy(hKernelStack, Ethread->Tcb.KernelStack, xKernelStackSize);
// Update TlsData address if used
if (Ethread->Tcb.TlsData) {
Ethread->Tcb.TlsData = reinterpret_cast<xbox::PVOID>(Host2XbStackBaseReserved - (xStackBase - xTlsData));
}
// Set stacks addresses
Ethread->Tcb.StackBase = reinterpret_cast<xbox::PVOID>(Host2XbStackBaseReserved);
Ethread->Tcb.StackLimit = hTib->StackLimit; // Always point to host's StackLimit.
Ethread->Tcb.KernelStack = hKernelStack;
// We can safely delete kernel stack as there is no virtualization environment implemented.
xbox::MmDeleteKernelStack(reinterpret_cast<xbox::PVOID>(xStackBase), reinterpret_cast<xbox::PVOID>(xStackLimit));
}
#endif
// Initialize a fake PrcbData.CurrentThread
{
// TODO: Do we need NtTib's overwrite in ENABLE_KTHREAD_SWITCHING usage?
// Set the stack details over to NtTib's structure.
NewPcr->NtTib.StackBase = EmuGetTIBStackBase(Ethread->Tcb.StackBase);
NewPcr->NtTib.StackLimit = Ethread->Tcb.StackLimit;
// Set PrcbData.CurrentThread
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]);
Prcb->CurrentThread->KernelApcDisable = 0;
Prcb->CurrentThread->ApcState.ApcQueueable = TRUE;
Prcb->CurrentThread->ApcState.Process = &KiUniqueProcess;
Prcb->CurrentThread->ApcState.Process->ThreadQuantum = KiUniqueProcess.ThreadQuantum;
// Initialize the thread header and its wait list
Prcb->CurrentThread->Header.Type = xbox::ThreadObject;
Prcb->CurrentThread->Header.Size = sizeof(xbox::KTHREAD) / sizeof(xbox::long_xt);
InitializeListHead(&Prcb->CurrentThread->Header.WaitListHead);
// Also initialize the timer associated with the thread
xbox::KeInitializeTimer(&Prcb->CurrentThread->Timer);
xbox::PKWAIT_BLOCK WaitBlock = &Prcb->CurrentThread->TimerWaitBlock;
WaitBlock->Object = &Prcb->CurrentThread->Timer;
WaitBlock->WaitKey = (xbox::cshort_xt)STATUS_TIMEOUT;
WaitBlock->WaitType = xbox::WaitAny;
WaitBlock->Thread = Prcb->CurrentThread;
WaitBlock->WaitListEntry.Flink = &Prcb->CurrentThread->Timer.Header.WaitListHead;
WaitBlock->WaitListEntry.Blink = &Prcb->CurrentThread->Timer.Header.WaitListHead;
}
// Make the KPCR struct available to EmuKeGetPcr()
EmuKeSetPcr(NewPcr);
EmuLog(LOG_LEVEL::DEBUG, "Installed KPCR in TIB_ArbitraryDataSlot (with pTLS = 0x%.8X)", pTLS);
EmuLog(LOG_LEVEL::DEBUG, "Installed KPCR in TIB_ArbitraryDataSlot (with Ethread->Tcb.TlsData = 0x%.8X)", Ethread->Tcb.TlsData);
_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

@ -29,16 +29,20 @@
#include "common\xbe\Xbe.h"
#include <windows.h>
// Get Xbox's TIB StackBase address from thread's StackBase.
xbox::PVOID EmuGetTIBStackBase(xbox::PVOID ThreadStackBase);
// generate stack size reserved for xbox threads to write on.
// espBaseAddress will return aligned DOWN address, do not rely on return's size for offset usage.
xbox::dword_xt EmuGenerateStackSize(xbox::addr_xt& espBaseAddress, IN xbox::ulong_xt TlsDataSize);
// initialize fs segment selector emulation
extern void EmuInitFS();
// generate fs segment selector
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);
void EmuGenerateFS(xbox::PETHREAD Ethread, unsigned XboxThreadStackBaseReserved = 0, unsigned XboxThreadStackSizeReserved = 0);
// free kpcr allocated for the thread
template<bool IsHostThread = false>
void EmuKeFreePcr();
void EmuKeSetPcr(xbox::KPCR *Pcr);
@ -50,9 +54,4 @@ 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