aboutsummaryrefslogtreecommitdiffstats
path: root/mes (follow)
Commit message (Collapse)AuthorAge
* MEC/PM4 round 9: stop the KIQ port on cost/benefit, name the cheaper routeuser2026-09-28
| | | | | | | | | | | | | | | | | | | | | | | Ported the last two prerequisites from gfx_v11_0.c: RS64 compute enable turns on all four pipes together with MEC_HALT=0 (CP_MEC_RS64_CNTL 0x04000000 -> 0x3c000000), and kiq_init_register programs CP_MEC_DOORBELL_RANGE_LOWER/UPPER, outside which the CP silently ignores doorbells. Neither closed the gap. With the right slot, CP_HQD_ACTIVE=1, the scheduler pointed at the queue, all pipes active, the MEC unhalted and the doorbell range programmed, the queue still does not retire a NOP. The remaining candidate is the MEC firmware's own per-pipe initialisation, since amdgpu loads MEC microcode per pipe and the RS64 MEC likely only services pipes initialised at load time -- a substantially larger port. Cost decides it: each failed attempt leaves an undequeueable HQD and GRBM_STATUS 0xa800302c that only a cold ATX cycle clears, and three have now been spent on one open question. Stopping the thread. The cheaper route is recorded for whoever picks it up: MAP_QUEUES sits at 0x3241c in the validated PM4 table, inside the ~12 KB dispatcher sub_31360, and whether any path from that label consults a privilege bit is a bounded static reverse with no hardware cost and no power cycles.
* MEC/PM4 round 8: port amdgpu's KIQ slot selection; queue activates, still stallsuser2026-09-28
| | | | | | | | | | | | | | | | | | | | | | | | Continuing round 7's blocker by reading amdgpu's KIQ bring-up and porting it. Three concrete fixes. The KIQ slot was wrong, not merely unallocated: amdgpu_gfx_kiq_acquire scans the queue bitmap downward requiring queue == 0, and gfx11 has num_mec = 1, so ring->me = mec + 1 = 1. tinygrad hardcodes me=2/pipe=1, a VF layout, which is exactly why round 7 saw CP_HQD_ACTIVE = 0. The real slot is me=1 pipe=3 queue=0, reachable through setup_ring's ordinary path at idx=12 so the MQD is still built by driver code -- CP_HQD_ACTIVE now reads 1. The HIQ pointer encoding is RLC_CP_SCHEDULERS = (me<<5)|(pipe<<3)|queue|0x80 per gfx_v11_0_kiq_setting. And because gfx11 is RS64, per-pipe enables live in CP_MEC_RS64_CNTL rather than CP_MEC_CNTL; it read 0x04000000, only pipe0 active. Still stalled: with the right slot, an active HQD, the scheduler pointed at it and pipe3 enabled, CP_HQD_PQ_RPTR stays 0 while the doorbell and CP_HQD_PQ_WPTR_LO both reach the queue. A pipe-0 queue on the identical code path retires the same NOP, so the submission mechanism is sound and the gap is pipe-3 specific -- most likely the per-pipe HPD/EOP programming from amdgpu's compute resume. CP_MEC_RS64_CNTL restored to its original value. MAP_QUEUES remains untested, but the slot, scheduler encoding and pipe-enable register are now known and verified, leaving one named piece.
* MEC/PM4 round 7: HIQ plumbing blocked, blocker characterised preciselyuser2026-09-28
| | | | | | | | | | | | | | | | | | | | | | Attempted to stand up a KIQ/HIQ ring to give round 6 its missing control. Cleared two obstacles: tinygrad's mqd_paddr has 2+is_vf entries while setup_ring(kiq_xcc=0) indexes 2 (fixed by appending an allocation so the driver's own MQD code runs unchanged), and the KIQ's doorbell index 0 is outside the apertures AM enables (re-pointed at MEC_RING0+4, an index the compute probe had already rung). Did not clear the third: CP_HQD_ACTIVE reads back 0 after setup_ring, so the CP rejects the queue and no NOP retires. Root cause is that tinygrad's KIQ path is VF-only and hardcodes me=2/pipe=1/queue=0, while amdgpu picks them dynamically via amdgpu_gfx_kiq_acquire() and additionally needs the HPD EOP region, MEC pipe enablement and the KIQ doorbell aperture -- an amdgpu KIQ port rather than a patch. The MAP_QUEUES chain therefore stays open and untested, with no claim in either direction. It would have been easy to combine round 6's stall with this round's dead KIQ into "MAP_QUEUES is gated everywhere"; neither result supports that, because a queue that never activates cannot refuse anything.
* MEC/PM4 round 6: MAP_QUEUES chain is untested, not closeduser2026-09-28
| | | | | | | | | | | | | | | | | | | | | | Round 5 closed the direct route (unprivileged queues cannot write registers), so the follow-up is the indirect one: use MAP_QUEUES from an unprivileged queue to map a queue whose MQD sets priv_state=1, then write registers through it. That would be a user->kernel chain requiring no memory-safety bug at all. The test is inconclusive and is recorded that way. MAP_QUEUES stalls the queue identically at priv_state=0 and priv_state=1, and identically again after seeding the target MQD with the one the driver had just built. Because the privileged queue stalls too, the stall is not a privilege effect -- MAP_QUEUES is an HIQ/KIQ-pipe packet and is not serviced on an ordinary compute ring. Worth stating because the failure mode is deceptive: a stall looks exactly like the round-5 privilege fault, and without the priv_state=1 comparison this would have been written up as "the gate covers MAP_QUEUES too". The probe's verdict text is corrected to say a stall alone proves nothing. The chain stays open. Testing it needs HIQ submission with a user-owned ring, which tinygrad's AM does not expose (setup_ring(kiq_xcc=0) indexes past its allocated MQD slots) -- a driver-plumbing blocker, not a firmware one.
* MEC/PM4 round 5 [HW]: the user->kernel register boundary holdsuser2026-09-28
| | | | | | | | | | | | | | | | | | | | | | | | | | | PM4 is submitted by unprivileged user queues and WRITE_DATA(dst_sel=register) names a memory-mapped register, so an ungated path would be the user->kernel primitive none of the DMCUB findings can be. Tested as a 2x2 on silicon, because either factor alone is uninterpretable: dst=memory dst=register priv_state=0 wrote refused, queue then faults priv_state=1 wrote wrote, queue alive The memory column proves the packet encoding, ring, doorbell and submission path are correct -- without it, "the register did not change" is equally consistent with a malformed packet, which is exactly what made the first SDMA RB_PRIV=0 run inconclusive. The priv_state=1 row rules out the other explanation, that WRITE_DATA->register is simply unsupported on MEC. Privilege was changed by live-flipping CP_HQD_PQ_CONTROL (0x8509 -> 0xc0008509) on an already-running queue, so that bit is the only difference between the two runs -- the same isolation technique used for SDMA's RB_PRIV. Verdict: the boundary is real and enforced on gfx11. Across both user-submittable engines the pattern is identical -- memory confined by the VM, registers gated by a per-queue privilege bit, and the gate holds. Also fixes the probe's verdict text, which printed the unprivileged conclusion for a privileged run.
* MEC/PM4 round 4: correct the dispatch table, refute the length-bound guessuser2026-09-28
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Acting on the synthesis, which put PM4 at the top because it is the only user-submittable surface left unaudited. Round 1's table base was 0x100 low -- the same nested-$PS1 error that shifted every DMCUB address in this tree. At the corrected base 0x415d8 all five of Round 1's anchor opcodes verify, and 5 of 7 populated slots carry published PACKET3_* names (MAP_QUEUES, UNMAP_QUEUES, QUERY_STATUS, RUN_LIST, MAP_PROCESS_VM). At the old base, zero did, which is what makes the naming a validation rather than a decoration. That table covers queue management only. Two scans found no second dispatch structure -- no further (handler, flags) pairs and no plain pointer table that opcode-indexes onto the enum -- so the remaining packets are dispatched by compare chains, matching Round 2's reading. Round 1's suspected per-packet length bound is refuted: MAP_QUEUES carries 2241 where UNMAP/QUERY/RUN_LIST carry 2 and MAP_PROCESS_VM carries 1. Those are not dword counts for those packets, so the low half is a flags field and the parser's length validation lives in code. New tooling (mes/mec_audit.py: table/bounds/taint) taints from the packet data port 0x6F8(tp) with dataflow that respects both calls and basic blocks. The block rule was added after the pass produced a striking false lead: a raw packet dword shifted left 36 into MMIO-aperture bits and used as a store address, which would have been a host-controlled register write. An unconditional j sits just before that block, so the base comes from elsewhere; block-awareness removed it and 16 other sinks. Of the 75 survivors, 27 assemble a 64-bit GPU VA from two packet dwords and are VM-confined as proven for SDMA. Still no finding.
* Audit MES, PFP and ME; settle the IMU ISA as Xtensauser2026-09-28
MES is the queue scheduler and, like the DMCUB inbox, is driven by host API packets -- it had never been read here. Confirm RV64 by real decode (92%), recover the container (code at 0xf0005000, data at 0xf0100000), and audit the packet surface: the 256-byte stride matches MES_API_MAX, both dispatch sites are bounds-checked, and 31 of 33 memcpy sites use constant lengths, so DMCUB's FINDING-1 pattern is absent. Retract, in place, an earlier identification of the two dispatchers against the kernel enums. The bounds were exact ABI fits, which felt like confirmation, but the table entries disassemble as mid-block fragments and epilogues; the prologue-scan function boundaries behind that model are unreliable on this image. MESAPI_MISC__WRITE_REG is left explicitly unresolved because the screening dataflow is not call-clobber aware. PFP and ME are also RV64 (container-parsed, load addr 0x3000). IMU is Tensilica Xtensa, correcting this tree's "undetermined": the old call was made over the whole container, which is dominated by the zero-padded DRAM half.