2619 lines
75 KiB
C++
2619 lines
75 KiB
C++
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
|
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
|
// ******************************************************************
|
|
// *
|
|
// * 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.
|
|
// *
|
|
// * (c) 2002-2003 Aaron Robinson <caustik@caustik.com>
|
|
// * (c) 2016 Patrick van Logchem <pvanlogchem@gmail.com>
|
|
// *
|
|
// * All rights reserved
|
|
// *
|
|
// ******************************************************************
|
|
|
|
// Acknowledgment (timer functions): ReactOS (GPLv2)
|
|
// https://github.com/reactos/reactos
|
|
|
|
// KeSetSystemTime
|
|
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/ke/clock.c
|
|
* PURPOSE: System Clock Support
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
*/
|
|
|
|
// KeInitializeTimerEx, KeSetTimer, KeSetTimerEx, KeCancelTimer
|
|
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/ke/timerobj.c
|
|
* PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers)
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
*/
|
|
|
|
// COPYING file:
|
|
/*
|
|
GNU GENERAL PUBLIC LICENSE
|
|
Version 2, June 1991
|
|
|
|
ReactOS may be used, runtime linked, and distributed with non-free software
|
|
(meaning that such software has no obligations to open-source, or render
|
|
free, their non-free code) such as commercial device drivers and commercial
|
|
applications. This exception does not alter any other responsibilities of
|
|
the licensee under the GPL (meaning that such software must still obey
|
|
the GPL for the free ("open-sourced") code that has been integrated into
|
|
the said software).
|
|
*/
|
|
|
|
|
|
#define LOG_PREFIX CXBXR_MODULE::KE
|
|
|
|
|
|
#include <core\kernel\exports\xboxkrnl.h> // For KeBugCheck, etc.
|
|
#include "Logging.h" // For LOG_FUNC()
|
|
#include "EmuKrnlLogging.h"
|
|
|
|
// prevent name collisions
|
|
namespace NtDll
|
|
{
|
|
#include "core\kernel\support\EmuNtDll.h" // For NtDelayExecution(), etc.
|
|
};
|
|
|
|
#include "core\kernel\init\CxbxKrnl.h" // For CxbxrAbort
|
|
#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, )
|
|
#include "EmuKrnl.h" // For InitializeListHead(), etc.
|
|
#include "EmuKrnlKe.h"
|
|
#include "core\kernel\support\EmuFile.h" // For IsEmuHandle(), NtStatusToString()
|
|
#include "core\kernel\support\NativeHandle.h"
|
|
#include "Timer.h"
|
|
#include "Util.h"
|
|
|
|
#pragma warning(disable:4005) // Ignore redefined status values
|
|
#include <ntstatus.h>
|
|
|
|
#include <chrono>
|
|
#include <thread>
|
|
#include <windows.h>
|
|
#include <map>
|
|
|
|
// Copied over from Dxbx.
|
|
// TODO : Move towards thread-simulation based Dpc emulation
|
|
typedef struct _DpcData {
|
|
CRITICAL_SECTION Lock;
|
|
HANDLE DpcEvent;
|
|
xbox::LIST_ENTRY DpcQueue; // TODO : Use KeGetCurrentPrcb()->DpcListHead instead
|
|
} DpcData;
|
|
|
|
DpcData g_DpcData = { 0 }; // Note : g_DpcData is initialized in InitDpcThread()
|
|
|
|
xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value)
|
|
{
|
|
// Weird construction because there doesn't seem to exist an implicit
|
|
// conversion of LARGE_INTEGER to ULONGLONG :
|
|
return *((PULONGLONG)&value);
|
|
}
|
|
|
|
#define TestForAlertPending(Alertable) \
|
|
if (Alertable) { \
|
|
if (Thread->Alerted[WaitMode] != FALSE) { \
|
|
Thread->Alerted[WaitMode] = FALSE; \
|
|
WaitStatus = X_STATUS_ALERTED; \
|
|
break; \
|
|
} else if ((WaitMode != KernelMode) && \
|
|
(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) == FALSE) { \
|
|
Thread->ApcState.UserApcPending = TRUE; \
|
|
WaitStatus = X_STATUS_USER_APC; \
|
|
break; \
|
|
} else if (Thread->Alerted[KernelMode] != FALSE) { \
|
|
Thread->Alerted[KernelMode] = FALSE; \
|
|
WaitStatus = X_STATUS_ALERTED; \
|
|
break; \
|
|
} \
|
|
} else if ((WaitMode != KernelMode) && (Thread->ApcState.UserApcPending)) { \
|
|
WaitStatus = X_STATUS_USER_APC; \
|
|
break; \
|
|
}
|
|
|
|
|
|
// ******************************************************************
|
|
// * 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 EmuKeGetPcr()
|
|
{
|
|
// See EmuKeSetPcr()
|
|
xbox::PKPCR Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot);
|
|
|
|
// If this fails, 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
|
|
assert(Pcr);
|
|
|
|
return Pcr;
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * KeGetCurrentPrcb()
|
|
// ******************************************************************
|
|
xbox::KPRCB *KeGetCurrentPrcb()
|
|
{
|
|
return &(EmuKeGetPcr()->PrcbData);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * KeSetSystemTime()
|
|
// ******************************************************************
|
|
xbox::void_xt NTAPI xbox::KeSetSystemTime
|
|
(
|
|
IN xbox::PLARGE_INTEGER NewTime,
|
|
OUT xbox::PLARGE_INTEGER OldTime
|
|
)
|
|
{
|
|
KIRQL OldIrql, OldIrql2;
|
|
LARGE_INTEGER DeltaTime, HostTime;
|
|
PLIST_ENTRY ListHead, NextEntry;
|
|
PKTIMER Timer;
|
|
LIST_ENTRY TempList, TempList2;
|
|
ULONG Hand, i;
|
|
|
|
/* Sanity checks */
|
|
assert((NewTime->u.HighPart & 0xF0000000) == 0);
|
|
assert(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
|
|
/* Lock the dispatcher, and raise IRQL */
|
|
KiTimerLock();
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
OldIrql2 = KfRaiseIrql(HIGH_LEVEL);
|
|
|
|
/* Query the system time now */
|
|
KeQuerySystemTime(OldTime);
|
|
|
|
/* Surely, we won't set the system time here, but we CAN remember a delta to the host system time */
|
|
HostTime.QuadPart = OldTime->QuadPart - HostSystemTimeDelta.load();
|
|
HostSystemTimeDelta = NewTime->QuadPart - HostTime.QuadPart;
|
|
|
|
/* Calculate the difference between the new and the old time */
|
|
DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart;
|
|
|
|
/* Lower IRQL back */
|
|
KfLowerIrql(OldIrql2);
|
|
|
|
/* Setup a temporary list of absolute timers */
|
|
InitializeListHead(&TempList);
|
|
|
|
/* Loop current timers */
|
|
for (i = 0; i < TIMER_TABLE_SIZE; i++)
|
|
{
|
|
/* Loop the entries in this table and lock the timers */
|
|
ListHead = &KiTimerTableListHead[i].Entry;
|
|
NextEntry = ListHead->Flink;
|
|
while (NextEntry != ListHead)
|
|
{
|
|
/* Get the timer */
|
|
Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
|
|
NextEntry = NextEntry->Flink;
|
|
|
|
/* Is it absolute? */
|
|
if (Timer->Header.Absolute)
|
|
{
|
|
/* Remove it from the timer list */
|
|
KiRemoveEntryTimer(Timer, i);
|
|
|
|
/* Insert it into our temporary list */
|
|
InsertTailList(&TempList, &Timer->TimerListEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Setup a temporary list of expired timers */
|
|
InitializeListHead(&TempList2);
|
|
|
|
/* Loop absolute timers */
|
|
while (TempList.Flink != &TempList)
|
|
{
|
|
/* Get the timer */
|
|
Timer = CONTAINING_RECORD(TempList.Flink, KTIMER, TimerListEntry);
|
|
RemoveEntryList(&Timer->TimerListEntry);
|
|
|
|
/* Update the due time and handle */
|
|
Timer->DueTime.QuadPart -= DeltaTime.QuadPart;
|
|
Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart);
|
|
|
|
/* Lock the timer and re-insert it */
|
|
if (KiInsertTimerTable(Timer, Hand))
|
|
{
|
|
/* Remove it from the timer list */
|
|
KiRemoveEntryTimer(Timer, Hand);
|
|
|
|
/* Insert it into our temporary list */
|
|
InsertTailList(&TempList2, &Timer->TimerListEntry);
|
|
}
|
|
}
|
|
|
|
/* Process expired timers. This releases the dispatcher and timer locks */
|
|
KiTimerListExpire(&TempList2, OldIrql);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * KeInitializeTimer()
|
|
// ******************************************************************
|
|
xbox::void_xt NTAPI xbox::KeInitializeTimer
|
|
(
|
|
IN PKTIMER Timer
|
|
)
|
|
{
|
|
LOG_FORWARD("KeInitializeTimerEx");
|
|
|
|
KeInitializeTimerEx(Timer, NotificationTimer);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * KeEmptyQueueApc()
|
|
// ******************************************************************
|
|
xbox::void_xt xbox::KeEmptyQueueApc()
|
|
{
|
|
PKTHREAD kThread = KeGetCurrentThread();
|
|
KeEnterCriticalRegion();
|
|
kThread->ApcState.ApcQueueable = FALSE;
|
|
KeLeaveCriticalRegion();
|
|
|
|
KiApcListMtx.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);
|
|
}
|
|
}
|
|
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)
|
|
|
|
// Forward KeRaiseIrql() to KfRaiseIrql()
|
|
#define KeRaiseIrql(NewIrql, OldIrql) \
|
|
*(OldIrql) = KfRaiseIrql(NewIrql)
|
|
|
|
void ExecuteDpcQueue()
|
|
{
|
|
xbox::PKDPC pkdpc;
|
|
|
|
// While we're working with the DpcQueue, we need to be thread-safe :
|
|
EnterCriticalSection(&(g_DpcData.Lock));
|
|
|
|
// if (g_DpcData._fShutdown)
|
|
// break; // while
|
|
|
|
// Assert(g_DpcData._dwThreadId == GetCurrentThreadId());
|
|
// Assert(g_DpcData._dwDpcThreadId == 0);
|
|
// g_DpcData._dwDpcThreadId = g_DpcData._dwThreadId;
|
|
// Assert(g_DpcData._dwDpcThreadId != 0);
|
|
|
|
// Are there entries in the DpqQueue?
|
|
while (!IsListEmpty(&(g_DpcData.DpcQueue)))
|
|
{
|
|
// Extract the head entry and retrieve the containing KDPC pointer for it:
|
|
pkdpc = CONTAINING_RECORD(RemoveHeadList(&(g_DpcData.DpcQueue)), xbox::KDPC, DpcListEntry);
|
|
// Mark it as no longer linked into the DpcQueue
|
|
pkdpc->Inserted = FALSE;
|
|
// Set DpcRoutineActive to support KeIsExecutingDpc:
|
|
KeGetCurrentPrcb()->DpcRoutineActive = TRUE; // Experimental
|
|
EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC at 0x%.8X", pkdpc->DeferredRoutine);
|
|
|
|
// Call the Deferred Procedure :
|
|
pkdpc->DeferredRoutine(
|
|
pkdpc,
|
|
pkdpc->DeferredContext,
|
|
pkdpc->SystemArgument1,
|
|
pkdpc->SystemArgument2);
|
|
|
|
KeGetCurrentPrcb()->DpcRoutineActive = FALSE; // Experimental
|
|
}
|
|
|
|
// Assert(g_DpcData._dwThreadId == GetCurrentThreadId());
|
|
// Assert(g_DpcData._dwDpcThreadId == g_DpcData._dwThreadId);
|
|
// g_DpcData._dwDpcThreadId = 0;
|
|
LeaveCriticalSection(&(g_DpcData.Lock));
|
|
}
|
|
|
|
void InitDpcThread()
|
|
{
|
|
DWORD dwThreadId = 0;
|
|
|
|
InitializeCriticalSection(&(g_DpcData.Lock));
|
|
InitializeListHead(&(g_DpcData.DpcQueue));
|
|
EmuLogEx(CXBXR_MODULE::INIT, LOG_LEVEL::DEBUG, "Creating DPC event\n");
|
|
g_DpcData.DpcEvent = CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE, /*bInitialState=*/FALSE, /*lpName=*/nullptr);
|
|
}
|
|
|
|
static constexpr uint32_t XBOX_TSC_FREQUENCY = 733333333; // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock)
|
|
static constexpr uint32_t XBOX_ACPI_FREQUENCY = 3375000; // Xbox ACPI frequency (3.375 mhz)
|
|
|
|
ULONGLONG CxbxGetPerformanceCounter(bool acpi)
|
|
{
|
|
const int64_t period = acpi ? XBOX_ACPI_FREQUENCY : XBOX_TSC_FREQUENCY;
|
|
return Timer_GetScaledPerformanceCounter(period);
|
|
}
|
|
|
|
void CxbxInitPerformanceCounters()
|
|
{
|
|
// Let's initialize the Dpc handling thread too,
|
|
// here for now (should be called by our caller)
|
|
InitDpcThread();
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x005C - KeAlertResumeThread()
|
|
// ******************************************************************
|
|
// Source:Dxbx
|
|
XBSYSAPI EXPORTNUM(92) xbox::ntstatus_xt NTAPI xbox::KeAlertResumeThread
|
|
(
|
|
IN HANDLE ThreadHandle,
|
|
IN OUT PULONG PreviousSuspendCount
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(ThreadHandle)
|
|
LOG_FUNC_ARG_OUT(PreviousSuspendCount)
|
|
LOG_FUNC_END;
|
|
|
|
// TODO : Result = NtDll::NtAlertResumeThread(ThreadHandle, PreviousSuspendCount);
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(S_OK);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x005D - KeAlertThread()
|
|
// ******************************************************************
|
|
// Source:Dxbx
|
|
XBSYSAPI EXPORTNUM(93) xbox::ntstatus_xt NTAPI xbox::KeAlertThread
|
|
(
|
|
IN HANDLE ThreadHandle
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(ThreadHandle);
|
|
|
|
// TODO : Result = NtDll::NtAlertThread(ThreadHandle);
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(S_OK);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x005E - KeBoostPriorityThread()
|
|
// ******************************************************************
|
|
// Source:Dxbx
|
|
XBSYSAPI EXPORTNUM(94) xbox::ntstatus_xt NTAPI xbox::KeBoostPriorityThread
|
|
(
|
|
IN PKTHREAD Thread,
|
|
IN KPRIORITY Increment
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Thread);
|
|
LOG_FUNC_ARG(Increment);
|
|
LOG_FUNC_END;
|
|
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(S_OK);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x005F - KeBugCheck()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(95) xbox::void_xt NTAPI xbox::KeBugCheck
|
|
(
|
|
IN ulong_xt BugCheckMode
|
|
)
|
|
{
|
|
LOG_FORWARD("KeBugCheckEx");
|
|
|
|
KeBugCheckEx(BugCheckMode, 0, 0, 0, 0);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0060 - KeBugCheckEx()
|
|
// ******************************************************************
|
|
// Source:Dxbx
|
|
XBSYSAPI EXPORTNUM(96) xbox::ntstatus_xt NTAPI xbox::KeBugCheckEx
|
|
(
|
|
IN dword_xt BugCheckCode,
|
|
IN PVOID BugCheckParameter1,
|
|
IN PVOID BugCheckParameter2,
|
|
IN PVOID BugCheckParameter3,
|
|
IN PVOID BugCheckParameter4
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(BugCheckCode)
|
|
LOG_FUNC_ARG(BugCheckParameter1)
|
|
LOG_FUNC_ARG(BugCheckParameter2)
|
|
LOG_FUNC_ARG(BugCheckParameter3)
|
|
LOG_FUNC_ARG(BugCheckParameter4)
|
|
LOG_FUNC_END;
|
|
|
|
static bool KeBugCheckIgnored = false;
|
|
|
|
if (KeBugCheckIgnored) {
|
|
RETURN(S_OK);
|
|
}
|
|
|
|
char buffer[1024];
|
|
sprintf(buffer, "The running software triggered KeBugCheck with the following information\n"
|
|
"BugCheckCode: 0x%.8X\n"
|
|
"BugCheckParameter1: 0x%p\n"
|
|
"BugCheckParameter2: 0x%p\n"
|
|
"BugCheckParameter3: 0x%p\n"
|
|
"BugCheckParameter4: 0x%p\n"
|
|
"\nThis is the Xbox equivalent to a BSOD and would cause the console to automatically reboot\n"
|
|
"\nContinue Execution (Not Recommended)?\n",
|
|
BugCheckCode, BugCheckParameter1, BugCheckParameter2, BugCheckParameter3, BugCheckParameter4);
|
|
|
|
int result = MessageBoxA(g_hEmuWindow, buffer, "KeBugCheck", MB_YESNO | MB_ICONWARNING);
|
|
|
|
if (result == IDNO) {
|
|
CxbxrAbort(NULL);
|
|
}
|
|
|
|
KeBugCheckIgnored = true;
|
|
|
|
RETURN(S_OK);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0061 - KeCancelTimer()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(96) xbox::boolean_xt NTAPI xbox::KeCancelTimer
|
|
(
|
|
IN PKTIMER Timer
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(Timer);
|
|
|
|
KIRQL OldIrql;
|
|
BOOLEAN Inserted;
|
|
|
|
assert(Timer);
|
|
|
|
/* Lock the Database and Raise IRQL */
|
|
KiTimerLock();
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
|
|
/* Check if it's inserted, and remove it if it is */
|
|
Inserted = Timer->Header.Inserted;
|
|
if (Inserted) {
|
|
KxRemoveTreeTimer(Timer);
|
|
}
|
|
|
|
/* Release Dispatcher Lock */
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
KiTimerUnlock();
|
|
|
|
/* Return the old state */
|
|
RETURN(Inserted);
|
|
}
|
|
|
|
xbox::PKINTERRUPT EmuInterruptList[MAX_BUS_INTERRUPT_LEVEL + 1] = { 0 };
|
|
|
|
// ******************************************************************
|
|
// * 0x0062 - KeConnectInterrupt()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(98) xbox::boolean_xt NTAPI xbox::KeConnectInterrupt
|
|
(
|
|
IN PKINTERRUPT InterruptObject
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(InterruptObject);
|
|
|
|
BOOLEAN ret = FALSE;
|
|
KIRQL OldIrql;
|
|
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
|
|
// here we have to connect the interrupt object to the vector
|
|
if (!InterruptObject->Connected)
|
|
{
|
|
// One interrupt per IRQ - only set when not set yet :
|
|
if (EmuInterruptList[InterruptObject->BusInterruptLevel] == NULL)
|
|
{
|
|
InterruptObject->Connected = TRUE;
|
|
EmuInterruptList[InterruptObject->BusInterruptLevel] = InterruptObject;
|
|
HalEnableSystemInterrupt(InterruptObject->BusInterruptLevel, InterruptObject->Mode);
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
// else do nothing
|
|
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0063 - KeDelayExecutionThread()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
|
|
(
|
|
IN KPROCESSOR_MODE WaitMode,
|
|
IN boolean_xt Alertable,
|
|
IN PLARGE_INTEGER Interval
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(WaitMode)
|
|
LOG_FUNC_ARG(Alertable)
|
|
LOG_FUNC_ARG(Interval)
|
|
LOG_FUNC_END;
|
|
|
|
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
|
|
// We can't remove NtDll::NtDelayExecution until all APCs queued by Io are implemented by our kernel as well
|
|
// Test case: Metal Slug 3
|
|
LARGE_INTEGER NewTime;
|
|
PLARGE_INTEGER pNewTime;
|
|
if (Interval) {
|
|
LARGE_INTEGER ExpireTime, DueTime;
|
|
ExpireTime.QuadPart = DueTime.QuadPart = Interval->QuadPart;
|
|
pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
|
|
}
|
|
else {
|
|
NewTime.QuadPart = ~0ull;
|
|
pNewTime = &NewTime;
|
|
}
|
|
|
|
xbox::ntstatus_xt ret = WaitApc([Alertable]() -> std::optional<ntstatus_xt> {
|
|
NtDll::LARGE_INTEGER ExpireTime;
|
|
ExpireTime.QuadPart = 0;
|
|
NTSTATUS Status = NtDll::NtDelayExecution(Alertable, &ExpireTime);
|
|
// Any success codes that are not related to alerting should be masked out
|
|
if (Status >= 0 && Status != STATUS_ALERTED && Status != STATUS_USER_APC) {
|
|
return std::nullopt;
|
|
}
|
|
return std::make_optional<ntstatus_xt>(Status);
|
|
}, pNewTime, Alertable, WaitMode);
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0064 - KeDisconnectInterrupt()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(100) xbox::void_xt NTAPI xbox::KeDisconnectInterrupt
|
|
(
|
|
IN PKINTERRUPT InterruptObject
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(InterruptObject);
|
|
|
|
KIRQL OldIrql;
|
|
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
|
|
// Do the reverse of KeConnectInterrupt
|
|
if (InterruptObject->Connected) { // Text case : d3dbvt.xbe
|
|
// Mark InterruptObject as not connected anymore
|
|
HalDisableSystemInterrupt(InterruptObject->BusInterruptLevel);
|
|
EmuInterruptList[InterruptObject->BusInterruptLevel] = NULL;
|
|
InterruptObject->Connected = FALSE;
|
|
}
|
|
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0065 - KeEnterCriticalRegion()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(101) xbox::void_xt NTAPI xbox::KeEnterCriticalRegion
|
|
(
|
|
void_xt
|
|
)
|
|
{
|
|
LOG_FUNC();
|
|
PKTHREAD thread = KeGetCurrentThread();
|
|
thread->KernelApcDisable--;
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0067 - KeGetCurrentIrql()
|
|
// ******************************************************************
|
|
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 = EmuKeGetPcr();
|
|
KIRQL Irql = (KIRQL)Pcr->Irql;
|
|
|
|
RETURN_TYPE(KIRQL_TYPE, Irql);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0068 - KeGetCurrentThread()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(104) xbox::PKTHREAD NTAPI xbox::KeGetCurrentThread(void)
|
|
{
|
|
LOG_FUNC();
|
|
|
|
// Probably correct, but untested and currently faked in EmuGenerateFS
|
|
// (to make this correct, we need to improve our thread emulation)
|
|
KTHREAD *ret = KeGetCurrentPrcb()->CurrentThread;
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0069 - KeInitializeApc()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(105) xbox::void_xt NTAPI xbox::KeInitializeApc
|
|
(
|
|
IN PKAPC Apc,
|
|
IN PKTHREAD Thread,
|
|
IN PKKERNEL_ROUTINE KernelRoutine,
|
|
IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
|
|
IN PKNORMAL_ROUTINE NormalRoutine OPTIONAL,
|
|
IN KPROCESSOR_MODE ApcMode OPTIONAL,
|
|
IN PVOID NormalContext OPTIONAL
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Apc)
|
|
LOG_FUNC_ARG(Thread)
|
|
LOG_FUNC_ARG(KernelRoutine)
|
|
LOG_FUNC_ARG(RundownRoutine)
|
|
LOG_FUNC_ARG(NormalRoutine)
|
|
LOG_FUNC_ARG(ApcMode)
|
|
LOG_FUNC_ARG(NormalContext)
|
|
LOG_FUNC_END;
|
|
|
|
// inialize Apc field values
|
|
Apc->Type = ApcObject;
|
|
Apc->ApcMode = ApcMode;
|
|
Apc->Inserted = FALSE;
|
|
Apc->Thread = Thread;
|
|
Apc->KernelRoutine = KernelRoutine;
|
|
Apc->RundownRoutine = RundownRoutine;
|
|
Apc->NormalRoutine = NormalRoutine;
|
|
Apc->NormalContext = NormalContext;
|
|
if (NormalRoutine == NULL) {
|
|
Apc->ApcMode = KernelMode;
|
|
Apc->NormalContext = NULL;
|
|
}
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x006A - KeInitializeDeviceQueue()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(106) xbox::void_xt NTAPI xbox::KeInitializeDeviceQueue
|
|
(
|
|
OUT PKDEVICE_QUEUE DeviceQueue
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG_OUT(DeviceQueue);
|
|
|
|
DeviceQueue->Type = DeviceQueueObject;
|
|
DeviceQueue->Size = sizeof(KDEVICE_QUEUE);
|
|
DeviceQueue->Busy = FALSE;
|
|
|
|
InitializeListHead(&DeviceQueue->DeviceListHead);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x006B - KeInitializeDpc()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(107) xbox::void_xt NTAPI xbox::KeInitializeDpc
|
|
(
|
|
KDPC *Dpc,
|
|
PKDEFERRED_ROUTINE DeferredRoutine,
|
|
PVOID DeferredContext
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Dpc)
|
|
LOG_FUNC_ARG(DeferredRoutine)
|
|
LOG_FUNC_ARG(DeferredContext)
|
|
LOG_FUNC_END;
|
|
|
|
// inialize Dpc field values
|
|
Dpc->Type = DpcObject;
|
|
Dpc->Inserted = FALSE;
|
|
Dpc->DeferredRoutine = DeferredRoutine;
|
|
Dpc->DeferredContext = DeferredContext;
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x006C - KeInitializeEvent()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(108) xbox::void_xt NTAPI xbox::KeInitializeEvent
|
|
(
|
|
IN PRKEVENT Event,
|
|
IN EVENT_TYPE Type,
|
|
IN boolean_xt SignalState
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Event)
|
|
LOG_FUNC_ARG(Type)
|
|
LOG_FUNC_ARG(SignalState)
|
|
LOG_FUNC_END;
|
|
|
|
// HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own
|
|
// In this case, it is already initialized so no need todo anything
|
|
// Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection)
|
|
DWORD flags = 0;
|
|
if (GetHandleInformation((HANDLE)Event, &flags)) {
|
|
return;
|
|
}
|
|
|
|
// Setup the Xbox event struct
|
|
Event->Header.Type = Type;
|
|
Event->Header.Size = sizeof(KEVENT) / sizeof(LONG);
|
|
Event->Header.SignalState = SignalState;
|
|
InitializeListHead(&(Event->Header.WaitListHead));
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x006D - KeInitializeInterrupt()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(109) xbox::void_xt NTAPI xbox::KeInitializeInterrupt
|
|
(
|
|
OUT PKINTERRUPT Interrupt,
|
|
IN PKSERVICE_ROUTINE ServiceRoutine,
|
|
IN PVOID ServiceContext,
|
|
IN ulong_xt Vector,
|
|
IN KIRQL Irql,
|
|
IN KINTERRUPT_MODE InterruptMode,
|
|
IN BOOLEAN ShareVector
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG_OUT(Interrupt)
|
|
LOG_FUNC_ARG(ServiceRoutine)
|
|
LOG_FUNC_ARG(ServiceContext)
|
|
LOG_FUNC_ARG(Vector)
|
|
LOG_FUNC_ARG_TYPE(KIRQL_TYPE, Irql)
|
|
LOG_FUNC_ARG(InterruptMode)
|
|
LOG_FUNC_ARG(ShareVector)
|
|
LOG_FUNC_END;
|
|
|
|
Interrupt->ServiceRoutine = ServiceRoutine;
|
|
Interrupt->ServiceContext = ServiceContext;
|
|
Interrupt->BusInterruptLevel = VECTOR2IRQ(Vector);
|
|
Interrupt->Irql = Irql;
|
|
Interrupt->Connected = FALSE;
|
|
// Unused : Interrupt->ShareVector = ShareVector;
|
|
Interrupt->Mode = InterruptMode;
|
|
// Interrupt->ServiceCount = 0; // not neccesary?
|
|
|
|
// Interrupt->DispatchCode = []?; //TODO : Populate this interrupt dispatch
|
|
// code block, patch it up so it works with the address of this Interrupt
|
|
// struct and calls the right dispatch routine (depending on InterruptMode).
|
|
LOG_INCOMPLETE();
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x006E - KeInitializeMutant()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(110) xbox::void_xt NTAPI xbox::KeInitializeMutant
|
|
(
|
|
IN PRKMUTANT Mutant,
|
|
IN boolean_xt InitialOwner
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Mutant)
|
|
LOG_FUNC_ARG(InitialOwner)
|
|
LOG_FUNC_END;
|
|
|
|
// Initialize header :
|
|
Mutant->Header.Type = MutantObject;
|
|
Mutant->Header.Size = sizeof(KMUTANT) / sizeof(LONG);
|
|
InitializeListHead(&Mutant->Header.WaitListHead);
|
|
// Initialize specific fields :
|
|
Mutant->Abandoned = FALSE;
|
|
if (InitialOwner == TRUE) {
|
|
PKTHREAD pThread = KeGetCurrentThread();
|
|
Mutant->Header.SignalState = 0;
|
|
Mutant->OwnerThread = pThread;
|
|
|
|
KIRQL oldIRQL;
|
|
KiLockDispatcherDatabase(&oldIRQL);
|
|
|
|
PLIST_ENTRY pMutantList = pThread->MutantListHead.Blink;
|
|
InsertHeadList(pMutantList, &Mutant->MutantListEntry);
|
|
|
|
KiUnlockDispatcherDatabase(oldIRQL);
|
|
}
|
|
else {
|
|
Mutant->Header.SignalState = 1;
|
|
Mutant->OwnerThread = NULL;
|
|
}
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x006F - KeInitializeQueue()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(111) xbox::void_xt NTAPI xbox::KeInitializeQueue
|
|
(
|
|
IN PKQUEUE Queue,
|
|
IN ulong_xt Count OPTIONAL
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Queue)
|
|
LOG_FUNC_ARG(Count)
|
|
LOG_FUNC_END;
|
|
|
|
// Initialize header :
|
|
Queue->Header.Type = QueueObject;
|
|
Queue->Header.Size = sizeof(KQUEUE) / sizeof(LONG);
|
|
Queue->Header.SignalState = 0;
|
|
InitializeListHead(&Queue->Header.WaitListHead);
|
|
// Initialize specific fields :
|
|
InitializeListHead(&Queue->EntryListHead);
|
|
InitializeListHead(&Queue->ThreadListHead);
|
|
Queue->CurrentCount = 0;
|
|
Queue->MaximumCount = (Count > 1) ? Count : 1;
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0070 - KeInitializeSemaphore()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(112) xbox::void_xt NTAPI xbox::KeInitializeSemaphore
|
|
(
|
|
IN PRKSEMAPHORE Semaphore,
|
|
IN long_xt Count,
|
|
IN long_xt Limit
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Semaphore)
|
|
LOG_FUNC_ARG(Count)
|
|
LOG_FUNC_ARG(Limit)
|
|
LOG_FUNC_END;
|
|
|
|
// Initialize header :
|
|
Semaphore->Header.Type = SemaphoreObject;
|
|
Semaphore->Header.Size = sizeof(KSEMAPHORE) / sizeof(LONG);
|
|
Semaphore->Header.SignalState = Count;
|
|
InitializeListHead(&Semaphore->Header.WaitListHead);
|
|
// Initialize specific fields :
|
|
Semaphore->Limit = Limit;
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0071 - KeInitializeTimerEx()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(113) xbox::void_xt NTAPI xbox::KeInitializeTimerEx
|
|
(
|
|
IN PKTIMER Timer,
|
|
IN TIMER_TYPE Type
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Timer)
|
|
LOG_FUNC_ARG(Type)
|
|
LOG_FUNC_END;
|
|
|
|
assert(Timer);
|
|
|
|
// Initialize header :
|
|
Timer->Header.Type = Type + TimerNotificationObject;
|
|
Timer->Header.Inserted = FALSE;
|
|
Timer->Header.Size = sizeof(KTIMER) / sizeof(ULONG);
|
|
Timer->Header.SignalState = 0;
|
|
InitializeListHead(&(Timer->Header.WaitListHead));
|
|
// Initialize specific fields :
|
|
Timer->TimerListEntry.Blink = NULL;
|
|
Timer->TimerListEntry.Flink = NULL;
|
|
Timer->DueTime.QuadPart = 0;
|
|
Timer->Period = 0;
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(114) xbox::boolean_xt NTAPI xbox::KeInsertByKeyDeviceQueue
|
|
(
|
|
IN PKDEVICE_QUEUE DeviceQueue,
|
|
IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry,
|
|
IN ulong_xt SortKey
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(DeviceQueue)
|
|
LOG_FUNC_ARG(DeviceQueueEntry)
|
|
LOG_FUNC_ARG(SortKey)
|
|
LOG_FUNC_END;
|
|
|
|
BOOLEAN Res = FALSE;
|
|
|
|
// We should lock the device queue here
|
|
|
|
DeviceQueueEntry->SortKey = SortKey;
|
|
if (DeviceQueue->Busy) {
|
|
LIST_ENTRY *pListEntry = DeviceQueue->DeviceListHead.Flink;
|
|
while (pListEntry != &DeviceQueue->DeviceListHead) {
|
|
KDEVICE_QUEUE_ENTRY *pQueueEntry = CONTAINING_RECORD(pListEntry, KDEVICE_QUEUE_ENTRY, DeviceListEntry);
|
|
if (SortKey < pQueueEntry->SortKey) {
|
|
break;
|
|
}
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
pListEntry = pListEntry->Blink;
|
|
InsertHeadList(pListEntry, &DeviceQueueEntry->DeviceListEntry);
|
|
Res = TRUE;
|
|
}
|
|
else {
|
|
DeviceQueue->Busy = TRUE;
|
|
}
|
|
|
|
DeviceQueueEntry->Inserted = Res;
|
|
|
|
// We should unlock the device queue here
|
|
|
|
RETURN(Res);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0073 - KeInsertDeviceQueue()
|
|
// * This implementation is inspired by ReactOS source code
|
|
// * Ref: https://github.com/reactos/reactos/blob/master/ntoskrnl/ke/devqueue.c
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(115) xbox::boolean_xt NTAPI xbox::KeInsertDeviceQueue
|
|
(
|
|
IN PKDEVICE_QUEUE DeviceQueue,
|
|
IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(DeviceQueue)
|
|
LOG_FUNC_ARG(DeviceQueueEntry)
|
|
LOG_FUNC_END;
|
|
|
|
BOOLEAN Res = FALSE;
|
|
|
|
// We should lock the device queue here
|
|
|
|
if (DeviceQueue->Busy == TRUE) {
|
|
InsertTailList(&DeviceQueue->DeviceListHead, &DeviceQueueEntry->DeviceListEntry);
|
|
Res = TRUE;
|
|
}
|
|
else {
|
|
DeviceQueue->Busy = TRUE;
|
|
}
|
|
|
|
DeviceQueueEntry->Inserted = Res;
|
|
|
|
// We should unlock the device queue here
|
|
|
|
RETURN(Res);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(116) xbox::long_xt NTAPI xbox::KeInsertHeadQueue
|
|
(
|
|
IN PRKQUEUE Queue,
|
|
IN PLIST_ENTRY Entry
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Queue)
|
|
LOG_FUNC_ARG(Entry)
|
|
LOG_FUNC_END;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(0);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(117) xbox::long_xt NTAPI xbox::KeInsertQueue
|
|
(
|
|
IN PRKQUEUE Queue,
|
|
IN PLIST_ENTRY Entry
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Queue)
|
|
LOG_FUNC_ARG(Entry)
|
|
LOG_FUNC_END;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(0);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0076 - KeInsertQueueApc()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc
|
|
(
|
|
IN PRKAPC Apc,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2,
|
|
IN KPRIORITY Increment
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Apc)
|
|
LOG_FUNC_ARG(SystemArgument1)
|
|
LOG_FUNC_ARG(SystemArgument2)
|
|
LOG_FUNC_ARG(Increment)
|
|
LOG_FUNC_END;
|
|
|
|
KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
|
|
|
|
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 {
|
|
KiApcListMtx.lock();
|
|
InsertTailList(&kThread->ApcState.ApcListHead[Apc->ApcMode], &Apc->ApcListEntry);
|
|
Apc->Inserted = TRUE;
|
|
KiApcListMtx.unlock();
|
|
|
|
// We can only attempt to execute the queued apc right away if it is been inserted in the current thread, because otherwise the KTHREAD
|
|
// in the fs selector will not be correct
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0077 - KeInsertQueueDpc()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(119) xbox::boolean_xt NTAPI xbox::KeInsertQueueDpc
|
|
(
|
|
IN PKDPC Dpc,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Dpc)
|
|
LOG_FUNC_ARG(SystemArgument1)
|
|
LOG_FUNC_ARG(SystemArgument2)
|
|
LOG_FUNC_END;
|
|
|
|
// For thread safety, enter the Dpc lock:
|
|
EnterCriticalSection(&(g_DpcData.Lock));
|
|
// TODO : Instead, disable interrupts - use KeRaiseIrql(HIGH_LEVEL, &(KIRQL)OldIrql) ?
|
|
|
|
BOOLEAN NeedsInsertion = (Dpc->Inserted == FALSE);
|
|
|
|
if (NeedsInsertion) {
|
|
// Remember the arguments and link it into our DpcQueue :
|
|
Dpc->Inserted = TRUE;
|
|
Dpc->SystemArgument1 = SystemArgument1;
|
|
Dpc->SystemArgument2 = SystemArgument2;
|
|
InsertTailList(&(g_DpcData.DpcQueue), &(Dpc->DpcListEntry));
|
|
// TODO : Instead of DpcQueue, add the DPC to KeGetCurrentPrcb()->DpcListHead
|
|
// Signal the Dpc handling code there's work to do
|
|
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
|
// OpenXbox has this instead:
|
|
// if (!pKPRCB->DpcRoutineActive && !pKPRCB->DpcInterruptRequested) {
|
|
// pKPRCB->DpcInterruptRequested = TRUE;
|
|
}
|
|
|
|
// Thread-safety is no longer required anymore
|
|
LeaveCriticalSection(&(g_DpcData.Lock));
|
|
// TODO : Instead, enable interrupts - use KeLowerIrql(OldIrql) ?
|
|
|
|
RETURN(NeedsInsertion);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0079 - KeIsExecutingDpc()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(121) xbox::boolean_xt NTAPI xbox::KeIsExecutingDpc
|
|
()
|
|
{
|
|
LOG_FUNC();
|
|
|
|
BOOLEAN ret = (BOOLEAN)KeGetCurrentPrcb()->DpcRoutineActive;
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0078 - KeInterruptTime
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(120) xbox::KSYSTEM_TIME xbox::KeInterruptTime = { 0, 0, 0 };
|
|
|
|
// ******************************************************************
|
|
// * 0x007A - KeLeaveCriticalRegion()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(122) xbox::void_xt NTAPI xbox::KeLeaveCriticalRegion
|
|
(
|
|
void_xt
|
|
)
|
|
{
|
|
LOG_FUNC();
|
|
|
|
PKTHREAD thread = KeGetCurrentThread();
|
|
thread->KernelApcDisable++;
|
|
if(thread->KernelApcDisable == 0) {
|
|
LIST_ENTRY *apcListHead = &thread->ApcState.ApcListHead[KernelMode];
|
|
if(apcListHead->Flink != apcListHead) {
|
|
thread->ApcState.KernelApcPending = TRUE;
|
|
HalRequestSoftwareInterrupt(APC_LEVEL);
|
|
}
|
|
}
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(123) xbox::long_xt NTAPI xbox::KePulseEvent
|
|
(
|
|
IN PRKEVENT Event,
|
|
IN KPRIORITY Increment,
|
|
IN boolean_xt Wait
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Event)
|
|
LOG_FUNC_ARG(Increment)
|
|
LOG_FUNC_ARG(Wait)
|
|
LOG_FUNC_END;
|
|
|
|
KIRQL OldIrql;
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
|
|
// HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own
|
|
// In this case, we must call the NtDll function
|
|
// Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection)
|
|
DWORD flags = 0;
|
|
if (GetHandleInformation((HANDLE)Event, &flags)) {
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
return NtDll::NtPulseEvent((HANDLE)Event, nullptr);
|
|
}
|
|
|
|
LONG OldState = Event->Header.SignalState;
|
|
if ((OldState == 0) && (IsListEmpty(&Event->Header.WaitListHead) == FALSE)) {
|
|
Event->Header.SignalState = 1;
|
|
// TODO: KiWaitTest(Event, Increment);
|
|
// For now, we just sleep to give other threads time to wake
|
|
// KiWaitTest and related functions require correct thread scheduling to implement first
|
|
// This will have to wait until CPU emulation at v1.0
|
|
Sleep(1);
|
|
}
|
|
|
|
Event->Header.SignalState = 0;
|
|
|
|
if (Wait != FALSE) {
|
|
PRKTHREAD Thread = KeGetCurrentThread();
|
|
Thread->WaitIrql = OldIrql;
|
|
Thread->WaitNext = Wait;
|
|
} else {
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
}
|
|
|
|
RETURN(OldState);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(124) xbox::long_xt NTAPI xbox::KeQueryBasePriorityThread
|
|
(
|
|
IN PKTHREAD Thread
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(Thread);
|
|
|
|
KIRQL OldIrql;
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
|
|
// It cannot fail because all thread handles are created by ob
|
|
const auto& nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
|
|
long_xt ret = GetThreadPriority(*nativeHandle);
|
|
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x007D - KeQueryInterruptTime()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(125) xbox::ulonglong_xt NTAPI xbox::KeQueryInterruptTime(void)
|
|
{
|
|
// TODO : Some software might call KeQueryInterruptTime often and fill the log quickly,
|
|
// in which case we should not LOG_FUNC nor RETURN (use normal return instead).
|
|
LOG_FUNC();
|
|
|
|
ULONGLONG ret;
|
|
|
|
LARGE_INTEGER InterruptTime;
|
|
|
|
while (true)
|
|
{
|
|
// Don't use NtDll::QueryInterruptTime, it's too new (Windows 10).
|
|
// Instead, read KeInterruptTime from our kernel thunk table.
|
|
InterruptTime.u.HighPart = KeInterruptTime.High1Time;
|
|
InterruptTime.u.LowPart = KeInterruptTime.LowPart;
|
|
|
|
// Read InterruptTime atomically with a spinloop to avoid errors
|
|
// when High1Time and High2Time differ (during unprocessed overflow in LowPart).
|
|
if (InterruptTime.u.HighPart == KeInterruptTime.High2Time)
|
|
break;
|
|
}
|
|
|
|
ret = LARGE_INTEGER2ULONGLONG(InterruptTime);
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x007E - KeQueryPerformanceCounter()
|
|
// NOTE: The KeQueryPerformance* functions run at the ACPI clock
|
|
// The XAPI QueryPerformance* functions run at the TSC clock
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(126) xbox::ulonglong_xt NTAPI xbox::KeQueryPerformanceCounter(void)
|
|
{
|
|
LOG_FUNC();
|
|
ULONGLONG ret;
|
|
ret = CxbxGetPerformanceCounter(/*acpi=*/true);
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x007F - KeQueryPerformanceFrequency()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(127) xbox::ulonglong_xt NTAPI xbox::KeQueryPerformanceFrequency(void)
|
|
{
|
|
LOG_FUNC();
|
|
ULONGLONG ret = XBOX_ACPI_FREQUENCY;
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0080 - KeQuerySystemTime()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(128) xbox::void_xt NTAPI xbox::KeQuerySystemTime
|
|
(
|
|
PLARGE_INTEGER CurrentTime
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(CurrentTime);
|
|
|
|
LARGE_INTEGER SystemTime;
|
|
|
|
while (true)
|
|
{
|
|
SystemTime.u.HighPart = KeSystemTime.High1Time;
|
|
SystemTime.u.LowPart = KeSystemTime.LowPart;
|
|
|
|
// Read SystemTime atomically with a spinloop to avoid errors
|
|
// when High1Time and High2Time differ (during unprocessed overflow in LowPart).
|
|
if (SystemTime.u.HighPart == KeSystemTime.High2Time)
|
|
break;
|
|
}
|
|
|
|
*CurrentTime = SystemTime;
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0081 - KeRaiseIrqlToDpcLevel()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(129) xbox::uchar_xt NTAPI xbox::KeRaiseIrqlToDpcLevel()
|
|
{
|
|
LOG_FORWARD(KfRaiseIrql);
|
|
|
|
return KfRaiseIrql(DISPATCH_LEVEL);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0082 - KeRaiseIrqlToSynchLevel()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(130) xbox::uchar_xt NTAPI xbox::KeRaiseIrqlToSynchLevel()
|
|
{
|
|
LOG_FORWARD(KfRaiseIrql);
|
|
|
|
return KfRaiseIrql(SYNC_LEVEL);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(131) xbox::long_xt NTAPI xbox::KeReleaseMutant
|
|
(
|
|
IN PRKMUTANT Mutant,
|
|
IN KPRIORITY Increment,
|
|
IN boolean_xt Abandoned,
|
|
IN boolean_xt Wait
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Mutant)
|
|
LOG_FUNC_ARG(Increment)
|
|
LOG_FUNC_ARG(Abandoned)
|
|
LOG_FUNC_ARG(Wait)
|
|
LOG_FUNC_END;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(0);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(132) xbox::long_xt NTAPI xbox::KeReleaseSemaphore
|
|
(
|
|
IN PRKSEMAPHORE Semaphore,
|
|
IN KPRIORITY Increment,
|
|
IN long_xt Adjustment,
|
|
IN boolean_xt Wait
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Semaphore)
|
|
LOG_FUNC_ARG(Increment)
|
|
LOG_FUNC_ARG(Adjustment)
|
|
LOG_FUNC_ARG(Wait)
|
|
LOG_FUNC_END;
|
|
|
|
UCHAR orig_irql = KeRaiseIrqlToDpcLevel();
|
|
LONG initial_state = Semaphore->Header.SignalState;
|
|
LONG adjusted_signalstate = Semaphore->Header.SignalState + Adjustment;
|
|
|
|
BOOL limit_reached = adjusted_signalstate > Semaphore->Limit;
|
|
BOOL signalstate_overflow = adjusted_signalstate < initial_state;
|
|
if (limit_reached || signalstate_overflow) {
|
|
KiUnlockDispatcherDatabase(orig_irql);
|
|
ExRaiseStatus(X_STATUS_SEMAPHORE_LIMIT_EXCEEDED);
|
|
}
|
|
Semaphore->Header.SignalState = adjusted_signalstate;
|
|
|
|
//TODO: Implement KiWaitTest
|
|
#if 0
|
|
if ((initial_state == 0) && (IsListEmpty(&Semaphore->Header.WaitListHead) == FALSE)) {
|
|
KiWaitTest(&Semaphore->Header, Increment);
|
|
}
|
|
#endif
|
|
|
|
if (Wait) {
|
|
PKTHREAD current_thread = KeGetCurrentThread();
|
|
current_thread->WaitNext = TRUE;
|
|
current_thread->WaitIrql = orig_irql;
|
|
}
|
|
else {
|
|
KiUnlockDispatcherDatabase(orig_irql);
|
|
}
|
|
|
|
RETURN(initial_state);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(133) xbox::PKDEVICE_QUEUE_ENTRY NTAPI xbox::KeRemoveByKeyDeviceQueue
|
|
(
|
|
IN PKDEVICE_QUEUE DeviceQueue,
|
|
IN ulong_xt SortKey
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(DeviceQueue)
|
|
LOG_FUNC_ARG(SortKey)
|
|
LOG_FUNC_END;
|
|
|
|
KDEVICE_QUEUE_ENTRY *pEntry;
|
|
|
|
if (IsListEmpty(&DeviceQueue->DeviceListHead)) {
|
|
DeviceQueue->Busy = FALSE;
|
|
pEntry = NULL;
|
|
}
|
|
else {
|
|
LIST_ENTRY *pListEntry = DeviceQueue->DeviceListHead.Flink;
|
|
while (pListEntry != &DeviceQueue->DeviceListHead) {
|
|
pEntry = CONTAINING_RECORD(pListEntry, KDEVICE_QUEUE_ENTRY, DeviceListEntry);
|
|
if (SortKey <= pEntry->SortKey) {
|
|
break;
|
|
}
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
if (pListEntry != &DeviceQueue->DeviceListHead) {
|
|
RemoveEntryList(&pEntry->DeviceListEntry);
|
|
|
|
}
|
|
else {
|
|
pListEntry = RemoveHeadList(&DeviceQueue->DeviceListHead);
|
|
pEntry = CONTAINING_RECORD(pListEntry, KDEVICE_QUEUE_ENTRY, DeviceListEntry);
|
|
}
|
|
|
|
pEntry->Inserted = FALSE;
|
|
}
|
|
|
|
RETURN(pEntry);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(134) xbox::PKDEVICE_QUEUE_ENTRY NTAPI xbox::KeRemoveDeviceQueue
|
|
(
|
|
IN PKDEVICE_QUEUE DeviceQueue
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(DeviceQueue);
|
|
|
|
KDEVICE_QUEUE_ENTRY *pEntry;
|
|
|
|
// We should lock the device queue here
|
|
|
|
if (IsListEmpty(&DeviceQueue->DeviceListHead)) {
|
|
DeviceQueue->Busy = FALSE;
|
|
pEntry = NULL;
|
|
}
|
|
else {
|
|
LIST_ENTRY *pListEntry = RemoveHeadList(&DeviceQueue->DeviceListHead);
|
|
pEntry = CONTAINING_RECORD(pListEntry, KDEVICE_QUEUE_ENTRY, DeviceListEntry);
|
|
pEntry->Inserted = FALSE;
|
|
}
|
|
|
|
// We should unlock the device queue here
|
|
|
|
RETURN(pEntry);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(135) xbox::boolean_xt NTAPI xbox::KeRemoveEntryDeviceQueue
|
|
(
|
|
IN PKDEVICE_QUEUE DeviceQueue,
|
|
IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(DeviceQueue)
|
|
LOG_FUNC_ARG(DeviceQueueEntry)
|
|
LOG_FUNC_END;
|
|
|
|
|
|
KIRQL oldIRQL;
|
|
KiLockDispatcherDatabase(&oldIRQL);
|
|
// We should lock the device queue here
|
|
|
|
BOOLEAN currentlyInserted = DeviceQueueEntry->Inserted;
|
|
if (currentlyInserted) {
|
|
DeviceQueueEntry->Inserted = FALSE;
|
|
RemoveEntryList(&DeviceQueueEntry->DeviceListEntry);
|
|
}
|
|
|
|
KiUnlockDispatcherDatabase(oldIRQL);
|
|
// We should unlock the device queue here
|
|
|
|
RETURN(currentlyInserted);
|
|
}
|
|
|
|
|
|
XBSYSAPI EXPORTNUM(136) xbox::PLIST_ENTRY NTAPI xbox::KeRemoveQueue
|
|
(
|
|
IN PRKQUEUE Queue,
|
|
IN KPROCESSOR_MODE WaitMode,
|
|
IN PLARGE_INTEGER Timeout
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Queue)
|
|
LOG_FUNC_ARG(WaitMode)
|
|
LOG_FUNC_ARG(Timeout)
|
|
LOG_FUNC_END;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(NULL);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0089 - KeRemoveQueueDpc()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(137) xbox::boolean_xt NTAPI xbox::KeRemoveQueueDpc
|
|
(
|
|
IN PKDPC Dpc
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(Dpc);
|
|
|
|
// TODO : Instead of using a lock, emulate the Clear Interrupt Flag (cli) instruction
|
|
// See https://msdn.microsoft.com/is-is/library/y14401ab(v=vs.80).aspx
|
|
EnterCriticalSection(&(g_DpcData.Lock));
|
|
|
|
BOOLEAN Inserted = Dpc->Inserted;
|
|
|
|
if (Inserted)
|
|
{
|
|
RemoveEntryList(&(Dpc->DpcListEntry));
|
|
Dpc->Inserted = FALSE;
|
|
}
|
|
|
|
// TODO : Instead of using a lock, emulate the Set Interrupt Flag (sti) instruction
|
|
// See https://msdn.microsoft.com/en-us/library/ad820yz3(v=vs.80).aspx
|
|
LeaveCriticalSection(&(g_DpcData.Lock));
|
|
|
|
RETURN(Inserted);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x008A - KeResetEvent()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(138) xbox::long_xt NTAPI xbox::KeResetEvent
|
|
(
|
|
IN PRKEVENT Event
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(Event);
|
|
|
|
KIRQL OldIrql;
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
|
|
// HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own
|
|
// In this case, we must call the NtDll function
|
|
// Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection)
|
|
DWORD flags = 0;
|
|
if (GetHandleInformation((HANDLE)Event, &flags)) {
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
return NtDll::NtResetEvent((HANDLE)Event, nullptr);
|
|
}
|
|
|
|
LONG OldState = Event->Header.SignalState;
|
|
Event->Header.SignalState = 0;
|
|
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
return OldState;
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x008B - KeRestoreFloatingPointState()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(139) xbox::ntstatus_xt NTAPI xbox::KeRestoreFloatingPointState
|
|
(
|
|
IN PKFLOATING_SAVE PublicFloatSave
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(PublicFloatSave);
|
|
|
|
NTSTATUS ret = X_STATUS_SUCCESS;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x008C - KeResumeThread()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(140) xbox::ulong_xt NTAPI xbox::KeResumeThread
|
|
(
|
|
IN PKTHREAD Thread
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(Thread);
|
|
|
|
NTSTATUS ret = X_STATUS_SUCCESS;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(141) xbox::PLIST_ENTRY NTAPI xbox::KeRundownQueue
|
|
(
|
|
IN PRKQUEUE Queue
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(Queue);
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(NULL);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x008E - KeSaveFloatingPointState()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(142) xbox::ntstatus_xt NTAPI xbox::KeSaveFloatingPointState
|
|
(
|
|
OUT PKFLOATING_SAVE PublicFloatSave
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG_OUT(PublicFloatSave);
|
|
|
|
NTSTATUS ret = X_STATUS_SUCCESS;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x008F - KeSetBasePriorityThread()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(143) xbox::long_xt NTAPI xbox::KeSetBasePriorityThread
|
|
(
|
|
IN PKTHREAD Thread,
|
|
IN long_xt Priority
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG_OUT(Thread)
|
|
LOG_FUNC_ARG_OUT(Priority)
|
|
LOG_FUNC_END;
|
|
|
|
KIRQL oldIRQL;
|
|
KiLockDispatcherDatabase(&oldIRQL);
|
|
|
|
// It cannot fail because all thread handles are created by ob
|
|
const auto &nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
|
|
LONG ret = GetThreadPriority(*nativeHandle);
|
|
|
|
// This would work normally, but it will slow down the emulation,
|
|
// don't do that if the priority is higher then normal (so our own)!
|
|
if (Priority <= THREAD_PRIORITY_NORMAL) {
|
|
BOOL result = SetThreadPriority(*nativeHandle, Priority);
|
|
if (!result) {
|
|
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority failed: %s", WinError2Str().c_str());
|
|
}
|
|
}
|
|
|
|
KiUnlockDispatcherDatabase(oldIRQL);
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(144) xbox::boolean_xt NTAPI xbox::KeSetDisableBoostThread
|
|
(
|
|
IN PKTHREAD Thread,
|
|
IN boolean_xt Disable
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Thread)
|
|
LOG_FUNC_ARG(Disable)
|
|
LOG_FUNC_END;
|
|
|
|
KIRQL oldIRQL;
|
|
KiLockDispatcherDatabase(&oldIRQL);
|
|
|
|
// It cannot fail because all thread handles are created by ob
|
|
const auto &nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
|
|
|
|
boolean_xt prevDisableBoost = Thread->DisableBoost;
|
|
Thread->DisableBoost = (CHAR)Disable;
|
|
|
|
BOOL bRet = SetThreadPriorityBoost(*nativeHandle, Disable);
|
|
if (!bRet) {
|
|
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriorityBoost failed: %s", WinError2Str().c_str());
|
|
}
|
|
|
|
KiUnlockDispatcherDatabase(oldIRQL);
|
|
|
|
RETURN(prevDisableBoost);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0091 - KeSetEvent()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(145) xbox::long_xt NTAPI xbox::KeSetEvent
|
|
(
|
|
IN PRKEVENT Event,
|
|
IN KPRIORITY Increment,
|
|
IN boolean_xt Wait
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Event)
|
|
LOG_FUNC_ARG(Increment)
|
|
LOG_FUNC_ARG(Wait)
|
|
LOG_FUNC_END;
|
|
|
|
KIRQL OldIrql;
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
|
|
// HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own
|
|
// In this case, we must call the NtDll function
|
|
// Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection)
|
|
DWORD flags = 0;
|
|
if (GetHandleInformation((HANDLE)Event, &flags)) {
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
return NtDll::NtSetEvent((HANDLE)Event, nullptr);
|
|
}
|
|
|
|
LONG OldState = Event->Header.SignalState;
|
|
if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
|
|
Event->Header.SignalState = 1;
|
|
} else {
|
|
PKWAIT_BLOCK WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink, KWAIT_BLOCK, WaitListEntry);
|
|
if ((Event->Header.Type == NotificationEvent) ||
|
|
(WaitBlock->WaitType != WaitAny)) {
|
|
if (OldState == 0) {
|
|
Event->Header.SignalState = 1;
|
|
// TODO: KiWaitTest(Event, Increment);
|
|
// For now, we just sleep to give other threads time to wake
|
|
// See KePulseEvent
|
|
Sleep(1);
|
|
}
|
|
} else {
|
|
// TODO: KiUnwaitThread(WaitBlock->Thread, (NTSTATUS)WaitBlock->WaitKey, Increment);
|
|
// For now, we just sleep to give other threads time to wake
|
|
// See KePulseEvent
|
|
Sleep(1);
|
|
}
|
|
}
|
|
|
|
if (Wait != FALSE) {
|
|
PRKTHREAD Thread = KeGetCurrentThread();
|
|
Thread->WaitNext = Wait;
|
|
Thread->WaitIrql = OldIrql;
|
|
} else {
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
}
|
|
|
|
RETURN(OldState);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(146) xbox::void_xt NTAPI xbox::KeSetEventBoostPriority
|
|
(
|
|
IN PRKEVENT Event,
|
|
IN PRKTHREAD *Thread
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Event)
|
|
LOG_FUNC_ARG(Thread)
|
|
LOG_FUNC_END;
|
|
KIRQL OldIrql;
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
|
|
// HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own
|
|
// In this case, we must do nothing. Anything else *will* cause crashes
|
|
// Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection)
|
|
DWORD flags = 0;
|
|
if (GetHandleInformation((HANDLE)Event, &flags)) {
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
return;
|
|
}
|
|
|
|
if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
|
|
Event->Header.SignalState = 1;
|
|
} else {
|
|
PRKTHREAD WaitThread = CONTAINING_RECORD(Event->Header.WaitListHead.Flink, KWAIT_BLOCK, WaitListEntry)->Thread;
|
|
|
|
if (Thread != nullptr) {
|
|
*Thread = WaitThread;
|
|
}
|
|
|
|
WaitThread->Quantum = WaitThread->ApcState.Process->ThreadQuantum;
|
|
// TODO: KiUnwaitThread(WaitThread, X_STATUS_SUCCESS, 1);
|
|
// For now, we just sleep to give other threads time to wake
|
|
// See KePulseEvent
|
|
Sleep(1);
|
|
}
|
|
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
}
|
|
|
|
XBSYSAPI EXPORTNUM(147) xbox::KPRIORITY NTAPI xbox::KeSetPriorityProcess
|
|
(
|
|
IN PKPROCESS Process,
|
|
IN KPRIORITY BasePriority
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Process)
|
|
LOG_FUNC_ARG(BasePriority)
|
|
LOG_FUNC_END;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(BasePriority);
|
|
}
|
|
|
|
|
|
// ******************************************************************
|
|
// * 0x0094 - KeSetPriorityThread()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(148) xbox::boolean_xt NTAPI xbox::KeSetPriorityThread
|
|
(
|
|
IN PKTHREAD Thread,
|
|
IN long_xt Priority
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG_OUT(Thread)
|
|
LOG_FUNC_ARG_OUT(Priority)
|
|
LOG_FUNC_END;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(1);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0095 - KeSetTimer()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(149) xbox::boolean_xt NTAPI xbox::KeSetTimer
|
|
(
|
|
IN PKTIMER Timer,
|
|
IN LARGE_INTEGER DueTime,
|
|
IN PKDPC Dpc OPTIONAL
|
|
)
|
|
{
|
|
LOG_FORWARD("KeSetTimerEx");
|
|
|
|
// Call KeSetTimerEx with a period of zero
|
|
return KeSetTimerEx(Timer, DueTime, 0, Dpc);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0096 - KeSetTimerEx()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(150) xbox::boolean_xt NTAPI xbox::KeSetTimerEx
|
|
(
|
|
IN PKTIMER Timer,
|
|
IN LARGE_INTEGER DueTime,
|
|
IN long_xt Period OPTIONAL,
|
|
IN PKDPC Dpc OPTIONAL
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Timer)
|
|
LOG_FUNC_ARG(DueTime)
|
|
LOG_FUNC_ARG(Period)
|
|
LOG_FUNC_ARG(Dpc)
|
|
LOG_FUNC_END;
|
|
|
|
BOOLEAN Inserted;
|
|
BOOLEAN RequestInterrupt = FALSE;
|
|
KIRQL OldIrql;
|
|
ULONG Hand;
|
|
|
|
assert(Timer);
|
|
assert(Timer->Header.Type == TimerNotificationObject || Timer->Header.Type == TimerSynchronizationObject);
|
|
|
|
KiTimerLock();
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
|
|
// Same as KeCancelTimer(Timer) :
|
|
Inserted = Timer->Header.Inserted;
|
|
if (Inserted != FALSE) {
|
|
// Do some unlinking if already inserted in the linked list
|
|
KxRemoveTreeTimer(Timer);
|
|
}
|
|
|
|
/* Set Default Timer Data */
|
|
Timer->Dpc = Dpc;
|
|
Timer->Period = Period;
|
|
if (!KiComputeDueTime(Timer, DueTime, (PULONG)&Hand))
|
|
{
|
|
/* Signal the timer */
|
|
RequestInterrupt = KiSignalTimer(Timer);
|
|
|
|
/* Check if we need to do an interrupt */
|
|
if (RequestInterrupt) {
|
|
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Insert the timer */
|
|
Timer->Header.SignalState = FALSE;
|
|
KxInsertTimer(Timer, Hand);
|
|
}
|
|
|
|
/* Exit the dispatcher */
|
|
KiTimerUnlock();
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
|
|
RETURN(Inserted);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0097 - KeStallExecutionProcessor()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(151) xbox::void_xt NTAPI xbox::KeStallExecutionProcessor
|
|
(
|
|
IN ulong_xt MicroSeconds
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(MicroSeconds);
|
|
|
|
// WinAPI Sleep usually sleeps for a minimum of 15ms, we want us.
|
|
// Thanks to C++11, we can do this in a nice way without resorting to
|
|
// QueryPerformanceCounter
|
|
std::this_thread::sleep_for(std::chrono::microseconds(MicroSeconds));
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0098 - KeSuspendThread()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(152) xbox::ulong_xt NTAPI xbox::KeSuspendThread
|
|
(
|
|
IN PKTHREAD Thread
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(Thread);
|
|
|
|
NTSTATUS ret = X_STATUS_SUCCESS;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x0099 - KeSynchronizeExecution()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(153) xbox::boolean_xt NTAPI xbox::KeSynchronizeExecution
|
|
(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
|
|
IN PVOID SynchronizeContext
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Interrupt)
|
|
LOG_FUNC_ARG(SynchronizeRoutine)
|
|
LOG_FUNC_ARG(SynchronizeContext)
|
|
LOG_FUNC_END;
|
|
|
|
BOOLEAN ret = TRUE;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x009A - KeSystemTime
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(154) xbox::KSYSTEM_TIME xbox::KeSystemTime = { 0, 0, 0 };
|
|
|
|
// ******************************************************************
|
|
// * 0x009B - KeTestAlertThread()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(155) xbox::boolean_xt NTAPI xbox::KeTestAlertThread
|
|
(
|
|
IN KPROCESSOR_MODE AlertMode
|
|
)
|
|
{
|
|
LOG_FUNC_ONE_ARG(AlertMode);
|
|
|
|
BOOLEAN ret = TRUE;
|
|
|
|
LOG_UNIMPLEMENTED();
|
|
|
|
RETURN(ret);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x009C - KeTickCount
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(156) xbox::dword_xt VOLATILE xbox::KeTickCount = 0;
|
|
|
|
// ******************************************************************
|
|
// * 0x009D - KeTimeIncrement
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(157) xbox::ulong_xt xbox::KeTimeIncrement = CLOCK_TIME_INCREMENT;
|
|
|
|
// ******************************************************************
|
|
// * 0x009E - KeWaitForMultipleObjects()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|
(
|
|
IN ulong_xt Count,
|
|
IN PVOID Object[],
|
|
IN WAIT_TYPE WaitType,
|
|
IN KWAIT_REASON WaitReason,
|
|
IN KPROCESSOR_MODE WaitMode,
|
|
IN boolean_xt Alertable,
|
|
IN PLARGE_INTEGER Timeout OPTIONAL,
|
|
IN PKWAIT_BLOCK WaitBlockArray OPTIONAL
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Count)
|
|
LOG_FUNC_ARG(Object)
|
|
LOG_FUNC_ARG(WaitType)
|
|
LOG_FUNC_ARG(WaitReason)
|
|
LOG_FUNC_ARG(WaitMode)
|
|
LOG_FUNC_ARG(Alertable)
|
|
LOG_FUNC_ARG(Timeout)
|
|
LOG_FUNC_ARG(WaitBlockArray)
|
|
LOG_FUNC_END;
|
|
|
|
// If the lock is not already held, lock it
|
|
PRKTHREAD Thread = KeGetCurrentThread();
|
|
if (Thread->WaitNext) {
|
|
Thread->WaitNext = FALSE;
|
|
}
|
|
else {
|
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|
}
|
|
|
|
// Wait Loop
|
|
// This loop ends
|
|
PLARGE_INTEGER OriginalTime = Timeout;
|
|
LARGE_INTEGER DueTime, NewTime;
|
|
KWAIT_BLOCK StackWaitBlock;
|
|
PKWAIT_BLOCK WaitBlock = &StackWaitBlock;
|
|
BOOLEAN WaitSatisfied;
|
|
NTSTATUS WaitStatus;
|
|
PKMUTANT ObjectMutant;
|
|
// Hack variable (remove this when the thread scheduler is here)
|
|
bool timeout_set = false;
|
|
do {
|
|
// Check if we need to let an APC run. This should immediately trigger APC interrupt via a call to UnlockDispatcherDatabase
|
|
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
|
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|
}
|
|
else {
|
|
WaitSatisfied = TRUE;
|
|
Thread->WaitStatus = X_STATUS_SUCCESS;
|
|
|
|
for (ULONG Index = 0; Index < Count; Index += 1) {
|
|
ObjectMutant = (PKMUTANT)Object[Index];
|
|
if (WaitType == WaitAny) {
|
|
if (ObjectMutant->Header.Type == MutantObject) {
|
|
// If the object is a mutant object and it has been acquired MINGLONG times, raise an exception
|
|
// If the mutant is signalled, we can satisfy the wait
|
|
if ((ObjectMutant->Header.SignalState > 0) || (Thread == ObjectMutant->OwnerThread)) {
|
|
if (ObjectMutant->Header.SignalState != MINLONG) {
|
|
KiWaitSatisfyMutant(ObjectMutant, Thread);
|
|
WaitStatus = (NTSTATUS)(Index | Thread->WaitStatus);
|
|
goto NoWait;
|
|
}
|
|
else {
|
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|
ExRaiseStatus(X_STATUS_MUTANT_LIMIT_EXCEEDED);
|
|
}
|
|
}
|
|
}
|
|
else if (ObjectMutant->Header.SignalState) {
|
|
// Otherwise, if the signal state is > 0, we can still just satisfy the wait
|
|
KiWaitSatisfyOther(ObjectMutant);
|
|
WaitStatus = Index;
|
|
goto NoWait;
|
|
}
|
|
} else {
|
|
if (ObjectMutant->Header.Type == MutantObject) {
|
|
if ((Thread == ObjectMutant->OwnerThread) && (ObjectMutant->Header.SignalState == MINLONG)) {
|
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|
ExRaiseStatus(X_STATUS_MUTANT_LIMIT_EXCEEDED);
|
|
} else if ((ObjectMutant->Header.SignalState <= 0) && (Thread != ObjectMutant->OwnerThread)) {
|
|
WaitSatisfied = FALSE;
|
|
}
|
|
} else if (ObjectMutant->Header.SignalState <= 0) {
|
|
WaitSatisfied = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// If we reached here, the wait could not be satisfied immediately, so we must setup a WaitBlock
|
|
WaitBlock = &WaitBlockArray[Index];
|
|
WaitBlock->Object = ObjectMutant;
|
|
WaitBlock->WaitKey = (cshort_xt)(Index);
|
|
WaitBlock->WaitType = WaitType;
|
|
WaitBlock->Thread = Thread;
|
|
WaitBlock->NextWaitBlock = &WaitBlockArray[Index + 1];
|
|
}
|
|
|
|
// Check if the wait can be satisfied immediately
|
|
if ((WaitType == WaitAll) && (WaitSatisfied)) {
|
|
WaitBlock->NextWaitBlock = &WaitBlockArray[0];
|
|
KiWaitSatisfyAll(WaitBlock);
|
|
WaitStatus = (NTSTATUS)Thread->WaitStatus;
|
|
goto NoWait;
|
|
}
|
|
|
|
TestForAlertPending(Alertable);
|
|
|
|
// Handle a Timeout if specified
|
|
if (Timeout != nullptr) {
|
|
// If the timeout is 0, do not wait
|
|
if (!(Timeout->u.LowPart | Timeout->u.HighPart)) {
|
|
WaitStatus = (NTSTATUS)(STATUS_TIMEOUT);
|
|
goto NoWait;
|
|
}
|
|
|
|
// Setup a timer for the thread but only once (for now)
|
|
if (!timeout_set) {
|
|
KiTimerLock();
|
|
PKTIMER Timer = &Thread->Timer;
|
|
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
|
|
WaitBlock->NextWaitBlock = WaitTimer;
|
|
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
|
|
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
|
|
WaitTimer->NextWaitBlock = WaitBlock;
|
|
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
|
|
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
|
|
KiTimerUnlock();
|
|
goto NoWait;
|
|
}
|
|
|
|
// Boring, ensure that we only set the thread timer once. Otherwise, this will cause to insert the same
|
|
// thread timer over and over in the timer list, which will prevent KiTimerExpiration from removing these
|
|
// duplicated timers and thus it will attempt to endlessly remove the same unremoved timers, causing a deadlock.
|
|
// This can be removed once KiSwapThread and the kernel/user APCs are implemented
|
|
timeout_set = true;
|
|
DueTime.QuadPart = Timer->DueTime.QuadPart;
|
|
KiTimerUnlock();
|
|
}
|
|
|
|
// KiTimerExpiration has removed the timer but the objects were not signaled, so we have a timeout
|
|
// (remove this when the thread scheduler is here)
|
|
if (Thread->Timer.Header.Inserted == FALSE) {
|
|
WaitStatus = (NTSTATUS)(STATUS_TIMEOUT);
|
|
goto NoWait;
|
|
}
|
|
}
|
|
else {
|
|
WaitBlock->NextWaitBlock = WaitBlock;
|
|
}
|
|
|
|
WaitBlock->NextWaitBlock = &WaitBlockArray[0];
|
|
WaitBlock = &WaitBlockArray[0];
|
|
do {
|
|
ObjectMutant = (PKMUTANT)WaitBlock->Object;
|
|
//InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
|
WaitBlock = WaitBlock->NextWaitBlock;
|
|
} while (WaitBlock != &WaitBlockArray[0]);
|
|
|
|
|
|
/*
|
|
TODO: We can't implement this and the return values until we have our own thread scheduler
|
|
For now, we'll have to implement waiting here instead of the scheduler.
|
|
This code can all be enabled once we have CPU emulation and our own scheduler in v1.0
|
|
*/
|
|
|
|
// Insert the WaitBlock
|
|
//InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
|
|
|
// If the current thread is processing a queue object, wake other treads using the same queue
|
|
PRKQUEUE Queue = (PRKQUEUE)Thread->Queue;
|
|
if (Queue != NULL) {
|
|
// TODO:KiActivateWaiterQueue(Queue);
|
|
}
|
|
|
|
// Insert the thread into the Wait List
|
|
Thread->Alertable = Alertable;
|
|
Thread->WaitMode = WaitMode;
|
|
Thread->WaitReason = (UCHAR)WaitReason;
|
|
Thread->WaitTime = KeTickCount;
|
|
//Thread->State = Waiting;
|
|
//KiInsertWaitList(WaitMode, Thread);
|
|
|
|
//WaitStatus = (NTSTATUS)KiSwapThread();
|
|
|
|
//if (WaitStatus == X_STATUS_USER_APC) {
|
|
// KiExecuteUserApc();
|
|
//}
|
|
|
|
// If the thread was not awakened for an APC, return the Wait Status
|
|
//if (WaitStatus != STATUS_KERNEL_APC) {
|
|
// return WaitStatus;
|
|
//}
|
|
|
|
// TODO: Remove this after we have our own scheduler and the above is implemented
|
|
Sleep(0);
|
|
|
|
// Reduce the timout if necessary
|
|
if (Timeout != nullptr) {
|
|
Timeout = KiComputeWaitInterval(OriginalTime, &DueTime, &NewTime);
|
|
}
|
|
}
|
|
|
|
// Raise IRQL to DISPATCH_LEVEL and lock the database (only if it's not already at this level)
|
|
if (KeGetCurrentIrql() != DISPATCH_LEVEL) {
|
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|
}
|
|
} while (TRUE);
|
|
|
|
// NOTE: we don't need to remove the wait blocks for the object and/or the timer because InsertTailList is disabled at
|
|
// the moment, which means they are never attached to the object wait list. TimerWaitBlock can also stay attached to the timer wait
|
|
// list since KiTimerExpiration disregards it for now.
|
|
|
|
// The waiting thead has been alerted, or an APC needs to be delivered
|
|
// So unlock the dispatcher database, lower the IRQ and return the status
|
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|
if (WaitStatus == X_STATUS_USER_APC) {
|
|
KiExecuteUserApc();
|
|
}
|
|
|
|
RETURN(WaitStatus);
|
|
|
|
NoWait:
|
|
// The wait was satisfied without actually waiting
|
|
// Unlock the database and return the status
|
|
//TODO: KiAdjustQuantumThread(Thread);
|
|
|
|
// Don't forget to remove the thread timer if the objects were signaled before the timer expired
|
|
// (remove this when the thread scheduler is here)
|
|
if (timeout_set && Thread->Timer.Header.Inserted == TRUE) {
|
|
KiTimerLock();
|
|
KxRemoveTreeTimer(&Thread->Timer);
|
|
KiTimerUnlock();
|
|
}
|
|
|
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|
|
|
if (WaitStatus == X_STATUS_USER_APC) {
|
|
KiExecuteUserApc();
|
|
}
|
|
|
|
RETURN(WaitStatus);
|
|
}
|
|
|
|
// ******************************************************************
|
|
// * 0x009F - KeWaitForSingleObject()
|
|
// ******************************************************************
|
|
XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|
(
|
|
IN PVOID Object,
|
|
IN KWAIT_REASON WaitReason,
|
|
IN KPROCESSOR_MODE WaitMode,
|
|
IN boolean_xt Alertable,
|
|
IN PLARGE_INTEGER Timeout OPTIONAL
|
|
)
|
|
{
|
|
LOG_FUNC_BEGIN
|
|
LOG_FUNC_ARG(Object)
|
|
LOG_FUNC_ARG(WaitReason)
|
|
LOG_FUNC_ARG(WaitMode)
|
|
LOG_FUNC_ARG(Alertable)
|
|
LOG_FUNC_ARG(Timeout)
|
|
LOG_FUNC_END;
|
|
|
|
// If the lock is not already held, lock it
|
|
PRKTHREAD Thread = KeGetCurrentThread();
|
|
if (Thread->WaitNext) {
|
|
Thread->WaitNext = FALSE;
|
|
}
|
|
else {
|
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|
}
|
|
|
|
// Wait Loop
|
|
// This loop ends
|
|
PLARGE_INTEGER OriginalTime = Timeout;
|
|
LARGE_INTEGER DueTime, NewTime;
|
|
KWAIT_BLOCK StackWaitBlock;
|
|
PKWAIT_BLOCK WaitBlock = &StackWaitBlock;
|
|
NTSTATUS WaitStatus;
|
|
// Hack variable (remove this when the thread scheduler is here)
|
|
bool timeout_set = false;
|
|
do {
|
|
// Check if we need to let an APC run. This should immediately trigger APC interrupt via a call to UnlockDispatcherDatabase
|
|
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
|
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|
} else {
|
|
PKMUTANT ObjectMutant = (PKMUTANT)Object;
|
|
Thread->WaitStatus = X_STATUS_SUCCESS;
|
|
|
|
if (ObjectMutant->Header.Type == MutantObject) {
|
|
// If the object is a mutant object and it has been acquired MINGLONG times, raise an exception
|
|
// If the mutant is signalled, we can satisfy the wait
|
|
if ((ObjectMutant->Header.SignalState > 0) || (Thread == ObjectMutant->OwnerThread)) {
|
|
if (ObjectMutant->Header.SignalState != MINLONG) {
|
|
KiWaitSatisfyMutant(ObjectMutant, Thread);
|
|
WaitStatus = (NTSTATUS)(Thread->WaitStatus);
|
|
goto NoWait;
|
|
}
|
|
else {
|
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|
ExRaiseStatus(X_STATUS_MUTANT_LIMIT_EXCEEDED);
|
|
}
|
|
}
|
|
}
|
|
else if (ObjectMutant->Header.SignalState > 0) {
|
|
// Otherwise, if the signal state is > 0, we can still just satisfy the wait
|
|
KiWaitSatisfyOther(ObjectMutant);
|
|
WaitStatus = X_STATUS_SUCCESS;
|
|
goto NoWait;
|
|
}
|
|
|
|
// If we reached here, the wait could not be satisfied immediately, so we must setup a WaitBlock
|
|
Thread->WaitBlockList = WaitBlock;
|
|
WaitBlock->Object = Object;
|
|
WaitBlock->WaitKey = (cshort_xt)(X_STATUS_SUCCESS);
|
|
WaitBlock->WaitType = WaitAny;
|
|
WaitBlock->Thread = Thread;
|
|
|
|
TestForAlertPending(Alertable);
|
|
|
|
// Handle a Timeout if specified
|
|
if (Timeout != nullptr) {
|
|
// If the timeout is 0, do not wait
|
|
if (!(Timeout->u.LowPart | Timeout->u.HighPart)) {
|
|
WaitStatus = (NTSTATUS)(STATUS_TIMEOUT);
|
|
goto NoWait;
|
|
}
|
|
|
|
// Setup a timer for the thread but only once (for now)
|
|
if (!timeout_set) {
|
|
KiTimerLock();
|
|
PKTIMER Timer = &Thread->Timer;
|
|
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
|
|
WaitBlock->NextWaitBlock = WaitTimer;
|
|
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
|
|
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
|
|
WaitTimer->NextWaitBlock = WaitBlock;
|
|
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
|
|
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
|
|
KiTimerUnlock();
|
|
goto NoWait;
|
|
}
|
|
|
|
// Boring, ensure that we only set the thread timer once. Otherwise, this will cause to insert the same
|
|
// thread timer over and over in the timer list, which will prevent KiTimerExpiration from removing these
|
|
// duplicated timers and thus it will attempt to endlessly remove the same unremoved timers, causing a deadlock.
|
|
// This can be removed once KiSwapThread and the kernel/user APCs are implemented
|
|
timeout_set = true;
|
|
DueTime.QuadPart = Timer->DueTime.QuadPart;
|
|
KiTimerUnlock();
|
|
}
|
|
|
|
// KiTimerExpiration has removed the timer but the object was not signaled, so we have a timeout
|
|
// (remove this when the thread scheduler is here)
|
|
if (Thread->Timer.Header.Inserted == FALSE) {
|
|
WaitStatus = (NTSTATUS)(STATUS_TIMEOUT);
|
|
goto NoWait;
|
|
}
|
|
}
|
|
else {
|
|
WaitBlock->NextWaitBlock = WaitBlock;
|
|
}
|
|
|
|
/*
|
|
TODO: We can't implement this and the return values until we have our own thread scheduler
|
|
For now, we'll have to implement waiting here instead of the scheduler.
|
|
This code can all be enabled once we have CPU emulation and our own scheduler in v1.0
|
|
*/
|
|
|
|
// Insert the WaitBlock
|
|
//InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
|
|
|
// If the current thread is processing a queue object, wake other treads using the same queue
|
|
PRKQUEUE Queue = (PRKQUEUE)Thread->Queue;
|
|
if (Queue != NULL) {
|
|
// TODO: KiActivateWaiterQueue(Queue);
|
|
}
|
|
|
|
// Insert the thread into the Wait List
|
|
Thread->Alertable = Alertable;
|
|
Thread->WaitMode = WaitMode;
|
|
Thread->WaitReason = (UCHAR)WaitReason;
|
|
Thread->WaitTime = KeTickCount;
|
|
// TODO: Thread->State = Waiting;
|
|
//KiInsertWaitList(WaitMode, Thread);
|
|
|
|
/*
|
|
WaitStatus = (NTSTATUS)KiSwapThread();
|
|
|
|
if (WaitStatus == X_STATUS_USER_APC) {
|
|
KiExecuteUserApc();
|
|
}
|
|
|
|
// If the thread was not awakened for an APC, return the Wait Status
|
|
if (WaitStatus != STATUS_KERNEL_APC) {
|
|
return WaitStatus;
|
|
} */
|
|
|
|
// TODO: Remove this after we have our own scheduler and the above is implemented
|
|
Sleep(0);
|
|
|
|
// Reduce the timout if necessary
|
|
if (Timeout != nullptr) {
|
|
Timeout = KiComputeWaitInterval(OriginalTime, &DueTime, &NewTime);
|
|
}
|
|
}
|
|
|
|
// Raise IRQL to DISPATCH_LEVEL and lock the database
|
|
if (KeGetCurrentIrql() != DISPATCH_LEVEL) {
|
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|
}
|
|
} while (TRUE);
|
|
|
|
// NOTE: we don't need to remove the wait blocks for the object and/or the timer because InsertTailList is disabled at
|
|
// the moment, which means they are never attached to the object wait list. TimerWaitBlock can also stay attached to the timer wait
|
|
// list since KiTimerExpiration disregards it for now.
|
|
|
|
// The waiting thead has been alerted, or an APC needs to be delivered
|
|
// So unlock the dispatcher database, lower the IRQ and return the status
|
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|
if (WaitStatus == X_STATUS_USER_APC) {
|
|
KiExecuteUserApc();
|
|
}
|
|
|
|
RETURN(WaitStatus);
|
|
|
|
NoWait:
|
|
// The wait was satisfied without actually waiting
|
|
// Unlock the database and return the status
|
|
//TODO: KiAdjustQuantumThread(Thread);
|
|
|
|
// Don't forget to remove the thread timer if the object was signaled before the timer expired
|
|
// (remove this when the thread scheduler is here)
|
|
if (timeout_set && Thread->Timer.Header.Inserted == TRUE) {
|
|
KiTimerLock();
|
|
KxRemoveTreeTimer(&Thread->Timer);
|
|
KiTimerUnlock();
|
|
}
|
|
|
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|
|
|
if (WaitStatus == X_STATUS_USER_APC) {
|
|
KiExecuteUserApc();
|
|
}
|
|
|
|
RETURN(WaitStatus);
|
|
}
|