From 8424a668bfc94dc37a4444862a9cc1251bf637de Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Tue, 24 Sep 2013 21:26:45 -0700 Subject: [PATCH] Basic implementation of events. --- .../kernel/modules/xboxkrnl/kernel_state.cc | 2 + .../kernel/modules/xboxkrnl/kernel_state.h | 4 +- .../modules/xboxkrnl/objects/sources.gypi | 2 + .../kernel/modules/xboxkrnl/objects/xevent.cc | 93 +++++++++++++++++++ .../kernel/modules/xboxkrnl/objects/xevent.h | 47 ++++++++++ .../modules/xboxkrnl/xboxkrnl_threading.cc | 83 +++++++++++------ .../modules/xboxkrnl/xboxkrnl_threading.h | 2 +- src/xenia/kernel/modules/xboxkrnl/xobject.cc | 89 ++++++++++++++++++ src/xenia/kernel/modules/xboxkrnl/xobject.h | 20 ++++ src/xenia/kernel/xbox.h | 5 +- 10 files changed, 314 insertions(+), 33 deletions(-) create mode 100644 src/xenia/kernel/modules/xboxkrnl/objects/xevent.cc create mode 100644 src/xenia/kernel/modules/xboxkrnl/objects/xevent.h diff --git a/src/xenia/kernel/modules/xboxkrnl/kernel_state.cc b/src/xenia/kernel/modules/xboxkrnl/kernel_state.cc index c4196152e..d63608d95 100644 --- a/src/xenia/kernel/modules/xboxkrnl/kernel_state.cc +++ b/src/xenia/kernel/modules/xboxkrnl/kernel_state.cc @@ -32,6 +32,7 @@ KernelState::KernelState(Runtime* runtime) : filesystem_ = runtime->filesystem(); object_table_ = new ObjectTable(); + object_mutex_ = xe_mutex_alloc(10000); } KernelState::~KernelState() { @@ -41,6 +42,7 @@ KernelState::~KernelState() { } // Delete all objects. + xe_mutex_free(object_mutex_); delete object_table_; filesystem_.reset(); diff --git a/src/xenia/kernel/modules/xboxkrnl/kernel_state.h b/src/xenia/kernel/modules/xboxkrnl/kernel_state.h index 448fa07b6..3ba1bf629 100644 --- a/src/xenia/kernel/modules/xboxkrnl/kernel_state.h +++ b/src/xenia/kernel/modules/xboxkrnl/kernel_state.h @@ -45,15 +45,13 @@ public: void SetExecutableModule(XModule* module); private: - X_HANDLE InsertObject(XObject* obj); - void RemoveObject(XObject* obj); - Runtime* runtime_; xe_memory_ref memory_; shared_ptr processor_; shared_ptr filesystem_; ObjectTable* object_table_; + xe_mutex_t* object_mutex_; XModule* executable_module_; diff --git a/src/xenia/kernel/modules/xboxkrnl/objects/sources.gypi b/src/xenia/kernel/modules/xboxkrnl/objects/sources.gypi index bc226c02c..ddbfdb3c3 100644 --- a/src/xenia/kernel/modules/xboxkrnl/objects/sources.gypi +++ b/src/xenia/kernel/modules/xboxkrnl/objects/sources.gypi @@ -1,6 +1,8 @@ # Copyright 2013 Ben Vanik. All Rights Reserved. { 'sources': [ + 'xevent.cc', + 'xevent.h', 'xmodule.cc', 'xmodule.h', 'xthread.cc', diff --git a/src/xenia/kernel/modules/xboxkrnl/objects/xevent.cc b/src/xenia/kernel/modules/xboxkrnl/objects/xevent.cc new file mode 100644 index 000000000..6e41fec23 --- /dev/null +++ b/src/xenia/kernel/modules/xboxkrnl/objects/xevent.cc @@ -0,0 +1,93 @@ +/** + ****************************************************************************** + * 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; +using namespace xe::kernel::xboxkrnl; + + +XEvent::XEvent(KernelState* kernel_state) : + XObject(kernel_state, kTypeEvent), + handle_(NULL) { +} + +XEvent::~XEvent() { +} + +void XEvent::Initialize(void* native_ptr, DISPATCH_HEADER& header) { + XEASSERTNULL(handle_); + + bool manual_reset; + switch (header.type_flags >> 24) { + case 0x00: // EventNotificationObject (manual reset) + manual_reset = true; + break; + case 0x01: // EventSynchronizationObject (auto reset) + manual_reset = false; + break; + default: + XEASSERTALWAYS(); + return; + } + + bool initial_state = header.signal_state ? true : false; + + handle_ = CreateEvent(NULL, manual_reset, initial_state, NULL); +} + +int32_t XEvent::Set(uint32_t priority_increment, bool wait) { + return SetEvent(handle_) ? 1 : 0; +} + +int32_t XEvent::Reset() { + return ResetEvent(handle_) ? 1 : 0; +} + +void XEvent::Clear() { + ResetEvent(handle_); +} + +X_STATUS XEvent::Wait(uint32_t wait_reason, uint32_t processor_mode, + uint32_t alertable, uint64_t* opt_timeout) { + DWORD timeout_ms; + if (opt_timeout) { + int64_t timeout_ticks = (int64_t)(*opt_timeout); + if (timeout_ticks > 0) { + // Absolute time, based on January 1, 1601. + // TODO(benvanik): convert time to relative time. + XEASSERTALWAYS(); + timeout_ms = 0; + } else if (timeout_ticks < 0) { + // Relative time. + timeout_ms = (DWORD)(-timeout_ticks / 10000); // Ticks -> MS + } else { + timeout_ms = 0; + } + } else { + timeout_ms = INFINITE; + } + + DWORD result = WaitForSingleObjectEx(handle_, timeout_ms, alertable); + switch (result) { + case WAIT_OBJECT_0: + return X_STATUS_SUCCESS; + case WAIT_IO_COMPLETION: + // Or X_STATUS_ALERTED? + return X_STATUS_USER_APC; + case WAIT_TIMEOUT: + return X_STATUS_TIMEOUT; + default: + case WAIT_FAILED: + case WAIT_ABANDONED: + return X_STATUS_ABANDONED_WAIT_0; + } +} \ No newline at end of file diff --git a/src/xenia/kernel/modules/xboxkrnl/objects/xevent.h b/src/xenia/kernel/modules/xboxkrnl/objects/xevent.h new file mode 100644 index 000000000..f51a28b9d --- /dev/null +++ b/src/xenia/kernel/modules/xboxkrnl/objects/xevent.h @@ -0,0 +1,47 @@ +/** + ****************************************************************************** + * 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_MODULES_XBOXKRNL_XEVENT_H_ +#define XENIA_KERNEL_MODULES_XBOXKRNL_XEVENT_H_ + +#include + +#include + + +namespace xe { +namespace kernel { +namespace xboxkrnl { + + +class XEvent : public XObject { +public: + XEvent(KernelState* kernel_state); + virtual ~XEvent(); + + void Initialize(void* native_ptr, DISPATCH_HEADER& header); + + int32_t Set(uint32_t priority_increment, bool wait); + int32_t Reset(); + void Clear(); + + virtual X_STATUS Wait(uint32_t wait_reason, uint32_t processor_mode, + uint32_t alertable, uint64_t* opt_timeout); + +private: + HANDLE handle_; +}; + + +} // namespace xboxkrnl +} // namespace kernel +} // namespace xe + + +#endif // XENIA_KERNEL_MODULES_XBOXKRNL_XEVENT_H_ diff --git a/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_threading.cc index a289718fb..04347c5b2 100644 --- a/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_threading.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -307,8 +308,48 @@ SHIM_CALL KeTlsSetValue_shim( } +int32_t xeKeSetEvent(void* event_ptr, uint32_t increment, uint32_t wait) { + KernelState* state = shared_kernel_state_; + XEASSERTNOTNULL(state); + + XEvent* ev = (XEvent*)XObject::GetObject(state, event_ptr); + XEASSERTNOTNULL(ev); + if (!ev) { + return 0; + } + + return ev->Set(increment, !!wait); +} + + +SHIM_CALL KeSetEvent_shim( + xe_ppc_state_t* ppc_state, KernelState* state) { + uint32_t event_ref = SHIM_GET_ARG_32(0); + uint32_t increment = SHIM_GET_ARG_32(1); + uint32_t wait = SHIM_GET_ARG_32(2); + + XELOGD( + "KeSetEvent(%.4X, %.4X, %.4X)", + event_ref, increment, wait); + + void* event_ptr = SHIM_MEM_ADDR(event_ref); + int32_t result = xeKeSetEvent(event_ptr, increment, wait); + + SHIM_SET_RETURN(result); +} + + int32_t xeKeResetEvent(void* event_ptr) { - return 0; + KernelState* state = shared_kernel_state_; + XEASSERTNOTNULL(state); + + XEvent* ev = (XEvent*)XEvent::GetObject(state, event_ptr); + XEASSERTNOTNULL(ev); + if (!ev) { + return 0; + } + + return ev->Reset(); } @@ -327,33 +368,19 @@ SHIM_CALL KeResetEvent_shim( } -int32_t xeKeSetEvent(void* event_ptr, uint32_t increment, uint32_t wait) { - return 0; -} - - -SHIM_CALL KeSetEvent_shim( - xe_ppc_state_t* ppc_state, KernelState* state) { - uint32_t event_ref = SHIM_GET_ARG_32(0); - uint32_t increment = SHIM_GET_ARG_32(1); - uint32_t wait = SHIM_GET_ARG_32(2); - - XELOGD( - "KeSetEvent(%.4X, %.4X, %.4X)", - event_ref, increment, wait); - - void* event_ptr = SHIM_MEM_ADDR(event_ref); - int32_t result = xeKeSetEvent( - event_ptr, increment, wait); - - SHIM_SET_RETURN(result); -} - - X_STATUS xeKeWaitForSingleObject( void* object_ptr, uint32_t wait_reason, uint32_t processor_mode, - uint32_t alertable, uint32_t* opt_timeout) { - return X_STATUS_NOT_IMPLEMENTED; + uint32_t alertable, uint64_t* opt_timeout) { + KernelState* state = shared_kernel_state_; + XEASSERTNOTNULL(state); + + XObject* object = XObject::GetObject(state, object_ptr); + if (!object) { + // The only kind-of failure code. + return X_STATUS_ABANDONED_WAIT_0; + } + + return object->Wait(wait_reason, processor_mode, alertable, opt_timeout); } @@ -370,7 +397,7 @@ SHIM_CALL KeWaitForSingleObject_shim( object, wait_reason, processor_mode, alertable, timeout_ptr); void* object_ptr = SHIM_MEM_ADDR(object); - uint32_t timeout = timeout_ptr ? SHIM_MEM_32(timeout_ptr) : 0; + uint64_t timeout = timeout_ptr ? SHIM_MEM_64(timeout_ptr) : 0; X_STATUS result = xeKeWaitForSingleObject( object_ptr, wait_reason, processor_mode, alertable, timeout_ptr ? &timeout : NULL); @@ -395,8 +422,8 @@ void xe::kernel::xboxkrnl::RegisterThreadingExports( SHIM_SET_MAPPING("xboxkrnl.exe", KeTlsGetValue, state); SHIM_SET_MAPPING("xboxkrnl.exe", KeTlsSetValue, state); - SHIM_SET_MAPPING("xboxkrnl.exe", KeResetEvent, state); SHIM_SET_MAPPING("xboxkrnl.exe", KeSetEvent, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeResetEvent, state); SHIM_SET_MAPPING("xboxkrnl.exe", KeWaitForSingleObject, state); } diff --git a/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_threading.h b/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_threading.h index f265225bb..9b18d19c3 100644 --- a/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_threading.h +++ b/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_threading.h @@ -33,8 +33,8 @@ int KeTlsFree(uint32_t tls_index); uint32_t xeKeTlsGetValue(uint32_t tls_index); int xeKeTlsSetValue(uint32_t tls_index, uint32_t tls_value); -int32_t xeKeResetEvent(void* event_ptr); int32_t xeKeSetEvent(void* event_ptr, uint32_t increment, uint32_t wait); +int32_t xeKeResetEvent(void* event_ptr); X_STATUS xeKeWaitForSingleObject( void* object_ptr, uint32_t wait_reason, uint32_t processor_mode, diff --git a/src/xenia/kernel/modules/xboxkrnl/xobject.cc b/src/xenia/kernel/modules/xboxkrnl/xobject.cc index 3707d806f..a2f55b26a 100644 --- a/src/xenia/kernel/modules/xboxkrnl/xobject.cc +++ b/src/xenia/kernel/modules/xboxkrnl/xobject.cc @@ -9,6 +9,9 @@ #include +#include +#include + using namespace xe; using namespace xe::kernel; @@ -69,3 +72,89 @@ void XObject::Release() { delete this; } } + +X_STATUS XObject::Wait(uint32_t wait_reason, uint32_t processor_mode, + uint32_t alertable, uint64_t* opt_timeout) { + return X_STATUS_SUCCESS; +} + +void XObject::LockType() { + xe_mutex_lock(shared_kernel_state_->object_mutex_); +} + +void XObject::UnlockType() { + xe_mutex_unlock(shared_kernel_state_->object_mutex_); +} + +XObject* XObject::GetObject(KernelState* kernel_state, void* native_ptr) { + // Unfortunately the XDK seems to inline some KeInitialize calls, meaning + // we never see it and just randomly start getting passed events/timers/etc. + // Luckily it seems like all other calls (Set/Reset/Wait/etc) are used and + // we don't have to worry about PPC code poking the struct. Because of that, + // we init on first use, store our pointer in the struct, and dereference it + // each time. + // We identify this by checking the low bit of wait_list_blink - if it's 1, + // we have already put our pointer in there. + + XObject::LockType(); + + DISPATCH_HEADER* header_be = (DISPATCH_HEADER*)native_ptr; + DISPATCH_HEADER header; + header.type_flags = XESWAP32(header_be->type_flags); + header.signal_state = XESWAP32(header_be->signal_state); + header.wait_list_flink = XESWAP32(header_be->wait_list_flink); + header.wait_list_blink = XESWAP32(header_be->wait_list_blink); + + if (header.wait_list_blink & 0x1) { + // Already initialized. + uint64_t object_ptr = + ((uint64_t)header.wait_list_flink << 32) | + ((header.wait_list_blink) & ~0x1); + XObject* object = reinterpret_cast(object_ptr); + // TODO(benvanik): assert nothing has been changed in the struct. + XObject::UnlockType(); + return object; + } else { + // First use, create new. + // http://www.nirsoft.net/kernel_struct/vista/KOBJECTS.html + XObject* object = NULL; + switch (header.type_flags & 0xFF) { + case 0: // EventNotificationObject + case 1: // EventSynchronizationObject + { + XEvent* ev = new XEvent(kernel_state); + ev->Initialize(native_ptr, header); + object = ev; + } + break; + case 2: // MutantObject + case 3: // ProcessObject + case 4: // QueueObject + case 5: // SemaphoreObject + case 6: // ThreadObject + case 7: // GateObject + case 8: // TimerNotificationObject + case 9: // TimerSynchronizationObject + case 18: // ApcObject + case 19: // DpcObject + case 20: // DeviceQueueObject + case 21: // EventPairObject + case 22: // InterruptObject + case 23: // ProfileObject + case 24: // ThreadedDpcObject + default: + XEASSERTALWAYS(); + XObject::UnlockType(); + return NULL; + } + + // Stash pointer in struct. + uint64_t object_ptr = reinterpret_cast(object); + object_ptr |= 0x1; + header_be->wait_list_flink = XESWAP32((uint32_t)(object_ptr >> 32)); + header_be->wait_list_blink = XESWAP32((uint32_t)(object_ptr & 0xFFFFFFFF)); + + XObject::UnlockType(); + return object; + } +} \ No newline at end of file diff --git a/src/xenia/kernel/modules/xboxkrnl/xobject.h b/src/xenia/kernel/modules/xboxkrnl/xobject.h index a91e48141..804cdd471 100644 --- a/src/xenia/kernel/modules/xboxkrnl/xobject.h +++ b/src/xenia/kernel/modules/xboxkrnl/xobject.h @@ -27,11 +27,21 @@ namespace kernel { namespace xboxkrnl { +// http://www.nirsoft.net/kernel_struct/vista/DISPATCHER_HEADER.html +typedef struct { + uint32_t type_flags; + uint32_t signal_state; + uint32_t wait_list_flink; + uint32_t wait_list_blink; +} DISPATCH_HEADER; + + class XObject { public: enum Type { kTypeModule = 0x00000001, kTypeThread = 0x00000002, + kTypeEvent = 0x00000003, }; XObject(KernelState* kernel_state, Type type); @@ -47,6 +57,16 @@ public: void Retain(); void Release(); + // Reference() + // Dereference() + + virtual X_STATUS Wait(uint32_t wait_reason, uint32_t processor_mode, + uint32_t alertable, uint64_t* opt_timeout); + + static void LockType(); + static void UnlockType(); + static XObject* GetObject(KernelState* kernel_state, void* native_ptr); + protected: Runtime* runtime(); xe_memory_ref memory(); // unretained diff --git a/src/xenia/kernel/xbox.h b/src/xenia/kernel/xbox.h index 49bd92b08..82e17c4d7 100644 --- a/src/xenia/kernel/xbox.h +++ b/src/xenia/kernel/xbox.h @@ -29,6 +29,10 @@ typedef uint32_t X_STATUS; #define XFAILED(s) (s & X_STATUS_UNSUCCESSFUL) #define XSUCCEEDED(s) !XFAILED(s) #define X_STATUS_SUCCESS ((uint32_t)0x00000000L) +#define X_STATUS_ABANDONED_WAIT_0 ((uint32_t)0x00000080L) +#define X_STATUS_USER_APC ((uint32_t)0x000000C0L) +#define X_STATUS_ALERTED ((uint32_t)0x00000101L) +#define X_STATUS_TIMEOUT ((uint32_t)0x00000102L) #define X_STATUS_UNSUCCESSFUL ((uint32_t)0xC0000001L) #define X_STATUS_NOT_IMPLEMENTED ((uint32_t)0xC0000002L) #define X_STATUS_ACCESS_VIOLATION ((uint32_t)0xC0000005L) @@ -46,7 +50,6 @@ typedef uint32_t X_STATUS; #define X_STATUS_INVALID_PARAMETER_2 ((uint32_t)0xC00000F0L) #define X_STATUS_INVALID_PARAMETER_3 ((uint32_t)0xC00000F1L) - // MEM_*, used by NtAllocateVirtualMemory #define X_MEM_COMMIT 0x00001000 #define X_MEM_RESERVE 0x00002000