Compare commits

..

58 Commits

Author SHA1 Message Date
lizzie 2d493dfff2 2026-09-06 23:28:49
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-06 23:28:49 +00:00
lizzie 59c2567cc1 2026-09-06 23:28:07
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-06 23:28:07 +00:00
lizzie 703a428254 ok windows 2026-09-06 06:03:20 +00:00
lizzie ff7bfceb3a ok no sin len 2026-09-06 06:03:20 +00:00
lizzie 2eb1d0ab99 fix bsd macro clash 2026-09-06 06:03:20 +00:00
lizzie 1cbc9a11f9 fix horrid lag on hb appstore 2026-09-06 06:03:20 +00:00
lizzie 6a07b35488 Trigger Build 2026-09-06 06:03:20 +00:00
lizzie a3ae683b6f Trigger Build 2026-09-06 06:03:20 +00:00
lizzie f1ae2d7300 no 2026-09-06 06:03:20 +00:00
lizzie 4e2ed8d40b me when evil android 2026-09-06 06:03:20 +00:00
lizzie 60fe25a20e fixups 2026-09-06 06:03:20 +00:00
lizzie 81bd4b0ae1 fix windows wsa E_DESTADDRREQ 2026-09-06 06:03:19 +00:00
lizzie ed06af3ff1 fucking shit 2026-09-06 06:03:19 +00:00
lizzie 4334740f57 windows prot=0 2026-09-06 06:03:19 +00:00
lizzie a4ac018bd0 fix pings? 2026-09-06 06:03:19 +00:00
lizzie f860d103dd fix winbloat 2026-09-06 06:03:19 +00:00
lizzie a25455ffce fractional time -W 2026-09-06 06:03:19 +00:00
lizzie 2f01c08ce7 ffs windows 2026-09-06 06:03:19 +00:00
lizzie 9e33da14d5 evil noperm 2026-09-06 06:03:19 +00:00
lizzie 9f8187b225 fuck errrno2 2026-09-06 06:03:19 +00:00
lizzie f074be4924 acurater errno 2026-09-06 06:03:19 +00:00
lizzie ccd5b7f245 ALL THE ERROR CODES 2026-09-06 06:03:19 +00:00
lizzie dd4c5ffb4f fixup errno 2026-09-06 06:03:19 +00:00
lizzie d25bc074e9 min of 1sec 2026-09-06 06:03:19 +00:00
lizzie 14a6c76931 account for timeout 2026-09-06 06:03:19 +00:00
lizzie d8f501049a fix tcp on ssbu mod 2026-09-06 06:03:19 +00:00
lizzie 5e3a7e8afa fallback only if it failed 2026-09-06 06:03:19 +00:00
lizzie 7a43448235 implode windows 2026-09-06 06:03:19 +00:00
lizzie 265cae12b2 better 2026-09-06 06:03:19 +00:00
lizzie 4ff81360f4 evil ping 2026-09-06 06:03:19 +00:00
lizzie 592bb45db9 don't murder me for this change 2026-09-06 06:03:19 +00:00
lizzie 8c5414e443 need the extra threads... 2026-09-06 06:03:19 +00:00
lizzie d4c846bf66 more gymnastics to give fake results? 2026-09-06 06:03:19 +00:00
lizzie 0db4c364e5 first fake icmp impl 2026-09-06 06:03:19 +00:00
lizzie 9f6f3eb4f1 fix ldn sockets being regarded as normal socks 2026-09-06 06:03:19 +00:00
lizzie 17a89c407a fx2 2026-09-06 06:03:19 +00:00
lizzie fee8bd2d9f fix??? 2026-09-06 06:03:19 +00:00
lizzie 0110e00b7c fix sockets 2026-09-06 06:03:19 +00:00
lizzie 0096fc097e struct pollfd 2026-09-06 06:03:19 +00:00
lizzie 5b575533f7 fixup tests 2026-09-06 06:03:19 +00:00
lizzie b6b1a4556a fix windows screaming 2026-09-06 06:03:19 +00:00
lizzie 8ce42d6cda musl scared of defines ooo 2026-09-06 06:03:18 +00:00
lizzie 29ca078dd6 airplane mode takes priority 2026-09-06 06:03:18 +00:00
lizzie b9a2f5f272 uh 2026-09-06 06:03:18 +00:00
lizzie 5eb5ba89a2 add tcp options 2026-09-06 06:03:18 +00:00
lizzie df8647c724 nuke extra threads, extra error support; use span instead of vector for poll fds 2026-09-06 06:03:18 +00:00
lizzie 3ed19a4b93 we love when cURL has bugs? 2026-09-06 06:03:18 +00:00
lizzie 16b1a2fd6f add extra polling types 2026-09-06 06:03:18 +00:00
lizzie 45765cb60d various fixes to sockopt 2026-09-06 06:03:18 +00:00
lizzie 15df5a9ea3 allow configure socket level 2026-09-06 06:03:18 +00:00
lizzie 0b5b5354fb coalesce the various sockopt 2026-09-06 06:03:18 +00:00
lizzie dfad814334 honour the provided MSG flags 2026-09-06 06:03:18 +00:00
lizzie 258d5f3bba extra fixups 2026-09-06 06:03:18 +00:00
lizzie 5cde479ab8 fixup stuffs for windows 2026-09-06 06:03:18 +00:00
lizzie cd8937ec05 fix messages native flags 2026-09-06 06:03:18 +00:00
lizzie e520d2937a properly handle writebuffer and dont write OOB 2026-09-06 06:03:18 +00:00
lizzie 0534478009 fixes for non BSD 2026-09-06 06:03:18 +00:00
lizzie 740b718549 [net] refactor to remove uneeded abstraction layer
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-06 06:03:18 +00:00
48 changed files with 1961 additions and 2846 deletions
-1
View File
@@ -108,7 +108,6 @@ add_library(
settings_input.h settings_input.h
settings_setting.h settings_setting.h
slot_vector.h slot_vector.h
socket_types.h
spin_lock.h spin_lock.h
stb.cpp stb.cpp
stb.h stb.h
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project // SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -11,7 +11,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/socket_types.h" #include "core/internal_network/socket_types.h"
#include "web_service/web_result.h" #include "web_service/web_result.h"
namespace AnnounceMultiplayerRoom { namespace AnnounceMultiplayerRoom {
-178
View File
@@ -1,178 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <string>
#include "common/common_types.h"
namespace Network {
/// Address families
enum class Domain : u8 {
Unspecified, ///< Represents 0, used in getaddrinfo hints
INET, ///< Address family for IPv4
};
/// Socket types
enum class Type {
Unspecified, ///< Represents 0, used in getaddrinfo hints
STREAM,
DGRAM,
RAW,
SEQPACKET,
};
/// Protocol values for sockets
enum class Protocol : u8 {
Unspecified, ///< Represents 0, usable in various places
IP,
ICMP,
TCP,
UDP,
IPV6,
RAW,
IGMP,
GGP,
IPV4,
ST,
EGP,
PIGP,
RCCMON,
NVPII,
PUP,
ARGUS,
EMCON,
XNET,
CHAOS,
MUX,
MEAS,
HMP,
PRM,
IDP,
TRUNK1,
TRUNK2,
LEAF1,
LEAF2,
RDP,
IRTP,
TP,
BLT,
NSP,
INP,
DCCP,
//TODO: 3PC,
IDPR,
XTP,
DDP,
CMTP,
TPXX,
IL,
SDRP,
ROUTING,
FRAGMENT,
IDRP,
RSVP,
GRE,
MHRP,
BHA,
ESP,
AH,
INLSP,
SWIPE,
NHRP,
MOBILE,
TLSP,
SKIP,
ICMPV6,
NONE,
DSTOPTS,
AHIP,
CFTP,
HELLO,
SATEXPAK,
KRYPTOLAN,
RVD,
IPPC,
ADFS,
SATMON,
VISA,
IPCV,
CPNX,
CPHB,
WSN,
PVP,
BRSATMON,
ND,
WBMON,
WBEXPAK,
EON,
VMTP,
SVMTP,
VINES,
TTP,
IGP,
DGP,
TCF,
IGRP,
OSPFIGP,
SRPC,
LARP,
MTP,
AX25,
IPEIP,
MICP,
SCCSP,
ETHERIP,
ENCAP,
APES,
GMTP,
IPCOMP,
SCTP,
MH,
UDPLITE,
HIP,
SHIM6,
PIM,
CARP,
PGM,
MPLS,
PFSYNC
};
/// Shutdown mode
enum class ShutdownHow {
RD,
WR,
RDWR,
};
/// Array of IPv4 address
using IPv4Address = std::array<u8, 4>;
/// Cross-platform sockaddr structure
struct SockAddrIn {
Domain family;
IPv4Address ip;
u16 portno;
};
constexpr u32 FLAG_MSG_PEEK = 0x2;
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
constexpr u32 FLAG_O_NONBLOCK = 0x800;
/// Cross-platform addrinfo structure
struct AddrInfo {
Domain family;
Type socket_type;
Protocol protocol;
SockAddrIn addr;
std::optional<std::string> canon_name;
};
} // namespace Network
+7
View File
@@ -1129,6 +1129,7 @@ add_library(core STATIC
internal_network/network_interface.h internal_network/network_interface.h
internal_network/socket_proxy.cpp internal_network/socket_proxy.cpp
internal_network/socket_proxy.h internal_network/socket_proxy.h
internal_network/socket_types.h
internal_network/sockets.h internal_network/sockets.h
internal_network/wifi_scanner.h internal_network/wifi_scanner.h
launch_timestamp_cache.cpp launch_timestamp_cache.cpp
@@ -1167,6 +1168,12 @@ add_library(core STATIC
tools/renderdoc.cpp tools/renderdoc.cpp
tools/renderdoc.h) tools/renderdoc.h)
if (UNIX AND NOT APPLE)
target_sources(core PRIVATE
internal_network/socket_icmp.cpp
internal_network/socket_icmp.h)
endif()
if (ENABLE_WIFI_SCAN) if (ENABLE_WIFI_SCAN)
target_sources(core PRIVATE internal_network/wifi_scanner.cpp) target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
if (LINUX) if (LINUX)
+1 -1
View File
@@ -18,7 +18,7 @@
#include "common/container/unordered_map.h" #include "common/container/unordered_map.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/socket_types.h" #include "core/internal_network/socket_types.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/ldn/ldn_results.h" #include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/ldn_types.h" #include "core/hle/service/ldn/ldn_types.h"
+7 -8
View File
@@ -129,6 +129,13 @@ ServerManager::~ServerManager() {
} }
} }
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
for (size_t i = 0; i < num_threads; i++) {
auto thread_name = fmt::format("{}:{}", name, i + 1);
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(std::move(thread_name), [&] { this->LoopProcessImpl(); }));
}
}
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) { void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
server_manager->m_system.RunServer(std::move(server_manager)); server_manager->m_system.RunServer(std::move(server_manager));
} }
@@ -245,14 +252,6 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
R_SUCCEED(); R_SUCCEED();
} }
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
for (size_t i = 0; i < num_threads; i++) {
auto thread_name = fmt::format("{}:{}", name, i + 1);
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(
std::move(thread_name), [&] { this->LoopProcessImpl(); }));
}
}
Result ServerManager::LoopProcess() { Result ServerManager::LoopProcess() {
SCOPE_EXIT { SCOPE_EXIT {
m_stopped.Set(); m_stopped.Set();
+4 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -48,8 +51,8 @@ public:
Result ManageDeferral(Kernel::KEvent** out_event); Result ManageDeferral(Kernel::KEvent** out_event);
Result LoopProcess(); Result LoopProcess();
void StartAdditionalHostThreads(const char* name, size_t num_threads);
void StartAdditionalHostThreads(const char* name, size_t num_threads);
static void RunServer(std::unique_ptr<ServerManager>&& server); static void RunServer(std::unique_ptr<ServerManager>&& server);
private: private:
+250 -288
View File
@@ -12,7 +12,7 @@
#include <fmt/ranges.h> #include <fmt/ranges.h>
#include "common/logging.h" #include "common/logging.h"
#include "common/socket_types.h" #include "core/internal_network/socket_types.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
@@ -20,6 +20,9 @@
#include "core/hle/service/sockets/sockets_translate.h" #include "core/hle/service/sockets/sockets_translate.h"
#include "core/internal_network/network.h" #include "core/internal_network/network.h"
#include "core/internal_network/socket_proxy.h" #include "core/internal_network/socket_proxy.h"
#if defined(__unix__) && !defined(__APPLE__)
#include "core/internal_network/socket_icmp.h"
#endif
#include "core/internal_network/sockets.h" #include "core/internal_network/sockets.h"
#include "network/network.h" #include "network/network.h"
#include <common/settings.h> #include <common/settings.h>
@@ -28,15 +31,18 @@ namespace Service::Sockets {
namespace { namespace {
bool IsConnectionBased(Type type) { [[nodiscard]] bool IsConnectionBased(Network::Type type) noexcept {
switch (type) { switch (type) {
case Type::STREAM: case Network::Type::STREAM:
case Network::Type::SEQPACKET:
return true; return true;
case Type::DGRAM: case Network::Type::RAW:
case Network::Type::DGRAM:
case Network::Type::RDM:
case Network::Type::Unspecified:
return false; return false;
default: default:
UNIMPLEMENTED_MSG("Unimplemented type={}", type); UNREACHABLE();
return false;
} }
} }
@@ -92,7 +98,7 @@ void BSD_USA::ConnectWork::Execute(BSD_USA* bsd) {
void BSD_USA::ConnectWork::Response(HLERequestContext& ctx) { void BSD_USA::ConnectWork::Response(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1); rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
} }
@@ -170,10 +176,9 @@ void BSD_USA::Socket(HLERequestContext& ctx) {
const u32 domain = rp.Pop<u32>(); const u32 domain = rp.Pop<u32>();
const u32 type = rp.Pop<u32>(); const u32 type = rp.Pop<u32>();
const u32 protocol = rp.Pop<u32>(); const u32 protocol = rp.Pop<u32>();
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol); LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
const auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol)); const auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@@ -189,8 +194,8 @@ void BSD_USA::SocketExempt(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol); LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol)); auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
if (bsd_errno == Errno::SUCCESS) { if (bsd_errno == Network::Errno::E_SUCCESS) {
bsd_errno = ShutdownImpl(fd, 0); bsd_errno = ShutdownImpl(fd, 0);
} }
@@ -264,13 +269,13 @@ void BSD_USA::GetPeerName(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={}", fd); LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize()); std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer); const Network::Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer); ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5}; IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0); rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size())); rb.Push<u32>(static_cast<u32>(write_buffer.size()));
} }
@@ -282,13 +287,13 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={}", fd); LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize()); std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer); const Network::Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer); ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5}; IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0); rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size())); rb.Push<u32>(static_cast<u32>(write_buffer.size()));
} }
@@ -296,21 +301,19 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
void BSD_USA::GetSockOpt(HLERequestContext& ctx) { void BSD_USA::GetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
const u32 level = rp.Pop<u32>(); const auto level = Network::SocketLevel(rp.Pop<u32>());
const auto optname = static_cast<OptName>(rp.Pop<u32>()); const auto optname = Network::OptName(rp.Pop<u32>());
std::vector<u8> optval(ctx.GetWriteBufferSize()); std::vector<u8> optval(ctx.GetWriteBufferSize());
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, optval.size());
optval.size()); const Network::Errno err = GetSockOptImpl(fd, level, optname, optval);
const Errno err = GetSockOptImpl(fd, level, optname, optval);
ctx.WriteBuffer(optval); ctx.WriteBuffer(optval);
IPC::ResponseBuilder rb{ctx, 5}; IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1); rb.Push<s32>(err == Network::Errno::E_SUCCESS ? 0 : -1);
rb.PushEnum(err); rb.PushEnum(err);
rb.Push<u32>(static_cast<u32>(optval.size())); rb.Push<u32>(static_cast<u32>(optval.size()));
} }
@@ -333,7 +336,7 @@ void BSD_USA::Fcntl(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg); LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg); const auto [ret, bsd_errno] = FcntlImpl(fd, Network::FcntlCmd(cmd), arg);
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@@ -345,13 +348,11 @@ void BSD_USA::SetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
const u32 level = rp.Pop<u32>(); const Network::SocketLevel level = Network::SocketLevel(rp.Pop<u32>());
const OptName optname = static_cast<OptName>(rp.Pop<u32>()); const Network::OptName optname = Network::OptName(rp.Pop<u32>());
const auto optval = ctx.ReadBuffer(); const auto optval = ctx.ReadBuffer();
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, u32(optname), optval.size());
static_cast<u32>(optname), optval.size());
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval)); BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
} }
@@ -473,7 +474,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
struct OutputParameters { struct OutputParameters {
s32 ret; s32 ret;
Errno bsd_errno; Network::Errno bsd_errno;
}; };
static_assert(sizeof(OutputParameters) == 0x8); static_assert(sizeof(OutputParameters) == 0x8);
@@ -485,7 +486,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
if (is_user) { if (is_user) {
rb.PushRaw(OutputParameters{ rb.PushRaw(OutputParameters{
.ret = 0, .ret = 0,
.bsd_errno = Errno::INVAL, .bsd_errno = Network::Errno::E_INVAL,
}); });
return; return;
} }
@@ -494,10 +495,10 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
if (auto* res = std::get_if<s32>(&res_v)) { if (auto* res = std::get_if<s32>(&res_v)) {
rb.PushRaw(OutputParameters{ rb.PushRaw(OutputParameters{
.ret = *res, .ret = *res,
.bsd_errno = Errno::SUCCESS, .bsd_errno = Network::Errno::E_SUCCESS,
}); });
} else { } else {
auto* err = std::get_if<Errno>(&res_v); auto* err = std::get_if<Network::Errno>(&res_v);
rb.PushRaw(OutputParameters{ rb.PushRaw(OutputParameters{
.ret = 0, .ret = 0,
.bsd_errno = *err, .bsd_errno = *err,
@@ -512,7 +513,7 @@ void BSD_USA::EventFd(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags); LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
BuildErrnoResponse(ctx, Errno::SUCCESS); BuildErrnoResponse(ctx, Network::Errno::E_SUCCESS);
} }
template <typename Work> template <typename Work>
@@ -521,132 +522,143 @@ void BSD_USA::ExecuteWork(HLERequestContext& ctx, Work work) {
work.Response(ctx); work.Response(ctx);
} }
std::pair<s32, Errno> BSD_USA::SocketImpl(Domain domain, Type type, Protocol protocol) { std::pair<s32, Network::Errno> BSD_USA::SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol) {
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW // user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) { LOG_DEBUG(Network, "domain={},type={},protocol={}", u32(domain), u32(type), u32(protocol));
if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) { if (is_user && (type == Network::Type::SEQPACKET || type == Network::Type::RAW)) {
if (type == Network::Type::RAW && domain == Network::Domain::INET && protocol == Network::Protocol::ICMP) {
// fine, can use on bsd:s and bsd:u // fine, can use on bsd:s and bsd:u
} else { } else {
return {-1, Errno::INVAL}; return {-1, Network::Errno::E_INVAL};
} }
} }
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0; [[maybe_unused]] const bool unk_flag = (u32(type) & 0x20000000) != 0;
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type"); UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000); type = Network::Type(u32(type) & ~0x20000000);
const s32 fd = FindFreeFileDescriptorHandle(); const s32 fd = FindFreeFileDescriptorHandle();
if (fd < 0) { if (fd < 0) {
LOG_ERROR(Service, "No more file descriptors available"); LOG_ERROR(Service, "No more file descriptors available");
return {-1, Errno::MFILE}; return {-1, Network::Errno::E_MFILE};
}
if (Settings::values.airplane_mode.GetValue() && IsConnectionBased(type)) {
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
file_descriptors[fd].reset();
return {-1, Network::Errno::E_NOTCONN};
} }
file_descriptors[fd] = FileDescriptor{}; file_descriptors[fd] = FileDescriptor{};
FileDescriptor& descriptor = *file_descriptors[fd]; FileDescriptor& descriptor = *file_descriptors[fd];
// ENONMEM might be thrown here // ENONMEM might be thrown here
LOG_INFO(Service, "New socket fd={},domain={},type={},prot={}", fd, domain, type, protocol);
LOG_INFO(Service, "New socket fd={}", fd); // While room is important -- we need to remember ICMP takes priority over **everything else**
// TODO: rework this so proxy sockets can be done transparently? -- like what if i need
// to browse the internet while playing LDN or something stupid like that?
auto room_member = Network::GetRoomMember().lock(); auto room_member = Network::GetRoomMember().lock();
if (room_member && room_member->IsConnected()) { if ((protocol != Network::Protocol::ICMP && protocol != Network::Protocol::ICMPV6)
&& (room_member && room_member->IsConnected())) {
descriptor.socket = std::make_shared<Network::ProxySocket>(); descriptor.socket = std::make_shared<Network::ProxySocket>();
descriptor.socket->fd = fd;
} else { } else {
descriptor.socket = std::make_shared<Network::Socket>(); descriptor.socket = std::make_shared<Network::Socket>();
} }
auto const bsd_errno = descriptor.socket->Initialize(domain, type, protocol);
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol)); #if defined(__unix__) && !defined(__APPLE__)
descriptor.is_connection_based = IsConnectionBased(type); // ...only unix has this issue it seems, ICMP works otherwise fine on win
if ((protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6)
if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) { && bsd_errno != Network::Errno::E_SUCCESS) {
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket"); LOG_WARNING(Network, "Using ICMP emulated socket");
return {-1, Errno::NOTCONN}; descriptor.socket = std::make_shared<Network::IcmpSocket>();
descriptor.socket->fd = fd;
} }
#endif
return {fd, Errno::SUCCESS}; descriptor.is_connection_based = IsConnectionBased(type);
#ifdef _WIN32
if (descriptor.is_connection_based && descriptor.socket->fd == INVALID_SOCKET) {
#else
if (descriptor.is_connection_based && descriptor.socket->fd == Network::Socket::INVALID_SOCKET) {
#endif
file_descriptors[fd].reset();
return {-1, bsd_errno};
}
return {fd, Network::Errno::E_SUCCESS};
} }
std::pair<s32, Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, std::pair<s32, Network::Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) {
s32 nfds, s32 timeout) { LOG_DEBUG(Network, "nfds={},timeout={}", nfds, timeout);
if (nfds <= 0) { if (nfds <= 0) {
// When no entries are provided, -1 is returned with errno zero // When no entries are provided, -1 is returned with errno zero
return {-1, Errno::SUCCESS}; return {-1, Network::Errno::E_SUCCESS};
} }
if (read_buffer.size() < nfds * sizeof(PollFD)) { if (read_buffer.size() < nfds * sizeof(Network::PollFD)) {
return {-1, Errno::INVAL}; return {-1, Network::Errno::E_INVAL};
} }
if (write_buffer.size() < nfds * sizeof(PollFD)) { if (write_buffer.size() < nfds * sizeof(Network::PollFD)) {
return {-1, Errno::INVAL}; return {-1, Network::Errno::E_INVAL};
} }
std::vector<PollFD> fds(nfds); std::span<const Network::PollFD> in_fds(reinterpret_cast<const Network::PollFD*>(read_buffer.data()), nfds);
std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD)); std::span<Network::PollFD> out_fds(reinterpret_cast<Network::PollFD*>(write_buffer.data()), nfds);
std::copy(in_fds.begin(), in_fds.end(), out_fds.begin());
if (timeout >= 0) { if (timeout >= 0) {
const s64 seconds = timeout / 1000; const s64 seconds = timeout / 1000;
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000); const u64 nanoseconds = 1'000'000 * (u64(timeout) % 1000);
if (seconds < 0) { if (seconds < 0) {
return {-1, Errno::INVAL}; return {-1, Network::Errno::E_INVAL};
} }
if (nanoseconds > 999'999'999) { if (nanoseconds > 999'999'999) {
return {-1, Errno::INVAL}; return {-1, Network::Errno::E_INVAL};
} }
} else if (timeout != -1) { } else if (timeout != -1) {
return {-1, Errno::INVAL}; return {-1, Network::Errno::E_INVAL};
} }
for (PollFD& pollfd : fds) { for (size_t i = 0; i < in_fds.size(); ++i) {
ASSERT(False(pollfd.revents)); ASSERT(out_fds[i].fd == in_fds[i].fd && False(in_fds[i].revents));
if (!IsFileDescriptorValid(in_fds[i].fd)) {
if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) { out_fds[i].revents = {};
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd); if (!file_descriptors[in_fds[i].fd])
pollfd.revents = PollEvents{}; out_fds[i].revents = Network::PollEvents::NVAL;
return {0, Errno::SUCCESS}; return {0, Network::Errno::E_SUCCESS};
}
const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
if (!descriptor) {
LOG_TRACE(Service, "File descriptor handle={} is not allocated", pollfd.fd);
pollfd.revents = PollEvents::Nval;
return {0, Errno::SUCCESS};
} }
} }
std::vector<Network::PollFD> host_pollfds(fds.size()); std::vector<Network::HostPollFD> host_pollfds(in_fds.size());
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [](PollFD pollfd) { std::transform(in_fds.begin(), in_fds.end(), host_pollfds.begin(), [](auto const e) {
Network::PollFD result; Network::HostPollFD result{};
result.socket = file_descriptors[pollfd.fd]->socket.get(); result.socket = file_descriptors[e.fd]->socket.get();
result.events = Translate(pollfd.events); result.events = e.events;
result.revents = Network::PollEvents{}; result.revents = {};
return result; return result;
}); });
auto const res = Network::Poll(host_pollfds, timeout);
const auto result = Network::Poll(host_pollfds, timeout); for (size_t i = 0; i < in_fds.size(); ++i)
out_fds[i].revents = host_pollfds[i].revents;
const size_t num = host_pollfds.size(); return res;
for (size_t i = 0; i < num; ++i) {
fds[i].revents = Translate(host_pollfds[i].revents);
}
std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
return Translate(result);
} }
std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) { std::pair<s32, Network::Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Network::Errno::E_BADF};
} }
const s32 new_fd = FindFreeFileDescriptorHandle(); const s32 new_fd = FindFreeFileDescriptorHandle();
if (new_fd < 0) { if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available"); LOG_ERROR(Service, "No more file descriptors available");
return {-1, Errno::MFILE}; return {-1, Network::Errno::E_MFILE};
} }
FileDescriptor& descriptor = *file_descriptors[fd]; FileDescriptor& descriptor = *file_descriptors[fd];
auto [result, bsd_errno] = descriptor.socket->Accept(); auto [result, bsd_errno] = descriptor.socket->Accept();
if (bsd_errno != Network::Errno::SUCCESS) { if (bsd_errno != Network::Errno::E_SUCCESS) {
return {-1, Translate(bsd_errno)}; return {-1, bsd_errno};
} }
file_descriptors[new_fd] = FileDescriptor{}; file_descriptors[new_fd] = FileDescriptor{};
@@ -654,267 +666,218 @@ std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer)
new_descriptor.socket = std::move(result.socket); new_descriptor.socket = std::move(result.socket);
new_descriptor.is_connection_based = descriptor.is_connection_based; new_descriptor.is_connection_based = descriptor.is_connection_based;
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); PutValue(write_buffer, result.sockaddr_in);
PutValue(write_buffer, guest_addr_in); return {new_fd, Network::Errno::E_SUCCESS};
return {new_fd, Errno::SUCCESS};
} }
Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) { Network::Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Network::Errno::E_BADF;
} }
ASSERT(addr.size() >= 16); ASSERT(addr.size() >= 16);
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF; return Network::Errno::E_BADF;
} }
auto addr_in = GetValue<SockAddrIn>(addr); auto addr_in = GetValue<Network::SockAddrIn>(addr);
return file_descriptors[fd]->socket->Bind(addr_in);
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
} }
Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) { Network::Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Network::Errno::E_BADF;
} }
ASSERT(addr.size() >= 16); ASSERT(addr.size() >= 16);
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF; return Network::Errno::E_BADF;
} }
auto addr_in = GetValue<SockAddrIn>(addr); auto addr_in = GetValue<Network::SockAddrIn>(addr);
const Network::Errno result = file_descriptors[fd]->socket->Connect(addr_in);
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); if (result == Network::Errno::E_ISCONN) {
if (result == Errno::ISCONN) {
LOG_DEBUG(Service, "returned ISCONN - socket already connected"); LOG_DEBUG(Service, "returned ISCONN - socket already connected");
return Errno::SUCCESS; return Network::Errno::E_SUCCESS;
} }
return result; return result;
} }
Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) { Network::Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Network::Errno::E_BADF;
} }
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF; return Network::Errno::E_BADF;
} }
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName(); const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
if (bsd_errno != Network::Errno::SUCCESS) { if (bsd_errno != Network::Errno::E_SUCCESS) {
return Translate(bsd_errno); return bsd_errno;
} }
const SockAddrIn guest_addrin = Translate(addr_in); ASSERT(write_buffer.size() >= addr_in.len);
write_buffer.resize(addr_in.len);
ASSERT(write_buffer.size() >= sizeof(guest_addrin)); PutValue(write_buffer, addr_in);
write_buffer.resize(sizeof(guest_addrin)); return bsd_errno;
PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno);
} }
Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) { Network::Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Network::Errno::E_BADF;
} }
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF; return Network::Errno::E_BADF;
} }
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName(); const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
if (bsd_errno != Network::Errno::SUCCESS) { if (bsd_errno != Network::Errno::E_SUCCESS) {
return Translate(bsd_errno); return bsd_errno;
} }
const SockAddrIn guest_addrin = Translate(addr_in); ASSERT(write_buffer.size() >= addr_in.len);
write_buffer.resize(addr_in.len);
ASSERT(write_buffer.size() >= sizeof(guest_addrin)); PutValue(write_buffer, addr_in);
write_buffer.resize(sizeof(guest_addrin)); return bsd_errno;
PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno);
} }
Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) { Network::Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
LOG_DEBUG(Network, "fd={},backlog={}", fd, backlog);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Network::Errno::E_BADF;
} }
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF; return Network::Errno::E_BADF;
} }
return Translate(file_descriptors[fd]->socket->Listen(backlog)); return file_descriptors[fd]->socket->Listen(backlog);
} }
std::pair<s32, Errno> BSD_USA::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { std::pair<s32, Network::Errno> BSD_USA::FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg) {
LOG_DEBUG(Network, "fd={},cmd={},arg={}", fd, u32(cmd), arg);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Network::Errno::E_BADF};
} }
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return {-1, Errno::BADF}; return {-1, Network::Errno::E_BADF};
} }
FileDescriptor& descriptor = *file_descriptors[fd]; FileDescriptor& descriptor = *file_descriptors[fd];
switch (cmd) { switch (cmd) {
case FcntlCmd::GETFL: case Network::FcntlCmd::GETFL:
ASSERT(arg == 0); ASSERT(arg == 0);
return {descriptor.flags, Errno::SUCCESS}; return {descriptor.flags, Network::Errno::E_SUCCESS};
case FcntlCmd::SETFL: { case Network::FcntlCmd::SETFL: {
const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0; const bool enable = (arg & u32(Network::FcntlFlags::NONBLOCK_NX)) != 0;
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); const Network::Errno bsd_errno = descriptor.socket->SetNonBlock(enable);
if (bsd_errno != Errno::SUCCESS) { if (bsd_errno != Network::Errno::E_SUCCESS) {
return {-1, bsd_errno}; return {-1, bsd_errno};
} }
descriptor.flags = arg; descriptor.flags = arg;
return {0, Errno::SUCCESS}; return {0, Network::Errno::E_SUCCESS};
} }
default: default:
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd); UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
return {-1, Errno::SUCCESS}; return {-1, Network::Errno::E_SUCCESS};
} }
} }
Errno BSD_USA::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) { Network::Errno BSD_USA::GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval) {
LOG_DEBUG(Network, "fd={},level={},optname={}", fd, u32(level), u32(optname));
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Network::Errno::E_BADF;
} }
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF; return Network::Errno::E_BADF;
} }
if (level != static_cast<u32>(SocketLevel::SOCKET)) { if (level != Network::SocketLevel::SOCKET) {
UNIMPLEMENTED_MSG("Unknown getsockopt level"); LOG_WARNING(Service, "(stubbed) level fd={}, level={}, optname={}", fd, level, optname);
return Errno::SUCCESS;
} }
Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
switch (optname) { switch (optname) {
case OptName::ERROR_: { case Network::OptName::ERROR_: {
auto [pending_err, getsockopt_err] = socket->GetPendingError(); auto [pending_err, getsockopt_err] = socket->GetPendingError();
if (getsockopt_err == Network::Errno::SUCCESS) { if (getsockopt_err == Network::Errno::E_SUCCESS) {
Errno translated_pending_err = Translate(pending_err);
ASSERT_OR_EXECUTE_MSG( ASSERT_OR_EXECUTE_MSG(
optval.size() == sizeof(Errno), { return Errno::INVAL; }, optval.size() == sizeof(Network::Errno), { return Network::Errno::E_INVAL; },
"Incorrect getsockopt option size"); "Incorrect getsockopt option size");
optval.resize(sizeof(Errno)); optval.resize(sizeof(Network::Errno));
PutValue(optval, translated_pending_err); PutValue(optval, pending_err);
} }
return Translate(getsockopt_err); return getsockopt_err;
} }
default: default:
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname); UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
return Errno::SUCCESS; return Network::Errno::E_SUCCESS;
} }
} }
Errno BSD_USA::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) { Network::Errno BSD_USA::SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
LOG_DEBUG(Service, "fd={},level={},optname={}", fd, level, optname);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Network::Errno::E_BADF;
} }
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF; return Network::Errno::E_BADF;
}
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
LOG_WARNING(Service, "(STUBBED) setsockopt with level={}, optname={}", level, optname);
return Errno::SUCCESS;
} }
Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
return socket->SetSockOpt(level, optname, optval);
if (optname == OptName::LINGER) {
ASSERT(optval.size() == sizeof(Linger));
auto linger = GetValue<Linger>(optval);
ASSERT(linger.onoff == 0 || linger.onoff == 1);
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
}
ASSERT(optval.size() == sizeof(u32));
auto value = GetValue<u32>(optval);
switch (optname) {
case OptName::REUSEADDR:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetReuseAddr(value != 0));
case OptName::KEEPALIVE:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetKeepAlive(value != 0));
case OptName::BROADCAST:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetBroadcast(value != 0));
case OptName::SNDBUF:
return Translate(socket->SetSndBuf(value));
case OptName::RCVBUF:
return Translate(socket->SetRcvBuf(value));
case OptName::SNDTIMEO:
return Translate(socket->SetSndTimeo(value));
case OptName::RCVTIMEO:
return Translate(socket->SetRcvTimeo(value));
case OptName::NOSIGPIPE:
LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
return Errno::SUCCESS;
default:
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
return Errno::SUCCESS;
}
} }
Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) { Network::Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
LOG_DEBUG(Network, "fd={},how={}", fd, how);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Network::Errno::E_BADF;
} }
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF; return Network::Errno::E_BADF;
} }
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how)); return file_descriptors[fd]->socket->Shutdown(Network::ShutdownHow(how));
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
} }
std::pair<s32, Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) { std::pair<s32, Network::Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Network::Errno::E_BADF};
} }
FileDescriptor& descriptor = *file_descriptors[fd]; FileDescriptor& descriptor = *file_descriptors[fd];
// Apply flags // Apply flags
using Network::FLAG_MSG_DONTWAIT; if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
using Network::FLAG_O_NONBLOCK; flags &= ~u32(Network::MsgOpt::DONTWAIT);
if ((flags & FLAG_MSG_DONTWAIT) != 0) { if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(true); descriptor.socket->SetNonBlock(true);
} }
} }
const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message)); const auto [ret, bsd_errno] = descriptor.socket->Recv(flags, message);
// Restore original state // Restore original state
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0)
descriptor.socket->SetNonBlock(false); descriptor.socket->SetNonBlock(false);
}
return {ret, bsd_errno}; return {ret, bsd_errno};
} }
std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::pair<s32, Network::Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::vector<u8>& addr) {
std::vector<u8>& addr) { LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Network::Errno::E_BADF};
} }
FileDescriptor& descriptor = *file_descriptors[fd]; FileDescriptor& descriptor = *file_descriptors[fd];
@@ -929,19 +892,17 @@ std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>&
} }
// Apply flags // Apply flags
using Network::FLAG_MSG_DONTWAIT; if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
using Network::FLAG_O_NONBLOCK; flags &= ~u32(Network::MsgOpt::DONTWAIT);
if ((flags & FLAG_MSG_DONTWAIT) != 0) { if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(true); descriptor.socket->SetNonBlock(true);
} }
} }
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in)); const auto [ret, bsd_errno] = descriptor.socket->RecvFrom(flags, message, p_addr_in);
// Restore original state // Restore original state
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
descriptor.socket->SetNonBlock(false); descriptor.socket->SetNonBlock(false);
} }
@@ -950,58 +911,59 @@ std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>&
addr.clear(); addr.clear();
} else { } else {
ASSERT(addr.size() >= 16); ASSERT(addr.size() >= 16);
const SockAddrIn result = Translate(addr_in); PutValue(addr, addr_in);
PutValue(addr, result);
} }
} }
return {ret, bsd_errno}; return {ret, bsd_errno};
} }
std::pair<s32, Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) { std::pair<s32, Network::Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Network::Errno::E_BADF};
} }
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return {-1, Errno::BADF}; return {-1, Network::Errno::E_BADF};
} }
return Translate(file_descriptors[fd]->socket->Send(message, flags)); return file_descriptors[fd]->socket->Send(message, flags);
} }
std::pair<s32, Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::pair<s32, Network::Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr) {
std::span<const u8> addr) { LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Network::Errno::E_BADF};
} }
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return {-1, Errno::BADF}; return {-1, Network::Errno::E_BADF};
} }
Network::SockAddrIn addr_in; Network::SockAddrIn addr_in{};
Network::SockAddrIn* p_addr_in = nullptr; Network::SockAddrIn* p_addr_in = nullptr;
if (!addr.empty()) { if (!addr.empty()) {
ASSERT(addr.size() >= 16); ASSERT(addr.size() >= 16);
auto guest_addr_in = GetValue<SockAddrIn>(addr); auto guest_addr_in = GetValue<Network::SockAddrIn>(addr);
addr_in = Translate(guest_addr_in); addr_in = guest_addr_in;
p_addr_in = &addr_in; p_addr_in = &addr_in;
} }
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in)); return file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in);
} }
Errno BSD_USA::CloseImpl(s32 fd) { Network::Errno BSD_USA::CloseImpl(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Network::Errno::E_BADF;
} }
if (!file_descriptors[fd]->socket) { if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket"); LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF; return Network::Errno::E_BADF;
} }
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close()); auto const bsd_errno = file_descriptors[fd]->socket->Close();
if (bsd_errno != Errno::SUCCESS) { if (bsd_errno != Network::Errno::E_SUCCESS) {
return bsd_errno; return bsd_errno;
} }
@@ -1011,26 +973,27 @@ Errno BSD_USA::CloseImpl(s32 fd) {
return bsd_errno; return bsd_errno;
} }
std::variant<s32, Errno> BSD_USA::DuplicateSocketImpl(s32 fd) { std::variant<s32, Network::Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Network::Errno::E_BADF;
} }
// Don't use IsFileDescriptorValid as new_fd isn't a proper handle yet
const s32 new_fd = FindFreeFileDescriptorHandle(); if (s32 const new_fd = FindFreeFileDescriptorHandle(); new_fd >= 0) {
if (new_fd < 0) { file_descriptors[new_fd] = FileDescriptor{
.socket = file_descriptors[fd]->socket,
.flags = file_descriptors[fd]->flags,
.is_connection_based = file_descriptors[fd]->is_connection_based,
};
return new_fd;
} else {
LOG_ERROR(Service, "No more file descriptors available"); LOG_ERROR(Service, "No more file descriptors available");
return Errno::MFILE; return Network::Errno::E_MFILE;
} }
file_descriptors[new_fd] = FileDescriptor{
.socket = file_descriptors[fd]->socket,
.flags = file_descriptors[fd]->flags,
.is_connection_based = file_descriptors[fd]->is_connection_based,
};
return new_fd;
} }
std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) { std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return std::nullopt; return std::nullopt;
} }
@@ -1042,41 +1005,40 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
} }
s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept { s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept {
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) { // first three file descriptors are reserved for:
if (!file_descriptors[fd]) { // STDOUT_FILENO, STDIN_FILENO and STDERR_FILENO
for (s32 fd = 0; fd < s32(file_descriptors.size()); ++fd)
if (!file_descriptors[fd])
return fd; return fd;
}
}
return -1; return -1;
} }
bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept { bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept {
if (fd > static_cast<s32>(MAX_FD) || fd < 0) { if (fd < 0 || fd >= s32(file_descriptors.size())) {
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd); LOG_ERROR(Service, "Invalid handle={}", fd);
return false; return false;
} }
if (!file_descriptors[fd]) { if (!file_descriptors[fd]) {
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd); LOG_ERROR(Service, "handle={} is not allocated", fd);
return false; return false;
} }
return true; return true;
} }
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept { void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept {
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1); rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
} }
void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) { void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
for (auto& optional_descriptor : file_descriptors) { for (auto& optional_descriptor : file_descriptors) {
if (!optional_descriptor.has_value()) { if (optional_descriptor.has_value()) {
continue; FileDescriptor& descriptor = *optional_descriptor;
descriptor.socket.get()->HandleProxyPacket(packet);
} }
FileDescriptor& descriptor = *optional_descriptor;
descriptor.socket.get()->HandleProxyPacket(packet);
} }
} }
+27 -27
View File
@@ -11,7 +11,7 @@
#include <variant> #include <variant>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/socket_types.h" #include "core/internal_network/socket_types.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "core/hle/service/sockets/sockets.h" #include "core/hle/service/sockets/sockets.h"
#include "network/network.h" #include "network/network.h"
@@ -35,8 +35,8 @@ public:
// These methods are called from SSL; the first two are also called from // These methods are called from SSL; the first two are also called from
// this class for the corresponding IPC methods. // this class for the corresponding IPC methods.
// On the real device, the SSL service makes IPC calls to this service. // On the real device, the SSL service makes IPC calls to this service.
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd); std::variant<s32, Network::Errno> DuplicateSocketImpl(s32 fd);
Errno CloseImpl(s32 fd); Network::Errno CloseImpl(s32 fd);
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd); std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
private: private:
@@ -58,7 +58,7 @@ private:
std::span<const u8> read_buffer; std::span<const u8> read_buffer;
std::vector<u8> write_buffer; std::vector<u8> write_buffer;
s32 ret{}; s32 ret{};
Errno bsd_errno{}; Network::Errno bsd_errno{};
}; };
struct AcceptWork { struct AcceptWork {
@@ -68,7 +68,7 @@ private:
s32 fd; s32 fd;
std::vector<u8> write_buffer; std::vector<u8> write_buffer;
s32 ret{}; s32 ret{};
Errno bsd_errno{}; Network::Errno bsd_errno{};
}; };
struct ConnectWork { struct ConnectWork {
@@ -77,7 +77,7 @@ private:
s32 fd; s32 fd;
std::span<const u8> addr; std::span<const u8> addr;
Errno bsd_errno{}; Network::Errno bsd_errno{};
}; };
struct RecvWork { struct RecvWork {
@@ -88,7 +88,7 @@ private:
u32 flags; u32 flags;
std::vector<u8> message; std::vector<u8> message;
s32 ret{}; s32 ret{};
Errno bsd_errno{}; Network::Errno bsd_errno{};
}; };
struct RecvFromWork { struct RecvFromWork {
@@ -100,7 +100,7 @@ private:
std::vector<u8> message; std::vector<u8> message;
std::vector<u8> addr; std::vector<u8> addr;
s32 ret{}; s32 ret{};
Errno bsd_errno{}; Network::Errno bsd_errno{};
}; };
struct SendWork { struct SendWork {
@@ -111,7 +111,7 @@ private:
u32 flags; u32 flags;
std::span<const u8> message; std::span<const u8> message;
s32 ret{}; s32 ret{};
Errno bsd_errno{}; Network::Errno bsd_errno{};
}; };
struct SendToWork { struct SendToWork {
@@ -123,7 +123,7 @@ private:
std::span<const u8> message; std::span<const u8> message;
std::span<const u8> addr; std::span<const u8> addr;
s32 ret{}; s32 ret{};
Errno bsd_errno{}; Network::Errno bsd_errno{};
}; };
void RegisterClient(HLERequestContext& ctx); void RegisterClient(HLERequestContext& ctx);
@@ -155,29 +155,29 @@ private:
template <typename Work> template <typename Work>
void ExecuteWork(HLERequestContext& ctx, Work work); void ExecuteWork(HLERequestContext& ctx, Work work);
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol); std::pair<s32, Network::Errno> SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol);
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout); std::pair<s32, Network::Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer); std::pair<s32, Network::Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
Errno BindImpl(s32 fd, std::span<const u8> addr); Network::Errno BindImpl(s32 fd, std::span<const u8> addr);
Errno ConnectImpl(s32 fd, std::span<const u8> addr); Network::Errno ConnectImpl(s32 fd, std::span<const u8> addr);
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer); Network::Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer); Network::Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno ListenImpl(s32 fd, s32 backlog); Network::Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); std::pair<s32, Network::Errno> FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg);
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval); Network::Errno GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval);
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval); Network::Errno SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval);
Errno ShutdownImpl(s32 fd, s32 how); Network::Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); std::pair<s32, Network::Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::pair<s32, Network::Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr); std::vector<u8>& addr);
std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message); std::pair<s32, Network::Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::pair<s32, Network::Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::span<const u8> addr); std::span<const u8> addr);
s32 FindFreeFileDescriptorHandle() noexcept; s32 FindFreeFileDescriptorHandle() noexcept;
bool IsFileDescriptorValid(s32 fd) const noexcept; bool IsFileDescriptorValid(s32 fd) const noexcept;
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept; void BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept;
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{}; static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
+40 -44
View File
@@ -102,39 +102,39 @@ static bool IsBlockedHost(const std::string& host) {
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; }); [&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
} }
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) { static NetDbError GetAddrInfoErrorToNetDbError(Network::GetAddrInfoError result) {
// These combinations have been verified on console (but are not // These combinations have been verified on console (but are not
// exhaustive). // exhaustive).
switch (result) { switch (result) {
case GetAddrInfoError::SUCCESS: case Network::GetAddrInfoError::SUCCESS:
return NetDbError::Success; return NetDbError::Success;
case GetAddrInfoError::AGAIN: case Network::GetAddrInfoError::AGAIN:
return NetDbError::TryAgain; return NetDbError::TryAgain;
case GetAddrInfoError::NODATA: case Network::GetAddrInfoError::NODATA:
return NetDbError::HostNotFound; return NetDbError::HostNotFound;
case GetAddrInfoError::SERVICE: case Network::GetAddrInfoError::SERVICE:
return NetDbError::Success; return NetDbError::Success;
default: default:
return NetDbError::HostNotFound; return NetDbError::HostNotFound;
} }
} }
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) { static Network::Errno GetAddrInfoErrorToErrno(Network::GetAddrInfoError result) {
// These combinations have been verified on console (but are not // These combinations have been verified on console (but are not
// exhaustive). // exhaustive).
switch (result) { switch (result) {
case GetAddrInfoError::SUCCESS: case Network::GetAddrInfoError::SUCCESS:
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for // Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
// some reason, but that doesn't seem useful to implement. // some reason, but that doesn't seem useful to implement.
return Errno::SUCCESS; return Network::Errno::E_SUCCESS;
case GetAddrInfoError::AGAIN: case Network::GetAddrInfoError::AGAIN:
return Errno::SUCCESS; return Network::Errno::E_SUCCESS;
case GetAddrInfoError::NODATA: case Network::GetAddrInfoError::NODATA:
return Errno::SUCCESS; return Network::Errno::E_SUCCESS;
case GetAddrInfoError::SERVICE: case Network::GetAddrInfoError::SERVICE:
return Errno::INVAL; return Network::Errno::E_INVAL;
default: default:
return Errno::SUCCESS; return Network::Errno::E_SUCCESS;
} }
} }
@@ -155,9 +155,7 @@ static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo // host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
// behaves the same on Unix and Windows, unlike gethostbyname where Windows // behaves the same on Unix and Windows, unlike gethostbyname where Windows
// doesn't implement h_errno. // doesn't implement h_errno.
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec, static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrInfo> vec, std::string_view host) {
std::string_view host) {
std::vector<u8> data; std::vector<u8> data;
// h_name: use the input hostname (append nul-terminated) // h_name: use the input hostname (append nul-terminated)
AppendNulTerminated(data, host); AppendNulTerminated(data, host);
@@ -165,12 +163,12 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
Append<u32_be>(data, 0); // count of h_aliases Append<u32_be>(data, 0); // count of h_aliases
// (If the count were nonzero, the aliases would be appended as nul-terminated here.) // (If the count were nonzero, the aliases would be appended as nul-terminated here.)
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype Append<u16_be>(data, u16(Network::Domain::INET)); // h_addrtype
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
// h_addr_list: // h_addr_list:
size_t count = vec.size(); size_t count = vec.size();
ASSERT(count <= UINT32_MAX); ASSERT(count <= UINT32_MAX);
Append<u32_be>(data, static_cast<uint32_t>(count)); Append<u32_be>(data, u32(count));
for (const Network::AddrInfo& addrinfo : vec) { for (const Network::AddrInfo& addrinfo : vec) {
// On the Switch, this is passed through htonl despite already being // On the Switch, this is passed through htonl despite already being
// big-endian, so it ends up as little-endian. // big-endian, so it ends up as little-endian.
@@ -182,7 +180,7 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
return data; return data;
} }
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) { static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
struct InputParameters { struct InputParameters {
u8 use_nsd_resolve; u8 use_nsd_resolve;
u32 cancel_handle; u32 cancel_handle;
@@ -205,7 +203,7 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
// Prevent resolution of Nintendo servers // Prevent resolution of Nintendo servers
if (IsBlockedHost(host)) { if (IsBlockedHost(host)) {
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host); LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
return {0, GetAddrInfoError::AGAIN}; return {0, Network::GetAddrInfoError::AGAIN};
} }
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt); auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
@@ -213,10 +211,10 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host); const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
const u32 data_size = u32(data.size()); const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0); ctx.WriteBuffer(data, 0);
return {data_size, GetAddrInfoError::SUCCESS}; return {data_size, Network::GetAddrInfoError::SUCCESS};
} }
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v); auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, Translate(*err)}; return {0, *err};
} }
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) { void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
@@ -224,7 +222,7 @@ void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
struct OutputParameters { struct OutputParameters {
NetDbError netdb_error; NetDbError netdb_error;
Errno bsd_errno; Network::Errno bsd_errno;
u32 data_size; u32 data_size;
}; };
static_assert(sizeof(OutputParameters) == 0xc); static_assert(sizeof(OutputParameters) == 0xc);
@@ -244,7 +242,7 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
struct OutputParameters { struct OutputParameters {
u32 data_size; u32 data_size;
NetDbError netdb_error; NetDbError netdb_error;
Errno bsd_errno; Network::Errno bsd_errno;
}; };
static_assert(sizeof(OutputParameters) == 0xc); static_assert(sizeof(OutputParameters) == 0xc);
@@ -257,24 +255,23 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
}); });
} }
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec, static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec, std::string_view host) {
std::string_view host) {
// Adapted from // Adapted from
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190 // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
std::vector<u8> data; std::vector<u8> data;
for (const Network::AddrInfo& addrinfo : vec) { for (const Network::AddrInfo& addrinfo : vec) {
// serialized addrinfo: // serialized addrinfo:
Append<u32_be>(data, 0xBEEFCAFE); // magic Append<u32_be>(data, 0xBEEFCAFE); // magic
Append<u32_be>(data, 0); // ai_flags Append<u32_be>(data, 0); // ai_flags
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family Append<u32_be>(data, u32(addrinfo.family)); // ai_family
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype Append<u32_be>(data, u32(addrinfo.socket_type)); // ai_socktype
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol Append<u32_be>(data, u32(addrinfo.protocol)); // ai_protocol
Append<u32_be>(data, 16); // ai_addrlen Append<u32_be>(data, 16); // ai_addrlen
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size // ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
// ai_addr: // ai_addr:
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family Append<u16_be>(data, u16(addrinfo.addr.family)); // sin_family
// On the Switch, the following fields are passed through htonl despite // On the Switch, the following fields are passed through htonl despite
// already being big-endian, so they end up as little-endian. // already being big-endian, so they end up as little-endian.
Append<u16_le>(data, addrinfo.addr.portno); // sin_port Append<u16_le>(data, addrinfo.addr.portno); // sin_port
@@ -296,7 +293,7 @@ static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& v
return data; return data;
} }
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) { static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
struct InputParameters { struct InputParameters {
u8 use_nsd_resolve; u8 use_nsd_resolve;
u32 cancel_handle; u32 cancel_handle;
@@ -321,7 +318,7 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
// Prevent resolution of Nintendo servers // Prevent resolution of Nintendo servers
if (IsBlockedHost(host)) { if (IsBlockedHost(host)) {
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host); LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
return {0, GetAddrInfoError::AGAIN}; return {0, Network::GetAddrInfoError::AGAIN};
} }
std::optional<std::string> service = std::nullopt; std::optional<std::string> service = std::nullopt;
@@ -331,24 +328,23 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
} }
// Serialized hints are also passed in a buffer, but are ignored for now. // Serialized hints are also passed in a buffer, but are ignored for now.
auto res_v = Network::GetAddressInfo(host, service); auto res_v = Network::GetAddressInfo(host, service);
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) { if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
const std::vector<u8> data = SerializeAddrInfo(*res, host); const std::vector<u8> data = SerializeAddrInfo(*res, host);
const u32 data_size = u32(data.size()); const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0); ctx.WriteBuffer(data, 0);
return {data_size, GetAddrInfoError::SUCCESS}; return {data_size, Network::GetAddrInfoError::SUCCESS};
} }
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v); auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, Translate(*err)}; return {0, *err};
} }
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) { void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx); auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
struct OutputParameters { struct OutputParameters {
Errno bsd_errno; Network::Errno bsd_errno;
GetAddrInfoError gai_error; Network::GetAddrInfoError gai_error;
u32 data_size; u32 data_size;
}; };
static_assert(sizeof(OutputParameters) == 0xc); static_assert(sizeof(OutputParameters) == 0xc);
@@ -364,7 +360,7 @@ void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) { void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
struct InputParameters { struct InputParameters {
GetAddrInfoError gai_errno; Network::GetAddrInfoError gai_errno;
}; };
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
auto input = rp.PopRaw<InputParameters>(); auto input = rp.PopRaw<InputParameters>();
@@ -382,9 +378,9 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
struct OutputParameters { struct OutputParameters {
u32 data_size; u32 data_size;
GetAddrInfoError gai_error; Network::GetAddrInfoError gai_error;
NetDbError netdb_error; NetDbError netdb_error;
Errno bsd_errno; Network::Errno bsd_errno;
}; };
static_assert(sizeof(OutputParameters) == 0x10); static_assert(sizeof(OutputParameters) == 0x10);
+1 -236
View File
@@ -8,6 +8,7 @@
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/internal_network/socket_types.h"
namespace Core { namespace Core {
class System; class System;
@@ -15,242 +16,6 @@ class System;
namespace Service::Sockets { namespace Service::Sockets {
enum class Errno : u32 {
SUCCESS = 0,
BADF = 9,
AGAIN = 11,
INVAL = 22,
MFILE = 24,
PIPE = 32,
MSGSIZE = 90,
CONNABORTED = 103,
CONNRESET = 104,
NOTCONN = 107,
TIMEDOUT = 110,
CONNREFUSED = 111,
INPROGRESS = 115,
ISCONN = 106,
};
enum class GetAddrInfoError : s32 {
SUCCESS = 0,
ADDRFAMILY = 1,
AGAIN = 2,
BADFLAGS = 3,
FAIL = 4,
FAMILY = 5,
MEMORY = 6,
NODATA = 7,
NONAME = 8,
SERVICE = 9,
SOCKTYPE = 10,
SYSTEM = 11,
BADHINTS = 12,
PROTOCOL = 13,
OVERFLOW_ = 14, // avoid name collision with Windows macro
OTHER = 15,
};
enum class Domain : u32 {
Unspecified = 0,
INET = 2,
};
enum class Type : u32 {
Unspecified = 0,
STREAM = 1,
DGRAM = 2,
RAW = 3,
SEQPACKET = 5,
};
enum class Protocol : u32 {
IP = 0,
ICMP = 1,
TCP = 6,
UDP = 17,
//
IPV6 = 41,
RAW = 255,
//
HOPOPTS = 0,
IGMP = 2,
GGP = 3,
IPV4 = 4,
ST = 7,
EGP = 8,
PIGP = 9,
RCCMON = 10,
NVPII = 11,
PUP = 12,
ARGUS = 13,
EMCON = 14,
XNET = 15,
CHAOS = 16,
MUX = 18,
MEAS = 19,
HMP = 20,
PRM = 21,
IDP = 22,
TRUNK1 = 23,
TRUNK2 = 24,
LEAF1 = 25,
LEAF2 = 26,
RDP = 27,
IRTP = 28,
TP = 29,
BLT = 30,
NSP = 31,
INP = 32,
DCCP = 33,
//3PC = 34,
IDPR = 35,
XTP = 36,
DDP = 37,
CMTP = 38,
TPXX = 39,
IL = 40,
SDRP = 42,
ROUTING = 43,
FRAGMENT = 44,
IDRP = 45,
RSVP = 46,
GRE = 47,
MHRP = 48,
BHA = 49,
ESP = 50,
AH = 51,
INLSP = 52,
SWIPE = 53,
NHRP = 54,
MOBILE = 55,
TLSP = 56,
SKIP = 57,
ICMPV6 = 58,
NONE = 59,
DSTOPTS = 60,
AHIP = 61,
CFTP = 62,
HELLO = 63,
SATEXPAK = 64,
KRYPTOLAN = 65,
RVD = 66,
IPPC = 67,
ADFS = 68,
SATMON = 69,
VISA = 70,
IPCV = 71,
CPNX = 72,
CPHB = 73,
WSN = 74,
PVP = 75,
BRSATMON = 76,
ND = 77,
WBMON = 78,
WBEXPAK = 79,
EON = 80,
VMTP = 81,
SVMTP = 82,
VINES = 83,
TTP = 84,
IGP = 85,
DGP = 86,
TCF = 87,
IGRP = 88,
OSPFIGP = 89,
SRPC = 90,
LARP = 91,
MTP = 92,
AX25 = 93,
IPEIP = 94,
MICP = 95,
SCCSP = 96,
ETHERIP = 97,
ENCAP = 98,
APES = 99,
GMTP = 100,
IPCOMP = 108,
SCTP = 132,
MH = 135,
UDPLITE = 136,
HIP = 139,
SHIM6 = 140,
PIM = 103,
CARP = 112,
PGM = 113,
MPLS = 137,
PFSYNC = 240,
};
enum class SocketLevel : u32 {
IP = 0,
TCP = 6,
SOCKET = 0xffff, // i.e. SOL_SOCKET
};
enum class OptName : u32 {
REUSEADDR = 0x4,
KEEPALIVE = 0x8,
BROADCAST = 0x20,
LINGER = 0x80,
SNDBUF = 0x1001,
RCVBUF = 0x1002,
SNDTIMEO = 0x1005,
RCVTIMEO = 0x1006,
ERROR_ = 0x1007, // avoid name collision with Windows macro
NOSIGPIPE = 0x800, // at least according to libnx
ACCEPTFILTER = 0x1000,
BINTIME = 0x2000,
NO_OFFLOAD = 0x4000,
NO_DDP = 0x8000,
};
enum class ShutdownHow : s32 {
RD = 0,
WR = 1,
RDWR = 2,
};
enum class FcntlCmd : s32 {
GETFL = 3,
SETFL = 4,
};
struct SockAddrIn {
u8 len;
u8 family;
u16 portno;
std::array<u8, 4> ip;
std::array<u8, 248> zeroes;
};
static_assert(sizeof(SockAddrIn) == 0x100);
enum class PollEvents : u16 {
// Using Pascal case because IN is a macro on Windows.
In = 1 << 0,
Pri = 1 << 1,
Out = 1 << 2,
Err = 1 << 3,
Hup = 1 << 4,
Nval = 1 << 5,
RdNorm = 1 << 6,
RdBand = 1 << 7,
WrBand = 1 << 8,
};
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
s32 fd;
PollEvents events;
PollEvents revents;
};
struct Linger {
u32 onoff;
u32 linger;
};
void LoopProcess(Core::System& system); void LoopProcess(Core::System& system);
} // namespace Service::Sockets } // namespace Service::Sockets
@@ -15,388 +15,42 @@
namespace Service::Sockets { namespace Service::Sockets {
Errno Translate(Network::Errno value) { const char* Translate(Network::GetAddrInfoError error) {
switch (value) {
case Network::Errno::SUCCESS:
return Errno::SUCCESS;
case Network::Errno::BADF:
return Errno::BADF;
case Network::Errno::AGAIN:
return Errno::AGAIN;
case Network::Errno::INVAL:
return Errno::INVAL;
case Network::Errno::MFILE:
return Errno::MFILE;
case Network::Errno::PIPE:
return Errno::PIPE;
case Network::Errno::CONNREFUSED:
return Errno::CONNREFUSED;
case Network::Errno::NOTCONN:
return Errno::NOTCONN;
case Network::Errno::TIMEDOUT:
return Errno::TIMEDOUT;
case Network::Errno::CONNABORTED:
return Errno::CONNABORTED;
case Network::Errno::CONNRESET:
return Errno::CONNRESET;
case Network::Errno::INPROGRESS:
return Errno::INPROGRESS;
case Network::Errno::ISCONN:
return Errno::ISCONN;
default:
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
return Errno::SUCCESS;
}
}
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
return {value.first, Translate(value.second)};
}
GetAddrInfoError Translate(Network::GetAddrInfoError error) {
switch (error) {
case Network::GetAddrInfoError::SUCCESS:
return GetAddrInfoError::SUCCESS;
case Network::GetAddrInfoError::ADDRFAMILY:
return GetAddrInfoError::ADDRFAMILY;
case Network::GetAddrInfoError::AGAIN:
return GetAddrInfoError::AGAIN;
case Network::GetAddrInfoError::BADFLAGS:
return GetAddrInfoError::BADFLAGS;
case Network::GetAddrInfoError::FAIL:
return GetAddrInfoError::FAIL;
case Network::GetAddrInfoError::FAMILY:
return GetAddrInfoError::FAMILY;
case Network::GetAddrInfoError::MEMORY:
return GetAddrInfoError::MEMORY;
case Network::GetAddrInfoError::NODATA:
return GetAddrInfoError::NODATA;
case Network::GetAddrInfoError::NONAME:
return GetAddrInfoError::NONAME;
case Network::GetAddrInfoError::SERVICE:
return GetAddrInfoError::SERVICE;
case Network::GetAddrInfoError::SOCKTYPE:
return GetAddrInfoError::SOCKTYPE;
case Network::GetAddrInfoError::SYSTEM:
return GetAddrInfoError::SYSTEM;
case Network::GetAddrInfoError::BADHINTS:
return GetAddrInfoError::BADHINTS;
case Network::GetAddrInfoError::PROTOCOL:
return GetAddrInfoError::PROTOCOL;
case Network::GetAddrInfoError::OVERFLOW_:
return GetAddrInfoError::OVERFLOW_;
case Network::GetAddrInfoError::OTHER:
return GetAddrInfoError::OTHER;
default:
UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
return GetAddrInfoError::OTHER;
}
}
const char* Translate(GetAddrInfoError error) {
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254 // https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
switch (error) { switch (error) {
case GetAddrInfoError::SUCCESS: case Network::GetAddrInfoError::SUCCESS:
return "Success"; return "Success";
case GetAddrInfoError::ADDRFAMILY: case Network::GetAddrInfoError::ADDRFAMILY:
return "Address family for hostname not supported"; return "Address family for hostname not supported";
case GetAddrInfoError::AGAIN: case Network::GetAddrInfoError::AGAIN:
return "Temporary failure in name resolution"; return "Temporary failure in name resolution";
case GetAddrInfoError::BADFLAGS: case Network::GetAddrInfoError::BADFLAGS:
return "Invalid value for ai_flags"; return "Invalid value for ai_flags";
case GetAddrInfoError::FAIL: case Network::GetAddrInfoError::FAIL:
return "Non-recoverable failure in name resolution"; return "Non-recoverable failure in name resolution";
case GetAddrInfoError::FAMILY: case Network::GetAddrInfoError::FAMILY:
return "ai_family not supported"; return "ai_family not supported";
case GetAddrInfoError::MEMORY: case Network::GetAddrInfoError::MEMORY:
return "Memory allocation failure"; return "Memory allocation failure";
case GetAddrInfoError::NODATA: case Network::GetAddrInfoError::NODATA:
return "No address associated with hostname"; return "No address associated with hostname";
case GetAddrInfoError::NONAME: case Network::GetAddrInfoError::NONAME:
return "hostname nor servname provided, or not known"; return "hostname nor servname provided, or not known";
case GetAddrInfoError::SERVICE: case Network::GetAddrInfoError::SERVICE:
return "servname not supported for ai_socktype"; return "servname not supported for ai_socktype";
case GetAddrInfoError::SOCKTYPE: case Network::GetAddrInfoError::SOCKTYPE:
return "ai_socktype not supported"; return "ai_socktype not supported";
case GetAddrInfoError::SYSTEM: case Network::GetAddrInfoError::SYSTEM:
return "System error returned in errno"; return "System error returned in errno";
case GetAddrInfoError::BADHINTS: case Network::GetAddrInfoError::BADHINTS:
return "Invalid value for hints"; return "Invalid value for hints";
case GetAddrInfoError::PROTOCOL: case Network::GetAddrInfoError::PROTOCOL:
return "Resolved protocol is unknown"; return "Resolved protocol is unknown";
case GetAddrInfoError::OVERFLOW_: case Network::GetAddrInfoError::OVERFLOW_:
return "Argument buffer overflow"; return "Argument buffer overflow";
default: default:
return "Unknown error"; return "Unknown error";
} }
} }
Network::Domain Translate(Domain domain) {
switch (domain) {
case Domain::Unspecified:
return Network::Domain::Unspecified;
case Domain::INET:
return Network::Domain::INET;
default:
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
return {};
}
}
Domain Translate(Network::Domain domain) {
switch (domain) {
case Network::Domain::Unspecified:
return Domain::Unspecified;
case Network::Domain::INET:
return Domain::INET;
default:
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
return {};
}
}
Network::Type Translate(Type type) {
switch (type) {
case Type::Unspecified:
return Network::Type::Unspecified;
case Type::STREAM:
return Network::Type::STREAM;
case Type::DGRAM:
return Network::Type::DGRAM;
case Type::RAW:
return Network::Type::RAW;
case Type::SEQPACKET:
return Network::Type::SEQPACKET;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
return Network::Type{};
}
}
Type Translate(Network::Type type) {
switch (type) {
case Network::Type::Unspecified: return Type::Unspecified;
case Network::Type::STREAM: return Type::STREAM;
case Network::Type::DGRAM: return Type::DGRAM;
case Network::Type::RAW: return Type::RAW;
case Network::Type::SEQPACKET: return Type::SEQPACKET;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
return Type{};
}
}
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RAW) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RCCMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NVPII) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ARGUS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(EMCON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(XNET) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CHAOS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MUX) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MEAS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(HMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PRM) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK1) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK2) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF1) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF2) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IRTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(BLT) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(INP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(3PC)*/ \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDPR) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(XTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TPXX) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IL) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SDRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MHRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(BHA) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(INLSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SWIPE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NHRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MOBILE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TLSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SKIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(AHIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CFTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(HELLO) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATEXPAK) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(KRYPTOLAN) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RVD) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPPC) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ADFS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(VISA) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCV) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPNX) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPHB) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(WSN) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PVP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(BRSATMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBEXPAK) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(EON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(VMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SVMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(VINES) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCF) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(OSPFIGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SRPC) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(LARP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(AX25) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPEIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MICP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCCSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ETHERIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(APES) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(GMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCOMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MH) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(HIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SHIM6) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CARP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC)
[[nodiscard]] Network::Protocol Translate(Protocol protocol) {
switch (protocol) {
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Protocol::name: return Network::Protocol::name;
NETWORK_PROTOCOL_TRANSLATE_LIST
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
default:
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
return {};
}
}
[[nodiscard]] Protocol Translate(Network::Protocol protocol) {
switch (protocol) {
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Network::Protocol::name: return Protocol::name;
NETWORK_PROTOCOL_TRANSLATE_LIST
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
default:
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
return {};
}
}
#undef NETWORK_PROTOCOL_TRANSLATE_LIST
Network::PollEvents Translate(PollEvents flags) {
Network::PollEvents result{};
const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
if (True(flags & from)) {
flags &= ~from;
result |= to;
}
};
translate(PollEvents::In, Network::PollEvents::In);
translate(PollEvents::Pri, Network::PollEvents::Pri);
translate(PollEvents::Out, Network::PollEvents::Out);
translate(PollEvents::Err, Network::PollEvents::Err);
translate(PollEvents::Hup, Network::PollEvents::Hup);
translate(PollEvents::Nval, Network::PollEvents::Nval);
translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
translate(PollEvents::RdBand, Network::PollEvents::RdBand);
translate(PollEvents::WrBand, Network::PollEvents::WrBand);
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
return result;
}
PollEvents Translate(Network::PollEvents flags) {
PollEvents result{};
const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
if (True(flags & from)) {
flags &= ~from;
result |= to;
}
};
translate(Network::PollEvents::In, PollEvents::In);
translate(Network::PollEvents::Pri, PollEvents::Pri);
translate(Network::PollEvents::Out, PollEvents::Out);
translate(Network::PollEvents::Err, PollEvents::Err);
translate(Network::PollEvents::Hup, PollEvents::Hup);
translate(Network::PollEvents::Nval, PollEvents::Nval);
translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
translate(Network::PollEvents::RdBand, PollEvents::RdBand);
translate(Network::PollEvents::WrBand, PollEvents::WrBand);
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
return result;
}
Network::SockAddrIn Translate(SockAddrIn value) {
// All lengths are valid, from [0 upto 256]
return {
.family = Translate(Domain(value.family)),
.ip = value.ip,
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
};
}
SockAddrIn Translate(Network::SockAddrIn value) {
return {
.len = 16,
.family = static_cast<u8>(Translate(value.family)),
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
.ip = value.ip,
.zeroes = {},
};
}
Network::ShutdownHow Translate(ShutdownHow how) {
switch (how) {
case ShutdownHow::RD:
return Network::ShutdownHow::RD;
case ShutdownHow::WR:
return Network::ShutdownHow::WR;
case ShutdownHow::RDWR:
return Network::ShutdownHow::RDWR;
default:
UNIMPLEMENTED_MSG("Unimplemented how={}", how);
return {};
}
}
} // namespace Service::Sockets } // namespace Service::Sockets
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,49 +14,7 @@
namespace Service::Sockets { namespace Service::Sockets {
/// Translate abstract errno to guest errno
Errno Translate(Network::Errno value);
/// Translate abstract return value errno pair to guest return value errno pair
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
/// Translate abstract getaddrinfo error to guest getaddrinfo error
GetAddrInfoError Translate(Network::GetAddrInfoError value);
/// Translate guest error to string /// Translate guest error to string
const char* Translate(GetAddrInfoError value); const char* Translate(Network::GetAddrInfoError value);
/// Translate guest domain to abstract domain
Network::Domain Translate(Domain domain);
/// Translate abstract domain to guest domain
Domain Translate(Network::Domain domain);
/// Translate guest type to abstract type
Network::Type Translate(Type type);
/// Translate abstract type to guest type
Type Translate(Network::Type type);
/// Translate guest protocol to abstract protocol
Network::Protocol Translate(Protocol protocol);
/// Translate abstract protocol to guest protocol
Protocol Translate(Network::Protocol protocol);
/// Translate guest poll event flags to abstract poll event flags
Network::PollEvents Translate(PollEvents flags);
/// Translate abstract poll event flags to guest poll event flags
PollEvents Translate(Network::PollEvents flags);
/// Translate guest socket address structure to abstract socket address structure
Network::SockAddrIn Translate(SockAddrIn value);
/// Translate abstract socket address structure to guest socket address structure
SockAddrIn Translate(Network::SockAddrIn value);
/// Translate guest shutdown mode to abstract shutdown mode
Network::ShutdownHow Translate(ShutdownHow how);
} // namespace Service::Sockets } // namespace Service::Sockets
+2 -2
View File
@@ -132,7 +132,7 @@ public:
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u"); auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
if (bsd) { if (bsd) {
auto err = bsd->CloseImpl(fd); auto err = bsd->CloseImpl(fd);
if (err != Service::Sockets::Errno::SUCCESS) { if (err != Network::Errno::E_SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err); LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
} }
} }
@@ -203,7 +203,7 @@ private:
const bool non_block = mode == IoMode::NonBlocking; const bool non_block = mode == IoMode::NonBlocking;
const Network::Errno error = socket->SetNonBlock(non_block); const Network::Errno error = socket->SetNonBlock(non_block);
if (error != Network::Errno::SUCCESS) { if (error != Network::Errno::E_SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block); LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
} }
return ResultSuccess; return ResultSuccess;
@@ -290,10 +290,10 @@ public:
BIO_clear_retry_flags(bio); BIO_clear_retry_flags(bio);
auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0); auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
switch (err) { switch (err) {
case Network::Errno::SUCCESS: case Network::Errno::E_SUCCESS:
*actual_p = actual; *actual_p = actual;
return 1; return 1;
case Network::Errno::AGAIN: case Network::Errno::E_AGAIN:
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY); BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
return 0; return 0;
default: default:
@@ -309,13 +309,13 @@ public:
BIO_clear_retry_flags(bio); BIO_clear_retry_flags(bio);
auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len}); auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
switch (err) { switch (err) {
case Network::Errno::SUCCESS: case Network::Errno::E_SUCCESS:
*actual_p = actual; *actual_p = actual;
if (actual == 0) { if (actual == 0) {
self->got_read_eof = true; self->got_read_eof = true;
} }
return actual ? 1 : 0; return actual ? 1 : 0;
case Network::Errno::AGAIN: case Network::Errno::E_AGAIN:
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY); BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
return 0; return 0;
default: default:
@@ -146,11 +146,11 @@ public:
const auto read_span = std::span(ciphertext_read_buf).subspan(offset, fill_size); const auto read_span = std::span(ciphertext_read_buf).subspan(offset, fill_size);
const auto [actual, err] = socket->Recv(0, read_span); const auto [actual, err] = socket->Recv(0, read_span);
switch (err) { switch (err) {
case Network::Errno::SUCCESS: case Network::Errno::E_SUCCESS:
ASSERT(static_cast<size_t>(actual) <= fill_size); ASSERT(static_cast<size_t>(actual) <= fill_size);
ciphertext_read_buf.resize(offset + actual); ciphertext_read_buf.resize(offset + actual);
return ResultSuccess; return ResultSuccess;
case Network::Errno::AGAIN: case Network::Errno::E_AGAIN:
ciphertext_read_buf.resize(offset); ciphertext_read_buf.resize(offset);
return ResultWouldBlock; return ResultWouldBlock;
default: default:
@@ -165,12 +165,12 @@ public:
while (!ciphertext_write_buf.empty()) { while (!ciphertext_write_buf.empty()) {
const auto [actual, err] = socket->Send(ciphertext_write_buf, 0); const auto [actual, err] = socket->Send(ciphertext_write_buf, 0);
switch (err) { switch (err) {
case Network::Errno::SUCCESS: case Network::Errno::E_SUCCESS:
ASSERT(static_cast<size_t>(actual) <= ciphertext_write_buf.size()); ASSERT(static_cast<size_t>(actual) <= ciphertext_write_buf.size());
ciphertext_write_buf.erase(ciphertext_write_buf.begin(), ciphertext_write_buf.erase(ciphertext_write_buf.begin(),
ciphertext_write_buf.begin() + actual); ciphertext_write_buf.begin() + actual);
break; break;
case Network::Errno::AGAIN: case Network::Errno::E_AGAIN:
return ResultWouldBlock; return ResultWouldBlock;
default: default:
LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err); LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err);
@@ -195,7 +195,7 @@ public:
LOG_CRITICAL(Service_SSL, "op={}, offset={} actual={}/{} err={}", is_read, offset, LOG_CRITICAL(Service_SSL, "op={}, offset={} actual={}/{} err={}", is_read, offset,
actual, cur.size(), static_cast<s32>(err)); actual, cur.size(), static_cast<s32>(err));
switch (err) { switch (err) {
case Network::Errno::SUCCESS: case Network::Errno::E_SUCCESS:
offset += actual; offset += actual;
if (actual == 0) { if (actual == 0) {
ASSERT(is_read); ASSERT(is_read);
@@ -203,7 +203,7 @@ public:
return errSecEndOfData; return errSecEndOfData;
} }
break; break;
case Network::Errno::AGAIN: case Network::Errno::E_AGAIN:
*dataLength = offset; *dataLength = offset;
return errSSLWouldBlock; return errSSLWouldBlock;
default: default:
+3 -2
View File
@@ -1,10 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <common/socket_types.h>
#include <mutex> #include <mutex>
#include "core/internal_network/socket_types.h"
namespace Network { namespace Network {
File diff suppressed because it is too large Load Diff
+9 -63
View File
@@ -13,7 +13,7 @@
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/socket_types.h" #include "core/internal_network/socket_types.h"
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
@@ -31,68 +31,10 @@ namespace Network {
class SocketBase; class SocketBase;
class Socket; class Socket;
/// Error code for network functions struct HostPollFD {
enum class Errno { SocketBase* socket = nullptr;
SUCCESS, Network::PollEvents events = {};
BADF, Network::PollEvents revents = {};
INVAL,
MFILE,
PIPE,
NOTCONN,
AGAIN,
CONNREFUSED,
CONNRESET,
CONNABORTED,
HOSTUNREACH,
NETDOWN,
NETUNREACH,
TIMEDOUT,
MSGSIZE,
INPROGRESS,
ISCONN,
OTHER,
};
enum class GetAddrInfoError {
SUCCESS,
ADDRFAMILY,
AGAIN,
BADFLAGS,
FAIL,
FAMILY,
MEMORY,
NODATA,
NONAME,
SERVICE,
SOCKTYPE,
SYSTEM,
BADHINTS,
PROTOCOL,
OVERFLOW_,
OTHER,
};
/// Cross-platform poll fd structure
enum class PollEvents : u16 {
// Using Pascal case because IN is a macro on Windows.
In = 1 << 0,
Pri = 1 << 1,
Out = 1 << 2,
Err = 1 << 3,
Hup = 1 << 4,
Nval = 1 << 5,
RdNorm = 1 << 6,
RdBand = 1 << 7,
WrBand = 1 << 8,
};
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
SocketBase* socket;
PollEvents events;
PollEvents revents;
}; };
class NetworkInstance { class NetworkInstance {
@@ -101,6 +43,10 @@ public:
~NetworkInstance(); ~NetworkInstance();
}; };
sockaddr_in TranslateFromSockAddrIn(Network::SockAddrIn input);
Network::SockAddrIn TranslateToSockAddrIn(sockaddr_in input);
s32 TranslateMsgOptToNative(s32 flags);
void CancelPendingSocketOperations(); void CancelPendingSocketOperations();
void RestartSocketOperations(); void RestartSocketOperations();
@@ -46,7 +46,7 @@ namespace Network {
#ifdef _WIN32 #ifdef _WIN32
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() { std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
ULONG buf_size = 0; ULONG buf_size = 0;
if (GetAdaptersAddresses( if (GetAdaptersAddresses(
@@ -66,7 +66,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
return {}; return {};
} }
std::vector<Network::NetworkInterface> result; std::vector<NetworkInterface> result;
for (auto* a = addrs; a; a = a->Next) { for (auto* a = addrs; a; a = a->Next) {
@@ -89,7 +89,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr) gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
->sin_addr; ->sin_addr;
result.emplace_back(Network::NetworkInterface{ result.emplace_back(NetworkInterface{
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}), .name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
.ip_address = ip, .ip_address = ip,
.subnet_mask = mask, .subnet_mask = mask,
@@ -103,7 +103,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
#else #else
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() { std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#if defined(__ANDROID__) || defined(__linux__) #if defined(__ANDROID__) || defined(__linux__)
struct ifaddrs* ifaddr = nullptr; struct ifaddrs* ifaddr = nullptr;
if (getifaddrs(&ifaddr) != 0) { if (getifaddrs(&ifaddr) != 0) {
@@ -135,7 +135,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0"); LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
} }
#endif #endif
std::vector<Network::NetworkInterface> ifaces; std::vector<NetworkInterface> ifaces;
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */ if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */ || ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
@@ -149,7 +149,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
}); });
in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here
gw.s_addr = it != routes.end() ? it->gateway : 0; gw.s_addr = it != routes.end() ? it->gateway : 0;
ifaces.emplace_back(Network::NetworkInterface{ ifaces.emplace_back(NetworkInterface{
.name = ifa->ifa_name, .name = ifa->ifa_name,
.ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr, .ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr, .subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
@@ -159,7 +159,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
return ifaces; return ifaces;
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__)
std::vector<Network::NetworkInterface> ifaces; std::vector<NetworkInterface> ifaces;
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
if (fd < 0) { if (fd < 0) {
LOG_ERROR(Network, "socket: {}", std::strerror(errno)); LOG_ERROR(Network, "socket: {}", std::strerror(errno));
@@ -191,7 +191,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
size_t msglen = rtm->rtm_msglen - sizeof(*ifm); size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
char const* p = (char const*)(ifm + 1); char const* p = (char const*)(ifm + 1);
Network::NetworkInterface iface{}; NetworkInterface iface{};
for (size_t i = 0; i < RTAX_MAX; i++) for (size_t i = 0; i < RTAX_MAX; i++)
if ((ifm->ifm_addrs & (1 << i)) != 0) { if ((ifm->ifm_addrs & (1 << i)) != 0) {
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p); struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
@@ -220,7 +220,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
#endif // _WIN32 #endif // _WIN32
std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() { std::optional<NetworkInterface> GetSelectedNetworkInterface() {
auto const& sel_if = Settings::values.network_interface.GetValue(); auto const& sel_if = Settings::values.network_interface.GetValue();
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) { if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
if (sel_if.empty()) if (sel_if.empty())
+277
View File
@@ -0,0 +1,277 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <cerrno>
#include <chrono>
#include <mutex>
#include <thread>
#ifdef __unix__
#include <spawn.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
#include "common/assert.h"
#include "common/logging.h"
#include "core/internal_network/socket_icmp.h"
extern "C" {
extern char **environ;
}
namespace Network {
namespace {
u16 ComputeChecksum(std::span<const u8> data) {
u32 sum = 0;
for (size_t i = 0; i < data.size(); i += 2) {
u32 value = (u32(data[i + 0]) << 8ull) | u32(data[i + 1]); //big endian
sum += value;
}
if (data.size() % 2 != 0){
sum += u16(data[data.size() - 1]) << 8;
}
while ((sum >> 16) != 0)
sum = (sum & 0xffff) + (sum >> 16);
return (~sum) & 0xffff;
}
}
IcmpSocket::~IcmpSocket() {
if (fd == INVALID_SOCKET) {
return;
}
fd = INVALID_SOCKET;
}
Errno IcmpSocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
LOG_WARNING(Network, "(stubbed) level={},optname={},optval={}", level, optname, optval.size());
if (optname == Network::OptName::RCVTIMEO) {
if (optval.size() < sizeof(Network::Timeval))
return Errno::E_INVAL;
std::memcpy(&rcv_timeo, optval.data(), sizeof(rcv_timeo));
}
return Errno::E_SUCCESS;
}
Errno IcmpSocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
return Errno::E_SUCCESS;
}
std::pair<IcmpSocket::AcceptResult, Errno> IcmpSocket::Accept() {
LOG_WARNING(Network, "(stubbed) called");
return {AcceptResult{}, Errno::E_SUCCESS};
}
Errno IcmpSocket::Connect(Network::SockAddrIn addr_in) {
LOG_WARNING(Network, "(stubbed) called");
connected_addr = addr_in;
return Errno::E_SUCCESS;
}
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetPeerName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
}
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetSockName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
}
Errno IcmpSocket::Bind(Network::SockAddrIn addr) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::E_SUCCESS;
}
Errno IcmpSocket::Listen(s32 backlog) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::E_SUCCESS;
}
Errno IcmpSocket::Shutdown(ShutdownHow how) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::E_SUCCESS;
}
std::pair<s32, Errno> IcmpSocket::Recv(int flags, std::span<u8> message) {
LOG_DEBUG(Network, "(stubbed) called");
return connected_addr.has_value()
? RecvFrom(flags, message, nullptr)
: std::make_pair(s32(0), Errno::E_NOTCONN);
}
std::pair<s32, Errno> IcmpSocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "(stubbed) called");
ASSERT(flags == 0);
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
#if !defined(__OPENORBIS__) && (defined(__FreeBSD__) || defined(__linux__))
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
const auto timestamp = std::chrono::steady_clock::now();
while (true) {
{
std::lock_guard guard(pings_mutex);
// find ping process that is finished running
for (auto it = pings.begin(); it != pings.end();) {
pid_t result = waitpid(it->ping_pid, &it->ping_status, WNOHANG);
// ping process is still running, go to next
if (result != it->ping_pid) {
++it;
continue;
}
// ping process is finished, remove and handle it
it = pings.erase(it);
if (it->ping_status == 0) {
if (addr) {
addr->family = it->family;
addr->ip = it->ip;
addr->portno = it->portno;
addr->len = 16;
addr->zeroes = {};
}
std::array<u8, 8> data{
0,
0,
0, //checksum
0,
it->seq_ident[0],
it->seq_ident[1],
it->seq_ident[2],
it->seq_ident[3]
};
auto const csum = ComputeChecksum(std::span<const u8>{data.begin(), data.end()});
data[2] = u8(csum >> 8); //hi
data[3] = u8(csum); //lo
auto const n = std::min(data.size(), message.size());
std::copy(data.begin(), data.begin() + n, message.begin());
return {s32(n), Errno::E_SUCCESS};
}
}
}
if (!blocking)
return {-1, Errno::E_AGAIN};
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
if (time_diff_ms > rcv_timeout_ms)
return {-1, Errno::E_TIMEDOUT};
std::this_thread::yield();
}
#endif
return {-1, Errno::E_INVAL};
}
std::pair<s32, Errno> IcmpSocket::Send(std::span<const u8> message, int flags) {
LOG_DEBUG(Network, "(stubbed) called");
if (connected_addr.has_value())
return SendTo(flags, message, std::addressof(connected_addr.value()));
return {s32(0), Errno::E_NOTCONN};
}
std::pair<s32, Errno> IcmpSocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "(stubbed) called");
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
// 0 -> 8 (IPv4), 128 (IPv6)
// 1 -> 0
// 2..4 -> checksum
// 4..6 -> ident
// 6..8 -> seq
// PLEASE DON'T KILL ME, I SWEAR THIS IS LEGITIMATELY THE BEST WAY TO DO IT
// IF YOU OPEN socket() GOOGLE WILL STRAIGHT UP IP BAN YOU AFTER 2 HOURS
#if !(defined(__OPENORBIS__) || defined(__ANDROID__)) && (defined(__FreeBSD__) || defined(__linux__))
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
if (!addr)
return {-1, Errno::E_DESTADDRREQ};
if (message.size() >= 8) {
std::string ip_str = fmt::format(
"{}.{}.{}.{}",
addr->ip[0],
addr->ip[1],
addr->ip[2],
addr->ip[3]
);
#ifdef __FreeBSD__
// ping -W option is a nonfractional int (milliseconds)
std::string timeout_str = fmt::format("{}", rcv_timeout_ms);
std::vector<char*> argv = {
const_cast<char*>("ping"),
const_cast<char*>("-c"),
const_cast<char*>("1"),
const_cast<char*>("-W"),
timeout_str.data(),
ip_str.data(),
nullptr
};
#elif defined(__linux__)
// ping -W option is a fractional float (seconds)
auto const rcv_timeout_s = f64(rcv_timeout_ms) / 1000.0;
std::string timeout_str = fmt::format("{}", rcv_timeout_s);
std::vector<char*> argv = {
const_cast<char*>("ping"),
const_cast<char*>("-c"),
const_cast<char*>("1"),
const_cast<char*>("-W"),
timeout_str.data(),
ip_str.data(),
nullptr
};
#endif
pid_t ping_pid;
// we should pass in attributes to stop stdout spam, but im too lazy to figure that out
if (posix_spawnp(&ping_pid, "ping", nullptr, nullptr, argv.data(), environ) != 0) {
LOG_ERROR(Network, "Unable to start ping process for emulated ICMP socket");
return {-1, Errno::E_INVAL};
}
std::lock_guard guard(pings_mutex);
if (pings.size() >= pings.max_size())
pings.erase(pings.begin());
pings.push_back(PingProcessData{
.ip = addr->ip,
.portno = addr->portno,
.ping_pid = ping_pid,
.ping_status = 0,
.seq_ident = {
message[4],
message[5],
message[6],
message[7]
},
.family = addr->family,
});
return {s32(message.size()), Errno::E_SUCCESS};
}
#endif
return {-1, Errno::E_INVAL};
}
Errno IcmpSocket::Close() {
LOG_DEBUG(Network, "called");
fd = INVALID_SOCKET;
return Errno::E_SUCCESS;
}
std::pair<Errno, Errno> IcmpSocket::GetPendingError() {
LOG_DEBUG(Network, "called");
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
}
bool IcmpSocket::IsOpened() const {
return fd != INVALID_SOCKET;
}
void IcmpSocket::HandleProxyPacket(const ProxyPacket& packet) {
LOG_WARNING(Network, "(stubbed) called");
}
Errno IcmpSocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::E_SUCCESS;
}
} // namespace Network
+56
View File
@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <span>
#include <utility>
#include <sys/types.h>
#include <mutex>
#include <boost/container/static_vector.hpp>
#include "core/internal_network/socket_types.h"
#include "core/internal_network/sockets.h"
namespace Network {
struct PingProcessData {
IPv4Address ip;
u16 portno;
pid_t ping_pid;
pid_t ping_status;
std::array<u8, 4> seq_ident;
u8 family;
};
class IcmpSocket : public Network::SocketBase {
public:
explicit IcmpSocket() noexcept = default;
~IcmpSocket() override;
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
Errno Close() override;
std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(Network::SockAddrIn addr_in) override;
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
Errno Bind(Network::SockAddrIn addr) override;
Errno Listen(s32 backlog) override;
Errno Shutdown(ShutdownHow how) override;
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
std::pair<Errno, Errno> GetPendingError() override;
bool IsOpened() const override;
void HandleProxyPacket(const ProxyPacket& packet) override;
Errno SetNonBlock(bool enable) override;
boost::container::static_vector<PingProcessData, 128> pings;
std::optional<SockAddrIn> connected_addr;
std::mutex pings_mutex;
Network::Timeval rcv_timeo;
bool blocking = true;
};
} // namespace Network
+70 -108
View File
@@ -47,71 +47,82 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
received_packets.push(decompressed); received_packets.push(decompressed);
} }
template <typename T> Errno ProxySocket::SetNonBlock(bool enable) {
Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) { blocking = !enable;
LOG_DEBUG(Network, "(STUBBED) called"); return Errno::E_SUCCESS;
return Errno::SUCCESS; }
Errno ProxySocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
LOG_DEBUG(Network, "level={},optname={},optval={}", level, optname, optval.size());
// numeric values?
if (optval.size() >= sizeof(u32)) {
u32 value;
std::memcpy(&value, optval.data(), sizeof(value));
if (optname == Network::OptName::BROADCAST)
broadcast = bool(value);
if (optname == Network::OptName::SNDTIMEO)
send_timeout = value;
if (optname == Network::OptName::RCVTIMEO)
receive_timeout = value;
}
return Errno::E_SUCCESS;
} }
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) { Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
protocol = socket_protocol; protocol = socket_protocol;
SetSockOpt(fd, SO_TYPE, type); return Errno::E_SUCCESS;
return Errno::SUCCESS;
} }
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() { std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
LOG_WARNING(Network, "(STUBBED) called"); LOG_WARNING(Network, "(stubbed) called");
return {AcceptResult{}, Errno::SUCCESS}; return {AcceptResult{}, Errno::E_SUCCESS};
} }
Errno ProxySocket::Connect(SockAddrIn addr_in) { Errno ProxySocket::Connect(Network::SockAddrIn addr_in) {
LOG_WARNING(Network, "(STUBBED) called"); LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS; return Errno::E_SUCCESS;
} }
std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() { std::pair<Network::SockAddrIn, Errno> ProxySocket::GetPeerName() {
LOG_WARNING(Network, "(STUBBED) called"); LOG_WARNING(Network, "(stubbed) called");
return {SockAddrIn{}, Errno::SUCCESS}; return {Network::SockAddrIn{}, Errno::E_SUCCESS};
} }
std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() { std::pair<Network::SockAddrIn, Errno> ProxySocket::GetSockName() {
LOG_WARNING(Network, "(STUBBED) called"); LOG_WARNING(Network, "(stubbed) called");
return {SockAddrIn{}, Errno::SUCCESS}; return {Network::SockAddrIn{}, Errno::E_SUCCESS};
} }
Errno ProxySocket::Bind(SockAddrIn addr) { Errno ProxySocket::Bind(Network::SockAddrIn addr) {
if (is_bound) { if (is_bound) {
LOG_WARNING(Network, "Rebinding Socket is unimplemented!"); LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
return Errno::SUCCESS; return Errno::E_SUCCESS;
} }
local_endpoint = addr; local_endpoint = addr;
is_bound = true; is_bound = true;
return Errno::E_SUCCESS;
return Errno::SUCCESS;
} }
Errno ProxySocket::Listen(s32 backlog) { Errno ProxySocket::Listen(s32 backlog) {
LOG_WARNING(Network, "(STUBBED) called"); LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS; return Errno::E_SUCCESS;
} }
Errno ProxySocket::Shutdown(ShutdownHow how) { Errno ProxySocket::Shutdown(ShutdownHow how) {
LOG_WARNING(Network, "(STUBBED) called"); LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS; return Errno::E_SUCCESS;
} }
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) { std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
LOG_WARNING(Network, "(STUBBED) called"); LOG_WARNING(Network, "(stubbed) called");
ASSERT(flags == 0); ASSERT(flags == 0);
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)())); ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
return {s32(0), Errno::E_SUCCESS};
return {static_cast<s32>(0), Errno::SUCCESS};
} }
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) { std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
ASSERT(flags == 0); ASSERT(flags == 0);
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)())); ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
// TODO (flTobi): Verify the timeout behavior and break when connection is lost // TODO (flTobi): Verify the timeout behavior and break when connection is lost
const auto timestamp = std::chrono::steady_clock::now(); const auto timestamp = std::chrono::steady_clock::now();
@@ -128,88 +139,85 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, So
} }
if (!blocking) { if (!blocking) {
return {-1, Errno::AGAIN}; return {-1, Errno::E_AGAIN};
} }
std::this_thread::yield(); std::this_thread::yield();
const auto time_diff = std::chrono::steady_clock::now() - timestamp; const auto time_diff = std::chrono::steady_clock::now() - timestamp;
const auto time_diff_ms = const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
if (time_diff_ms > timeout) { if (time_diff_ms > timeout) {
return {-1, Errno::TIMEDOUT}; return {-1, Errno::E_TIMEDOUT};
} }
} }
} }
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr, std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length) {
std::size_t max_length) { LOG_DEBUG(Network, "called");
ProxyPacket& packet = received_packets.front(); ProxyPacket& packet = received_packets.front();
if (addr) { if (addr) {
addr->family = Domain::INET; addr->len = 16;
addr->family = u8(Network::Domain::INET);
addr->ip = packet.local_endpoint.ip; // The senders ip address addr->ip = packet.local_endpoint.ip; // The senders ip address
addr->portno = packet.local_endpoint.portno; // The senders port number addr->portno = packet.local_endpoint.portno; // The senders port number
addr->zeroes = {};
} }
bool peek = (flags & FLAG_MSG_PEEK) != 0; bool peek = (flags & u32(Network::MsgOpt::PEEK)) != 0;
std::size_t read_bytes; std::size_t read_bytes;
if (packet.data.size() > max_length) { if (packet.data.size() > max_length) {
read_bytes = max_length; read_bytes = max_length;
memcpy(message.data(), packet.data.data(), max_length); std::memcpy(message.data(), packet.data.data(), max_length);
if (protocol == Protocol::UDP) { if (protocol == Protocol::UDP) {
if (!peek) { if (!peek) {
received_packets.pop(); received_packets.pop();
} }
return {-1, Errno::MSGSIZE}; return {-1, Errno::E_MSGSIZE};
} else if (protocol == Protocol::TCP) { } else if (protocol == Protocol::TCP) {
std::vector<u8> numArray(packet.data.size() - max_length); std::vector<u8> numArray(packet.data.size() - max_length);
std::copy(packet.data.begin() + max_length, packet.data.end(), std::copy(packet.data.begin() + max_length, packet.data.end(), std::back_inserter(numArray));
std::back_inserter(numArray));
packet.data = numArray; packet.data = numArray;
} }
} else { } else {
read_bytes = packet.data.size(); read_bytes = packet.data.size();
memcpy(message.data(), packet.data.data(), read_bytes); std::memcpy(message.data(), packet.data.data(), read_bytes);
if (!peek) { if (!peek) {
received_packets.pop(); received_packets.pop();
} }
} }
return {static_cast<u32>(read_bytes), Errno::SUCCESS}; return {u32(read_bytes), Errno::E_SUCCESS};
} }
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) { std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
LOG_WARNING(Network, "(STUBBED) called"); LOG_WARNING(Network, "(stubbed) called");
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)())); ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
ASSERT(flags == 0); ASSERT(flags == 0);
return {s32(0), Errno::E_SUCCESS};
return {static_cast<s32>(0), Errno::SUCCESS};
} }
void ProxySocket::SendPacket(ProxyPacket& packet) { void ProxySocket::SendPacket(ProxyPacket& packet) {
if (auto room_member = Network::GetRoomMember().lock()) { if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) { if (room_member->IsConnected()) {
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), packet.data.size());
packet.data.size());
room_member->SendProxyPacket(packet); room_member->SendProxyPacket(packet);
} }
} }
} }
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message, std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
const SockAddrIn* addr) { LOG_DEBUG(Network, "called");
ASSERT(flags == 0); ASSERT(flags == 0);
if (!is_bound) { if (!is_bound) {
LOG_ERROR(Network, "ProxySocket is not bound!"); LOG_ERROR(Network, "ProxySocket is not bound!");
return {static_cast<s32>(message.size()), Errno::SUCCESS}; return {s32(message.size()), Errno::E_SUCCESS};
} }
if (auto room_member = Network::GetRoomMember().lock()) { if (auto room_member = Network::GetRoomMember().lock()) {
if (!room_member->IsConnected()) { if (!room_member->IsConnected()) {
return {static_cast<s32>(message.size()), Errno::SUCCESS}; return {s32(message.size()), Errno::E_SUCCESS};
} }
} }
@@ -234,66 +242,20 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
SendPacket(packet); SendPacket(packet);
return {static_cast<s32>(message.size()), Errno::SUCCESS}; return {s32(message.size()), Errno::E_SUCCESS};
} }
Errno ProxySocket::Close() { Errno ProxySocket::Close() {
LOG_DEBUG(Network, "called");
fd = INVALID_SOCKET; fd = INVALID_SOCKET;
closed = true; closed = true;
return Errno::SUCCESS; return Errno::E_SUCCESS;
}
Errno ProxySocket::SetLinger(bool enable, u32 linger) {
struct Linger {
u16 linger_enable;
u16 linger_time;
} values;
values.linger_enable = enable ? 1 : 0;
values.linger_time = static_cast<u16>(linger);
return SetSockOpt(fd, SO_LINGER, values);
}
Errno ProxySocket::SetReuseAddr(bool enable) {
return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
}
Errno ProxySocket::SetBroadcast(bool enable) {
broadcast = enable;
return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
}
Errno ProxySocket::SetSndBuf(u32 value) {
return SetSockOpt(fd, SO_SNDBUF, value);
}
Errno ProxySocket::SetKeepAlive(bool enable) {
return Errno::SUCCESS;
}
Errno ProxySocket::SetRcvBuf(u32 value) {
return SetSockOpt(fd, SO_RCVBUF, value);
}
Errno ProxySocket::SetSndTimeo(u32 value) {
send_timeout = value;
return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
}
Errno ProxySocket::SetRcvTimeo(u32 value) {
receive_timeout = value;
return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
}
Errno ProxySocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::SUCCESS;
} }
std::pair<Errno, Errno> ProxySocket::GetPendingError() { std::pair<Errno, Errno> ProxySocket::GetPendingError() {
LOG_DEBUG(Network, "(STUBBED) called"); LOG_DEBUG(Network, "called");
return {Errno::SUCCESS, Errno::SUCCESS}; return {Errno::E_SUCCESS, Errno::E_SUCCESS};
} }
bool ProxySocket::IsOpened() const { bool ProxySocket::IsOpened() const {
+10 -27
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -29,13 +29,13 @@ public:
std::pair<AcceptResult, Errno> Accept() override; std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(SockAddrIn addr_in) override; Errno Connect(Network::SockAddrIn addr_in) override;
std::pair<SockAddrIn, Errno> GetPeerName() override; std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<SockAddrIn, Errno> GetSockName() override; std::pair<Network::SockAddrIn, Errno> GetSockName() override;
Errno Bind(SockAddrIn addr) override; Errno Bind(Network::SockAddrIn addr) override;
Errno Listen(s32 backlog) override; Errno Listen(s32 backlog) override;
@@ -43,9 +43,9 @@ public:
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override; std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override; std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr, std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr,
std::size_t max_length); std::size_t max_length);
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override; std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
@@ -53,28 +53,11 @@ public:
void SendPacket(ProxyPacket& packet); void SendPacket(ProxyPacket& packet);
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) override; const Network::SockAddrIn* addr) override;
Errno SetLinger(bool enable, u32 linger) override;
Errno SetReuseAddr(bool enable) override;
Errno SetBroadcast(bool enable) override;
Errno SetKeepAlive(bool enable) override;
Errno SetSndBuf(u32 value) override;
Errno SetRcvBuf(u32 value) override;
Errno SetSndTimeo(u32 value) override;
Errno SetRcvTimeo(u32 value) override;
Errno SetNonBlock(bool enable) override; Errno SetNonBlock(bool enable) override;
template <typename T> Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
Errno SetSockOpt(SOCKET fd, int option, T value);
std::pair<Errno, Errno> GetPendingError() override; std::pair<Errno, Errno> GetPendingError() override;
@@ -86,7 +69,7 @@ private:
u32 send_timeout = 0; u32 send_timeout = 0;
u32 receive_timeout = 0; u32 receive_timeout = 0;
bool is_bound = false; bool is_bound = false;
SockAddrIn local_endpoint{}; Network::SockAddrIn local_endpoint{};
bool blocking = true; bool blocking = true;
std::queue<ProxyPacket> received_packets; std::queue<ProxyPacket> received_packets;
Protocol protocol; Protocol protocol;
+470
View File
@@ -0,0 +1,470 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <string>
#include "common/common_types.h"
#include "common/common_funcs.h"
// Most of these structures are direct mappings of guest's
// expectations for these values, in other words, they're the
// values that HOS is expected to use AND handle.
namespace Network {
enum class Errno : u32 {
E_SUCCESS = 0,
E_PERM = 1,
E_NOENT = 2,
E_SRCH = 3,
E_INTR = 4,
E_IO = 5,
E_NXIO = 6,
E_2BIG = 7,
E_NOEXEC = 8,
E_BADF = 9,
E_CHILD = 10,
E_AGAIN = 11,
E_NOMEM = 12,
E_ACCES = 13,
E_FAULT = 14,
E_NOTBLK = 15,
E_BUSY = 16,
E_EXIST = 17,
E_XDEV = 18,
E_NODEV = 19,
E_NOTDIR = 20,
E_ISDIR = 21,
E_INVAL = 22,
E_NFILE = 23,
E_MFILE = 24,
E_NOTTY = 25,
E_TXTBSY = 26,
E_FBIG = 27,
E_NOSPC = 28,
E_SPIPE = 29,
E_ROFS = 30,
E_MLINK = 31,
E_PIPE = 32,
E_DOM = 33,
E_RANGE = 34,
E_DEADLK = 35,
E_NAMETOOLONG = 36,
E_NOLCK = 37,
E_NOSYS = 38,
E_NOTEMPTY = 39,
E_LOOP = 40,
E_NOMSG = 42,
E_IDRM = 43,
E_CHRNG = 44,
E_L2NSYNC = 45,
E_L3HLT = 46,
E_L3RST = 47,
E_LNRNG = 48,
E_UNATCH = 49,
E_NOCSI = 50,
E_L2HLT = 51,
E_BADE = 52,
E_BADR = 53,
E_XFULL = 54,
E_NOANO = 55,
E_BADRQC = 56,
E_BADSSL = 57,
E_BFONT = 59,
E_NOSTR = 60,
E_NODATA = 61,
E_TIME = 62,
E_NOSR = 63,
E_NONET = 64,
E_NOPKG = 65,
E_REMOTE = 66,
E_NOLINK = 67,
E_ADV = 68,
E_SRMNT = 69,
E_COMM = 70,
E_PROTO = 71,
E_MULTIHOP = 72,
E_DOTDOT = 73,
E_BADMSG = 74,
E_OVERFLOW = 75,
E_NOTUNUQ = 76,
E_BADFD = 77,
E_REMCHG = 78,
E_LIBACC = 79,
E_LIBBAD = 80,
E_LIBSCN = 81,
E_LIBMAX = 82,
E_LIBEXEC = 83,
E_ILSEQ = 84,
E_RESTART = 85,
E_STRPIPE = 86,
E_USERS = 87,
E_NOTSOCK = 88,
E_DESTADDRREQ = 89,
E_MSGSIZE = 90,
E_PROTOTYPE = 91,
E_NOPROTOOPT = 92,
E_PROTONOSUPPORT = 93,
E_SOCKTNOSUPPORT = 94,
E_OPNOTSUPP = 95,
E_PFNOSUPPORT = 96,
E_AFNOSUPPORT = 97,
E_ADDRINUSE = 98,
E_ADDRNOTAVAIL = 99,
E_NETDOWN = 100,
E_NETUNREACH = 101,
E_NETRESET = 102,
E_CONNABORTED = 103,
E_CONNRESET = 104,
E_NOBUFS = 105,
E_ISCONN = 106,
E_NOTCONN = 107,
E_SHUTDOWN = 108,
E_TOOMANYREFS = 109,
E_TIMEDOUT = 110,
E_CONNREFUSED = 111,
E_HOSTDOWN = 112,
E_HOSTUNREACH = 113,
E_ALREADY = 114,
E_INPROGRESS = 115,
E_STALE = 116,
E_UCLEAN = 117,
E_NOTNAM = 118,
E_NAVAIL = 119,
E_ISNAM = 120,
E_REMOTEIO = 121,
E_DQUOT = 122,
E_NOMEDIUM = 123,
E_MEDIUMTYPE = 124,
E_CANCELED = 125,
E_NOKEY = 126,
E_KEYEXPIRED = 127,
E_KEYREVOKED = 128,
E_KEYREJECTED = 129,
E_OWNERDEAD = 130,
E_NOTRECOVERABLE = 131,
E_RFKILL = 132,
E_HWPOISON = 133,
E_PROCLIM = 156,
};
enum class GetAddrInfoError : s32 {
SUCCESS = 0,
ADDRFAMILY = 1,
AGAIN = 2,
BADFLAGS = 3,
FAIL = 4,
FAMILY = 5,
MEMORY = 6,
NODATA = 7,
NONAME = 8,
SERVICE = 9,
SOCKTYPE = 10,
SYSTEM = 11,
BADHINTS = 12,
PROTOCOL = 13,
OVERFLOW_ = 14, // avoid name collision with Windows macro
OTHER = 15,
};
enum class Domain : u32 {
Unspecified = 0,
UNIX = 1,
INET = 2,
IMPLINK = 3,
PUP = 4,
CHAOS = 5,
NETBIOS = 6,
ISO = 7,
ECMA = 8,
DATAKIT = 9,
CCITT = 10,
SNA = 11,
DECnet = 12,
DLI = 13,
LAT = 14,
HYLINK = 15,
APPLETALK = 16,
ROUTE = 17,
LINK = 18,
COIP = 20,
CNT = 21,
IPX = 23,
SIP = 24,
ISDN = 26,
INET6 = 28,
NATM = 29,
ATM = 30,
NETGRAPH = 32,
SLOW = 33,
SCLUSTER = 34,
ARP = 35,
BLUETOOTH = 36,
IEEE80211 = 37,
NETLINK = 38,
INET_SDP = 40,
INET6_SDP = 42,
};
enum class Type : u32 {
Unspecified = 0,
STREAM = 1,
DGRAM = 2,
RAW = 3,
RDM = 4,
SEQPACKET = 5,
};
enum class Protocol : u32 {
IP = 0,
ICMP = 1,
TCP = 6,
UDP = 17,
//
IPV6 = 41,
RAW = 255,
//
HOPOPTS = 0,
IGMP = 2,
GGP = 3,
IPV4 = 4,
ST = 7,
EGP = 8,
PIGP = 9,
RCCMON = 10,
NVPII = 11,
PUP = 12,
ARGUS = 13,
EMCON = 14,
XNET = 15,
CHAOS = 16,
MUX = 18,
MEAS = 19,
HMP = 20,
PRM = 21,
IDP = 22,
TRUNK1 = 23,
TRUNK2 = 24,
LEAF1 = 25,
LEAF2 = 26,
RDP = 27,
IRTP = 28,
TP = 29,
BLT = 30,
NSP = 31,
INP = 32,
DCCP = 33,
//3PC = 34,
IDPR = 35,
XTP = 36,
DDP = 37,
CMTP = 38,
TPXX = 39,
IL = 40,
SDRP = 42,
ROUTING = 43,
FRAGMENT = 44,
IDRP = 45,
RSVP = 46,
GRE = 47,
MHRP = 48,
BHA = 49,
ESP = 50,
AH = 51,
INLSP = 52,
SWIPE = 53,
NHRP = 54,
MOBILE = 55,
TLSP = 56,
SKIP = 57,
ICMPV6 = 58,
NONE = 59,
DSTOPTS = 60,
AHIP = 61,
CFTP = 62,
HELLO = 63,
SATEXPAK = 64,
KRYPTOLAN = 65,
RVD = 66,
IPPC = 67,
ADFS = 68,
SATMON = 69,
VISA = 70,
IPCV = 71,
CPNX = 72,
CPHB = 73,
WSN = 74,
PVP = 75,
BRSATMON = 76,
ND = 77,
WBMON = 78,
WBEXPAK = 79,
EON = 80,
VMTP = 81,
SVMTP = 82,
VINES = 83,
TTP = 84,
IGP = 85,
DGP = 86,
TCF = 87,
IGRP = 88,
OSPFIGP = 89,
SRPC = 90,
LARP = 91,
MTP = 92,
AX25 = 93,
IPEIP = 94,
MICP = 95,
SCCSP = 96,
ETHERIP = 97,
ENCAP = 98,
APES = 99,
GMTP = 100,
IPCOMP = 108,
SCTP = 132,
MH = 135,
UDPLITE = 136,
HIP = 139,
SHIM6 = 140,
PIM = 103,
CARP = 112,
PGM = 113,
MPLS = 137,
PFSYNC = 240,
};
enum class SocketLevel : u32 {
IP = 0,
ICMP = 1,
TCP = 6,
UDP = 17,
CONFIG = 0xfffe,
SOCKET = 0xffff, // i.e. SOL_SOCKET
};
enum class MsgOpt : u32 {
OOB = 0x00001,
PEEK = 0x00002,
DONTROUTE = 0x00004,
EOR_ = 0x00008,
TRUNC = 0x00010,
CTRUNC = 0x00020,
WAITALL = 0x00040,
DONTWAIT = 0x00080,
EOF_ = 0x00100,
NOSIGNAL = 0x20000,
};
enum class OptName : u32 {
DEBUG = 0x0001,
ACCEPTCONN = 0x0002,
REUSEADDR = 0x0004,
KEEPALIVE = 0x0008,
DONTROUTE = 0x0010,
BROADCAST = 0x0020,
USELOOPBACK = 0x0040,
LINGER = 0x0080,
OOBINLINE = 0x0100,
REUSEPORT = 0x0200,
TIMESTAMP = 0x0400,
NOSIGPIPE = 0x0800, // at least according to libnx
ACCEPTFILER = 0x1000,
SNDBUF = 0x1001,
RCVBUF = 0x1002,
SNDTIMEO = 0x1005,
RCVTIMEO = 0x1006,
ERROR_ = 0x1007, // avoid name collision with Windows macro
ACCEPTFILTER = 0x1000,
BINTIME = 0x2000,
NO_OFFLOAD = 0x4000,
NO_DDP = 0x8000,
};
enum class TcpOptName : u32 {
NODELAY = 0x0001,
MAXSEG = 0x0002,
NOPUSH = 0x0004,
NOOPT = 0x0008,
MS5SIG = 0x0010,
INFO = 0x0020
};
enum class ShutdownHow : s32 {
RD = 0,
WR = 1,
RDWR = 2,
};
enum class FcntlCmd : s32 {
GETFL = 3,
SETFL = 4,
};
enum class FcntlFlags : u32 {
NONBLOCK = 0x004,
NONBLOCK_NX = 0x800,
// Provided for convenience
NONBLOCK_ANY = u32(NONBLOCK) | u32(NONBLOCK_NX),
};
/// Array of IPv4 address
using IPv4Address = std::array<u8, 4>;
struct SockAddrIn {
u8 len;
u8 family;
u16 portno;
IPv4Address ip;
std::array<u8, 248> zeroes;
};
static_assert(sizeof(SockAddrIn) == 0x100);
enum class PollEvents : u16 {
// Using Pascal case because IN is a macro on Windows.
IN_ = 0x0001,
PRI_ = 0x0002,
OUT_ = 0x0004,
ERR_ = 0x0008,
HUP_ = 0x0010,
NVAL = 0x0020,
RDNORM = 0x0040,
RDBAND = 0x0080,
WRBAND = 0x0100,
IGNEOF = 0x2000,
};
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
s32 fd;
Network::PollEvents events;
Network::PollEvents revents;
};
static_assert(sizeof(PollFD) == 8);
struct Linger {
s32 onoff;
s32 linger;
};
static_assert(sizeof(Linger) == 8);
struct Timeval {
u64 tv_sec;
u64 tv_usec;
};
static_assert(sizeof(Timeval) == 16);
/// @brief Cross-platform addrinfo structure (not guest)
struct AddrInfo {
Domain family;
Type socket_type;
Protocol protocol;
SockAddrIn addr;
std::optional<std::string> canon_name;
};
} // namespace Network
+19 -54
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -30,7 +30,7 @@ public:
struct AcceptResult { struct AcceptResult {
std::unique_ptr<SocketBase> socket; std::unique_ptr<SocketBase> socket;
SockAddrIn sockaddr_in; Network::SockAddrIn sockaddr_in;
}; };
SocketBase() = default; SocketBase() = default;
@@ -46,13 +46,13 @@ public:
virtual std::pair<AcceptResult, Errno> Accept() = 0; virtual std::pair<AcceptResult, Errno> Accept() = 0;
virtual Errno Connect(SockAddrIn addr_in) = 0; virtual Errno Connect(Network::SockAddrIn addr_in) = 0;
virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0; virtual std::pair<Network::SockAddrIn, Errno> GetPeerName() = 0;
virtual std::pair<SockAddrIn, Errno> GetSockName() = 0; virtual std::pair<Network::SockAddrIn, Errno> GetSockName() = 0;
virtual Errno Bind(SockAddrIn addr) = 0; virtual Errno Bind(Network::SockAddrIn addr) = 0;
virtual Errno Listen(s32 backlog) = 0; virtual Errno Listen(s32 backlog) = 0;
@@ -60,31 +60,16 @@ public:
virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0; virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 0; virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) = 0;
virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0; virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) = 0;
const SockAddrIn* addr) = 0;
virtual Errno SetLinger(bool enable, u32 linger) = 0;
virtual Errno SetReuseAddr(bool enable) = 0;
virtual Errno SetKeepAlive(bool enable) = 0;
virtual Errno SetBroadcast(bool enable) = 0;
virtual Errno SetSndBuf(u32 value) = 0;
virtual Errno SetRcvBuf(u32 value) = 0;
virtual Errno SetSndTimeo(u32 value) = 0;
virtual Errno SetRcvTimeo(u32 value) = 0;
virtual Errno SetNonBlock(bool enable) = 0; virtual Errno SetNonBlock(bool enable) = 0;
virtual Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) = 0;
virtual std::pair<Errno, Errno> GetPendingError() = 0; virtual std::pair<Errno, Errno> GetPendingError() = 0;
virtual bool IsOpened() const = 0; virtual bool IsOpened() const = 0;
@@ -95,7 +80,6 @@ public:
return fd; return fd;
} }
protected:
SOCKET fd = INVALID_SOCKET; SOCKET fd = INVALID_SOCKET;
}; };
@@ -114,13 +98,13 @@ public:
std::pair<AcceptResult, Errno> Accept() override; std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(SockAddrIn addr_in) override; Errno Connect(Network::SockAddrIn addr_in) override;
std::pair<SockAddrIn, Errno> GetPeerName() override; std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<SockAddrIn, Errno> GetSockName() override; std::pair<Network::SockAddrIn, Errno> GetSockName() override;
Errno Bind(SockAddrIn addr) override; Errno Bind(Network::SockAddrIn addr) override;
Errno Listen(s32 backlog) override; Errno Listen(s32 backlog) override;
@@ -128,38 +112,19 @@ public:
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override; std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override; std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override; std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
const SockAddrIn* addr) override;
Errno SetLinger(bool enable, u32 linger) override;
Errno SetReuseAddr(bool enable) override;
Errno SetKeepAlive(bool enable) override;
Errno SetBroadcast(bool enable) override;
Errno SetSndBuf(u32 value) override;
Errno SetRcvBuf(u32 value) override;
Errno SetSndTimeo(u32 value) override;
Errno SetRcvTimeo(u32 value) override;
Errno SetNonBlock(bool enable) override; Errno SetNonBlock(bool enable) override;
template <typename T> Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
Errno SetSockOpt(SOCKET fd, int option, T value);
std::pair<Errno, Errno> GetPendingError() override; std::pair<Errno, Errno> GetPendingError() override;
template <typename T> Errno GetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<u8> value);
std::pair<T, Errno> GetSockOpt(SOCKET fd, int option);
bool IsOpened() const override; bool IsOpened() const override;
@@ -169,6 +134,6 @@ private:
bool is_non_blocking = false; bool is_non_blocking = false;
}; };
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); std::pair<s32, Errno> Poll(std::span<Network::HostPollFD> poll_fds, s32 timeout);
} // namespace Network } // namespace Network
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project // SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -12,7 +12,7 @@
#include <vector> #include <vector>
#include "common/announce_multiplayer_room.h" #include "common/announce_multiplayer_room.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/socket_types.h" #include "core/internal_network/socket_types.h"
#include "network/verify_user.h" #include "network/verify_user.h"
namespace Network { namespace Network {
+8 -4
View File
@@ -10,7 +10,7 @@
#include <thread> #include <thread>
#include "common/assert.h" #include "common/assert.h"
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "common/socket_types.h" #include "core/internal_network/socket_types.h"
#include "enet/enet.h" #include "enet/enet.h"
#include "network/packet.h" #include "network/packet.h"
#include "network/room_member.h" #include "network/room_member.h"
@@ -358,19 +358,23 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
// Parse the ProxyPacket from the packet // Parse the ProxyPacket from the packet
u8 local_family; u8 local_family;
packet.Read(local_family); packet.Read(local_family);
proxy_packet.local_endpoint.family = static_cast<Domain>(local_family); proxy_packet.local_endpoint.len = 16;
proxy_packet.local_endpoint.family = u8(Network::Domain(local_family));
packet.Read(proxy_packet.local_endpoint.ip); packet.Read(proxy_packet.local_endpoint.ip);
packet.Read(proxy_packet.local_endpoint.portno); packet.Read(proxy_packet.local_endpoint.portno);
proxy_packet.local_endpoint.zeroes = {};
u8 remote_family; u8 remote_family;
packet.Read(remote_family); packet.Read(remote_family);
proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family); proxy_packet.remote_endpoint.len = 16;
proxy_packet.remote_endpoint.family = u8(Network::Domain(remote_family));
packet.Read(proxy_packet.remote_endpoint.ip); packet.Read(proxy_packet.remote_endpoint.ip);
packet.Read(proxy_packet.remote_endpoint.portno); packet.Read(proxy_packet.remote_endpoint.portno);
proxy_packet.local_endpoint.zeroes = {};
u8 protocol_type; u8 protocol_type;
packet.Read(protocol_type); packet.Read(protocol_type);
proxy_packet.protocol = static_cast<Protocol>(protocol_type); proxy_packet.protocol = Network::Protocol(protocol_type);
packet.Read(proxy_packet.broadcast); packet.Read(proxy_packet.broadcast);
packet.Read(proxy_packet.data); packet.Read(proxy_packet.data);
+4 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project // SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -12,7 +12,7 @@
#include <vector> #include <vector>
#include "common/announce_multiplayer_room.h" #include "common/announce_multiplayer_room.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/socket_types.h" #include "core/internal_network/socket_types.h"
#include "network/room.h" #include "network/room.h"
namespace Network { namespace Network {
@@ -39,8 +39,8 @@ struct LDNPacket {
/// Information about the received proxy packets. /// Information about the received proxy packets.
struct ProxyPacket { struct ProxyPacket {
SockAddrIn local_endpoint; Network::SockAddrIn local_endpoint;
SockAddrIn remote_endpoint; Network::SockAddrIn remote_endpoint;
Protocol protocol; Protocol protocol;
bool broadcast; bool broadcast;
std::vector<u8> data; std::vector<u8> data;
+10 -5
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -12,16 +15,18 @@ TEST_CASE("Network::Errors", "[core]") {
Network::Socket socks[2]; Network::Socket socks[2];
for (Network::Socket& sock : socks) { for (Network::Socket& sock : socks) {
REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM, REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
Network::Protocol::TCP) == Network::Errno::SUCCESS); Network::Protocol::TCP) == Network::Errno::E_SUCCESS);
} }
Network::SockAddrIn addr{ Network::SockAddrIn addr{
Network::Domain::INET, 16,
{127, 0, 0, 1}, u8(Network::Domain::INET),
1, // hopefully nobody running this test has something listening on port 1 1, // hopefully nobody running this test has something listening on port 1
{127, 0, 0, 1},
{},
}; };
REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED); REQUIRE(socks[0].Connect(addr) == Network::Errno::E_CONNREFUSED);
std::vector<u8> message{1, 2, 3, 4}; std::vector<u8> message{1, 2, 3, 4};
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN); REQUIRE(socks[1].Recv(0, message).second == Network::Errno::E_NOTCONN);
} }
-3
View File
@@ -20,7 +20,6 @@ add_library(video_core STATIC
buffer_cache/buffer_cache.h buffer_cache/buffer_cache.h
buffer_cache/memory_tracker_base.h buffer_cache/memory_tracker_base.h
buffer_cache/usage_tracker.h buffer_cache/usage_tracker.h
buffer_cache/virtual_range_cache.h
buffer_cache/word_manager.h buffer_cache/word_manager.h
cache_types.h cache_types.h
capture.h capture.h
@@ -167,8 +166,6 @@ add_library(video_core STATIC
renderer_vulkan/vk_fence_manager.h renderer_vulkan/vk_fence_manager.h
renderer_vulkan/vk_graphics_pipeline.cpp renderer_vulkan/vk_graphics_pipeline.cpp
renderer_vulkan/vk_graphics_pipeline.h renderer_vulkan/vk_graphics_pipeline.h
renderer_vulkan/vk_multi_range_buffer.cpp
renderer_vulkan/vk_multi_range_buffer.h
renderer_vulkan/vk_master_semaphore.cpp renderer_vulkan/vk_master_semaphore.cpp
renderer_vulkan/vk_master_semaphore.h renderer_vulkan/vk_master_semaphore.h
renderer_vulkan/vk_pipeline_cache.cpp renderer_vulkan/vk_pipeline_cache.cpp
+26 -134
View File
@@ -112,13 +112,6 @@ void BufferCache<P>::TickFrame() {
async_buffers_death_ring.clear(); async_buffers_death_ring.clear();
} }
template <class P>
void BufferCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) {
if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) {
virtual_ranges.Unmap(as_id, gpu_addr, size);
}
}
template <class P> template <class P>
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) { void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
if (memory_tracker.IsRegionGpuModified(device_addr, size)) { if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
@@ -215,8 +208,8 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
BufferId buffer_b; BufferId buffer_b;
do { do {
channel_state->has_deleted_buffers = false; channel_state->has_deleted_buffers = false;
buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount), false); buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount));
buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount), false); buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount));
} while (channel_state->has_deleted_buffers); } while (channel_state->has_deleted_buffers);
auto& src_buffer = slot_buffers[buffer_a]; auto& src_buffer = slot_buffers[buffer_a];
auto& dest_buffer = slot_buffers[buffer_b]; auto& dest_buffer = slot_buffers[buffer_b];
@@ -272,7 +265,7 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
ClearDownload(*cpu_dst_address, size); ClearDownload(*cpu_dst_address, size);
gpu_modified_ranges.Subtract(*cpu_dst_address, size); gpu_modified_ranges.Subtract(*cpu_dst_address, size);
const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size), false); const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
Buffer& dest_buffer = slot_buffers[buffer]; Buffer& dest_buffer = slot_buffers[buffer];
const u32 offset = dest_buffer.Offset(*cpu_dst_address); const u32 offset = dest_buffer.Offset(*cpu_dst_address);
runtime.ClearBuffer(dest_buffer, offset, size, value); runtime.ClearBuffer(dest_buffer, offset, size, value);
@@ -294,7 +287,7 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_ad
template <class P> template <class P>
std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer( std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
DAddr device_addr, u32 size, ObtainBufferSynchronize sync_info, ObtainBufferOperation post_op) { DAddr device_addr, u32 size, ObtainBufferSynchronize sync_info, ObtainBufferOperation post_op) {
const BufferId buffer_id = FindBuffer(device_addr, size, false); const BufferId buffer_id = FindBuffer(device_addr, size);
Buffer& buffer = slot_buffers[buffer_id]; Buffer& buffer = slot_buffers[buffer_id];
// synchronize op // synchronize op
@@ -1005,85 +998,11 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index); channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index);
} }
template <class P>
void BufferCache<P>::ResolveMultiRangeStorage(Binding& binding, bool is_written,
std::vector<MultiRangeSegment>& pool) {
binding.segment_first = 0;
binding.segment_count = 0;
if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) {
if (binding.gpu_addr == 0 || binding.size == 0) {
return;
}
if (is_written && !runtime.PrefersSparseSources()) {
return;
}
const VirtualSegments* found =
virtual_ranges.Query(*gpu_memory, binding.gpu_addr, binding.size);
if (!found || found->size() < 2) {
return;
}
const VirtualSegments segments = *found;
const u32 first = static_cast<u32>(pool.size());
const bool prefer_sparse = runtime.PrefersSparseSources();
for (const VirtualSegment& segment : segments) {
const BufferId buffer_id =
FindBuffer(segment.device_addr, segment.size, prefer_sparse);
if (!buffer_id) {
pool.resize(first);
return;
}
pool.push_back(MultiRangeSegment{
.buffer_id = buffer_id,
.device_addr = segment.device_addr,
.size = segment.size,
});
}
binding.segment_first = first;
binding.segment_count = static_cast<u32>(segments.size());
}
}
template <class P>
bool BufferCache<P>::BindMultiRangeStorage(const Binding& binding, bool is_written,
std::span<const MultiRangeSegment> pool) {
if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) {
if (binding.segment_count < 2) {
return false;
}
if (binding.segment_first + binding.segment_count > pool.size()) {
return false;
}
const u64 key = (static_cast<u64>(gpu_memory->GetID()) << 48) ^ binding.gpu_addr;
runtime.ResetMultiRange();
for (u32 index = 0; index < binding.segment_count; ++index) {
const MultiRangeSegment& segment = pool[binding.segment_first + index];
Buffer& buffer = slot_buffers[segment.buffer_id];
TouchBuffer(buffer, segment.buffer_id);
if (SynchronizeBuffer(buffer, segment.device_addr, segment.size)) {
runtime.InvalidateMultiRange(key);
}
const u32 offset = buffer.Offset(segment.device_addr);
buffer.MarkUsage(offset, segment.size);
if (is_written) {
MarkWrittenBuffer(segment.buffer_id, segment.device_addr, segment.size);
}
runtime.PushMultiRangeSource(buffer, offset, segment.size);
}
return runtime.BindMultiRangeStorageBuffer(key, is_written);
} else {
return false;
}
}
template <class P> template <class P>
void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) { void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
u32 binding_index = 0; u32 binding_index = 0;
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
const Binding& binding = channel_state->storage_buffers[stage][index]; const Binding& binding = channel_state->storage_buffers[stage][index];
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
if (BindMultiRangeStorage(binding, is_written, graphics_segments)) {
return;
}
Buffer& buffer = slot_buffers[binding.buffer_id]; Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id); TouchBuffer(buffer, binding.buffer_id);
const u32 size = binding.size; const u32 size = binding.size;
@@ -1091,6 +1010,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
const u32 offset = buffer.Offset(binding.device_addr); const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, size); buffer.MarkUsage(offset, size);
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
if (is_written) { if (is_written) {
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
@@ -1219,11 +1139,6 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
u32 binding_index = 0; u32 binding_index = 0;
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) { ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
const Binding& binding = channel_state->compute_storage_buffers[index]; const Binding& binding = channel_state->compute_storage_buffers[index];
const bool is_written =
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
if (BindMultiRangeStorage(binding, is_written, compute_segments)) {
return;
}
Buffer& buffer = slot_buffers[binding.buffer_id]; Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id); TouchBuffer(buffer, binding.buffer_id);
const u32 size = binding.size; const u32 size = binding.size;
@@ -1231,6 +1146,8 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
const u32 offset = buffer.Offset(binding.device_addr); const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, size); buffer.MarkUsage(offset, size);
const bool is_written =
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
if (is_written) { if (is_written) {
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
@@ -1276,7 +1193,6 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
template <class P> template <class P>
void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) { void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
graphics_segments.clear();
BufferOperations([&]() { BufferOperations([&]() {
if (is_indexed) { if (is_indexed) {
UpdateIndexBuffer(); UpdateIndexBuffer();
@@ -1296,7 +1212,6 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
template <class P> template <class P>
void BufferCache<P>::DoUpdateComputeBuffers() { void BufferCache<P>::DoUpdateComputeBuffers() {
compute_segments.clear();
BufferOperations([&]() { BufferOperations([&]() {
UpdateComputeUniformBuffers(); UpdateComputeUniformBuffers();
UpdateComputeStorageBuffers(); UpdateComputeStorageBuffers();
@@ -1319,11 +1234,11 @@ void BufferCache<P>::UpdateIndexBuffer() {
auto inline_index_size = static_cast<u32>(draw_state.inline_index_draw_indexes.size()); auto inline_index_size = static_cast<u32>(draw_state.inline_index_draw_indexes.size());
u32 buffer_size = Common::AlignUp(inline_index_size, CACHING_PAGESIZE); u32 buffer_size = Common::AlignUp(inline_index_size, CACHING_PAGESIZE);
if (inline_buffer_id == NULL_BUFFER_ID) [[unlikely]] { if (inline_buffer_id == NULL_BUFFER_ID) [[unlikely]] {
inline_buffer_id = CreateBuffer(0, buffer_size, false); inline_buffer_id = CreateBuffer(0, buffer_size);
} }
if (slot_buffers[inline_buffer_id].SizeBytes() < buffer_size) [[unlikely]] { if (slot_buffers[inline_buffer_id].SizeBytes() < buffer_size) [[unlikely]] {
slot_buffers.erase(inline_buffer_id); slot_buffers.erase(inline_buffer_id);
inline_buffer_id = CreateBuffer(0, buffer_size, false); inline_buffer_id = CreateBuffer(0, buffer_size);
} }
channel_state->index_buffer = Binding{ channel_state->index_buffer = Binding{
.device_addr = 0, .device_addr = 0,
@@ -1346,7 +1261,7 @@ void BufferCache<P>::UpdateIndexBuffer() {
channel_state->index_buffer = Binding{ channel_state->index_buffer = Binding{
.device_addr = *device_addr, .device_addr = *device_addr,
.size = size, .size = size,
.buffer_id = FindBuffer(*device_addr, size, false), .buffer_id = FindBuffer(*device_addr, size),
}; };
} }
@@ -1383,7 +1298,7 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) { if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) {
size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
} }
const BufferId buffer_id = FindBuffer(*device_addr, size, false); const BufferId buffer_id = FindBuffer(*device_addr, size);
const Binding binding{ const Binding binding{
.device_addr = *device_addr, .device_addr = *device_addr,
.size = size, .size = size,
@@ -1404,7 +1319,7 @@ void BufferCache<P>::UpdateDrawIndirect() {
binding = Binding{ binding = Binding{
.device_addr = *device_addr, .device_addr = *device_addr,
.size = static_cast<u32>(size), .size = static_cast<u32>(size),
.buffer_id = FindBuffer(*device_addr, static_cast<u32>(size), false), .buffer_id = FindBuffer(*device_addr, static_cast<u32>(size)),
}; };
}; };
if (current_draw_indirect->include_count) { if (current_draw_indirect->include_count) {
@@ -1428,7 +1343,7 @@ void BufferCache<P>::UpdateUniformBuffers(size_t stage) {
channel_state->dirty_uniform_buffers[stage] |= 1U << index; channel_state->dirty_uniform_buffers[stage] |= 1U << index;
} }
// Resolve buffer // Resolve buffer
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
}); });
} }
@@ -1437,10 +1352,8 @@ void BufferCache<P>::UpdateStorageBuffers(size_t stage) {
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
// Resolve buffer // Resolve buffer
Binding& binding = channel_state->storage_buffers[stage][index]; Binding& binding = channel_state->storage_buffers[stage][index];
const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size, false); const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size);
binding.buffer_id = buffer_id; binding.buffer_id = buffer_id;
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
ResolveMultiRangeStorage(binding, is_written, graphics_segments);
}); });
} }
@@ -1448,7 +1361,7 @@ template <class P>
void BufferCache<P>::UpdateTextureBuffers(size_t stage) { void BufferCache<P>::UpdateTextureBuffers(size_t stage) {
ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) { ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) {
Binding& binding = channel_state->texture_buffers[stage][index]; Binding& binding = channel_state->texture_buffers[stage][index];
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
}); });
} }
@@ -1472,7 +1385,7 @@ void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) {
channel_state->transform_feedback_buffers[index] = NULL_BINDING; channel_state->transform_feedback_buffers[index] = NULL_BINDING;
return; return;
} }
const BufferId buffer_id = FindBuffer(*device_addr, size, false); const BufferId buffer_id = FindBuffer(*device_addr, size);
channel_state->transform_feedback_buffers[index] = Binding{ channel_state->transform_feedback_buffers[index] = Binding{
.device_addr = *device_addr, .device_addr = *device_addr,
.size = size, .size = size,
@@ -1494,7 +1407,7 @@ void BufferCache<P>::UpdateComputeUniformBuffers() {
binding.size = cbuf.size; binding.size = cbuf.size;
} }
} }
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
}); });
} }
@@ -1503,10 +1416,7 @@ void BufferCache<P>::UpdateComputeStorageBuffers() {
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) { ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
// Resolve buffer // Resolve buffer
Binding& binding = channel_state->compute_storage_buffers[index]; Binding& binding = channel_state->compute_storage_buffers[index];
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
const bool is_written =
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
ResolveMultiRangeStorage(binding, is_written, compute_segments);
}); });
} }
@@ -1514,7 +1424,7 @@ template <class P>
void BufferCache<P>::UpdateComputeTextureBuffers() { void BufferCache<P>::UpdateComputeTextureBuffers() {
ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) { ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) {
Binding& binding = channel_state->compute_texture_buffers[index]; Binding& binding = channel_state->compute_texture_buffers[index];
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
}); });
} }
@@ -1530,7 +1440,7 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u3
} }
template <class P> template <class P>
BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible) { BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size) {
if (device_addr == 0) { if (device_addr == 0) {
return NULL_BUFFER_ID; return NULL_BUFFER_ID;
} }
@@ -1540,18 +1450,10 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
Buffer& buffer = slot_buffers[buffer_id]; Buffer& buffer = slot_buffers[buffer_id];
WaitForGpuFenceIfNeeded(buffer); WaitForGpuFenceIfNeeded(buffer);
if (buffer.IsInBounds(device_addr, size)) { if (buffer.IsInBounds(device_addr, size)) {
bool usable = true; return buffer_id;
if constexpr (requires { buffer.IsSparseCompatible(); }) {
if (sparse_compatible && !buffer.IsSparseCompatible()) {
usable = false;
}
}
if (usable) {
return buffer_id;
}
} }
} }
return CreateBuffer(device_addr, size, sparse_compatible); return CreateBuffer(device_addr, size);
} }
template <class P> template <class P>
@@ -1673,15 +1575,13 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
} }
template <class P> template <class P>
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size, BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
bool sparse_compatible) {
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE); DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE); device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
wanted_size = static_cast<u32>(device_addr_end - device_addr); wanted_size = static_cast<u32>(device_addr_end - device_addr);
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
const u32 size = static_cast<u32>(overlap.end - overlap.begin); const u32 size = static_cast<u32>(overlap.end - overlap.begin);
const BufferId new_buffer_id = const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size);
slot_buffers.insert(runtime, overlap.begin, size, sparse_compatible);
auto& new_buffer = slot_buffers[new_buffer_id]; auto& new_buffer = slot_buffers[new_buffer_id];
const size_t size_bytes = new_buffer.SizeBytes(); const size_t size_bytes = new_buffer.SizeBytes();
runtime.ClearBuffer(new_buffer, 0, size_bytes, 0); runtime.ClearBuffer(new_buffer, 0, size_bytes, 0);
@@ -1845,7 +1745,7 @@ void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_
ClearDownload(dest_address, copy_size); ClearDownload(dest_address, copy_size);
gpu_modified_ranges.Subtract(dest_address, copy_size); gpu_modified_ranges.Subtract(dest_address, copy_size);
BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size), false); BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size));
auto& buffer = slot_buffers[buffer_id]; auto& buffer = slot_buffers[buffer_id];
SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size)); SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size));
@@ -1931,9 +1831,6 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
template <class P> template <class P>
void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
if constexpr (requires { runtime.OnBufferDeleted(slot_buffers[buffer_id]); }) {
runtime.OnBufferDeleted(slot_buffers[buffer_id]);
}
bool dirty_index{false}; bool dirty_index{false};
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> dirty_vertex_buffers; boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> dirty_vertex_buffers;
const auto scalar_replace = [buffer_id](Binding& binding) { const auto scalar_replace = [buffer_id](Binding& binding) {
@@ -2037,14 +1934,9 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
// The end address used for size calculation does not need to be aligned // The end address used for size calculation does not need to be aligned
const DAddr cpu_end = Common::AlignUp(*device_addr + size, Core::DEVICE_PAGESIZE); const DAddr cpu_end = Common::AlignUp(*device_addr + size, Core::DEVICE_PAGESIZE);
u32 binding_size = static_cast<u32>(cpu_end - *aligned_device_addr);
if (is_written) {
binding_size = aligned_size;
}
const Binding binding{ const Binding binding{
.device_addr = *aligned_device_addr, .device_addr = *aligned_device_addr,
.gpu_addr = aligned_gpu_addr, .size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_device_addr),
.size = binding_size,
.buffer_id = BufferId{}, .buffer_id = BufferId{},
}; };
return binding; return binding;
@@ -29,7 +29,6 @@
#include "common/settings.h" #include "common/settings.h"
#include "common/slot_vector.h" #include "common/slot_vector.h"
#include "video_core/buffer_cache/buffer_base.h" #include "video_core/buffer_cache/buffer_base.h"
#include "video_core/buffer_cache/virtual_range_cache.h"
#include "video_core/control/channel_state_cache.h" #include "video_core/control/channel_state_cache.h"
#include "video_core/delayed_destruction_ring.h" #include "video_core/delayed_destruction_ring.h"
#include "video_core/dirty_flags.h" #include "video_core/dirty_flags.h"
@@ -82,17 +81,8 @@ static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast<u32>(4_KiB);
struct Binding { struct Binding {
DAddr device_addr{}; DAddr device_addr{};
GPUVAddr gpu_addr{};
u32 size{}; u32 size{};
BufferId buffer_id; BufferId buffer_id;
u32 segment_first{};
u32 segment_count{};
};
struct MultiRangeSegment {
BufferId buffer_id;
DAddr device_addr{};
u32 size{};
}; };
struct TextureBufferBinding : Binding { struct TextureBufferBinding : Binding {
@@ -225,14 +215,6 @@ public:
void TickFrame(); void TickFrame();
bool BindMultiRangeStorage(const Binding& binding, bool is_written,
std::span<const MultiRangeSegment> pool);
void ResolveMultiRangeStorage(Binding& binding, bool is_written,
std::vector<MultiRangeSegment>& pool);
void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size);
void WriteMemory(DAddr device_addr, u64 size); void WriteMemory(DAddr device_addr, u64 size);
void CachedWriteMemory(DAddr device_addr, u64 size); void CachedWriteMemory(DAddr device_addr, u64 size);
@@ -432,7 +414,7 @@ private:
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size); void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size);
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible); [[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size);
void WaitForGpuFenceIfNeeded(Buffer& buffer); void WaitForGpuFenceIfNeeded(Buffer& buffer);
@@ -440,8 +422,7 @@ private:
void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score); void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score);
[[nodiscard]] BufferId CreateBuffer(DAddr device_addr, u32 wanted_size, [[nodiscard]] BufferId CreateBuffer(DAddr device_addr, u32 wanted_size);
bool sparse_compatible);
void Register(BufferId buffer_id); void Register(BufferId buffer_id);
@@ -532,9 +513,6 @@ private:
using TickType = u64; using TickType = u64;
}; };
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache; Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
VirtualRangeCache virtual_ranges;
std::vector<MultiRangeSegment> graphics_segments;
std::vector<MultiRangeSegment> compute_segments;
u64 frame_tick = 0; u64 frame_tick = 0;
u64 total_used_memory = 0; u64 total_used_memory = 0;
u64 minimum_memory = 0; u64 minimum_memory = 0;
@@ -1,173 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <atomic>
#include <limits>
#include <mutex>
#include <optional>
#include <vector>
#include <boost/container/small_vector.hpp>
#include "common/common_types.h"
#include "common/container/unordered_map.h"
#include "video_core/memory_manager.h"
namespace VideoCommon {
struct VirtualSegment {
GPUVAddr gpu_addr;
DAddr device_addr;
u32 size;
};
using VirtualSegments = boost::container::small_vector<VirtualSegment, 8>;
class VirtualRangeCache {
public:
static constexpr size_t MAX_ENTRIES = 8192;
static constexpr size_t MAX_DEFERRED = 4096;
const VirtualSegments* Query(Tegra::MemoryManager& memory, GPUVAddr gpu_addr, u32 size) {
if (has_deferred.load(std::memory_order_acquire)) {
ApplyDeferred();
}
if (entries.size() > MAX_ENTRIES) {
entries.clear();
}
const size_t as_id = memory.GetID();
const u64 key = MakeKey(as_id, gpu_addr);
const auto it = entries.find(key);
if (it != entries.end() && it->second.as_id == as_id &&
it->second.gpu_addr == gpu_addr && it->second.size == size) {
return &it->second.segments;
}
Entry entry{};
entry.as_id = as_id;
entry.gpu_addr = gpu_addr;
entry.size = size;
const auto ranges = memory.GetSubmappedRange(gpu_addr, size);
GPUVAddr expected = gpu_addr;
bool contiguous = true;
for (const auto& [range_addr, range_size] : ranges) {
if (range_addr != expected || range_size == 0) {
contiguous = false;
break;
}
const std::optional<DAddr> device_addr = memory.GpuToCpuAddress(range_addr);
if (!device_addr || *device_addr == 0) {
contiguous = false;
break;
}
if (range_size > static_cast<size_t>((std::numeric_limits<u32>::max)())) {
contiguous = false;
break;
}
entry.segments.push_back(VirtualSegment{
.gpu_addr = range_addr,
.device_addr = *device_addr,
.size = static_cast<u32>(range_size),
});
expected += range_size;
}
if (!contiguous || expected != gpu_addr + size) {
entry.segments.clear();
}
const auto result = entries.insert_or_assign(key, std::move(entry));
return &result.first->second.segments;
}
void Unmap(size_t as_id, GPUVAddr gpu_addr, u64 size) {
if (size == 0) {
return;
}
{
std::scoped_lock lock{deferred_mutex};
if (!deferred.empty()) {
DeferredUnmap& last = deferred.back();
if (last.as_id == as_id && last.gpu_addr + last.size == gpu_addr) {
last.size += size;
has_deferred.store(true, std::memory_order_release);
return;
}
}
if (deferred.size() >= MAX_DEFERRED) {
deferred.clear();
deferred_overflow = true;
} else {
deferred.push_back(DeferredUnmap{
.as_id = as_id,
.gpu_addr = gpu_addr,
.size = size,
});
}
}
has_deferred.store(true, std::memory_order_release);
}
private:
struct Entry {
VirtualSegments segments;
size_t as_id{};
GPUVAddr gpu_addr{};
u32 size{};
};
struct DeferredUnmap {
size_t as_id;
GPUVAddr gpu_addr;
u64 size;
};
static u64 MakeKey(size_t as_id, GPUVAddr gpu_addr) {
return (static_cast<u64>(as_id) << 48) ^ gpu_addr;
}
void ApplyDeferred() {
std::vector<DeferredUnmap> pending;
bool overflow = false;
{
std::scoped_lock lock{deferred_mutex};
has_deferred.store(false, std::memory_order_release);
pending.swap(deferred);
overflow = deferred_overflow;
deferred_overflow = false;
}
if (overflow) {
entries.clear();
return;
}
if (pending.empty() || entries.empty()) {
return;
}
for (auto it = entries.begin(); it != entries.end();) {
const Entry& entry = it->second;
const GPUVAddr entry_end = entry.gpu_addr + entry.size;
bool overlaps = false;
for (const DeferredUnmap& unmap : pending) {
if (unmap.as_id != entry.as_id) {
continue;
}
if (entry.gpu_addr < unmap.gpu_addr + unmap.size && unmap.gpu_addr < entry_end) {
overlaps = true;
break;
}
}
if (overlaps) {
it = entries.erase(it);
} else {
++it;
}
}
}
::Common::unordered_map<u64, Entry> entries;
std::vector<DeferredUnmap> deferred;
std::mutex deferred_mutex;
std::atomic<bool> has_deferred{false};
bool deferred_overflow{};
};
} // namespace VideoCommon
@@ -52,7 +52,7 @@ constexpr std::array PROGRAM_LUT{
Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params) Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
: VideoCommon::BufferBase(null_params) {} : VideoCommon::BufferBase(null_params) {}
Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_, bool) Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
: VideoCommon::BufferBase(cpu_addr_, size_bytes_) { : VideoCommon::BufferBase(cpu_addr_, size_bytes_) {
buffer.Create(); buffer.Create();
if (runtime.device.HasDebuggingToolAttached()) { if (runtime.device.HasDebuggingToolAttached()) {
@@ -23,8 +23,7 @@ class BufferCacheRuntime;
class Buffer : public VideoCommon::BufferBase { class Buffer : public VideoCommon::BufferBase {
public: public:
explicit Buffer(BufferCacheRuntime&, DAddr cpu_addr, u64 size_bytes, explicit Buffer(BufferCacheRuntime&, DAddr cpu_addr, u64 size_bytes);
bool sparse_compatible);
explicit Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams); explicit Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams);
void ImmediateUpload(size_t offset, std::span<const u8> data) noexcept; void ImmediateUpload(size_t offset, std::span<const u8> data) noexcept;
@@ -56,8 +56,7 @@ size_t BytesPerIndex(VkIndexType index_type) {
} }
} }
vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allocator, u64 size, vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allocator, u64 size) {
VkDeviceSize sparse_alignment) {
VkBufferUsageFlags flags = VkBufferUsageFlags flags =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
@@ -83,9 +82,6 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
.queueFamilyIndexCount = 0, .queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr, .pQueueFamilyIndices = nullptr,
}; };
if (sparse_alignment > 1) {
return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal, sparse_alignment);
}
return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal); return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
} }
} // Anonymous namespace } // Anonymous namespace
@@ -103,14 +99,10 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_p
} }
} }
Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_, Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
bool sparse_compatible_)
: VideoCommon::BufferBase(cpu_addr_, size_bytes_), device{&runtime.device}, : VideoCommon::BufferBase(cpu_addr_, size_bytes_), device{&runtime.device},
scheduler{&runtime.scheduler}, scheduler{&runtime.scheduler},
buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes(), buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())}, tracker{SizeBytes()} {
runtime.SparseAlignmentFor(sparse_compatible_))},
tracker{SizeBytes()} {
sparse_compatible = sparse_compatible_;
if (runtime.device.HasDebuggingToolAttached()) { if (runtime.device.HasDebuggingToolAttached()) {
buffer.SetObjectNameEXT(fmt::format("Buffer {:#x}", CpuAddr()).c_str()); buffer.SetObjectNameEXT(fmt::format("Buffer {:#x}", CpuAddr()).c_str());
} }
@@ -356,8 +348,7 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
staging_pool{staging_pool_}, guest_descriptor_queue{guest_descriptor_queue_}, staging_pool{staging_pool_}, guest_descriptor_queue{guest_descriptor_queue_},
quad_index_pass(device, scheduler, descriptor_pool, staging_pool, quad_index_pass(device, scheduler, descriptor_pool, staging_pool,
compute_pass_descriptor_queue), compute_pass_descriptor_queue) {
multi_range_buffers(device_) {
const VkDriverIdKHR driver_id = device.GetDriverID(); const VkDriverIdKHR driver_id = device.GetDriverID();
limit_dynamic_storage_buffers = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || limit_dynamic_storage_buffers = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY; driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
@@ -545,37 +536,6 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si
}); });
} }
bool BufferCacheRuntime::BindMultiRangeStorageBuffer(u64 key, bool is_written) {
if (multi_range_sources.empty() || multi_range_total == 0) {
return false;
}
const MultiRangeRef ref = multi_range_buffers.Get(device, scheduler, memory_allocator, key,
multi_range_sources, multi_range_total);
if (ref.handle == VK_NULL_HANDLE) {
return false;
}
if (is_written && !ref.sparse) {
return false;
}
if (ref.needs_gather) {
PreCopyBarrier();
VkDeviceSize dst_offset = 0;
for (const MultiRangeSource& source : multi_range_sources) {
const std::array<VideoCommon::BufferCopy, 1> copy{VideoCommon::BufferCopy{
.src_offset = u64(source.offset),
.dst_offset = u64(dst_offset),
.size = size_t(source.size),
}};
CopyBuffer(ref.handle, source.handle, copy, false);
dst_offset += source.size;
}
PostCopyBarrier();
multi_range_buffers.MarkGathered(key);
}
guest_descriptor_queue.AddBuffer(ref.handle, ref.address, 0, ref.size);
return true;
}
void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format,
u32 base_vertex, u32 num_indices, VkBuffer buffer, u32 base_vertex, u32 num_indices, VkBuffer buffer,
u32 offset, [[maybe_unused]] u32 size) { u32 offset, [[maybe_unused]] u32 size) {
@@ -8,14 +8,11 @@
#include <limits> #include <limits>
#include <boost/container/small_vector.hpp>
#include "video_core/buffer_cache/buffer_cache_base.h" #include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/buffer_cache/memory_tracker_base.h" #include "video_core/buffer_cache/memory_tracker_base.h"
#include "video_core/buffer_cache/usage_tracker.h" #include "video_core/buffer_cache/usage_tracker.h"
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/renderer_vulkan/vk_multi_range_buffer.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
#include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/surface.h" #include "video_core/surface.h"
@@ -34,8 +31,7 @@ class BufferCacheRuntime;
class Buffer : public VideoCommon::BufferBase { class Buffer : public VideoCommon::BufferBase {
public: public:
explicit Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params); explicit Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params);
explicit Buffer(BufferCacheRuntime& runtime, VAddr cpu_addr_, u64 size_bytes_, explicit Buffer(BufferCacheRuntime& runtime, VAddr cpu_addr_, u64 size_bytes_);
bool sparse_compatible_);
[[nodiscard]] VkBufferView View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format); [[nodiscard]] VkBufferView View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
@@ -47,14 +43,6 @@ public:
return device_address; return device_address;
} }
[[nodiscard]] bool IsSparseCompatible() const noexcept {
return sparse_compatible;
}
[[nodiscard]] vk::MemoryLocation Location() const noexcept {
return buffer.Location();
}
[[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept { [[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
return tracker.IsUsed(offset, size); return tracker.IsUsed(offset, size);
} }
@@ -89,7 +77,6 @@ private:
VkDeviceAddress device_address{}; VkDeviceAddress device_address{};
u64 last_usage_tick{}; u64 last_usage_tick{};
bool is_null{}; bool is_null{};
bool sparse_compatible{};
}; };
class QuadArrayIndexBuffer; class QuadArrayIndexBuffer;
@@ -138,7 +125,7 @@ public:
void PreCopyBarrier(); void PreCopyBarrier();
void CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier, std::span<const VideoCommon::BufferCopy> copies, bool barrier,
bool can_reorder_upload = false); bool can_reorder_upload = false);
@@ -168,46 +155,6 @@ public:
return ref.mapped_span; return ref.mapped_span;
} }
[[nodiscard]] VkDeviceSize SparseAlignmentFor(bool sparse_compatible) const noexcept {
if (!sparse_compatible || !multi_range_buffers.use_sparse) {
return 0;
}
return multi_range_buffers.block_size;
}
[[nodiscard]] bool PrefersSparseSources() const noexcept {
return multi_range_buffers.use_sparse;
}
void ResetMultiRange() noexcept {
multi_range_sources.clear();
multi_range_total = 0;
}
void PushMultiRangeSource(const Buffer& buffer, u32 offset, u32 size) {
const vk::MemoryLocation location = buffer.Location();
multi_range_sources.push_back(MultiRangeSource{
.handle = buffer.Handle(),
.memory = location.memory,
.memory_offset = location.offset,
.offset = offset,
.size = size,
.write_tick = buffer.getWriteTick(),
.memory_type = location.memory_type,
});
multi_range_total += size;
}
bool BindMultiRangeStorageBuffer(u64 key, bool is_written);
void InvalidateMultiRange(u64 key) {
multi_range_buffers.Invalidate(key);
}
void OnBufferDeleted(const Buffer& buffer) {
multi_range_buffers.DropOwner(scheduler, buffer.Handle());
}
void BindUniformBuffer(const Buffer& buffer, u32 offset, u32 size) { void BindUniformBuffer(const Buffer& buffer, u32 offset, u32 size) {
BindBuffer(buffer, offset, size); BindBuffer(buffer, offset, size);
} }
@@ -261,10 +208,6 @@ private:
std::unique_ptr<Uint8Pass> uint8_pass; std::unique_ptr<Uint8Pass> uint8_pass;
QuadIndexedPass quad_index_pass; QuadIndexedPass quad_index_pass;
MultiRangeBufferCache multi_range_buffers;
boost::container::small_vector<MultiRangeSource, 16> multi_range_sources;
VkDeviceSize multi_range_total{};
bool limit_dynamic_storage_buffers = false; bool limit_dynamic_storage_buffers = false;
u32 max_dynamic_storage_buffers = (std::numeric_limits<u32>::max)(); u32 max_dynamic_storage_buffers = (std::numeric_limits<u32>::max)();
}; };
@@ -1,338 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <mutex>
#include <utility>
#include "video_core/renderer_vulkan/vk_multi_range_buffer.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/vulkan_common/vulkan_device.h"
namespace Vulkan {
MultiRangeBufferCache::MultiRangeBufferCache(const Device& device) {
sparse_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
if (device.IsBufferDeviceAddressSupported()) {
sparse_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
}
if (!device.IsSparseBindingSupported()) {
return;
}
u32 memory_type_bits = 0;
const VkDeviceSize queried = QueryBlockSize(device, memory_type_bits);
if (queried == 0 || memory_type_bits == 0) {
return;
}
block_size = queried;
sparse_memory_type_bits = memory_type_bits;
use_sparse = true;
}
VkDeviceSize MultiRangeBufferCache::QueryBlockSize(const Device& device,
u32& memory_type_bits) const {
const VkDevice logical = *device.GetLogical();
const auto& dld = device.GetDispatchLoader();
const VkBufferCreateInfo probe_ci{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT,
.size = DEFAULT_BLOCK_SIZE,
.usage = sparse_usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VkBuffer probe{};
if (dld.vkCreateBuffer(logical, &probe_ci, nullptr, &probe) != VK_SUCCESS) {
return 0;
}
const SparseBuffer owned{probe, logical, dld};
const VkBufferMemoryRequirementsInfo2 reqs_info{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2,
.pNext = nullptr,
.buffer = probe,
};
VkMemoryRequirements2 reqs2{
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
.pNext = nullptr,
.memoryRequirements = {},
};
dld.vkGetBufferMemoryRequirements2(logical, &reqs_info, &reqs2);
memory_type_bits = reqs2.memoryRequirements.memoryTypeBits;
return reqs2.memoryRequirements.alignment;
}
u64 MultiRangeBufferCache::HashSources(std::span<const MultiRangeSource> sources) const {
u64 hash = 0xcbf29ce484222325ULL;
const auto mix = [&hash](u64 value) {
hash ^= value;
hash *= 0x100000001b3ULL;
};
for (const MultiRangeSource& source : sources) {
mix(u64(source.handle));
mix(u64(source.offset));
mix(u64(source.size));
}
return hash;
}
u64 MultiRangeBufferCache::HashContent(std::span<const MultiRangeSource> sources) const {
u64 hash = 0xcbf29ce484222325ULL;
for (const MultiRangeSource& source : sources) {
hash ^= source.write_tick;
hash *= 0x100000001b3ULL;
}
return hash;
}
bool MultiRangeBufferCache::CanBindSparse(std::span<const MultiRangeSource> sources) const {
return use_sparse &&
std::none_of(sources.begin(), sources.end(),
[block = block_size, bits = sparse_memory_type_bits](auto const& e) {
const VkDeviceSize memory_offset = e.memory_offset + e.offset;
return e.memory == VK_NULL_HANDLE || e.memory_type >= 32 ||
((bits >> e.memory_type) & 1) == 0 ||
(memory_offset % block) != 0 || (e.size % block) != 0;
});
}
SparseBuffer MultiRangeBufferCache::CreateSparse(const Device& device, Scheduler& scheduler,
std::span<const MultiRangeSource> sources,
VkDeviceSize total) {
const VkDevice logical = *device.GetLogical();
const auto& dld = device.GetDispatchLoader();
const VkBufferCreateInfo buffer_ci{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT,
.size = total,
.usage = sparse_usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VkBuffer raw{};
if (dld.vkCreateBuffer(logical, &buffer_ci, nullptr, &raw) != VK_SUCCESS) {
return SparseBuffer{};
}
SparseBuffer handle{raw, logical, dld};
std::vector<VkSparseMemoryBind> binds;
binds.reserve(sources.size());
VkDeviceSize resource_offset = 0;
for (const MultiRangeSource& source : sources) {
binds.push_back(VkSparseMemoryBind{
.resourceOffset = resource_offset,
.size = source.size,
.memory = source.memory,
.memoryOffset = source.memory_offset + source.offset,
.flags = 0,
});
resource_offset += source.size;
}
const VkSparseBufferMemoryBindInfo buffer_bind{
.buffer = raw,
.bindCount = static_cast<u32>(binds.size()),
.pBinds = binds.data(),
};
const VkBindSparseInfo bind_info{
.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,
.pNext = nullptr,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.bufferBindCount = 1,
.pBufferBinds = &buffer_bind,
.imageOpaqueBindCount = 0,
.pImageOpaqueBinds = nullptr,
.imageBindCount = 0,
.pImageBinds = nullptr,
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
const VkFenceCreateInfo fence_ci{
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
};
vk::Fence fence = device.GetLogical().CreateFence(fence_ci);
VkResult bind_result = VK_ERROR_UNKNOWN;
{
std::scoped_lock lock{scheduler.submit_mutex};
bind_result = device.GetGraphicsQueue().BindSparse(bind_info, *fence);
}
if (bind_result != VK_SUCCESS) {
return SparseBuffer{};
}
fence.Wait();
return handle;
}
void MultiRangeBufferCache::RetireEntry(Scheduler& scheduler, Entry& entry) {
if (!entry.sparse_handle && !entry.gathered) {
return;
}
if (retired.size() == retired.capacity()) {
DrainRetired(scheduler);
}
if (retired.size() == retired.capacity()) {
u64 oldest = retired.front().tick;
for (const Retired& item : retired) {
if (item.tick < oldest) {
oldest = item.tick;
}
}
scheduler.Wait(oldest);
DrainRetired(scheduler);
}
retired.push_back(Retired{
.handle = std::move(entry.sparse_handle),
.gathered = std::move(entry.gathered),
.tick = scheduler.CurrentTick(),
});
}
void MultiRangeBufferCache::DrainRetired(Scheduler& scheduler) {
size_t index = 0;
while (index < retired.size()) {
if (scheduler.IsFree(retired[index].tick)) {
if (index + 1 != retired.size()) {
retired[index] = std::move(retired.back());
}
retired.pop_back();
} else {
++index;
}
}
}
MultiRangeRef MultiRangeBufferCache::Get(const Device& device, Scheduler& scheduler,
MemoryAllocator& memory_allocator, u64 key,
std::span<const MultiRangeSource> sources,
VkDeviceSize total) {
if (sources.empty() || total == 0) {
return MultiRangeRef{};
}
if (!retired.empty()) {
DrainRetired(scheduler);
}
const u64 geometry = HashSources(sources);
const u64 content = HashContent(sources);
const auto it = entries.find(key);
if (it != entries.end() && it->second.geometry == geometry && it->second.size == total) {
Entry& entry = it->second;
if (entry.content != content) {
entry.content = content;
entry.dirty = true;
}
MultiRangeRef ref{
.handle = *entry.sparse_handle,
.address = entry.address,
.size = entry.size,
.sparse = true,
.needs_gather = false,
};
if (!entry.sparse_handle) {
ref.handle = *entry.gathered;
ref.sparse = false;
ref.needs_gather = entry.dirty;
}
return ref;
}
if (it != entries.end()) {
RetireEntry(scheduler, it->second);
entries.erase(it);
}
Entry entry{};
entry.geometry = geometry;
entry.content = content;
entry.size = total;
if (CanBindSparse(sources)) {
entry.sparse_handle = CreateSparse(device, scheduler, sources, total);
if (entry.sparse_handle) {
entry.owners.reserve(sources.size());
for (const MultiRangeSource& source : sources) {
entry.owners.push_back(source.handle);
}
}
}
if (!entry.sparse_handle) {
VkBufferUsageFlags flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
if (device.IsBufferDeviceAddressSupported()) {
flags |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
}
const VkBufferCreateInfo gather_ci{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = total,
.usage = flags,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
entry.gathered = memory_allocator.CreateBuffer(gather_ci, MemoryUsage::DeviceLocal);
entry.dirty = true;
}
if (device.IsBufferDeviceAddressSupported()) {
VkBuffer address_handle = *entry.sparse_handle;
if (!entry.sparse_handle) {
address_handle = *entry.gathered;
}
entry.address = device.GetLogical().GetBufferDeviceAddress(address_handle);
}
MultiRangeRef ref{
.handle = *entry.sparse_handle,
.address = entry.address,
.size = entry.size,
.sparse = true,
.needs_gather = false,
};
if (!entry.sparse_handle) {
ref.handle = *entry.gathered;
ref.sparse = false;
ref.needs_gather = true;
}
entries.emplace(key, std::move(entry));
return ref;
}
void MultiRangeBufferCache::MarkGathered(u64 key) {
if (auto const it = entries.find(key); it != entries.end()) {
it->second.dirty = false;
}
}
void MultiRangeBufferCache::DropOwner(Scheduler& scheduler, VkBuffer owner) {
if (owner == VK_NULL_HANDLE) {
return;
}
for (auto it = entries.begin(); it != entries.end();) {
Entry& entry = it->second;
bool owned = false;
for (const VkBuffer handle : entry.owners) {
if (handle == owner) {
owned = true;
break;
}
}
if (!owned) {
++it;
continue;
}
RetireEntry(scheduler, entry);
it = entries.erase(it);
}
}
void MultiRangeBufferCache::Invalidate(u64 key) {
if (auto const it = entries.find(key); it != entries.end()) {
it->second.dirty = true;
}
}
} // namespace Vulkan
@@ -1,105 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <span>
#include <vector>
#include <boost/container/static_vector.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/container/unordered_map.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
using SparseBuffer = vk::Handle<VkBuffer, VkDevice, vk::DeviceDispatch>;
class Device;
class Scheduler;
struct MultiRangeSource {
VkBuffer handle{};
VkDeviceMemory memory{};
VkDeviceSize memory_offset{};
VkDeviceSize offset{};
VkDeviceSize size{};
u64 write_tick{};
u32 memory_type{};
};
struct MultiRangeRef {
VkBuffer handle{};
VkDeviceAddress address{};
VkDeviceSize size{};
bool sparse{};
bool needs_gather{};
};
class MultiRangeBufferCache final {
public:
static constexpr VkDeviceSize DEFAULT_BLOCK_SIZE = 64 * 1024;
static constexpr size_t MAX_RETIRED = 256;
explicit MultiRangeBufferCache(const Device& device);
YUZU_NON_COPYABLE(MultiRangeBufferCache);
[[nodiscard]] MultiRangeRef Get(const Device& device, Scheduler& scheduler,
MemoryAllocator& memory_allocator, u64 key,
std::span<const MultiRangeSource> sources,
VkDeviceSize total);
void MarkGathered(u64 key);
void Invalidate(u64 key);
void DropOwner(Scheduler& scheduler, VkBuffer owner);
VkDeviceSize block_size{DEFAULT_BLOCK_SIZE};
bool use_sparse{};
private:
struct Retired {
SparseBuffer handle;
vk::Buffer gathered;
u64 tick{};
};
struct Entry {
vk::Buffer gathered;
SparseBuffer sparse_handle;
std::vector<VkBuffer> owners;
VkDeviceAddress address{};
VkDeviceSize size{};
u64 geometry{};
u64 content{};
bool dirty{true};
};
[[nodiscard]] u64 HashSources(std::span<const MultiRangeSource> sources) const;
[[nodiscard]] u64 HashContent(std::span<const MultiRangeSource> sources) const;
[[nodiscard]] bool CanBindSparse(std::span<const MultiRangeSource> sources) const;
[[nodiscard]] SparseBuffer CreateSparse(const Device& device, Scheduler& scheduler,
std::span<const MultiRangeSource> sources,
VkDeviceSize total);
[[nodiscard]] VkDeviceSize QueryBlockSize(const Device& device, u32& memory_type_bits) const;
void RetireEntry(Scheduler& scheduler, Entry& entry);
void DrainRetired(Scheduler& scheduler);
::Common::unordered_map<u64, Entry> entries;
boost::container::static_vector<Retired, MAX_RETIRED> retired;
u32 sparse_memory_type_bits{};
VkBufferUsageFlags sparse_usage{};
};
} // namespace Vulkan
@@ -819,7 +819,6 @@ void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {
std::scoped_lock lock{texture_cache.mutex}; std::scoped_lock lock{texture_cache.mutex};
texture_cache.UnmapGPUMemory(as_id, addr, size); texture_cache.UnmapGPUMemory(as_id, addr, size);
} }
buffer_cache.UnmapGPUMemory(as_id, addr, size);
} }
void RasterizerVulkan::SignalFence(std::function<void()>&& func) { void RasterizerVulkan::SignalFence(std::function<void()>&& func) {
@@ -1570,8 +1570,6 @@ void Device::SetupFamilies(VkSurfaceKHR surface) {
} }
if (graphics) { if (graphics) {
graphics_family = *graphics; graphics_family = *graphics;
graphics_family_sparse_binding =
(queue_family_properties[*graphics].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) != 0;
} }
if (present) { if (present) {
present_family = *present; present_family = *present;
@@ -317,10 +317,6 @@ public:
return properties.driver.driverID; return properties.driver.driverID;
} }
bool IsSparseBindingSupported() const {
return features.features.sparseBinding && graphics_family_sparse_binding;
}
/// Returns true for tile-based deferred renderers. /// Returns true for tile-based deferred renderers.
bool IsTiler() const { bool IsTiler() const {
switch (GetDriverID()) { switch (GetDriverID()) {
@@ -1151,7 +1147,6 @@ private:
u32 instance_version{}; ///< Vulkan instance version. u32 instance_version{}; ///< Vulkan instance version.
u32 graphics_family{}; ///< Main graphics queue family index. u32 graphics_family{}; ///< Main graphics queue family index.
u32 present_family{}; ///< Main present queue family index. u32 present_family{}; ///< Main present queue family index.
bool graphics_family_sparse_binding{};
struct Extensions { struct Extensions {
#define EXTENSION(prefix, macro_name, var_name) bool var_name{}; #define EXTENSION(prefix, macro_name, var_name) bool var_name{};
@@ -275,63 +275,9 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsa
const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{}; const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{};
const bool is_coherent = (property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0; const bool is_coherent = (property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
const vk::MemoryLocation location{ return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data,
.memory = alloc_info.deviceMemory, is_coherent,
.offset = alloc_info.offset, device.GetDispatchLoader());
.memory_type = alloc_info.memoryType,
};
return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, is_coherent,
location, device.GetDispatchLoader());
}
vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage,
VkDeviceSize min_alignment) const {
if (min_alignment <= 1) {
return CreateBuffer(ci, usage);
}
VkMemoryPropertyFlags anv_flags = 0;
if (usage == MemoryUsage::Stream &&
device.GetDriverID() == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA) {
anv_flags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
}
u32 memory_type_bits = valid_memory_types;
if (usage == MemoryUsage::Stream) {
memory_type_bits = 0u;
}
const VmaAllocationCreateInfo alloc_ci = {
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage),
.usage = MemoryUsageVma(usage),
.requiredFlags = 0,
.preferredFlags = MemoryUsagePreferredVmaFlags(usage) | anv_flags,
.memoryTypeBits = memory_type_bits,
.pool = VK_NULL_HANDLE,
.pUserData = nullptr,
.priority = 0.f,
};
VkBuffer handle{};
VmaAllocationInfo alloc_info{};
VmaAllocation allocation{};
VkMemoryPropertyFlags property_flags{};
vk::Check(vmaCreateBufferWithAlignment(allocator, &ci, &alloc_ci, min_alignment, &handle,
&allocation, &alloc_info));
vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags);
u8 *data = reinterpret_cast<u8 *>(alloc_info.pMappedData);
std::span<u8> mapped_data{};
if (data) {
mapped_data = std::span<u8>{data, ci.size};
}
const bool is_coherent = (property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
const vk::MemoryLocation location{
.memory = alloc_info.deviceMemory,
.offset = alloc_info.offset,
.memory_type = alloc_info.memoryType,
};
return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, is_coherent,
location, device.GetDispatchLoader());
} }
MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements &reqs, MemoryUsage usage) MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements &reqs, MemoryUsage usage)
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -107,9 +107,6 @@ namespace Vulkan {
vk::Buffer CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const; vk::Buffer CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const;
vk::Buffer CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage,
VkDeviceSize min_alignment) const;
/** /**
* Commits a memory with the specified requirements. * Commits a memory with the specified requirements.
* *
@@ -229,7 +229,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkGetPipelineExecutableStatisticsKHR); X(vkGetPipelineExecutableStatisticsKHR);
X(vkGetSemaphoreCounterValue); X(vkGetSemaphoreCounterValue);
X(vkMapMemory); X(vkMapMemory);
X(vkQueueBindSparse);
X(vkQueueSubmit); X(vkQueueSubmit);
X(vkQueueSubmit2); X(vkQueueSubmit2);
X(vkResetFences); X(vkResetFences);
+3 -22
View File
@@ -345,7 +345,6 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkGetQueryPoolResults vkGetQueryPoolResults{}; PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue{}; PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue{};
PFN_vkMapMemory vkMapMemory{}; PFN_vkMapMemory vkMapMemory{};
PFN_vkQueueBindSparse vkQueueBindSparse{};
PFN_vkQueueSubmit vkQueueSubmit{}; PFN_vkQueueSubmit vkQueueSubmit{};
PFN_vkQueueSubmit2 vkQueueSubmit2{}; PFN_vkQueueSubmit2 vkQueueSubmit2{};
PFN_vkResetFences vkResetFences{}; PFN_vkResetFences vkResetFences{};
@@ -741,20 +740,13 @@ private:
const DeviceDispatch* dld = nullptr; const DeviceDispatch* dld = nullptr;
}; };
struct MemoryLocation {
VkDeviceMemory memory{};
VkDeviceSize offset{};
u32 memory_type{};
};
class Buffer { class Buffer {
public: public:
explicit Buffer(VkBuffer handle_, VkDevice owner_, VmaAllocator allocator_, explicit Buffer(VkBuffer handle_, VkDevice owner_, VmaAllocator allocator_,
VmaAllocation allocation_, std::span<u8> mapped_, bool is_coherent_, VmaAllocation allocation_, std::span<u8> mapped_, bool is_coherent_,
MemoryLocation location_, const DeviceDispatch& dld_) noexcept const DeviceDispatch& dld_) noexcept
: handle{handle_}, owner{owner_}, allocator{allocator_}, : handle{handle_}, owner{owner_}, allocator{allocator_},
allocation{allocation_}, mapped{mapped_}, location{location_}, allocation{allocation_}, mapped{mapped_}, is_coherent{is_coherent_}, dld{&dld_} {}
is_coherent{is_coherent_}, dld{&dld_} {}
Buffer() = default; Buffer() = default;
Buffer(const Buffer&) = delete; Buffer(const Buffer&) = delete;
@@ -762,7 +754,7 @@ public:
Buffer(Buffer&& rhs) noexcept Buffer(Buffer&& rhs) noexcept
: handle{std::exchange(rhs.handle, VkBuffer{})}, owner{rhs.owner}, allocator{rhs.allocator}, : handle{std::exchange(rhs.handle, VkBuffer{})}, owner{rhs.owner}, allocator{rhs.allocator},
allocation{rhs.allocation}, mapped{rhs.mapped}, location{rhs.location}, allocation{rhs.allocation}, mapped{rhs.mapped},
is_coherent{rhs.is_coherent}, dld{rhs.dld} {} is_coherent{rhs.is_coherent}, dld{rhs.dld} {}
Buffer& operator=(Buffer&& rhs) noexcept { Buffer& operator=(Buffer&& rhs) noexcept {
@@ -772,7 +764,6 @@ public:
allocator = rhs.allocator; allocator = rhs.allocator;
allocation = rhs.allocation; allocation = rhs.allocation;
mapped = rhs.mapped; mapped = rhs.mapped;
location = rhs.location;
is_coherent = rhs.is_coherent; is_coherent = rhs.is_coherent;
dld = rhs.dld; dld = rhs.dld;
return *this; return *this;
@@ -820,10 +811,6 @@ public:
void SetObjectNameEXT(const char* name) const; void SetObjectNameEXT(const char* name) const;
MemoryLocation Location() const noexcept {
return location;
}
private: private:
void Release() const noexcept; void Release() const noexcept;
@@ -832,7 +819,6 @@ private:
VmaAllocator allocator = nullptr; VmaAllocator allocator = nullptr;
VmaAllocation allocation = nullptr; VmaAllocation allocation = nullptr;
std::span<u8> mapped = {}; std::span<u8> mapped = {};
MemoryLocation location{};
bool is_coherent = false; bool is_coherent = false;
const DeviceDispatch* dld = nullptr; const DeviceDispatch* dld = nullptr;
}; };
@@ -857,11 +843,6 @@ public:
return dld->vkQueueSubmit2(queue, submit_infos.size(), submit_infos.data(), fence); return dld->vkQueueSubmit2(queue, submit_infos.size(), submit_infos.data(), fence);
} }
VkResult BindSparse(Span<VkBindSparseInfo> bind_infos,
VkFence fence = VK_NULL_HANDLE) const noexcept {
return dld->vkQueueBindSparse(queue, bind_infos.size(), bind_infos.data(), fence);
}
VkResult Present(const VkPresentInfoKHR& present_info) const noexcept { VkResult Present(const VkPresentInfoKHR& present_info) const noexcept {
return dld->vkQueuePresentKHR(queue, &present_info); return dld->vkQueuePresentKHR(queue, &present_info);
} }