<feed xmlns='http://www.w3.org/2005/Atom'>
<title>amd-psp-re.git/psp_out/FINDING-RING-LOADTA-WORKS-RAS-SPECIFIC-FAILURE.md, branch main</title>
<subtitle>AMD PSP reverse engineering, emulator and vulnerability research</subtitle>
<id>https://git.sl0p.foo/amd-psp-re.git/atom/psp_out/FINDING-RING-LOADTA-WORKS-RAS-SPECIFIC-FAILURE.md?h=main</id>
<link rel='self' href='https://git.sl0p.foo/amd-psp-re.git/atom/psp_out/FINDING-RING-LOADTA-WORKS-RAS-SPECIFIC-FAILURE.md?h=main'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/'/>
<updated>2026-09-24T12:35:31Z</updated>
<entry>
<title>Record the inconclusive attempt to find RAS's init parameter source</title>
<updated>2026-09-24T12:35:31Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-24T12:35:31Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/commit/?id=140adcc09055c6af5527975d4e55645cdb22a06d'/>
<id>urn:sha1:140adcc09055c6af5527975d4e55645cdb22a06d</id>
<content type='text'>
Tried to identify what legitimately produces the word RAS receives as 0, by
reading sOS's init parameter block around 0x202D24 -- [r4+1] (the 0xff gate that
decides whether params get filled at all), [r4+4], [r5+0x30], and the assembled
block at the send.

Every read fails with a Unicorn error: these are sOS virtual addresses and
uc.mem_read does not resolve them in that hook context. The measurement says
nothing about the firmware, so nothing is concluded from it in either direction.

Recorded rather than discarded, because the failure mode is non-obvious and the
next person would otherwise spend the same effort rediscovering it. The next
attempt needs the VA-&gt;PA resolution first, which the harnesses already do
elsewhere for TA address spaces.
</content>
</entry>
<entry>
<title>RAS authors 0xffff000b itself; locate the single fix-point, do not patch it</title>
<updated>2026-09-24T11:59:33Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-24T11:59:33Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/commit/?id=7f6cd68aff688f5a26934102356650070c86d787'/>
<id>urn:sha1:7f6cd68aff688f5a26934102356650070c86d787</id>
<content type='text'>
Ruled out the timeout reading first: both TAs execute during the send (HDCP 9
blocks, RAS 13), so RAS runs its handler and answers rather than going silent.

Mechanism, from RAS's own code:

    00402c6c  bl   #0x402ed0
    00402c70  cbz  r0, #0x402c94      ; callee returned 0 -&gt; error
    00402c94  ldr  r0, [pc, #0x30]    ; lit = 0xffff000b

0x402ed0 scans its argument for the first nonzero nibble and requires it to be
5, 6 or 7. RAS receives r0 = 0 as its first argument -- passed in, not read from
memory (r0=0x0, r1=0x412010, r2=0x412008) -- so the callee returns 0 at once.

The experiment needed its own control, and that turned out to matter. Forcing r0
at the function entry changed nothing, which reads as "hypothesis refuted":

    baseline                              0xffff000b
    force r0 AFTER the call, at 0x402C70  0x00000000   &lt;- success
    force r0 at entry,       0x402C64     0xffff000b   &lt;- no effect

The middle row is the positive control: reg_write from a code hook does affect
this TA, and supplying a nonzero value at the decision point makes the whole
LOAD_TA succeed. The entry-site null is an artefact of writing a register at a
translation-block boundary. Without that control I would have recorded a false
refutation.

So RAS's image loads and verifies, and its init declines only because one input
word arrives as 0. This is an emulator environment gap, not a firmware defect,
and it is deliberately NOT patched -- a forced register here would be precisely
the "stub returning 0 is a silent lie" failure mode this project avoids. The
remaining work is finding what legitimately produces the value; the card runs
RAS 27.0.2.5 successfully, so it is observable on hardware.
</content>
</entry>
<entry>
<title>Trace RAS's 0xffff000b to the TA init handshake, not the loader</title>
<updated>2026-09-24T11:51:38Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-24T11:51:38Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/commit/?id=097706f2cb09f8a8ee05539b3fc2b052149653b6'/>
<id>urn:sha1:097706f2cb09f8a8ee05539b3fc2b052149653b6</id>
<content type='text'>
Continuing past 0x202CF2, tee_load_ta's sl == 2 arm checks a typeword against
0x4154, then sends command 3 (init/create) via ta_send_command at 0x202D4C and
takes its return as the status. Measured for both TAs:

    HDCP  typeword 0x4154 ok, ta_send_command -&gt; 0x0          ring 0x00000000
    RAS   typeword 0x4154 ok, ta_send_command -&gt; 0xffff000b   ring 0xffff000b

Both pass the gate, both reach the send; the host's status is that return
verbatim. Full chain:

    ring LOAD_TA -&gt; tee_cmd_dispatch (tbb @0x202922, cmd 1 -&gt; 0x2029AC)
                 -&gt; tee_load_ta @0x202C24
                 -&gt; bl_load_ta_image        SUCCEEDS for both, r4 == 0
                 -&gt; ta_send_command(cmd 3)  HDCP 0x0, RAS 0xffff000b   &lt;- origin

So RAS's image loads and verifies fine and the failure is the init handshake
with the loaded TA. That agrees with the independent observation in this same
harness that RAS's init gate at VA 0x4060E0 reads 0x00 while HDCP's reads 0x90.

The remaining question is therefore not "why does LOAD_TA reject RAS" but "why
does the RAS TA fail its init", a TA-side environment question -- the load trace
shows sysdrv_rpc_3104/3105/1005 involvement.

Recorded one caveat rather than glossing it: ta_send_command returning
0xffff000b does not prove the TA authored that code; it may be a timeout. The
cheap discriminator is whether RAS's command handler executes during the send.

Suite green.
</content>
</entry>
<entry>
<title>Narrow RAS's 0xffff000b: not the image loader, and two hypotheses refuted</title>
<updated>2026-09-24T11:07:24Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-24T11:07:24Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/commit/?id=db89f0078277ba8a5ffe812d2994ff5514dea955'/>
<id>urn:sha1:db89f0078277ba8a5ffe812d2994ff5514dea955</id>
<content type='text'>
Differentially traced HDCP (loads) against RAS (0xffff000b), fresh build each,
filtering the idle spin loop that swamps the trace with 343k blocks of phase
drift.

Refuted "RAS is already loaded at boot": HDCP is equally loaded and succeeds.

Refuted "RAS trips the size guard at 0x2041F2", which was the natural reading of
the ta_prop_get_heap_pages trace. bl_load_ta_image has exactly one site setting
error 0xb, reached from a page-count check and an available-memory check.
Hooking both shows RAS never reaches it and the two TAs pass identically:
combined field 0x6 against a 0x400 limit, need 0xf000, avail 0x3e0000, FITS.

The runs are block-identical for 186 blocks and part at 0x2047F0,
bl_load_ta_image's continuation test. r4 == 0 for BOTH, so the loader succeeds
for RAS as well; HDCP loops back to +0x9c for a further segment while RAS falls
out to 0x20480E and returns. In tee_load_ta the error branch at 0x202CCE is not
taken for RAS, confirming a zero return, and RAS proceeds through the sl == 2
arm to 0x202CF2.

So 0xffff000b is produced after the image loader, on tee_load_ta's path through
0x202CF2, with sl discriminating RAS from HDCP. The image loads fine; something
downstream rejects it.

Also worth recording: this trace was truncated twice by hook ranges ending
exactly at the addresses that mattered (0x2029A0, then 0x204000), each time
producing a plausible but wrong divergence point. Widening the range changed the
answer both times.
</content>
</entry>
<entry>
<title>Ring LOAD_TA works; the failure is RAS-specific, not a harness defect</title>
<updated>2026-09-24T10:50:56Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-24T10:50:56Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/commit/?id=e4f3eb5d4971a1d30aefbc8f5900d83b23a1c211'/>
<id>urn:sha1:e4f3eb5d4971a1d30aefbc8f5900d83b23a1c211</id>
<content type='text'>
With Ring.response() reading the correct address, LOAD_TA over the host ring can
finally be evaluated, and the picture is different again:

    RAS image                 status 0xffff000b  session 0
    HDCP image                status 0x00000000  session 0    &lt;- SUCCEEDS
    RAS, 0x40 bytes corrupted status 0x34 (52)                &lt;- signature failure
    all-zero image            status 0x2d (45)

Four distinct outcomes, so the path discriminates. LOAD_TA over the ring WORKS:
HDCP loads with status 0. The earlier "LOAD_TA registers no session" was the
stale readback, and the follow-on "LOAD_TA fails" holds only for RAS. The 0x34
matches the signature-verification failure code load_asd.py reports for a
bit-flipped image, so verification is live and correct.

It is also not "already loaded": HDCP is equally loaded at boot and succeeds.

Located where it fails. tee_cmd_dispatch reaches cmd 1 via tbb [pc, r6] at
0x202922 (table 0x202926, cmd 1 -&gt; 0x2029AC); the handler calls tee_load_ta at
0x202C24 and takes its return as the status, so 0xffff000b originates inside
tee_load_ta. The sOS trace during the failing load runs through TA manifest
parsing -- ta_prop_get, ta_prop_get_heap_pages, ta_prop_get_appid, bl_rpc_probe,
sysdrv_rpc_3104/3105/1005 -- which points at a resource or property condition
specific to RAS's manifest rather than image validity.

What this unblocks: HDCP loads and returns a session over the ring, so the
INVOKE_CMD path can now be exercised end to end in full_system against a TA that
loads. That was what blocked command-coverage work, and it no longer depends on
solving RAS first.

Suite green.
</content>
</entry>
</feed>
