Skip to content

fix(iop): serve the cdvdfsv RPC family from a ROM-resident HLE service - #403

Open
LeandroSSB wants to merge 3 commits into
ran-j:mainfrom
LeandroSSB:pr-09-cdvdfsv-hle
Open

LeandroSSB wants to merge 3 commits into
ran-j:mainfrom
LeandroSSB:pr-09-cdvdfsv-hle

Conversation

@LeandroSSB

Copy link
Copy Markdown

Root cause

cdvdman/cdvdfsv register their EE-facing RPC servers from the IOP ROM at boot — they never arrive as disc IRX modules. The runtime only published services from load-emulated modules, so the four cdvdfsv SIDs (Ccmd 0x80000592, Scmd 0x80000593, Stcmd 0x80000595, search 0x8000059A) had no owner: the libcdvd bind (sceSifBindRpc polling until client->server != 0) spun forever, and no sceCdRead ever issued.

With bind fixed, the boot handshake (SifCallRpc(scmd, 0x27, ...)) received a zeroed buffer and the caller spun on recv[0] == 0.

Fix

  • IOP HLE service cdvdman (the mcserv/libsd/dbcman pattern; no module aliases — it is ROM-resident, not a loadable module) publishing the four cdvdfsv routes.
  • handleRpc answers a neutral ready / no-error / zero payload and logs unmodeled commands (rate-limited, unknown_rpc_logs metric) so unknown Scmd/Ccmd traffic stays visible without spamming.
  • writeBootHandshakeReply answers {1, 0, 0} on Scmd 0x27 (the boot-denominator handshake: success word 1, no error, zero payload) so the EE caller leaves the handshake spin.

Test

ps2_iop_tests "cdvdman ROM service serves the cdvdfsv RPC family": the four SIDs bind without any module load, the reply is delivered to the receive buffer, unknown commands are answered neutral + logged; the handshake first word is pinned == 1 (the original != 0 assertion was satisfied by 0xCC prefill poison — a false positive, hardened in a follow-up commit here).

Measured (MOH:Vanguard boot)

Bind delay loop 13.132 enters → 1; bind → call → callback cycle 3.644x / 60 s (~61 Hz); the game exits the handshake and issues 1.867 sceCdRead(LBN 0x10) PVD probes.

Related: #358 (Mana Khemia) documents the cdvdfsv bind gap; #359 fixes sceCdLayerSearchFile / loadModuleBuffer / sceSifSearchModuleByAddress — complementary, it does not publish the cdvdfsv servers or answer the Scmd 0x27 handshake, which is the half this PR covers.

🤖 Generated with Claude Code

Suite evidence

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

LeandroSSB and others added 3 commits October 26, 2026 00:30
Games bind the cdvdman/cdvdfsv SIF RPC endpoints (Ccmd 0x80000592,
Scmd 0x80000593, Stcmd 0x80000595, search 0x8000059A) during libcdvd
init and poll client->server until a server descriptor is published.
Those servers live in the IOP ROM, so no disc module ever registers
them and the bind loop never completes: SLUS_215.97 spins forever in
SifBindRpc(0x80000593) before any file access.

Add a cdvdman IopService that owns the four SIDs with no module
aliases (ROM modules are always resident). It claims the family's
RPCs and answers with the neutral ready/no-error/zero-data payload,
logging unmodeled commands (rate-limited, debug metric) so real
semantics can follow observed traffic.

Test note: ps2xTest PS2IopSubsystem gains a case asserting the four
SIDs are bindable without module loads, Scmd 0x27 is claimed and
replies success, and unknown Scmd commands are answered plus logged.
Suite 447/447.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After the cdvdfsv family became bindable, SLUS_215.97 reached the next
stage of its libcdvd init: SifCallRpc(Scmd 0x80000593, 0x27, ..., 12-byte
receive), then a poll of the receive buffer's first word. The neutral
zeroed answer kept the game in that poll forever (3.6k cycles in 60s,
~61Hz, alternated with the syscall-stub WaitSema helpers).

Answer the handshake with {1, 0, 0}: nonzero first word unlocks the
poll, neutral drive state in the two words the game copies aside.

Test note: the PS2IopSubsystem cdvdman case now asserts the Scmd 0x27
reply's first word is nonzero. Suite 447/447.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The success assert for the Scmd 0x27 boot handshake only checked the
first reply word for nonzero, which the 0xCC poison prefill already
satisfies: a service that claims the RPC but never writes the reply
buffer would pass. writeBootHandshakeReply answers {1,0,0}, so pin the
assertion to the actual success word.

Suite 448/448.

Co-Authored-By: Claude Fable 5 <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