From d7c13a621e7f26eafbfb8553c131928927c52a3d Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Wed, 28 Oct 2026 04:23:43 +0000 Subject: [PATCH 1/2] feat(libSceNet): implement the pool-checked Show*WithMemory diagnostics sceNetShowIfconfigWithMemory, sceNetShowPolicyWithMemory and sceNetShowRoute6WithMemory threw NotImplemented_nid_no_patch. They now behave like the existing sceNetShowRouteWithMemory: an unknown memory pool id fails with NET_EBADF (errno set), a live pool returns success without printing. The behaviour follows shadPS4's net.cpp, which does the same for these three. The new silent stubs are listed in the libSceNet technical debt file, and the tests cover the invalid id, live pool and destroyed pool cases. --- core/libs/prx/libSceNet/Export.cpp | 9 ++++++--- core/libs/tests/GuestSceNet.cpp | 9 +++++++++ docs/dev/debt/libSceNet.md | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 29c7a2d2dda0..01650813ff75 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -2560,7 +2560,8 @@ int APS5_VABI sceNetSetDnsInfo(const NetDnsInfo* info, std::uint32_t flags) { } int APS5_VABI sceNetShowIfconfigWithMemory(std::int32_t poolId) { - NotImplemented_nid_no_patch(__func__); + std::lock_guard lk(g_mutex); + if (g_pools.find(poolId) == g_pools.end()) return fail(NET_EBADF); return 0; } @@ -2569,7 +2570,8 @@ int APS5_VABI sceNetShowPolicy(void) { } int APS5_VABI sceNetShowPolicyWithMemory(std::int32_t poolId) { - NotImplemented_nid_no_patch(__func__); + std::lock_guard lk(g_mutex); + if (g_pools.find(poolId) == g_pools.end()) return fail(NET_EBADF); return 0; } @@ -2619,7 +2621,8 @@ int APS5_VABI sceNetShowRoute6(void) { } int APS5_VABI sceNetShowRoute6WithMemory(std::int32_t poolId) { - NotImplemented_nid_no_patch(__func__); + std::lock_guard lk(g_mutex); + if (g_pools.find(poolId) == g_pools.end()) return fail(NET_EBADF); return 0; } diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index 963c1eb14a28..c41d2d535d39 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -255,9 +255,15 @@ static void CheckNetstat() { Require(sceNetShowRoute6() == 0); Require(Failed(sceNetShowNetstatWithMemory(-1), 9)); Require(Failed(sceNetShowRouteWithMemory(-1), 9)); + Require(Failed(sceNetShowIfconfigWithMemory(-1), 9)); + Require(Failed(sceNetShowPolicyWithMemory(-1), 9)); + Require(Failed(sceNetShowRoute6WithMemory(-1), 9)); const int pool = sceNetPoolCreate("netstat", 4096, 0); Require(pool > 0); Require(sceNetShowRouteWithMemory(pool) == 0); + Require(sceNetShowIfconfigWithMemory(pool) == 0); + Require(sceNetShowPolicyWithMemory(pool) == 0); + Require(sceNetShowRoute6WithMemory(pool) == 0); const auto empty = CaptureNetstat(pool); Require(empty.find("bytes=4096 sockets=0") != std::string::npos); const int socket = sceNetSocket("netstat", 2, 1, 0); @@ -279,6 +285,9 @@ static void CheckNetstat() { Require(sceNetPoolDestroy(pool) == 0); Require(Failed(sceNetShowNetstatWithMemory(pool), 9)); Require(Failed(sceNetShowRouteWithMemory(pool), 9)); + Require(Failed(sceNetShowIfconfigWithMemory(pool), 9)); + Require(Failed(sceNetShowPolicyWithMemory(pool), 9)); + Require(Failed(sceNetShowRoute6WithMemory(pool), 9)); } static void CheckPoolStats() { diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index f016f29d101a..575f6af87b83 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -1,8 +1,8 @@ # libSceNet technical debt +- [sceNetShowRouteWithMemory, sceNetShowIfconfigWithMemory, sceNetShowPolicyWithMemory, sceNetShowRoute6WithMemory](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - validates memory pool ID and returns success without printing anything: the `(poolId)` signature and pool validation follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp) and `sceNetShowNetstatWithMemory`; shadPS4 stubs the Ifconfig, Policy and Route6 forms the same way, validating the pool and returning success. What the console prints and where is unverified ### Silent stubs -- [sceNetShowRouteWithMemory](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - validates memory pool ID and returns success without printing anything: the `(poolId)` signature and pool validation follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp) and `sceNetShowNetstatWithMemory`. What the console prints and where is unverified - [sceNetShowIfconfig, sceNetShowNetstat](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) print the console's layout (a PS5 on firmware 13.40, measured 2026-10-19) with what the emulator knows. Ifconfig prints `eth0` from the host interface `sceNetCtlGetInfo` reports, without its IPv6 address, `UP6`, link status, traffic counters or times, then `lo0` as the console does and the two IPv4 DNS servers, with `::, ::` for IPv6. It leaves out the wireless interfaces and the pool and statistics sections. Netstat lists only the title's sockets and epolls; the console lists every process's. The self and non-blocking flags, addresses, buffer sizes and names are filled; the R, S, RE and SE flags, the field after the type, queue lengths, rates and drops print as for an idle socket, TCP states are only `LISTEN`, `ESTABLISHED` and `CLOSED`, and the self flag on epolls is inferred. Before initialization both return the not-initialized error. - [sceNetShowPolicy, sceNetShowNetstatEx, sceNetShowRoute, sceNetShowRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - return success without printing anything: the `()` signatures and the success return follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/net.cpp), which stubs each the same way. PPSA01530 fails to load without the `sceNetShowIfconfig` export (#3177); PPSA11386 imports `sceNetShowNetstat` but does not call it during start-up. What the console prints and where is unverified From c1e321f26dd862e034989d98dfe5bccbd436337c Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Wed, 28 Oct 2026 07:39:14 +0000 Subject: [PATCH 2/2] feat(libSceNet): implement sceNetGetStatisticsInfo sceNetGetStatisticsInfo threw NotImplemented_nid_no_patch. It now fills the six 32-bit fields of the statistics block (kernel memory free and minimum free, packet count, QoS packet count, library memory free and minimum free) and fails with NET_EFAULT, setting errno, for a null pointer. The layout and the "plenty free, nothing queued" answer follow shadPS4's net.cpp and net_types.h; the host has no counter for the console's network memory, so the values are not measured. The debt file records this, and the test checks the null pointer, the field invariants and that the flags argument is ignored. --- core/libs/prx/libSceNet/Export.cpp | 4 +++- core/libs/prx/libSceNet/include/Net.hpp | 9 ++++++++- core/libs/tests/GuestSceNet.cpp | 17 +++++++++++++++++ docs/dev/debt/libSceNet.md | 1 + 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 01650813ff75..829424a63248 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -2538,7 +2538,9 @@ int APS5_VABI sceNetClearDnsCache(int flags) { } int APS5_VABI sceNetGetStatisticsInfo(NetStatisticsInfo* info, std::uint32_t flags) { - NotImplemented_nid_no_patch(__func__); + if (info == nullptr) return fail(NET_EFAULT); + constexpr std::int32_t FREE_BYTES = 16 * 1024 * 1024; + *info = {FREE_BYTES, FREE_BYTES, 0, 0, FREE_BYTES, FREE_BYTES}; return 0; } diff --git a/core/libs/prx/libSceNet/include/Net.hpp b/core/libs/prx/libSceNet/include/Net.hpp index deffa39409fd..86205fa855eb 100644 --- a/core/libs/prx/libSceNet/include/Net.hpp +++ b/core/libs/prx/libSceNet/include/Net.hpp @@ -87,7 +87,14 @@ struct NetSockInfo6 { std::int32_t tx_wait; std::int32_t reserved[2]; }; -struct NetStatisticsInfo; +struct NetStatisticsInfo { + std::int32_t kernel_mem_free_size; + std::int32_t kernel_mem_free_min; + std::int32_t packet_count; + std::int32_t packet_qos_count; + std::int32_t libnet_mem_free_size; + std::int32_t libnet_mem_free_min; +}; struct NetInterfaceStats; struct NetRouteInfo; struct NetDebugParameter; diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index c41d2d535d39..e180606a2ce7 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -301,6 +301,22 @@ static void CheckPoolStats() { Require(Failed(sceNetGetMemoryPoolStats(pool, &stats), 9)); } +static void CheckStatisticsInfo() { + Require(Failed(sceNetGetStatisticsInfo(nullptr, 0), 14)); + NetStatisticsInfo info; + std::memset(&info, 0xaa, sizeof(info)); + Require(sceNetGetStatisticsInfo(&info, 0) == 0); + Require(sizeof(info) == 24); + Require(info.kernel_mem_free_size > 0 && info.kernel_mem_free_min > 0); + Require(info.libnet_mem_free_size > 0 && info.libnet_mem_free_min > 0); + Require(info.kernel_mem_free_min <= info.kernel_mem_free_size); + Require(info.libnet_mem_free_min <= info.libnet_mem_free_size); + Require(info.packet_count == 0 && info.packet_qos_count == 0); + NetStatisticsInfo again; + Require(sceNetGetStatisticsInfo(&again, 0x1234) == 0); + Require(std::memcmp(&again, &info, sizeof(info)) == 0); +} + static void CheckIfName() { constexpr std::array names = { "lo0", "eth0", "eth1", "dbg0", "wlan0", "wlan1", "gbe0", "bt0", "phone0", "pppoe0"}; @@ -2169,6 +2185,7 @@ int main() { CheckEpollWritable(); CheckSocketHostErrors(); CheckPoolStats(); + CheckStatisticsInfo(); CheckIfName(); CheckRandom(); CheckByteOrderConversion(); diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 575f6af87b83..f54727a85099 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -5,6 +5,7 @@ - [sceNetShowIfconfig, sceNetShowNetstat](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) print the console's layout (a PS5 on firmware 13.40, measured 2026-10-19) with what the emulator knows. Ifconfig prints `eth0` from the host interface `sceNetCtlGetInfo` reports, without its IPv6 address, `UP6`, link status, traffic counters or times, then `lo0` as the console does and the two IPv4 DNS servers, with `::, ::` for IPv6. It leaves out the wireless interfaces and the pool and statistics sections. Netstat lists only the title's sockets and epolls; the console lists every process's. The self and non-blocking flags, addresses, buffer sizes and names are filled; the R, S, RE and SE flags, the field after the type, queue lengths, rates and drops print as for an idle socket, TCP states are only `LISTEN`, `ESTABLISHED` and `CLOSED`, and the self flag on epolls is inferred. Before initialization both return the not-initialized error. - [sceNetShowPolicy, sceNetShowNetstatEx, sceNetShowRoute, sceNetShowRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - return success without printing anything: the `()` signatures and the success return follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/net.cpp), which stubs each the same way. PPSA01530 fails to load without the `sceNetShowIfconfig` export (#3177); PPSA11386 imports `sceNetShowNetstat` but does not call it during start-up. What the console prints and where is unverified +- [sceNetGetStatisticsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - fills the six 32-bit fields (kernel memory free and minimum free, packet count, QoS packet count, library memory free and minimum free) with 16 MiB free, nothing queued and no flag check, and a null pointer fails with `EFAULT`, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp) does; the layout follows its `net_types.h`. The host has no counters for the console's network memory, so the values are not measured and the call works before `sceNetInit`. ### Unknown function info