Files
omarchycn/test/shell.d/nvidia-kms-migration-test.sh
ecd57bceee Drop the kms hook when the proprietary NVIDIA driver handles early KMS (#6791)
* Drop the kms hook when the proprietary NVIDIA driver handles early KMS

install/hardware/nvidia.sh early-loads nvidia_drm (modeset=1) for early KMS,
but HOOKS still carried the kms hook, so autodetect pulled nouveau and
~100 MB of its GSP firmware into every initramfs for a driver that never
runs. On a Limine UKI setup that meant a 256 MB image where ~144 MB is
normal, doubled again by the fallback history on /boot.

Filter kms out of HOOKS when nvidia_drm is in MODULES (nvidia.conf sorts
before this drop-in) and every PCI display controller is NVIDIA. Hybrid
systems keep kms so the iGPU retains early KMS at the LUKS prompt.

Verified on an RTX 4090 (nvidia-open-dkms 610.57.04): UKI shrinks
256,183,296 -> 144,066,048 bytes, nouveau and its firmware gone, the
nvidia-utils GSP blobs and all four nvidia modules retained, Plymouth
still owns the LUKS prompt via nvidia_drm.

Fixes #6790

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Address review: quote literals, harden PCI detection, add shell tests

Quote fixed string literals in the [[ ]] comparisons per AGENTS.md, and
read the PCI tree through OMARCHY_PCI_DEVICES_PATH, the same seam
bin/omarchy-hw-nvidia already uses.

Require a positively identified NVIDIA display controller before dropping
kms: an empty or unreadable PCI tree previously counted as "no non-NVIDIA
GPU" and would have dropped the hook. Unexpected trees now keep kms.

Cover the conditional in test/shell.d/nvidia-kms-hook-test.sh: nvidia-only,
hybrid, no nvidia_drm, MODULES unset under set -u, audio-function-only,
empty tree, and a device directory missing its sysfs attributes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Treat unreadable PCI devices as inconclusive and make the test hermetic

A device whose class/vendor attributes cannot be read could be another
GPU, so skipping it let a readable NVIDIA GPU beside it drop kms without
having verified the whole tree. Count it as a non-NVIDIA sighting so kms
stays, and cover the mixed case in the test.

The test also sourced the host's /etc/vconsole.conf under set -u, where a
valid KEYMAP-only file makes the XKBLAYOUT expansion fail in the subshell
and ties the result to the machine running it. Predefine XKBLAYOUT and
FILES before sourcing the config.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Rebuild existing initramfses once the kms hook no longer applies

The settings package deploys the new omarchy_hooks.conf conditional, but
nothing rebuilds the initramfs when only a mkinitcpio drop-in changes, so
existing NVIDIA-only installs would carry nouveau's ~100 MB of GSP
firmware until their next kernel update. Following the precedent of
1784476564, add a migration that rebuilds via limine-mkinitcpio — once
per machine, and only where evaluating the installed drop-ins shows the
conditional actually dropped kms, so hybrid machines, non-NVIDIA
machines, and user-edited configs are left alone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Document the mid-sourcing MODULES caveat in the kms conditional

A later-sorting drop-in that resets MODULES outright (as
surface_device_modules.conf does) would strip nvidia_drm after kms was
already dropped. Every machine Omarchy writes such a file for is hybrid
Intel and keeps kms through the PCI scan, but that is worth stating so
the invariant is not broken by accident.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: David Heinemeier Hansson <david@hey.com>
2026-08-13 10:48:18 +02:00

132 lines
3.7 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
migration="$ROOT/migrations/1786605598.sh"
packaged_hooks="$ROOT/etc/mkinitcpio.conf.d/omarchy_hooks.conf"
test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT
stub_bin="$test_tmp/bin"
calls="$test_tmp/calls.log"
mkdir -p "$stub_bin"
: >"$calls"
cat >"$stub_bin/omarchy-cmd-present" <<'SH'
#!/bin/bash
(( ${LIMINE_MKINITCPIO_INSTALLED:-1} == 1 ))
SH
cat >"$stub_bin/sudo" <<'SH'
#!/bin/bash
printf 'sudo' >>"$TEST_LOG"
printf '\t%s' "$@" >>"$TEST_LOG"
printf '\n' >>"$TEST_LOG"
"$@"
SH
cat >"$stub_bin/limine-mkinitcpio" <<'SH'
#!/bin/bash
echo 'limine-mkinitcpio' >>"$TEST_LOG"
SH
chmod +x "$stub_bin"/*
hooks_conf="$test_tmp/omarchy_hooks.conf"
nvidia_conf="$test_tmp/nvidia.conf"
rebuild_marker="$test_tmp/rebuild-complete"
cp "$packaged_hooks" "$hooks_conf"
echo 'MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)' >"$nvidia_conf"
# Each argument is a PCI device as "vendor:class", in sysfs's own format.
write_pci_devices() {
rm -rf "$test_tmp/devices"
mkdir -p "$test_tmp/devices"
local index=0
local spec
for spec in "$@"; do
local slot
slot=$(printf '0000:%02x:00.0' "$index")
mkdir -p "$test_tmp/devices/$slot"
printf '%s\n' "${spec%%:*}" >"$test_tmp/devices/$slot/vendor"
printf '%s\n' "${spec##*:}" >"$test_tmp/devices/$slot/class"
index=$((index + 1))
done
}
run_migration() {
PATH="$stub_bin:$PATH" \
TEST_LOG="$calls" \
OMARCHY_MKINITCPIO_HOOKS_CONF="$hooks_conf" \
OMARCHY_MKINITCPIO_NVIDIA_CONF="$nvidia_conf" \
OMARCHY_KMS_REBUILD_MARKER="$rebuild_marker" \
OMARCHY_PCI_DEVICES_PATH="$test_tmp/devices" \
bash -euo pipefail "$migration" >/dev/null
}
# NVIDIA-only machine with the proprietary driver: the packaged conditional
# drops kms, so the stale initramfs must be rebuilt once.
write_pci_devices 0x10de:0x030000
run_migration
grep -Fxq 'limine-mkinitcpio' "$calls" ||
fail "an NVIDIA-only machine rebuilds its initramfs"
[[ -f $rebuild_marker ]] || fail "the rebuild records the machine-wide repair"
pass "migration rebuilds the initramfs on an NVIDIA-only machine"
: >"$calls"
run_migration
[[ ! -s $calls ]] || fail "a recorded rebuild is not repeated" "$(cat "$calls")"
pass "migration is machine-idempotent across users"
rm -f "$rebuild_marker"
: >"$calls"
run_migration
grep -Fxq 'limine-mkinitcpio' "$calls" ||
fail "an interrupted rebuild is retried"
pass "migration retries an interrupted rebuild"
# Hybrid machine: the conditional keeps kms, so the initramfs already matches.
write_pci_devices 0x1002:0x030000 0x10de:0x030200
rm -f "$rebuild_marker"
: >"$calls"
run_migration
[[ ! -s $calls ]] || fail "a hybrid machine is left alone" "$(cat "$calls")"
[[ ! -e $rebuild_marker ]] || fail "an untouched machine is not marked as repaired"
pass "migration skips a hybrid machine that keeps kms"
# A user-edited hooks conf predating the conditional (the packaged update sits
# in a .pacnew) still carries kms unconditionally: nothing to rebuild for.
write_pci_devices 0x10de:0x030000
echo 'HOOKS=(base udev autodetect modconf kms block filesystems fsck)' >"$hooks_conf"
: >"$calls"
run_migration
[[ ! -s $calls ]] || fail "a user-edited hooks conf is left alone" "$(cat "$calls")"
pass "migration skips a hooks conf without the conditional"
cp "$packaged_hooks" "$hooks_conf"
: >"$calls"
LIMINE_MKINITCPIO_INSTALLED=0 run_migration
[[ ! -s $calls ]] || fail "installs without limine-mkinitcpio are skipped" "$(cat "$calls")"
mv "$nvidia_conf" "$nvidia_conf.away"
run_migration
mv "$nvidia_conf.away" "$nvidia_conf"
[[ ! -s $calls ]] || fail "machines without the proprietary NVIDIA driver are skipped" "$(cat "$calls")"
pass "migration skips installs it does not apply to"