aboutsummaryrefslogtreecommitdiffstats
path: root/psp_emu/tests/test_smoke.py
blob: 56ab20a9da747b170f1ceaea13743f90e38951ec (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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
"""Smoke tests: images load, ARM/Thumb detection works, leaf routines execute."""
import os, sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from pspemu import Component, consts


def test_load_all():
    for name in ("hdcp", "asd", "dtm", "sys_drv"):
        c = Component(name)
        assert c.entry, name
        print(f"  {name:8s} entry={c.entry:#x} regions={len(c.cmap.regions)}")


def test_mode_detection():
    """Call-site voting must classify the mixed ARM/Thumb leaf routines."""
    c = Component("hdcp")
    assert c.mode_of(consts.HDCP["ta_memcpy"]) is False, "ta_memcpy must be ARM"
    assert c.mode_of(consts.HDCP["ta_memclr"]) is True, "ta_memclr must be Thumb"
    assert c.mode_of(consts.HDCP["subcmd12_rxinfo"]) is True
    assert c.mode_of(consts.HDCP["verify_mac"]) is True
    print("  ta_memcpy=ARM  ta_memclr=Thumb  subcmd12=Thumb  verify_mac=Thumb")


def test_memcpy_leaf():
    c = Component("hdcp")
    src, dst = c.alloc(64), c.alloc(64)
    c.write(src, bytes(range(64)))
    c.run_function(consts.HDCP["ta_memcpy"], (dst, src, 32))
    assert c.read(dst, 32) == bytes(range(32)), "ta_memcpy mismatch"
    assert c.read(dst + 32, 8) == b"\x00" * 8, "ta_memcpy overran"
    print("  ta_memcpy: 32 bytes copied, no overrun")


def test_memclr_leaf():
    c = Component("hdcp")
    d = c.alloc(64)
    c.write(d, b"\xAA" * 64)
    c.run_function(consts.HDCP["ta_memclr"], (d, 16))
    assert c.read(d, 16) == b"\x00" * 16, "memclr failed"
    assert c.read(d + 16, 8) == b"\xAA" * 8, "memclr overran"
    print("  ta_memclr: 16 bytes cleared, no overrun")


def test_write_tracer():
    """The tracer must recover a copy's exact extent (core investigative tool)."""
    c = Component("hdcp")
    src, dst = c.alloc(256), c.alloc(256)
    c.write(src, bytes(i & 0xFF for i in range(256)))
    tr = c.trace_writes([(dst, dst + 256, "dst")])
    c.run_function(consts.HDCP["ta_memcpy"], (dst, src, 100))
    ext = tr.extent(dst, dst + 256)
    assert ext is not None, "no writes traced"
    lo, hi, n = ext
    assert lo == dst and hi == dst + 100, f"extent {lo-dst}..{hi-dst} != 0..100"
    print(f"  tracer recovered exact extent: +{lo-dst}..+{hi-dst} ({n} writes)")


def test_ccp_sha224_and_error_names():
    """SHA-224 (CCP SHA type 2) and the CCP command-error table.

    Type 2 came from AMD's own ccp.h rather than from observation -- no firmware
    we hold requests SHA-224 -- so it is verified against hashlib here instead
    of being trusted. Empty and multi-block inputs are both covered, since the
    padding path is where a borrowed constant would most plausibly be wrong.
    """
    import hashlib, struct
    from pspemu.ccp import SHA_TYPES, SHA_NAMED_UNIMPLEMENTED, cmd_error_name

    name, ctxlen, blocksz, fmt, compress, dlen = SHA_TYPES[2]
    assert (name, dlen) == ("SHA-224", 28), f"unexpected SHA-224 entry {name}/{dlen}"
    iv = [0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
          0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4]
    for msg in (b"", b"abc", b"the quick brown fox" * 7, bytes(range(256))):
        data = msg + b"\x80"
        data += b"\x00" * ((blocksz - (len(msg) + 1 + 8)) % blocksz)
        data += (len(msg) * 8).to_bytes(8, "big")
        state = list(iv)
        for i in range(0, len(data), blocksz):
            state = compress(state, data[i:i + blocksz])
        got = struct.pack(fmt, *state)[:dlen]
        assert got == hashlib.sha224(msg).digest(), f"SHA-224 mismatch at len {len(msg)}"
    print(f"  SHA-224 matches hashlib on 4 inputs (0..256 bytes)")

    assert cmd_error_name(0x14) == "IDMA_PROTECTION_FAULT"
    assert cmd_error_name(0x24) == "ODMA_PROTECTION_FAULT"
    assert cmd_error_name(0x00) == "NONE"
    assert "UNKNOWN" in cmd_error_name(0x99), "out-of-table code must be flagged"
    assert SHA_NAMED_UNIMPLEMENTED == {1: "SHA-1"}, "SHA-1 must stay named-but-unimplemented"
    print("  CCP error names resolve; unknown codes flagged, not invented")


if __name__ == "__main__":
    fails = 0
    for fn in (test_load_all, test_mode_detection, test_memcpy_leaf,
               test_memclr_leaf, test_write_tracer,
               test_ccp_sha224_and_error_names):
        print(fn.__name__)
        try:
            fn(); print("   PASS")
        except Exception as e:
            fails += 1; print(f"   FAIL: {e}")
    sys.exit(1 if fails else 0)