Skip to content

fix(iop): accept temporary IOP sids (bit31) and answer the loader queue's cid-0 SifSendCmd with a completion reply - #404

Open
LeandroSSB wants to merge 1 commit into
ran-j:mainfrom
LeandroSSB:pr-11-sif-bit31-cid1
Open

LeandroSSB wants to merge 1 commit into
ran-j:mainfrom
LeandroSSB:pr-11-sif-bit31-cid1

Conversation

@LeandroSSB

Copy link
Copy Markdown

Root cause (two contract gaps in the EE-side SIF path)

  1. Temporary sids are refused. Games' IOP glue requests temporary sids (bit31 set, e.g. 0x80000006 for its module-query channel). SifBindRpc only served registered routes / emulated servers, so client->server stayed null forever and the SDK glue spun in its bind-retry wall — on MOH:Vanguard a 0x100000-iteration delay loop at the module-presence check, boot frozen at 199 CD reads.
  2. The loader queue's cid-0 SifSendCmd is black-holed. The game's loader queue submits work with sceSifSendCmd(cid=0); completion is signaled by an IOP-authored cid=1 reply carrying the same handle. The runtime let the request vanish, so the consumer polled a ticket that never retired (measured: exactly one SifSendCmd in the whole run, ack frozen while the producer counter grew).

Fix

  • SifBindRpc allocates the server descriptor for sids with bit31 set (the temporary per-function range games use for their own IOP modules); registered-sid handling and plain-sid refusal are unchanged.
  • sceSifSendCmd synthesizes the 32-byte cid=1 completion reply for cid=0 requests of >= 0x18 bytes: [+0xC] = the request packet's +0x14 handle echoed verbatim, [+0x10] = 2 (complete status), dispatched through the existing ps2_stubs::dispatchSifCommand so the game's own registered cmd handler runs as a queued EE invocation. The reply shape is audited against the game's handler: it reads exactly +0xC (handle) and +0x10 (selector); the other bytes are never loaded. cid != 0 traffic (the game's own reply direction) is never echoed.
  • SifCallRpc with rpc 0xFF and a 4-byte receive buffer answers the "2800" version tag the glue's version probe accepts instead of zeros, so the module-query init completes its version match.

Test

ps2_sif_rpc_tests / ps2_sif_dma_tests: bind of a bit31 sid completes the handshake and stays stable across clients; plain unregistered sids still refuse; the version probe answers the accepted tag while other unhandled rpcs keep the zero fallback; a cid=0 command yields exactly one synthesized cid=1 reply with the echoed handle, consumed by a registered completion handler (semaphore-parked consumer wakes and observes the reply fields).

Measured (MOH:Vanguard)

SDK bind-retry wall extinct (boot-thread park 0 of 260+ samples, was ~77%); CD reads resume 199 → 226; after the reply fix the loader poll region is extinct and the main thread leaves the loader for engine code.

Note: this PR deliberately carries no address-specific diagnostics — the bring-up probes used to decode the two contracts are not part of the fix.

Related: #339 delivers sceSifSendCmd to the IOP and models SIF_CMD_SET_SREG — a different slice of the same surface; it does not synthesize the cid-1 completion reply nor accept the bit31 bind, so the two are complementary.

🤖 Generated with Claude Code

Suite evidence

Fresh configure+build of ps2x_tests on this branch (VS 2022, Release): 451/451 passed, 0 failed.

…-0 SifSendCmd with a completion reply

Two contract gaps in the EE-side SIF path:

1. Games' IOP glue requests *temporary* sids (bit31 set, e.g. 0x80000006
   for the module-query channel). SifBindRpc refused them (no registered
   route, no emulated server), so client->server stayed null forever and
   the SDK glue spun in its bind-retry wall (on MOH:Vanguard: a
   0x100000-iteration delay loop at the module-presence check, boot
   frozen). SifBindRpc now allocates the server descriptor for the
   temporary range; registered-sid handling and plain-sid refusal are
   unchanged.
2. The game's loader queue issues sceSifSendCmd(cid=0) requests whose
   completion is signaled by an IOP-authored cid=1 reply carrying the
   same handle; the runtime let the request vanish, so the consumer
   polled a ticket that never retired. sceSifSendCmd now synthesizes the
   32-byte cid=1 completion reply for cid=0 requests of >= 0x18 bytes:
   [+0xC] = the request packet's +0x14 handle (echoed verbatim),
   [+0x10] = 2 (the complete-record status), dispatched through the
   existing ps2_stubs::dispatchSifCommand so the game's own registered
   handler runs as a queued EE invocation. The handler reads exactly
   +0xC (handle) and +0x10 (selector); delivery is a verbatim memcpy of
   the request block. cid != 0 traffic (the game's own reply direction)
   is never echoed.

Also answer SifCallRpc with rpc 0xFF and a 4-byte receive buffer with
the "2800" version tag the glue's version probe accepts instead of
zeros, so the module-query init completes its version match.

Tests (ps2_sif_rpc_tests, ps2_sif_dma_tests): bind of a bit31 sid
completes the handshake and stays stable across clients; plain
unregistered sids still refuse; the version probe answers the accepted
tag while other unhandled rpcs keep the zero fallback; a cid=0 command
yields exactly one synthesized cid=1 reply with the echoed handle,
consumed by the game's registered completion handler.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant