Repository navigation
Conversation
…are-metal GPU tables Add the M5 Pro/Max configuration (T6050) and its OF match, selected by the ID registers. A G17 configuration now names its firmware protocol, and the 26A428/25G83 graph tables become optional, since the M5 graph carries its own. Probe refuses an M5 board that has no bare-metal GPU page tables, and the M5 boot entry point stays closed until one does. [ rebased onto integration/aurora: rewritten from t6050-gpu 6613f5ab7b7e..15e1b3e4fa44 (3 commits); SPTM/APIF removed; adapted to integration's g17::Config, ProbeConfig and Firmware::Identified; original 6613f5ab7b7e, 1a021061f226, 15e1b3e4fa44 ] Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…d context pins Add the UAT pieces the M5 queues use: sparse-tile binds and their zeroed and per-VM sink tiles, render pool mappings that move with a pool identity, the metrics alias, execution-context pins and the M5 dynamic kernel ranges. On M5, a healthy last-job release skips the VM drain. [ rebased onto integration/aurora: rewritten from t6050-gpu 6613f5ab7b7e..692d42fb35d7 (5 commits); SPTM/APIF removed; adapted to integration's mmu.rs (7.3 GPUVM, UatQuirks, ExecutionRoot, SG leases); comments and names reworded; M5 paths restored where the first rewrite lost them: one prepare_bind_object, with the sparse-tile bind; the G15/G16 adopted UAT (Uat::new_adopted_inner) sets m5: false, for integration's own config (defconfig + asahi.config), which builds G16 beside G17 and failed with E0063 without it; original 6613f5ab7b7e, e25ba8a6fb2b, 1a021061f226, f49e91b3f364, 692d42fb35d7 ] Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Add the M5 firmware's InitData layout and its graph builder, and let InitData address the M5 bundle, private state, recovery views, effort word and control ring at their M5 locations. Firmware events stay off on M5 until its queues take submission, and the M5 effort word is published as zero. [ rebased onto integration/aurora: rewritten from t6050-gpu 6613f5ab7b7e..77dc6eb34210 (5 commits); SPTM/APIF removed; adapted to integration's InitData (status_b and views fields) and fw::Variant; comments and names reworded; M5 paths restored where the first rewrite lost them: the M5 metrics binding; original 6613f5ab7b7e, e25ba8a6fb2b, 1a021061f226, f49e91b3f364, 77dc6eb34210 ] Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…-list layout Add the M5 pool descriptor and free-list encodings, the render storage demand they are sized from, and M5 render and compute pools beside the existing ones. Render pool identities become leases that can move between logical contexts, so idle storage is reused instead of rebuilt. [ rebased onto integration/aurora: rewritten from t6050-gpu 6613f5ab7b7e..77dc6eb34210 (4 commits); SPTM/APIF removed; adapted to integration's freelist (capacity refusals, lower-window placement, per-context render pools); comments and names reworded; original 6613f5ab7b7e, e25ba8a6fb2b, cda233bc91f7, 77dc6eb34210 ] Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…mand port Add the M5 queue records, configurations and notifications, and the host command port that installs a queue pair and submits work. On M5, tiling, fragment and compute share the compute ring of each priority, whose outer credits are claimed before either host port is written, all under the device mutex. [ rebased onto integration/aurora: rewritten from t6050-gpu 6613f5ab7b7e..f49e91b3f364 (4 commits); SPTM/APIF removed; adapted to integration's InitData work publication and resource wait domains; comments and names reworded; M5 paths restored where the first rewrite lost them: the UAT's range for the compute kick rings and timestamps, the M5 control records, reports and statistics mask, the M5 firmware preparation, the fault window read, and the scheduler page from the retention pool; original 6613f5ab7b7e, e25ba8a6fb2b, 1a021061f226, f49e91b3f364 ] Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Add the render and compute program resources, the shader-write, stage-prefix and whole-attachment render flags, matrix and no-query compute, and their feature bits. The command parser takes the resource commands and the stage-prefix tail; validation applies the M5 command set only where the firmware variant has it. [ rebased onto integration/aurora: rewritten from t6050-gpu 6613f5ab7b7e..a507978ff411 (7 commits); SPTM/APIF removed; adapted to integration's UAPI numbering, ray-query stages and fw::Variant; the M5 UAPI flags and features in the UAPI header with agx3c Mesa's numbers, the stage-prefix fields appended to drm_asahi_cmd_render; interim forms until the render submission commit; comments and names reworded; M5 paths restored where the first rewrite lost them: VM_BIND accepts DRM_ASAHI_BIND_SPARSE_TILE; original 6613f5ab7b7e, e25ba8a6fb2b, bd4a3d7a0c97, 2f58522abec7, 2c5c77b6aaba, e393a52c7151, a507978ff411 ] Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Encode the M5 compute program and descriptor, install M5 compute queues cold and refresh their priority on reuse, and publish compute as M5 work on the shared ring. Timestamps come from the queue's own record, waits on other queues stay in firmware, and the classic compute paths are never taken on M5. A closed context's hung M5 compute is killed through its queue's exit, and the kill settles once the full-queue retirement witness covers the killed commands. [ rebased onto integration/aurora: rewritten from t6050-gpu 6613f5ab7b7e..e393a52c7151 (7 commits); SPTM/APIF removed; adapted to integration's compute queue (cached graph, descriptor ring), completions and runtime; interim forms until the render submission commit; comments and names reworded; M5 paths restored where the first rewrite lost them: the firmware's own compute queue record, the shared compute aperture and global state left to G17P, every compute doorbell in the submission history, the M5 compute pool format, the kill of a closed context's hung compute settled by the full-queue witness, the full CPU refill floor on M5 with the feed's guard against unexecuted intervals and only exact compute execution records feeding it, and the item ring's count and cursor checks before its room is computed; M5 compute capacity threaded through integration's admission (rebind, readiness, growth, rearm); original 6613f5ab7b7e, e25ba8a6fb2b, f49e91b3f364, 7c95830ea3a1, 77dc6eb34210, ee68abb10520, e393a52c7151 ] Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Add the M5 tiling and fragment command encodings, their register programs and kick assembly, the M5 block geometry and buffer layout, and the per-generation render state views. Nothing submits them yet. [ rebased onto integration/aurora: rewritten from t6050-gpu 6613f5ab7b7e..77dc6eb34210 (6 commits); SPTM/APIF removed; adapted to integration's fw::render arguments and register programs; interim forms until the render submission commit; comments and names reworded; original 6613f5ab7b7e, 3d2524b7e48a, e25ba8a6fb2b, 5a71513221de, bd4a3d7a0c97, 77dc6eb34210 ] Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Build, park and reuse M5 render pairs and submit renders through them on the device-mutex path, renewing the parameter-buffer token when its binding changes. Attachment conflicts across pairs are ordered in firmware, and unrelated whole-attachment renders are not ordered. [ rebased onto integration/aurora: rewritten from t6050-gpu 6613f5ab7b7e..c3505169be1b (11 commits); SPTM/APIF removed; adapted to integration's render queue, buffer manager and device-mutex render path; the M5 features reported in GET_PARAMS except no query access; comments and names reworded; the no-query-access feature defined but not reported, as in the tested reference; M5 paths restored where the first rewrite lost them: a context's render channel moved to a new pair once its publications retire, and the replay of compute a render consumes, held until the owner's renders park; original 6613f5ab7b7e, e25ba8a6fb2b, 1d74fda93777, cda233bc91f7, 3cfa33f3237f, 77dc6eb34210, b0fdab9e3b55, 2f58522abec7, 2c5c77b6aaba, a507978ff411, c3505169be1b ] Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…ged renders On M5, Mesa that already sends staged renders under DRM_ASAHI_FEATURE_RENDER_STAGE_PREFIXES sends them in a 368-byte form. This kernel accepts a staged render only as the full 480-byte struct drm_asahi_cmd_render, so the first such render failed with EINVAL and the application lost its device. That Mesa now leaves staged renders off and keeps working; Mesa that sends the full command finds them under a new bit. The kernel reports the full-command form as DRM_ASAHI_FEATURE_RENDER_STAGE_PREFIXES_V2 (bit 31) and no longer reports bit 25, which stays defined for the shorter form. What the kernel accepts under DRM_ASAHI_RENDER_STAGE_PREFIXES is unchanged. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Add a host GPU power-budget governor for M5 (asahi.m5_thermal_governor, off by default). It holds the SoC hotspot near a target by lowering the budget the firmware reads, shares the heat with a CPU frequency ceiling, and ramps the fans with the hotspot under load. Publishers note GPU activity with or without the device mutex. [ rebased onto integration/aurora: rewritten from t6050-gpu 77dc6eb34210..60a4dd529842 (6 commits); SPTM/APIF removed; adapted to integration's Shared work items and the port's CLPC demand; comments and names reworded; original 77dc6eb34210, 0a75aae77faf, 284638c2a784, cc2c9850d389, f5de511cb487, 60a4dd529842 ] Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
drm_sched_fini cancels each job left on the pending list through cancel_job and frees it right after, while the job's completion callback is still registered on the scheduler fence; cancel_job must signal that fence. When the queue could not settle the packet in time (a busy or failed runtime, or a settle racing another queue's timeout handling), cancel returned with the fence unsignalled. A later settle then ran the freed job's callback: drm_sched_job_done on a job with no s_fence, or a NULL callback once the memory was reused, both seen as oopses in drm_sched_job_timedout under 12-process synchronization CTS. Fail such a completion while its queue still owns it (the VM stays pinned until the queue retires it) and wait briefly for a signaller that already claimed it. Signed-off-by: Diljit Singh <hey@djdev.me> Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
The M5 Max shares the T6050 SoC and the gpu,t6050 binding with the M5
Pro, but its GPU has four clusters of ten cores instead of two, all eight
AMCCs enabled instead of four, and its own voltage tables.
Move the board-specific HW data behind M5Board { J714, J716c } and pass
the board from the Config to the HW data and initdata builders:
- Performance states carry a core and an SRAM voltage per table. HW perf
lane n carries table n and lanes past the board's table count carry
table 0, which reproduces the J714 data exactly. J716c has four tables
(0 == 3, 1 == 2); they, the auxiliary-domain table and the
relative-frequency curve come from its ADT.
- Cluster count 2 / 4, core count 20 / 40 and core type 0x18 / 0x19
(the core type follows the ID variant).
- The minimum parameter-buffer size: 2 MiB / 3 MiB.
- The register map. J716c maps all eight AMCC windows in slot 3 (MCC
reg[14..21]), and four instances, one per cluster, in slots
20 and 24, where J714 has two at the same stride. Slot 44 is a fixed
pair on both.
Gpu::new() checks the board's cluster and core counts against the GPU's.
Only J714 is probed; the following commits complete the J716c data and
the last one probes it.
The J714 data is unchanged by construction; not retested on J714.
Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
[ J775D: backport onto integration/aurora: adapted to integration's g17::Config (the board check in Gpu::new, the board field of the T6040 and T6041 configurations) and InitData::build_m5; the SPTM boot path's board argument removed; comments reworded for publication (code unchanged by it), and message lines that named register offsets; original fbbb15c50bee ]
Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
The three AON PTD records (RT0g, RT1g, RT0r) carry device-specific table indices. Encode them per board; the M5 Max rows are 0x58, 0xa3 and 0xb8. Developed with Claude Code. Signed-off-by: Erik Bourget <erik@ebourget.net> [ J775D: backport onto integration/aurora; original c280d3b1777e ] Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Both boards bind gpu,t6050 and probe used the J714 configuration unconditionally, so a Max was rejected at the identity check (ID_VERSION variant 4 instead of 3). Add the J716c Config (variant 4, GpuVariant::C, M5Board::J716c) with its crash-log buffers at the ADT gfx-data-shared-ro-base and gfx1-data-shared-ro-base, and select the configuration from the ID registers after powering AFR, with the same Resources the GPU then boots with. probe_g17() now takes those Resources from its caller; on the other G17 path they are created before the DMA mask is set instead of after. On J714 the only change is that AFR is powered and the ID read once more during probe; both are idempotent. Developed with Claude Code. Signed-off-by: Erik Bourget <erik@ebourget.net> [ J775D: backport onto integration/aurora: adapted to integration's probe_m5, probe_g17 and Config; the SPTM profile and the SPTM probe with its AFR power-up removed, so J716c, like J714, is refused at probe until it has bare-metal GPU tables; original 11a9c30bacae ] Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
The M5 Max uses 8 MiB render blocks and an 8 GiB private-memory address inventory, twice the M5 Pro's 4 MiB and 4 GiB. With the M5 Pro geometry, compute work on the Max ends in firmware recoveries within seconds. Select the render pool from the board at probe, before any pool is built, and size render and compute pool geometry and budgets with it. The M5 Pro values are unchanged. Developed with Claude Code. Signed-off-by: Erik Bourget <erik@ebourget.net> [ J775D: backport onto integration/aurora: adapted to integration's compute_m5 and PoolGeometry; comments reworded for publication (code unchanged by it), and one word of the message; original 8d4eed54d540 ] Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
The last segment-control word of an M5 render is the part's minimum parameter-buffer size in 32 KiB pages. It was passed the segment count (64), which is right only on the M5 Pro. Derive it from the physical cluster count instead, for every render including single-group ones: 64 pages on the M5 Pro (unchanged) and 96 on the M5 Max. A compile-time check ties the page count to each board's HW data. Developed with Claude Code. Signed-off-by: Erik Bourget <erik@ebourget.net> [ J775D: backport onto integration/aurora: adapted to integration's queue/render/m5_render_submission.rs; the m5_render_resources.rs hunk dropped, as integration has no such file; comments reworded for publication (code unchanged by it), and message lines that named register offsets; original d290b4a2ceab ] Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…k on After a compute timeout, the driver asks the firmware to kill the closed context's remaining commands and, once the kill is acknowledged and every command settled, returns the queue to service with fresh backing. On M5 the next owner's first command on that queue then never runs: it retires with a zero-duration record and nothing written, and that client sees a lost device. Commands after it run normally. A killed QID stays killed in the firmware until a queue configuration with its QoS update set binds it to a scheduler page other than the one the firmware has for it. The new owner's first publication carries such a configuration, but its command reaches the still-killed queue first. On M5, reset a killed queue's install state, so the next owner's first publication sends the configuration alone and waits until the firmware consumed it before any command is published. The install record now goes at the ring's current producer: a reinstalled queue restarts at the firmware's consumer, not at zero, where the append was refused. Other GPUs retire a killed queue instead: it keeps its QID and installed graph but takes no owner again, and its backing and exited binding are still freed. Developed with Claude Code. Signed-off-by: Erik Bourget <erik@ebourget.net> [ J775D: backport onto integration/aurora: M5 half only: the retirement of killed queues on other GPUs and the reinstall and retirement logs removed; adapted to integration's recycle_killed; original 457ef7786341 ] Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On M5 the recovery path reads no firmware fault reason, so a blamed compute command whose completion record shows zero duration is taken to have never executed: its blame is withdrawn and the command is replayed. But firmware writes exactly that record when it cancels a command that was running, which is what happens when that command faulted. The replay then faults again on the next kick. Read the GPU MMU fault status at every M5 recovery. When it holds a valid fault, report the recovery as a page fault, as the other G17 profile does from the firmware's reason word, so attribution keeps the firmware's blame and the command is not withdrawn. Also log the decoded fault: access, reason, address, page-table level, requestor and VM slot. Developed with Claude Code. Signed-off-by: Erik Bourget <erik@ebourget.net> [ J775D: backport onto integration/aurora: adapted to integration's Backend::fault_sources, where InitData::is_m5 already exists; the classify hunk dropped, as integration does not withdraw a blamed compute command; the recovery log prefix changed to the series' "asahi:"; original 6b85752ad673 ] Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
No change for any GPU that runs today: the T6040, T6041 and J713 builds keep their digests. It lets a later commit identify the M5 Ultra's (J775D) GPU firmware, whose loaded image holds two roles plus ranges the boot loader rewrites on every boot. fwid admits a GPU firmware build by the SHA-256 of its loaded TEXT with the 8 entropy bytes of its two entropy records zeroed. That assumes one firmware role per TEXT region and nothing else the boot loader changes. Neither holds for every SoC: the boot loader can load two GPU roles' TEXT back to back into one region, each with its own pair of entropy records, and fill other ranges of it on every boot (two RTKit sections it patches and two load fields of each image header). A build now lists its entropy records per role (at most two roles) and the (offset, length) ranges the boot loader fills. Both are zeroed in a private copy before hashing, so a loaded image and the firmware file it came from hash alike. A build with no boot-filled ranges is hashed as before, in place, with only its entropy zeroed: every existing build keeps its digest. A record counts only if it is whole (its tag, length and 8 entropy bytes all inside TEXT), in both paths and when an unknown image's records are located, so a tag in the last 8 bytes of an unknown image is refused like any other unknown image instead of indexing past the end. measure() copies the loaded TEXT twice, before anything is written to the GPU, and keeps the copy only if both agree; Measured::find() looks a build's images up in that one measurement, and Measured::identity() states an unknown image in the form a pinned build would take (sizes, each role's records, the number of boot-filled ranges zeroed, the digest). identify() is built on them. Testing: the fwid unit tests cover two roles, boot-filled ranges, malformed records and ranges (each leaving the image unchanged) and a record without its entropy bytes, in a pinned build's lookup and in an unknown image's report. They pass on the host: fwid.rs built by rustc --test against a stand-in for the kernel crate (KUnit itself was not run). Without the whole-record check the new test panics on the slice. The existing builds (T6040, T6041 and J713) name their one pair of records and no boot-filled ranges, and keep their digests. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…oard No change in what is written for J714 or J716c. The per-board values move into a table so the M5 Ultra (J775D), which runs the same firmware build with other values, can be added as a third board. M5Board picks each per-board value of the M5 graph by a match on the board: performance and auxiliary-domain states, core count and revision, AON records, register windows, render pool and minimum parameter buffer. The policy controller configuration and the leakage calibration it was made for stay outside it. A third T6050 board runs the same firmware build with other values of each, more voltage tables than a performance state holds and a second auxiliary lane. Collect the values in fw::m5::initdata::Board, a table per board (J714 and J716C, with M5Board's values), and name a configuration's board in Config::m5 in place of Config::m5_board. The builder, the HW data and AON encoders, the render pool selection, the board check and the maximum frequency read the table, and the table carries the controller configuration and its calibration. A performance state holds up to eight voltage tables and an auxiliary state two lanes; a board names how many tables it uses, and lanes past them carry table 0, as before. J716C keeps J714's controller configuration and calibration, so, as before, the configuration applies only to a part with J714's calibration. No change in what is written for J714 or J716c. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On the M5 Ultra Mac Studio (J775D) the GPU driver found no configuration
for the chip ("No M5 configuration for GPU variant") and the GPU stayed
off. It now boots both GPU firmware roles on bare metal and brings up
the M5 firmware graph; jobs are accepted from the later submission
commit on.
J775D (T6050, G17D, two dies) runs the M5 firmware's protocol with the
UAT roots both roles publish (FirmwareProfile::M5PublishedRoots).
- The J775D board: both dies' register windows with their explicit
instances (32), one voltage table per cluster in the main
performance table, the two-lane auxiliary table, the six Ultra AON
records, core type 0x1a revision 2, 8 clusters of 10 cores and the
minimum parameter buffer for eight clusters. No policy controller
configuration. Values from the J775D ADT.
- hw/t6050.rs CONFIG_ULTRA, one of the configurations the ID registers
select at probe (variant 6, two dies), with the UAT policy this board has run with:
tables in no-map reservations, every table change written back,
page-by-page TLB invalidation, the handoff's no-slot value without the lock,
and the handoff byte after the flush records 1. Crash logs in
fw-data. Its core layout is eight clusters of ten cores, with the
eight-cluster operand-state layout (OperandState::EIGHT_BY_TEN).
- M5 GPUs are matched on the board's cluster and core-slot counts,
the values the M5 layouts are sized from, not on enabled cores: the
J775D has 64 of its 80 cores enabled. The T6050 core layouts drop
their own copy of the counts.
- The M5 firmware build (V26A428_M5): the 26A428 variant with the M5
command set and no host power hold around render-pair registration
(Variant::registration_power_hold). The bare-metal M5 firmware powers
the cores for work by its own policy: under a hold, J775D's
core-power witness reads unpowered until the hold is released, so
taking one only times out.
- Gpu::boot_m5: link the kernel lower root's tables the firmware
reaches (entries 1, 7 and the canonical arena's 8) before either role
starts, boot both roles, adopt the published roots, build the M5 graph
in the shared kernel window and bind its metrics object, attach the host
command port to the graph's views, then the M5 preparation, InitData to the primary and the secondary, POWER_ON_DONE,
and wait for the opening records. A failed bootstrap keeps the
session. The graph builder, the M5 preparation and the metrics
binding are the M5 tree's.
Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On the M5 Ultra Mac Studio (J775D) the driver would write its firmware tables to whatever GPU firmware the boot loader left loaded, unchecked. It now drives only the known build, and any other image fails the GPU probe before a single GPU write, with the rest of boot unaffected. On bare metal nothing vouches for the firmware the boot loader left in fw-text: the driver would write the 26A428 firmware's InitData graph to whatever was loaded. J775D now names the build it admits (Firmware::Identified), so Gpu::new identifies the loaded TEXT among its images before anything is written to the GPU, as for the other identified G17 SoCs. fw-text holds the primary's TEXT then the secondary's, each with its own pair of entropy records, and the boot loader refills eight ranges per boot (two sections and two header load fields per role). The build zeroes all of them, so the live image and the firmware file hash alike (c35354407602...ce9cc8251). A mismatch logs the measured identity and the probe fails before any GPU write; the rest of boot continues. A digest pinned per boot would refuse the genuine firmware on the next boot. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
A Rust DRM driver without core_object_args() read and wrote past the end of objects the DRM core created for it, such as a dma-buf imported from another device; on the M5 Ultra Mac Studio (J775D) this bug in an earlier GPU driver tree panicked the kernel and kept the desktop from starting. Such a driver (tyr, in this tree) now refuses the import with EINVAL; asahi has core_object_args() and is not affected. Handle lookups and every Object<T> accessor assume that each GEM object of the device is a whole Object<T>. The DRM core creates imported and dumb objects through gem_create_object, which the shmem abstraction installs only for drivers with core_object_args(). For any other driver the core allocates a bare drm_gem_shmem_object instead, and the Rust side then reads and writes past its end. Commit 2dc22a1 ("rust: drm: gem: shmem: let drivers allocate the objects the core creates") lets drivers with core_object_args() allocate them; this completes it for the others. Install gem_prime_import_sg_table and dumb_create only with gem_create_object. A driver without core_object_args() then refuses an import from another device with -EINVAL instead of corrupting memory. In this tree that is tyr; asahi has core_object_args() and keeps both. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On the M5 Ultra Mac Studio (J775D) the GPU never left its base performance state (338 MHz) under any load, so every GPU workload ran at the lowest clock. The firmware's power controllers now get the Ultra's own power model and ramp the GPU up. The M5 builder installs the policy controllers' configuration only for a board whose leakage calibration words match (J714). J775D has none, so every controller word stayed zero, and the firmware never left its base performance state (P1, 338 MHz) under any load. Give J775D its own arm, as J714 has one: encode_m5_policy_controller_defaults_j775d starts from the J714 defaults and writes the Ultra's power model over them: - the modeled maximum power, 345262 mW, in the policy's maximum and ceiling words, and from there in Region C's host power budget; - the relative power curves of the main and auxiliary domains; - the leakage inputs of eight clusters and two dies; - the controllers' gains and utilization thresholds; - the temperature-sensor channel masks of both dies. The model uses synthetic leakage inputs, valid for every J775D, so the J775D arm needs no calibration words. J714 is unchanged. The J775D arm follows the shape of Erik Bourget's J716c controller defaults (f7f650b887cd, not carried here): the J714 defaults first, then the board's own values over them. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…ltra On the M5 Ultra Mac Studio (J775D) the idle GPU sat at 338 MHz and light passes, ramping from there, settled at the second to fourth performance states. The idle and ramp floor is now the Ultra's own P3. The policy's performance-controller floor came from the J714 defaults: P1, in the policy's three floor words and as the base of HwData's relative-frequency tables. J775D's floor is P3. With P1 the idle GPU sat at 338 MHz, and a busy pass ramped from there in steps of three states, so light passes lost the first steps and settled at P2 to P4. J775D's controller arm now writes its floor (state x 100) into the three floor words, and its relative-frequency tables are based at P3: 0 at and below the floor, 100 at P13. relative_frequency() takes the floor; J714 keeps P1. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On the M5 Ultra Mac Studio (J775D) renders were refused once a context slot had been reused 255 times: a render-churn test failed at its 258th run. Renders now keep working however often a slot is reused; other GPUs are unchanged. An execution context slot's reuse generation is 8 bits and wrapped from 255 to 0, which M5 reads as "no generation": the 256th reuse of one context slot refused its next render's context token (render churn failed at run 258). On M5 it now wraps from 255 to 1. Other GPUs keep the wrapping generation: the change is gated on the M5 UAT. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On the M5 Ultra Mac Studio (J775D) every Vulkan application failed at vkCreateDevice: Mesa's sparse-tile bind for its descriptor table was refused with ENOTSUPP on the driver's own page tables. The bind now succeeds and devices are created. DRM_ASAHI_FEATURE_SPARSE_TILES is reported for every M5 firmware profile, but only SPTM-provided tables could map a sparse tile; on a directly owned table every BIND_SPARSE_TILE returned ENOTSUPP, so Mesa could not bind its descriptor table reservation and vkCreateDevice failed. A directly owned table has no per-frame mapping bound. Map the range by repeating the tile's first page, which is zeroed: the read-only fill reads zeros and the writable fill writes to this VM's own sink page, the same semantics the SPTM tables provide. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On the M5 Ultra (J775D) the idle GPU went to standby after 1500 us, the smaller M5 part's delay, instead of the Ultra's own 3500 us. Region C's idle standby timer, how long the idle GPU waits before it goes to standby, came from the shared M5 Region C image: 1500 us, the value of the smaller M5 part. J775D's is 3500 us. Board::idle_standby_us names the boards whose timer differs, and the builder writes it into Region C (set_m5_region_idle_standby). The rest of the idle configuration is unchanged. J714 keeps 1500 us. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On the M5 Ultra (J775D) the firmware's fast boost stopped raising the GPU clock once the GPU idled more than 10 percent of a boost window, where the Ultra's tolerance is 20 percent. The firmware's fast-active boost keeps raising the performance state while the GPU idles for no more than a tolerated share of each boost window. The policy's tolerance, a byte in the boost block, kept the controller default of 10 percent. J775D's core type takes 20 percent: its controller arm writes it. J714 keeps 10. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…U for every client On M5, a client could bind its own memory over the kernel's mappings in the compact window at 64 GiB of its VM, where the kernel places the parameter buffers' page lists, render targets, scratch and the metrics records the firmware reads. The bind replaced the kernel's page-table entries, so the GPU then used client memory in place of that state, and the later unmap of either mapping cleared the other's. An M5 client VM now reserves the compact window against VM_BIND from creation, as it already reserves the queue arenas. The bind check refuses only ranges registered as driver mappings, and the compact window was never registered in a client VM: its mappings were placed there at render or compute setup, after the VM existed. The kernel range a client names at VM_CREATE need not cover the window, so the client's own range check did not catch it either. Mesa's M5 layout keeps that window inside its own driver reservation, so no Mesa bind is refused. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On M5, GPU work that never finished could hold its queue, queue ID, render pairs, pool backing and memory for as long as other clients kept the GPU busy, or for as long as its owner kept the queue open. Every such path is now bounded: - A job queued behind an older node of its own context was renewed by the watchdog at every poll, with no count, while any client kept the firmware's device-wide progress word moving. When the older node was failed work held until the firmware drains it, the renewal had no end. It now stops once the VM has failed work, mirroring the rule that a failed blocker of another VM excuses nothing, and it is bounded by the existing thirty-second shared budget counted on the job's deferrals. The older node's own job runs under the same allowances and was published earlier, so a healthy older node drains or fails within that window. - A compute command the watchdog timed out was never killed while its owner kept the queue open: a context kill was published only for a released queue or a closed context. A timed-out queue's context is now killed once the quarantine delay has passed; its VM has already failed, so the owner loses nothing it could still use. Spared work, which a recovery returns to service, is not killed, and a running ray traversal is still never killed, as before. - The kill of a closed context with a timed-out command was postponed while any other client had work in flight and the GPU retired something every five seconds, on the assumption that its commands retire like any others. A timed-out command now counts as failed compute for the kill, as a firmware-reported fault already does, and any closed context's kill waits at most the recovery bound after it closed. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…ther process takes it Physical compute queues and render pairs move between processes, but the client-visible state behind them did not move cleanly with them. A previous owner could keep reading and writing the state the next owner's GPU work used, and the next owner could read what the previous owner's commands left. Each handover now gives the next owner state the previous owner can no longer write, and clears what it left: - Compute: when a queue moved to another VM, the previous VM kept read-write mappings of the queue's kick ring, preemption buffer, usage page and operand state until the new owner's first command retired, and the kick ring kept the previous owner's entries. The previous owner's mappings are now removed before the new owner's are created, and the kick ring is cleared with the other state pages. A move needs an idle queue whose pool release the firmware consumed, so the firmware no longer uses those views; the previous context's scheduler state, which the queue may still run until the new owner's first command, stays retained as before. If the new owner releases the queue before publishing anything, the queue is left detached, as for an exited owner, instead of restoring the removed views. - Render: a parked pair goes to whichever queue takes its render slot next, and storage of a ring that never wrote an entry returns to a stock any VM's next ring could take. The rings kept their contents, and stock storage handed to another VM stayed mapped read-write in the VM that created it. A parked pair's rings are now cleared when it goes to another VM's context, and stock storage is reused only by the VM that created it. A parked pair moved to another VM still leaves the previous VM's mapping of its rings in place, since that mapping lasts as long as the VM. The read-only client kick rings later in this series keep that VM from writing the new owner's entries. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
… another process reuses After a GPU context was released, the firmware's QoS table kept naming its scheduler page for that QoS ID until the ID's next owner published work. On M5 scheduler pages are pooled, so the page could meanwhile be zeroed and given to another process's context, and a firmware read of that row would see the other process's scheduler state. A released ID's scheduler and share rows are now cleared before a new context is created, and at each event turn. A released ID is recorded when its lease drops and forgotten again if a new context takes it, since that owner writes its own row at its first publication. Rows of IDs with work still in flight are left alone. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
When a user bind and a kernel mapping claimed the same GPU addresses in one VM, the page-table code logged the collision and overwrote the existing entries, so the GPU silently used the newer mapping's pages in place of the older one's, such as client memory in place of state the firmware reads, and the later unmap of either cleared the survivor's pages. On the A18 Pro a client could reach this by binding where the kernel later places that client's queue aliases. A map operation now fails with EBUSY and leaves the existing mapping untouched, and the A18 Pro arenas are reserved against binds from VM creation. - The structural fix: no mapping path maps over a live entry on purpose (user binds run after the VM manager removed every overlapping mapping, kernel mappings take fresh ranges, a residency restore clears its own range first), so a live entry is always another mapping's. Each map operation checks its whole range before its first store, because its failure path clears the whole range it asked for; the store loop refuses a live entry too, as a backstop. - The A18 Pro instance: the kick and descriptor rings, render generation state and client-private compute and render backing are placed in two arenas when a queue is installed, after the VM exists, and were registered as driver mappings only then. A bind made earlier was not refused, nor one checked at parse time and committed after an alias was installed. With both arenas reserved at creation, the reserved set no longer changes. Mesa already keeps this range out of its allocator on the A18 Pro, so no Mesa bind is refused. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On M5, a compute dispatch with shader-context scratch passes the hardware an auxiliary allocation word, the per-core page count of its scratch storage, unchanged from userspace. The pool behind it was sized for a fixed eight pages per core whatever the word said, so a dispatch asking for more (Mesa's ray-query dispatches ask for 14) let the hardware use storage past what its pool holds. Any value was accepted, also without scratch. The pool now covers the requested count, and a nonzero word without scratch is refused with EINVAL. Render already sizes its pool from the same word: its spill budget adds the published auxiliary page count, which includes the eight-page tail, for every core. Compute now does the same, with the eight-page tail as the floor, so no dispatch gets a smaller pool than before. Mesa's ray-query compute pools grow by six pages per core. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…PU pool sizes On M5, the render and compute pools behind a client's queues are sized from that client's own program records, up to the board's whole pool inventory (16 GiB on the M5 Ultra), and allocated and zeroed at once as kernel memory. Kernel memory counts toward the device total but is never refused and is attributed to no process, so one unprivileged client could pin many gigabytes per queue: other clients' allocations then failed with ENOMEM, and the OOM killer could not see the culprit. An M5 pool backing is now admitted against its client's and the device's memory limits before it is allocated, as a user buffer of the same size would be, and a refusal fails the submission with ENOMEM. A client VM now carries the memory accounting of the file that created it, so every place that builds an M5 pool for a context's VM charges that client: compute pools at install, re-arm and growth, and render pools when their backing is built. The charge is released with the backing. Kernel VMs and the other GPUs' pools are unchanged. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
On GPUs with independent queues, QUEUE_CREATE had no per-client limit, so one unprivileged client could open queues until the device's execution contexts were gone. Every other process, the compositor included, then failed to create a queue with EBUSY: a GPU denial of service for the whole seat. A client now holds at most half of the device's independent queues, and its next QUEUE_CREATE fails with EBUSY. The limit comes from the same queue limit GET_PARAMS reports, so it follows each GPU's budget (29 of 58 where all execution contexts are available), and leaves the other half to the rest of the seat. The file's queue table hands out the lowest free ID, so bounding the IDs bounds the queues the file holds. GPUs without independent queues are unchanged. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…er GPU VM Each sparse tile was a 64-page physically contiguous allocation, 1 MiB with 16 KiB pages, of which the page tables read only the first UAT page. Every VM that did a sparse-tile bind allocated two, so any client could pin 2 MiB of high-order memory per VM it created, exhausting or fragmenting contiguous memory for the rest of the system. A tile is now one UAT page. The allocation's order is derived from the UAT page size, so the tile stays naturally aligned to a UAT page whatever the kernel page size. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…r overwrite kernel memory On GPUs whose page tables the firmware builds in reserved memory (the T6040, T6041 and T6050), a page-table walk followed any table descriptor the firmware or bootloader had written, and accepted a child table anywhere in System RAM. A corrupted or unexpected descriptor below the shared kernel root would then have the walk store PTEs into an arbitrary kernel page. A table page must now be in the reserved regions or be one the driver allocated; anything else fails the walk with EIO. Freeing already refuses tables the driver does not own, so only the write path was exposed. The firmware's own tables on these boards are reserved no-map regions, and every table the driver adds is recorded when it is allocated, so no legitimate walk is refused. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…pletion and scheduler pages On M5, the render completion stamps, each context's scheduler and work-state pages, and each compute queue's support page were mapped for both the firmware and the GPU. The kernel trusts what they hold. They sit in the kernel's upper half, which every execution context includes. Whether user shaders could reach that half was never established. This removes the GPU permission either way: on M5 these pages are now firmware-only. On the Studio, every GPU path ran clean with them firmware-only, so the GPU itself doesn't need them. Scope, by page set: - Render completion stamps (initdata/m5.rs): allocated only on M5. - Scheduler pages (context.rs): M5 takes them from the retained pool, Neo allocates its own. Firmware-only on M5 only; Neo is unchanged. - Work-state pages (context.rs): shared code, allocated on Neo too. Firmware-only on M5 only; Neo is unchanged. - Compute support page (queue/compute.rs): Neo allocates a larger one for its own use. Firmware-only on M5 only; Neo is unchanged. set_fw_cached_prefix now derives the cached prefix from the mapping's own protection, changing only the cache attribute, instead of rewriting it to the firmware-and-GPU protection. Its readback already refused any change other than the cache attribute. So a firmware-only pooled object used to fail queue creation with EFAULT, and every source that succeeded is one for which the derived protection is the same as before. That covers every pooled family on Neo and M5 today, all firmware-and-GPU shared, so the change is identity there. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…e kernel validated them Every client VM had a read-write view of its queues' kick rings, the entries the kernel builds from validated state for each command: queue and descriptor addresses, dependencies, QoS slot, event masks and the register programs' addresses. A client's GPU work could therefore rewrite an entry after the kernel had built and checked it, before the firmware consumed it. A render pair's rings also stay mapped in every VM that ran on them. Client views of kick rings are now read-only. The host writes kick entries through the kernel's own mapping and the firmware reads them through its own; neither uses the client's view, and nothing in the driver reads an entry back. The client's view is there for the GPU's reads, as the descriptor rings' client views, already read-only, are. The kernel's lower and firmware mappings keep their permissions. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…emory On T6050 with display firmware 27, when the display co-processor's firmware named a buffer in a request that was none of the host's pending command buffers, the host resolved the address through the whole IOMMU domain and copied up to 1 MiB to or from whatever it translated to, ignoring the mapping's permissions. A reply could thus be written into memory the co-processor may only read, such as another process's framebuffer or an imported buffer, and a request could read any memory the co-processor can reach. An address that translated to a device's registers was mapped cacheable and copied through, which can take the system down. Only the firmware's own retained mappings are now followed, a reply needs a mapping that grants writes, and an address in Linux's RAM or in any claimed memory resource is refused. The DART driver gains a resolver for this that refuses any address Linux mapped itself and returns the protection the firmware's leaves grant, for the four-level subtrees and root slots it retains. DRM_APPLE now follows the DART driver's build choice, since it calls into it. Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
… under memory pressure Two kinds of G17 work signal job fences: the event work, which completes jobs, and each scheduler's timeout work, which fails timed-out ones. Memory reclaim may wait on those fences, but neither ran on a reclaim-safe workqueue. The event work ran on an M5 driver queue allocated without WQ_MEM_RECLAIM, or on system_highpri_wq; the timeout work ran on system_percpu_wq, drm_sched's default. When a pool has no idle worker and creating one needs memory, that work cannot run, and neither the fences nor reclaim make progress. Send all of it to one reclaim-safe queue, aurora_g17_events. On M5 it is unbound and high priority as before, so the event work still follows the submitting CPU; elsewhere it is per-CPU and high priority, the counterpart of system_highpri_wq, so the event work runs where it did. Every scheduler takes the queue as its timeout queue and keeps a reference until it has been finalized. Their run and free work already used reclaim-safe queues. For that, the Rust OwnedQueue gains reclaim-safe unbound and per-CPU constructors (restrict() refuses a per-CPU queue), and the Rust DRM scheduler binding's new_with_queues() takes an optional submission queue and an optional shared timeout queue, in place of new_with_submit_wq(). Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
…he report and fault-ownership changes
With KUnit enabled, asahi no longer built: two of its tests still
called interfaces that later commits had changed.
- f04338837f3f ("drm/asahi: g17: M5: install M5 queues and submit
through the host command port") gave Reports::record() the family's
statistics tag mask and 64 tags per role. The report tests now pass
the G17 mask and index the second role at TAGS, and a new case checks
that tag 32 counts under M5's mask only and that tags past 63 are
refused under any mask.
- e7ddf2b8e869 ("drm/asahi: g17: Fix a process keeping access to a GPU
queue after another process takes it") added Previous::moved. The
same-VM replacement test sets it false.
Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
- HW+0x10a8: the active MCC mask for all eight AMCCs (0xff), matching the slot 3 windows, instead of the inherited 0xf. - HW+0x100c (16) and HW+0x1048 (40, all cores). J714 keeps the inherited values, and J775D its own. Developed with Claude Code. Signed-off-by: Erik Bourget <erik@ebourget.net>
The M5 Max's controller defaults were J714's, applied only to a part with J714's calibration words, which no M5 Max has. Give it its own: the J714 defaults, including the relative power curves, with the leakage of its four clusters, its base and AFR values, a maximum power of 196098 mW, and the found masks of its AON PTD records, applied for the J716c calibration words. As on J714, a unit whose calibration words differ keeps the default budget. Developed with Claude Code. Signed-off-by: Erik Bourget <erik@ebourget.net>
The M5 Max (J716c) already has its board data, but its configuration still names no firmware and probe refuses it as having no bare-metal GPU tables. Give it the M5 Ultra's bare-metal profile: both roles publish their kernel roots, the secondary's at 0x40000 in `pagetables`, each asks for its crash log inside `fw-data`, and the UAT policy is the Ultra's. Admit the G17C firmware build of both roles: primary TEXT 0x50000, then the secondary's 0x54000, with the boot-filled ranges zeroed as on the Ultra. Only the M5 Pro is refused at probe now. The firmware identity test covers the Max's image too. Tested on a J716c: the GPU boots both roles and runs the desktop through zink and Vulkan. Developed with Claude Code. Signed-off-by: Erik Bourget <erik@ebourget.net>
Bind the T6050 GPU through "apple,agx-g17s", the compatible its M5 Max
("apple,agx-g17c") and M5 Ultra ("apple,agx-g17d") nodes fall back to,
in place of "apple,agx-t6050", which no binding documents. The GPU's ID
registers still choose the board's configuration.
Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
d078ca1 to
62cd1cc
Compare
a07459c to
5b0f106
Compare
b1615e3 to
0dd0833
Compare
DjDeveloperr
left a comment
There was a problem hiding this comment.
Approved: the M5 Pro runs with these commits, and they're already on this PR's base.
All four commits are on funkzy/t6050-gpu-on-integration (0dd0833) as 21a5bde, 6d8d401, 15fa55b and e1bf8cf. Two are patch-identical. The other two differ only in a doc comment and in #1012's later ProbeConfig::M5 → G17M5 rename. That's why GitHub shows a conflict: nothing is left here to merge.
On the M5 Pro (J714sAP, G17S), that base plus our J714 enablement passes:
- compute: 65,536 words, 0 wrong;
- the 256x256 triangle, crc32 d5e4bea9, identical to the M2 Pro reference render;
- dEQP s0 smoke: 109 Pass, 2 NotSupported, 0 Fail.
The MCC-mask/topology and controller-default commits only set J716C's board fields, so J714 is unaffected. The G17S match is what J714 binds through.
|
Leaving this open: as in my review, all four commits are already on #1012's branch ( 🤖 Generated with Claude Code |
What this is
#1012 runs the M5 Ultra on bare metal and refuses the M5 Max at probe. This series makes the M5 Max (J716c, G17C, one T6050 die) work on #1012's driver:
+0x10a8names all eight memory cache controllers (0xff), and+0x100c/+0x1048carry the M5 Max's topology (16, and its 40 cores). These come fromt6050-gpu, which the M5 Max ran on, and were not carried into drm/asahi, drm/apple: M5 GPU and firmware-27 display on integration/aurora #1012.t6050-gpu. drm/asahi, drm/apple: M5 GPU and firmware-27 display on integration/aurora #1012 only had J714's, which no M5 Max matches.fw-data, its UAT policy) and the identity of the G17C firmware both roles load. Only the M5 Pro is refused at probe now.apple,agx-g17s, which the M5 Max'sapple,agx-g17cand the M5 Ultra'sapple,agx-g17dfall back to, in place of the undocumentedapple,agx-t6050.The M5 Pro and the M5 Ultra are unchanged.
Dependencies
apple,agx-g17s(aura-internal).ASAHI_M5_BRINGUP=1.Testing (J716c, bare metal)
defconfig+asahi.config, with KUnit.Known issues
Multi-queue synchronization on the M5 Max is still weaker on this driver than on the earlier native-runtime one. In the CTS sample above, two GPU MMU faults (each failing one client's render; the GPU stays up) and 17 job timeouts (each costing one client its device), mostly in
synchronization*.op.multi_queue,cross_instanceand timeline-semaphore tests. 65 tests fail or crash here that pass or flake on the native-runtime driver. Follow-up work.🤖 Generated with Claude Code