Skip to content

iommu/apple-dart: T6050's locked display DARTs and firmware's four-level subtrees - #893

Closed
funkzy wants to merge 3 commits into
aurora-silicon:aurora-wipfrom
funkzy:pr/t6050-dart-fw-tree
Closed

funkzy wants to merge 3 commits into
aurora-silicon:aurora-wipfrom
funkzy:pr/t6050-dart-fw-tree

Conversation

@funkzy

@funkzy funkzy commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Summary

On the M5 Ultra Mac Studio (J775D) with Linux on the hardware, the boot loader leaves every display DART locked on
four-level roots of its own (dart-dcpextN stream 23, dart-dispextN streams 0 and 16), with no reserved-table
handoff, and the display co-processor's firmware places its log buffer behind firmware's slots of those roots.
apple-dart's retained-root path (fw_mirror) covers the locked roots on T6041, T8140 and T8142. These commits add
T6050 to it, and teach it to resolve and reserve firmware's subtrees in a four-level root, only on T6050.

What it changes

  1. dt-bindings: iommu: apple,dart: apple,t6050-dart, t8110 fallback. The same patch is the first commit of the
    T6050 PCIe PR on t6050/pcie; whichever lands first, the other drops it.
  2. apple,t6050-dart joins fw_mirror's list: a locked DART with no reserved-table handoff keeps firmware's root
    slots, and Linux maps only into its own.
  3. fw_tree (set only for apple,t6050-dart): mapping a locked four-level root records the subtrees behind
    firmware's slots (each root and mid-level entry and a mapping of the table it points to). iova_to_phys()
    resolves a DVA there through them, reading leaf entries live; an entry that no longer matches what was recorded
    resolves nothing. The slots are reported as reserved, so the IOVA allocator never places a Linux mapping there.
    Linux still never writes into them. The three-level path's reserved-RAM check would refuse the tables (T6050's
    boot loader places them outside the memory node), so a subtree's table is mapped if it is reserved RAM or memory
    that no resource claims; an entry naming Linux's own RAM or a device's registers is refused and resolves
    nothing.
    • iova_to_phys() now asks the lookup for four-level domains too. Without a recorded subtree it finds nothing
      there, as before: the three-level snapshot never takes a four-level root.
    • The reservation walk runs only for the reserved-table handoff and fw_tree, so the other fw_mirror DARTs
      read no new registers.
    • The host test takes the five new functions, the two fields and the subtree's struct, and twelve cases: a
      recorded subtree resolves; a changed root or mid-level entry, or no recorded subtree, resolves nothing; with
      fw_tree a four-level root's firmware slot is reserved; a fw_mirror DART without fw_tree neither resolves
      nor reserves anything there; a subtree freed after a failed allocation touches only what was allocated; a
      subtree table is taken as reserved RAM or unclaimed memory, never as Linux's RAM, a claimed range or a
      misaligned address.

Why

Without 2 and 3 the DCP's shmem_setup cannot resolve the buffer its firmware placed at its own DVA, so the DCP's
log is never mapped, and nothing keeps a Linux mapping out of firmware's slots.

What else sees it

Only apple,t6050-dart sets fw_tree, and only J775D's DT has it. On T6041, T8140 and T8142 nothing changes: their
four-level roots still resolve and reserve nothing (the host test's tree-mirror-only case), and the reservation
walk, which reads each stream's DART_TCR, does not run for them. Before widening
fw_tree, check whether any of their locked streams is four-level and what its foreign slots hold, since the
reservation moves DMA placement for every client of that stream. An APIF guest's DARTs (apple,dart-apif) are a
separate driver.

Testing (J775D, M5 Ultra)

The kernel: our J775D series (integration/aurora's lineage, as on the Studio every day) with these commits in place
of the series' own forms, booted once on 2026-11-02 (the first form) and once on 2026-11-03 (these commits):

  • "shmem_setup: iova: 100014ac000 -> pa: 117d8cb0000" on dcpext0 (at 1.33 s in the second boot): the firmware-placed
    log buffer resolves, to the physical address the boot loader's segment ranges give for it, through tables the
    new check accepts.
  • The display came up on dcpext0 with Hyprland both times, no DCP crash, no panic, BUG, Oops, WARNING or SError line
    (the first boot: 682 swaps submitted and 682 completed in the first minute).
  • Host test (tools/asahi/apple-dart-fw-tests.py): 86 of 86 cases pass on this branch (aurora-wip: 74 of 74).
    Mutations of the new code are each caught by a new case: the mid-level entry check, the fw_tree gate on the
    reservation, the partial-allocation guard in the free, and the claimed-memory check. apple-dart-range-tests.py
    passes on both.
  • Builds on aurora-wip, each commit: 0 errors, 0 warnings.
  • checkpatch: the kernel code has one alignment note on a signature kept as it was and one kzalloc_obj suggestion
    (the file uses both forms). The fixture lines follow the fixture's own dense style, which checkpatch flags
    throughout that file.

Not tested

  • fw_tree on any DART but T6050's; the four-level root's teardown under load (it runs at unbind and shutdown).
  • The crash log buffer through this path: dcpext0 requests it only when it crashes.
  • A subtree entry that names claimed memory: only the host test makes one.

Dependencies

None in git. The J775D DT that uses the compatible comes with the J775D board; the DCP side is the T6050 bare-metal
DCP PR on #485.

funkzy and others added 3 commits November 2, 2026 14:35
Add apple,t6050-dart, the DART of the T6050 (M5 Ultra) PCIe ports and
display co-processors, with the t8110 fallback like the other recent
SoCs.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On T6050 the boot loader leaves every display DART locked on its own
four-level roots (dart-dcpextN stream 23, dart-dispextN streams 0 and
16), with no reserved-table handoff. That is the case fw_mirror already
covers on T6041, T8140 and T8142: firmware's root slots are kept and
Linux maps only into the slots it owns. Add apple,t6050-dart to that
list.

Only the native apple-dart driver reads this; an APIF guest's DARTs
(apple,dart-apif) are a separate driver and unchanged.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A co-processor behind a locked four-level root can place buffers at DVAs
of its own, in the root slots firmware holds. T6050's DCP does: its log
ring lies in its shared data segment and its crash log in its shared
segment, both mapped by firmware before Linux starts. apple-dart never
looked behind those slots, so iova_to_phys() returned 0 for those DVAs
and RTKit could not read either buffer.

With fw_tree set, mapping a locked root records the subtrees behind
firmware's slots (each root and mid-level entry and a mapping of the
table it points to), and iova_to_phys() resolves a DVA in those slots
through them. Leaf entries are read live, and an entry that no longer
matches what was recorded resolves nothing. The slots are also reported
as reserved regions, so the IOVA allocator never places a Linux mapping
there. Linux still never writes into firmware's slots.

The three-level path's reserved-RAM check would refuse these tables:
T6050's boot loader places them outside the memory node. A subtree's
table is mapped if it is reserved RAM or memory that no resource claims
(apple_dart_fw_tree_table()), so an entry that names Linux's own RAM or
a device's registers is refused and its DVAs resolve nothing.

iova_to_phys() now asks the lookup for four-level domains too. Without
a recorded subtree it still finds nothing there: the three-level
snapshot (apple_dart_snapshot_fw_root()) never takes a four-level root.

The host test (tools/asahi/apple-dart-fw-tests.py) takes the five new
functions, the two fields and the subtree's struct, and twelve cases: a
recorded subtree resolves; a changed root or mid-level entry, or no
recorded subtree, resolves nothing; with fw_tree the firmware slot of a
four-level root is reserved, and a fw_mirror DART without fw_tree
neither resolves nor reserves anything there; a subtree freed after a
failed allocation touches only what was allocated; and a subtree table
is taken as reserved RAM or unclaimed memory, never as Linux's RAM, a
claimed range or a misaligned address.

fw_tree is set only for apple,t6050-dart, where it is tested. On T6041,
T8140 and T8142 nothing changes: the reservation walk runs only for the
reserved-table handoff and fw_tree, so their DARTs see no new register
reads. Before widening fw_tree, check whether any of their locked
streams is four-level and what its foreign slots hold, since the
reservation moves DMA placement for every client of that stream.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@funkzy

funkzy commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

Closing: this change now lives in the funkzy/j775d branch of aurora-silicon/linux-aurora (the J775D series as built and booted, 681521644671), which dj merges directly; no PR flow for it any more.

@funkzy funkzy closed this Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant