feat(libkernel): record exception handlers
sceKernelInstallExceptionHandler and sceKernelRemoveExceptionHandler threw. They now keep one handler per allowed signal (SIGHUP, SIGILL, SIGFPE, SIGBUS, SIGSEGV, SIGUSR1, as in shadPS4): another signal or a null handler returns EINVAL, a second handler for a signal returns EAGAIN, and removing clears it. Delivery is not implemented and is recorded in the technical debt; sceKernelRaiseException still throws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0518f0e021
commit
3b8ef5bc4f
|
|
@ -245,6 +245,15 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_signals PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
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:$<TARGET_FILE_DIR:libkernel>")
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -2,19 +2,39 @@
|
|||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/KernelErrors.hpp"
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::array<int, 6> AllowedSignals{1, 4, 8, 10, 11, 30};
|
||||
|
||||
std::mutex handlersLock;
|
||||
std::array<void*, 32> 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
|
||||
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<int>(0x80020016);
|
||||
static constexpr int SCE_KERNEL_ERROR_EAGAIN = static_cast<int>(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<void*>(&Handler);
|
||||
auto* other = reinterpret_cast<void*>(&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);
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue