Basic implementation of events.

This commit is contained in:
Ben Vanik 2013-09-24 21:26:45 -07:00
parent 474ecea277
commit 8424a668bf
10 changed files with 314 additions and 33 deletions

View File

@ -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();

View File

@ -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<cpu::Processor> processor_;
shared_ptr<fs::FileSystem> filesystem_;
ObjectTable* object_table_;
xe_mutex_t* object_mutex_;
XModule* executable_module_;

View File

@ -1,6 +1,8 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'xevent.cc',
'xevent.h',
'xmodule.cc',
'xmodule.h',
'xthread.cc',

View File

@ -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 <xenia/kernel/modules/xboxkrnl/objects/xevent.h>
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;
}
}

View File

@ -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 <xenia/kernel/modules/xboxkrnl/xobject.h>
#include <xenia/kernel/xbox.h>
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_

View File

@ -12,6 +12,7 @@
#include <xenia/kernel/shim_utils.h>
#include <xenia/kernel/modules/xboxkrnl/kernel_state.h>
#include <xenia/kernel/modules/xboxkrnl/xboxkrnl_private.h>
#include <xenia/kernel/modules/xboxkrnl/objects/xevent.h>
#include <xenia/kernel/modules/xboxkrnl/objects/xthread.h>
@ -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);
}

View File

@ -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,

View File

@ -9,6 +9,9 @@
#include <xenia/kernel/modules/xboxkrnl/xobject.h>
#include <xenia/kernel/modules/xboxkrnl/xboxkrnl_private.h>
#include <xenia/kernel/modules/xboxkrnl/objects/xevent.h>
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<XObject*>(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<uint64_t>(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;
}
}

View File

@ -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

View File

@ -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