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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
# Review of `new/` — Routes A and B verified against the firmware
Independent verification of the other researcher's Route A and Route B, done in IDA
against `sys_drv2.elf`. Their claims are **[D]**-tagged static analysis; this records
which parts I could confirm, which I could not, and what I found instead.
## Confirmed: the mailbox model and the host-open set
`off_E2579C` is the MP0 register block + `0x10000`, so their §1.2 offset formula
(`N>=64 -> 0x10A00 + 4*(N-64)`) is directly checkable. It holds:
| mailbox | dword index | handler found |
|---|---|---|
| **112/113** | 688/689 | **`platform_range_get`** (Route A target) |
| **115** | 691 | **`tsu_mailbox_dispatch`** (Route B / §4.1 target) |
| 117 / 118 / 125 | 693/694/701 | irq scan + dispatch |
| perm word 112-127 | 430 | `init_mailbox_state`, `irq_enable_group1/2/3`, `tsu_mailbox_task` |
Their §1.3 claim that the firmware opens these to the host itself is confirmed: five
OR-only RMWs, `|= 0xF` (112,113) in `init_mailbox_state` and `|= 0x3C0` (115,116) in
**`tsu_mailbox_task`** — the flash/update task opening its own mailbox.
Their §1.5 command encoding is also confirmed: `platform_range_get` takes the command id
from `BYTE1` (bits 15:8) with bit31 as submit, exactly as documented.
## Route A — the claimed defect is REAL, but one level deeper than stated
They describe the mailbox-112 handler as having "a physical-range composition path that
ignores the return status of the mapping call". **That part does not hold** for
`platform_reg_write` (`0xE0F06C`):
* it **does** check the `SVC 0x7D` map return (`MOVS R4, R0; BEQ locret`);
* it **does** bounds-check: `v11 + v3 <= base + 1380` where `v11 = base + off + 812`,
i.e. `host_offset + host_len <= 568` inside a 1380-byte mapping. No 32-bit wrap is
reachable (both fields are 16-bit, base is a low PSP address).
But their **"paging cursor that advances by a caller-supplied length with no clamp and is
never reset"** is confirmed, in the sink `msmu_handle_param` (`0xE0F1B4`), `a1 == 1` path:
```c
v17 = *(_DWORD *)(base + 808); // cursor
phys_range_compose3(a2, v17, a3); // composes a range AT the cursor
...
*(_DWORD *)(v14 + 808) += a3; // cursor += caller length, never reset here
```
`a3` is bounded per call (`v15[203] >= a3`, and `<=568` upstream), but the **cursor is
cumulative and unbounded** — every command-2 submission walks it further, and
`phys_range_compose3` composes at wherever it has reached. That is a genuine
walk-off primitive, reachable from a mailbox the firmware grants the host by design.
## Route B — interface confirmed, contents not yet probed
`tsu_mailbox_dispatch` (`0xE1C948`) is a real dispatcher: command byte in BYTE2 of the
status word at `off_E2579C + 2764` (= mailbox 115), unknown commands return `0xFFFF000A`,
and the handled set includes `flash_ctx_set` (cmd 2/3), `tsu_parse_rom_image` (cmd 4),
`flash_info_get` (cmd 11) and `desc_slot_alloc` (cmd 18) — matching their §4.1 table.
**Never probed by us:** our earlier "C2P mailbox not host-reachable" result tested
C2PMSG_35/36, a different pair. 115/116 is untested surface.
## HARDWARE RESULT — Route A is NOT host-reachable; Route B IS
Measured directly on the card. The permission array itself is **not host-readable**
(`0x0381069C..0x038106B8` all return the byte-swapped index), so it was probed
behaviourally instead: write a non-palindromic bit31-set pattern to each mailbox and read
it back. Writes that land read back EXACT; writes that are not decoded read back
byte-swapped, the same signature as a dead SMN read.
| mailbox | behaviour | meaning |
|---|---|---|
| **112, 113** | BYTESWAP | **Route A: host writes do NOT land** |
| **115, 116** | **EXACT** | **Route B (TSU flash/update): host CAN write** |
| 118 | EXACT | display channel open |
| 122 | EXACT | open (not in their §1.3 list) |
| 125 | EXACT | platform id open |
| 108-111, 114, 117, 119-121, 123-124, 126-127 | BYTESWAP | not host-decoded |
Measured open set `{115, 116, 118, 122, 125}` vs their §1.3 prediction
`{112, 113, 115, 116, 117, 118, 125}`: **four agree** (115, 116, 118, 125), 112/113/117 do
not, and 122 is open but unlisted.
**So Route A cannot be driven from the host on this card** — its mailboxes are not
decoded for us, regardless of the `|= 0xF` the firmware performs. The unbounded-cursor
defect in `msmu_handle_param` is real but not host-reachable by this path.
**Route B's interface is confirmed open**, which is the more valuable half: mailbox 115
also reads `0x80000000` (bit31 set = completed), so the TSU task has run and is idle.
### Two test-design errors caught en route
1. First write-test used `0xA5A5A5A5`/`0x5A5A5A5A` — both **byteswap-invariant**, so they
could not distinguish a real write from the artifact. Re-run with `0x12345678`.
2. An earlier decode of the permission array printed an "open set" from **mirror values**
— pure garbage. Always check a read against its byte-swapped index before decoding it.
## Recommended order
1. **Read the permission array** (`0x0381069C..0x038106B8`) — free, no writes, and it says
which of these interfaces is actually open right now. Expect `0x0C003FCF` in the word
covering 112-127.
2. **Route B liveness only**: TSU commands `0x01` and `0x0B` (read-only). Do **not** run
`0x04` with an update image without an external programmer attached.
3. **Route A**: submit command 6 (the no-op/`v1=1` case) first to confirm the channel
answers, then command 2 once, and re-read the cursor's effect before repeating.
--------------------------------------------------------------------------------
## Validity guard (`regguard.py`)
Three times in this project a dead access path produced a confident wrong answer:
an "open mailbox" set decoded from byte-swapped mirrors; `navi31_bench.py`'s
`cmd_scan` reporting **80** open mailboxes from eight identical reads of the PSP
heartbeat counter; and a write-test "confirming" access using `0xA5A5A5A5` /
`0x5A5A5A5A`, which are **byteswap-invariant** and cannot distinguish a real
readback from a swapping artifact.
All three are mechanically detectable, so they are now a function call rather than
something to remember:
* `classify_read(read_fn, addrs)` -> `REAL / MIRROR / ALIAS / DEAD / UNSTABLE`
(requires >= 3 distinct addresses so aliasing is detectable at all)
* `is_writable(read_fn, write_fn, addr)` -> `WRITABLE / BYTESWAP / IGNORED / MIXED`,
using patterns asserted at import time to be non-palindromic
* `guard_decode(words, addrs, decoder)` -> **raises** instead of decoding an
artifact
`test_regguard.py` validates it against cases whose truth was established by other
means -- 13/13 pass:
| case | expected | got |
|---|---|---|
| perm array via BAR | ALIAS | ALIAS |
| perm array via SMN | MIRROR | MIRROR |
| Route C window via SMN | MIRROR | MIRROR |
| known-real SMN addrs | USABLE | USABLE |
| mbx115/116 | WRITABLE | WRITABLE |
| mbx112/113 | BYTESWAP | BYTESWAP |
| `guard_decode` on both bad word sets | refuse | refused |
Any future register result in this project should go through it before being
interpreted.
|