diff --git a/import/OpenXDK/include/xboxkrnl/kernel.h b/import/OpenXDK/include/xboxkrnl/kernel.h index ece42c91b..49cf32eb3 100644 --- a/import/OpenXDK/include/xboxkrnl/kernel.h +++ b/import/OpenXDK/include/xboxkrnl/kernel.h @@ -180,7 +180,10 @@ XBSYSAPI EXPORTNUM(119) BOOLEAN NTAPI KeInsertQueueDpc // ****************************************************************** XBSYSAPI EXPORTNUM(120) PKSYSTEM_TIME KeInterruptTime; -XBSYSAPI VOID *KeIsExecutingDpc; +// ****************************************************************** +// * 0x0079 - KeIsExecutingDpc() +// ****************************************************************** +XBSYSAPI EXPORTNUM(121) BOOLEAN NTAPI KeIsExecutingDpc(); // ****************************************************************** // * 0x007A - KeLeaveCriticalRegion() diff --git a/import/OpenXDK/include/xboxkrnl/xboxkrnl.h b/import/OpenXDK/include/xboxkrnl/xboxkrnl.h index 1f34f334b..a1a51e582 100644 --- a/import/OpenXDK/include/xboxkrnl/xboxkrnl.h +++ b/import/OpenXDK/include/xboxkrnl/xboxkrnl.h @@ -1767,9 +1767,9 @@ typedef VOID(*PCREATE_THREAD_NOTIFY_ROUTINE) ); // ****************************************************************** -// * KPCRB +// * KPRCB (Kernel PRocesor Control Block) // ****************************************************************** -// * +// * // * NOTE: INCOMPLETE!! // * // ****************************************************************** @@ -1779,11 +1779,16 @@ typedef struct _KPRCB struct _KTHREAD* NextThread; // 0x04, KPCR : 0x2C struct _KTHREAD* IdleThread; // 0x08, KPCR : 0x30 + ULONG Unknown1[8]; // 0x0C, KPCR : 0x34 + + ULONG DpcRoutineActive; // 0x2C, KPCR : 0x54 + // This is the total size of the structure (presumably) - UCHAR Unknown[0x250]; // 0x0C, KPCR : 0x34 + UCHAR Unknown[0x22C]; } KPRCB, *PKPRCB; + // ****************************************************************** // * KPCR // ****************************************************************** diff --git a/src/CxbxKrnl/EmuKrnlKe.cpp b/src/CxbxKrnl/EmuKrnlKe.cpp index 834c12089..2176231c1 100644 --- a/src/CxbxKrnl/EmuKrnlKe.cpp +++ b/src/CxbxKrnl/EmuKrnlKe.cpp @@ -131,6 +131,51 @@ xboxkrnl::PLIST_ENTRY RemoveTailList(xboxkrnl::PLIST_ENTRY pListHead) return Result; } +#define KiRemoveTreeTimer(Timer) \ + (Timer)->Header.Inserted = FALSE; \ + RemoveEntryList(&(Timer)->TimerListEntry) + +BOOLEAN KiInsertTimerTable( + IN xboxkrnl::LARGE_INTEGER Interval, + xboxkrnl::ULONGLONG, + IN xboxkrnl::PKTIMER Timer +) +{ + // TODO + return TRUE; +} + +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); +} + // ****************************************************************** // * KeGetPcr() // * NOTE: This is a macro on the Xbox, however we implement it @@ -138,7 +183,7 @@ xboxkrnl::PLIST_ENTRY RemoveTailList(xboxkrnl::PLIST_ENTRY pListHead) // ****************************************************************** xboxkrnl::KPCR* KeGetPcr() { - xboxkrnl::KPCR* Pcr = nullptr; + xboxkrnl::KPCR* Pcr; __asm { push eax @@ -150,6 +195,14 @@ xboxkrnl::KPCR* KeGetPcr() return Pcr; } +// ****************************************************************** +// * KeGetCurrentPrcb() +// ****************************************************************** +xboxkrnl::KPRCB *KeGetCurrentPrcb() +{ + return &(KeGetPcr()->PrcbData); +} + DWORD BootTickCount = 0; // The Xbox GetTickCount is measured in milliseconds, just like the native GetTickCount. @@ -176,6 +229,7 @@ DWORD __stdcall EmuThreadDpcHandler(LPVOID lpVoid) while (true) { + // While we're working with the DpcQueue, we need to be thread-safe : EnterCriticalSection(&(g_DpcData.Lock)); // if (g_DpcData._fShutdown) @@ -186,15 +240,22 @@ DWORD __stdcall EmuThreadDpcHandler(LPVOID lpVoid) // 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)), xboxkrnl::KDPC, DpcListEntry); - pkdpc->DpcListEntry.Flink = nullptr; + // Mark it as no longer linked into the DpcQueue + pkdpc->DpcListEntry.Flink = NULL; + // Set DpcRoutineActive to support KeIsExecutingDpc: + KeGetCurrentPrcb()->DpcRoutineActive = TRUE; // Experimental + // Call the Deferred Procedure : pkdpc->DeferredRoutine( pkdpc, pkdpc->DeferredContext, pkdpc->SystemArgument1, pkdpc->SystemArgument2); + KeGetCurrentPrcb()->DpcRoutineActive = FALSE; // Experimental } dwWait = INFINITE; @@ -387,9 +448,15 @@ XBSYSAPI EXPORTNUM(96) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeCancelTimer { LOG_FUNC_ONE_ARG(Timer); - LOG_UNIMPLEMENTED(); + BOOLEAN Inserted; - RETURN(TRUE); + Inserted = Timer->Header.Inserted; + if (Inserted != FALSE) { + // Do some unlinking if already inserted in the linked list + KiRemoveTreeTimer(Timer); + } + + RETURN(Inserted); } xboxkrnl::PKINTERRUPT EmuInterruptList[MAX_BUS_INTERRUPT_LEVEL + 1][MAX_NUM_INTERRUPTS] = { 0 }; @@ -456,7 +523,32 @@ XBSYSAPI EXPORTNUM(100) xboxkrnl::VOID NTAPI xboxkrnl::KeDisconnectInterrupt { LOG_FUNC_ONE_ARG(InterruptObject); - LOG_UNIMPLEMENTED(); + // Do the reverse of KeConnectInterrupt + // Untested (no known test cases) and not thread safe : + if (InterruptObject->Connected) + { + // Mark InterruptObject as not connected anymore + InterruptObject->Connected = FALSE; + // Search for it in the registered list of interrupts: + for(int i = 0; i < EmuFreeInterrupt[InterruptObject->BusInterruptLevel]; i++) + { + // Is it here? + if (EmuInterruptList[InterruptObject->BusInterruptLevel][i] == InterruptObject) + { + // Return the free slot : + int last = --EmuFreeInterrupt[InterruptObject->BusInterruptLevel]; + // Is this not the last slot? + if (i < last) + // Instead of shifting everything, move the last one into the slot of this InterruptObject : + EmuInterruptList[InterruptObject->BusInterruptLevel][i] = EmuInterruptList[InterruptObject->BusInterruptLevel][last]; + + // Clear the now-free (last) slot : + EmuInterruptList[InterruptObject->BusInterruptLevel][last] = NULL; + // Stop searching + break; + } + } + } } // ****************************************************************** @@ -592,8 +684,8 @@ XBSYSAPI EXPORTNUM(113) xboxkrnl::VOID NTAPI xboxkrnl::KeInitializeTimerEx LOG_FUNC_ARG(Type) LOG_FUNC_END; - Timer->Header.Type = Type + 8; // 8 = TimerNotificationObject - Timer->Header.Inserted = 0; + Timer->Header.Type = Type + TimerNotificationObject; + Timer->Header.Inserted = FALSE; Timer->Header.Size = sizeof(KTIMER) / sizeof(ULONG); Timer->Header.SignalState = 0; @@ -621,25 +713,41 @@ XBSYSAPI EXPORTNUM(119) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeInsertQueueDpc LOG_FUNC_ARG(SystemArgument2) LOG_FUNC_END; - if (Dpc->DpcListEntry.Flink == nullptr) + // Before going thread-save, check if the Dpc is not linked yet? + if (Dpc->DpcListEntry.Flink == NULL) { + // For thread safety, enter the Dpc lock: EnterCriticalSection(&(g_DpcData.Lock)); - - if (Dpc->DpcListEntry.Flink == nullptr) + // Is the Dpc still not linked yet ? + if (Dpc->DpcListEntry.Flink == NULL) { + // Remember the arguments and link it into our DpcQueue : Dpc->SystemArgument1 = SystemArgument1; Dpc->SystemArgument2 = SystemArgument2; InsertTailList(&(g_DpcData.DpcQueue), &(Dpc->DpcListEntry)); + // Signal the Dpc handling code there's work to do + SetEvent(g_DpcData.DpcEvent); } - + // Thread-safety is no longer required anymore LeaveCriticalSection(&(g_DpcData.Lock)); - - SetEvent(g_DpcData.DpcEvent); } RETURN(TRUE); } +// ****************************************************************** +// * 0x0079 - KeIsExecutingDpc() +// ****************************************************************** +XBSYSAPI EXPORTNUM(121) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeIsExecutingDpc +() +{ + LOG_FUNC(); + + BOOLEAN ret = KeGetCurrentPrcb()->DpcRoutineActive; + + RETURN(ret); +} + // ****************************************************************** // * 0x0078 - KeInterruptTime // ****************************************************************** @@ -678,13 +786,11 @@ XBSYSAPI EXPORTNUM(125) xboxkrnl::ULONGLONG NTAPI xboxkrnl::KeQueryInterruptTime LARGE_INTEGER InterruptTime; - // Don't use NtDll::QueryInterruptTime, it's too new (Windows 10) - // Instead, read KeInterruptTime from our kernel thunk table, - // which we coupled to the host InterruptTime in ConnectWindowsTimersToThunkTable: -// NtDll::PKSYSTEM_TIME KeInterruptTime = (NtDll::PKSYSTEM_TIME)CxbxKrnl_KernelThunkTable[120]; - while (true) { + // Don't use NtDll::QueryInterruptTime, it's too new (Windows 10) + // Instead, read KeInterruptTime from our kernel thunk table, + // which we coupled to the host InterruptTime in ConnectWindowsTimersToThunkTable: InterruptTime.u.HighPart = KeInterruptTime->High1Time; InterruptTime.u.LowPart = KeInterruptTime->LowPart; // TODO : Should we apply HostSystemTimeDelta to InterruptTime too? @@ -907,10 +1013,6 @@ XBSYSAPI EXPORTNUM(149) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimer return KeSetTimerEx(Timer, DueTime, 0, Dpc); } -#define KiRemoveTreeTimer(Timer) \ - (Timer)->Header.Inserted = FALSE; \ - RemoveEntryList(&(Timer)->TimerListEntry) - // ****************************************************************** // * 0x0096 - KeSetTimerEx() // ****************************************************************** @@ -933,10 +1035,11 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx LARGE_INTEGER Interval; LARGE_INTEGER SystemTime; - if (Timer->Header.Type != 8 && Timer->Header.Type != 9) { + if (Timer->Header.Type != TimerNotificationObject && Timer->Header.Type != TimerSynchronizationObject) { CxbxKrnlCleanup("Assertion: '(Timer)->Header.Type == TimerNotificationObject) || ((Timer)->Header.Type == TimerSynchronizationObject)' in KeSetTimerEx()"); } + // Same as KeCancelTimer(Timer) : Inserted = Timer->Header.Inserted; if (Inserted != FALSE) { // Do some unlinking if already inserted in the linked list @@ -947,7 +1050,7 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx Timer->Dpc = Dpc; Timer->Period = Period; - if (/*!KiInsertTreeTimer(Timer, DueTime)*/ TRUE) { + if (!KiInsertTreeTimer(Timer, DueTime)) { if (!IsListEmpty(&(Timer->Header.WaitListHead))) { // KiWaitTest(Timer, 0); } @@ -961,9 +1064,8 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx if (Period != 0) { // Prepare the repetition if Timer is periodic Interval.QuadPart = (LONGLONG)(-10 * 1000) * Timer->Period; - while (/*!KiInsertTreeTimer(Timer, Interval)*/FALSE) { + while (!KiInsertTreeTimer(Timer, Interval)) ; - } } } /* Dxbx has this : diff --git a/src/CxbxKrnl/KernelThunk.cpp b/src/CxbxKrnl/KernelThunk.cpp index 86efa196c..49ffa95f8 100644 --- a/src/CxbxKrnl/KernelThunk.cpp +++ b/src/CxbxKrnl/KernelThunk.cpp @@ -188,7 +188,7 @@ extern "C" CXBXKRNL_API uint32 CxbxKrnl_KernelThunkTable[379] = (uint32)PANIC(0x0076), // 0x0076 (118) KeInsertQueueApc (uint32)FUNC(&xboxkrnl::KeInsertQueueDpc), // 0x0077 (119) (uint32)VARIABLE(0x0078), // 0x0078 (120) KeInterruptTime (Set by ConnectWindowsTimersToThunkTable) - (uint32)PANIC(0x0079), // 0x0079 (121) KeIsExecutingDpc + (uint32)FUNC(&xboxkrnl::KeIsExecutingDpc), // 0x0079 (121) (uint32)FUNC(&xboxkrnl::KeLeaveCriticalRegion), // 0x007A (122) (uint32)PANIC(0x007B), // 0x007B (123) KePulseEvent (uint32)PANIC(0x007C), // 0x007C (124) KeQueryBasePriorityThread