From 5f94688c83e0b7a7e631dc190e2cb1661f9c7b35 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 9 Nov 2024 17:53:09 +0100 Subject: [PATCH] Prepare poll support --- src/windows-emulator/devices/afd_endpoint.cpp | 127 +++++++++++++++++- src/windows-emulator/devices/afd_types.hpp | 15 +++ src/windows-emulator/process_context.hpp | 7 +- src/windows-emulator/windows_emulator.cpp | 18 ++- 4 files changed, 159 insertions(+), 8 deletions(-) diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index dfad092..17210b3 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -2,6 +2,7 @@ #include "afd_types.hpp" #include "../windows_emulator.hpp" +#include "../syscall_utils.hpp" #include #include @@ -31,6 +32,35 @@ namespace return win_emu.emu().read_memory(data.buffer); } + std::pair> get_poll_info( + windows_emulator& win_emu, const io_device_context& c) + { + constexpr auto info_size = offsetof(AFD_POLL_INFO, Handles); + if (!c.input_buffer || c.input_buffer_length < info_size) + { + throw std::runtime_error("Bad AFD poll data"); + } + + AFD_POLL_INFO poll_info{}; + win_emu.emu().read_memory(c.input_buffer, &poll_info, info_size); + + std::vector handle_info{}; + + const emulator_object handle_info_obj{win_emu.emu(), c.input_buffer + info_size}; + + if (c.input_buffer_length < (info_size + sizeof(AFD_POLL_HANDLE_INFO) * poll_info.NumberOfHandles)) + { + throw std::runtime_error("Bad AFD poll handle data"); + } + + for (ULONG i = 0; i < poll_info.NumberOfHandles; ++i) + { + handle_info.emplace_back(handle_info_obj.read(i)); + } + + return {std::move(poll_info), std::move(handle_info)}; + } + struct afd_endpoint : io_device { bool in_poll{}; @@ -122,14 +152,15 @@ namespace return this->ioctl_send_datagram(win_emu, c); case AFD_RECEIVE_DATAGRAM: return this->ioctl_receive_datagram(win_emu, c); + case AFD_POLL: + return ioctl_poll(win_emu, c); case AFD_SET_CONTEXT: - return STATUS_SUCCESS; case AFD_GET_INFORMATION: return STATUS_SUCCESS; + default: + win_emu.logger.print(color::gray, "Unsupported AFD IOCTL: %X\n", c.io_control_code); + return STATUS_NOT_SUPPORTED; } - - win_emu.logger.print(color::gray, "Unsupported AFD IOCTL: %X\n", c.io_control_code); - return STATUS_NOT_SUPPORTED; } NTSTATUS ioctl_bind(windows_emulator& win_emu, const io_device_context& c) const @@ -156,6 +187,94 @@ namespace return STATUS_SUCCESS; } + static std::vector resolve_endpoints(windows_emulator& win_emu, + const std::span handles) + { + auto& proc = win_emu.process(); + + std::vector endpoints{}; + endpoints.reserve(handles.size()); + + for (const auto& handle : handles) + { + auto* device = proc.devices.get(reinterpret_cast(handle.Handle)); + if (!device) + { + throw std::runtime_error("Bad device!"); + } + + auto* endpoint = device->get_internal_device(); + if (!endpoint) + { + throw std::runtime_error("Device is not an AFD endpoint!"); + } + + endpoints.push_back(endpoint); + } + + return endpoints; + } + + static bool is_poll_done(windows_emulator& win_emu, const io_device_context& c, emulator_thread& t, + const std::optional timeout) + { + const auto [info, handles] = get_poll_info(win_emu, c); + const auto endpoints = resolve_endpoints(win_emu, handles); + + std::vector poll_data{}; + poll_data.resize(endpoints.size()); + + for (size_t i = 0; i < endpoints.size(); ++i) + { + auto& pfd = poll_data.at(i); + //auto& handle = handles.at(i); + + pfd.fd = *endpoints.at(i)->s; + pfd.events = POLLIN; + pfd.revents = pfd.events; + } + + const auto count = poll(poll_data.data(), static_cast(poll_data.size()), 0); + if (count > 0) + { + emulator_object{win_emu.emu(), c.input_buffer}.access([&](AFD_POLL_INFO& info) + { + info.NumberOfHandles = count; + }); + + t.pending_status = STATUS_SUCCESS; + return true; + } + + if (timeout && *timeout < std::chrono::steady_clock::now()) + { + t.pending_status = STATUS_TIMEOUT; + return true; + } + + return false; + } + + static NTSTATUS ioctl_poll(windows_emulator& win_emu, const io_device_context& c) + { + const auto [info, handles] = get_poll_info(win_emu, c); + (void)resolve_endpoints(win_emu, handles); + + std::optional timeout{}; + if (info.Timeout.QuadPart) + { + timeout = convert_delay_interval_to_time_point(info.Timeout); + } + + win_emu.process().active_thread->thread_blocker = [timeout, c](windows_emulator& emu, emulator_thread& t) + { + return is_poll_done(emu, c, t, timeout); + }; + + win_emu.yield_thread(); + return STATUS_PENDING; + } + NTSTATUS ioctl_receive_datagram(windows_emulator& win_emu, const io_device_context& c) { auto& emu = win_emu.emu(); diff --git a/src/windows-emulator/devices/afd_types.hpp b/src/windows-emulator/devices/afd_types.hpp index a168380..30a87df 100644 --- a/src/windows-emulator/devices/afd_types.hpp +++ b/src/windows-emulator/devices/afd_types.hpp @@ -70,6 +70,21 @@ typedef struct _AFD_RECV_DATAGRAM_INFO PULONG AddressLength; } AFD_RECV_DATAGRAM_INFO, *PAFD_RECV_DATAGRAM_INFO; +typedef struct _AFD_POLL_HANDLE_INFO +{ + HANDLE Handle; + ULONG PollEvents; + NTSTATUS Status; +} AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO; + +typedef struct _AFD_POLL_INFO +{ + LARGE_INTEGER Timeout; + ULONG NumberOfHandles; + BOOLEAN Unique; + AFD_POLL_HANDLE_INFO Handles[1]; +} AFD_POLL_INFO, *PAFD_POLL_INFO; + #define _AFD_REQUEST(ioctl) \ ((((ULONG)(ioctl)) >> 2) & 0x03FF) #define _AFD_BASE(ioctl) \ diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 4bb6a23..18d8474 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -26,6 +26,8 @@ #define GDT_LIMIT 0x1000 #define GDT_ENTRY_SIZE 0x8 +class windows_emulator; + struct ref_counted_object { uint32_t ref_count{1}; @@ -220,6 +222,9 @@ class emulator_thread : ref_counted_object bool alerted{false}; std::optional await_time{}; + // TODO: Get rid of that! + std::function thread_blocker{}; + std::optional pending_status{}; std::optional gs_segment; @@ -234,7 +239,7 @@ class emulator_thread : ref_counted_object return this->await_time.has_value() && this->await_time.value() < std::chrono::steady_clock::now(); } - bool is_thread_ready(process_context& context); + bool is_thread_ready(windows_emulator& win_emu); void save(x64_emulator& emu) { diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index f8b89fd..dbb6f9a 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -520,7 +520,7 @@ namespace auto& emu = win_emu.emu(); auto& context = win_emu.process(); - if (!thread.is_thread_ready(context)) + if (!thread.is_thread_ready(win_emu)) { return false; } @@ -679,13 +679,25 @@ void emulator_thread::mark_as_ready(const NTSTATUS status) this->waiting_for_alert = false; } -bool emulator_thread::is_thread_ready(process_context& context) +bool emulator_thread::is_thread_ready(windows_emulator& win_emu) { if (this->exit_status.has_value()) { return false; } + if (this->thread_blocker) + { + const auto res = this->thread_blocker(win_emu, *this); + if (res) + { + this->thread_blocker = {}; + return true; + } + + return false; + } + if (this->waiting_for_alert) { if (this->alerted) @@ -704,7 +716,7 @@ bool emulator_thread::is_thread_ready(process_context& context) if (this->await_object.has_value()) { - if (is_object_signaled(context, *this->await_object)) + if (is_object_signaled(win_emu.process(), *this->await_object)) { this->mark_as_ready(STATUS_WAIT_0); return true;