Implement KeSetTimerEx (in progress)

This commit is contained in:
ergo720 2018-12-27 18:01:35 +01:00
parent 0fc2a7c3b2
commit 3f50f6de29
3 changed files with 160 additions and 81 deletions

View File

@ -897,7 +897,9 @@ XBSYSAPI EXPORTNUM(113) xboxkrnl::VOID NTAPI xboxkrnl::KeInitializeTimerEx
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Timer)
LOG_FUNC_ARG(Type)
LOG_FUNC_END;
LOG_FUNC_END;
assert(Timer);
// Initialize header :
Timer->Header.Type = Type + TimerNotificationObject;
@ -1742,45 +1744,53 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx
LOG_FUNC_ARG(DueTime)
LOG_FUNC_ARG(Period)
LOG_FUNC_ARG(Dpc)
LOG_FUNC_END;
LOG_FUNC_END;
BOOLEAN Inserted;
BOOLEAN Inserted;
BOOLEAN RequestInterrupt = FALSE;
LARGE_INTEGER Interval;
LARGE_INTEGER SystemTime;
if (Timer->Header.Type != TimerNotificationObject && Timer->Header.Type != TimerSynchronizationObject) {
CxbxKrnlCleanup("Assertion: '(Timer)->Header.Type == TimerNotificationObject) || ((Timer)->Header.Type == TimerSynchronizationObject)' in KeSetTimerEx()");
}
LARGE_INTEGER SystemTime;
KIRQL OldIrql;
ULONG Hand;
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) :
Inserted = Timer->Header.Inserted;
if (Inserted != FALSE) {
// Do some unlinking if already inserted in the linked list
KiRemoveTreeTimer(Timer);
KxRemoveTreeTimer(Timer);
}
/* Set Default Timer Data */
Timer->Dpc = Dpc;
Timer->Period = Period;
if (!KiComputeDueTime(Timer, DueTime, &Hand))
{
/* Signal the timer */
RequestInterrupt = KiSignalTimer(Timer);
/* Release the dispatcher lock */
KiReleaseDispatcherLockFromDpcLevel();
/* 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 */
KiExitDispatcher(OldIrql);
Timer->Header.SignalState = FALSE;
Timer->Dpc = Dpc;
Timer->Period = Period;
if (!KiInsertTreeTimer(Timer, DueTime)) {
if (!IsListEmpty(&(Timer->Header.WaitListHead))) {
// KiWaitTest(Timer, 0);
}
if (Dpc != NULL) {
// Call the Dpc routine if one is specified
KeQuerySystemTime(&SystemTime);
KeInsertQueueDpc(Timer->Dpc, (PVOID)SystemTime.u.LowPart, (PVOID)SystemTime.u.HighPart);
}
if (Period != 0) {
// Prepare the repetition if Timer is periodic
Interval.QuadPart = (LONGLONG)(-10 * 1000) * Timer->Period;
while (!KiInsertTreeTimer(Timer, Interval))
;
}
}
/* Dxbx has this :
EnterCriticalSection(&(g_DpcData.Lock));
if (Timer->TimerListEntry.Flink == nullptr)

View File

@ -33,7 +33,13 @@
// *
// * All rights reserved
// *
// ******************************************************************
// ******************************************************************
// Acknowledgment: ReactOS (GPLv2)
// https://github.com/reactos/reactos
// Changed from ReactOS: slight changes to the Hand parameter usage
#define _XBOXKRNL_DEFEXTRN_
#define LOG_PREFIX CXBXR_MODULE::KI
@ -46,46 +52,94 @@ namespace xboxkrnl
#include "Logging.h" // For LOG_FUNC()
#include "EmuKrnlLogging.h"
//#include "EmuKrnl.h" // For InitializeListHead(), etc.
xboxkrnl::BOOLEAN KiInsertTimerTable(
IN xboxkrnl::LARGE_INTEGER Interval,
xboxkrnl::ULONGLONG,
IN xboxkrnl::PKTIMER Timer
)
{
// TODO
return TRUE;
}
xboxkrnl::BOOLEAN KiInsertTreeTimer(
IN xboxkrnl::PKTIMER Timer,
IN xboxkrnl::LARGE_INTEGER Interval
)
{
// Is the given time absolute (indicated by a positive number)?
if (Interval.u.HighPart >= 0) {
// Convert absolute time to a time relative to the system time :
xboxkrnl::LARGE_INTEGER SystemTime;
xboxkrnl::KeQuerySystemTime(&SystemTime);
Interval.QuadPart = SystemTime.QuadPart - Interval.QuadPart;
if (Interval.u.HighPart >= 0) {
// If the relative time is already passed, return without inserting :
Timer->Header.Inserted = FALSE;
Timer->Header.SignalState = TRUE;
return FALSE;
}
Timer->Header.Absolute = TRUE;
}
else
// Negative intervals are relative, not absolute :
Timer->Header.Absolute = FALSE;
if (Timer->Period == 0)
Timer->Header.SignalState = FALSE;
Timer->Header.Inserted = TRUE;
return KiInsertTimerTable(Interval, xboxkrnl::KeQueryInterruptTime(), Timer);
}
#include "EmuKrnlKi.h"
// ReactOS uses a size of 512, but disassembling the kernel reveals it to be 32 instead
#define TIMER_TABLE_SIZE 32
xboxkrnl::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTreeTimer(
IN xboxkrnl::PKTIMER Timer,
IN xboxkrnl::LARGE_INTEGER Interval
)
{
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))
{
/* Insert the timer */
if (KiInsertTimerTable(Timer, Hand))
{
/* It was already there, remove it */
KiRemoveEntryTimer(Timer);
Timer->Header.Inserted = FALSE;
}
else
{
/* Otherwise, we're now inserted */
Inserted = TRUE;
}
}
/* Release the lock and return insert status */
return Inserted;
}
xboxkrnl::ULONG xboxkrnl::KiComputeTimerTableIndex(
IN xboxkrnl::ULONGLONG Interval
)
{
return (Interval / KeMaximumIncrement) & (TIMER_TABLE_SIZE - 1);
}
xboxkrnl::BOOLEAN xboxkrnl::KiComputeDueTime(
IN xboxkrnl::PKTIMER Timer,
IN xboxkrnl::LARGE_INTEGER DueTime,
OUT xboxkrnl::PULONG Hand)
{
LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime;
/* Convert to relative time if needed */
Timer->Header.Absolute = FALSE;
if (DueTime.u.HighPart >= 0)
{
/* Get System Time */
KeQuerySystemTime(&SystemTime);
/* Do the conversion */
DifferenceTime.QuadPart = SystemTime.QuadPart - DueTime.QuadPart;
/* Make sure it hasn't already expired */
Timer->Header.Absolute = TRUE;
if (DifferenceTime.u.HighPart >= 0)
{
/* Cancel everything */
DBG_PRINTF("Timer %p already expired\n", Timer);
Timer->Header.SignalState = TRUE;
Timer->DueTime.QuadPart = 0;
*Hand = 0;
return FALSE;
}
/* Set the time as Absolute */
DueTime = DifferenceTime;
}
/* Get the Interrupt Time */
InterruptTime.QuadPart = KeQueryInterruptTime();
/* Recalculate due time */
Timer->DueTime.QuadPart = InterruptTime.QuadPart - DueTime.QuadPart;
/* Get the handle */
*Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart);
Timer->Header.Inserted = TRUE;
return TRUE;
}

View File

@ -32,7 +32,9 @@
// *
// ******************************************************************
#pragma once
namespace xboxkrnl
{
#define KiLockDispatcherDatabase(OldIrql) \
*(OldIrql) = KeRaiseIrqlToDpcLevel()
@ -44,9 +46,22 @@
#define KiRemoveTreeTimer(Timer) \
(Timer)->Header.Inserted = FALSE; \
RemoveEntryList(&(Timer)->TimerListEntry)
RemoveEntryList(&(Timer)->TimerListEntry)
typedef struct _KTIMER_TABLE_ENTRY
{
LIST_ENTRY Entry;
ULARGE_INTEGER Time;
} KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY;
xboxkrnl::BOOLEAN KiInsertTreeTimer(
IN xboxkrnl::PKTIMER Timer,
IN xboxkrnl::LARGE_INTEGER Interval
);
BOOLEAN FASTCALL KiInsertTreeTimer(
IN PKTIMER Timer,
IN LARGE_INTEGER Interval
);
BOOLEAN KiComputeDueTime(
IN PKTIMER Timer,
IN LARGE_INTEGER DueTime,
OUT PULONG Hand
);
};