Added APCs support to kernel

This commit is contained in:
ergo720 2022-01-02 17:10:54 +01:00
parent 607a48e3ea
commit 114be1b7c9
10 changed files with 237 additions and 122 deletions

View File

@ -261,7 +261,7 @@ XBSYSAPI EXPORTNUM(206) ntstatus_xt NTAPI NtQueueApcThread
IN PIO_APC_ROUTINE ApcRoutine,
IN PVOID ApcRoutineContext OPTIONAL,
IN PIO_STATUS_BLOCK ApcStatusBlock OPTIONAL,
IN ulong_xt ApcReserved OPTIONAL
IN PVOID ApcReserved OPTIONAL
);
// ******************************************************************

View File

@ -29,6 +29,7 @@
#include <core\kernel\exports\xboxkrnl.h>
#include "core\kernel\support\EmuFS.h"
#include <cstdio>
#include <cctype>
#include <clocale>
@ -119,12 +120,6 @@ xbox::PLIST_ENTRY RemoveTailList(xbox::PLIST_ENTRY pListHead)
return Result;
}
// ******************************************************************
// * Declaring this in a header causes errors with xboxkrnl
// * namespace, so we must declare it within any file that uses it
// ******************************************************************
xbox::KPCR* WINAPI KeGetPcr();
// Interrupts
extern volatile DWORD HalInterruptRequestRegister;
@ -157,8 +152,8 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql)
case PASSIVE_LEVEL:
KiUnexpectedInterrupt();
break;
case APC_LEVEL: // = 1 // HalpApcInterrupt
EmuLog(LOG_LEVEL::WARNING, "Unimplemented Software Interrupt (APC)"); // TODO : ExecuteApcQueue();
case APC_LEVEL: // = 1
xbox::KiExecuteKernelApc();
break;
case DISPATCH_LEVEL: // = 2
ExecuteDpcQueue();
@ -374,7 +369,7 @@ XBSYSAPI EXPORTNUM(160) xbox::KIRQL FASTCALL xbox::KfRaiseIrql
LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, NewIrql);
// Inlined KeGetCurrentIrql() :
PKPCR Pcr = KeGetPcr();
PKPCR Pcr = EmuKeGetPcr();
KIRQL OldIrql = (KIRQL)Pcr->Irql;
// Set new before check
@ -402,7 +397,7 @@ XBSYSAPI EXPORTNUM(161) xbox::void_xt FASTCALL xbox::KfLowerIrql
{
LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, NewIrql);
KPCR* Pcr = KeGetPcr();
KPCR* Pcr = EmuKeGetPcr();
if (g_bIsDebugKernel && NewIrql > Pcr->Irql) {
KIRQL OldIrql = Pcr->Irql;
@ -450,12 +445,23 @@ XBSYSAPI EXPORTNUM(163) xbox::void_xt FASTCALL xbox::KiUnlockDispatcherDatabase
{
LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, OldIrql);
if (!(KeGetCurrentPrcb()->DpcRoutineActive)) // Avoid KeIsExecutingDpc(), as that logs
// Wrong, this should only happen when OldIrql >= DISPATCH_LEVEL
if (!(KeGetCurrentPrcb()->DpcRoutineActive)) { // Avoid KeIsExecutingDpc(), as that logs
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
LOG_INCOMPLETE(); // TODO : Thread-switch?
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.
// We can't perform our own threads switching now, so we will just check the current thread
if (KeGetCurrentThread()->ApcState.KernelApcPending) {
KiExecuteKernelApc();
}
}
KfLowerIrql(OldIrql);
LOG_INCOMPLETE(); // TODO : Thread-switch?
}
// ******************************************************************

View File

@ -35,6 +35,7 @@
#include "EmuKrnlLogging.h"
#include "core\kernel\init\CxbxKrnl.h" // For CxbxrKrnlAbort, and CxbxExec
#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, )
#include "core\kernel\support\EmuFS.h"
#include "EmuKrnl.h"
#include "devices\x86\EmuX86.h" // HalReadWritePciSpace needs this
#include "EmuShared.h"
@ -59,13 +60,6 @@ uint32_t ResetOrShutdownDataValue = 0;
// global list of routines executed during a reboot
xbox::LIST_ENTRY ShutdownRoutineList = { &ShutdownRoutineList , &ShutdownRoutineList }; // see InitializeListHead()
// ******************************************************************
// * Declaring this in a header causes errors with xboxkrnl
// * namespace, so we must declare it within any file that uses it
// ******************************************************************
xbox::KPCR* WINAPI KeGetPcr();
#define TRAY_CLOSED_MEDIA_PRESENT 0x60
#define TRAY_CLOSED_NO_MEDIA 0x40
#define TRAY_OPEN 0x10
@ -458,10 +452,10 @@ XBSYSAPI EXPORTNUM(48) xbox::void_xt FASTCALL xbox::HalRequestSoftwareInterrupt
HalInterruptRequestRegister |= InterruptMask;
// Get current IRQL
PKPCR Pcr = KeGetPcr();
PKPCR Pcr = EmuKeGetPcr();
KIRQL CurrentIrql = (KIRQL)Pcr->Irql;
// Get pending Software Interrupts (by masking of the HW interrupt bits)
// Get pending Software Interrupts (by masking off the HW interrupt bits)
uint8_t SoftwareInterrupt = HalInterruptRequestRegister & 3;
// Get the highest pending software interrupt level

View File

@ -128,11 +128,11 @@ xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value)
// ******************************************************************
// * KeGetPcr()
// * EmuKeGetPcr()
// * NOTE: This is a macro on the Xbox, however we implement it
// * as a function so it can suit our emulated KPCR structure
// ******************************************************************
xbox::KPCR* WINAPI KeGetPcr()
xbox::KPCR* WINAPI EmuKeGetPcr()
{
xbox::PKPCR Pcr;
@ -140,7 +140,7 @@ xbox::KPCR* WINAPI KeGetPcr()
Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot);
if (Pcr == nullptr) {
// If we reach here, it's a bug: it means we are executing xbox code from a host thread, and we have forgotten to intialize
// If we reach here, it's a bug: it means we are executing xbox code from a host thread, and we have forgotten to initialize
// the xbox thread first
CxbxrKrnlAbort("KeGetPCR returned nullptr: Was this called from a non-xbox thread?");
}
@ -153,7 +153,7 @@ xbox::KPCR* WINAPI KeGetPcr()
// ******************************************************************
xbox::KPRCB *KeGetCurrentPrcb()
{
return &(KeGetPcr()->PrcbData);
return &(EmuKeGetPcr()->PrcbData);
}
// ******************************************************************
@ -595,7 +595,7 @@ XBSYSAPI EXPORTNUM(103) xbox::KIRQL NTAPI xbox::KeGetCurrentIrql(void)
{
LOG_FUNC(); // TODO : Remove nested logging on this somehow, so we can call this (instead of inlining)
KPCR* Pcr = KeGetPcr();
KPCR* Pcr = EmuKeGetPcr();
KIRQL Irql = (KIRQL)Pcr->Irql;
RETURN_TYPE(KIRQL_TYPE, Irql);
@ -993,6 +993,9 @@ XBSYSAPI EXPORTNUM(117) xbox::long_xt NTAPI xbox::KeInsertQueue
RETURN(0);
}
// ******************************************************************
// * 0x0076 - KeInsertQueueApc()
// ******************************************************************
XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc
(
IN PRKAPC Apc,
@ -1008,9 +1011,47 @@ XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc
LOG_FUNC_ARG(Increment)
LOG_FUNC_END;
LOG_UNIMPLEMENTED();
KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
RETURN(TRUE);
PKTHREAD kThread = Apc->Thread;
if (kThread->ApcState.ApcQueueable == FALSE) {
KfLowerIrql(OldIrql);
RETURN(FALSE);
}
else {
Apc->SystemArgument1 = SystemArgument1;
Apc->SystemArgument2 = SystemArgument2;
if (Apc->Inserted) {
KfLowerIrql(OldIrql);
RETURN(FALSE);
}
else {
g_ApcListMtx.lock();
InsertTailList(&kThread->ApcState.ApcListHead[Apc->ApcMode], &Apc->ApcListEntry);
g_ApcListMtx.unlock();
Apc->Inserted = TRUE;
// 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
if (kThread == KeGetCurrentThread()) {
if (Apc->ApcMode == KernelMode) { // kernel apc
// NOTE: this is wrong, we should check the thread state instead of just signaling the kernel apc, but we currently
// don't set the appropriate state in kthread
kThread->ApcState.KernelApcPending = TRUE;
KiExecuteKernelApc();
}
else if ((kThread->WaitMode == UserMode) && (kThread->Alertable)) { // user apc
// NOTE: this should also check the thread state
kThread->ApcState.UserApcPending = TRUE;
KiExecuteUserApc();
}
}
KfLowerIrql(OldIrql);
RETURN(TRUE);
}
}
}
// ******************************************************************
@ -1087,10 +1128,10 @@ XBSYSAPI EXPORTNUM(122) xbox::void_xt NTAPI xbox::KeLeaveCriticalRegion
PKTHREAD thread = KeGetCurrentThread();
thread->KernelApcDisable++;
if(thread->KernelApcDisable == 0) {
LIST_ENTRY *apcListHead = &thread->ApcState.ApcListHead[0/*=KernelMode*/];
LIST_ENTRY *apcListHead = &thread->ApcState.ApcListHead[KernelMode];
if(apcListHead->Flink != apcListHead) {
thread->ApcState.KernelApcPending = 1; // TRUE
HalRequestSoftwareInterrupt(1); // APC_LEVEL
thread->ApcState.KernelApcPending = TRUE;
HalRequestSoftwareInterrupt(APC_LEVEL);
}
}
}
@ -2168,7 +2209,7 @@ NoWait:
KiUnlockDispatcherDatabase(Thread->WaitIrql);
if (WaitStatus == X_STATUS_USER_APC) {
// TODO: KiDeliverUserApc();
KiExecuteUserApc();
}
RETURN(WaitStatus);
@ -2352,7 +2393,7 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
// So unlock the dispatcher database, lower the IRQ and return the status
KiUnlockDispatcherDatabase(Thread->WaitIrql);
if (WaitStatus == X_STATUS_USER_APC) {
//TODO: KiDeliverUserApc();
KiExecuteUserApc();
}
RETURN(WaitStatus);

View File

@ -80,6 +80,7 @@ the said software).
#include <core\kernel\exports\xboxkrnl.h> // For KeBugCheck, etc.
#include "core\kernel\support\EmuFS.h"
#include "Logging.h" // For LOG_FUNC()
#include "EmuKrnl.h" // for the list support functions
#include "EmuKrnlKi.h"
@ -877,3 +878,53 @@ xbox::void_xt FASTCALL xbox::KiWaitSatisfyAll
return;
}
template<bool KernelApc>
static xbox::void_xt KiExecuteApc()
{
xbox::PKTHREAD kThread = xbox::KeGetCurrentThread();
int ApcMode;
if constexpr (KernelApc) {
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::g_ApcListMtx.lock();
while (!IsListEmpty(&kThread->ApcState.ApcListHead[ApcMode])) {
if (KernelApc && (kThread->KernelApcDisable != 0)) {
xbox::g_ApcListMtx.unlock();
return;
}
xbox::PLIST_ENTRY Entry = kThread->ApcState.ApcListHead[ApcMode].Flink;
xbox::PKAPC Apc = CONTAINING_RECORD(Entry, xbox::KAPC, ApcListEntry);
RemoveEntryList(Entry);
xbox::g_ApcListMtx.unlock();
Apc->Inserted = FALSE;
// NOTE: we never use KernelRoutine
if (Apc->NormalRoutine != xbox::zeroptr) {
(Apc->NormalRoutine)(Apc->NormalContext, Apc->SystemArgument1, Apc->SystemArgument2);
}
xbox::ExFreePool(Apc);
xbox::g_ApcListMtx.lock();
}
xbox::g_ApcListMtx.unlock();
}
xbox::void_xt xbox::KiExecuteKernelApc()
{
KiExecuteApc<true>();
}
xbox::void_xt xbox::KiExecuteUserApc()
{
KiExecuteApc<false>();
}

View File

@ -47,14 +47,16 @@ namespace xbox
int Acquired;
} KI_TIMER_LOCK;
// NOTE: since the apc list is per-thread, we could also create a different mutex for each kthread
std::mutex g_ApcListMtx;
xbox::void_xt KiInitSystem();
void_xt KiInitSystem();
xbox::void_xt KiTimerLock();
void_xt KiTimerLock();
xbox::void_xt KiTimerUnlock();
void_xt KiTimerUnlock();
xbox::void_xt KiClockIsr
void_xt KiClockIsr
(
IN unsigned int ScalingFactor
);
@ -64,25 +66,25 @@ namespace xbox
IN ULARGE_INTEGER CurrentTime
);
xbox::void_xt KxInsertTimer
void_xt KxInsertTimer
(
IN PKTIMER Timer,
IN ulong_xt Hand
);
xbox::void_xt FASTCALL KiCompleteTimer
void_xt FASTCALL KiCompleteTimer
(
IN PKTIMER Timer,
IN ulong_xt Hand
);
xbox::void_xt KiRemoveEntryTimer
void_xt KiRemoveEntryTimer
(
IN PKTIMER Timer,
IN ulong_xt Hand
);
xbox::void_xt KxRemoveTreeTimer
void_xt KxRemoveTreeTimer
(
IN PKTIMER Timer
);
@ -99,7 +101,7 @@ namespace xbox
IN LARGE_INTEGER Interval
);
xbox::ulong_xt KiComputeTimerTableIndex
ulong_xt KiComputeTimerTableIndex
(
IN ulonglong_xt Interval
);
@ -116,7 +118,7 @@ namespace xbox
IN PKTIMER Timer
);
xbox::void_xt NTAPI KiTimerExpiration
void_xt NTAPI KiTimerExpiration
(
IN PKDPC Dpc,
IN PVOID DeferredContext,
@ -124,16 +126,19 @@ namespace xbox
IN PVOID SystemArgument2
);
xbox::void_xt FASTCALL KiTimerListExpire
void_xt FASTCALL KiTimerListExpire
(
IN PLIST_ENTRY ExpiredListHead,
IN KIRQL OldIrql
);
xbox::void_xt FASTCALL KiWaitSatisfyAll
void_xt FASTCALL KiWaitSatisfyAll
(
IN PKWAIT_BLOCK WaitBlock
);
void_xt KiExecuteKernelApc();
void_xt KiExecuteUserApc();
};
extern xbox::KPROCESS KiUniqueProcess;

View File

@ -30,6 +30,7 @@
#include <core\kernel\exports\xboxkrnl.h> // For NtAllocateVirtualMemory, etc.
#include "EmuKrnl.h"
#include "Logging.h" // For LOG_FUNC()
#include "EmuKrnlLogging.h"
@ -41,6 +42,7 @@ namespace NtDll
#include "core\kernel\init\CxbxKrnl.h" // For CxbxrKrnlAbort
#include "core\kernel\exports\EmuKrnlKe.h"
#include "EmuKrnlKi.h"
#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, )
#include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc.
#include "core\kernel\memory-manager\VMManager.h" // For g_VMManager
@ -54,12 +56,16 @@ namespace NtDll
#include <unordered_map>
#include <mutex>
#include <future>
// Used to keep track of duplicate handles created by NtQueueApcThread()
std::unordered_map<HANDLE, HANDLE> g_DuplicateHandles;
// 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()
@ -173,15 +179,6 @@ XBSYSAPI EXPORTNUM(187) xbox::ntstatus_xt NTAPI xbox::NtClose
if (GetHandleInformation(Handle, &flags) != 0) {
// This was a native handle, call NtDll::NtClose
ret = NtDll::NtClose(Handle);
// Delete duplicate threads created by our implementation of NtQueueApcThread()
if (GetHandleInformation(g_DuplicateHandles[Handle], &flags) != 0)
{
EmuLog(LOG_LEVEL::DEBUG, "Closing duplicate handle...");
CloseHandle(g_DuplicateHandles[Handle]);
g_DuplicateHandles.erase(Handle);
}
}
}
@ -1028,7 +1025,7 @@ XBSYSAPI EXPORTNUM(206) xbox::ntstatus_xt NTAPI xbox::NtQueueApcThread
IN PIO_APC_ROUTINE ApcRoutine,
IN PVOID ApcRoutineContext OPTIONAL,
IN PIO_STATUS_BLOCK ApcStatusBlock OPTIONAL,
IN ulong_xt ApcReserved OPTIONAL
IN PVOID ApcReserved OPTIONAL
)
{
LOG_FUNC_BEGIN
@ -1039,54 +1036,27 @@ XBSYSAPI EXPORTNUM(206) xbox::ntstatus_xt NTAPI xbox::NtQueueApcThread
LOG_FUNC_ARG(ApcReserved)
LOG_FUNC_END;
// In order for NtQueueApcThread or QueueUserAPC to work, you must... I repeat...
// YOU MUST duplicate the handle with the appropriate permissions first! So far,
// the only game that I know of using this is Metal Slug 3, and it won't launch
// without it. Other SNK games might use it also, beware.
PETHREAD Thread;
ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast<PVOID *>(&Thread));
if (!X_NT_SUCCESS(result)) {
RETURN(result);
}
// TODO: Use our implementation of NtDuplicateObject instead?
HANDLE hApcThread = NULL;
// Just to be safe, let's see if the appropriate permissions are even set for the
// target thread first...
NTSTATUS ret = NtDll::NtQueueApcThread(
(NtDll::HANDLE)ThreadHandle,
(NtDll::PIO_APC_ROUTINE)ApcRoutine,
ApcRoutineContext,
(NtDll::PIO_STATUS_BLOCK)ApcStatusBlock,
ApcReserved);
if( FAILED( ret ) )
{
EmuLog(LOG_LEVEL::WARNING, "Duplicating handle with THREAD_SET_CONTEXT..." );
// If we get here, then attempt to duplicate the thread.
if(!DuplicateHandle(g_CurrentProcessHandle, ThreadHandle, g_CurrentProcessHandle, &hApcThread, THREAD_SET_CONTEXT,FALSE,0))
EmuLog(LOG_LEVEL::WARNING, "DuplicateHandle failed!");
else
{
g_DuplicateHandles[ThreadHandle] = hApcThread; // Save this thread because we'll need to de-reference it later
EmuLog(LOG_LEVEL::DEBUG, "DuplicateHandle returned 0x%X (ThreadId 0x%.4X)", hApcThread, GetThreadId( hApcThread ) );
PKAPC Apc = static_cast<PKAPC>(ExAllocatePoolWithTag(sizeof(KAPC), 'pasP'));
if (Apc != zeroptr) {
KeInitializeApc(Apc, &Thread->Tcb, zeroptr, zeroptr, reinterpret_cast<PKNORMAL_ROUTINE>(ApcRoutine), UserMode, ApcRoutineContext);
if (!KeInsertQueueApc(Apc, ApcStatusBlock, ApcReserved, 0)) {
ExFreePool(Apc);
result = X_STATUS_UNSUCCESSFUL;
}
ret = NtDll::NtQueueApcThread(
(NtDll::HANDLE)hApcThread,
(NtDll::PIO_APC_ROUTINE)ApcRoutine,
ApcRoutineContext,
(NtDll::PIO_STATUS_BLOCK)ApcStatusBlock,
ApcReserved);
}
if (FAILED(ret))
{
EmuLog(LOG_LEVEL::WARNING, "NtQueueApcThread failed!");
CloseHandle( g_DuplicateHandles[ThreadHandle] );
g_DuplicateHandles.erase( ThreadHandle );
else {
result = X_STATUS_NO_MEMORY;
}
RETURN(ret);
ObfDereferenceObject(Thread);
RETURN(result);
}
// ******************************************************************
@ -1159,8 +1129,8 @@ XBSYSAPI EXPORTNUM(207) xbox::ntstatus_xt NTAPI xbox::NtQueryDirectoryFile
ret = NtDll::NtQueryDirectoryFile(
FileHandle,
Event,
(NtDll::PIO_APC_ROUTINE)ApcRoutine,
ApcContext,
(NtDll::PIO_APC_ROUTINE)ApcRoutine,
ApcContext,
(NtDll::IO_STATUS_BLOCK*)IoStatusBlock,
/*FileInformation=*/NtFileDirInfo,
NtFileDirectoryInformationSize + NtPathBufferSize,
@ -2227,20 +2197,53 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
// This function can wait on thread handles, which are currently created by ob,
// so we need to check their presence in the handle array
for (ulong_xt i = 0; i < Count; ++i) {
if (const auto &nativeHandle = GetNativeHandle(Handles[i])) {
// This a ob handle, so replace it with its native counterpart
// This is a ob handle, so replace it with its native counterpart
Handles[i] = *nativeHandle;
}
}
return NtDll::NtWaitForMultipleObjects(
Count,
Handles,
(NtDll::OBJECT_WAIT_TYPE)WaitType,
Alertable,
(NtDll::PLARGE_INTEGER)Timeout);
// 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();
auto &fut = std::async(std::launch::async, [eThread, &Exit]() {
while (true) {
xbox::g_ApcListMtx.lock();
bool Empty = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[UserMode]);
xbox::g_ApcListMtx.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
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);
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);
}
}
// ******************************************************************

View File

@ -38,6 +38,7 @@
#include "Logging.h" // For LOG_FUNC()
#include "EmuKrnlLogging.h"
#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnl_TLS
#include "EmuKrnl.h"
#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, )
#include "core\kernel\support\EmuFS.h" // For EmuGenerateFS
#include "core\kernel\support\NativeHandle.h"
@ -404,12 +405,24 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread
}
}*/
PKTHREAD kThread = KeGetCurrentThread();
kThread->ApcState.ApcQueueable = FALSE;
g_ApcListMtx.lock();
for (int Mode = KernelMode; Mode < MaximumMode; ++Mode) {
while (!IsListEmpty(&kThread->ApcState.ApcListHead[Mode])) {
PLIST_ENTRY Entry = kThread->ApcState.ApcListHead[Mode].Flink;
PKAPC Apc = CONTAINING_RECORD(Entry, KAPC, ApcListEntry);
RemoveEntryList(Entry);
ExFreePool(Apc);
}
}
g_ApcListMtx.unlock();
EmuKeFreeThread();
KiUniqueProcess.StackCount--;
_endthreadex(ExitStatus);
// ExitThread(ExitStatus);
// CxbxKrnlTerminateThread();
}
// ******************************************************************

View File

@ -116,9 +116,6 @@ NT_TIB *GetNtTib()
return (NT_TIB *)__readfsdword(TIB_LinearSelfAddress);
}
xbox::KPCR* WINAPI KeGetPcr();
uint32_t fs_lock = 0;
__declspec(naked) void LockFS()
@ -160,7 +157,7 @@ __declspec(naked) void UnlockFS()
void EmuKeSetPcr(xbox::KPCR *Pcr)
{
// Store the Xbox KPCR pointer in FS (See KeGetPcr())
// Store the Xbox KPCR pointer in FS (See EmuKeGetPcr())
//
// Note : Cxbx currently doesn't do preemptive thread switching,
// which implies that thread-state management is done by Windows.
@ -190,7 +187,7 @@ void EmuKeSetPcr(xbox::KPCR *Pcr)
void EmuKeFreePcr()
{
xbox::PKPCR Pcr = KeGetPcr();
xbox::PKPCR Pcr = EmuKeGetPcr();
xbox::PVOID Dummy;
xbox::ulong_xt Size;
@ -227,8 +224,8 @@ void EmuKeFreeThread()
__declspec(naked) void EmuFS_RefreshKPCR()
{
// Backup all registers, call KeGetPcr and then restore all registers
// KeGetPcr makes sure a valid KPCR exists for the current thread
// Backup all registers, call EmuKeGetPcr and then restore all registers
// EmuKeGetPcr makes sure a valid KPCR exists for the current thread
// and creates it if missing, we backup and restore all registers
// to keep it safe to call in our patches
// This function can be later expanded to do nice things
@ -236,7 +233,7 @@ __declspec(naked) void EmuFS_RefreshKPCR()
__asm {
pushfd
pushad
call KeGetPcr
call EmuKeGetPcr
popad
popfd
ret
@ -806,6 +803,8 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread)
EThread->Tcb.TlsData = pNewTLS;
// Set PrcbData.CurrentThread
Prcb->CurrentThread = (xbox::KTHREAD*)EThread;
Prcb->CurrentThread->KernelApcDisable = 0;
Prcb->CurrentThread->ApcState.ApcQueueable = TRUE;
// 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);
@ -821,7 +820,7 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PVOID Ethread)
WaitBlock->WaitListEntry.Blink = &Prcb->CurrentThread->Timer.Header.WaitListHead;
}
// Make the KPCR struct available to KeGetPcr()
// Make the KPCR struct available to EmuKeGetPcr()
EmuKeSetPcr(NewPcr);
EmuLog(LOG_LEVEL::DEBUG, "Installed KPCR in TIB_ArbitraryDataSlot (with pTLS = 0x%.8X)", pTLS);

View File

@ -39,6 +39,9 @@ void EmuKeFreeThread();
// free kpcr allocated for the thread
void EmuKeFreePcr();
void EmuKeSetPcr(xbox::KPCR *Pcr);
xbox::KPCR *_stdcall EmuKeGetPcr();
typedef struct
{
std::vector<uint8_t> data;