[Kernel] Implement NtReadFileScatter via adding XFile::ReadScatter

This commit is contained in:
emoose 2020-12-28 23:32:30 +00:00 committed by Rick Gibbed
parent 0400eba274
commit 4b13ecb752
3 changed files with 147 additions and 0 deletions

View File

@ -265,6 +265,93 @@ dword_result_t NtReadFile(dword_t file_handle, dword_t event_handle,
}
DECLARE_XBOXKRNL_EXPORT2(NtReadFile, kFileSystem, kImplemented, kHighFrequency);
dword_result_t NtReadFileScatter(dword_t file_handle, dword_t event_handle,
lpvoid_t apc_routine_ptr, lpvoid_t apc_context,
pointer_t<X_IO_STATUS_BLOCK> io_status_block,
lpdword_t segment_array, dword_t length,
lpqword_t byte_offset_ptr) {
X_STATUS result = X_STATUS_SUCCESS;
bool signal_event = false;
auto ev = kernel_state()->object_table()->LookupObject<XEvent>(event_handle);
if (event_handle && !ev) {
result = X_STATUS_INVALID_HANDLE;
}
auto file = kernel_state()->object_table()->LookupObject<XFile>(file_handle);
if (!file) {
result = X_STATUS_INVALID_HANDLE;
}
if (XSUCCEEDED(result)) {
if (true || file->is_synchronous()) {
// Synchronous.
uint32_t bytes_read = 0;
result = file->ReadScatter(
segment_array.guest_address(), length,
byte_offset_ptr ? static_cast<uint64_t>(*byte_offset_ptr) : -1,
&bytes_read, apc_context);
if (io_status_block) {
io_status_block->status = result;
io_status_block->information = bytes_read;
}
// Queue the APC callback. It must be delivered via the APC mechanism even
// though were are completing immediately.
// Low bit probably means do not queue to IO ports.
if ((uint32_t)apc_routine_ptr & ~1) {
if (apc_context) {
auto thread = XThread::GetCurrentThread();
thread->EnqueueApc(static_cast<uint32_t>(apc_routine_ptr) & ~1u,
apc_context, io_status_block, 0);
}
}
if (!file->is_synchronous()) {
result = X_STATUS_PENDING;
}
// Mark that we should signal the event now. We do this after
// we have written the info out.
signal_event = true;
} else {
// TODO(benvanik): async.
// TODO: On Windows it might be worth trying to use Win32 ReadFileScatter
// here instead of handling it ourselves
// 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.guest_address(), buffer_length, byte_offset,
// request);
if (io_status_block) {
io_status_block->status = X_STATUS_PENDING;
io_status_block->information = 0;
}
result = X_STATUS_PENDING;
}
}
if (XFAILED(result) && io_status_block) {
io_status_block->status = result;
io_status_block->information = 0;
}
if (ev && signal_event) {
ev->Set(0, false);
}
return result;
}
DECLARE_XBOXKRNL_EXPORT1(NtReadFileScatter, kFileSystem, kImplemented);
dword_result_t NtWriteFile(dword_t file_handle, dword_t event_handle,
function_t apc_routine, lpvoid_t apc_context,
pointer_t<X_IO_STATUS_BLOCK> io_status_block,

View File

@ -178,6 +178,62 @@ X_STATUS XFile::Read(uint32_t buffer_guest_address, uint32_t buffer_length,
return result;
}
X_STATUS XFile::ReadScatter(uint32_t segments_guest_address, uint32_t length,
uint64_t byte_offset, uint32_t* out_bytes_read,
uint32_t apc_context) {
X_STATUS result = X_STATUS_SUCCESS;
// segments points to an array of buffer pointers of type
// "FILE_SEGMENT_ELEMENT", but they can just be treated as normal pointers
xe::be<uint32_t>* segments = reinterpret_cast<xe::be<uint32_t>*>(
memory()->TranslateVirtual(segments_guest_address));
// TODO: not sure if this is meant to change depending on buffer address?
// (only game seen using this always seems to use 4096-byte buffers)
uint32_t page_size = 4096;
uint32_t read_total = 0;
uint32_t read_remain = length;
while (read_remain) {
uint32_t read_length = read_remain;
uint32_t read_buffer = *segments;
if (read_length > page_size) {
read_length = page_size;
segments++;
}
uint32_t bytes_read = 0;
result = Read(read_buffer, read_length,
byte_offset ? ((byte_offset != -1 && byte_offset != -2)
? byte_offset + read_total
: byte_offset)
: -1,
&bytes_read, apc_context, false);
if (result != X_STATUS_SUCCESS) {
break;
}
read_total += bytes_read;
read_remain -= read_length;
}
if (out_bytes_read) {
*out_bytes_read = uint32_t(read_total);
}
XIOCompletion::IONotification notify;
notify.apc_context = apc_context;
notify.num_bytes = uint32_t(read_total);
notify.status = result;
NotifyIOCompletionPorts(notify);
async_event_->Set();
return result;
}
X_STATUS XFile::Write(uint32_t buffer_guest_address, uint32_t buffer_length,
uint64_t byte_offset, uint32_t* out_bytes_written,
uint32_t apc_context) {

View File

@ -101,6 +101,10 @@ class XFile : public XObject {
uint64_t byte_offset, uint32_t* out_bytes_read,
uint32_t apc_context, bool notify_completion = true);
X_STATUS ReadScatter(uint32_t segments_guest_address, uint32_t length,
uint64_t byte_offset, uint32_t* out_bytes_read,
uint32_t apc_context);
X_STATUS Write(uint32_t buffer_guess_address, uint32_t buffer_length,
uint64_t byte_offset, uint32_t* out_bytes_written,
uint32_t apc_context);