Added APCs support to kernel
This commit is contained in:
parent
607a48e3ea
commit
114be1b7c9
|
@ -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
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -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?
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue