diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 2e5364b1b..4af255f56 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -245,6 +245,15 @@ if(BUILD_TESTING) set_tests_properties(guest_signals PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_exception_handler_tests ${LIBS_INCLUDE_DIR}/tests/GuestExceptionHandler.cpp) + target_include_directories(guest_exception_handler_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_exception_handler_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_exception_handler_tests) + add_test(NAME guest_exception_handler COMMAND guest_exception_handler_tests) + if(WIN32) + set_tests_properties(guest_exception_handler PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_sync_on_address_tests ${LIBS_INCLUDE_DIR}/tests/GuestSyncOnAddress.cpp) target_include_directories(guest_sync_on_address_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_sync_on_address_tests PRIVATE libkernel libc) diff --git a/core/libs/prx/libkernel/System/src/Exception.cpp b/core/libs/prx/libkernel/System/src/Exception.cpp index 8e59a6b68..ba53dbdba 100644 --- a/core/libs/prx/libkernel/System/src/Exception.cpp +++ b/core/libs/prx/libkernel/System/src/Exception.cpp @@ -2,19 +2,39 @@ #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libkernel/KernelErrors.hpp" +#include +#include + +namespace { + +constexpr std::array AllowedSignals{1, 4, 8, 10, 11, 30}; + +std::mutex handlersLock; +std::array handlers{}; + +bool Allowed(int signum) { + for (const int allowed : AllowedSignals) + if (allowed == signum) return true; + return false; +} + +} extern "C" { int APS5_VABI sceKernelInstallExceptionHandler(int signum, void* handler) { - (void)signum; - (void)handler; - NotImplemented_nid_no_patch(__func__); + if (!Allowed(signum) || handler == nullptr) return SCE_KERNEL_ERROR_EINVAL; + std::lock_guard lock(handlersLock); + if (handlers[signum] != nullptr) return SCE_KERNEL_ERROR_EAGAIN; + handlers[signum] = handler; return 0; } int APS5_VABI sceKernelRemoveExceptionHandler(int signum) { - (void)signum; - NotImplemented_nid_no_patch(__func__); + if (!Allowed(signum)) return SCE_KERNEL_ERROR_EINVAL; + std::lock_guard lock(handlersLock); + handlers[signum] = nullptr; return 0; } diff --git a/core/libs/tests/GuestExceptionHandler.cpp b/core/libs/tests/GuestExceptionHandler.cpp new file mode 100644 index 000000000..fbb5dfd8f --- /dev/null +++ b/core/libs/tests/GuestExceptionHandler.cpp @@ -0,0 +1,33 @@ +#include "SceTypes.hpp" +#include +#include + +extern "C" { +int APS5_VABI sceKernelInstallExceptionHandler(int signum, void* handler); +int APS5_VABI sceKernelRemoveExceptionHandler(int signum); +} + +static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast(0x80020016); +static constexpr int SCE_KERNEL_ERROR_EAGAIN = static_cast(0x80020023); + +static void APS5_VABI Handler(int, void*) {} +static void APS5_VABI Other(int, void*) {} + +static void Require(bool value) { if (!value) std::abort(); } + +int main() { + auto* handler = reinterpret_cast(&Handler); + auto* other = reinterpret_cast(&Other); + for (const int rejected : {0, 2, 9, 31, 128, -1}) + Require(sceKernelInstallExceptionHandler(rejected, handler) == SCE_KERNEL_ERROR_EINVAL); + Require(sceKernelInstallExceptionHandler(30, nullptr) == SCE_KERNEL_ERROR_EINVAL); + Require(sceKernelRemoveExceptionHandler(9) == SCE_KERNEL_ERROR_EINVAL); + for (const int signum : {1, 4, 8, 10, 11, 30}) { + Require(sceKernelInstallExceptionHandler(signum, handler) == 0); + Require(sceKernelInstallExceptionHandler(signum, other) == SCE_KERNEL_ERROR_EAGAIN); + Require(sceKernelRemoveExceptionHandler(signum) == 0); + Require(sceKernelInstallExceptionHandler(signum, other) == 0); + Require(sceKernelRemoveExceptionHandler(signum) == 0); + } + Require(sceKernelRemoveExceptionHandler(30) == 0); +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 02ea84a84..9c0d96338 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -83,6 +83,8 @@ Throughout the project, every function at every stage either **does exactly what ### Functional +- [sceKernelInstallExceptionHandler](../../core/libs/prx/libkernel/System/src/Exception.cpp) records the handler but nothing delivers to it: a host fault still ends the process, and `sceKernelRaiseException` is not implemented. PPSA12544's PS5Util module (Unity) installs a SIGUSR1 handler that reads `uc_mcontext.mc_rsp` (offset 0xf8 of the context) and raises SIGUSR1 on other threads to stop them for garbage collection, so delivery must run the handler on the target thread with a FreeBSD `ucontext` + - [libSceUlt queues](../../core/libs/prx/libSceUlt/Export.cpp) use preallocated host storage instead of the supplied guest work area; its existing size formula and the 512-byte object layouts remain unverified. Waiting-queue pool thread limits and waiter priority ordering are not implemented. Finalization wakes blocked queue callers with `ULT_ERROR_STATE`. - [MIMG `tfe` and `lwe`](../../core/shader/recompiler/RdnaDecoder/src/RdnaImageOpDecoder.cpp) never write the status VGPR after the data. On hardware it is written only when a texel fetch hits an unmapped page of a partially resident texture, or, for `lwe` on sample/gather, when the LOD is below the T# `MIN_LOD_WARN`; texture memory is always resident here, and `lwe` on sample, gather and `image_get_lod` throws. - The shader recompiler [ignores `s_setreg_b32`](../../core/shader/recompiler/Translation/src/ScalarInstructions.cpp): writes to MODE (float rounding and denormal controls) and the other hardware registers have no effect, and `s_getreg_b32` reads of the MODE round fields report round to nearest even even after `s_setreg_b32` wrote another mode. The initial round mode from the shader's program registers is not read either.