<feed xmlns='http://www.w3.org/2005/Atom'>
<title>amd-psp-re.git/README.md, branch main</title>
<subtitle>AMD PSP reverse engineering, emulator and vulnerability research</subtitle>
<id>https://git.sl0p.foo/amd-psp-re.git/atom/README.md?h=main</id>
<link rel='self' href='https://git.sl0p.foo/amd-psp-re.git/atom/README.md?h=main'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/'/>
<updated>2026-09-23T21:06:53Z</updated>
<entry>
<title>Restructure the findings corpus: add INDEX.md, resolve duplicates, re-prioritise</title>
<updated>2026-09-23T21:06:53Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-23T21:06:53Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/commit/?id=9a1971a4ceed193ad4a3a0da9fe21621ed36795c'/>
<id>urn:sha1:9a1971a4ceed193ad4a3a0da9fe21621ed36795c</id>
<content type='text'>
131 documents had accumulated organically, with three overlapping "summary"
files, several topics split across 3-6 documents, and ten retracted documents
whose filenames still read as findings. Restructured rather than rewritten, so
no history is lost.

Added psp_out/INDEX.md as the map, built from a scan of all 131 files rather
than memory:

  * the spine -- the four documents that govern the rest (CONSTRAINTS,
    VULN-AUDIT-WHAT-WE-ACTUALLY-HAVE as the vulnerability register,
    ATTACK-SURFACE, INDEX);
  * RETRACTED, do not cite -- the ten files carrying retraction banners,
    listed explicitly because their names mislead;
  * duplicate clusters with the authoritative file named for each of ten
    topics (subcmd12, ASD 0x308, SRM walker, Route C, VCN, TMR fence, rollback,
    SMN map, emulator bring-up, hardware validation);
  * settled negatives, 17 of them, flagged as "do NOT re-run" since repeating
    them is the likeliest way to waste a session;
  * methodology, including the recurring failure mode named once: a measurement
    producing a confident result its own control invalidates, with this
    session's five instances listed;
  * priorities P1-P4, each with its blocker and next concrete step, plus an
    explicit deprioritised list.

Updated the vulnerability register for this session, since it predated it:
added the VCN type-49 write as a Tier-1 entry with its precise wording
constraint ("completes a register-write loop over attacker-controlled entries",
not "arbitrary VCN register write"); qualified Route C's "nothing reads those
registers" with the fact that the scan covered only images we hold and the
window has since been shown to be a PSP-private space, still unidentified; and
corrected the Tier-1 summary from one write primitive to two, neither with a
demonstrated consumer.

Annotated nine non-authoritative documents with a pointer banner so a reader
landing on them directly is not misled, and pointed README at the index.

Retraction checker clean over 59 harvested claims.
</content>
</entry>
<entry>
<title>full_system.py: sOS + SYS_DRV + all four TAs executing in one run</title>
<updated>2026-09-23T12:32:35Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-23T12:32:35Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/commit/?id=0ef1faeeaa247eb7460bc14a4ef19a2561490964'/>
<id>urn:sha1:0ef1faeeaa247eb7460bc14a4ef19a2561490964</id>
<content type='text'>
"sOS + TAs running" was already claimed by boot_sos_and_tas, but that claim
could not name a single TA. It classifies blocks by VA, and every TA runs at
VA 0x400000 in its OWN address space, so its "TA/other=28 blocks" line cannot
distinguish HDCP from DTM from RAS -- the VA-aliasing trap this tree documents
elsewhere and then fell into here. Measured by (VA, live PA) instead, that run
was HDCP 205 / DTM 9 / RAS 8 blocks, and ASD zero.

ASD was the real gap. It is driver-class, its boot-time staging slot is gated
off, and it arrives over the host ring -- which boot_sos_and_tas has no ring to
do, and load_asd.py does only in isolation. This harness does both in one
system: boot sOS (bl_stage2_task loads and verifies the boot modules), bring the
ring up, then ring-load ASD.

Attribution is two independent signals that must agree: address space (ASD at
0xD00000, TAs at 0x400000, SYS_DRV at 0xE00000, sOS below 0x300000) and the live
physical page of each block, matched against staged image content. Content
matching intersects several 48-byte slices because a single slice near a page
start hits shared libc boilerplate and matches all five images.

Result, deterministic across runs:

    sys_drv 532806   hdcp 204 (0x420e000, 0x420b000)   dtm 9 (0x41f3000)
    ras 8 (0x41e0000, 0x41de000)   asd 145 (5 pages)   sOS 214934
    4 modules loaded+verified at boot, ASD over the ring, 0 faults

SKIP_ASD=1 is a control, and the first version of it was confounded: skipping
the ring load also removed the run time that drain provided, so HDCP and DTM
stopped executing too and the control proved nothing about ASD. Both paths now
get an equal settle budget, and the control isolates cleanly -- only ASD flips
to NO, RSA verifications drop 4 -&gt; 3 (ASD's), exit 1.
</content>
</entry>
<entry>
<title>ASD loads, verifies and runs (harness/load_asd.py)</title>
<updated>2026-09-22T17:20:08Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-09-22T17:20:08Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/commit/?id=0ade2e501121b3d413c0a02422d269e1444ddec6'/>
<id>urn:sha1:0ade2e501121b3d413c0a02422d269e1444ddec6</id>
<content type='text'>
The tree recorded ASD as "not loaded", gated off by an undecoded runtime flag,
with the note that on hardware it arrives through the host-initiated load path
"which needs the ring/mailbox model". That model exists now, so ASD is loaded
through it.

The boot-time gate, decoded. bl_stage2_task guards the +0x6F8000 staging slot:

    00204a32  ldr   r0, [r4, #4]
    00204a34  ldr.w r0, [r0, #0x3e8]
    00204a38  cmp   r0, #0
    00204a3a  bge   0x204a76        ; &gt;= 0 -&gt; skip the ASD load entirely

r4 is the global table at 0x2060E8, [r4+4] the state struct at 0x20E000, so the
flag is VA 0x20E3E8. Measured at the branch it is 0x00000000 both times it is
reached, and a write hook over the entire boot records ZERO writes to that word
-- nothing on this path ever sets it, so it keeps its zeroed BSS value and the
skip is unconditional. That also makes ASD's OVERSIZE staging line a red
herring: the image is never handed to the loader, so the 0x30000 slot bound is
never consulted.

Loading it the way hardware does -- boot, ring init, ring command 1 with the
image PA and length, the path already proven for HDCP:

    image: asd_full.bin 209152 bytes class body+0x14 = 0x5244 ('DR' driver)
    clean image   ASD blocks=81  faults=0  RSA ops=3

ASD executes 81 distinct basic blocks in its own address space at 0xD00000,
zero faults, after the firmware's own loader verified it on the CCP model.
Deterministic: 81 blocks on repeated runs.

Controls, since "it ran" is only meaningful if it could have failed:

    1 bit flipped in signed body   ASD blocks=0
    1 bit flipped in signature     ASD blocks=0

Both corrupted images execute ZERO ASD blocks, so the 81 are contingent on the
real RSA-PSS check, not on the harness writing bytes into memory. The harness
exits non-zero unless the positive control and both negatives hold. One fresh
emulator per observation, because a rejected load disturbs ring/session state
and reusing an instance makes later observations look dead for the wrong reason.

Scope stated in the finding: this is ASD loaded and running, NOT ASD command
handling. ASD is driver-class, not a TA, so it is not reached through
TA_InvokeCommandEntryPoint and the 81 blocks are its load/init path. Driving its
audited opcode surface needs its RPC dispatcher, which services.py still stubs
(AsdRpcService returns 0) and which Bus can route into but is not wired to by
default. That work is now unblocked rather than done: the real ASD is resident
and running instead of absent.

Regression: smoke, ccp_selftest, verify_real_image, boot_sos_full,
boot_sos_and_tas, boot_and_mailbox all unchanged; fuzz_ta_invoke still exits 2
by design.
</content>
</entry>
<entry>
<title>Track the firmware images and keep the symbol maps alongside them</title>
<updated>2026-09-22T09:20:28Z</updated>
<author>
<name>Peter Geissler</name>
<email>peter@haxx.in</email>
</author>
<published>2026-09-22T09:20:28Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/commit/?id=771ecaa4ec30e342116bc248873f62cdf6b16500'/>
<id>urn:sha1:771ecaa4ec30e342116bc248873f62cdf6b16500</id>
<content type='text'>
Adds 301 PSP/SMU firmware images (*.bin, *.elf, ~33 MB) that were previously
gitignored. They are public material: the module images come from
linux-firmware and publicly mirrored VBIOS dumps, and nothing was decrypted --
the encrypted on-flash stages (BOOT_LOADER#1, TRUSTED_OS) remain encrypted and
unanalysed, so sOS is the earliest stage present.

Checked the whole-flash dumps before publishing: they carry board SKU strings
only (XFX D702_XTX / 113-31XFSHBS1), no serials, MACs or unique per-board
identifiers.

With the images tracked, the symbol maps in psp_out/phase3/, vdiff/, sigs/ and
handover/symbols/ are now directly reproducible against the exact bytes they
were derived from, and data/MANIFEST.sha256 still pins all 301 hashes.

Still excluded: IDA databases (228 MB, regenerable and churn-prone),
psp_out/decompiled/ (Hex-Rays output mechanically derived from AMD code), and
AMD-internal register maps, which are vendor documents rather than public
firmware.
</content>
</entry>
<entry>
<title>Initial import: AMD PSP reverse engineering, emulator and vulnerability research</title>
<updated>2026-09-22T09:14:57Z</updated>
<author>
<name>Peter Geissler</name>
<email>peter@haxx.in</email>
</author>
<published>2026-09-22T09:14:57Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/amd-psp-re.git/commit/?id=649992b26c75eb13ace40c6eec499895a69a1132'/>
<id>urn:sha1:649992b26c75eb13ace40c6eec499895a69a1132</id>
<content type='text'>
Split out of a tinygrad working tree, where all of this lived untracked.
tinygrad remains a dependency for hardware-facing scripts only.

Contents:
  psp_emu/  Unicorn-based PSP emulator that boots the real signed firmware from
            the ARM reset vector: sOS -&gt; SYS_DRV -&gt; HDCP/DTM/RAS TAs, with a CCP
            crypto device model faithful enough to run the firmware's own
            drivers (RSA-PSS/SHA-256 container verification passes 5/5 with
            5/5 negatives rejected). A simulated host drives the real
            mailbox/ring path through to TA_InvokeCommandEntryPoint.
  psp_out/  Static analysis: six PSP modules fully named (asd 1562, sys_drv
            1141, sos 527, ras 119, hdcp 109, dtm 14) plus SMU 1033, a
            cross-version diffing pipeline over 34+ builds, per-handler
            annotated analysis and ~125 findings documents.

Deliberately not tracked: AMD firmware images, the Hex-Rays decompilation
derived from them, and IDA databases. data/MANIFEST.sha256 pins the SHA-256 of
all 301 blobs results were derived from, so findings remain traceable to their
inputs without redistributing AMD code. 6 MB tracked instead of 315 MB.

Verified from the new location: test_smoke 5/5, ccp_selftest 11/11,
verify_real_image 5/5 + 5/5 negatives, fuzz_ta_invoke controls passing and
host-driven TA invoke working (0.73s per invoke).
</content>
</entry>
</feed>
