diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index 9c5f89017..427e70bd2 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -189,6 +189,16 @@ if(BUILD_TESTING) "PATH=path_list_prepend:$") endif() + add_test_executable(guest_memory_pool_stats_tests tests/GuestMemoryPoolStats.cpp) + target_include_directories(guest_memory_pool_stats_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(guest_memory_pool_stats_tests PRIVATE libSceSsl libSceHttp2 libSceNet libkernel libc) + configure_windows_unwind(guest_memory_pool_stats_tests) + add_test(NAME guest_memory_pool_stats COMMAND guest_memory_pool_stats_tests) + if(WIN32) + set_tests_properties(guest_memory_pool_stats PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() + add_test_executable(guest_system_gesture_tests tests/GuestSystemGesture.cpp) target_include_directories(guest_system_gesture_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(guest_system_gesture_tests PRIVATE libSceSystemGesture libc) diff --git a/core/libs/prx/libSceHttp2/Export.cpp b/core/libs/prx/libSceHttp2/Export.cpp index 215d674e5..d7874e9c9 100644 --- a/core/libs/prx/libSceHttp2/Export.cpp +++ b/core/libs/prx/libSceHttp2/Export.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include "prx/libkernel/Equeue/Equeue.hpp" @@ -13,6 +14,8 @@ // No network is emulated: contexts, templates and requests can be created, but any request // that would touch the network fails with the library's network error. static constexpr int ERROR_NETWORK = static_cast(0x80436063); +static constexpr int ERROR_INVALID_ID = static_cast(0x817B1100); +static constexpr int ERROR_NULL_POINTER = static_cast(0x817B1225); static std::atomic g_nextHandle{1}; static std::mutex g_poolsMutex; static std::map g_pools; @@ -256,10 +259,13 @@ int APS5_VABI sceHttp2AbortRequest(int req_id) { } int APS5_VABI sceHttp2GetMemoryPoolStats(int lib_http2_ctx_id, Http2MemoryPoolStats* stats) { - if (stats == nullptr) APS5_INVALID_ARG_EX; + if (stats == nullptr) return ERROR_NULL_POINTER; std::lock_guard lock(g_poolsMutex); const auto pool = g_pools.find(lib_http2_ctx_id); - if (pool == g_pools.end()) throw std::invalid_argument("sceHttp2GetMemoryPoolStats: unknown context"); + if (pool == g_pools.end()) { + std::memset(stats, 0, sizeof(*stats)); + return ERROR_INVALID_ID; + } *stats = {pool->second, 0, 0, 0}; return 0; } diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 2bd2c3eb7..85ba8b7f3 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -912,14 +912,14 @@ int APS5_VABI sceNetEpollCreate(const char* name, int flags) { } int APS5_VABI sceNetGetMemoryPoolStats(int memid, NetMemoryPoolStats* stats) { - if (stats == nullptr) { - return fail(NET_EINVAL); - } std::lock_guard lk(g_mutex); const auto pool = g_pools.find(memid); if (pool == g_pools.end()) { return fail(NET_EBADF); } + if (stats == nullptr) { + return fail(NET_EINVAL); + } *stats = {pool->second, 0, 0}; return 0; } diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index a1623ff2a..e8fd49499 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include @@ -19,6 +20,8 @@ namespace { constexpr int ERROR_NOT_FOUND = static_cast(0x8095F004); constexpr int ERROR_INVALID_ARG = static_cast(0x8095177A); +constexpr int ERROR_INVALID_ID = static_cast(0x8095F006); +constexpr int ERROR_INVALID_VALUE = static_cast(0x8095F007); struct SslData { char* ptr; @@ -115,10 +118,13 @@ int APS5_VABI sceSslUnloadCert(int sslCtxId) { } int APS5_VABI sceSslGetMemoryPoolStats(int ssl_ctx_id, SslMemoryPoolStats* stats) { - if (stats == nullptr) APS5_INVALID_ARG_EX; + if (stats == nullptr) return ERROR_INVALID_VALUE; std::lock_guard lock(g_poolsMutex); const auto pool = g_pools.find(ssl_ctx_id); - if (pool == g_pools.end()) throw std::invalid_argument("sceSslGetMemoryPoolStats: unknown context"); + if (pool == g_pools.end()) { + std::memset(stats, 0, sizeof(*stats)); + return ERROR_INVALID_ID; + } *stats = {static_cast(pool->second), 0, 0, 0}; return 0; } diff --git a/core/libs/tests/GuestMemoryPoolStats.cpp b/core/libs/tests/GuestMemoryPoolStats.cpp new file mode 100644 index 000000000..7aec461e1 --- /dev/null +++ b/core/libs/tests/GuestMemoryPoolStats.cpp @@ -0,0 +1,90 @@ +#include "prx/libc/include/general/VabiMacros.hpp" + +#include +#include +#include +#include +#include + +struct PoolStats { + std::size_t pool_size; + std::size_t max_inuse_size; + std::size_t current_inuse_size; + std::int32_t reserved; +}; + +struct NetPoolStats { + std::size_t pool_size; + std::size_t max_inuse_size; + std::size_t current_inuse_size; +}; + +extern "C" { +int APS5_VABI sceSslInit_nid_postfix(std::size_t); +int APS5_VABI sceSslTerm_nid_postfix(int); +int APS5_VABI sceSslGetMemoryPoolStats(int, void*); +int APS5_VABI sceHttp2Init(int, int, std::size_t, int); +int APS5_VABI sceHttp2Term(int); +int APS5_VABI sceHttp2GetMemoryPoolStats(int, void*); +int APS5_VABI sceNetPoolCreate(const char*, int, int); +int APS5_VABI sceNetPoolDestroy(int); +int APS5_VABI sceNetGetMemoryPoolStats(int, void*); +} + +static void Require(bool value) { if (!value) std::abort(); } + +using StatsCall = int(APS5_VABI*)(int, void*); + +static void CheckRejected(StatsCall call, int id, int expected) { + std::array buffer; + buffer.fill(0x5a); + Require(call(id, buffer.data()) == expected); + for (std::size_t i = 0; i < sizeof(PoolStats); ++i) Require(buffer[i] == 0); + for (std::size_t i = sizeof(PoolStats); i < buffer.size(); ++i) Require(buffer[i] == 0x5a); +} + +int main() { + constexpr int sslInvalidId = static_cast(0x8095F006); + constexpr int sslInvalidValue = static_cast(0x8095F007); + constexpr int http2InvalidId = static_cast(0x817B1100); + constexpr int http2NullPointer = static_cast(0x817B1225); + constexpr int netBadDescriptor = static_cast(0x80410109); + constexpr int netInvalid = static_cast(0x80410116); + static_assert(sizeof(PoolStats) == 0x20 && sizeof(NetPoolStats) == 0x18); + + const int ssl = sceSslInit_nid_postfix(0x40000); + Require(ssl > 0); + PoolStats stats{}; + Require(sceSslGetMemoryPoolStats(ssl, &stats) == 0 && stats.pool_size == 0x40000); + Require(sceSslGetMemoryPoolStats(ssl, nullptr) == sslInvalidValue); + Require(sceSslGetMemoryPoolStats(12345, nullptr) == sslInvalidValue); + CheckRejected(sceSslGetMemoryPoolStats, 12345, sslInvalidId); + CheckRejected(sceSslGetMemoryPoolStats, -1, sslInvalidId); + + const int http2 = sceHttp2Init(1, ssl, 0x40000, 1); + Require(http2 > 0); + Require(sceHttp2GetMemoryPoolStats(http2, &stats) == 0 && stats.pool_size == 0x40000); + Require(sceHttp2GetMemoryPoolStats(http2, nullptr) == http2NullPointer); + Require(sceHttp2GetMemoryPoolStats(12345, nullptr) == http2NullPointer); + CheckRejected(sceHttp2GetMemoryPoolStats, 12345, http2InvalidId); + CheckRejected(sceHttp2GetMemoryPoolStats, -1, http2InvalidId); + Require(sceHttp2Term(http2) == 0); + CheckRejected(sceHttp2GetMemoryPoolStats, http2, http2InvalidId); + + Require(sceSslTerm_nid_postfix(ssl) == 0); + CheckRejected(sceSslGetMemoryPoolStats, ssl, sslInvalidId); + + const int pool = sceNetPoolCreate("stats", 0x20000, 0); + Require(pool > 0); + NetPoolStats netStats{}; + Require(sceNetGetMemoryPoolStats(pool, &netStats) == 0 && netStats.pool_size == 0x20000); + Require(sceNetGetMemoryPoolStats(pool, nullptr) == netInvalid); + Require(sceNetGetMemoryPoolStats(12345, nullptr) == netBadDescriptor); + std::array untouched; + untouched.fill(0x5a); + auto buffer = untouched; + Require(sceNetGetMemoryPoolStats(12345, buffer.data()) == netBadDescriptor && buffer == untouched); + Require(sceNetPoolDestroy(pool) == 0); + Require(sceNetGetMemoryPoolStats(pool, nullptr) == netBadDescriptor); + return 0; +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 090027395..ba5d8c340 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -212,7 +212,7 @@ Throughout the project, every function at every stage either **does exactly what - [scePadSetAngularVelocityBiasCorrectionState](../../core/libs/prx/libScePad/Export.cpp) (libScePad) - inferred signature `(int handle, bool enabled)` follows `scePadSetTiltCorrectionState` and `scePadSetAngularVelocityDeadbandState`; an invalid handle returns `PAD_ERROR_INVALID_HANDLE`, disabling is a no-op, and enabling throws because bias correction is not modelled - [sceKernelGetOperationMode](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the signature `(int* mode, int* submode)` comes from PPSA12544, which logs both values after the call; the values are unknown and 0 is reported for both, as in the [prosper](https://github.com/mattias800/prosper/pull/4153) reimplementation. Null outputs throw - Font, Http2, Net, Ssl, SystemService and libkernel exports imported by PPSA12544 and added without an implementation have unknown signatures -- [sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats, sceNetGetMemoryPoolStats](../../core/libs/prx/libSceNet/Export.cpp) (libSceHttp2, libSceSsl, libSceNet) - signatures `(int id, stats*)` and the statistics layouts (pool size, peak and current use, and for Http2 and Ssl a 32-bit reserved field) follow the PS4 SDK's `SceHttp2MemoryPoolStats`, `SceSslMemoryPoolStats` and `SceNetMemoryPoolStats`, not confirmed for the PS5. The pool size is the one given at init or pool creation; nothing is allocated from these pools, so the peak and current use are 0. Net returns `EBADF` for an unknown pool and `EINVAL` for a null output, as its other pool calls do; for Http2 and Ssl the error codes are unknown, so both cases throw +- [sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats, sceNetGetMemoryPoolStats](../../core/libs/prx/libSceNet/Export.cpp) (libSceHttp2, libSceSsl, libSceNet) - the signatures `(int id, stats*)`, the layouts (pool size, peak and current use, and for Http2 and Ssl a 32-bit reserved field) and the error codes were measured on a PS5 (firmware 13.60). The pool size is the one given at init or pool creation; nothing is allocated from these pools, so the peak and current use are 0, where the console already reports use right after initialization (0x15C0 bytes for Ssl, 0x1640 for Http2 and 4 for a Net pool) - [sceSslGetCaList, sceSslFreeCaList, sceSslUnloadCert](../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and the 16-byte `SceSslCaList` layout (an array of certificate handles and a 32-bit count) follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs) and shadPS4's `OrbisSslCaList`, not confirmed for the PS5. On the console the list holds the certificate authorities the SSL service has; [shadPS4](https://github.com/shadps4-emu/shadPS4/pull/4104) returns a placeholder certificate from `sceSslGetCaCerts` because Rise of the Tomb Raider fails its initialization checks without one. No root certificate bundle is shipped and no loaded certificate is kept, so here a context holds none: `sceSslGetCaList` clears the list and returns `NOT_FOUND`, as `sceSslGetCaCerts` does, `sceSslFreeCaList` accepts only that cleared list, and `sceSslUnloadCert` (imported by PPSA15508) has nothing to drop and is assumed to return 0. A null list returns `0x8095177A`, which `sceSslGetCaCerts` and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/59183dbfa81ed7efdff6069177fed50de0552725/src/libs/network.cpp) return for a null argument (shadPS4, PS4, returns `0x8095F007`). An unknown context throws (KytyPS5 returns `SSL_ERROR_INVALID_ID`, `0x8095F006`, from `sceSslGetCaCerts`), and so does a list `sceSslGetCaList` did not return - [sceHttp2SetResolveRetry](../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - signature `(int id, int32_t retry)` from libSceHttp's `sceHttpSetResolveRetry`; the value is accepted and ignored like the other Http2 settings, since no request reaches the network - [sceHttp2WebSocketCreateRequest, sceHttp2WebSocketCloseAsync, sceHttp2WebSocketSendTextMessageAsync, sceHttp2WebSocketSendDataMessageAsync](../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - imported by PPSA16064 and not called during its start-up; unknown signatures