Files
Xenia-Canary/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_threading.cc

256 lines
6.0 KiB
C++

/**
******************************************************************************
* 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 <xenia/kernel/modules/xboxkrnl/xboxkrnl_threading.h>
#include <xenia/kernel/shim_utils.h>
#include <xenia/kernel/xbox.h>
#include <xenia/kernel/modules/xboxkrnl/objects/xthread.h>
using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::xboxkrnl;
namespace {
// r13 + 0x100: pointer to thread local state
// Thread local state:
// 0x14C: thread id
// 0x150: if >0 then error states don't get set
// 0x160: last error
// GetCurrentThreadId:
// lwz r11, 0x100(r13)
// lwz r3, 0x14C(r11)
// RtlGetLastError:
// lwz r11, 0x150(r13)
// if (r11 != 0) {
// lwz r11, 0x100(r13)
// stw r3, 0x160(r11)
// }
// RtlSetLastError:
// lwz r11, 0x150(r13)
// if (r11 != 0) {
// lwz r11, 0x100(r13)
// stw r3, 0x160(r11)
// }
// RtlSetLastNTError:
// r3 = RtlNtStatusToDosError(r3)
// lwz r11, 0x150(r13)
// if (r11 != 0) {
// lwz r11, 0x100(r13)
// stw r3, 0x160(r11)
// }
void ExCreateThread_shim(
xe_ppc_state_t* ppc_state, KernelState* state) {
// DWORD
// LPHANDLE Handle,
// DWORD StackSize,
// LPDWORD ThreadId,
// LPVOID XapiThreadStartup, ?? often 0
// LPVOID StartAddress,
// LPVOID StartContext,
// DWORD CreationFlags // 0x80?
uint32_t handle_ptr = SHIM_GET_ARG_32(0);
uint32_t stack_size = SHIM_GET_ARG_32(1);
uint32_t thread_id_ptr = SHIM_GET_ARG_32(2);
uint32_t xapi_thread_startup = SHIM_GET_ARG_32(3);
uint32_t start_address = SHIM_GET_ARG_32(4);
uint32_t start_context = SHIM_GET_ARG_32(5);
uint32_t creation_flags = SHIM_GET_ARG_32(6);
XELOGD(
"ExCreateThread(%.8X, %d, %.8X, %.8X, %.8X, %.8X, %.8X)",
handle_ptr,
stack_size,
thread_id_ptr,
xapi_thread_startup,
start_address,
start_context,
creation_flags);
XThread* thread = new XThread(
state, stack_size, xapi_thread_startup, start_address, start_context,
creation_flags);
X_STATUS result_code = thread->Create();
if (XFAILED(result_code)) {
// Failed!
thread->Release();
XELOGE("Thread creation failed: %.8X", result_code);
SHIM_SET_RETURN(result_code);
return;
}
if (handle_ptr) {
SHIM_SET_MEM_32(handle_ptr, thread->handle());
}
if (thread_id_ptr) {
SHIM_SET_MEM_32(thread_id_ptr, thread->thread_id());
}
SHIM_SET_RETURN(result_code);
}
void KeGetCurrentProcessType_shim(
xe_ppc_state_t* ppc_state, KernelState* state) {
// DWORD
XELOGD(
"KeGetCurrentProcessType()");
SHIM_SET_RETURN(X_PROCTYPE_USER);
}
// The TLS system used here is a bit hacky, but seems to work.
// Both Win32 and pthreads use unsigned longs as TLS indices, so we can map
// right into the system for these calls. We're just round tripping the IDs and
// hoping for the best.
// http://msdn.microsoft.com/en-us/library/ms686801
void KeTlsAlloc_shim(
xe_ppc_state_t* ppc_state, KernelState* state) {
// DWORD
XELOGD(
"KeTlsAlloc()");
uint32_t tls_index;
#if XE_PLATFORM(WIN32)
tls_index = TlsAlloc();
#else
pthread_key_t key;
if (pthread_key_create(&key, NULL)) {
tls_index = X_TLS_OUT_OF_INDEXES;
} else {
tls_index = (uint32_t)key;
}
#endif // WIN32
SHIM_SET_RETURN(tls_index);
}
// http://msdn.microsoft.com/en-us/library/ms686804
void KeTlsFree_shim(
xe_ppc_state_t* ppc_state, KernelState* state) {
// BOOL
// _In_ DWORD dwTlsIndex
uint32_t tls_index = SHIM_GET_ARG_32(0);
XELOGD(
"KeTlsFree(%.8X)",
tls_index);
if (tls_index == X_TLS_OUT_OF_INDEXES) {
SHIM_SET_RETURN(0);
return;
}
int result_code = 0;
#if XE_PLATFORM(WIN32)
result_code = TlsFree(tls_index);
#else
result_code = pthread_key_delete(tls_index) == 0;
#endif // WIN32
SHIM_SET_RETURN(result_code);
}
// http://msdn.microsoft.com/en-us/library/ms686812
void KeTlsGetValue_shim(
xe_ppc_state_t* ppc_state, KernelState* state) {
// LPVOID
// _In_ DWORD dwTlsIndex
uint32_t tls_index = SHIM_GET_ARG_32(0);
XELOGD(
"KeTlsGetValue(%.8X)",
tls_index);
uint32_t value = 0;
#if XE_PLATFORM(WIN32)
value = (uint32_t)((uint64_t)TlsGetValue(tls_index));
#else
value = (uint32_t)((uint64_t)pthread_getspecific(tls_index));
#endif // WIN32
if (!value) {
XELOGW("KeTlsGetValue should SetLastError if result is NULL");
// TODO(benvanik): SetLastError
}
SHIM_SET_RETURN(value);
}
// http://msdn.microsoft.com/en-us/library/ms686818
void KeTlsSetValue_shim(
xe_ppc_state_t* ppc_state, KernelState* state) {
// BOOL
// _In_ DWORD dwTlsIndex,
// _In_opt_ LPVOID lpTlsValue
uint32_t tls_index = SHIM_GET_ARG_32(0);
uint32_t tls_value = SHIM_GET_ARG_32(1);
XELOGD(
"KeTlsSetValue(%.8X, %.8X)",
tls_index, tls_value);
int result_code = 0;
#if XE_PLATFORM(WIN32)
result_code = TlsSetValue(tls_index, (LPVOID)tls_value);
#else
result_code = pthread_setspecific(tls_index, (void*)tls_value) == 0;
#endif // WIN32
SHIM_SET_RETURN(result_code);
}
}
void xe::kernel::xboxkrnl::RegisterThreadingExports(
ExportResolver* export_resolver, KernelState* state) {
#define SHIM_SET_MAPPING(ordinal, shim, impl) \
export_resolver->SetFunctionMapping("xboxkrnl.exe", ordinal, \
state, (xe_kernel_export_shim_fn)shim, (xe_kernel_export_impl_fn)impl)
SHIM_SET_MAPPING(0x0000000D, ExCreateThread_shim, NULL);
SHIM_SET_MAPPING(0x00000066, KeGetCurrentProcessType_shim, NULL);
SHIM_SET_MAPPING(0x00000152, KeTlsAlloc_shim, NULL);
SHIM_SET_MAPPING(0x00000153, KeTlsFree_shim, NULL);
SHIM_SET_MAPPING(0x00000154, KeTlsGetValue_shim, NULL);
SHIM_SET_MAPPING(0x00000155, KeTlsSetValue_shim, NULL);
#undef SET_MAPPING
}