aboutsummaryrefslogtreecommitdiffstats

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.