aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/test_rollback_51.py
blob: 5fb0ab4b3acabd871e463088c16b6ad0d47ef7eb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""Attempt to boot the card on PSP firmware 0.49.0.51 (a downgrade from the
stock 0.49.0.53 container) and report whether the bootloader accepts it.

This is the first operation in this project that could plausibly have a
persistent effect (if an anti-rollback fuse is enforced), so it is deliberate,
isolated, and reports state before/after.

Requires the PSP to be in a fresh state: run psp_out/psp_recover.py FIRST,
otherwise is_sos_alive() is True and init_hw() loads nothing.

usage: python3 psp_out/test_rollback_51.py [--stock]
   --stock : load the normal .53 container instead (recovery / control run)
"""
import ctypes, sys, hashlib
import tinygrad.runtime.support.am.amdev as amdev
from tinygrad.runtime.autogen.am import am
from tinygrad.helpers import mv_address

STOCK = '--stock' in sys.argv
BLOB_51 = '/tmp/lfw/36934f24c008.bin'      # linux-firmware commit with 0.49.0.51

def container_versions(data: bytes):
    blob = memoryview(bytearray(data))
    chdr = am.struct_common_firmware_header.from_address(mv_address(blob))
    H = getattr(am, "struct_psp_firmware_header_v%d_%d" % (chdr.header_version_major, chdr.header_version_minor))
    hdr = H.from_address(mv_address(blob))
    out = {}
    for i in range(hdr.psp_fw_bin_count):
        d = am.struct_psp_fw_bin_desc.from_address(ctypes.addressof(hdr.psp_fw_bin) + i * ctypes.sizeof(am.struct_psp_fw_bin_desc))
        v = d.fw_version
        out[d.fw_type] = "%d.%d.%d.%d" % (v >> 24 & 0xff, v >> 16 & 0xff, v >> 8 & 0xff, v & 0xff)
    return out

if not STOCK:
    data51 = open(BLOB_51, 'rb').read()
    vs = container_versions(data51)
    print("substituting container: SYS_DRV=%s SOS=%s sha=%s" % (
        vs[am.PSP_FW_TYPE_PSP_SYS_DRV], vs[am.PSP_FW_TYPE_PSP_SOS],
        hashlib.sha256(data51).hexdigest()[:12]))
    _orig = amdev.fetch_fw
    def patched(path, name, sha256):
        if name == 'psp_13_0_0_sos.bin':
            print("  -> fetch_fw('%s') INTERCEPTED, returning 0.49.0.51" % name)
            return data51
        return _orig(path, name, sha256)
    amdev.fetch_fw = patched
else:
    print("STOCK run: using the normal pinned container (.53)")

print("initialising device ...")
ok, err = False, None
try:
    from tinygrad import Device
    d = Device["AMD"]
    adev = d.iface.dev_impl
    psp = adev.psp
    ok = psp.is_sos_alive()
    print("RESULT: sOS alive = %s" % ok)
    loaded = container_versions(bytes(adev.fw._raw_sos)) if hasattr(adev.fw, '_raw_sos') else None
    # functional check: a trivial ring command round-trip
    if ok:
        SZ = ctypes.sizeof(am.struct_psp_gfx_cmd_resp)
        adev.vram.view(psp.cmd_paddr, SZ)[:] = b"\x00" * SZ
        c = am.struct_psp_gfx_cmd_resp(cmd_id=0x0f)   # GET_FW_ATTESTATION, read-only
        try:
            psp._ring_submit(c); st = 0
        except RuntimeError:
            st = int.from_bytes(bytes(adev.vram.view(psp.cmd_paddr, SZ)[:])[864:868], "little")
        except TimeoutError:
            st = -1
        print("RESULT: ring cmd 0x0f status = %s" % ("TIMEOUT" if st < 0 else hex(st)))
except Exception as e:
    err = e
    print("RESULT: init FAILED: %r" % (e,))

print("DONE ok=%s err=%s" % (ok, type(err).__name__ if err else None))