From b6fce711a9fb53c1b91577e78bd2700455ca26c9 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Sat, 29 Dec 2018 23:13:21 +0100 Subject: [PATCH] Completed KeSetTimerEx --- src/core/kernel/exports/EmuKrnl.cpp | 11 +- src/core/kernel/exports/EmuKrnl.h | 2 +- src/core/kernel/exports/EmuKrnlKe.cpp | 13 +- src/core/kernel/exports/EmuKrnlKi.cpp | 202 +++++++++++++++++++++++++- src/core/kernel/exports/EmuKrnlKi.h | 42 +++++- 5 files changed, 245 insertions(+), 25 deletions(-) diff --git a/src/core/kernel/exports/EmuKrnl.cpp b/src/core/kernel/exports/EmuKrnl.cpp index 66ed3de96..3adf056d2 100644 --- a/src/core/kernel/exports/EmuKrnl.cpp +++ b/src/core/kernel/exports/EmuKrnl.cpp @@ -96,19 +96,22 @@ void InsertTailList(xboxkrnl::PLIST_ENTRY pListHead, xboxkrnl::PLIST_ENTRY pEntr } //#define RemoveEntryList(e) do { PLIST_ENTRY f = (e)->Flink, b = (e)->Blink; f->Blink = b; b->Flink = f; (e)->Flink = (e)->Blink = NULL; } while (0) - -void RemoveEntryList(xboxkrnl::PLIST_ENTRY pEntry) + +// Returns TRUE if the list has become empty after removing the element, FALSE otherwise. +xboxkrnl::BOOLEAN RemoveEntryList(xboxkrnl::PLIST_ENTRY pEntry) { xboxkrnl::PLIST_ENTRY _EX_Flink = pEntry->Flink; xboxkrnl::PLIST_ENTRY _EX_Blink = pEntry->Blink; - if (_EX_Flink != nullptr) { + if (_EX_Blink != nullptr) { _EX_Blink->Flink = _EX_Flink; } if (_EX_Flink != nullptr) { _EX_Flink->Blink = _EX_Blink; - } + } + + return (_EX_Flink == _EX_Blink); } xboxkrnl::PLIST_ENTRY RemoveHeadList(xboxkrnl::PLIST_ENTRY pListHead) diff --git a/src/core/kernel/exports/EmuKrnl.h b/src/core/kernel/exports/EmuKrnl.h index 9484eae17..3cd9e8da8 100644 --- a/src/core/kernel/exports/EmuKrnl.h +++ b/src/core/kernel/exports/EmuKrnl.h @@ -52,7 +52,7 @@ void InsertHeadList(xboxkrnl::PLIST_ENTRY pListHead, xboxkrnl::PLIST_ENTRY pEntr void InsertTailList(xboxkrnl::PLIST_ENTRY pListHead, xboxkrnl::PLIST_ENTRY pEntry); //#define RemoveEntryList(e) do { PLIST_ENTRY f = (e)->Flink, b = (e)->Blink; f->Blink = b; b->Flink = f; (e)->Flink = (e)->Blink = NULL; } while (0) -void RemoveEntryList(xboxkrnl::PLIST_ENTRY pEntry); +xboxkrnl::BOOLEAN RemoveEntryList(xboxkrnl::PLIST_ENTRY pEntry); xboxkrnl::PLIST_ENTRY RemoveHeadList(xboxkrnl::PLIST_ENTRY pListHead); xboxkrnl::PLIST_ENTRY RemoveTailList(xboxkrnl::PLIST_ENTRY pListHead); diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index b3b7bd276..29bcd02e6 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -1756,8 +1756,6 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx assert(Timer); assert(Timer->Header.Type == TimerNotificationObject || Timer->Header.Type == TimerSynchronizationObject); - // NOTE: in ReactOS, this function raises the irql to SYNCH_LEVEL and it's a nop in single-cpu systems. I disassembled the function - // from my kernel dump and saw that it calls KeRaiseIrqlToDpcLevel KiLockDispatcherDatabase(&OldIrql); // Same as KeCancelTimer(Timer) : @@ -1775,11 +1773,10 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx /* Signal the timer */ RequestInterrupt = KiSignalTimer(Timer); - /* Release the dispatcher lock */ - KiReleaseDispatcherLockFromDpcLevel(); - /* Check if we need to do an interrupt */ - if (RequestInterrupt) HalRequestSoftwareInterrupt(DISPATCH_LEVEL); + if (RequestInterrupt) { + HalRequestSoftwareInterrupt(DISPATCH_LEVEL); + } } else { @@ -1789,7 +1786,7 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx } /* Exit the dispatcher */ - KiExitDispatcher(OldIrql); + KiUnlockDispatcherDatabase(OldIrql); /* Dxbx has this : EnterCriticalSection(&(g_DpcData.Lock)); @@ -1893,8 +1890,6 @@ XBSYSAPI EXPORTNUM(155) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeTestAlertThread // ****************************************************************** XBSYSAPI EXPORTNUM(156) xboxkrnl::DWORD VOLATILE xboxkrnl::KeTickCount = 0; -const xboxkrnl::ULONG CLOCK_TIME_INCREMENT = 0x2710; - // ****************************************************************** // * 0x009D - KeTimeIncrement // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlKi.cpp b/src/core/kernel/exports/EmuKrnlKi.cpp index c86501a9c..31477e084 100644 --- a/src/core/kernel/exports/EmuKrnlKi.cpp +++ b/src/core/kernel/exports/EmuKrnlKi.cpp @@ -51,7 +51,7 @@ namespace xboxkrnl }; #include "Logging.h" // For LOG_FUNC() -#include "EmuKrnlLogging.h" +#include "EmuKrnl.h" // for the list support functions #include "EmuKrnlKi.h" // ReactOS uses a size of 512, but disassembling the kernel reveals it to be 32 instead @@ -60,6 +60,123 @@ namespace xboxkrnl xboxkrnl::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE]; +VOID xboxkrnl::KxInsertTimer( + IN xboxkrnl::PKTIMER Timer, + IN xboxkrnl::ULONG Hand +) +{ + /* Try to insert the timer */ + if (KiInsertTimerTable(Timer, Hand)) + { + /* Complete it */ + KiCompleteTimer(Timer, Hand); + } +} + +VOID FASTCALL xboxkrnl::KiCompleteTimer( + IN xboxkrnl::PKTIMER Timer, + IN xboxkrnl::ULONG Hand +) +{ + LIST_ENTRY ListHead; + BOOLEAN RequestInterrupt = FALSE; + + /* Remove it from the timer list */ + KiRemoveEntryTimer(Timer, Hand); + + /* Link the timer list to our stack */ + ListHead.Flink = &Timer->TimerListEntry; + ListHead.Blink = &Timer->TimerListEntry; + Timer->TimerListEntry.Flink = &ListHead; + Timer->TimerListEntry.Blink = &ListHead; + + /* Signal the timer if it's still on our list */ + if (!IsListEmpty(&ListHead)) { + RequestInterrupt = KiSignalTimer(Timer); + } + + /* Request a DPC if needed */ + if (RequestInterrupt) HalRequestSoftwareInterrupt(DISPATCH_LEVEL); +} + +VOID xboxkrnl::KiRemoveEntryTimer( + IN xboxkrnl::PKTIMER Timer, + IN xboxkrnl::ULONG Hand +) +{ + ULONG Hand; + PKTIMER_TABLE_ENTRY TableEntry; + + /* Remove the timer from the timer list and check if it's empty */ + if (RemoveEntryList(&Timer->TimerListEntry)) + { + /* Get the respective timer table entry */ + TableEntry = &KiTimerTableListHead[Hand]; + if (&TableEntry->Entry == TableEntry->Entry.Flink) + { + /* Set the entry to an infinite absolute time */ + TableEntry->Time.u.HighPart = 0xFFFFFFFF; + } + } + + /* Clear the list entries so we can tell the timer is gone */ + Timer->TimerListEntry.Flink = NULL; + Timer->TimerListEntry.Blink = NULL; +} + +xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTimerTable( + IN xboxkrnl::PKTIMER Timer, + IN xboxkrnl::ULONG Hand +) +{ + LARGE_INTEGER InterruptTime; + LONGLONG DueTime = Timer->DueTime.QuadPart; + BOOLEAN Expired = FALSE; + PLIST_ENTRY ListHead, NextEntry; + PKTIMER CurrentTimer; + + DBG_PRINTF("%s: inserting Timer %p, Hand: %lu\n", __func__, Timer, Hand); + + /* Check if the period is zero */ + if (!Timer->Period) { + Timer->Header.SignalState = FALSE; + } + + /* Loop the timer list backwards */ + ListHead = &KiTimerTableListHead[Hand].Entry; + NextEntry = ListHead->Blink; + while (NextEntry != ListHead) + { + /* Get the timer */ + CurrentTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); + + /* Now check if we can fit it before */ + if ((ULONGLONG)DueTime >= CurrentTimer->DueTime.QuadPart) break; + + /* Keep looping */ + NextEntry = NextEntry->Blink; + } + + /* Looped all the list, insert it here and get the interrupt time again */ + InsertHeadList(NextEntry, &Timer->TimerListEntry); + + /* Check if we didn't find it in the list */ + if (NextEntry == ListHead) + { + /* Set the time */ + KiTimerTableListHead[Hand].Time.QuadPart = DueTime; + + /* Make sure it hasn't expired already */ + InterruptTime.QuadPart = KeQueryInterruptTime(); + if (DueTime <= InterruptTime.QuadPart) { + Expired = TRUE; + } + } + + /* Return expired state */ + return Expired; +} + xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTreeTimer( IN xboxkrnl::PKTIMER Timer, IN xboxkrnl::LARGE_INTEGER Interval @@ -68,9 +185,6 @@ xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTreeTimer( BOOLEAN Inserted = FALSE; ULONG Hand = 0; - /* This should only be called at dpc level */ - assert(KeGetCurrentIrql() == DISPATCH_LEVEL); - /* Setup the timer's due time */ if (KiComputeDueTime(Timer, Interval, &Hand)) { @@ -78,7 +192,7 @@ xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTreeTimer( if (KiInsertTimerTable(Timer, Hand)) { /* It was already there, remove it */ - KiRemoveEntryTimer(Timer); + KiRemoveEntryTimer(Timer, Hand); Timer->Header.Inserted = FALSE; } else @@ -88,7 +202,6 @@ xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTreeTimer( } } - /* Release the lock and return insert status */ return Inserted; } @@ -96,7 +209,7 @@ xboxkrnl::ULONG xboxkrnl::KiComputeTimerTableIndex( IN xboxkrnl::ULONGLONG Interval ) { - return (Interval / KeMaximumIncrement) & (TIMER_TABLE_SIZE - 1); + return (Interval / CLOCK_TIME_INCREMENT) & (TIMER_TABLE_SIZE - 1); } xboxkrnl::BOOLEAN xboxkrnl::KiComputeDueTime( @@ -143,3 +256,78 @@ xboxkrnl::BOOLEAN xboxkrnl::KiComputeDueTime( Timer->Header.Inserted = TRUE; return TRUE; } + +xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiSignalTimer( + IN xboxkrnl::PKTIMER Timer +) +{ + BOOLEAN RequestInterrupt = FALSE; + PKDPC Dpc = Timer->Dpc; + ULONG Period = Timer->Period; + LARGE_INTEGER Interval, SystemTime; + + /* Set default values */ + Timer->Header.Inserted = FALSE; + Timer->Header.SignalState = TRUE; + + /* Check if the timer has waiters */ + if (!IsListEmpty(&Timer->Header.WaitListHead)) + { + /* Check the type of event */ + if (Timer->Header.Type == TimerNotificationObject) + { + /* Unwait the thread */ + //KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); + } + else + { + /* Otherwise unwait the thread and signal the timer */ + //KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); + } + } + + /* Check if we have a period */ + if (Period) + { + /* Calculate the interval and insert the timer */ + Interval.QuadPart = Int32x32To64(Period, -10000); + while (!KiInsertTreeTimer(Timer, Interval)); + } + + /* Check if we have a DPC */ + if (Dpc) + { + /* Insert it in the queue */ + KeQuerySystemTime(&SystemTime); + KeInsertQueueDpc(Dpc, + ULongToPtr(SystemTime.u.LowPart), + ULongToPtr(SystemTime.u.HighPart)); + RequestInterrupt = TRUE; + } + + /* Return whether we need to request a DPC interrupt or not */ + return RequestInterrupt; +} + +VOID xboxkrnl::KxRemoveTreeTimer( + IN xboxkrnl::PKTIMER Timer +) +{ + ULONG Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); + PKTIMER_TABLE_ENTRY TimerEntry; + + /* Set the timer as non-inserted */ + Timer->Header.Inserted = FALSE; + + /* Remove it from the timer list */ + if (RemoveEntryList(&Timer->TimerListEntry)) + { + /* Get the entry and check if it's empty */ + TimerEntry = &KiTimerTableListHead[Hand]; + if (IsListEmpty(&TimerEntry->Entry)) + { + /* Clear the time then */ + TimerEntry->Time.u.HighPart = 0xFFFFFFFF; + } + } +} diff --git a/src/core/kernel/exports/EmuKrnlKi.h b/src/core/kernel/exports/EmuKrnlKi.h index 5777adf73..84de924ce 100644 --- a/src/core/kernel/exports/EmuKrnlKi.h +++ b/src/core/kernel/exports/EmuKrnlKi.h @@ -32,9 +32,7 @@ // * // ****************************************************************** #pragma once - -namespace xboxkrnl -{ + #define KiLockDispatcherDatabase(OldIrql) \ *(OldIrql) = KeRaiseIrqlToDpcLevel() @@ -48,20 +46,56 @@ namespace xboxkrnl (Timer)->Header.Inserted = FALSE; \ RemoveEntryList(&(Timer)->TimerListEntry) +namespace xboxkrnl +{ typedef struct _KTIMER_TABLE_ENTRY { LIST_ENTRY Entry; ULARGE_INTEGER Time; - } KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY; + } KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY; + + const ULONG CLOCK_TIME_INCREMENT = 0x2710; + + VOID KxInsertTimer( + IN PKTIMER Timer, + IN ULONG Hand + ); + + VOID FASTCALL KiCompleteTimer( + IN PKTIMER Timer, + IN ULONG Hand + ); + + VOID KiRemoveEntryTimer( + IN PKTIMER Timer, + IN ULONG Hand + ); + + BOOLEAN FASTCALL KiInsertTimerTable( + IN PKTIMER Timer, + IN ULONG Hand + ); BOOLEAN FASTCALL KiInsertTreeTimer( IN PKTIMER Timer, IN LARGE_INTEGER Interval ); + ULONG KiComputeTimerTableIndex( + IN ULONGLONG Interval + ); + BOOLEAN KiComputeDueTime( IN PKTIMER Timer, IN LARGE_INTEGER DueTime, OUT PULONG Hand ); + + BOOLEAN FASTCALL KiSignalTimer( + IN PKTIMER Timer + ); + + VOID KxRemoveTreeTimer( + IN PKTIMER Timer + ); };