From 61e873cd31e51bad15ded2ce98d8167a3adc4de1 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sat, 11 Jan 2014 22:12:05 -0800 Subject: [PATCH] Starting work on APCs, though nothing is hitting them yet. --- src/xenia/kernel/native_list.cc | 22 ++-- src/xenia/kernel/native_list.h | 10 +- src/xenia/kernel/objects/xthread.cc | 15 +++ src/xenia/kernel/objects/xthread.h | 15 ++- src/xenia/kernel/xboxkrnl_threading.cc | 175 ++++++++++++++++++++++++- src/xenia/kernel/xobject.cc | 2 +- 6 files changed, 209 insertions(+), 30 deletions(-) diff --git a/src/xenia/kernel/native_list.cc b/src/xenia/kernel/native_list.cc index ec83955dc..fbf792124 100644 --- a/src/xenia/kernel/native_list.cc +++ b/src/xenia/kernel/native_list.cc @@ -21,40 +21,40 @@ NativeList::NativeList(Memory* memory) : void NativeList::Insert(uint32_t ptr) { uint8_t* mem = memory_->membase(); - XESETUINT32BE(mem + ptr + 4, head_); - XESETUINT32BE(mem + ptr + 8, 0); + XESETUINT32BE(mem + ptr + 0, head_); + XESETUINT32BE(mem + ptr + 4, 0); if (head_) { - XESETUINT32BE(mem + head_ + 8, ptr); + XESETUINT32BE(mem + head_ + 4, ptr); } head_ = ptr; } bool NativeList::IsQueued(uint32_t ptr) { uint8_t* mem = memory_->membase(); - uint32_t flink = XEGETUINT32BE(mem + ptr + 4); - uint32_t blink = XEGETUINT32BE(mem + ptr + 8); + uint32_t flink = XEGETUINT32BE(mem + ptr + 0); + uint32_t blink = XEGETUINT32BE(mem + ptr + 4); return head_ == ptr || flink || blink; } void NativeList::Remove(uint32_t ptr) { uint8_t* mem = memory_->membase(); - uint32_t flink = XEGETUINT32BE(mem + ptr + 4); - uint32_t blink = XEGETUINT32BE(mem + ptr + 8); + uint32_t flink = XEGETUINT32BE(mem + ptr + 0); + uint32_t blink = XEGETUINT32BE(mem + ptr + 4); if (ptr == head_) { head_ = flink; if (flink) { - XESETUINT32BE(mem + flink + 8, 0); + XESETUINT32BE(mem + flink + 4, 0); } } else { if (blink) { - XESETUINT32BE(mem + blink + 4, flink); + XESETUINT32BE(mem + blink + 0, flink); } if (flink) { - XESETUINT32BE(mem + flink + 8, blink); + XESETUINT32BE(mem + flink + 4, blink); } } + XESETUINT32BE(mem + ptr + 0, 0); XESETUINT32BE(mem + ptr + 4, 0); - XESETUINT32BE(mem + ptr + 8, 0); } uint32_t NativeList::Shift() { diff --git a/src/xenia/kernel/native_list.h b/src/xenia/kernel/native_list.h index 52f5c2d15..e242d822f 100644 --- a/src/xenia/kernel/native_list.h +++ b/src/xenia/kernel/native_list.h @@ -23,10 +23,10 @@ namespace kernel { // List is designed for storing pointers to objects in the guest heap. // All values in the list should be assumed to be in big endian. -// Entries, as kernel objects, are assumed to have a LIST_ENTRY struct at +4b. +// Pass LIST_ENTRY pointers. // struct MYOBJ { // uint32_t stuff; -// LIST_ENTRY list_entry; <-- manipulated +// LIST_ENTRY list_entry; <-- pass this // ... // } @@ -34,9 +34,9 @@ class NativeList { public: NativeList(Memory* memory); - void Insert(uint32_t ptr); - bool IsQueued(uint32_t ptr); - void Remove(uint32_t ptr); + void Insert(uint32_t list_entry_ptr); + bool IsQueued(uint32_t list_entry_ptr); + void Remove(uint32_t list_entry_ptr); uint32_t Shift(); private: diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 7e7e73ba3..4fc4ec20a 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -53,6 +54,9 @@ XThread::XThread(KernelState* kernel_state, creation_params_.stack_size = 16 * 1024 * 1024; } + apc_lock_ = xe_mutex_alloc(); + apc_list_ = new NativeList(kernel_state->memory()); + event_ = new XEvent(kernel_state); event_->Initialize(true, false); @@ -64,6 +68,9 @@ XThread::~XThread() { // Unregister first to prevent lookups while deleting. kernel_state_->UnregisterThread(this); + delete apc_list_; + xe_mutex_free(apc_lock_); + event_->Release(); PlatformDestroy(); @@ -382,6 +389,14 @@ void XThread::LowerIrql(uint32_t new_irql) { irql_ = new_irql; } +void XThread::LockApc() { + xe_mutex_lock(apc_lock_); +} + +void XThread::UnlockApc() { + xe_mutex_unlock(apc_lock_); +} + int32_t XThread::QueryPriority() { return GetThreadPriority(thread_handle_); } diff --git a/src/xenia/kernel/objects/xthread.h b/src/xenia/kernel/objects/xthread.h index d9f222e47..a11bad9ea 100644 --- a/src/xenia/kernel/objects/xthread.h +++ b/src/xenia/kernel/objects/xthread.h @@ -14,18 +14,13 @@ #include - -namespace xe { -namespace cpu { -class XenonThreadState; -} -} +XEDECLARECLASS2(xe, cpu, XenonThreadState); namespace xe { namespace kernel { - +class NativeList; class XEvent; @@ -59,6 +54,10 @@ public: uint32_t RaiseIrql(uint32_t new_irql); void LowerIrql(uint32_t new_irql); + void LockApc(); + void UnlockApc(); + NativeList* apc_list() const { return apc_list_; } + int32_t QueryPriority(); void SetPriority(int32_t increment); @@ -91,6 +90,8 @@ private: char* name_; uint32_t irql_; + xe_mutex_t* apc_lock_; + NativeList* apc_list_; XEvent* event_; }; diff --git a/src/xenia/kernel/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl_threading.cc index 1e52d1980..3fb5da1f2 100644 --- a/src/xenia/kernel/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl_threading.cc @@ -1323,6 +1323,159 @@ SHIM_CALL KeLeaveCriticalRegion_shim( } +SHIM_CALL NtQueueApcThread_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t thread_handle = SHIM_GET_ARG_32(0); + uint32_t apc_routine = SHIM_GET_ARG_32(1); + uint32_t arg1 = SHIM_GET_ARG_32(2); + uint32_t arg2 = SHIM_GET_ARG_32(3); + uint32_t arg3 = SHIM_GET_ARG_32(4); // ? + XELOGD( + "NtQueueApcThread(%.8X, %.8X, %.8X, %.8X, %.8X)", + thread_handle, apc_routine, arg1, arg2, arg3); + + // Alloc APC object (from somewhere) and insert. +} + + +SHIM_CALL KeInitializeApc_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t apc_ptr = SHIM_GET_ARG_32(0); + uint32_t thread = SHIM_GET_ARG_32(1); + uint32_t kernel_routine = SHIM_GET_ARG_32(2); + uint32_t rundown_routine = SHIM_GET_ARG_32(3); + uint32_t normal_routine = SHIM_GET_ARG_32(4); + uint32_t processor_mode = SHIM_GET_ARG_32(5); + uint32_t normal_context = SHIM_GET_ARG_32(6); + + XELOGD( + "KeInitializeApc(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, %.8X)", + apc_ptr, thread, kernel_routine, rundown_routine, normal_routine, + processor_mode, normal_context); + + // KAPC is 0x28(40) bytes? (what's passed to ExAllocatePoolWithTag) + // This is 4b shorter than NT - looks like the reserved dword at +4 is gone + uint32_t type = 18; // ApcObject + uint32_t unk0 = 0; + uint32_t size = 0x28; + uint32_t unk1 = 0; + SHIM_SET_MEM_32(apc_ptr + 0, + (type << 24) | (unk0 << 16) | (size << 8) | (unk1)); + SHIM_SET_MEM_32(apc_ptr + 4, thread); // known offset - derefed by games + SHIM_SET_MEM_32(apc_ptr + 8, 0); // flink + SHIM_SET_MEM_32(apc_ptr + 12, 0); // blink + SHIM_SET_MEM_32(apc_ptr + 16, kernel_routine); + SHIM_SET_MEM_32(apc_ptr + 20, rundown_routine); + SHIM_SET_MEM_32(apc_ptr + 24, normal_routine); + SHIM_SET_MEM_32(apc_ptr + 28, normal_routine ? normal_context : 0); + SHIM_SET_MEM_32(apc_ptr + 32, 0); // arg1 + SHIM_SET_MEM_32(apc_ptr + 36, 0); // arg2 + uint32_t state_index = 0; + uint32_t inserted = 0; + SHIM_SET_MEM_32(apc_ptr + 40, + (state_index << 24) | (processor_mode << 16) | (inserted << 8)); +} + + +SHIM_CALL KeInsertQueueApc_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t apc_ptr = SHIM_GET_ARG_32(0); + uint32_t arg1 = SHIM_GET_ARG_32(1); + uint32_t arg2 = SHIM_GET_ARG_32(2); + uint32_t priority_increment = SHIM_GET_ARG_32(3); + + XELOGD( + "KeInsertQueueApc(%.8X, %.8X, %.8X, %.8X)", + apc_ptr, arg1, arg2, priority_increment); + + uint32_t thread_ptr = SHIM_MEM_32(apc_ptr + 4); + XThread* thread = (XThread*)XObject::GetObject( + state, SHIM_MEM_ADDR(thread_ptr)); + if (!thread) { + SHIM_SET_RETURN(0); + return; + } + + // Lock thread. + thread->LockApc(); + + // Fail if already inserted. + if (SHIM_MEM_32(apc_ptr + 40) & 0xFF00) { + thread->UnlockApc(); + SHIM_SET_RETURN(0); + return; + } + + // Prep APC. + SHIM_SET_MEM_32(apc_ptr + 32, arg1); + SHIM_SET_MEM_32(apc_ptr + 36, arg2); + SHIM_SET_MEM_32(apc_ptr + 40, + (SHIM_MEM_32(apc_ptr + 40) & ~0xFF00) | (1 << 8)); + + auto apc_list = thread->apc_list(); + + uint32_t list_entry_ptr = apc_ptr + 8; + apc_list->Insert(list_entry_ptr); + + // Unlock thread. + thread->UnlockApc(); + + SHIM_SET_RETURN(1); +} + + +SHIM_CALL KeRemoveQueueApc_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t apc_ptr = SHIM_GET_ARG_32(0); + + XELOGD( + "KeRemoveQueueApc(%.8X)", + apc_ptr); + + bool result = false; + + uint32_t thread_ptr = SHIM_MEM_32(apc_ptr + 4); + XThread* thread = (XThread*)XObject::GetObject( + state, SHIM_MEM_ADDR(thread_ptr)); + if (!thread) { + SHIM_SET_RETURN(0); + return; + } + + thread->LockApc(); + + if (!(SHIM_MEM_32(apc_ptr + 40) & 0xFF00)) { + thread->UnlockApc(); + SHIM_SET_RETURN(0); + return; + } + + auto apc_list = thread->apc_list(); + uint32_t list_entry_ptr = apc_ptr + 8; + if (apc_list->IsQueued(list_entry_ptr)) { + apc_list->Remove(list_entry_ptr); + result = true; + } + + thread->UnlockApc(); + + SHIM_SET_RETURN(result ? 1 : 0); +} + + +SHIM_CALL KiApcNormalRoutineNop_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t unk0 = SHIM_GET_ARG_32(0); // output? + uint32_t unk1 = SHIM_GET_ARG_32(1); // 0x13 + + XELOGD( + "KiApcNormalRoutineNop(%.8X, %.8X)", + unk0, unk1); + + SHIM_SET_RETURN(0); +} + + SHIM_CALL KeInitializeDpc_shim( PPCContext* ppc_state, KernelState* state) { uint32_t dpc_ptr = SHIM_GET_ARG_32(0); @@ -1334,9 +1487,9 @@ SHIM_CALL KeInitializeDpc_shim( dpc_ptr, routine, context); // KDPC (maybe) 0x18 bytes? - uint32_t type = 0; + uint32_t type = 19; // DpcObject uint32_t importance = 0; - uint32_t number = 0; + uint32_t number = 0; // ? SHIM_SET_MEM_32(dpc_ptr + 0, (type << 24) | (importance << 16) | (number)); SHIM_SET_MEM_32(dpc_ptr + 4, 0); // flink @@ -1358,6 +1511,8 @@ SHIM_CALL KeInsertQueueDpc_shim( "KeInsertQueueDpc(%.8X, %.8X, %.8X)", dpc_ptr, arg1, arg2); + uint32_t list_entry_ptr = dpc_ptr + 4; + // Lock dispatcher. auto dispatcher = state->dispatcher(); dispatcher->Lock(); @@ -1365,7 +1520,7 @@ SHIM_CALL KeInsertQueueDpc_shim( auto dpc_list = dispatcher->dpc_list(); // If already in a queue, abort. - if (dpc_list->IsQueued(dpc_ptr)) { + if (dpc_list->IsQueued(list_entry_ptr)) { SHIM_SET_RETURN(0); dispatcher->Unlock(); return; @@ -1375,7 +1530,7 @@ SHIM_CALL KeInsertQueueDpc_shim( SHIM_SET_MEM_32(dpc_ptr + 20, arg1); SHIM_SET_MEM_32(dpc_ptr + 24, arg2); - dpc_list->Insert(dpc_ptr); + dpc_list->Insert(list_entry_ptr); dispatcher->Unlock(); @@ -1393,12 +1548,14 @@ SHIM_CALL KeRemoveQueueDpc_shim( bool result = false; + uint32_t list_entry_ptr = dpc_ptr + 4; + auto dispatcher = state->dispatcher(); dispatcher->Lock(); auto dpc_list = dispatcher->dpc_list(); - if (dpc_list->IsQueued(dpc_ptr)) { - dpc_list->Remove(dpc_ptr); + if (dpc_list->IsQueued(list_entry_ptr)) { + dpc_list->Remove(list_entry_ptr); result = true; } @@ -1470,6 +1627,12 @@ void xe::kernel::xboxkrnl::RegisterThreadingExports( SHIM_SET_MAPPING("xboxkrnl.exe", KeEnterCriticalRegion, state); SHIM_SET_MAPPING("xboxkrnl.exe", KeLeaveCriticalRegion, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", NtQueueApcThread, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeInitializeApc, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeInsertQueueApc, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeRemoveQueueApc, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KiApcNormalRoutineNop, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeInitializeDpc, state); SHIM_SET_MAPPING("xboxkrnl.exe", KeInsertQueueDpc, state); SHIM_SET_MAPPING("xboxkrnl.exe", KeRemoveQueueDpc, state); diff --git a/src/xenia/kernel/xobject.cc b/src/xenia/kernel/xobject.cc index b849fb328..f406689a6 100644 --- a/src/xenia/kernel/xobject.cc +++ b/src/xenia/kernel/xobject.cc @@ -224,7 +224,7 @@ XObject* XObject::GetObject(KernelState* kernel_state, void* native_ptr, object = ev; } break; - case 2: // MutantObjectt + case 2: // MutantObject { XMutant* mutant = new XMutant(kernel_state); mutant->InitializeNative(native_ptr, header);