Repository navigation
feat(libkernel): implement _umtx_op wait and wake operations - #1482
Conversation
cac1c6d to
85c43cd
Compare
|
Tested on Windows 10 (19045), MinGW-w64 GCC 15.2.0 (WinLibs r7), Release, Intel Core i7-12700H (20 logical CPUs). I merged it onto
Tested with AI assistance (Claude Code). |
|
One difference from FreeBSD 11 in the WAIT_UINT path. Here the case does I checked it on the PR head (85c43cd, merged onto main 29e533a, Linux, GCC 16.2.1, Release). The PR's own
It doesn't matter much in practice, and I can't say what the PS5 kernel does. Since the description says this follows AI-assisted: yes |
|
For the two cases where PPSA12544's own |
adriadam10
left a comment
There was a problem hiding this comment.
Thanks for the detailed write-up. Two things before this can go in:
- As @oneandonlydean asked, throw (
NotImplemented_nid_no_patch) instead of returning an error in the two cases where the title'slibc.prxdisagrees with FreeBSD 11: the 4-byte microsecond timeout with size argument 0 on the WAIT ops, and operation 1 (SyncOnAddress.cpp:250-254,UmtxDeadlineat:128). Please also say how a libc call is told apart from PS5Util'stimespeccall if both pass size 0; if they can't be told apart, say so in TechnicalDebt and throw only on what is certain. - As @fisherjoey noted, FreeBSD's
do_waitwith compat32 compares the zero-extended word against the fullval, soWAIT_UINTwith any high bit set invalreturns 0 at once.SyncOnAddress.cpp:243truncates to 32 bits, andGuestUmtxOp.cpp:83asserts the opposite. Pass the full value and return 0 when it doesn't fit (after the timeout/null/alignment checks), and flip that assertion.
Update the TechnicalDebt line (TechnicalDebt.md:132) to match. Happy to look again once pushed.
|
Another title this unblocks on Linux: God of War Sons of Sparta (PPSA28997, Unity IL2CPP). On
|
|
For priority, an update from a catalog run on
Two more stop here once the relinker gets past their SDK syscall stubs with the opt-in flag discussed in #2136: OliSe (PPSA01153) and PS5CEMU-HAR (PPSA99360). That makes 11. No rush, @awake-devel. Thanks for the PR. |
|
Windows data point, two more catalog titles. Head
|
Titles that ship their own libc.prx or Unity's PS5Util.prx import _umtx_op (04AjkP0jO9U) for atomic waits, call_once and futex-style waits. Implement UMTX_OP_WAIT, UMTX_OP_WAKE, UMTX_OP_WAIT_UINT, UMTX_OP_WAIT_UINT_PRIVATE and UMTX_OP_WAKE_PRIVATE as FreeBSD 11's kern_umtx.c does, on the sceKernelSyncOnAddressWait waiter queue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
85c43cd to
3879e7e
Compare
adriadam10
left a comment
There was a problem hiding this comment.
Thanks all for the testing. Pushed the review fixes on top of your commit:
UMTX_OP_WAIT_UINT/_PRIVATEnow pass the fullvaland return 0 at once when it doesn't fit in 32 bits (FreeBSD's zero-extended compare), after the timeout/null/alignment checks; test flipped. Commit by @babyhuey (help/umtx-op-1482), cherry-picked.- Operation 1 throws
NotImplemented_nid_no_patch; operation 0 keepsEOPNOTSUPP. - A timeout with size argument 0 that isn't a valid
timespec(negative seconds, nanoseconds outside [0, 1e9)) throws instead of returningEINVAL. A libc microsecond value can't be told apart from atimespecby the size alone, so a value that reads as a validtimespecis waited on as one; this is in TechnicalDebt.EINVALtests now pass a size.
Rebased on main; guest_umtx_op and guest_sync_on_address pass; the test no longer links without the change.
What
Hollow Knight: Silksong (PPSA12544) ships its own
sce_module/libc.prxand Unity'sMedia/Modules/PS5Util.prx. Both import_umtx_op(NID04AjkP0jO9U) from libkernel, which AnyPS5 does not export, so on Linux the relinked title stops at load withundefined symbol: 04AjkP0jO9U.This adds
_umtx_opto libkernel for the operations those modules pass, following FreeBSD 11's kern_umtx.c:UMTX_OP_WAITobjequalsval; return 0 at once when it differs (do_wait, compat32 = 0)UMTX_OP_WAIT_UINT/_PRIVATEdo_wait, compat32 = 1)UMTX_OP_WAKE/_PRIVATEvalwaiters;val <= 0still wakes one (umtxq_signal_queue)Timeouts follow
umtx_copyin_umtx_time: whenuaddr(the size) is at most 16,uaddr2is a relativetimespec; otherwise it is an_umtx_time, absolute withUMTX_ABSTIMEon its clock. A negative second count or a nanosecond count outside [0, 1e9) fails withEINVAL, and a wait that times out fails withETIMEDOUT. Errors are returned as -1 with the guesterrnoset, like the system call. A null object givesEFAULT(exceptWAKE_PRIVATE, which does not look up the address). Operations 0 and 1 fail withEOPNOTSUPPand operations above 26 withEINVAL, as in FreeBSD 11'sop_table. The mutex, condition variable, rwlock, semaphore, shm and robust-list operations throwNotImplemented, as does a misaligned object.Waiters use the existing
sceKernelSyncOnAddressWaitqueue andTimedWait(no platform-specific code), so_umtx_opandsceKernelSyncOnAddressWakewake each other's waiters.What the title calls. I disassembled the two modules:
PS5Util.prx: a wait helper at 0x700 (UMTX_OP_WAIT, atimespecbuilt from a microsecond timeout) and a wake helper at 0x750 (UMTX_OP_WAKE). The result is read through__error.libc.prx: helpers at 0x116b80 (UMTX_OP_WAIT_UINT), 0x116b90 (UMTX_OP_WAIT) and 0x116ba0 (UMTX_OP_WAKE). They are used by exported atomic-wait functions (name stringSceLibcV2AtomicWait) and by a call_once (SceLibcIV2Once), which waits with operation 1 and wakes every waiter withUMTX_OP_WAKE(INT_MAX).Two things in these modules disagree with FreeBSD and could not be checked without a console. Both are recorded in TechnicalDebt:
sceKernelSyncOnAddressWait), while PS5Util passes atimespec. With FreeBSD's rule, libc's 4-byte value is read as atimespec. The nanosecond half is then almost always out of range, so the wait returnsEINVALat once and libc's loop checks again. Only the untimed waits (null timeout) block.EOPNOTSUPP. The once loop then checks the flag again until it reaches the done state, so it spins instead of sleeping.AI-assisted: the call sites were found and this change was written with Claude Code.
This PR was investigated and written by Claude Code (AI), and posted by me after review.
Tested
Arch Linux (kernel 7.2.8), i7-8750H, GTX 1050 Ti Max-Q (NVIDIA 580.178.04), GCC 16.2.1, Release build. Each run is compared with unmodified
mainbc1fbe8 on the same machine. All runs setAPS5_WRITE_WATCH_IMPORTS=watch; without it this NVIDIA driver creates no Vulkan device (#1289) and the GPU tests are skipped.ctest -j4: 424/424 passed, 0 skipped, 0 failed (baseline 423/423, 0 skipped). The only new test isguest_umtx_op.VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ctest -j2 -V: 424/424 passed. 27Validation Errorlines, the same as the baseline, with identical per-VUID counts.New test
core/libs/tests/GuestUmtxOp.cpp(modelled onGuestSyncOnAddress) covers:WAITand 32-bit forWAIT_UINT;timespec, relative_umtx_timeand absoluteCLOCK_MONOTONICtimeouts, including one in the past;INT_MAX), with a waiter on another address left untouched;Mutation check. With the test kept and the fix changed, the test failed every time:
UMTX_OP_WAITcompares 32 bits: test fails;The fix was then restored byte for byte and rebuilt, and
guest_umtx_opandguest_sync_on_addresspassed.Windows: not tested. The change has no Windows-specific code; it uses
std::uint64_tfor the guestu_long.Silksong (PPSA12544), Linux, relinked with feat(relinker): add --module-dir for modules loaded at run time #846 (
--module-dir Media/Plugins) and local throwing stubs for unrelated missing imports. Onmainthe loader fails withundefined symbol: 04AjkP0jO9U. With this change, all modules load, the title starts, reads/app0/Media/boot.config, and stops at a later, unrelated gap:sceKernelLoadStartModule with start arguments not implemented. The log does not show whether_umtx_opran before that point, so the wait and wake paths are covered only by the unit test.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code
https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8