diff --git a/src/xenia/kernel/modules/xboxkrnl/async_request.cc b/src/xenia/kernel/modules/xboxkrnl/async_request.cc index b1f7bc909..06778e364 100644 --- a/src/xenia/kernel/modules/xboxkrnl/async_request.cc +++ b/src/xenia/kernel/modules/xboxkrnl/async_request.cc @@ -13,24 +13,30 @@ #include +using namespace std; using namespace xe; using namespace xe::kernel; using namespace xe::kernel::xboxkrnl; XAsyncRequest::XAsyncRequest( - XObject* object, + KernelState* kernel_state, XObject* object, CompletionCallback callback, void* callback_context) : - object_(object), + kernel_state_(kernel_state), object_(object), callback_(callback), callback_context_(callback_context), - wait_event_(0), apc_routine_(0), apc_context_(0) { object_->Retain(); } XAsyncRequest::~XAsyncRequest() { - if (wait_event_) { - wait_event_->Release(); + for (vector::iterator it = wait_events_.begin(); + it != wait_events_.end(); ++it) { + (*it)->Release(); } object_->Release(); } + +void XAsyncRequest::AddWaitEvent(XEvent* ev) { + ev->Retain(); + wait_events_.push_back(ev); +} diff --git a/src/xenia/kernel/modules/xboxkrnl/async_request.h b/src/xenia/kernel/modules/xboxkrnl/async_request.h index 21558fce3..fb17306c1 100644 --- a/src/xenia/kernel/modules/xboxkrnl/async_request.h +++ b/src/xenia/kernel/modules/xboxkrnl/async_request.h @@ -20,6 +20,7 @@ namespace xe { namespace kernel { namespace xboxkrnl { +class KernelState; class XEvent; class XObject; @@ -29,24 +30,24 @@ public: typedef void (*CompletionCallback)(XAsyncRequest* request, void* context); XAsyncRequest( - XObject* object, + KernelState* kernel_state, XObject* object, CompletionCallback callback, void* callback_context); virtual ~XAsyncRequest(); + KernelState* kernel_state() const { return kernel_state_; } XObject* object() const { return object_; } - XEvent* wait_event() const { return wait_event_; } - uint32_t apc_routine() const { return apc_routine_; } - uint32_t apc_context() const { return apc_context_; } + void AddWaitEvent(XEvent* ev); // Complete(result) protected: + KernelState* kernel_state_; XObject* object_; CompletionCallback callback_; void* callback_context_; - XEvent* wait_event_; + std::vector wait_events_; uint32_t apc_routine_; uint32_t apc_context_; }; diff --git a/src/xenia/kernel/modules/xboxkrnl/fs/entry.h b/src/xenia/kernel/modules/xboxkrnl/fs/entry.h index d56d8f437..0fba7b2fc 100644 --- a/src/xenia/kernel/modules/xboxkrnl/fs/entry.h +++ b/src/xenia/kernel/modules/xboxkrnl/fs/entry.h @@ -13,10 +13,15 @@ #include #include +#include + namespace xe { namespace kernel { namespace xboxkrnl { + +class XAsyncRequest; + namespace fs { @@ -67,6 +72,16 @@ public: //virtual void Query() = 0; + X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset, + size_t* out_bytes_read) { + return X_STATUS_NOT_IMPLEMENTED; + } + X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset, + XAsyncRequest* request) { + // queue completion of failure + return X_STATUS_NOT_IMPLEMENTED; + } + virtual MemoryMapping* CreateMemoryMapping( xe_file_mode file_mode, const size_t offset, const size_t length) = 0; }; diff --git a/src/xenia/kernel/modules/xboxkrnl/objects/xfile.cc b/src/xenia/kernel/modules/xboxkrnl/objects/xfile.cc index 95924a062..542d2b5b9 100644 --- a/src/xenia/kernel/modules/xboxkrnl/objects/xfile.cc +++ b/src/xenia/kernel/modules/xboxkrnl/objects/xfile.cc @@ -9,6 +9,8 @@ #include +#include +#include #include @@ -20,13 +22,41 @@ using namespace xe::kernel::xboxkrnl::fs; XFile::XFile(KernelState* kernel_state, FileEntry* entry) : entry_(entry), + position_(0), XObject(kernel_state, kTypeFile) { + async_event_ = new XEvent(kernel_state); + async_event_->Initialize(false, false); } XFile::~XFile() { + // TODO(benvanik): signal that the file is closing? + async_event_->Set(0, false); + async_event_->Delete(); +} + +X_STATUS XFile::Wait(uint32_t wait_reason, uint32_t processor_mode, + uint32_t alertable, uint64_t* opt_timeout) { + // Wait until some async operation completes. + return async_event_->Wait( + wait_reason, processor_mode, alertable, opt_timeout); +} + +X_STATUS XFile::Read(void* buffer, size_t buffer_length, size_t byte_offset, + size_t* out_bytes_read) { + if (byte_offset == -1) { + // Read from current position. + } + X_STATUS result = entry_->Read(buffer, buffer_length, byte_offset, out_bytes_read); + if (XSUCCEEDED(result)) { + position_ += *out_bytes_read; + } + return result; } X_STATUS XFile::Read(void* buffer, size_t buffer_length, size_t byte_offset, XAsyncRequest* request) { - return X_STATUS_ACCESS_DENIED; + // Also tack on our event so that any waiters wake. + request->AddWaitEvent(async_event_); + position_ = byte_offset; + return entry_->Read(buffer, buffer_length, byte_offset, request); } diff --git a/src/xenia/kernel/modules/xboxkrnl/objects/xfile.h b/src/xenia/kernel/modules/xboxkrnl/objects/xfile.h index 1218ab9ff..1190f4cc1 100644 --- a/src/xenia/kernel/modules/xboxkrnl/objects/xfile.h +++ b/src/xenia/kernel/modules/xboxkrnl/objects/xfile.h @@ -13,25 +13,41 @@ #include #include -#include -#include namespace xe { namespace kernel { namespace xboxkrnl { +class XAsyncRequest; +class XEvent; +namespace fs { +class FileEntry; +} + class XFile : public XObject { public: XFile(KernelState* kernel_state, fs::FileEntry* entry); - virtual ~XFile(); + virtual ~XFile(); + + virtual X_STATUS Wait(uint32_t wait_reason, uint32_t processor_mode, + uint32_t alertable, uint64_t* opt_timeout); + // TODO(benvanik): Create/Open + + X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset, + size_t* out_bytes_read); X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset, XAsyncRequest* request); private: fs::FileEntry* entry_; + XEvent* async_event_; + + // TODO(benvanik): create flags, open state, etc. + + size_t position_; }; diff --git a/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_io.cc b/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_io.cc index d842d0a66..e3a5bccff 100644 --- a/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_io.cc +++ b/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_io.cc @@ -10,8 +10,10 @@ #include #include +#include #include #include +#include #include @@ -105,6 +107,16 @@ SHIM_CALL NtOpenFile_shim( SHIM_SET_RETURN(X_STATUS_NO_SUCH_FILE); } +class xeNtReadFileState { +public: + uint32_t x; +}; +void xeNtReadFileCompleted(XAsyncRequest* request, xeNtReadFileState* state) { + // TODO(benvanik): set io_status_block_ptr + delete request; + delete state; +} + SHIM_CALL NtReadFile_shim( xe_ppc_state_t* ppc_state, KernelState* state) { uint32_t file_handle = SHIM_GET_ARG_32(0); @@ -115,10 +127,10 @@ SHIM_CALL NtReadFile_shim( uint32_t buffer = SHIM_GET_ARG_32(5); uint32_t buffer_length = SHIM_GET_ARG_32(6); uint32_t byte_offset_ptr = SHIM_GET_ARG_32(7); - uint32_t key = SHIM_GET_ARG_32(8); + size_t byte_offset = byte_offset_ptr ? SHIM_MEM_64(byte_offset_ptr) : 0; XELOGD( - "NtReadFile(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, %d, %d, %.8X)", + "NtReadFile(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, %d, %d)", file_handle, event_handle, apc_routine_ptr, @@ -126,24 +138,69 @@ SHIM_CALL NtReadFile_shim( io_status_block_ptr, buffer, buffer_length, - byte_offset_ptr, - key); + byte_offset_ptr); // Async not supported yet. XEASSERTNULL(apc_routine_ptr); - X_STATUS result = X_STATUS_INVALID_HANDLE; + X_STATUS result = X_STATUS_SUCCESS; uint32_t info = 0; - XFile* file = NULL; - result = state->object_table()->GetObject( - file_handle, (XObject**)&file); - if (result == X_STATUS_SUCCESS) { - // TODO(benvanik): read! + // Grab event to signal. + XEvent* ev = NULL; + bool signal_event = false; + if (event_handle) { + result = state->object_table()->GetObject( + event_handle, (XObject**)&ev); + } - file->Release(); - result = X_STATUS_SUCCESS; - info = 0; // number of bytes read + // Grab file. + XFile* file = NULL; + if (XSUCCEEDED(result)) { + result = state->object_table()->GetObject( + file_handle, (XObject**)&file); + } + + // Execute read. + if (XSUCCEEDED(result)) { + // Reset event before we begin. + if (ev) { + ev->Reset(); + } + + // TODO(benvanik): async path. + if (true) { + // Synchronous request. + if (byte_offset == 0xFFFFFFFFfffffffe) { + // FILE_USE_FILE_POINTER_POSITION + byte_offset = -1; + } + + // Read now. + size_t bytes_read = 0; + result = file->Read( + SHIM_MEM_ADDR(buffer), buffer_length, byte_offset, + &bytes_read); + if (XSUCCEEDED(result)) { + info = (int32_t)bytes_read; + } + + // Mark that we should signal the event now. We do this after + // we have written the info out. + signal_event = true; + } else { + // X_STATUS_PENDING if not returning immediately. + // XFile is waitable and signalled after each async req completes. + // reset the input event (->Reset()) + /*xeNtReadFileState* call_state = new xeNtReadFileState(); + XAsyncRequest* request = new XAsyncRequest( + state, file, + (XAsyncRequest::CompletionCallback)xeNtReadFileCompleted, + call_state);*/ + //result = file->Read(buffer, buffer_length, byte_offset, request); + result = X_STATUS_PENDING; + info = 0; + } } if (io_status_block_ptr) { @@ -151,6 +208,16 @@ SHIM_CALL NtReadFile_shim( SHIM_SET_MEM_32(io_status_block_ptr + 4, info); // Information } + if (file) { + file->Release(); + } + if (ev) { + if (signal_event) { + ev->Set(0, false); + } + ev->Release(); + } + SHIM_SET_RETURN(result); } diff --git a/src/xenia/kernel/modules/xboxkrnl/xobject.cc b/src/xenia/kernel/modules/xboxkrnl/xobject.cc index cce202433..f6539cab7 100644 --- a/src/xenia/kernel/modules/xboxkrnl/xobject.cc +++ b/src/xenia/kernel/modules/xboxkrnl/xobject.cc @@ -73,6 +73,10 @@ void XObject::Release() { } } +X_STATUS XObject::Delete() { + return shared_kernel_state_->object_table()->RemoveHandle(handle_); +} + X_STATUS XObject::Wait(uint32_t wait_reason, uint32_t processor_mode, uint32_t alertable, uint64_t* opt_timeout) { return X_STATUS_SUCCESS; @@ -85,15 +89,15 @@ void XObject::LockType() { 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, + +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(); @@ -115,46 +119,46 @@ XObject* XObject::GetObject(KernelState* kernel_state, void* native_ptr) { 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->InitializeNative(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; - } + // 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->InitializeNative(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 d196ed8d5..31c9d9389 100644 --- a/src/xenia/kernel/modules/xboxkrnl/xobject.h +++ b/src/xenia/kernel/modules/xboxkrnl/xobject.h @@ -27,7 +27,7 @@ namespace kernel { namespace xboxkrnl { -// http://www.nirsoft.net/kernel_struct/vista/DISPATCHER_HEADER.html +// http://www.nirsoft.net/kernel_struct/vista/DISPATCHER_HEADER.html typedef struct { uint32_t type_flags; uint32_t signal_state; @@ -57,6 +57,7 @@ public: bool ReleaseHandle(); void Retain(); void Release(); + X_STATUS Delete(); // Reference() // Dereference() diff --git a/src/xenia/kernel/xbox.h b/src/xenia/kernel/xbox.h index e1304eb9d..1e4f95f23 100644 --- a/src/xenia/kernel/xbox.h +++ b/src/xenia/kernel/xbox.h @@ -30,9 +30,10 @@ typedef uint32_t X_STATUS; #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_USER_APC ((uint32_t)0x000000C0L) +#define X_STATUS_ALERTED ((uint32_t)0x00000101L) +#define X_STATUS_TIMEOUT ((uint32_t)0x00000102L) +#define X_STATUS_PENDING ((uint32_t)0x00000103L) #define X_STATUS_UNSUCCESSFUL ((uint32_t)0xC0000001L) #define X_STATUS_NOT_IMPLEMENTED ((uint32_t)0xC0000002L) #define X_STATUS_ACCESS_VIOLATION ((uint32_t)0xC0000005L)