diff --git a/src/xenia/kernel/dispatcher.cc b/src/xenia/kernel/dispatcher.cc new file mode 100644 index 000000000..30d1639a2 --- /dev/null +++ b/src/xenia/kernel/dispatcher.cc @@ -0,0 +1,37 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + +#include +#include + + +using namespace xe; +using namespace xe::kernel; + + +Dispatcher::Dispatcher(KernelState* kernel_state) : + kernel_state_(kernel_state) { + lock_ = xe_mutex_alloc(); + dpc_list_ = new NativeList(kernel_state->memory()); +} + +Dispatcher::~Dispatcher() { + delete dpc_list_; + xe_mutex_free(lock_); +} + +void Dispatcher::Lock() { + xe_mutex_lock(lock_); +} + +void Dispatcher::Unlock() { + xe_mutex_unlock(lock_); +} diff --git a/src/xenia/kernel/dispatcher.h b/src/xenia/kernel/dispatcher.h new file mode 100644 index 000000000..6f8bb6cf4 --- /dev/null +++ b/src/xenia/kernel/dispatcher.h @@ -0,0 +1,52 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XBOXKRNL_DISPATCHER_H_ +#define XENIA_KERNEL_XBOXKRNL_DISPATCHER_H_ + +#include +#include + +#include + + +namespace xe { +namespace kernel { + +class KernelState; +class NativeList; + + +class Dispatcher { +public: + Dispatcher(KernelState* kernel_state); + virtual ~Dispatcher(); + + KernelState* kernel_state() const { return kernel_state_; } + + void Lock(); + void Unlock(); + + NativeList* dpc_list() const { return dpc_list_; } + +private: + +private: + KernelState* kernel_state_; + + xe_mutex_t* lock_; + NativeList* dpc_list_; +}; + + +} // namespace kernel +} // namespace xe + + +#endif // XENIA_KERNEL_XBOXKRNL_DISPATCHER_H_ diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index e2f338bec..d804fec9b 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -35,6 +36,8 @@ KernelState::KernelState(Emulator* emulator) : processor_ = emulator->processor(); file_system_ = emulator->file_system(); + dispatcher_ = new Dispatcher(this); + object_table_ = new ObjectTable(); object_mutex_ = xe_mutex_alloc(10000); @@ -49,6 +52,8 @@ KernelState::~KernelState() { xe_mutex_free(object_mutex_); delete object_table_; + delete dispatcher_; + XEASSERT(shared_kernel_state_ == this); shared_kernel_state_ = NULL; } diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index 94b6dc673..944169918 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -21,6 +21,7 @@ XEDECLARECLASS1(xe, Emulator); XEDECLARECLASS2(xe, cpu, Processor); +XEDECLARECLASS2(xe, kernel, Dispatcher); XEDECLARECLASS2(xe, kernel, XModule); XEDECLARECLASS2(xe, kernel, XThread); XEDECLARECLASS2(xe, kernel, XUserModule); @@ -43,6 +44,8 @@ public: cpu::Processor* processor() const { return processor_; } fs::FileSystem* file_system() const { return file_system_; } + Dispatcher* dispatcher() const { return dispatcher_; } + ObjectTable* object_table() const { return object_table_; } XModule* GetModule(const char* name); @@ -59,6 +62,8 @@ private: cpu::Processor* processor_; fs::FileSystem* file_system_; + Dispatcher* dispatcher_; + ObjectTable* object_table_; xe_mutex_t* object_mutex_; std::unordered_map threads_by_id_; diff --git a/src/xenia/kernel/native_list.cc b/src/xenia/kernel/native_list.cc new file mode 100644 index 000000000..ec83955dc --- /dev/null +++ b/src/xenia/kernel/native_list.cc @@ -0,0 +1,68 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + + +using namespace xe; +using namespace xe::kernel; + + +NativeList::NativeList(Memory* memory) : + memory_(memory), + head_(kInvalidPointer) { +} + +void NativeList::Insert(uint32_t ptr) { + uint8_t* mem = memory_->membase(); + XESETUINT32BE(mem + ptr + 4, head_); + XESETUINT32BE(mem + ptr + 8, 0); + if (head_) { + XESETUINT32BE(mem + head_ + 8, 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); + 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); + if (ptr == head_) { + head_ = flink; + if (flink) { + XESETUINT32BE(mem + flink + 8, 0); + } + } else { + if (blink) { + XESETUINT32BE(mem + blink + 4, flink); + } + if (flink) { + XESETUINT32BE(mem + flink + 8, blink); + } + } + XESETUINT32BE(mem + ptr + 4, 0); + XESETUINT32BE(mem + ptr + 8, 0); +} + +uint32_t NativeList::Shift() { + if (!head_) { + return 0; + } + + uint32_t ptr = head_; + Remove(ptr); + return ptr; +} diff --git a/src/xenia/kernel/native_list.h b/src/xenia/kernel/native_list.h new file mode 100644 index 000000000..52f5c2d15 --- /dev/null +++ b/src/xenia/kernel/native_list.h @@ -0,0 +1,55 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XBOXKRNL_NATIVE_LIST_H_ +#define XENIA_KERNEL_XBOXKRNL_NATIVE_LIST_H_ + +#include +#include + +#include + + +namespace xe { +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. +// struct MYOBJ { +// uint32_t stuff; +// LIST_ENTRY list_entry; <-- manipulated +// ... +// } + +class NativeList { +public: + NativeList(Memory* memory); + + void Insert(uint32_t ptr); + bool IsQueued(uint32_t ptr); + void Remove(uint32_t ptr); + uint32_t Shift(); + +private: + const uint32_t kInvalidPointer = 0xE0FE0FFF; + +private: + Memory* memory_; + uint32_t head_; +}; + + +} // namespace kernel +} // namespace xe + + +#endif // XENIA_KERNEL_XBOXKRNL_NATIVE_LIST_H_ diff --git a/src/xenia/kernel/sources.gypi b/src/xenia/kernel/sources.gypi index a61051028..e60e6772e 100644 --- a/src/xenia/kernel/sources.gypi +++ b/src/xenia/kernel/sources.gypi @@ -3,10 +3,14 @@ 'sources': [ 'async_request.cc', 'async_request.h', + 'dispatcher.cc', + 'dispatcher.h', 'kernel.h', 'kernel_state.cc', 'kernel_state.h', 'modules.h', + 'native_list.cc', + 'native_list.h', 'object_table.cc', 'object_table.h', 'xam_content.cc', diff --git a/src/xenia/kernel/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl_threading.cc index f8fe457e3..1e52d1980 100644 --- a/src/xenia/kernel/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl_threading.cc @@ -9,7 +9,9 @@ #include +#include #include +#include #include #include #include @@ -1321,6 +1323,92 @@ SHIM_CALL KeLeaveCriticalRegion_shim( } +SHIM_CALL KeInitializeDpc_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t dpc_ptr = SHIM_GET_ARG_32(0); + uint32_t routine = SHIM_GET_ARG_32(1); + uint32_t context = SHIM_GET_ARG_32(2); + + XELOGD( + "KeInitializeDpc(%.8X, %.8X, %.8X)", + dpc_ptr, routine, context); + + // KDPC (maybe) 0x18 bytes? + uint32_t type = 0; + uint32_t importance = 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 + SHIM_SET_MEM_32(dpc_ptr + 8, 0); // blink + SHIM_SET_MEM_32(dpc_ptr + 12, routine); + SHIM_SET_MEM_32(dpc_ptr + 16, context); + SHIM_SET_MEM_32(dpc_ptr + 20, 0); // arg1 + SHIM_SET_MEM_32(dpc_ptr + 24, 0); // arg2 +} + + +SHIM_CALL KeInsertQueueDpc_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t dpc_ptr = SHIM_GET_ARG_32(0); + uint32_t arg1 = SHIM_GET_ARG_32(1); + uint32_t arg2 = SHIM_GET_ARG_32(2); + + XELOGD( + "KeInsertQueueDpc(%.8X, %.8X, %.8X)", + dpc_ptr, arg1, arg2); + + // Lock dispatcher. + auto dispatcher = state->dispatcher(); + dispatcher->Lock(); + + auto dpc_list = dispatcher->dpc_list(); + + // If already in a queue, abort. + if (dpc_list->IsQueued(dpc_ptr)) { + SHIM_SET_RETURN(0); + dispatcher->Unlock(); + return; + } + + // Prep DPC. + SHIM_SET_MEM_32(dpc_ptr + 20, arg1); + SHIM_SET_MEM_32(dpc_ptr + 24, arg2); + + dpc_list->Insert(dpc_ptr); + + dispatcher->Unlock(); + + SHIM_SET_RETURN(1); +} + + +SHIM_CALL KeRemoveQueueDpc_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t dpc_ptr = SHIM_GET_ARG_32(0); + + XELOGD( + "KeRemoveQueueDpc(%.8X)", + dpc_ptr); + + bool result = false; + + auto dispatcher = state->dispatcher(); + dispatcher->Lock(); + + auto dpc_list = dispatcher->dpc_list(); + if (dpc_list->IsQueued(dpc_ptr)) { + dpc_list->Remove(dpc_ptr); + result = true; + } + + dispatcher->Unlock(); + + SHIM_SET_RETURN(result ? 1 : 0); +} + + + } // namespace kernel } // namespace xe @@ -1381,4 +1469,8 @@ void xe::kernel::xboxkrnl::RegisterThreadingExports( SHIM_SET_MAPPING("xboxkrnl.exe", KeEnterCriticalRegion, state); SHIM_SET_MAPPING("xboxkrnl.exe", KeLeaveCriticalRegion, state); + + SHIM_SET_MAPPING("xboxkrnl.exe", KeInitializeDpc, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeInsertQueueDpc, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeRemoveQueueDpc, state); }