From 1a6b5000dc50160dddf46c227885cb2fbfa0928c Mon Sep 17 00:00:00 2001 From: Marcelo Alcantara Date: Mon, 26 Oct 2026 10:56:53 +1000 Subject: [PATCH 1/3] mesa-m3: admit the 15-inch Air and select system Mesa automatically The system-Mesa handoff admitted only the J613 and J516S, and only after a file opt-out. Without one, a T8122 Air with a working native system Mesa still got LIBGL_ALWAYS_SOFTWARE=1 whenever /opt/mesa-m3 was not used. Admit apple,j615 with apple,t8122 wherever the J613 is admitted: the hook's board case and mesa-m3-system-check's board parser. A root that names both Airs, or one twice, is still refused. Run the same bounded admission after the /opt/mesa-m3 gate on J613, J615 and J516S Hyprland sessions without requiring the opt-out, for every gate reason except not-supported and user-setup. When it passes, the session keeps system Mesa even where the gate would have used /opt/mesa-m3, because the system Mesa supersedes the legacy build. The state is inactive, reason=system-mesa, fallback=none, and the detail names the board and the gate's own decision. Any refusal leaves the gate's decision and fallback as before; mesa_m3=off and nomodeset still refuse. An automatic admission writes the same attempt as an /opt/mesa-m3 login, so an unconfirmed compositor sends the next login of the boot to software instead of looping. The file opt-out path still writes none. Without an opt-out, an explicit t8122-profile selection or a 25G83 GPU handoff node stays on the native profile's own gate. Bump mesa-m3 to 26.1.4.m3.2-9 and refresh the hook, confirm script and helper checksums. --- tools/aurora-sep/recipes/mesa-m3/PKGBUILD | 10 +- .../recipes/mesa-m3/mesa-m3-session-confirm | 5 +- .../recipes/mesa-m3/mesa-m3-session-env | 139 ++++++++----- .../recipes/mesa-m3/mesa-m3-system-check.c | 20 +- .../recipes/mesa-m3/mesa-m3.install | 2 + .../recipes/mesa-m3/tests/SYSTEM-HANDOFF.md | 30 ++- .../recipes/mesa-m3/tests/test-session-env.sh | 190 +++++++++++++++++- .../recipes/mesa-m3/tests/test-system-board.c | 15 +- .../mesa-m3/tests/test-system-provider.py | 164 +++++++++++++++ 9 files changed, 510 insertions(+), 65 deletions(-) diff --git a/tools/aurora-sep/recipes/mesa-m3/PKGBUILD b/tools/aurora-sep/recipes/mesa-m3/PKGBUILD index 2c80497680e0af..0a92f43ea8a255 100644 --- a/tools/aurora-sep/recipes/mesa-m3/PKGBUILD +++ b/tools/aurora-sep/recipes/mesa-m3/PKGBUILD @@ -5,12 +5,14 @@ # the exact j613-25g83-hal200 token, firmware26.6.2 handoff and runtime HAL200/USC3. # The J615 shares that profile experimentally: the session hook admits it only # with /chosen asahi,j615-25g83-experimental="1" (share/mesa-m3/native25-boards). +# In Hyprland sessions on J613, J615 and J516S the hook prefers a system Mesa in +# /usr that mesa-m3-system-check admits; /opt/mesa-m3 then stays inactive. # The package builds both sources sequentially and checks compiler definitions, # generated kernels, queue sandbox behavior, TLS loading and native MRT clears. # No profile selection substitutes another Mac's firmware or calibration. pkgname=mesa-m3 pkgver=26.1.4.m3.2 -pkgrel=8 +pkgrel=9 _commit=7ff89a4da387bd561b78efd5f09c98ca8ae18538 pkgdesc="Mesa in /opt/mesa-m3 for the M3 Pro (G15S) and M3 (G15G) GPUs; used by uwsm sessions on T6030, and on T8122 when opted in" arch=('aarch64') @@ -85,7 +87,7 @@ sha256sums=('7ed24969985dce91f089d34dbe1c55cbcb8c95765e3e5bc0f268d27fbead2222' '10abda78df16e650a77443f50cc46a15e418c42d4d9680c6a61616b232c9bdef' '6c98fab6f9d70449bd7f5258c34eac773836908e060ce5bef86dc5b0ce595e61' '826016665cd0424a614b67add921c0c79c3c698a4d104ea5712518449b75f27c' - '9cbc38f4bee27ab72d4a288f2741ab1eeb70112ca473a5c98a9695cdfcbe56f7' + 'bbf0bea6cb9310fd0e3f890020c14783f490761451355951e2d965800da8c1e5' '6144af8b2e0f04ea615e8c4149e69c45ac49e630c46b2a58f0d1095d977798b2' '530948fc1a124de21c030a497e2ef5351742ba4ad4653484fe30fd1697ad7141' '644cf4a5ab57b7badec75843e2899b68d41490c4851d6885227180d6090c25ea' @@ -94,10 +96,10 @@ sha256sums=('7ed24969985dce91f089d34dbe1c55cbcb8c95765e3e5bc0f268d27fbead2222' '04fae9bf32a09a574b6ed9ab9d7afb97340df252b9269ed789303e8561a61a21' '96c3101c15c73d26cd3086bc8ac4e8eb35d42300d495ca0ffd2bd03960ef3332' '9c7c3050d3ec15fce80a6e59d099567b396f1844a43695aa4377e02ac696f566' - '2a15cfdbc160b78e5a3a8e99d5898d40a54db28592f1a4ce04a600990a731f87' + '17a3c74a34681cb202767de73cfbe62c3bcbb93a16093cd058eaa955cd8776ea' '7eaae7de828e15208fccc84db2b910f38bb1288176419f7af7f4675b5c542ce4' 'c888e9dce99e8379aa21e9c5895cbe1b7e879a9319bca144193eddc66f9d4051' - 'e80947130044c2dc3671c24c7bff4f523dcc033f7e96f71e334d89a663e2742e') + '6ea402187cd93305315e7a0fae0012eaead15083916985b1e32ee1cc6cf9469c') # The qualified M3 Pro build's generated kernel files and libclc. build() stops # when one of ours differs. libagx_shaders.c is compared through kernel-digest.py: diff --git a/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-confirm b/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-confirm index c54676d8f84eb9..2360287ac5c80c 100755 --- a/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-confirm +++ b/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-confirm @@ -1,8 +1,9 @@ #!/bin/sh # SPDX-License-Identifier: MIT # mesa-m3-session-confirm [--settle SECONDS]: write the success marker of this -# boot's login with /opt/mesa-m3, but only on live evidence that its -# compositor is up and serving. Run by mesa-m3-session-confirm.service after +# boot's login with /opt/mesa-m3 (or with the system Mesa that the session hook +# admitted automatically), but only on live evidence that its compositor is up +# and serving. Run by mesa-m3-session-confirm.service after # graphical-session.target (reaching the target alone proves nothing). # # The attempt (${XDG_STATE_HOME:-~/.local/state}/mesa-m3/attempt, written by diff --git a/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-env b/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-env index a90c3b5d2bc143..94c9a66616c19b 100644 --- a/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-env +++ b/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-env @@ -49,9 +49,10 @@ # search failed) # gpu-fault the kernel log shows an M3 GPU or display fault this boot # (on a T8122 also a failed first-job verdict of its start) -# previous-failed a login with /opt/mesa-m3 earlier this boot has no success -# marker: its compositor was not seen alive and answering -# on its Wayland socket after the settle time (see +# previous-failed a login with /opt/mesa-m3 (or with the system Mesa admitted +# automatically) earlier this boot has no success marker: +# its compositor was not seen alive and answering on its +# Wayland socket after the settle time (see # mesa-m3-session-confirm.service). The attempt and the # marker are in ${XDG_STATE_HOME:-~/.local/state}/mesa-m3, # which a logout does not clear (/run/user/ can go @@ -60,20 +61,37 @@ # M1, M2, the M3 Max and anything else are left alone. # # When it does not use /opt/mesa-m3 but an M3 render node is there and this -# user can open it, the system Mesa would be refused by the kernel (it is not -# M3-aware), and a compositor that renders through an EGL device on that node +# user can open it, a system Mesa that is not M3-aware would be refused by the +# kernel, and a compositor that renders through an EGL device on that node # (Hyprland) would stop at start. For every inactive reason except user-setup # it therefore exports LIBGL_ALWAYS_SOFTWARE=1 (and MESA_M3_FALLBACK=software, # which marks it as this hook's): the session renders in software. For # user-setup it exports nothing: the session's own Mesa setup, which it uses, # decides. The state file says which (fallback=software|none). # -# A file opt-out (/etc/mesa-m3/disable or the user's mesa-m3/disable) on a -# J613 (T8122) or a J516S (T6030) is the exception: when the same node, -# display, kernel-log, user-setup and same-boot attempt checks pass and -# mesa-m3-system-check admits /usr, the session keeps system Mesa -# (fallback=none, no new attempt). The whole check runs in its own sh and is -# refused after 20 seconds. mesa_m3=off stays on the software fallback. +# A system Mesa on /usr that drives the GPU natively is the exception, on a +# J613 or J615 (T8122, the 13- and 15-inch M3 Air) or a J516S (T6030) in a +# Hyprland session. It is checked after the gate above, whatever the gate +# decided, except not-supported and user-setup (the session's own Mesa setup +# still decides). When the node, display, kernel-log, user-setup +# (--system-provider) and same-boot attempt checks pass and +# mesa-m3-system-check admits /usr, the session keeps the system Mesa +# (fallback=none) and /opt/mesa-m3 stays inactive, even where the gate would +# have used it: the system Mesa supersedes the legacy build. The whole check +# runs in its own sh and is refused after 20 seconds. mesa_m3=off and +# nomodeset refuse it, and any refusal leaves the gate's decision and its +# fallback exactly as above. Its state: +# system-mesa no opt-out: the system Mesa was admitted automatically. +# Like active, it records an attempt, so a login whose +# compositor is not confirmed (mesa-m3-session-confirm) +# turns the next login of this boot into the software +# fallback instead of a login loop. +# opt-out a file opt-out (/etc/mesa-m3/disable or the user's +# mesa-m3/disable) whose system Mesa was admitted: as +# before, no new attempt. +# Without a file opt-out, the 25G83 native profile stays on its own gate: an +# /etc/mesa-m3/t8122-profile selection, or a node with the +# apple,j613-25g83-gpu-handoff property, is not admitted automatically. # # Each login writes /run/user//mesa-m3-session.state (atomically, 0644, # key=value lines, first line schema=aurora.mesa-m3-session/2) and logs one @@ -255,13 +273,14 @@ mesa_m3_gate() { # opt-out uses before it will leave the system Mesa in place. _mm3_klog=$(_mm3_kernel_log) || { echo "$_mm3_klog"; return 1; } - # A login that chose /opt/mesa-m3 earlier this boot must have a success - # marker bound to it (same boot_id, session_id and hook_run_id), written by - # mesa-m3-session-confirm.service only after its compositor was alive and - # answering on its Wayland socket after the settle time. Otherwise this + # A login that chose /opt/mesa-m3 (or the system Mesa automatically) earlier + # this boot must have a success marker bound to it (same boot_id, + # session_id and hook_run_id), written by mesa-m3-session-confirm.service + # only after its compositor was alive and answering on its Wayland socket + # after the settle time. Otherwise this # login stays off it: no login loop when the compositor cannot run on it. if ! _mm3_legacy_attempt_clear "$_mm3_root"; then - echo "previous-failed the last login with /opt/mesa-m3 this boot (hook_run_id ${_mm3_v:-unknown}) has no success marker: its compositor was not seen alive and answering, so this login renders in software; to try the GPU again: rm $_mm3_att, then log out and in (or reboot)" + echo "previous-failed the last GPU login this boot (hook_run_id ${_mm3_v:-unknown}) has no success marker: its compositor was not seen alive and answering, so this login renders in software; to try the GPU again: rm $_mm3_att, then log out and in (or reboot)" return 1 fi @@ -361,9 +380,9 @@ _mm3_kernel_log() { return 0 } -# Return 0 when this boot has no unmatched /opt/mesa-m3 attempt. Return 1 -# when an attempt for this boot_id has no success marker bound to its -# hook_run_id and session_id. Never creates, deletes or rewrites either file. +# Return 0 when this boot has no unmatched attempt (of /opt/mesa-m3 or of an +# automatic system Mesa). Return 1 when an attempt for this boot_id has no +# success marker bound to its hook_run_id and session_id. Never creates, deletes or rewrites either file. # On failure, _mm3_att and _mm3_v are the attempt path and its hook_run_id. _mm3_legacy_attempt_clear() { _mm3_att="$1${XDG_STATE_HOME:-$HOME/.local/state}/mesa-m3/attempt" @@ -385,27 +404,33 @@ _mm3_legacy_attempt_clear() { return 0 } -# File opt-out on J613/T8122 or J516S/T6030 may keep the system Mesa. Prints -# one detail line and returns 0 only when every check below passes. A helper -# exit of 0 is not enough on its own. Does not write an attempt or touch a -# marker. mesa_m3=off is not a file opt-out. +# The system Mesa on J613 or J615 (T8122) or J516S (T6030), automatically or +# after a file opt-out. Prints one detail line and returns 0 only when every +# check below passes. A helper exit of 0 is not enough on its own. Does not +# write an attempt or touch a marker (the caller records an automatic one). +# mesa_m3=off and nomodeset refuse it; mesa_m3=off is not a file opt-out. _mm3_system_admit() { case ${XDG_CURRENT_DESKTOP:-} in Hyprland|hyprland) ;; *) return 1 ;; esac _mm3_cmdline=$(cat "$1/proc/cmdline" 2>/dev/null) || return 1 case " $_mm3_cmdline " in *" mesa_m3=off "*|*" nomodeset "*) return 1 ;; esac + _mm3_auto=0 if [ ! -e "$1/etc/mesa-m3/disable" ] && [ ! -e "$1${XDG_CONFIG_HOME:-$HOME/.config}/mesa-m3/disable" ]; then - return 1 + # Automatic: an explicit 25G83 native profile selection keeps its gate. + _mm3_auto=1 + [ ! -e "$1/etc/mesa-m3/t8122-profile" ] || return 1 fi _mm3_dt=$(tr '\0' '\n' 2>/dev/null < "$1/proc/device-tree/compatible") _mm3_soc=$(_mm3_soc_of "$1") _mm3_chip= _mm3_identity=$(printf '%s\n' "$_mm3_dt" | grep -E '^apple,j') _mm3_socs=$(printf '%s\n' "$_mm3_dt" | grep -E '^apple,t') + # Exactly one board and one SoC compatible. The 13-inch (J613) and 15-inch + # (J615) M3 Air pass the same T8122 checks; mesa-m3-system-check repeats them. case "$_mm3_soc:$_mm3_identity:$_mm3_socs" in - t8122:apple,j613:apple,t8122) _mm3_chip=t8122 ;; + t8122:apple,j613:apple,t8122 | t8122:apple,j615:apple,t8122) _mm3_chip=t8122 ;; t6030:apple,j516s:apple,t6030) _mm3_chip=t6030 ;; *) return 1 ;; esac @@ -422,6 +447,10 @@ _mm3_system_admit() { _mm3_drv=$(basename "$(readlink -f "$_mm3_node/device/driver" 2>/dev/null)" 2>/dev/null) [ "$_mm3_drv" = asahi ] || return 1 tr '\0' '\n' 2>/dev/null < "$_mm3_node/device/of_node/compatible" | grep -qx "apple,agx-$_mm3_chip" || return 1 + # A node handed off for the 25G83 firmware belongs to the native profile. + if [ "$_mm3_auto" -eq 1 ] && [ -e "$_mm3_node/device/of_node/apple,j613-25g83-gpu-handoff" ]; then + return 1 + fi _mm3_dev="$1/dev/dri/${_mm3_node##*/}" [ -r "$_mm3_dev" ] && [ -w "$_mm3_dev" ] || return 1 _mm3_dcp=$(_mm3_native_display "$1" "$_mm3_chip") || return 1 @@ -448,7 +477,7 @@ _mm3_system_admit() { timeout -k 2 5 env -i PATH=/usr/bin:/bin "$_mm3_helper" \ "/dev/dri/${_mm3_node##*/}" "$_mm3_chip" >/dev/null 2>&1 [ "$?" -eq 0 ] || return 1 - printf '%s\n' "system Mesa on /usr admits /dev/dri/${_mm3_node##*/} ($_mm3_chip); /opt/mesa-m3 stays inactive" + printf '%s\n' "system Mesa on /usr admits /dev/dri/${_mm3_node##*/} ($_mm3_chip, $_mm3_identity); /opt/mesa-m3 stays inactive" } # _mm3_system_admit in its own sh, under one timeout for the whole check. A @@ -501,7 +530,8 @@ _mm3_state() { return 1 } -# _mm3_attempt ROOT: record that this login uses /opt/mesa-m3, in +# _mm3_attempt ROOT: record that this login uses /opt/mesa-m3 (or the system +# Mesa admitted automatically), in # ${XDG_STATE_HOME:-~/.local/state}/mesa-m3/attempt (kept across a logout; a # later boot's gate ignores it by boot_id). It counts as failed until # mesa-m3-session-confirm.service writes a success marker beside it with the @@ -533,7 +563,35 @@ mesa_m3_session_env() { [ -r /proc/sys/kernel/random/uuid ] && IFS= read -r _mm3_run < /proc/sys/kernel/random/uuid _mm3_run=${_mm3_run:-unknown} [ "${_mm3_decision%% *}" = not-supported ] || export MESA_M3_HOOK_RUN_ID="$_mm3_run" - if [ "$_mm3_on" -eq 0 ]; then + # The system Mesa first, whatever the gate decided (see the header), except + # where this is neither a T6030 nor a T8122 or the session's own Mesa setup + # chose. A refusal leaves the gate's decision and fallback as they were. + _mm3_admitted=0 + case ${_mm3_decision%% *} in + not-supported | user-setup) ;; + *) + _mm3_sys=$(_mm3_system_admit_bounded "${1:-}") + [ "$?" -ne 0 ] || _mm3_admitted=1 ;; + esac + if [ "$_mm3_admitted" -eq 1 ]; then + # Only the pair this hook itself exports. Anything else in the + # environment, including a user's own LIBGL_ALWAYS_SOFTWARE without + # this marker, stays. + if [ "${LIBGL_ALWAYS_SOFTWARE:-}" = 1 ] && [ "${MESA_M3_FALLBACK:-}" = software ]; then + unset LIBGL_ALWAYS_SOFTWARE + unset MESA_M3_FALLBACK + fi + if [ "${_mm3_decision%% *}" = opt-out ]; then + # A file opt-out: as before, no new attempt. + _mm3_state "${1:-}" inactive opt-out "${_mm3_decision#* }; $_mm3_sys" none + else + # Automatic: recorded like an active login, so that a compositor + # that is not confirmed sends the next login of this boot to the + # software fallback (previous-failed) instead of looping. + _mm3_state "${1:-}" inactive system-mesa "$_mm3_sys; the /opt/mesa-m3 gate said: $_mm3_decision" none + _mm3_attempt "${1:-}" + fi + elif [ "$_mm3_on" -eq 0 ]; then case ":${LD_LIBRARY_PATH:-}:" in *":$_mm3_p/lib:"*) ;; *) export LD_LIBRARY_PATH="$_mm3_p/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" ;; @@ -562,31 +620,14 @@ mesa_m3_session_env() { _mm3_attempt "${1:-}" else # Never the system Mesa on an M3 render node it can open (Hyprland - # would stop at start), except where this session's own setup chose, - # or a file opt-out whose system-Mesa handoff passed. + # would stop at start), except where this session's own setup chose. _mm3_fb=none - _mm3_detail=${_mm3_decision#* } - _mm3_admitted=0 - if [ "${_mm3_decision%% *}" = opt-out ]; then - _mm3_sys=$(_mm3_system_admit_bounded "${1:-}") - if [ "$?" -eq 0 ]; then - _mm3_admitted=1 - _mm3_detail="$_mm3_detail; $_mm3_sys" - # Only the pair this hook itself exports. Anything else in the - # environment, including a user's own LIBGL_ALWAYS_SOFTWARE - # without this marker, stays. - if [ "${LIBGL_ALWAYS_SOFTWARE:-}" = 1 ] && [ "${MESA_M3_FALLBACK:-}" = software ]; then - unset LIBGL_ALWAYS_SOFTWARE - unset MESA_M3_FALLBACK - fi - fi - fi - if [ "$_mm3_admitted" -eq 0 ] && [ "${_mm3_decision%% *}" != user-setup ] && _mm3_node_open "${1:-}"; then + if [ "${_mm3_decision%% *}" != user-setup ] && _mm3_node_open "${1:-}"; then export LIBGL_ALWAYS_SOFTWARE=1 export MESA_M3_FALLBACK=software _mm3_fb=software fi - _mm3_state "${1:-}" inactive "${_mm3_decision%% *}" "$_mm3_detail" "$_mm3_fb" + _mm3_state "${1:-}" inactive "${_mm3_decision%% *}" "${_mm3_decision#* }" "$_mm3_fb" fi } @@ -602,4 +643,4 @@ unset _mm3_root _mm3_p _mm3_nodes _mm3_node _mm3_n _mm3_drv _mm3_dcp _mm3_c _mm3 _mm3_body _mm3_kv _mm3_dir _mm3_tmp _mm3_fb _mm3_run _mm3_att _mm3_ok _mm3_faults _mm3_ign _mm3_igd \ _mm3_soc _mm3_name _mm3_dt _mm3_base _mm3_profile _mm3_requested _mm3_files _mm3_gate_ifs _mm3_board _mm3_note \ _mm3_klog _mm3_sys _mm3_admitted _mm3_detail _mm3_helper _mm3_chip _mm3_identity _mm3_socs _mm3_cmdline \ - _mm3_self + _mm3_self _mm3_auto diff --git a/tools/aurora-sep/recipes/mesa-m3/mesa-m3-system-check.c b/tools/aurora-sep/recipes/mesa-m3/mesa-m3-system-check.c index 8f6177750196d5..a80c435cc0808b 100644 --- a/tools/aurora-sep/recipes/mesa-m3/mesa-m3-system-check.c +++ b/tools/aurora-sep/recipes/mesa-m3/mesa-m3-system-check.c @@ -15,12 +15,24 @@ #include #include +/* The boards whose system Mesa the session hook may admit: the 13-inch (J613) + * and 15-inch (J615) M3 Air on the T8122, the J516S on the T6030. + */ +static bool +board_name_matches(const char *value, const char *soc) +{ + if (!strcmp(soc, "t8122")) + return !strcmp(value, "apple,j613") || !strcmp(value, "apple,j615"); + if (!strcmp(soc, "t6030")) + return !strcmp(value, "apple,j516s"); + return false; +} + static bool board_compat_matches(const char *bytes, size_t length, const char *soc) { - const char *board = !strcmp(soc, "t8122") ? "apple,j613" : - !strcmp(soc, "t6030") ? "apple,j516s" : NULL; - if (!board || !length || bytes[length - 1] != '\0') + if ((strcmp(soc, "t8122") && strcmp(soc, "t6030")) || + !length || bytes[length - 1] != '\0') return false; unsigned boards = 0, socs = 0; size_t offset = 0; @@ -31,7 +43,7 @@ board_compat_matches(const char *bytes, size_t length, const char *soc) return false; const char *value = bytes + offset; if (!strncmp(value, "apple,j", 7)) { - if (strcmp(value, board)) + if (!board_name_matches(value, soc)) return false; boards++; } else if (!strncmp(value, "apple,t", 7)) { diff --git a/tools/aurora-sep/recipes/mesa-m3/mesa-m3.install b/tools/aurora-sep/recipes/mesa-m3/mesa-m3.install index a19e70799264e0..0a80099acc7354 100644 --- a/tools/aurora-sep/recipes/mesa-m3/mesa-m3.install +++ b/tools/aurora-sep/recipes/mesa-m3/mesa-m3.install @@ -9,6 +9,8 @@ post_install() { echo "mesa-m3: takes effect at the next login to a uwsm session on an M3 Pro (T6030)," echo "mesa-m3: and on an M3 (T8122) only with /etc/mesa-m3/t8122-gpu-experiment: its GPU" echo "mesa-m3: is experimental (the installer writes the file for --m3-gpu-experiment)." + echo "mesa-m3: In a Hyprland session on a 13- or 15-inch M3 Air (J613, J615) or a J516S," + echo "mesa-m3: a system Mesa in /usr that drives the GPU is preferred over /opt/mesa-m3." echo "mesa-m3: It does nothing on other machines. Each login's decision is in" echo "mesa-m3: /run/user//mesa-m3-session.state and the journal (tag mesa-m3)." _render_note diff --git a/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md b/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md index c5b7c978049197..0465b9f8784696 100644 --- a/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md +++ b/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md @@ -26,13 +26,37 @@ scanner refuses instead of holding the login. The helper keeps its own 5-second timeout. A failed check keeps the existing software fallback. `mesa_m3=off` and -`nomodeset` remain recovery choices. Other boards, including J615, remain -outside this system handoff. Explicit legacy profile selection is unchanged. -No opt-in flags, attempt records or success markers are added or removed. +`nomodeset` remain recovery choices. Other boards remain outside this system +handoff. Explicit legacy profile selection is unchanged. Only the exact software-fallback pair previously exported by this hook can be cleared. The state remains `inactive`, `reason=opt-out`, `fallback=none` when system Mesa admission succeeds. +## J615 and automatic selection + +The 15-inch Air (J615, T8122) is admitted with the same checks as the J613, in +the hook and in `mesa-m3-system-check`. A root that names both Airs, or one of +them twice, is refused. + +The file opt-out is no longer required. In a Hyprland session on J613, J615 +or J516S, the hook runs the same bounded admission after its `/opt/mesa-m3` +gate, whatever that gate decided, except `not-supported` and `user-setup` +(the session's own Mesa setup still decides). When it passes, the session +keeps system Mesa even where the gate would have used `/opt/mesa-m3`: the +system Mesa supersedes the legacy build. The state is `inactive`, +`reason=system-mesa`, `fallback=none`; its detail names the admitted board and +the gate's own decision. Any refusal leaves the gate's decision and fallback +exactly as before. + +An automatic admission writes the same attempt as an `/opt/mesa-m3` login. +`mesa-m3-session-confirm` confirms it in the same way, and an unconfirmed +login sends the next login of that boot to the software fallback, instead of +a login loop. The file opt-out path still writes no attempt. + +Without a file opt-out, the 25G83 native profile stays on its own gate: an +`/etc/mesa-m3/t8122-profile` selection, or a render node with +`apple,j613-25g83-gpu-handoff`, is not admitted automatically. + The helper proves only that the system EGL device and a context open. It does not allocate GBM buffers, render or present. A system-Mesa login that passes admission and then fails later is not recorded: this path writes no attempt, diff --git a/tools/aurora-sep/recipes/mesa-m3/tests/test-session-env.sh b/tools/aurora-sep/recipes/mesa-m3/tests/test-session-env.sh index f10616501ea707..df4064e876a410 100755 --- a/tools/aurora-sep/recipes/mesa-m3/tests/test-session-env.sh +++ b/tools/aurora-sep/recipes/mesa-m3/tests/test-session-env.sh @@ -676,6 +676,7 @@ rm "$J516S/etc/mesa-m3/t8122-profile" system_root() { case "$2" in air) _hd="apple,j613 apple,t8122"; _hg=$G15G ;; + air15) _hd="apple,j615 apple,t8122"; _hg=$G15G ;; *) _hd="apple,j516s apple,t6030"; _hg=$G15S ;; esac _hr=$(mkroot "handoff-$1" "$_hd" "$_hg" yes 1 yes) @@ -741,11 +742,21 @@ rm "$HRA/etc/mesa-m3/disable" mkdir -p "$HRA$T/home/.config/mesa-m3" : > "$HRA$T/home/.config/mesa-m3/disable" handoff "$HRA" none yes +if grep -q 'detail=.*admits /dev/dri/renderD128 (t8122, apple,j613)' "$HRA/run/user/$UID_/mesa-m3-session.state"; then + echo 'ok handoff: the state names the admitted board' +else echo 'FAIL handoff: board in the state detail'; fail=1; fi if [ "$(cat "$HRA/opt/mesa-m3/libexec/system-check.args")" = "$(printf '/dev/dri/renderD128\nt8122')" ] && grep -qx 'PATH=/usr/bin:/bin' "$HRA/opt/mesa-m3/libexec/system-check.env" && [ -z "$(grep -vE '^(PATH=/usr/bin:/bin|PWD=.*|SHLVL=[0-9]+|_=.*)$' "$HRA/opt/mesa-m3/libexec/system-check.env")" ]; then echo 'ok handoff: helper receives exact device/SoC and empty selector environment' else echo 'FAIL handoff: helper argv/environment'; fail=1; fi +# The 15-inch Air (J615) passes the same file opt-out handoff as the J613. +HRJ=$(system_root optout-j615 air15) +handoff "$HRJ" none yes +if [ "$(cat "$HRJ/opt/mesa-m3/libexec/system-check.args")" = "$(printf '/dev/dri/renderD128\nt8122')" ] && + grep -q 'detail=.*admits /dev/dri/renderD128 (t8122, apple,j615)' "$HRJ/run/user/$UID_/mesa-m3-session.state"; then + echo 'ok handoff: J615 helper receives the T8122 node and the state names apple,j615' +else echo 'FAIL handoff: J615 helper argv or state'; fail=1; fi HRP=$(system_root root-optout pro) handoff "$HRP" none yes handoff "$HRP" none yes LIBGL_ALWAYS_SOFTWARE=1 MESA_M3_FALLBACK=software @@ -775,7 +786,7 @@ for journal in noperm error match bank dcp g15g verdict; do HR=$(system_root "journal-$journal" pro) handoff "$HR" software no FAKE_JOURNAL="$journal" done -for dt in 'apple,j615 apple,t8122' 'apple,j514s apple,t6030' \ +for dt in 'apple,j615 apple,t6030' 'apple,j514s apple,t6030' \ 'apple,j614s apple,t6030' 'apple,j613 apple,j615 apple,t8122' \ 'apple,j613 apple,t6030' 'apple,j516s apple,t6030 apple,t8122'; do HR=$(system_root "board-$(printf '%s' "$dt" | tr ' ,' '--')" pro) @@ -785,6 +796,15 @@ for dt in 'apple,j615 apple,t8122' 'apple,j514s apple,t6030' \ case "$dt" in *t8122*) nul apple,agx-t8122 apple,agx-t6030 > "$HR/sys/devices/platform/gpu0/of_node/compatible" ;; esac handoff "$HR" software no done +# On a T8122 GPU and display, any board but the two Airs is refused, and so +# is a root naming both of them or one twice. +for dt in 'apple,j504 apple,t8122' 'apple,j615s apple,t8122' 'apple,j516s apple,t8122' \ + 'apple,j615 apple,j613 apple,t8122' 'apple,j615 apple,j615 apple,t8122' \ + 'apple,j615 apple,t8122 apple,t8122'; do + HR=$(system_root "air-board-$(printf '%s' "$dt" | tr ' ,' '--')" air15) + nul $dt > "$HR/proc/device-tree/compatible" + handoff "$HR" software no +done HR=$(system_root no-display pro) rm "$HR/sys/class/drm/card2/device" handoff "$HR" software no @@ -845,5 +865,173 @@ rm "$HOK" sed -i 's/boot_id=boot-a/boot_id=old-boot/' "$HATT" handoff "$HR" none yes +# Automatic system Mesa: no opt-out file. On J613, J615 and J516S a Hyprland +# session keeps the system Mesa when every check passes, whatever the +# /opt/mesa-m3 gate decided (reason system-mesa, fallback none, an attempt for +# the loop guard). Every refusal leaves the gate's decision and fallback. +auto_root() { # NAME air|air15|pro + _ar=$(system_root "auto-$1" "$2") + rm "$_ar/etc/mesa-m3/disable" + echo "$_ar" +} +# auto_run ROOT [VAR=value...]: one hook run under $TSH; prints +# "DECISION REASON FALLBACK helper=yes|no attempt=new|unchanged|none sw=.. legacy=.."; +# the run's exports and state are left in $T/auto.out. +auto_run() { + _ar=$1; shift + rm -f "$_ar/opt/mesa-m3/libexec/system-check.args" "$_ar/run/user/$UID_/mesa-m3-session.state" "$T/log" + _aa="$_ar$T/home/.local/state/mesa-m3/attempt" + _ab=none + [ ! -f "$_aa" ] || _ab=$(sha256sum "$_aa" | cut -d' ' -f1) + env -i PATH="$T/bin:/usr/bin:/bin" HOME="$T/home" FAKE_LOG="$T/log" XDG_SESSION_ID=5 \ + XDG_CURRENT_DESKTOP=Hyprland "$@" "$TSH" -c ' + MESA_M3_SESSION_ENV_NO_RUN=1 . "$1" + mesa_m3_session_env "$2" + printf "software=%s\nmarker=%s\nlegacy=%s\nrun=%s\n" "${LIBGL_ALWAYS_SOFTWARE:-}" "${MESA_M3_FALLBACK:-}" "${MESA_M3_SESSION:-}" "${MESA_M3_HOOK_RUN_ID:-}" + cat "$2/run/user/$(id -u)/mesa-m3-session.state" + ' sh "$ENVFILE" "$_ar" > "$T/auto.out" 2>&1 + _aaf=none + [ ! -f "$_aa" ] || _aaf=$(sha256sum "$_aa" | cut -d' ' -f1) + if [ "$_aaf" = none ]; then _aat=none + elif [ "$_aaf" = "$_ab" ]; then _aat=unchanged + elif grep -qx "hook_run_id=$(sed -n 's/^run=//p' "$T/auto.out")" "$_aa"; then _aat=new + else _aat=foreign; fi + _ac=no + [ ! -e "$_ar/opt/mesa-m3/libexec/system-check.args" ] || _ac=yes + echo "$(sed -n 's/^decision=//p' "$T/auto.out") $(sed -n 's/^reason=//p' "$T/auto.out") $(sed -n 's/^fallback=//p' "$T/auto.out") helper=$_ac attempt=$_aat sw=$(sed -n 's/^software=//p' "$T/auto.out") legacy=$(sed -n 's/^legacy=//p' "$T/auto.out")" +} +expect_auto() { # NAME "WANT" ROOT [VAR=value...] + _en=$1 _ew=$2 + shift 2 + _eg=$(auto_run "$@") + if [ "$_eg" = "$_ew" ]; then echo "ok auto $_en: $_eg" + else echo "FAIL auto $_en: got '$_eg', want '$_ew'"; sed 's/^/ /' "$T/auto.out"; fail=1; fi +} +auto_fresh() { rm -f "$1$T/home/.local/state/mesa-m3/attempt" "$1$T/home/.local/state/mesa-m3/session-ok"; } +SYS='inactive system-mesa none helper=yes attempt=new sw= legacy=' +# The 13- and 15-inch Air without the T8122 experiment marker: the gate says +# experimental, which used to mean the software fallback. +AJ613=$(auto_root j613 air) +expect_auto "J613 without opt-out" "$SYS" "$AJ613" +grep -qx 'detail=system Mesa on /usr admits /dev/dri/renderD128 (t8122, apple,j613); /opt/mesa-m3 stays inactive; the /opt/mesa-m3 gate said: experimental .*' "$T/auto.out" && + echo "ok auto J613: the state names the system Mesa, the board and the gate's own decision" || + { echo "FAIL auto J613 detail: $(grep '^detail=' "$T/auto.out")"; fail=1; } +AJ615=$(auto_root j615 air15) +expect_auto "J615 without opt-out" "$SYS" "$AJ615" +if [ "$(cat "$AJ615/opt/mesa-m3/libexec/system-check.args")" = "$(printf '/dev/dri/renderD128\nt8122')" ] && + [ -z "$(grep -vE '^(PATH=/usr/bin:/bin|PWD=.*|SHLVL=[0-9]+|_=.*)$' "$AJ615/opt/mesa-m3/libexec/system-check.env")" ] && + grep -q '^detail=system Mesa on /usr admits /dev/dri/renderD128 (t8122, apple,j615);' "$T/auto.out"; then + echo "ok auto J615: helper receives the T8122 node and an empty environment; the state names apple,j615" +else echo "FAIL auto J615: helper argv/environment or detail"; fail=1; fi +# The attempt is bound to this login exactly as an active one is, so +# mesa-m3-session-confirm can confirm it (see test-confirm.sh). +_arun=$(sed -n 's/^run=//p' "$T/auto.out") +if [ "$(head -n 1 "$AJ615$T/home/.local/state/mesa-m3/attempt")" = schema=aurora.mesa-m3-attempt/2 ] && + grep -qx 'boot_id=boot-a' "$AJ615$T/home/.local/state/mesa-m3/attempt" && + grep -qx 'session_id=5' "$AJ615$T/home/.local/state/mesa-m3/attempt" && + grep -qx "hook_run_id=$_arun" "$T/auto.out"; then + echo "ok auto J615: attempt bound to boot_id, session_id and the exported hook_run_id (= state)" +else echo "FAIL auto J615 attempt: $(cat "$AJ615$T/home/.local/state/mesa-m3/attempt")"; fail=1; fi +check_state "system-mesa (J615)" inactive system-mesa none "$(auto_fresh "$AJ615"; state "$AJ615" 5)" +auto_fresh "$AJ615" +# The leftover software pair of an earlier login is the hook's own: cleared. +# A user's own LIBGL_ALWAYS_SOFTWARE is a user setup (below). +expect_auto "J615 clears its own leftover pair" "$SYS" "$AJ615" LIBGL_ALWAYS_SOFTWARE=1 MESA_M3_FALLBACK=software +auto_fresh "$AJ615" +# The system Mesa wins where the gate would use /opt/mesa-m3: the J613 opted +# in to the experiment, and the J516S. +mkdir -p "$AJ613/etc/mesa-m3"; : > "$AJ613/etc/mesa-m3/t8122-gpu-experiment" +auto_fresh "$AJ613" +expect_auto "J613 opted in: system wins over /opt" "$SYS" "$AJ613" +grep -q '^detail=.*the /opt/mesa-m3 gate said: active T8122' "$T/auto.out" && + echo "ok auto J613 opted in: the state keeps the gate's active verdict" || { echo "FAIL auto J613 opted-in detail"; fail=1; } +rm "$AJ613/etc/mesa-m3/t8122-gpu-experiment"; auto_fresh "$AJ613" +AJ516=$(auto_root j516s pro) +expect_auto "J516S: system wins over /opt" "$SYS" "$AJ516" +grep -q '^detail=system Mesa on /usr admits /dev/dri/renderD128 (t6030, apple,j516s);.*gate said: active T6030' "$T/auto.out" && + echo "ok auto J516S: the state names t6030, apple,j516s and the gate's active verdict" || { echo "FAIL auto J516S detail"; fail=1; } +auto_fresh "$AJ516" +# Exactly the run id exported: none of /opt/mesa-m3's variables, no fallback. +exs=$(env -i PATH="$T/bin:/usr/bin:/bin" HOME="$T/home" FAKE_LOG="$T/log" XDG_CURRENT_DESKTOP=Hyprland LD_LIBRARY_PATH=/x "$TSH" -c ' + MESA_M3_SESSION_ENV_NO_RUN=1 . "$1"; unset MESA_M3_SESSION_ENV_NO_RUN + env | sort > "$HOME/b7"; mesa_m3_session_env "$2" >/dev/null; env | sort > "$HOME/a7" + diff "$HOME/b7" "$HOME/a7" | grep "^[<>]" | grep -v -E "^[<>] (_=|SHLVL=)" | sed "s/^> //"' sh "$ENVFILE" "$AJ516" | + sed -E 's/^MESA_M3_HOOK_RUN_ID=[0-9a-f-]{36}$/MESA_M3_HOOK_RUN_ID=/') +[ "$exs" = "MESA_M3_HOOK_RUN_ID=" ] && echo "ok auto J516S: only the run id exported, LD_LIBRARY_PATH untouched" || + { echo "FAIL auto J516S exports: $exs"; fail=1; } +auto_fresh "$AJ516" +# No login loop: an automatic login whose compositor was not confirmed sends +# the next login of this boot to the software fallback; a matching success +# marker lets the system Mesa in again; another boot's attempt does not count. +expect_auto "J615 first login" "$SYS" "$AJ615" +_arun=$(sed -n 's/^run=//p' "$T/auto.out") +expect_auto "J615 after an unconfirmed login" "inactive experimental software helper=no attempt=unchanged sw=1 legacy=" "$AJ615" +printf 'schema=aurora.mesa-m3-session-ok/1\nboot_id=boot-a\nsession_id=5\nhook_run_id=%s\n' "$_arun" > "$AJ615$T/home/.local/state/mesa-m3/session-ok" +expect_auto "J615 after a confirmed login" "$SYS" "$AJ615" +echo boot-b > "$AJ615/proc/sys/kernel/random/boot_id" +expect_auto "J615 after another boot's unconfirmed login" "$SYS" "$AJ615" +echo boot-a > "$AJ615/proc/sys/kernel/random/boot_id"; auto_fresh "$AJ615" +expect_auto "J516S first login" "$SYS" "$AJ516" +expect_auto "J516S after an unconfirmed login" "inactive previous-failed software helper=no attempt=unchanged sw=1 legacy=" "$AJ516" +auto_fresh "$AJ516" +# Fail closed: each refusal keeps today's decision and fallback exactly. +GATEX='inactive experimental software helper=no attempt=none sw=1 legacy=' +expect_auto "J615 mesa_m3=off" "inactive opt-out software helper=no attempt=none sw=1 legacy=" "$(R=$(auto_root j615-off air15); echo 'root=UUID=x rw mesa_m3=off quiet' > "$R/proc/cmdline"; echo "$R")" +expect_auto "J615 nomodeset" "$GATEX" "$(R=$(auto_root j615-nomodeset air15); echo 'root=UUID=x rw nomodeset quiet' > "$R/proc/cmdline"; echo "$R")" +expect_auto "J516S mesa_m3=off" "inactive opt-out software helper=no attempt=none sw=1 legacy=" "$(R=$(auto_root j516-off pro); echo 'root=UUID=x rw mesa_m3=off quiet' > "$R/proc/cmdline"; echo "$R")" +R=$(auto_root j615-helper-fails air15); : > "$R/opt/mesa-m3/libexec/system-check.fail" +expect_auto "J615 helper refuses" "inactive experimental software helper=yes attempt=none sw=1 legacy=" "$R" +R=$(auto_root j615-helper-stalls air15); : > "$R/opt/mesa-m3/libexec/system-check.sleep" +expect_auto "J615 helper times out" "inactive experimental software helper=yes attempt=none sw=1 legacy=" "$R" +R=$(auto_root j615-helper-missing air15); rm "$R/opt/mesa-m3/libexec/mesa-m3-system-check" +expect_auto "J615 helper missing" "$GATEX" "$R" +R=$(auto_root j516-helper-fails pro); : > "$R/opt/mesa-m3/libexec/system-check.fail" +expect_auto "J516S helper refuses: /opt as before" "active active none helper=yes attempt=new sw= legacy=/opt/mesa-m3" "$R" +# The J615 without its nodes: no render node (GPU not started), no display, +# another GPU, two nodes. +expect_auto "J615 without a render node" "inactive experimental none helper=no attempt=none sw= legacy=" \ + "$(R=$(auto_root j615-nonode air15); rm -r "$R/sys/class/drm/renderD128"; echo "$R")" +expect_auto "J615 without the display" "$GATEX" "$(R=$(auto_root j615-nodcp air15); rm "$R/sys/class/drm/card2/device"; echo "$R")" +expect_auto "J615 with the T6030 display" "$GATEX" \ + "$(R=$(auto_root j615-dcps air15); nul apple,t6030-display-subsystem > "$R/sys/devices/platform/display/of_node/compatible"; echo "$R")" +expect_auto "J615 with two render nodes" "inactive experimental software helper=no attempt=none sw=1 legacy=" \ + "$(R=$(auto_root j615-twonodes air15); mkdir "$R/sys/class/drm/renderD129"; echo "$R")" +expect_auto "J615 with the T6030 GPU" "inactive experimental none helper=no attempt=none sw= legacy=" \ + "$(R=$(auto_root j615-g15s air15); nul $G15S > "$R/sys/devices/platform/gpu0/of_node/compatible"; echo "$R")" +# Unknown or ambiguous boards: the gate's decision stands. +expect_auto "unknown T8122 board (J504)" "$GATEX" "$(R=$(auto_root j504 air15); nul apple,j504 apple,t8122 > "$R/proc/device-tree/compatible"; echo "$R")" +expect_auto "both Air boards" "$GATEX" "$(R=$(auto_root j613j615 air15); nul apple,j613 apple,j615 apple,t8122 > "$R/proc/device-tree/compatible"; echo "$R")" +expect_auto "14-inch M3 Pro (J514S): /opt as before" "active active none helper=no attempt=new sw= legacy=/opt/mesa-m3" \ + "$(R=$(auto_root j514s pro); nul apple,j514s apple,t6030 > "$R/proc/device-tree/compatible"; echo "$R")" +# The 25G83 native profile keeps its own gate: an explicit selection, or a +# node handed off for that firmware without one. +expect_auto "J613 with an explicit t8122-profile" "$GATEX" "$(R=$(auto_root j613-profile air); echo j613-25g83-hal200 > "$R/etc/mesa-m3/t8122-profile"; echo "$R")" +expect_auto "J615 with the 25G83 GPU handoff" "$GATEX" \ + "$(R=$(auto_root j615-hal200 air15); printf '\000\000\000\001' > "$R/sys/devices/platform/gpu0/of_node/apple,j613-25g83-gpu-handoff"; echo "$R")" +# Kernel log, desktop and user setup. +for journal in noperm error g15g verdict dcp; do + expect_auto "J615 journal $journal" "$GATEX" "$(auto_root "j615-journal-$journal" air15)" FAKE_JOURNAL="$journal" +done +expect_auto "J615 in a sway session" "$GATEX" "$AJ615" XDG_CURRENT_DESKTOP=sway +auto_fresh "$AJ615" +expect_auto "J615 with LIBGL_ALWAYS_SOFTWARE of the user's" "$GATEX" "$AJ615" LIBGL_ALWAYS_SOFTWARE=1 +auto_fresh "$AJ615" +R=$(auto_root j516-user-mesa pro); P="$T/home/mesa" +mkdir -p "$R$T/home/.config/uwsm" "$R$P/lib"; : > "$R$P/lib/libgallium-26.1.4.so" +printf 'export LIBGL_DRIVERS_PATH=%s/lib/dri\n' "$P" > "$R$T/home/.config/uwsm/env-hyprland" +expect_auto "J516S whose user chose a Mesa" "inactive user-setup none helper=no attempt=none sw= legacy=" "$R" +R=$(auto_root j615-late-software air15); mkdir -p "$R$T/home/.config/uwsm" +printf 'export LIBGL_ALWAYS_SOFTWARE+=1\n' > "$R$T/home/.config/uwsm/env" +expect_auto "J615 with a late append override" "$GATEX" "$R" +R=$(auto_root j615-no-hook air15); rm "$R/opt/mesa-m3/libexec/mesa-m3-session-env" +expect_auto "J615 without the installed hook" "$GATEX" "$R" +# A stalled journal is bounded (20 s, then 2 s to kill) on the automatic path. +R=$(auto_root j615-stall air15) +_hstart=$(date +%s) +expect_auto "J615 stalled journal" "$GATEX" "$R" FAKE_JOURNAL=stall +_hspent=$(( $(date +%s) - _hstart )) +if [ "$_hspent" -le 30 ]; then echo "ok auto J615 stalled journal: refused after ${_hspent}s" +else echo "FAIL auto J615 stalled journal: took ${_hspent}s"; fail=1; fi + [ "$fail" = 0 ] && echo "ALL PASS" || echo "SOME FAILED" exit "$fail" diff --git a/tools/aurora-sep/recipes/mesa-m3/tests/test-system-board.c b/tools/aurora-sep/recipes/mesa-m3/tests/test-system-board.c index 1aa86befdcdba2..2acf61e6e0f666 100644 --- a/tools/aurora-sep/recipes/mesa-m3/tests/test-system-board.c +++ b/tools/aurora-sep/recipes/mesa-m3/tests/test-system-board.c @@ -13,10 +13,21 @@ int main(void) CHECK(true, "t8122", "apple,j613\0apple,t8122\0apple,arm-platform\0"); CHECK(true, "t6030", "apple,j516s\0apple,t6030\0apple,arm-platform\0"); CHECK(true, "t6030", "apple,arm-platform\0apple,t6030\0apple,j516s\0"); - CHECK(false, "t8122", "apple,j615\0apple,t8122\0"); + CHECK(true, "t8122", "apple,j615\0apple,t8122\0apple,arm-platform\0"); + CHECK(true, "t8122", "apple,arm-platform\0apple,t8122\0apple,j615\0"); + CHECK(false, "t6030", "apple,j615\0apple,t6030\0"); + CHECK(false, "t8122", "apple,j615\0apple,j615\0apple,t8122\0"); + CHECK(false, "t8122", "apple,j615\0apple,t8122\0apple,t8122\0"); + CHECK(false, "t8122", "apple,j615\0"); + CHECK(false, "t8122", "apple,j615\0apple,t8122"); + CHECK(false, "t8122", "apple,j616\0apple,t8122\0"); + CHECK(false, "t8122", "apple,j615s\0apple,t8122\0"); + CHECK(false, "t8122", "apple,j504\0apple,t8122\0"); + CHECK(false, "t8122", "apple,j516s\0apple,t8122\0"); CHECK(false, "t6030", "apple,j514s\0apple,t6030\0"); CHECK(false, "t6030", "apple,j516s\0apple,t6031\0"); CHECK(false, "t8122", "apple,j613\0apple,j615\0apple,t8122\0"); + CHECK(false, "t8122", "apple,j615\0apple,j613\0apple,t8122\0"); CHECK(false, "t8122", "apple,j613\0apple,j613\0apple,t8122\0"); CHECK(false, "t6030", "apple,j516s\0apple,t6030\0apple,t6030\0"); CHECK(false, "t6030", "apple,j516s\0apple,t6030\0apple,t8122\0"); @@ -27,6 +38,6 @@ int main(void) CHECK(false, "t6030", "apple,j516s\0"); CHECK(false, "t8140", "apple,j516s\0apple,t6030\0"); CHECK(false, "t6030", ""); - puts("17 board parser controls passed"); + puts("28 board parser controls passed"); return 0; } diff --git a/tools/aurora-sep/recipes/mesa-m3/tests/test-system-provider.py b/tools/aurora-sep/recipes/mesa-m3/tests/test-system-provider.py index e528270b7c9444..4bfc444327f05e 100644 --- a/tools/aurora-sep/recipes/mesa-m3/tests/test-system-provider.py +++ b/tools/aurora-sep/recipes/mesa-m3/tests/test-system-provider.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 """The system-provider decision must see legacy selectors as overrides.""" from pathlib import Path +import shutil import subprocess import tempfile import unittest @@ -227,5 +228,168 @@ def test_missing_file_does_not_mask_unresolved_choice(self): self.assertNotEqual(result.returncode, 0, result.stdout) +class SystemAdmissionTest(unittest.TestCase): + """_mm3_system_admit: which boards and nodes may keep the system Mesa. + + The helper is a stub that records its arguments; its own board parser is + covered by test-system-board.c. Without a file opt-out the admission is + automatic; the same checks apply either way. + """ + + BOARDS = {"j613": ("apple,j613", "apple,t8122"), + "j615": ("apple,j615", "apple,t8122"), + "j516s": ("apple,j516s", "apple,t6030")} + + def setUp(self): + self.temporary = tempfile.TemporaryDirectory() + self.addCleanup(self.temporary.cleanup) + self.base = Path(self.temporary.name) + self.bin = self.base / "bin" + self.bin.mkdir() + journal = self.bin / "journalctl" + journal.write_text('#!/bin/sh\n[ -z "${FAKE_FAULT:-}" ] || echo "$FAKE_FAULT"\n' + 'echo "asahi 292400000.gpu: M3: firmware ready"\n') + journal.chmod(0o755) + + def make_root(self, board="j615", node=True, display=True): + compatibles = self.BOARDS.get(board, (board, "apple,t8122")) + soc = compatibles[-1].split(",")[1] + root = self.base / ("root-" + board) + (root / "proc/device-tree").mkdir(parents=True) + (root / "proc/cmdline").write_text("root=UUID=x rw quiet\n") + (root / "proc/device-tree/compatible").write_bytes( + b"".join(c.encode() + b"\0" for c in (*compatibles, "apple,arm-platform"))) + driver = root / "sys/bus/platform/drivers/asahi" + driver.mkdir(parents=True) + (root / "sys/class/drm").mkdir(parents=True) + (root / "dev/dri").mkdir(parents=True) + if node: + gpu = root / "sys/devices/platform/gpu0" + (gpu / "of_node").mkdir(parents=True) + (gpu / "of_node/compatible").write_bytes(b"apple,agx-" + soc.encode() + b"\0") + (gpu / "driver").symlink_to(driver) + (root / "sys/class/drm/renderD128").mkdir() + (root / "sys/class/drm/renderD128/device").symlink_to(gpu) + (root / "dev/dri/renderD128").touch(mode=0o600) + if display: + dcp = root / "sys/devices/platform/display" + (dcp / "of_node").mkdir(parents=True) + (dcp / "of_node/compatible").write_bytes( + b"apple," + soc.encode() + b"-display-subsystem\0") + (root / "sys/class/drm/card2").mkdir() + (root / "sys/class/drm/card2/device").symlink_to(dcp) + prefix = root / "opt/mesa-m3" + (prefix / "libexec").mkdir(parents=True) + (prefix / "share/mesa-m3").mkdir(parents=True) + (prefix / "lib").mkdir() + (prefix / "lib/libgallium-26.1.4.so").touch() + shutil.copy(RECIPE / "mesa-m3-user-setup", prefix / "libexec/mesa-m3-user-setup") + shutil.copy(RECIPE / "user-setup.list", prefix / "share/mesa-m3/user-setup.list") + helper = prefix / "libexec/mesa-m3-system-check" + helper.write_text('#!/bin/sh\nprintf "%s\\n" "$@" > "${0%/*}/args"\n' + '[ ! -e "${0%/*}/refuse" ]\n') + helper.chmod(0o755) + (root / "home/tester/.config").mkdir(parents=True) + return root + + def admit(self, root, **environment): + hook = RECIPE / "mesa-m3-session-env" + return subprocess.run( + ["sh", "-c", 'MESA_M3_SESSION_ENV_NO_RUN=1 . "$1" && _mm3_p=/opt/mesa-m3 && ' + '_mm3_system_admit "$2"', "sh", str(hook), str(root)], + env={"PATH": str(self.bin) + ":/usr/bin:/bin", "HOME": "/home/tester", + "XDG_CURRENT_DESKTOP": "Hyprland", **environment}, + capture_output=True, text=True, timeout=30) + + def helper_args(self, root): + path = root / "opt/mesa-m3/libexec/args" + return path.read_text().split() if path.exists() else None + + def test_supported_boards_are_admitted_without_opt_out(self): + for board, chip in (("j613", "t8122"), ("j615", "t8122"), ("j516s", "t6030")): + with self.subTest(board=board): + root = self.make_root(board) + result = self.admit(root) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + self.assertIn(f"({chip}, apple,{board})", result.stdout) + self.assertEqual(self.helper_args(root), ["/dev/dri/renderD128", chip]) + + def test_file_opt_out_still_admits(self): + root = self.make_root("j615") + (root / "etc/mesa-m3").mkdir(parents=True) + (root / "etc/mesa-m3/disable").touch() + self.assertEqual(self.admit(root).returncode, 0) + + def test_unknown_or_ambiguous_boards_are_refused(self): + for board in ("apple,j504", "apple,j615s", "apple,j516s", "apple,j614s"): + with self.subTest(board=board): + root = self.make_root(board) + self.assertEqual(self.admit(root).returncode, 1) + self.assertIsNone(self.helper_args(root)) + root = self.make_root("j615") + (root / "proc/device-tree/compatible").write_bytes(b"apple,j613\0apple,j615\0apple,t8122\0") + self.assertEqual(self.admit(root).returncode, 1) + self.assertIsNone(self.helper_args(root)) + + def test_j615_without_its_nodes_is_refused(self): + for node, display in ((False, True), (True, False)): + with self.subTest(node=node, display=display): + root = self.make_root("j615", node=node, display=display) + self.assertEqual(self.admit(root).returncode, 1) + self.assertIsNone(self.helper_args(root)) + shutil.rmtree(root) + + def test_kernel_command_line_refuses(self): + for flag in ("mesa_m3=off", "nomodeset"): + with self.subTest(flag=flag): + root = self.make_root("j615") + (root / "proc/cmdline").write_text(f"root=UUID=x rw {flag} quiet\n") + self.assertEqual(self.admit(root).returncode, 1) + self.assertIsNone(self.helper_args(root)) + shutil.rmtree(root) + + def test_helper_refusal_is_final(self): + root = self.make_root("j615") + (root / "opt/mesa-m3/libexec/refuse").touch() + result = self.admit(root) + self.assertEqual(result.returncode, 1) + self.assertEqual(result.stdout, "") + self.assertEqual(self.helper_args(root), ["/dev/dri/renderD128", "t8122"]) + + def test_kernel_fault_and_other_desktops_refuse(self): + root = self.make_root("j615") + self.assertEqual(self.admit(root, FAKE_FAULT="asahi: M3 G15G: firmware crashed").returncode, 1) + self.assertEqual(self.admit(root, XDG_CURRENT_DESKTOP="sway").returncode, 1) + self.assertIsNone(self.helper_args(root)) + + def test_native_25g83_profile_stays_on_its_gate_without_opt_out(self): + root = self.make_root("j613") + (root / "etc/mesa-m3").mkdir(parents=True) + (root / "etc/mesa-m3/t8122-profile").write_text("j613-25g83-hal200\n") + self.assertEqual(self.admit(root).returncode, 1) + (root / "etc/mesa-m3/disable").touch() + self.assertEqual(self.admit(root).returncode, 0) + root = self.make_root("j615") + handoff = root / "sys/devices/platform/gpu0/of_node/apple,j613-25g83-gpu-handoff" + handoff.write_bytes(b"\0\0\0\1") + self.assertEqual(self.admit(root).returncode, 1) + (root / "etc/mesa-m3").mkdir(parents=True) + (root / "etc/mesa-m3/disable").touch() + self.assertEqual(self.admit(root).returncode, 0) + + def test_unconfirmed_attempt_of_this_boot_refuses(self): + root = self.make_root("j615") + (root / "proc/sys/kernel/random").mkdir(parents=True) + (root / "proc/sys/kernel/random/boot_id").write_text("boot-a\n") + state = root / "home/tester/.local/state/mesa-m3" + state.mkdir(parents=True) + (state / "attempt").write_text("schema=aurora.mesa-m3-attempt/2\nboot_id=boot-a\n" + "session_id=5\nhook_run_id=run-1\n") + self.assertEqual(self.admit(root).returncode, 1) + (state / "session-ok").write_text("schema=aurora.mesa-m3-session-ok/1\nboot_id=boot-a\n" + "session_id=5\nhook_run_id=run-1\n") + self.assertEqual(self.admit(root).returncode, 0) + + if __name__ == "__main__": unittest.main() From 3ab3121743b4b058a7c6609efe52e029060aa9cc Mon Sep 17 00:00:00 2001 From: Marcelo Alcantara Date: Mon, 26 Oct 2026 11:07:00 +1000 Subject: [PATCH 2/3] mesa-m3: keep active /opt sessions and record the system attempt first Automatic system-Mesa admission now runs only where the /opt/mesa-m3 gate ends inactive, the cases that used to fall back to software. An active /opt/mesa-m3 is never replaced; moving those sessions would need a maintainer opt-in. The file opt-out path is unchanged. The automatic path writes its attempt before it commits the admission. If the attempt cannot be written, no unconfirmed login could send the next one to software, so the gate's decision and fallback stay and the state detail says the attempt was not recorded. Document recovery for automatic sessions (remove the attempt file, or boot with mesa_m3=off) separately from file opt-out sessions, and qualify the install message. Refresh the hook checksum. --- tools/aurora-sep/recipes/mesa-m3/PKGBUILD | 6 +- .../recipes/mesa-m3/mesa-m3-session-env | 87 +++++++++++-------- .../recipes/mesa-m3/mesa-m3.install | 5 +- .../recipes/mesa-m3/tests/SYSTEM-HANDOFF.md | 46 ++++++---- .../recipes/mesa-m3/tests/test-session-env.sh | 58 +++++++++---- 5 files changed, 127 insertions(+), 75 deletions(-) diff --git a/tools/aurora-sep/recipes/mesa-m3/PKGBUILD b/tools/aurora-sep/recipes/mesa-m3/PKGBUILD index 0a92f43ea8a255..d00a5bffee226a 100644 --- a/tools/aurora-sep/recipes/mesa-m3/PKGBUILD +++ b/tools/aurora-sep/recipes/mesa-m3/PKGBUILD @@ -5,8 +5,8 @@ # the exact j613-25g83-hal200 token, firmware26.6.2 handoff and runtime HAL200/USC3. # The J615 shares that profile experimentally: the session hook admits it only # with /chosen asahi,j615-25g83-experimental="1" (share/mesa-m3/native25-boards). -# In Hyprland sessions on J613, J615 and J516S the hook prefers a system Mesa in -# /usr that mesa-m3-system-check admits; /opt/mesa-m3 then stays inactive. +# Where /opt/mesa-m3 is not used, Hyprland sessions on J613, J615 and J516S keep +# a system Mesa in /usr that mesa-m3-system-check admits, not software rendering. # The package builds both sources sequentially and checks compiler definitions, # generated kernels, queue sandbox behavior, TLS loading and native MRT clears. # No profile selection substitutes another Mac's firmware or calibration. @@ -87,7 +87,7 @@ sha256sums=('7ed24969985dce91f089d34dbe1c55cbcb8c95765e3e5bc0f268d27fbead2222' '10abda78df16e650a77443f50cc46a15e418c42d4d9680c6a61616b232c9bdef' '6c98fab6f9d70449bd7f5258c34eac773836908e060ce5bef86dc5b0ce595e61' '826016665cd0424a614b67add921c0c79c3c698a4d104ea5712518449b75f27c' - 'bbf0bea6cb9310fd0e3f890020c14783f490761451355951e2d965800da8c1e5' + '8abf5fcf736b24dad81ffe6a10e137ded8727c004f17d9d723c27e04141b717f' '6144af8b2e0f04ea615e8c4149e69c45ac49e630c46b2a58f0d1095d977798b2' '530948fc1a124de21c030a497e2ef5351742ba4ad4653484fe30fd1697ad7141' '644cf4a5ab57b7badec75843e2899b68d41490c4851d6885227180d6090c25ea' diff --git a/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-env b/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-env index 94c9a66616c19b..c5ec443d47a23b 100644 --- a/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-env +++ b/tools/aurora-sep/recipes/mesa-m3/mesa-m3-session-env @@ -71,21 +71,22 @@ # # A system Mesa on /usr that drives the GPU natively is the exception, on a # J613 or J615 (T8122, the 13- and 15-inch M3 Air) or a J516S (T6030) in a -# Hyprland session. It is checked after the gate above, whatever the gate -# decided, except not-supported and user-setup (the session's own Mesa setup -# still decides). When the node, display, kernel-log, user-setup -# (--system-provider) and same-boot attempt checks pass and -# mesa-m3-system-check admits /usr, the session keeps the system Mesa -# (fallback=none) and /opt/mesa-m3 stays inactive, even where the gate would -# have used it: the system Mesa supersedes the legacy build. The whole check -# runs in its own sh and is refused after 20 seconds. mesa_m3=off and -# nomodeset refuse it, and any refusal leaves the gate's decision and its -# fallback exactly as above. Its state: +# Hyprland session. It is checked only where the gate above ends inactive, +# except not-supported and user-setup (the session's own Mesa setup still +# decides): an active /opt/mesa-m3 is never replaced. When the node, display, +# kernel-log, user-setup (--system-provider) and same-boot attempt checks pass +# and mesa-m3-system-check admits /usr, the session keeps the system Mesa +# (fallback=none) instead of the software fallback. The whole check runs in +# its own sh and is refused after 20 seconds. mesa_m3=off and nomodeset refuse +# it, and any refusal leaves the gate's decision and its fallback exactly as +# above. Its state: # system-mesa no opt-out: the system Mesa was admitted automatically. -# Like active, it records an attempt, so a login whose -# compositor is not confirmed (mesa-m3-session-confirm) +# Its attempt is recorded first, as for active, so a login +# whose compositor is not confirmed (mesa-m3-session-confirm) # turns the next login of this boot into the software -# fallback instead of a login loop. +# fallback instead of a login loop. When the attempt +# cannot be written, the gate's decision and fallback +# stay, and the detail says the attempt was not recorded. # opt-out a file opt-out (/etc/mesa-m3/disable or the user's # mesa-m3/disable) whose system Mesa was admitted: as # before, no new attempt. @@ -490,18 +491,24 @@ _mm3_system_admit_bounded() { sh "$_mm3_self" "${1:-}" "$_mm3_p" } -# _mm3_state ROOT DECISION REASON DETAIL FALLBACK: write the state file and the journal -# line. A value holding a newline or carriage return is refused. -_mm3_state() { +# _mm3_login_ids ROOT: this login's uid, session id and boot id, as the state +# file and the attempt record them. +_mm3_login_ids() { _mm3_uid=$(id -u) _mm3_sid=${XDG_SESSION_ID:-} [ -n "$_mm3_sid" ] || _mm3_sid=$(loginctl show-user "$_mm3_uid" -p Display --value 2>/dev/null) + _mm3_boot= + [ -r "$1/proc/sys/kernel/random/boot_id" ] && IFS= read -r _mm3_boot < "$1/proc/sys/kernel/random/boot_id" +} + +# _mm3_state ROOT DECISION REASON DETAIL FALLBACK: write the state file and the journal +# line. A value holding a newline or carriage return is refused. +_mm3_state() { + _mm3_login_ids "$1" _mm3_inv=$(systemctl show -p InvocationID --value "user@$_mm3_uid.service" 2>/dev/null) _mm3_ver= [ -r "$1$_mm3_p/share/mesa-m3/package-version" ] && IFS= read -r _mm3_ver < "$1$_mm3_p/share/mesa-m3/package-version" - _mm3_boot= - [ -r "$1/proc/sys/kernel/random/boot_id" ] && IFS= read -r _mm3_boot < "$1/proc/sys/kernel/random/boot_id" _mm3_cr=$(printf '\r') _mm3_nl=' ' @@ -563,16 +570,29 @@ mesa_m3_session_env() { [ -r /proc/sys/kernel/random/uuid ] && IFS= read -r _mm3_run < /proc/sys/kernel/random/uuid _mm3_run=${_mm3_run:-unknown} [ "${_mm3_decision%% *}" = not-supported ] || export MESA_M3_HOOK_RUN_ID="$_mm3_run" - # The system Mesa first, whatever the gate decided (see the header), except - # where this is neither a T6030 nor a T8122 or the session's own Mesa setup - # chose. A refusal leaves the gate's decision and fallback as they were. + # The system Mesa only where the gate ends inactive (see the header): never + # instead of an active /opt/mesa-m3, and not where this is neither a T6030 + # nor a T8122 or the session's own Mesa setup chose. A refusal leaves the + # gate's decision and fallback as they were. _mm3_admitted=0 - case ${_mm3_decision%% *} in - not-supported | user-setup) ;; - *) - _mm3_sys=$(_mm3_system_admit_bounded "${1:-}") - [ "$?" -ne 0 ] || _mm3_admitted=1 ;; - esac + _mm3_extra= + if [ "$_mm3_on" -ne 0 ]; then + case ${_mm3_decision%% *} in + not-supported | user-setup) ;; + *) + _mm3_sys=$(_mm3_system_admit_bounded "${1:-}") + [ "$?" -ne 0 ] || _mm3_admitted=1 ;; + esac + fi + # Automatic: the attempt comes first. Without it no unconfirmed login could + # send the next one to software, so a failed write keeps the fallback. + if [ "$_mm3_admitted" -eq 1 ] && [ "${_mm3_decision%% *}" != opt-out ]; then + _mm3_login_ids "${1:-}" + if ! _mm3_attempt "${1:-}"; then + _mm3_admitted=0 + _mm3_extra="; system Mesa admitted, but its attempt not recorded in ${XDG_STATE_HOME:-$HOME/.local/state}/mesa-m3, so this login keeps the fallback" + fi + fi if [ "$_mm3_admitted" -eq 1 ]; then # Only the pair this hook itself exports. Anything else in the # environment, including a user's own LIBGL_ALWAYS_SOFTWARE without @@ -585,11 +605,10 @@ mesa_m3_session_env() { # A file opt-out: as before, no new attempt. _mm3_state "${1:-}" inactive opt-out "${_mm3_decision#* }; $_mm3_sys" none else - # Automatic: recorded like an active login, so that a compositor - # that is not confirmed sends the next login of this boot to the - # software fallback (previous-failed) instead of looping. + # Automatic: its attempt is already recorded, so a compositor that + # is not confirmed sends the next login of this boot to the + # software fallback instead of looping. _mm3_state "${1:-}" inactive system-mesa "$_mm3_sys; the /opt/mesa-m3 gate said: $_mm3_decision" none - _mm3_attempt "${1:-}" fi elif [ "$_mm3_on" -eq 0 ]; then case ":${LD_LIBRARY_PATH:-}:" in @@ -627,7 +646,7 @@ mesa_m3_session_env() { export MESA_M3_FALLBACK=software _mm3_fb=software fi - _mm3_state "${1:-}" inactive "${_mm3_decision%% *}" "${_mm3_decision#* }" "$_mm3_fb" + _mm3_state "${1:-}" inactive "${_mm3_decision%% *}" "${_mm3_decision#* }$_mm3_extra" "$_mm3_fb" fi } @@ -635,7 +654,7 @@ if [ -z "${MESA_M3_SESSION_ENV_NO_RUN:-}" ]; then mesa_m3_session_env unset -f mesa_m3_gate mesa_m3_session_env _mm3_init _mm3_state _mm3_node_open _mm3_attempt \ _mm3_soc_of _mm3_25g83_board _mm3_native_display _mm3_kernel_log \ - _mm3_legacy_attempt_clear _mm3_system_admit _mm3_system_admit_bounded + _mm3_legacy_attempt_clear _mm3_system_admit _mm3_system_admit_bounded _mm3_login_ids fi unset _mm3_root _mm3_p _mm3_nodes _mm3_node _mm3_n _mm3_drv _mm3_dcp _mm3_c _mm3_dev _mm3_f _mm3_v \ _mm3_val _mm3_ifs _mm3_d _mm3_out _mm3_rc _mm3_decision _mm3_on \ @@ -643,4 +662,4 @@ unset _mm3_root _mm3_p _mm3_nodes _mm3_node _mm3_n _mm3_drv _mm3_dcp _mm3_c _mm3 _mm3_body _mm3_kv _mm3_dir _mm3_tmp _mm3_fb _mm3_run _mm3_att _mm3_ok _mm3_faults _mm3_ign _mm3_igd \ _mm3_soc _mm3_name _mm3_dt _mm3_base _mm3_profile _mm3_requested _mm3_files _mm3_gate_ifs _mm3_board _mm3_note \ _mm3_klog _mm3_sys _mm3_admitted _mm3_detail _mm3_helper _mm3_chip _mm3_identity _mm3_socs _mm3_cmdline \ - _mm3_self _mm3_auto + _mm3_self _mm3_auto _mm3_extra diff --git a/tools/aurora-sep/recipes/mesa-m3/mesa-m3.install b/tools/aurora-sep/recipes/mesa-m3/mesa-m3.install index 0a80099acc7354..f658e212a3d0ed 100644 --- a/tools/aurora-sep/recipes/mesa-m3/mesa-m3.install +++ b/tools/aurora-sep/recipes/mesa-m3/mesa-m3.install @@ -9,8 +9,9 @@ post_install() { echo "mesa-m3: takes effect at the next login to a uwsm session on an M3 Pro (T6030)," echo "mesa-m3: and on an M3 (T8122) only with /etc/mesa-m3/t8122-gpu-experiment: its GPU" echo "mesa-m3: is experimental (the installer writes the file for --m3-gpu-experiment)." - echo "mesa-m3: In a Hyprland session on a 13- or 15-inch M3 Air (J613, J615) or a J516S," - echo "mesa-m3: a system Mesa in /usr that drives the GPU is preferred over /opt/mesa-m3." + echo "mesa-m3: Where /opt/mesa-m3 is not used, a Hyprland session on a 13- or 15-inch" + echo "mesa-m3: M3 Air (J613, J615) or a J516S keeps a system Mesa in /usr that drives" + echo "mesa-m3: the GPU, instead of rendering in software; no marker file is needed." echo "mesa-m3: It does nothing on other machines. Each login's decision is in" echo "mesa-m3: /run/user//mesa-m3-session.state and the journal (tag mesa-m3)." _render_note diff --git a/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md b/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md index 0465b9f8784696..d5e3e9b191fec3 100644 --- a/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md +++ b/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md @@ -39,31 +39,39 @@ the hook and in `mesa-m3-system-check`. A root that names both Airs, or one of them twice, is refused. The file opt-out is no longer required. In a Hyprland session on J613, J615 -or J516S, the hook runs the same bounded admission after its `/opt/mesa-m3` -gate, whatever that gate decided, except `not-supported` and `user-setup` -(the session's own Mesa setup still decides). When it passes, the session -keeps system Mesa even where the gate would have used `/opt/mesa-m3`: the -system Mesa supersedes the legacy build. The state is `inactive`, -`reason=system-mesa`, `fallback=none`; its detail names the admitted board and -the gate's own decision. Any refusal leaves the gate's decision and fallback -exactly as before. - -An automatic admission writes the same attempt as an `/opt/mesa-m3` login. -`mesa-m3-session-confirm` confirms it in the same way, and an unconfirmed -login sends the next login of that boot to the software fallback, instead of -a login loop. The file opt-out path still writes no attempt. +or J516S, the hook runs the same bounded admission where its `/opt/mesa-m3` +gate ends inactive, except `not-supported` and `user-setup` (the session's +own Mesa setup still decides). An active `/opt/mesa-m3` is never replaced: +moving those sessions to system Mesa would need a maintainer opt-in. When +admission passes, the session keeps system Mesa instead of the software +fallback. The state is `inactive`, `reason=system-mesa`, `fallback=none`; its +detail names the admitted board and the gate's own decision. Any refusal +leaves the gate's decision and fallback exactly as before. + +An automatic admission records its attempt first, as an `/opt/mesa-m3` login +does. `mesa-m3-session-confirm` confirms it in the same way, and an +unconfirmed login sends the next login of that boot to the software fallback +instead of a login loop. When the attempt cannot be written, the admission +is dropped: the gate's decision and fallback stay, and the detail says the +attempt was not recorded. The file opt-out path still writes no attempt. Without a file opt-out, the 25G83 native profile stays on its own gate: an `/etc/mesa-m3/t8122-profile` selection, or a render node with `apple,j613-25g83-gpu-handoff`, is not admitted automatically. The helper proves only that the system EGL device and a context open. It does -not allocate GBM buffers, render or present. A system-Mesa login that passes -admission and then fails later is not recorded: this path writes no attempt, -so the legacy success-marker check cannot see it, and the next login is -admitted again. Recover with `mesa_m3=off` on the kernel command line or by -removing the opt-out file. Separate system-Mesa attempt and success markers -would make that recovery automatic. +not allocate GBM buffers, render or present. + +Recovery after a system-Mesa login that passes admission and then fails: + +- Automatic (`reason=system-mesa`): the unconfirmed attempt sends the next + login of the same boot to software. To retry the GPU in that boot, remove + `${XDG_STATE_HOME:-~/.local/state}/mesa-m3/attempt` and log in again; a + reboot also clears it. To stay in software, boot with `mesa_m3=off`. +- File opt-out (`reason=opt-out`): this path writes no attempt, so the + success-marker check cannot see the failure and the next login is admitted + again. Recover with `mesa_m3=off` on the kernel command line or by removing + the opt-out file. Run the host checks from this directory (a writable TMPDIR may be needed): diff --git a/tools/aurora-sep/recipes/mesa-m3/tests/test-session-env.sh b/tools/aurora-sep/recipes/mesa-m3/tests/test-session-env.sh index df4064e876a410..c77df77f6afe52 100755 --- a/tools/aurora-sep/recipes/mesa-m3/tests/test-session-env.sh +++ b/tools/aurora-sep/recipes/mesa-m3/tests/test-session-env.sh @@ -866,9 +866,10 @@ sed -i 's/boot_id=boot-a/boot_id=old-boot/' "$HATT" handoff "$HR" none yes # Automatic system Mesa: no opt-out file. On J613, J615 and J516S a Hyprland -# session keeps the system Mesa when every check passes, whatever the -# /opt/mesa-m3 gate decided (reason system-mesa, fallback none, an attempt for -# the loop guard). Every refusal leaves the gate's decision and fallback. +# session whose /opt/mesa-m3 gate ends inactive keeps the system Mesa when +# every check passes (reason system-mesa, fallback none, an attempt recorded +# first for the loop guard). An active gate is never replaced. Every refusal +# leaves the gate's decision and fallback. auto_root() { # NAME air|air15|pro _ar=$(system_root "auto-$1" "$2") rm "$_ar/etc/mesa-m3/disable" @@ -938,18 +939,21 @@ auto_fresh "$AJ615" # A user's own LIBGL_ALWAYS_SOFTWARE is a user setup (below). expect_auto "J615 clears its own leftover pair" "$SYS" "$AJ615" LIBGL_ALWAYS_SOFTWARE=1 MESA_M3_FALLBACK=software auto_fresh "$AJ615" -# The system Mesa wins where the gate would use /opt/mesa-m3: the J613 opted -# in to the experiment, and the J516S. +# An active /opt/mesa-m3 is never replaced: the J613 opted in to the +# experiment and the J516S keep it, and the helper is not run. +LEGACY='active active none helper=no attempt=new sw= legacy=/opt/mesa-m3' mkdir -p "$AJ613/etc/mesa-m3"; : > "$AJ613/etc/mesa-m3/t8122-gpu-experiment" auto_fresh "$AJ613" -expect_auto "J613 opted in: system wins over /opt" "$SYS" "$AJ613" -grep -q '^detail=.*the /opt/mesa-m3 gate said: active T8122' "$T/auto.out" && - echo "ok auto J613 opted in: the state keeps the gate's active verdict" || { echo "FAIL auto J613 opted-in detail"; fail=1; } +expect_auto "J613 opted in: /opt stays active" "$LEGACY" "$AJ613" rm "$AJ613/etc/mesa-m3/t8122-gpu-experiment"; auto_fresh "$AJ613" -AJ516=$(auto_root j516s pro) -expect_auto "J516S: system wins over /opt" "$SYS" "$AJ516" -grep -q '^detail=system Mesa on /usr admits /dev/dri/renderD128 (t6030, apple,j516s);.*gate said: active T6030' "$T/auto.out" && - echo "ok auto J516S: the state names t6030, apple,j516s and the gate's active verdict" || { echo "FAIL auto J516S detail"; fail=1; } +AJ516A=$(auto_root j516s-active pro) +expect_auto "J516S: /opt stays active" "$LEGACY" "$AJ516A" +# A J516S whose /opt/mesa-m3 gate ends inactive (incomplete prefix) gets the +# system Mesa instead of software. +AJ516=$(auto_root j516s pro); rm "$AJ516/opt/mesa-m3/lib/libvulkan_asahi.so" +expect_auto "J516S, /opt incomplete" "$SYS" "$AJ516" +grep -q '^detail=system Mesa on /usr admits /dev/dri/renderD128 (t6030, apple,j516s);.*gate said: incomplete-prefix' "$T/auto.out" && + echo "ok auto J516S: the state names t6030, apple,j516s and the gate's verdict" || { echo "FAIL auto J516S detail"; fail=1; } auto_fresh "$AJ516" # Exactly the run id exported: none of /opt/mesa-m3's variables, no fallback. exs=$(env -i PATH="$T/bin:/usr/bin:/bin" HOME="$T/home" FAKE_LOG="$T/log" XDG_CURRENT_DESKTOP=Hyprland LD_LIBRARY_PATH=/x "$TSH" -c ' @@ -962,18 +966,38 @@ exs=$(env -i PATH="$T/bin:/usr/bin:/bin" HOME="$T/home" FAKE_LOG="$T/log" XDG_CU auto_fresh "$AJ516" # No login loop: an automatic login whose compositor was not confirmed sends # the next login of this boot to the software fallback; a matching success -# marker lets the system Mesa in again; another boot's attempt does not count. +# marker lets the system Mesa in again, replacing the older confirmed attempt; +# another boot's attempt does not count. expect_auto "J615 first login" "$SYS" "$AJ615" _arun=$(sed -n 's/^run=//p' "$T/auto.out") expect_auto "J615 after an unconfirmed login" "inactive experimental software helper=no attempt=unchanged sw=1 legacy=" "$AJ615" printf 'schema=aurora.mesa-m3-session-ok/1\nboot_id=boot-a\nsession_id=5\nhook_run_id=%s\n' "$_arun" > "$AJ615$T/home/.local/state/mesa-m3/session-ok" -expect_auto "J615 after a confirmed login" "$SYS" "$AJ615" +expect_auto "J615 replaces an older confirmed attempt" "$SYS" "$AJ615" +grep -qx "hook_run_id=$_arun" "$AJ615$T/home/.local/state/mesa-m3/attempt" && + { echo "FAIL auto J615: the older attempt was kept"; fail=1; } || echo "ok auto J615: the older confirmed attempt is gone" echo boot-b > "$AJ615/proc/sys/kernel/random/boot_id" expect_auto "J615 after another boot's unconfirmed login" "$SYS" "$AJ615" echo boot-a > "$AJ615/proc/sys/kernel/random/boot_id"; auto_fresh "$AJ615" expect_auto "J516S first login" "$SYS" "$AJ516" -expect_auto "J516S after an unconfirmed login" "inactive previous-failed software helper=no attempt=unchanged sw=1 legacy=" "$AJ516" +expect_auto "J516S after an unconfirmed login" "inactive incomplete-prefix software helper=no attempt=unchanged sw=1 legacy=" "$AJ516" auto_fresh "$AJ516" +# The attempt comes first: when it cannot be written, the admission is dropped, +# the gate's software fallback stays and the state says why. +R=$(auto_root j615-attempt-fails air15); AD="$R$T/home/.local/state/mesa-m3" +mkdir -p "$AD"; chmod 500 "$AD" +if [ -w "$AD" ]; then echo "skip attempt-fails (running as root)"; else + expect_auto "J615 attempt not writable" "inactive experimental software helper=yes attempt=none sw=1 legacy=" "$R" + grep -q '^detail=the M3 (T8122) GPU is experimental.*; system Mesa admitted, but its attempt not recorded in ' "$T/auto.out" && + echo "ok auto J615: the state says the attempt was not recorded" || { echo "FAIL auto J615 attempt-fails detail: $(grep '^detail=' "$T/auto.out")"; fail=1; } + check_state "attempt not recorded (J615)" inactive experimental software "$(state "$R" 5)" + # An older confirmed attempt in that directory stays as it was. + chmod 700 "$AD" + printf 'schema=aurora.mesa-m3-attempt/2\nboot_id=boot-a\nsession_id=5\nhook_run_id=old-run\n' > "$AD/attempt" + printf 'schema=aurora.mesa-m3-session-ok/1\nboot_id=boot-a\nsession_id=5\nhook_run_id=old-run\n' > "$AD/session-ok" + chmod 500 "$AD" + expect_auto "J615 attempt not writable over a confirmed one" "inactive experimental software helper=yes attempt=unchanged sw=1 legacy=" "$R" +fi +chmod 700 "$AD" # Fail closed: each refusal keeps today's decision and fallback exactly. GATEX='inactive experimental software helper=no attempt=none sw=1 legacy=' expect_auto "J615 mesa_m3=off" "inactive opt-out software helper=no attempt=none sw=1 legacy=" "$(R=$(auto_root j615-off air15); echo 'root=UUID=x rw mesa_m3=off quiet' > "$R/proc/cmdline"; echo "$R")" @@ -985,8 +1009,8 @@ R=$(auto_root j615-helper-stalls air15); : > "$R/opt/mesa-m3/libexec/system-chec expect_auto "J615 helper times out" "inactive experimental software helper=yes attempt=none sw=1 legacy=" "$R" R=$(auto_root j615-helper-missing air15); rm "$R/opt/mesa-m3/libexec/mesa-m3-system-check" expect_auto "J615 helper missing" "$GATEX" "$R" -R=$(auto_root j516-helper-fails pro); : > "$R/opt/mesa-m3/libexec/system-check.fail" -expect_auto "J516S helper refuses: /opt as before" "active active none helper=yes attempt=new sw= legacy=/opt/mesa-m3" "$R" +R=$(auto_root j516-helper-fails pro); : > "$R/opt/mesa-m3/libexec/system-check.fail"; rm "$R/opt/mesa-m3/lib/libvulkan_asahi.so" +expect_auto "J516S helper refuses" "inactive incomplete-prefix software helper=yes attempt=none sw=1 legacy=" "$R" # The J615 without its nodes: no render node (GPU not started), no display, # another GPU, two nodes. expect_auto "J615 without a render node" "inactive experimental none helper=no attempt=none sw= legacy=" \ From 1c41eb8b971953666669fb9d41f6ca1ddf9f50c7 Mon Sep 17 00:00:00 2001 From: Marcelo Alcantara Date: Mon, 26 Oct 2026 11:11:54 +1000 Subject: [PATCH 3/3] mesa-m3: give opt-out recovery that cannot retry the same failing Mesa Removing the opt-out file now restores automatic selection, which may admit the failing system Mesa again; mesa_m3=off is the immediate way back to software. A reboot makes the earlier attempt inapplicable rather than deleting it. --- tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md b/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md index d5e3e9b191fec3..fdb6f87d6b7f83 100644 --- a/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md +++ b/tools/aurora-sep/recipes/mesa-m3/tests/SYSTEM-HANDOFF.md @@ -67,11 +67,13 @@ Recovery after a system-Mesa login that passes admission and then fails: - Automatic (`reason=system-mesa`): the unconfirmed attempt sends the next login of the same boot to software. To retry the GPU in that boot, remove `${XDG_STATE_HOME:-~/.local/state}/mesa-m3/attempt` and log in again; a - reboot also clears it. To stay in software, boot with `mesa_m3=off`. + reboot makes the previous attempt inapplicable. To stay in software, boot + with `mesa_m3=off`. - File opt-out (`reason=opt-out`): this path writes no attempt, so the success-marker check cannot see the failure and the next login is admitted - again. Recover with `mesa_m3=off` on the kernel command line or by removing - the opt-out file. + again. To force software rendering immediately, boot with `mesa_m3=off`. + Removing the opt-out file restores normal selection, which may retry the + system Mesa automatically. Run the host checks from this directory (a writable TMPDIR may be needed):