MES (Micro Engine Scheduler) — first-pass audit
Evidence class [ST] static, plus one [HW] loadability check on the card.
Target: fw/gc_11_0_0_mes_2.bin and gc_11_0_0_mes1.bin (gfx11 / Navi 31),
with gc_11_0_0_pfp.bin, gc_11_0_0_me.bin and the in-tree mec_11_0_0.bin
alongside as controls. Tool: mes_audit.py (needs capstone RISC-V;
/home/user/.venvs/cs6/bin/python).
Why it was a gap
MES is the queue scheduler the driver talks to with API packets
(mes_v11_api_def.h), i.e. it has the same shape of host-reachable command
surface as the DMCUB inbox — and this tree had never examined it. The ISA map in
psp_out/FIRMWARE-INVENTORY.md classified "MEC/MES" as RISC-V from a dword
histogram, but no MES image was even in the corpus and no code was ever read.
It is loadable on this card [HW]: the PSP accepts the MES ucode under
GFX_FW_TYPE_CP_MES (33) — RS64_MES (76) is refused with 0xffff0006, i.e. not a
supported type on this ASIC. Separately, gfx11 pulls PFP/ME/MES in through PSP
RLC autoload from the TOC rather than per-type LOAD_IP_FW, which is why the
tinygrad AM driver's load list shows only MEC.
Container (verified)
0x000 common_firmware_header + mes_firmware_header_v1_0 (header_size 0x48)
0x100 payload; its first 0x100 bytes are a nested PSP header ($PS1 at +0x10)
0x200 ucode image, size mes_ucode_size_bytes
-> the first 0x2000 bytes are zero and mes_uc_start_addr = 0xf0005000,
so the image base is 0xf0003000 and code starts at file 0x2200
+ data image at mes_ucode_data_offset_bytes -> mes_data_start_addr 0xf0100000
The nested $PS1 header is not a DMCUB quirk — it is present identically in
mes, mes1, mes_2, mec, pfp, me and imu. Any offset derived from a
payload without skipping it is 0x100 low, which is exactly the error corrected in
dmcub/AUDIT-DMCUB-02.md.
ISA and inventory
| image | ucode | RV64 decode | fn prologues | indirect sites |
|---|---|---|---|---|
gc_11_0_0_mes_2.bin |
0x1f910 | 92.1% | 257 | 18 |
gc_11_0_0_mes1.bin (KIQ) |
0x15c40 | — | 67 | 8 |
gc_11_0_0_pfp.bin |
0x169a0 | 90.4% | 15 | — |
gc_11_0_0_me.bin |
0x2adf0 | 95.1% | 7 | — |
mec_11_0_0.bin |
0x41300 | 89.5% | 46 | — |
Confirmed RV64 by real decode, not by histogram: the first instruction at
mes_uc_start_addr is a textbook prologue —
f0005000 130101f1 addi sp, sp, -0xf0
f0005004 233c910c sd s1, 0xd8(sp)
f0005008 9b042000 addiw s1, zero, 2
PFP/ME/MEC share the load address 0x3000 and are also RV64; their low prologue
counts reflect flat state-machine style code, not a decode failure (90-95%
coverage).
Command dispatch — all bounded
mes_audit.py <img> dispatch recovers every indirect jump/call with its table and
guard. Three are table dispatches of the kind the API packet handler compiles to,
and each is bounds-checked before the jr:
Superseded in part by AUDIT-MES-02-API.md: the three "tables" are one adjacent
array read at three bases, and the attempt to map table entries to named opcodes was
retracted after the entries turned out to be mid-block fragments. The bounds below
are direct reads of the code and stand; the "36 handlers" framing does not.
| site | table | entries | bound |
|---|---|---|---|
0xf0008aa4 |
0xf0100000 |
36 | li a5,4; bltu a5,s1 → index ≤ 4 |
0xf001919c |
0xf0100014 |
31 | li a4,0xa; bltu a4,a5 → index ≤ 10 |
0xf00196b0 |
0xf0100040 |
20 | index ≤ 19 |
The canonical one reads the opcode straight out of the host packet and bounds it:
f0019178 lw a5, 4(s0) ; opcode from the API packet
f001917c li a4, 0xa
f0019180 bltu a4, a5, 0xf001961c ; opcode > 10 -> default
f0019188 lui a4, 0xf0100 ; addi a4, a4, 0x14
f0019190 slli a5, a5, 2 ; add ; lw a5, 0(a5)
f001919c jr a5
Of the 18 indirect sites, 17 have a guard in the preceding window. The one that
does not (0xf0021d10) is not a command dispatch: it is a PC-relative
computed goto (auipc t0,0; add a3,a3,t0; jalr -0x68(a3)) inside an unrolled
byte-splat/memset routine, with the index derived from a length. Recorded as a
library artefact, not a finding.
What is not audited here
This is a first pass: container, ISA, inventory and dispatch bounds only. The part that matters for security is untouched:
- API packet field handling. MES API packets carry host-supplied 64-bit addresses (queue base, doorbell, rptr/wptr addresses, HQD state) and counts. Nothing here checks whether those are validated — that is the real equivalent of the DMCUB work and the obvious next step.
- No copy-length audit (MES's byte-copy routines were not identified).
- No per-opcode handler audit behind the three dispatch tables.
mes1(KIQ) only counted, not read.- PFP/ME/MEC: classified and container-parsed only.
Emulation is plausible here in a way it was not for DMUB: RV64 is supported by
both Unicorn and QEMU, so a psp_emu-style harness for MES would not need a new
CPU model.
