Repository navigation
Conversation
The command thread ran at priority 20. The EE kernel schedules strictly by priority, so a thread of the loaded program that never blocks and runs at a numerically lower priority starves it, and reset (and every other command) is never executed. The thread sleeps unless a command arrives, so a high priority costs the program nothing. Co-Authored-By: Claude Opus 5.5 <[email protected]>
SifIopSync() returns 1 once the IOP has finished booting (BOOTEND). pkoReset() looped while it returned 1, so it fell through at once and called SifInitRpc() on an IOP that was still rebooting. Use the same loop as restartIOP() in ps2link.c: while (!SifIopSync()); Co-Authored-By: Claude Opus 5.5 <[email protected]>
Only the program's main thread (userThreadID) was terminated. Other threads it created (e.g. an SDL audio thread, pad or network threads) kept running while the IOP was reset underneath them, for example blocked in or issuing RPCs. Remember which threads exist right before the first program is started (ps2link's own and any kernel/patch threads) and terminate and delete every other thread in pkoReset(). The list lives in .bss, so it is rebuilt after the ExecPS2() restart. Co-Authored-By: Claude Opus 5.5 <[email protected]>
A program may leave a GIF (channel 2) or VIF1 (channel 1) transfer running or PATH3 masked. Stop both channels (with CPND held while STR is cleared), then reset VIF1 and GIF before the IOP reset and the restart. Only kuseg registers (0x1000xxxx) are touched, so this also works when the EE runs in user mode. Co-Authored-By: Claude Opus 5.5 <[email protected]>
pkoReset() restarts ps2link with ExecPS2(&__start, ...). crt0 clears .bss but .data is kept. newlib marks stdio as initialized in _impure_data (.data, __cleanup), while the FILE structs and their locks (__sf) live in .bss. After the restart __sinit() therefore skips the re-initialization and the first printf() takes a NULL lock: TLB load exception, BadVAddr 8, EPC in __retarget_lock_acquire_recursive, called from the command thread's first dbgprintf(). It shows up on every start when IPCONFIG.DAT is missing (the initial boot prints "Error reading ipconfig.dat" and then restarts), and after a reset once ps2link has printed anything. libcglue clears the mark in __locks_init(), but through the full struct _reent of the installed headers. ps2link links newlib-nano (NEWLIB_NANO = 1), which is built with _REENT_SMALL, where __cleanup is at offset 32 instead of 52, so that reset hits another field. Clear the mark through the _REENT_SMALL layout before ExecPS2(). Co-Authored-By: Claude Opus 5.5 <[email protected]>
On reset, the IOP command thread first unmounted host: and deleted the tty driver, and only then sent PKO_RPC_RESET to the EE. With a program that keeps talking to the IOP (an SDL2 program printing once a second while streaming audio), the reset never ran: host: was gone (TCP 18193 refused), the program froze and only ICMP still answered. Send the command to the EE first, then remove host: and tty as before. Also import DelayThread, which the next commit uses. Co-Authored-By: Claude Opus 5.5 <[email protected]>
sceSifSetDma() returns 0 when the SIF DMA queue is full, e.g. while a program streams audio or pad data. pkoSendSifCmd() then dropped the command; for a reset that leaves ps2link dead. Retry for up to about one second. Co-Authored-By: Claude Opus 5.5 <[email protected]>
ps2sdk's InitThread() moves a program's main thread to priority 1 and SDL2 runs its audio thread one above that, at 0. When such a thread keeps the CPU busy, the command thread (priority 1) is woken by the SIF0 interrupt but never runs, so pkoReset() is never called. When the interrupt handler sees PKO_RPC_RESET, demote all threads of the program to the lowest priority and raise the command thread to 0 before waking it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
resetdid not reliably bring ps2link back after a program had run. With an SDL2 program (audio thread, pad, gsKit),resetdropped the host connection and ps2link never came back: TCP 18193 was refused, only ICMP answered, and the console needed a power cycle. A ps2link start withoutIPCONFIG.DAT(initial boot followed by a self-restart) also crashed on the firstprintf.Each cause is fixed in its own commit:
pkoReset: wait until the IOP has rebooted.while (SifIopSync());was inverted; nowwhile (!SifIopSync());, asrestartIOP()already does.pkoReset: terminate all threads of the loaded program, not only the main thread. The set of ps2link's own threads is recorded before the firstexecee.pkoReset: stop GIF and VIF1 DMA and reset GIF/VIF1. Only kuseg registers are touched, so this also works in user mode.pkoReset: let stdio re-initialize after the restart.ExecPS2(&__start)keeps.data, so newlib's__cleanupmark survives while theFILElocks in.bssare cleared. The firstprintfthen takes a NULL lock (TLB load, BadVAddr 8). libcglue clears the mark in__locks_init(), but at the offset of the fullstruct _reent. ps2link links newlib-nano (_REENT_SMALL,__cleanupat offset 32), so that reset misses the field. That part will also be reported to ps2sdk.InitThread()puts the main thread at priority 1, and SDL2 puts its audio thread at 0. The command thread was woken by the SIF0 interrupt but never ran. The interrupt handler now demotes the program's threads and raises the command thread to 0 when it seesPKO_RPC_RESET.Testing
Tested on real hardware: a PSX DESR-5100 (PS2-based), with ps2link at a fixed IP. Programs were started with
execee, each followed byreset, and then a check that ps2link answers again.resetokresethangs, power cycle neededIPCONFIG.DATprintfafter the self-restartresethangs, power cycle neededIPCONFIG.DATEach test program printed an end marker with
status=0before thereset. The diagnosis used on-screen markers inpkoReset: they showed that the reset packet reached the EE in the SIF0 interrupt, but the command thread never ran (fixed by 8). Those markers are not part of this PR.Not tested separately: whether commit 6 (order on the IOP) is still needed once commit 8 is in. The new order is safer, but the hardware test only covers the full set.
🤖 Generated with Claude Code