From df70f29ba3ef59da80f26faa9626864b5085d576 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 9 Nov 2024 17:53:09 +0100 Subject: [PATCH 1/4] 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; From 040fcff19abf21183578ffcbb0dab18d74121286 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 9 Nov 2024 21:40:14 +0100 Subject: [PATCH 2/4] Fix poll --- src/windows-emulator/devices/afd_endpoint.cpp | 163 +++++++++++++++--- src/windows-emulator/devices/afd_types.hpp | 26 +++ 2 files changed, 163 insertions(+), 26 deletions(-) diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index 17210b3..a9b32d4 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -61,6 +61,135 @@ namespace return {std::move(poll_info), std::move(handle_info)}; } + std::optional perform_poll(windows_emulator& win_emu, const io_device_context& c, + const std::span endpoints, + const std::span handles) + { + std::vector poll_data{}; + poll_data.resize(endpoints.size()); + + for (size_t i = 0; i < endpoints.size() && i < handles.size(); ++i) + { + auto& pfd = poll_data.at(i); + auto& handle = handles[i]; + + if (handle.PollEvents & (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE)) + { + pfd.events |= POLLRDNORM; + } + + if (handle.PollEvents & AFD_POLL_RECEIVE_EXPEDITED) + { + pfd.events |= POLLRDNORM; + } + + if (handle.PollEvents & AFD_POLL_RECEIVE_EXPEDITED) + { + pfd.events |= POLLRDBAND; + } + + if (handle.PollEvents & (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND)) + { + pfd.events |= POLLWRNORM; + } + + /*if ((pfd.events & POLLRDNORM) != 0) + handle.PollEvents |= (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE); + if ((pfd.events & POLLRDBAND) != 0) + handle.PollEvents |= AFD_POLL_RECEIVE_EXPEDITED; + + if ((pfd.events & POLLWRNORM) != 0) + handle.PollEvents |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND); + handle.PollEvents |= (AFD_POLL_DISCONNECT | AFD_POLL_ABORT);*/ + + // ----------------------------- + + pfd.fd = endpoints[i]; + pfd.events = POLLIN; + pfd.revents = pfd.events; + } + + const auto count = poll(poll_data.data(), static_cast(poll_data.size()), 0); + if (count > 0) + { + constexpr auto info_size = offsetof(AFD_POLL_INFO, Handles); + const emulator_object handle_info_obj{win_emu.emu(), c.input_buffer + info_size}; + + size_t current_index = 0; + + for (size_t i = 0; i < endpoints.size(); ++i) + { + const auto& pfd = poll_data.at(i); + if (pfd.revents == 0) + { + continue; + } + + ULONG events = 0; + + if (pfd.revents & POLLRDNORM) + { + events |= (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE); + } + + if (pfd.revents & POLLRDBAND) + { + events |= AFD_POLL_RECEIVE_EXPEDITED; + } + + if (pfd.revents & POLLWRNORM) + { + events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND); + } + + if ((pfd.revents & (POLLHUP | POLLERR)) == (POLLHUP | POLLERR)) + { + events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT); + } + else if (pfd.revents & POLLHUP) + { + events |= AFD_POLL_DISCONNECT; + } + + if (pfd.revents & POLLNVAL) + { + events |= AFD_POLL_LOCAL_CLOSE; + } + + /*if ((handle.PollEvents & (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE)) != 0 && (pfd.events & POLLRDNORM) != 0) + pfd.revents |= POLLRDNORM; + if ((handle.PollEvents & AFD_POLL_RECEIVE_EXPEDITED) != 0 && (pfd.events & POLLRDBAND) != 0) + pfd.revents |= POLLRDBAND; + if ((handle.PollEvents & (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND)) != 0 && (pfd.events & POLLWRNORM) != 0) + pfd.revents |= POLLWRNORM; + if ((handle.PollEvents & AFD_POLL_DISCONNECT) != 0) + pfd.revents |= POLLHUP; + if ((handle.PollEvents & (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT)) != 0) + pfd.revents |= POLLHUP | POLLERR; + if ((handle.PollEvents & AFD_POLL_LOCAL_CLOSE) != 0) + pfd.revents |= POLLNVAL;*/ + + auto entry = handle_info_obj.read(i); + entry.PollEvents = events; + entry.Status = STATUS_SUCCESS; + + handle_info_obj.write(entry, current_index++); + break; + } + + assert(current_index == static_cast(count)); + + emulator_object{win_emu.emu(), c.input_buffer}.access([&](AFD_POLL_INFO& info) + { + info.NumberOfHandles = count; + }); + + return STATUS_SUCCESS; + } + + return {}; + } + struct afd_endpoint : io_device { bool in_poll{}; @@ -187,12 +316,12 @@ namespace return STATUS_SUCCESS; } - static std::vector resolve_endpoints(windows_emulator& win_emu, - const std::span handles) + static std::vector resolve_endpoints(windows_emulator& win_emu, + const std::span handles) { auto& proc = win_emu.process(); - std::vector endpoints{}; + std::vector endpoints{}; endpoints.reserve(handles.size()); for (const auto& handle : handles) @@ -203,13 +332,13 @@ namespace throw std::runtime_error("Bad device!"); } - auto* endpoint = device->get_internal_device(); + const auto* endpoint = device->get_internal_device(); if (!endpoint) { throw std::runtime_error("Device is not an AFD endpoint!"); } - endpoints.push_back(endpoint); + endpoints.push_back(*endpoint->s); } return endpoints; @@ -221,28 +350,10 @@ namespace 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) + const auto status = perform_poll(win_emu, c, endpoints, handles); + if (status) { - emulator_object{win_emu.emu(), c.input_buffer}.access([&](AFD_POLL_INFO& info) - { - info.NumberOfHandles = count; - }); - - t.pending_status = STATUS_SUCCESS; + t.pending_status = *status; return true; } diff --git a/src/windows-emulator/devices/afd_types.hpp b/src/windows-emulator/devices/afd_types.hpp index 30a87df..944ae5e 100644 --- a/src/windows-emulator/devices/afd_types.hpp +++ b/src/windows-emulator/devices/afd_types.hpp @@ -85,6 +85,32 @@ typedef struct _AFD_POLL_INFO AFD_POLL_HANDLE_INFO Handles[1]; } AFD_POLL_INFO, *PAFD_POLL_INFO; +#define AFD_POLL_RECEIVE_BIT 0 +#define AFD_POLL_RECEIVE (1 << AFD_POLL_RECEIVE_BIT) +#define AFD_POLL_RECEIVE_EXPEDITED_BIT 1 +#define AFD_POLL_RECEIVE_EXPEDITED (1 << AFD_POLL_RECEIVE_EXPEDITED_BIT) +#define AFD_POLL_SEND_BIT 2 +#define AFD_POLL_SEND (1 << AFD_POLL_SEND_BIT) +#define AFD_POLL_DISCONNECT_BIT 3 +#define AFD_POLL_DISCONNECT (1 << AFD_POLL_DISCONNECT_BIT) +#define AFD_POLL_ABORT_BIT 4 +#define AFD_POLL_ABORT (1 << AFD_POLL_ABORT_BIT) +#define AFD_POLL_LOCAL_CLOSE_BIT 5 +#define AFD_POLL_LOCAL_CLOSE (1 << AFD_POLL_LOCAL_CLOSE_BIT) +#define AFD_POLL_CONNECT_BIT 6 +#define AFD_POLL_CONNECT (1 << AFD_POLL_CONNECT_BIT) +#define AFD_POLL_ACCEPT_BIT 7 +#define AFD_POLL_ACCEPT (1 << AFD_POLL_ACCEPT_BIT) +#define AFD_POLL_CONNECT_FAIL_BIT 8 +#define AFD_POLL_CONNECT_FAIL (1 << AFD_POLL_CONNECT_FAIL_BIT) +#define AFD_POLL_QOS_BIT 9 +#define AFD_POLL_QOS (1 << AFD_POLL_QOS_BIT) +#define AFD_POLL_GROUP_QOS_BIT 10 +#define AFD_POLL_GROUP_QOS (1 << AFD_POLL_GROUP_QOS_BIT) + +#define AFD_NUM_POLL_EVENTS 11 +#define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1) + #define _AFD_REQUEST(ioctl) \ ((((ULONG)(ioctl)) >> 2) & 0x03FF) #define _AFD_BASE(ioctl) \ From 980ffd5348fc83d74c8a95536f1a49c6094af1c5 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 9 Nov 2024 21:41:13 +0100 Subject: [PATCH 3/4] Remove notes --- src/windows-emulator/devices/afd_endpoint.cpp | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index a9b32d4..8820c4c 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -93,17 +93,6 @@ namespace pfd.events |= POLLWRNORM; } - /*if ((pfd.events & POLLRDNORM) != 0) - handle.PollEvents |= (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE); - if ((pfd.events & POLLRDBAND) != 0) - handle.PollEvents |= AFD_POLL_RECEIVE_EXPEDITED; - - if ((pfd.events & POLLWRNORM) != 0) - handle.PollEvents |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND); - handle.PollEvents |= (AFD_POLL_DISCONNECT | AFD_POLL_ABORT);*/ - - // ----------------------------- - pfd.fd = endpoints[i]; pfd.events = POLLIN; pfd.revents = pfd.events; @@ -156,19 +145,6 @@ namespace events |= AFD_POLL_LOCAL_CLOSE; } - /*if ((handle.PollEvents & (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE)) != 0 && (pfd.events & POLLRDNORM) != 0) - pfd.revents |= POLLRDNORM; - if ((handle.PollEvents & AFD_POLL_RECEIVE_EXPEDITED) != 0 && (pfd.events & POLLRDBAND) != 0) - pfd.revents |= POLLRDBAND; - if ((handle.PollEvents & (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND)) != 0 && (pfd.events & POLLWRNORM) != 0) - pfd.revents |= POLLWRNORM; - if ((handle.PollEvents & AFD_POLL_DISCONNECT) != 0) - pfd.revents |= POLLHUP; - if ((handle.PollEvents & (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT)) != 0) - pfd.revents |= POLLHUP | POLLERR; - if ((handle.PollEvents & AFD_POLL_LOCAL_CLOSE) != 0) - pfd.revents |= POLLNVAL;*/ - auto entry = handle_info_obj.read(i); entry.PollEvents = events; entry.Status = STATUS_SUCCESS; From 500485771457c1637d51b7058854c35b2f0d3474 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 10 Nov 2024 09:16:53 +0100 Subject: [PATCH 4/4] Get rid of thread blocker --- src/windows-emulator/devices/afd_endpoint.cpp | 288 ++++++++++-------- src/windows-emulator/io_device.hpp | 21 +- src/windows-emulator/process_context.hpp | 3 - src/windows-emulator/windows_emulator.cpp | 12 - 4 files changed, 173 insertions(+), 151 deletions(-) diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index 8820c4c..4c24a72 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -61,116 +61,130 @@ namespace return {std::move(poll_info), std::move(handle_info)}; } - std::optional perform_poll(windows_emulator& win_emu, const io_device_context& c, - const std::span endpoints, - const std::span handles) + int16_t map_afd_request_events_to_socket(const ULONG poll_events) { - std::vector poll_data{}; - poll_data.resize(endpoints.size()); + int16_t socket_events{}; - for (size_t i = 0; i < endpoints.size() && i < handles.size(); ++i) + if (poll_events & (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE)) { - auto& pfd = poll_data.at(i); - auto& handle = handles[i]; + socket_events |= POLLRDNORM; + } - if (handle.PollEvents & (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE)) - { - pfd.events |= POLLRDNORM; - } + if (poll_events & AFD_POLL_RECEIVE_EXPEDITED) + { + socket_events |= POLLRDNORM; + } - if (handle.PollEvents & AFD_POLL_RECEIVE_EXPEDITED) - { - pfd.events |= POLLRDNORM; - } + if (poll_events & AFD_POLL_RECEIVE_EXPEDITED) + { + socket_events |= POLLRDBAND; + } - if (handle.PollEvents & AFD_POLL_RECEIVE_EXPEDITED) - { - pfd.events |= POLLRDBAND; - } + if (poll_events & (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND)) + { + socket_events |= POLLWRNORM; + } - if (handle.PollEvents & (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND)) - { - pfd.events |= POLLWRNORM; - } + return socket_events; + } - pfd.fd = endpoints[i]; - pfd.events = POLLIN; - pfd.revents = pfd.events; - } + ULONG map_socket_response_events_to_afd(const int16_t socket_events) + { + ULONG afd_events = 0; - const auto count = poll(poll_data.data(), static_cast(poll_data.size()), 0); - if (count > 0) + if (socket_events & POLLRDNORM) { - constexpr auto info_size = offsetof(AFD_POLL_INFO, Handles); - const emulator_object handle_info_obj{win_emu.emu(), c.input_buffer + info_size}; + afd_events |= (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE); + } - size_t current_index = 0; + if (socket_events & POLLRDBAND) + { + afd_events |= AFD_POLL_RECEIVE_EXPEDITED; + } - for (size_t i = 0; i < endpoints.size(); ++i) - { - const auto& pfd = poll_data.at(i); - if (pfd.revents == 0) - { - continue; - } + if (socket_events & POLLWRNORM) + { + afd_events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND); + } - ULONG events = 0; + if ((socket_events & (POLLHUP | POLLERR)) == (POLLHUP | POLLERR)) + { + afd_events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT); + } + else if (socket_events & POLLHUP) + { + afd_events |= AFD_POLL_DISCONNECT; + } - if (pfd.revents & POLLRDNORM) - { - events |= (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE); - } + if (socket_events & POLLNVAL) + { + afd_events |= AFD_POLL_LOCAL_CLOSE; + } - if (pfd.revents & POLLRDBAND) - { - events |= AFD_POLL_RECEIVE_EXPEDITED; - } + return afd_events; + } - if (pfd.revents & POLLWRNORM) - { - events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND); - } + NTSTATUS perform_poll(windows_emulator& win_emu, const io_device_context& c, + const std::span endpoints, + const std::span handles) + { + std::vector poll_data{}; + poll_data.resize(endpoints.size()); - if ((pfd.revents & (POLLHUP | POLLERR)) == (POLLHUP | POLLERR)) - { - events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT); - } - else if (pfd.revents & POLLHUP) - { - events |= AFD_POLL_DISCONNECT; - } + for (size_t i = 0; i < endpoints.size() && i < handles.size(); ++i) + { + auto& pfd = poll_data.at(i); + auto& handle = handles[i]; - if (pfd.revents & POLLNVAL) - { - events |= AFD_POLL_LOCAL_CLOSE; - } + pfd.fd = endpoints[i]; + pfd.events = map_afd_request_events_to_socket(handle.PollEvents); + pfd.revents = pfd.events; + } - auto entry = handle_info_obj.read(i); - entry.PollEvents = events; - entry.Status = STATUS_SUCCESS; + const auto count = poll(poll_data.data(), static_cast(poll_data.size()), 0); + if (count <= 0) + { + return STATUS_PENDING; + } - handle_info_obj.write(entry, current_index++); - break; - } + constexpr auto info_size = offsetof(AFD_POLL_INFO, Handles); + const emulator_object handle_info_obj{win_emu.emu(), c.input_buffer + info_size}; - assert(current_index == static_cast(count)); + size_t current_index = 0; - emulator_object{win_emu.emu(), c.input_buffer}.access([&](AFD_POLL_INFO& info) + for (size_t i = 0; i < endpoints.size(); ++i) + { + const auto& pfd = poll_data.at(i); + if (pfd.revents == 0) { - info.NumberOfHandles = count; - }); + continue; + } - return STATUS_SUCCESS; + auto entry = handle_info_obj.read(i); + entry.PollEvents = map_socket_response_events_to_afd(pfd.revents); + entry.Status = STATUS_SUCCESS; + + handle_info_obj.write(entry, current_index++); + break; } - return {}; + assert(current_index == static_cast(count)); + + emulator_object{win_emu.emu(), c.input_buffer}.access([&](AFD_POLL_INFO& info) + { + info.NumberOfHandles = static_cast(current_index); + }); + + return STATUS_SUCCESS; } struct afd_endpoint : io_device { - bool in_poll{}; - std::optional s{}; - std::optional delayed_ioctl{}; + bool executing_delayed_ioctl_{}; + std::optional s_{}; + std::optional require_poll_{}; + std::optional delayed_ioctl_{}; + std::optional timeout_{}; afd_endpoint() { @@ -182,9 +196,9 @@ namespace ~afd_endpoint() override { - if (this->s) + if (this->s_) { - closesocket(*this->s); + closesocket(*this->s_); } } @@ -200,31 +214,70 @@ namespace network::socket::set_blocking(sock, false); - s = sock; + s_ = sock; } - void work(windows_emulator& win_emu) override + void delay_ioctrl(const io_device_context& c, + const std::optional timeout = {}, + const std::optional require_poll = {}) { - if (!this->delayed_ioctl || !this->s) + if (this->executing_delayed_ioctl_) { return; } - const auto is_ready = network::socket::is_socket_ready(*this->s, this->in_poll); - if (!is_ready) + this->timeout_ = timeout; + this->require_poll_ = require_poll; + this->delayed_ioctl_ = c; + } + + void clear_pending_state() + { + this->timeout_ = {}; + this->require_poll_ = {}; + this->delayed_ioctl_ = {}; + } + + void work(windows_emulator& win_emu) override + { + if (!this->delayed_ioctl_ || !this->s_) { return; } - this->execute_ioctl(win_emu, *this->delayed_ioctl); + this->executing_delayed_ioctl_ = true; + const auto _ = utils::finally([&] + { + this->executing_delayed_ioctl_ = false; + }); + + if (this->require_poll_.has_value()) + { + const auto is_ready = network::socket::is_socket_ready(*this->s_, *this->require_poll_); + if (!is_ready) + { + return; + } + } + + const auto status = this->execute_ioctl(win_emu, *this->delayed_ioctl_); + if (status == STATUS_PENDING) + { + if (!this->timeout_ || this->timeout_ > std::chrono::steady_clock::now()) + { + return; + } - auto* e = win_emu.process().events.get(this->delayed_ioctl->event); + write_io_status(this->delayed_ioctl_->io_status_block, STATUS_TIMEOUT); + } + + auto* e = win_emu.process().events.get(this->delayed_ioctl_->event); if (e) { e->signaled = true; } - this->delayed_ioctl = {}; + this->clear_pending_state(); } void deserialize(utils::buffer_deserializer&) override @@ -258,7 +311,7 @@ namespace case AFD_RECEIVE_DATAGRAM: return this->ioctl_receive_datagram(win_emu, c); case AFD_POLL: - return ioctl_poll(win_emu, c); + return this->ioctl_poll(win_emu, c); case AFD_SET_CONTEXT: case AFD_GET_INFORMATION: return STATUS_SUCCESS; @@ -284,7 +337,7 @@ namespace const network::address addr(address, address_size); - if (bind(*this->s, &addr.get_addr(), addr.get_size()) == SOCKET_ERROR) + if (bind(*this->s_, &addr.get_addr(), addr.get_size()) == SOCKET_ERROR) { return STATUS_ADDRESS_ALREADY_ASSOCIATED; } @@ -314,51 +367,34 @@ namespace throw std::runtime_error("Device is not an AFD endpoint!"); } - endpoints.push_back(*endpoint->s); + endpoints.push_back(*endpoint->s_); } return endpoints; } - static bool is_poll_done(windows_emulator& win_emu, const io_device_context& c, emulator_thread& t, - const std::optional timeout) + NTSTATUS ioctl_poll(windows_emulator& win_emu, const io_device_context& c) { const auto [info, handles] = get_poll_info(win_emu, c); const auto endpoints = resolve_endpoints(win_emu, handles); const auto status = perform_poll(win_emu, c, endpoints, handles); - if (status) + if (status != STATUS_PENDING) { - t.pending_status = *status; - return true; + return status; } - if (timeout && *timeout < std::chrono::steady_clock::now()) + if (!this->executing_delayed_ioctl_) { - 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); + } - std::optional timeout{}; - if (info.Timeout.QuadPart) - { - timeout = convert_delay_interval_to_time_point(info.Timeout); + this->delay_ioctrl(c, 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; } @@ -394,7 +430,7 @@ namespace std::vector data{}; data.resize(buffer.len); - const auto recevied_data = recvfrom(*this->s, data.data(), static_cast(data.size()), 0, + const auto recevied_data = recvfrom(*this->s_, data.data(), static_cast(data.size()), 0, reinterpret_cast(address.data()), &fromlength); if (recevied_data < 0) @@ -402,8 +438,7 @@ namespace const auto error = GET_SOCKET_ERROR(); if (error == SOCK_WOULDBLOCK) { - this->in_poll = true; - this->delayed_ioctl = c; + this->delay_ioctrl(c, {}, true); return STATUS_PENDING; } @@ -431,7 +466,7 @@ namespace NTSTATUS ioctl_send_datagram(windows_emulator& win_emu, const io_device_context& c) { - auto& emu = win_emu.emu(); + const auto& emu = win_emu.emu(); if (c.input_buffer_length < sizeof(AFD_SEND_DATAGRAM_INFO)) { @@ -449,7 +484,7 @@ namespace const auto data = emu.read_memory(buffer.buf, buffer.len); - const auto sent_data = sendto(*this->s, reinterpret_cast(data.data()), + const auto sent_data = sendto(*this->s_, reinterpret_cast(data.data()), static_cast(data.size()), 0 /* ? */, &target.get_addr(), target.get_size()); @@ -458,8 +493,7 @@ namespace const auto error = GET_SOCKET_ERROR(); if (error == SOCK_WOULDBLOCK) { - this->in_poll = false; - this->delayed_ioctl = c; + this->delay_ioctrl(c, {}, false); return STATUS_PENDING; } diff --git a/src/windows-emulator/io_device.hpp b/src/windows-emulator/io_device.hpp index 01986f6..fe06c74 100644 --- a/src/windows-emulator/io_device.hpp +++ b/src/windows-emulator/io_device.hpp @@ -29,6 +29,17 @@ struct io_device_creation_data uint32_t length; }; +inline void write_io_status(const emulator_object io_status_block, const NTSTATUS status) +{ + if (io_status_block) + { + io_status_block.access([&](IO_STATUS_BLOCK& status_block) + { + status_block.Status = status; + }); + } +} + struct io_device { io_device() = default; @@ -64,15 +75,7 @@ struct io_device } const auto result = this->io_control(win_emu, c); - - if (c.io_status_block) - { - c.io_status_block.access([&](IO_STATUS_BLOCK& status) - { - status.Status = result; - }); - } - + write_io_status(c.io_status_block, result); return result; } }; diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 18d8474..8fca160 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -222,9 +222,6 @@ 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; diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index dbb6f9a..f23277c 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -686,18 +686,6 @@ bool emulator_thread::is_thread_ready(windows_emulator& win_emu) 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)