citra-emu
/
citra-canary
Archived
1
0
Fork 0

Fix socket poll and handling in windows (#6166)

* Fix socket poll and handling in windows

* Fix clang

* Add guest timing adjust

* Use platform independent time fetch

* Use proper type in time_point

* Fix ambiguous function call

* Do suggestions

* Take cpu_clock_scale into account in tick adjust
This commit is contained in:
PabloMK7 2022-10-27 15:05:49 +02:00 committed by GitHub
parent a2daef2985
commit 4d684174e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 227 additions and 52 deletions

View File

@ -174,6 +174,22 @@ void Timing::Timer::MoveEvents() {
} }
} }
u32 Timing::Timer::StartAdjust() {
ASSERT((adjust_value_curr_handle & 1) == 0); // Should always be even
adjust_value_last = std::chrono::steady_clock::now();
return ++adjust_value_curr_handle;
}
void Timing::Timer::EndAdjust(u32 start_adjust_handle) {
std::chrono::time_point<std::chrono::steady_clock> new_timer = std::chrono::steady_clock::now();
ASSERT(new_timer >= adjust_value_last && start_adjust_handle == adjust_value_curr_handle);
AddTicks(nsToCycles(static_cast<float>(
std::chrono::duration_cast<std::chrono::nanoseconds>(new_timer - adjust_value_last)
.count() /
cpu_clock_scale)));
++adjust_value_curr_handle;
}
s64 Timing::Timer::GetMaxSliceLength() const { s64 Timing::Timer::GetMaxSliceLength() const {
const auto& next_event = event_queue.begin(); const auto& next_event = event_queue.begin();
if (next_event != event_queue.end()) { if (next_event != event_queue.end()) {

View File

@ -203,6 +203,11 @@ public:
void MoveEvents(); void MoveEvents();
// Use these two functions to adjust the guest system tick on host blocking operations, so
// that the guest can tell how much time passed during the host call.
u32 StartAdjust();
void EndAdjust(u32 start_adjust_handle);
private: private:
friend class Timing; friend class Timing;
// The queue is a min-heap using std::make_heap/push_heap/pop_heap. // The queue is a min-heap using std::make_heap/push_heap/pop_heap.
@ -227,6 +232,9 @@ public:
s64 downcount = MAX_SLICE_LENGTH; s64 downcount = MAX_SLICE_LENGTH;
s64 executed_ticks = 0; s64 executed_ticks = 0;
u64 idled_cycles = 0; u64 idled_cycles = 0;
std::chrono::time_point<std::chrono::steady_clock> adjust_value_last;
u32 adjust_value_curr_handle = 0;
// Stores a scaling for the internal clockspeed. Changing this number results in // Stores a scaling for the internal clockspeed. Changing this number results in
// under/overclocking the guest cpu // under/overclocking the guest cpu
double cpu_clock_scale = 1.0; double cpu_clock_scale = 1.0;

View File

@ -221,19 +221,25 @@ struct CTRPollFD {
/// Translates the resulting events of a Poll operation from 3ds specific to platform /// Translates the resulting events of a Poll operation from 3ds specific to platform
/// specific /// specific
static u32 TranslateToPlatform(Events input_event) { static u32 TranslateToPlatform(Events input_event, bool isOutput) {
#if _WIN32
constexpr bool isWin = true;
#else
constexpr bool isWin = false;
#endif
u32 ret = 0; u32 ret = 0;
if (input_event.pollin) if (input_event.pollin)
ret |= POLLIN; ret |= POLLIN;
if (input_event.pollpri) if (input_event.pollpri && !isWin)
ret |= POLLPRI; ret |= POLLPRI;
if (input_event.pollhup) if (input_event.pollhup && (!isWin || isOutput))
ret |= POLLHUP; ret |= POLLHUP;
if (input_event.pollerr) if (input_event.pollerr && (!isWin || isOutput))
ret |= POLLERR; ret |= POLLERR;
if (input_event.pollout) if (input_event.pollout)
ret |= POLLOUT; ret |= POLLOUT;
if (input_event.pollnval) if (input_event.pollnval && (isWin && isOutput))
ret |= POLLNVAL; ret |= POLLNVAL;
return ret; return ret;
} }
@ -242,20 +248,26 @@ struct CTRPollFD {
Events revents; ///< Events received (output) Events revents; ///< Events received (output)
/// Converts a platform-specific pollfd to a 3ds specific structure /// Converts a platform-specific pollfd to a 3ds specific structure
static CTRPollFD FromPlatform(pollfd const& fd) { static CTRPollFD FromPlatform(SOC::SOC_U& socu, pollfd const& fd) {
CTRPollFD result; CTRPollFD result;
result.events.hex = Events::TranslateTo3DS(fd.events).hex; result.events.hex = Events::TranslateTo3DS(fd.events).hex;
result.revents.hex = Events::TranslateTo3DS(fd.revents).hex; result.revents.hex = Events::TranslateTo3DS(fd.revents).hex;
result.fd = static_cast<u32>(fd.fd); for (auto iter = socu.open_sockets.begin(); iter != socu.open_sockets.end(); ++iter) {
if (iter->second.socket_fd == fd.fd) {
result.fd = iter->first;
break;
}
}
return result; return result;
} }
/// Converts a 3ds specific pollfd to a platform-specific structure /// Converts a 3ds specific pollfd to a platform-specific structure
static pollfd ToPlatform(CTRPollFD const& fd) { static pollfd ToPlatform(SOC::SOC_U& socu, CTRPollFD const& fd) {
pollfd result; pollfd result;
result.events = Events::TranslateToPlatform(fd.events); result.events = Events::TranslateToPlatform(fd.events, false);
result.revents = Events::TranslateToPlatform(fd.revents); result.revents = Events::TranslateToPlatform(fd.revents, true);
result.fd = fd.fd; auto iter = socu.open_sockets.find(fd.fd);
result.fd = (iter != socu.open_sockets.end()) ? iter->second.socket_fd : 0;
return result; return result;
} }
}; };
@ -351,6 +363,14 @@ struct CTRAddrInfo {
static_assert(sizeof(CTRAddrInfo) == 0x130, "Size of CTRAddrInfo is not correct"); static_assert(sizeof(CTRAddrInfo) == 0x130, "Size of CTRAddrInfo is not correct");
void SOC_U::PreTimerAdjust() {
timer_adjust_handle = Core::System::GetInstance().GetRunningCore().GetTimer().StartAdjust();
}
void SOC_U::PostTimerAdjust() {
Core::System::GetInstance().GetRunningCore().GetTimer().EndAdjust(timer_adjust_handle);
}
void SOC_U::CleanupSockets() { void SOC_U::CleanupSockets() {
for (auto sock : open_sockets) for (auto sock : open_sockets)
closesocket(sock.second.socket_fd); closesocket(sock.second.socket_fd);
@ -385,21 +405,28 @@ void SOC_U::Socket(Kernel::HLERequestContext& ctx) {
return; return;
} }
u32 ret = static_cast<u32>(::socket(domain, type, protocol)); u64 ret = static_cast<u64>(::socket(domain, type, protocol));
u32 socketHandle = GetNextSocketID();
if ((s32)ret != SOCKET_ERROR_VALUE) if ((s64)ret != SOCKET_ERROR_VALUE)
open_sockets[ret] = {ret, true}; open_sockets[socketHandle] = {static_cast<decltype(SocketHolder::socket_fd)>(ret), true};
if ((s32)ret == SOCKET_ERROR_VALUE) if ((s64)ret == SOCKET_ERROR_VALUE)
ret = TranslateError(GET_ERRNO); ret = TranslateError(GET_ERRNO);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.Push(ret); rb.Push(socketHandle);
} }
void SOC_U::Bind(Kernel::HLERequestContext& ctx) { void SOC_U::Bind(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x05, 2, 4); IPC::RequestParser rp(ctx, 0x05, 2, 4);
u32 socket_handle = rp.Pop<u32>(); u32 socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
u32 len = rp.Pop<u32>(); u32 len = rp.Pop<u32>();
rp.PopPID(); rp.PopPID();
auto sock_addr_buf = rp.PopStaticBuffer(); auto sock_addr_buf = rp.PopStaticBuffer();
@ -409,7 +436,7 @@ void SOC_U::Bind(Kernel::HLERequestContext& ctx) {
sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr); sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr);
s32 ret = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len)); s32 ret = ::bind(fd_info->second.socket_fd, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
if (ret != 0) if (ret != 0)
ret = TranslateError(GET_ERRNO); ret = TranslateError(GET_ERRNO);
@ -422,6 +449,12 @@ void SOC_U::Bind(Kernel::HLERequestContext& ctx) {
void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) { void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x13, 3, 2); IPC::RequestParser rp(ctx, 0x13, 3, 2);
u32 socket_handle = rp.Pop<u32>(); u32 socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
u32 ctr_cmd = rp.Pop<u32>(); u32 ctr_cmd = rp.Pop<u32>();
u32 ctr_arg = rp.Pop<u32>(); u32 ctr_arg = rp.Pop<u32>();
rp.PopPID(); rp.PopPID();
@ -436,11 +469,10 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
if (ctr_cmd == 3) { // F_GETFL if (ctr_cmd == 3) { // F_GETFL
#ifdef _WIN32 #ifdef _WIN32
posix_ret = 0; posix_ret = 0;
auto iter = open_sockets.find(socket_handle); if (fd_info->second.blocking == false)
if (iter != open_sockets.end() && iter->second.blocking == false)
posix_ret |= 4; // O_NONBLOCK posix_ret |= 4; // O_NONBLOCK
#else #else
int ret = ::fcntl(socket_handle, F_GETFL, 0); int ret = ::fcntl(fd_info->second.socket_fd, F_GETFL, 0);
if (ret == SOCKET_ERROR_VALUE) { if (ret == SOCKET_ERROR_VALUE) {
posix_ret = TranslateError(GET_ERRNO); posix_ret = TranslateError(GET_ERRNO);
return; return;
@ -452,7 +484,7 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
} else if (ctr_cmd == 4) { // F_SETFL } else if (ctr_cmd == 4) { // F_SETFL
#ifdef _WIN32 #ifdef _WIN32
unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0; unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0;
int ret = ioctlsocket(socket_handle, FIONBIO, &tmp); int ret = ioctlsocket(fd_info->second.socket_fd, FIONBIO, &tmp);
if (ret == SOCKET_ERROR_VALUE) { if (ret == SOCKET_ERROR_VALUE) {
posix_ret = TranslateError(GET_ERRNO); posix_ret = TranslateError(GET_ERRNO);
return; return;
@ -461,7 +493,7 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
if (iter != open_sockets.end()) if (iter != open_sockets.end())
iter->second.blocking = (tmp == 0); iter->second.blocking = (tmp == 0);
#else #else
int flags = ::fcntl(socket_handle, F_GETFL, 0); int flags = ::fcntl(fd_info->second.socket_fd, F_GETFL, 0);
if (flags == SOCKET_ERROR_VALUE) { if (flags == SOCKET_ERROR_VALUE) {
posix_ret = TranslateError(GET_ERRNO); posix_ret = TranslateError(GET_ERRNO);
return; return;
@ -471,7 +503,7 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
if (ctr_arg & 4) // O_NONBLOCK if (ctr_arg & 4) // O_NONBLOCK
flags |= O_NONBLOCK; flags |= O_NONBLOCK;
int ret = ::fcntl(socket_handle, F_SETFL, flags); int ret = ::fcntl(fd_info->second.socket_fd, F_SETFL, flags);
if (ret == SOCKET_ERROR_VALUE) { if (ret == SOCKET_ERROR_VALUE) {
posix_ret = TranslateError(GET_ERRNO); posix_ret = TranslateError(GET_ERRNO);
return; return;
@ -487,10 +519,16 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
void SOC_U::Listen(Kernel::HLERequestContext& ctx) { void SOC_U::Listen(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x03, 2, 2); IPC::RequestParser rp(ctx, 0x03, 2, 2);
u32 socket_handle = rp.Pop<u32>(); u32 socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
u32 backlog = rp.Pop<u32>(); u32 backlog = rp.Pop<u32>();
rp.PopPID(); rp.PopPID();
s32 ret = ::listen(socket_handle, backlog); s32 ret = ::listen(fd_info->second.socket_fd, backlog);
if (ret != 0) if (ret != 0)
ret = TranslateError(GET_ERRNO); ret = TranslateError(GET_ERRNO);
@ -505,11 +543,19 @@ void SOC_U::Accept(Kernel::HLERequestContext& ctx) {
// performing nonblocking operations and spinlock until the data is available // performing nonblocking operations and spinlock until the data is available
IPC::RequestParser rp(ctx, 0x04, 2, 2); IPC::RequestParser rp(ctx, 0x04, 2, 2);
const auto socket_handle = rp.Pop<u32>(); const auto socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
[[maybe_unused]] const auto max_addr_len = static_cast<socklen_t>(rp.Pop<u32>()); [[maybe_unused]] const auto max_addr_len = static_cast<socklen_t>(rp.Pop<u32>());
rp.PopPID(); rp.PopPID();
sockaddr addr; sockaddr addr;
socklen_t addr_len = sizeof(addr); socklen_t addr_len = sizeof(addr);
u32 ret = static_cast<u32>(::accept(socket_handle, &addr, &addr_len)); PreTimerAdjust();
u32 ret = static_cast<u32>(::accept(fd_info->second.socket_fd, &addr, &addr_len));
PostTimerAdjust();
if (static_cast<s32>(ret) != SOCKET_ERROR_VALUE) { if (static_cast<s32>(ret) != SOCKET_ERROR_VALUE) {
open_sockets[ret] = {ret, true}; open_sockets[ret] = {ret, true};
@ -552,12 +598,21 @@ void SOC_U::GetHostId(Kernel::HLERequestContext& ctx) {
void SOC_U::Close(Kernel::HLERequestContext& ctx) { void SOC_U::Close(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x0B, 1, 2); IPC::RequestParser rp(ctx, 0x0B, 1, 2);
u32 socket_handle = rp.Pop<u32>(); u32 socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
rp.PopPID(); rp.PopPID();
s32 ret = 0; s32 ret = 0;
open_sockets.erase(socket_handle);
ret = closesocket(socket_handle); PreTimerAdjust();
ret = closesocket(fd_info->second.socket_fd);
PostTimerAdjust();
open_sockets.erase(socket_handle);
if (ret != 0) if (ret != 0)
ret = TranslateError(GET_ERRNO); ret = TranslateError(GET_ERRNO);
@ -570,6 +625,12 @@ void SOC_U::Close(Kernel::HLERequestContext& ctx) {
void SOC_U::SendTo(Kernel::HLERequestContext& ctx) { void SOC_U::SendTo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x0A, 4, 6); IPC::RequestParser rp(ctx, 0x0A, 4, 6);
u32 socket_handle = rp.Pop<u32>(); u32 socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
u32 len = rp.Pop<u32>(); u32 len = rp.Pop<u32>();
u32 flags = rp.Pop<u32>(); u32 flags = rp.Pop<u32>();
u32 addr_len = rp.Pop<u32>(); u32 addr_len = rp.Pop<u32>();
@ -578,16 +639,18 @@ void SOC_U::SendTo(Kernel::HLERequestContext& ctx) {
auto dest_addr_buff = rp.PopStaticBuffer(); auto dest_addr_buff = rp.PopStaticBuffer();
s32 ret = -1; s32 ret = -1;
PreTimerAdjust();
if (addr_len > 0) { if (addr_len > 0) {
CTRSockAddr ctr_dest_addr; CTRSockAddr ctr_dest_addr;
std::memcpy(&ctr_dest_addr, dest_addr_buff.data(), sizeof(ctr_dest_addr)); std::memcpy(&ctr_dest_addr, dest_addr_buff.data(), sizeof(ctr_dest_addr));
sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr); sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
ret = ::sendto(socket_handle, reinterpret_cast<const char*>(input_buff.data()), len, flags, ret = ::sendto(fd_info->second.socket_fd, reinterpret_cast<const char*>(input_buff.data()),
&dest_addr, sizeof(dest_addr)); len, flags, &dest_addr, sizeof(dest_addr));
} else { } else {
ret = ::sendto(socket_handle, reinterpret_cast<const char*>(input_buff.data()), len, flags, ret = ::sendto(fd_info->second.socket_fd, reinterpret_cast<const char*>(input_buff.data()),
nullptr, 0); len, flags, nullptr, 0);
} }
PostTimerAdjust();
if (ret == SOCKET_ERROR_VALUE) if (ret == SOCKET_ERROR_VALUE)
ret = TranslateError(GET_ERRNO); ret = TranslateError(GET_ERRNO);
@ -600,6 +663,12 @@ void SOC_U::SendTo(Kernel::HLERequestContext& ctx) {
void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) { void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x7, 4, 4); IPC::RequestParser rp(ctx, 0x7, 4, 4);
u32 socket_handle = rp.Pop<u32>(); u32 socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
u32 len = rp.Pop<u32>(); u32 len = rp.Pop<u32>();
u32 flags = rp.Pop<u32>(); u32 flags = rp.Pop<u32>();
u32 addr_len = rp.Pop<u32>(); u32 addr_len = rp.Pop<u32>();
@ -613,19 +682,20 @@ void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) {
socklen_t src_addr_len = sizeof(src_addr); socklen_t src_addr_len = sizeof(src_addr);
s32 ret = -1; s32 ret = -1;
PreTimerAdjust();
if (addr_len > 0) { if (addr_len > 0) {
ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags, ret = ::recvfrom(fd_info->second.socket_fd, reinterpret_cast<char*>(output_buff.data()),
&src_addr, &src_addr_len); len, flags, &src_addr, &src_addr_len);
if (ret >= 0 && src_addr_len > 0) { if (ret >= 0 && src_addr_len > 0) {
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr); ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
std::memcpy(addr_buff.data(), &ctr_src_addr, sizeof(ctr_src_addr)); std::memcpy(addr_buff.data(), &ctr_src_addr, sizeof(ctr_src_addr));
} }
} else { } else {
ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags, ret = ::recvfrom(fd_info->second.socket_fd, reinterpret_cast<char*>(output_buff.data()),
NULL, 0); len, flags, NULL, 0);
addr_buff.resize(0); addr_buff.resize(0);
} }
PostTimerAdjust();
if (ret == SOCKET_ERROR_VALUE) { if (ret == SOCKET_ERROR_VALUE) {
ret = TranslateError(GET_ERRNO); ret = TranslateError(GET_ERRNO);
} else { } else {
@ -645,6 +715,12 @@ void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) {
// performing nonblocking operations and spinlock until the data is available // performing nonblocking operations and spinlock until the data is available
IPC::RequestParser rp(ctx, 0x08, 4, 2); IPC::RequestParser rp(ctx, 0x08, 4, 2);
u32 socket_handle = rp.Pop<u32>(); u32 socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
u32 len = rp.Pop<u32>(); u32 len = rp.Pop<u32>();
u32 flags = rp.Pop<u32>(); u32 flags = rp.Pop<u32>();
u32 addr_len = rp.Pop<u32>(); u32 addr_len = rp.Pop<u32>();
@ -657,19 +733,21 @@ void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) {
socklen_t src_addr_len = sizeof(src_addr); socklen_t src_addr_len = sizeof(src_addr);
s32 ret = -1; s32 ret = -1;
PreTimerAdjust();
if (addr_len > 0) { if (addr_len > 0) {
// Only get src adr if input adr available // Only get src adr if input adr available
ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags, ret = ::recvfrom(fd_info->second.socket_fd, reinterpret_cast<char*>(output_buff.data()),
&src_addr, &src_addr_len); len, flags, &src_addr, &src_addr_len);
if (ret >= 0 && src_addr_len > 0) { if (ret >= 0 && src_addr_len > 0) {
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr); ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
std::memcpy(addr_buff.data(), &ctr_src_addr, sizeof(ctr_src_addr)); std::memcpy(addr_buff.data(), &ctr_src_addr, sizeof(ctr_src_addr));
} }
} else { } else {
ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags, ret = ::recvfrom(fd_info->second.socket_fd, reinterpret_cast<char*>(output_buff.data()),
NULL, 0); len, flags, NULL, 0);
addr_buff.resize(0); addr_buff.resize(0);
} }
PostTimerAdjust();
s32 total_received = ret; s32 total_received = ret;
if (ret == SOCKET_ERROR_VALUE) { if (ret == SOCKET_ERROR_VALUE) {
@ -700,21 +778,32 @@ void SOC_U::Poll(Kernel::HLERequestContext& ctx) {
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different // The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different
// sizes) // sizes)
// so we have to copy the data // so we have to copy the data in order
std::vector<pollfd> platform_pollfd(nfds); std::vector<pollfd> platform_pollfd(nfds);
std::transform(ctr_fds.begin(), ctr_fds.end(), platform_pollfd.begin(), CTRPollFD::ToPlatform); for (u32 i = 0; i < nfds; i++) {
platform_pollfd[i] = CTRPollFD::ToPlatform(*this, ctr_fds[i]);
}
PreTimerAdjust();
s32 ret = ::poll(platform_pollfd.data(), nfds, timeout); s32 ret = ::poll(platform_pollfd.data(), nfds, timeout);
PostTimerAdjust();
// Now update the output pollfd structure // Now update the output 3ds_pollfd structure
std::transform(platform_pollfd.begin(), platform_pollfd.end(), ctr_fds.begin(), for (u32 i = 0; i < nfds; i++) {
CTRPollFD::FromPlatform); ctr_fds[i] = CTRPollFD::FromPlatform(*this, platform_pollfd[i]);
}
std::vector<u8> output_fds(nfds * sizeof(CTRPollFD)); std::vector<u8> output_fds(nfds * sizeof(CTRPollFD));
std::memcpy(output_fds.data(), ctr_fds.data(), nfds * sizeof(CTRPollFD)); std::memcpy(output_fds.data(), ctr_fds.data(), nfds * sizeof(CTRPollFD));
if (ret == SOCKET_ERROR_VALUE) if (ret == SOCKET_ERROR_VALUE) {
int err = GET_ERRNO;
LOG_ERROR(Service_SOC, "Socket error: {}", err);
ret = TranslateError(GET_ERRNO); ret = TranslateError(GET_ERRNO);
}
size_t test = platform_pollfd.size();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
@ -725,12 +814,18 @@ void SOC_U::Poll(Kernel::HLERequestContext& ctx) {
void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) { void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x17, 2, 2); IPC::RequestParser rp(ctx, 0x17, 2, 2);
const auto socket_handle = rp.Pop<u32>(); const auto socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
[[maybe_unused]] const auto max_addr_len = rp.Pop<u32>(); [[maybe_unused]] const auto max_addr_len = rp.Pop<u32>();
rp.PopPID(); rp.PopPID();
sockaddr dest_addr; sockaddr dest_addr;
socklen_t dest_addr_len = sizeof(dest_addr); socklen_t dest_addr_len = sizeof(dest_addr);
s32 ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len); s32 ret = ::getsockname(fd_info->second.socket_fd, &dest_addr, &dest_addr_len);
CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr); CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
std::vector<u8> dest_addr_buff(sizeof(ctr_dest_addr)); std::vector<u8> dest_addr_buff(sizeof(ctr_dest_addr));
@ -748,10 +843,16 @@ void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) {
void SOC_U::Shutdown(Kernel::HLERequestContext& ctx) { void SOC_U::Shutdown(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x0C, 2, 2); IPC::RequestParser rp(ctx, 0x0C, 2, 2);
u32 socket_handle = rp.Pop<u32>(); u32 socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
s32 how = rp.Pop<s32>(); s32 how = rp.Pop<s32>();
rp.PopPID(); rp.PopPID();
s32 ret = ::shutdown(socket_handle, how); s32 ret = ::shutdown(fd_info->second.socket_fd, how);
if (ret != 0) if (ret != 0)
ret = TranslateError(GET_ERRNO); ret = TranslateError(GET_ERRNO);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
@ -762,12 +863,18 @@ void SOC_U::Shutdown(Kernel::HLERequestContext& ctx) {
void SOC_U::GetPeerName(Kernel::HLERequestContext& ctx) { void SOC_U::GetPeerName(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x18, 2, 2); IPC::RequestParser rp(ctx, 0x18, 2, 2);
const auto socket_handle = rp.Pop<u32>(); const auto socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
[[maybe_unused]] const auto max_addr_len = rp.Pop<u32>(); [[maybe_unused]] const auto max_addr_len = rp.Pop<u32>();
rp.PopPID(); rp.PopPID();
sockaddr dest_addr; sockaddr dest_addr;
socklen_t dest_addr_len = sizeof(dest_addr); socklen_t dest_addr_len = sizeof(dest_addr);
const int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len); const int ret = ::getpeername(fd_info->second.socket_fd, &dest_addr, &dest_addr_len);
CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr); CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
std::vector<u8> dest_addr_buff(sizeof(ctr_dest_addr)); std::vector<u8> dest_addr_buff(sizeof(ctr_dest_addr));
@ -790,6 +897,12 @@ void SOC_U::Connect(Kernel::HLERequestContext& ctx) {
// performing nonblocking operations and spinlock until the data is available // performing nonblocking operations and spinlock until the data is available
IPC::RequestParser rp(ctx, 0x06, 2, 4); IPC::RequestParser rp(ctx, 0x06, 2, 4);
const auto socket_handle = rp.Pop<u32>(); const auto socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
[[maybe_unused]] const auto input_addr_len = rp.Pop<u32>(); [[maybe_unused]] const auto input_addr_len = rp.Pop<u32>();
rp.PopPID(); rp.PopPID();
auto input_addr_buf = rp.PopStaticBuffer(); auto input_addr_buf = rp.PopStaticBuffer();
@ -798,7 +911,9 @@ void SOC_U::Connect(Kernel::HLERequestContext& ctx) {
std::memcpy(&ctr_input_addr, input_addr_buf.data(), sizeof(ctr_input_addr)); std::memcpy(&ctr_input_addr, input_addr_buf.data(), sizeof(ctr_input_addr));
sockaddr input_addr = CTRSockAddr::ToPlatform(ctr_input_addr); sockaddr input_addr = CTRSockAddr::ToPlatform(ctr_input_addr);
s32 ret = ::connect(socket_handle, &input_addr, sizeof(input_addr)); PreTimerAdjust();
s32 ret = ::connect(fd_info->second.socket_fd, &input_addr, sizeof(input_addr));
PostTimerAdjust();
if (ret != 0) if (ret != 0)
ret = TranslateError(GET_ERRNO); ret = TranslateError(GET_ERRNO);
@ -830,6 +945,12 @@ void SOC_U::ShutdownSockets(Kernel::HLERequestContext& ctx) {
void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) { void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x11, 4, 2); IPC::RequestParser rp(ctx, 0x11, 4, 2);
u32 socket_handle = rp.Pop<u32>(); u32 socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
u32 level = rp.Pop<u32>(); u32 level = rp.Pop<u32>();
s32 optname = rp.Pop<s32>(); s32 optname = rp.Pop<s32>();
socklen_t optlen = static_cast<socklen_t>(rp.Pop<u32>()); socklen_t optlen = static_cast<socklen_t>(rp.Pop<u32>());
@ -847,7 +968,7 @@ void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) {
#endif #endif
} else { } else {
char* optval_data = reinterpret_cast<char*>(optval.data()); char* optval_data = reinterpret_cast<char*>(optval.data());
err = ::getsockopt(socket_handle, level, optname, optval_data, &optlen); err = ::getsockopt(fd_info->second.socket_fd, level, optname, optval_data, &optlen);
if (err == SOCKET_ERROR_VALUE) { if (err == SOCKET_ERROR_VALUE) {
err = TranslateError(GET_ERRNO); err = TranslateError(GET_ERRNO);
} }
@ -863,6 +984,12 @@ void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) {
void SOC_U::SetSockOpt(Kernel::HLERequestContext& ctx) { void SOC_U::SetSockOpt(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x12, 4, 4); IPC::RequestParser rp(ctx, 0x12, 4, 4);
const auto socket_handle = rp.Pop<u32>(); const auto socket_handle = rp.Pop<u32>();
auto fd_info = open_sockets.find(socket_handle);
if (fd_info == open_sockets.end()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_HANDLE);
return;
}
const auto level = rp.Pop<u32>(); const auto level = rp.Pop<u32>();
const auto optname = rp.Pop<s32>(); const auto optname = rp.Pop<s32>();
[[maybe_unused]] const auto optlen = static_cast<socklen_t>(rp.Pop<u32>()); [[maybe_unused]] const auto optlen = static_cast<socklen_t>(rp.Pop<u32>());
@ -879,7 +1006,7 @@ void SOC_U::SetSockOpt(Kernel::HLERequestContext& ctx) {
#endif #endif
} else { } else {
const char* optval_data = reinterpret_cast<const char*>(optval.data()); const char* optval_data = reinterpret_cast<const char*>(optval.data());
err = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval_data, err = static_cast<u32>(::setsockopt(fd_info->second.socket_fd, level, optname, optval_data,
static_cast<socklen_t>(optval.size()))); static_cast<socklen_t>(optval.size())));
if (err == SOCKET_ERROR_VALUE) { if (err == SOCKET_ERROR_VALUE) {
err = TranslateError(GET_ERRNO); err = TranslateError(GET_ERRNO);

View File

@ -6,6 +6,7 @@
#include <unordered_map> #include <unordered_map>
#include <boost/serialization/unordered_map.hpp> #include <boost/serialization/unordered_map.hpp>
#include "core/hle/result.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
namespace Core { namespace Core {
@ -16,7 +17,13 @@ namespace Service::SOC {
/// Holds information about a particular socket /// Holds information about a particular socket
struct SocketHolder { struct SocketHolder {
#ifdef _WIN32
using SOCKET = unsigned long long;
SOCKET socket_fd; ///< The socket descriptor
#else
u32 socket_fd; ///< The socket descriptor u32 socket_fd; ///< The socket descriptor
#endif // _WIN32
bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows. bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows.
private: private:
@ -34,6 +41,10 @@ public:
~SOC_U(); ~SOC_U();
private: private:
static constexpr ResultCode ERR_INVALID_HANDLE =
ResultCode(ErrorDescription::InvalidHandle, ErrorModule::SOC, ErrorSummary::InvalidArgument,
ErrorLevel::Permanent);
void Socket(Kernel::HLERequestContext& ctx); void Socket(Kernel::HLERequestContext& ctx);
void Bind(Kernel::HLERequestContext& ctx); void Bind(Kernel::HLERequestContext& ctx);
void Fcntl(Kernel::HLERequestContext& ctx); void Fcntl(Kernel::HLERequestContext& ctx);
@ -59,16 +70,29 @@ private:
void GetAddrInfoImpl(Kernel::HLERequestContext& ctx); void GetAddrInfoImpl(Kernel::HLERequestContext& ctx);
void GetNameInfoImpl(Kernel::HLERequestContext& ctx); void GetNameInfoImpl(Kernel::HLERequestContext& ctx);
// Socked ids
u32 next_socket_id = 3;
u32 GetNextSocketID() {
return next_socket_id++;
}
// System timer adjust
u32 timer_adjust_handle;
void PreTimerAdjust();
void PostTimerAdjust();
/// Close all open sockets /// Close all open sockets
void CleanupSockets(); void CleanupSockets();
/// Holds info about the currently open sockets /// Holds info about the currently open sockets
friend struct CTRPollFD;
std::unordered_map<u32, SocketHolder> open_sockets; std::unordered_map<u32, SocketHolder> open_sockets;
template <class Archive> template <class Archive>
void serialize(Archive& ar, const unsigned int) { void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this); ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
ar& open_sockets; ar& open_sockets;
ar& timer_adjust_handle;
} }
friend class boost::serialization::access; friend class boost::serialization::access;
}; };