# The twelve TA types this card accepts that we had never audited `LOAD_TA` accepts 16 TA components on this Navi 31 (`AUDIT-WINDOWS-DRIVER-CORPUS.md`). Four were already in scope (ASD, HDCP, DTM, RAS). These are the other twelve. All were triaged with `fw/ta_triage.py`, which reproduces the layout established on HDCP in idatui and is **validated against a known TA first** — on `hdcp_23.0.0.82` it returns entry `0x4042a0`, session table `0x407000`, guard `0x404334`, exactly the values `GUARD-INIT-CHAIN.md` recorded from IDA. *Gotcha worth recording: the entry trampoline is **ARM**, while the rest of the image is Thumb. Decoding it as Thumb yields fluent-looking garbage (`vhadd.s8 d0, d0, d4` …), not an obvious failure.* ## Structural triage (newest accepted build of each) | TA | version | size | invoke @ | command surface | |---|---|---|---|---| | **GCP** | 22.0.4.50 | 0x13000 | 0x404f20 | real, large | | **MERIT** | 24.0.4.42 | 0x15000 | 0x401ce8 | real, jump table | | **OPM** | 25.0.4.48 | 0x19000 | 0x402ae4 | real; ids incl. 0xa9/0xb1/0xb5/0xe1/0xe5 | | **POWERPLAY** | 34.0.0.20 | 0xa000 | 0x40271c | real, jump table | | **XGMI** | 32.0.0.20 | 0x6000 | 0x402a1c | real | | **FP** | 19.0.0.8 | 0x7000 | 0x4025b4 | real, jump table | | **AUC** | 16.0.0.9 | 0x6000 | 0x401198 | real, two jump tables | | **WSPE** | 30.0.4.27 | 0x4000 | 0x40240c | real | | **RAP** | 7.0.2.19 | 0x3000 | 0x4010e4 | real, two jump tables | | **OC** | 50.0.0.1 | 0x3000 | 0x4013b0 | real | | **CSU** | 42.0.0.6 | 0x2000 | 0x4010b0 | real (small) | | **TMZ** | 29.0.0.4 | 0x2000 | 0x4010c0 | **STUB — see below** | Every one is **class `TA`**, not `DR`. That matters: ASD is `DR` (driver class) and the PSP refuses to `INVOKE_CMD` it, which is the mechanism that made several ASD defects unreachable (`FINDING-ASD-IS-DRIVER-CLASS-NOT-INVOKABLE.md`). These twelve carry the class that *is* invokable, so unlike ASD they are candidate host-facing command surfaces. Every one also declares a stack guard at header `+0x20`. ## TMZ is an empty stub `AMD TMZ Application` (Trusted Memory Zone) is the most alarming name in the set and the least interesting image. Opened in idatui: **6 functions, all 2-4 bytes**, and every one of the five TEE entry points returns 0: ``` Create 0x4010ba -> return 0 OpenSession 0x4010c4 -> return 0 Destroy 0x4010be -> (empty) Invoke 0x4010c0 -> return 0 CloseSession 0x4010b8 -> (empty) ``` Both shipped builds (29.0.0.3 and 29.0.0.4) are stubs. It is a placeholder that authenticates and loads but does nothing — no attack surface, and worth saying plainly so nobody chases the name. ## RAP — first pass `AMD RAP Application` is the **Register Access Policy** validation TA; it matches amdgpu's `amdgpu_rap.c` (`TA_CMD_RAP__VALIDATE_L0 = 1`). 13 functions. `rap_invoke` (`0x4010e4`) is a thin wrapper — `memset(params+6, 24)`, call the dispatcher, then write back `params[2] = cmd | 0x80000000` and `params[3] = ret`. The dispatcher `rap_dispatch` (`0x401134`) runs `rap_precheck` first (short-circuits with result 2), then: * `cmd == 8` — special-cased: selects one of four `{range, policy}` pairs from `off_401334` according to flag bytes, then calls `rap_validate_range`; on success and a further flag, repeats with selector 4. * otherwise — `switch (cmd)` over the `off_4016F4` table, cases 1..6. ## Where the surface actually is Sizes grow steadily across the driver train — OPM `0x19000 -> 0x21000`, MERIT `0x15000 -> 0x1c000`, GCP `0x13000 -> 0x16000` — so these are actively developed, not legacy. OPM, MERIT and GCP are the three big ones and are the obvious next targets. **Not yet established:** whether the host can actually `INVOKE_CMD` these on this part. The class magic says they are the invokable kind and `LOAD_TA` accepts them, but that is an inference from the image plus the load result, not a measurement. The ASD story is the cautionary precedent — a TA that loads fine and still refuses every invoke. That test is a small hardware experiment and should be done before any of this is called attack surface. Artifacts: `fw/ta_triage.py`, ELFs in `psp_out/versions_win/elf/`. ================================================================================ # INVOKE probe [HW]: all twelve are host-invokable command surfaces The open question was whether the host can actually `INVOKE_CMD` these, since class magic plus a successful `LOAD_TA` is only an inference and ASD is the precedent for a TA that loads cleanly and refuses every invoke. ## Calibrating what the status means (this is what made the first attempt wrong) The first run used the *first* TA in `psp_13_0_0_ta.bin` as its positive control, got `0x4`, and stopped. That TA is **ASD** — driver class, the one that refuses everything. Sweeping command ids across all four bundle TAs gives the vocabulary: | bundle TA | cmd 0 | 1 | 2 | 0x0e | 0x0f | |---|---|---|---|---|---| | #1 ASD (`0x2f000`) | 4 | 4 | 4 | 4 | 4 | | #2 HDCP | ffff0000 | **0** | **0** | **0** | **0** | | #3 DTM | ffff0000 | ffff0000 | **0** | ffff0000 | ffff0000 | | #4 RAS | **0** | **0** | **0** | ffff000a | ffff000a | So `0x4` = the PSP will not dispatch to this TA at all; `0xffff0000` = dispatched, TA rejected the id; `0x0` = dispatched and accepted. **A single command id cannot decide invokability** — HDCP itself returns an error for id 0. The probe therefore tries several ids and calls a TA invokable if *any* id returns something other than `0x4`. ## Result — 12/12 invokable ``` POS bundle#1 (ASD) refused (0x4 on every id) <- negative control, reproduces the known behaviour POS bundle#8 INVOKABLE 0:0xffff0000 1:0x0 2:0x0 AUC INVOKABLE 0:0xffff0000 1:0x0 2:0x0 CSU INVOKABLE 0:0x0 1:0x0 2:0x0 FP INVOKABLE 0:0xffff0000 1:0x0 2:0x0 GCP INVOKABLE 0:0xffff0000 1:0x0 2:0x0 MERIT INVOKABLE 0:0x0 1:0x0 2:0x0 OC INVOKABLE 0:0x0 1:0x0 2:0x0 OPM INVOKABLE 0:0x0 1:0x0 2:0x0 RAP INVOKABLE 0:0xffff000a 1:0x0 2:0x0 TMZ INVOKABLE 0:0x0 1:0x0 2:0x0 WSPE INVOKABLE 0:0x0 1:0x0 2:0x0 XGMI INVOKABLE 0:0x0 1:0x0 2:0x0 POWERPLAY INVOKABLE 0:0x0 1:0x0 2:0x0 (see load quirk) ``` **TMZ answering `0x0` to every id is a clean static-to-hardware cross-check**: its decompiled invoke entry is `return 0`, and that is exactly what the silicon reports. ## POWERPLAY needs a prior TA load in the same boot POWERPLAY first reported `LOAD failed 0xffff0000`, contradicting the earlier sweep which accepted it. Isolating it: ``` clean boot, POWERPLAY as the FIRST load -> 0xffff0000 (even with the sweep's exact buffers) after any prior successful TA load -> 0x0, sid=1 after that prior TA is unloaded again -> 0x0, sid=0 ``` So it is not the container, the command buffer size, or load ordering within a sweep: the PSP refuses POWERPLAY as the very first `LOAD_TA` after reset and accepts it once the TA subsystem has serviced one load. The earlier sweep only ever saw it succeed because a positive control was always loaded first. Recorded as a quirk of this part, and as the reason a "first load after reset" harness can produce a false rejection. ## Standing Twelve TA types that this project never audited are **loadable and invokable on stock firmware, from the ordinary host path, with genuine signed AMD images** — against the four previously in scope. Eleven of them contain real dispatchers (TMZ is a stub). That is the attack surface statement the previous section deliberately withheld; it is now measured rather than inferred. What is still *not* claimed: that any specific command does anything interesting. The probe used ids 0-2 only and does not touch parameter buffers beyond a zeroed page. OPM, MERIT and GCP remain the obvious deep-dive targets. Artifact: `hw/invoke_newtas_probe.py`.