diff --git a/src/xenia/kernel/xboxkrnl/objects/sources.gypi b/src/xenia/kernel/xboxkrnl/objects/sources.gypi index 21ec444ef..68d41dbf7 100644 --- a/src/xenia/kernel/xboxkrnl/objects/sources.gypi +++ b/src/xenia/kernel/xboxkrnl/objects/sources.gypi @@ -7,6 +7,8 @@ 'xfile.h', 'xmodule.cc', 'xmodule.h', + 'xsemaphore.cc', + 'xsemaphore.h', 'xthread.cc', 'xthread.h', ], diff --git a/src/xenia/kernel/xboxkrnl/objects/xevent.cc b/src/xenia/kernel/xboxkrnl/objects/xevent.cc index e099da7ab..7fb88a9ec 100644 --- a/src/xenia/kernel/xboxkrnl/objects/xevent.cc +++ b/src/xenia/kernel/xboxkrnl/objects/xevent.cc @@ -21,6 +21,9 @@ XEvent::XEvent(KernelState* kernel_state) : } XEvent::~XEvent() { + if (handle_) { + CloseHandle(handle_); + } } void XEvent::Initialize(bool manual_reset, bool initial_state) { diff --git a/src/xenia/kernel/xboxkrnl/objects/xsemaphore.cc b/src/xenia/kernel/xboxkrnl/objects/xsemaphore.cc new file mode 100644 index 000000000..f41204d6a --- /dev/null +++ b/src/xenia/kernel/xboxkrnl/objects/xsemaphore.cc @@ -0,0 +1,83 @@ +/** + ****************************************************************************** + * 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; + + +XSemaphore::XSemaphore(KernelState* kernel_state) : + XObject(kernel_state, kTypeSemaphore), + handle_(NULL) { +} + +XSemaphore::~XSemaphore() { + if (handle_) { + CloseHandle(handle_); + } +} + +void XSemaphore::Initialize(int32_t initial_count, int32_t maximum_count) { + XEASSERTNULL(handle_); + + handle_ = CreateSemaphore(NULL, initial_count, maximum_count, NULL); +} + +void XSemaphore::InitializeNative(void* native_ptr, DISPATCH_HEADER& header) { + XEASSERTNULL(handle_); + + // NOT IMPLEMENTED + // We expect Initialize to be called shortly. +} + +int32_t XSemaphore::ReleaseSemaphore(int32_t release_count) { + LONG previous_count = 0; + ::ReleaseSemaphore(handle_, release_count, &previous_count); + return previous_count; +} + +X_STATUS XSemaphore::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; + } +} diff --git a/src/xenia/kernel/xboxkrnl/objects/xsemaphore.h b/src/xenia/kernel/xboxkrnl/objects/xsemaphore.h new file mode 100644 index 000000000..5eb99fc1f --- /dev/null +++ b/src/xenia/kernel/xboxkrnl/objects/xsemaphore.h @@ -0,0 +1,46 @@ +/** + ****************************************************************************** + * 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_XSEMAPHORE_H_ +#define XENIA_KERNEL_XBOXKRNL_XSEMAPHORE_H_ + +#include + +#include + + +namespace xe { +namespace kernel { +namespace xboxkrnl { + + +class XSemaphore : public XObject { +public: + XSemaphore(KernelState* kernel_state); + virtual ~XSemaphore(); + + void Initialize(int32_t initial_count, int32_t maximum_count); + void InitializeNative(void* native_ptr, DISPATCH_HEADER& header); + + int32_t ReleaseSemaphore(int32_t release_count); + + 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_XBOXKRNL_XSEMAPHORE_H_ diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index ea18775a3..aafb3ff2a 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -656,6 +657,74 @@ SHIM_CALL NtClearEvent_shim( } +void xeKeInitializeSemaphore( + void* semaphore_ptr, int32_t count, int32_t limit) { + KernelState* state = shared_kernel_state_; + XEASSERTNOTNULL(state); + + XSemaphore* sem = (XSemaphore*)XSemaphore::GetObject( + state, semaphore_ptr, 5 /* SemaphoreObject */); + XEASSERTNOTNULL(sem); + if (!sem) { + return; + } + + sem->Initialize(count, limit); +} + + +SHIM_CALL KeInitializeSemaphore_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t semaphore_ref = SHIM_GET_ARG_32(0); + int32_t count = SHIM_GET_ARG_32(1); + int32_t limit = SHIM_GET_ARG_32(2); + + XELOGD( + "KeInitializeSemaphore(%.8X, %d, %d)", + semaphore_ref, count, limit); + + void* semaphore_ptr = SHIM_MEM_ADDR(semaphore_ref); + xeKeInitializeSemaphore(semaphore_ptr, count, limit); +} + + +int32_t xeKeReleaseSemaphore( + void* semaphore_ptr, int32_t increment, int32_t adjustment, bool wait) { + KernelState* state = shared_kernel_state_; + XEASSERTNOTNULL(state); + + XSemaphore* sem = (XSemaphore*)XSemaphore::GetObject(state, semaphore_ptr); + XEASSERTNOTNULL(sem); + if (!sem) { + return 0; + } + + // TODO(benvanik): increment thread priority? + // TODO(benvanik): wait? + + return sem->ReleaseSemaphore(adjustment); +} + + +SHIM_CALL KeReleaseSemaphore_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t semaphore_ref = SHIM_GET_ARG_32(0); + int32_t increment = SHIM_GET_ARG_32(1); + int32_t adjustment = SHIM_GET_ARG_32(2); + int32_t wait = SHIM_GET_ARG_32(3); + + XELOGD( + "KeReleaseSemaphore(%.8X, %d, %d, #d)", + semaphore_ref, increment, adjustment, wait); + + void* semaphore_ptr = SHIM_MEM_ADDR(semaphore_ref); + int32_t result = xeKeReleaseSemaphore( + semaphore_ptr, increment, adjustment, wait == 1); + + SHIM_SET_RETURN(result); +} + + X_STATUS xeKeWaitForSingleObject( void* object_ptr, uint32_t wait_reason, uint32_t processor_mode, uint32_t alertable, uint64_t* opt_timeout) { @@ -829,6 +898,9 @@ void xe::kernel::xboxkrnl::RegisterThreadingExports( SHIM_SET_MAPPING("xboxkrnl.exe", KeResetEvent, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtClearEvent, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeInitializeSemaphore, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeReleaseSemaphore, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeWaitForSingleObject, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtWaitForSingleObjectEx, state); diff --git a/src/xenia/kernel/xboxkrnl/xobject.cc b/src/xenia/kernel/xboxkrnl/xobject.cc index ac456f452..c65ce92e3 100644 --- a/src/xenia/kernel/xboxkrnl/xobject.cc +++ b/src/xenia/kernel/xboxkrnl/xobject.cc @@ -11,6 +11,7 @@ #include #include +#include using namespace xe; @@ -104,7 +105,8 @@ void XObject::SetNativePointer(uint32_t native_ptr) { XObject::UnlockType(); } -XObject* XObject::GetObject(KernelState* kernel_state, void* native_ptr) { +XObject* XObject::GetObject(KernelState* kernel_state, void* native_ptr, + int32_t as_type) { // 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 @@ -123,6 +125,10 @@ XObject* XObject::GetObject(KernelState* kernel_state, void* native_ptr) { header.wait_list_flink = XESWAP32(header_be->wait_list_flink); header.wait_list_blink = XESWAP32(header_be->wait_list_blink); + if (as_type == -1) { + as_type = header.type_flags & 0xFF; + } + if (header.wait_list_blink & 0x1) { // Already initialized. uint64_t object_ptr = @@ -136,7 +142,7 @@ XObject* XObject::GetObject(KernelState* kernel_state, void* native_ptr) { // First use, create new. // http://www.nirsoft.net/kernel_struct/vista/KOBJECTS.html XObject* object = NULL; - switch (header.type_flags & 0xFF) { + switch (as_type) { case 0: // EventNotificationObject case 1: // EventSynchronizationObject { @@ -145,10 +151,16 @@ XObject* XObject::GetObject(KernelState* kernel_state, void* native_ptr) { object = ev; } break; + case 5: // SemaphoreObject + { + XSemaphore* sem = new XSemaphore(kernel_state); + sem->InitializeNative(native_ptr, header); + object = sem; + } + break; case 2: // MutantObject case 3: // ProcessObject case 4: // QueueObject - case 5: // SemaphoreObject case 6: // ThreadObject case 7: // GateObject case 8: // TimerNotificationObject diff --git a/src/xenia/kernel/xboxkrnl/xobject.h b/src/xenia/kernel/xboxkrnl/xobject.h index 61575985d..95bc2df3d 100644 --- a/src/xenia/kernel/xboxkrnl/xobject.h +++ b/src/xenia/kernel/xboxkrnl/xobject.h @@ -32,10 +32,11 @@ typedef struct { class XObject { public: enum Type { - kTypeModule = 0x00000001, - kTypeThread = 0x00000002, - kTypeEvent = 0x00000003, - kTypeFile = 0x00000004, + kTypeModule = 0x00000001, + kTypeThread = 0x00000002, + kTypeEvent = 0x00000003, + kTypeFile = 0x00000004, + kTypeSemaphore = 0x00000005, }; XObject(KernelState* kernel_state, Type type); @@ -61,7 +62,8 @@ public: static void LockType(); static void UnlockType(); - static XObject* GetObject(KernelState* kernel_state, void* native_ptr); + static XObject* GetObject(KernelState* kernel_state, void* native_ptr, + int32_t as_type = -1); protected: Memory* memory() const;