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>
This commit is contained in:
co-authored by
Claude Fable 5
David Heinemeier Hansson
parent
6fad76184f
commit
ecd57bceee
@@ -1,5 +1,45 @@
|
|||||||
HOOKS=(base udev plymouth keyboard autodetect microcode modconf kms keymap consolefont block encrypt filesystems fsck btrfs-overlayfs)
|
HOOKS=(base udev plymouth keyboard autodetect microcode modconf kms keymap consolefont block encrypt filesystems fsck btrfs-overlayfs)
|
||||||
|
|
||||||
|
# The proprietary NVIDIA driver does early KMS itself: nvidia.conf (written by
|
||||||
|
# install/hardware/nvidia.sh, sourced before this file) early-loads nvidia_drm
|
||||||
|
# with modeset=1. Keeping the kms hook on such a system makes autodetect pull
|
||||||
|
# in nouveau — and ~100 MB of its GSP firmware — for a driver that never runs.
|
||||||
|
# Drop kms only when nvidia_drm is early-loaded and NVIDIA owns every display
|
||||||
|
# controller. Hybrid systems keep kms: their iGPU still needs it for early
|
||||||
|
# KMS at the LUKS prompt. So does anything unexpected, like a PCI tree that
|
||||||
|
# cannot be read.
|
||||||
|
#
|
||||||
|
# This reads MODULES midway through mkinitcpio's drop-in sourcing, so a
|
||||||
|
# later-sorting drop-in that resets MODULES outright — surface_device_modules.conf
|
||||||
|
# does — would strip nvidia_drm after kms was already dropped. Every machine
|
||||||
|
# Omarchy writes such a file for carries an Intel iGPU, which keeps kms here
|
||||||
|
# through the scan below; keep it that way.
|
||||||
|
if [[ " ${MODULES[*]:-} " == *" nvidia_drm "* ]]; then
|
||||||
|
_omarchy_nvidia_gpu=0
|
||||||
|
_omarchy_other_gpu=0
|
||||||
|
for _omarchy_pci in "${OMARCHY_PCI_DEVICES_PATH:-/sys/bus/pci/devices}"/*; do
|
||||||
|
if [[ ! -r $_omarchy_pci/class || ! -r $_omarchy_pci/vendor ]]; then
|
||||||
|
# An unreadable device could be another GPU. Inconclusive keeps kms.
|
||||||
|
_omarchy_other_gpu=1
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
[[ $(<"$_omarchy_pci/class") == "0x03"* ]] || continue
|
||||||
|
if [[ $(<"$_omarchy_pci/vendor") == "0x10de" ]]; then
|
||||||
|
_omarchy_nvidia_gpu=1
|
||||||
|
else
|
||||||
|
_omarchy_other_gpu=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((_omarchy_nvidia_gpu && !_omarchy_other_gpu)); then
|
||||||
|
_omarchy_hooks=()
|
||||||
|
for _omarchy_hook in "${HOOKS[@]}"; do
|
||||||
|
[[ $_omarchy_hook == "kms" ]] || _omarchy_hooks+=("$_omarchy_hook")
|
||||||
|
done
|
||||||
|
HOOKS=("${_omarchy_hooks[@]}")
|
||||||
|
fi
|
||||||
|
unset _omarchy_nvidia_gpu _omarchy_other_gpu _omarchy_pci _omarchy_hooks _omarchy_hook
|
||||||
|
fi
|
||||||
|
|
||||||
# Bundle vconsole.conf so Plymouth uses the configured keyboard layout at the
|
# Bundle vconsole.conf so Plymouth uses the configured keyboard layout at the
|
||||||
# LUKS prompt, but only when that layout types Latin letters. Passphrases are
|
# LUKS prompt, but only when that layout types Latin letters. Passphrases are
|
||||||
# Latin characters, so bundling a Hebrew/Greek/Cyrillic/Arabic layout would
|
# Latin characters, so bundling a Hebrew/Greek/Cyrillic/Arabic layout would
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
echo "Rebuild the initramfs so NVIDIA-only systems shed nouveau's unused GSP firmware"
|
||||||
|
|
||||||
|
# omarchy_hooks.conf now filters the kms hook out of HOOKS when the proprietary
|
||||||
|
# NVIDIA driver handles early KMS and NVIDIA owns every display controller
|
||||||
|
# (#6790). The settings package deploys that conditional, but nothing rebuilds
|
||||||
|
# the initramfs for a mkinitcpio drop-in change, so affected machines would
|
||||||
|
# carry ~100 MB of dead nouveau firmware until their next kernel update.
|
||||||
|
# Rebuild once, and only where the conditional actually changes the outcome:
|
||||||
|
# evaluate the installed drop-ins the way mkinitcpio does and check that kms
|
||||||
|
# dropped out. A user-edited omarchy_hooks.conf (pacman leaves the packaged
|
||||||
|
# update as a .pacnew) keeps kms and correctly skips the rebuild.
|
||||||
|
|
||||||
|
hooks_conf="${OMARCHY_MKINITCPIO_HOOKS_CONF:-/etc/mkinitcpio.conf.d/omarchy_hooks.conf}"
|
||||||
|
nvidia_conf="${OMARCHY_MKINITCPIO_NVIDIA_CONF:-/etc/mkinitcpio.conf.d/nvidia.conf}"
|
||||||
|
rebuild_marker="${OMARCHY_KMS_REBUILD_MARKER:-/var/lib/omarchy/migrations/1786605598}"
|
||||||
|
|
||||||
|
omarchy-cmd-present limine-mkinitcpio || exit 0
|
||||||
|
[[ -f $hooks_conf && -f $nvidia_conf ]] || exit 0
|
||||||
|
|
||||||
|
# The rebuild is machine-wide, but migrations run once per user: a marker
|
||||||
|
# records completion so another user's run does not repeat it, while a missing
|
||||||
|
# marker still retries an interrupted rebuild.
|
||||||
|
[[ ! -e $rebuild_marker ]] || exit 0
|
||||||
|
|
||||||
|
# Source the drop-ins in mkinitcpio's order (nvidia.conf sorts first) and read
|
||||||
|
# the HOOKS they produce. Skip conservatively if evaluation fails.
|
||||||
|
hooks=$(bash -c 'source "$1" && source "$2" && echo " ${HOOKS[*]} "' -- "$nvidia_conf" "$hooks_conf") || exit 0
|
||||||
|
|
||||||
|
[[ $hooks != *" kms "* ]] || exit 0
|
||||||
|
|
||||||
|
echo "This machine no longer uses the kms hook; rebuilding the initramfs without nouveau"
|
||||||
|
sudo limine-mkinitcpio
|
||||||
|
sudo install -Dm644 /dev/null "$rebuild_marker"
|
||||||
Executable
+103
@@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||||
|
|
||||||
|
tmp_dir=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$tmp_dir"' EXIT
|
||||||
|
|
||||||
|
hooks_conf="$ROOT/etc/mkinitcpio.conf.d/omarchy_hooks.conf"
|
||||||
|
|
||||||
|
# Each argument is a PCI device as "vendor:class", in sysfs's own format.
|
||||||
|
write_pci_devices() {
|
||||||
|
rm -rf "$tmp_dir/devices"
|
||||||
|
mkdir -p "$tmp_dir/devices"
|
||||||
|
|
||||||
|
local index=0
|
||||||
|
local spec
|
||||||
|
for spec in "$@"; do
|
||||||
|
local slot
|
||||||
|
slot=$(printf '0000:%02x:00.0' "$index")
|
||||||
|
mkdir -p "$tmp_dir/devices/$slot"
|
||||||
|
printf '%s\n' "${spec%%:*}" >"$tmp_dir/devices/$slot/vendor"
|
||||||
|
printf '%s\n' "${spec##*:}" >"$tmp_dir/devices/$slot/class"
|
||||||
|
index=$((index + 1))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sources the hook config the way mkinitcpio does — with earlier drop-ins
|
||||||
|
# already applied — and prints the resulting HOOKS. mkinitcpio does not run
|
||||||
|
# under set -u, but the config must survive it, so source under it anyway.
|
||||||
|
# "unset" leaves MODULES undeclared entirely.
|
||||||
|
resolved_hooks() {
|
||||||
|
local modules_decl=""
|
||||||
|
[[ $1 == "unset" ]] || modules_decl="MODULES=($1)"
|
||||||
|
|
||||||
|
# The vconsole block sources the host's /etc/vconsole.conf, which may set
|
||||||
|
# only KEYMAP; predefine XKBLAYOUT so its expansion survives set -u and the
|
||||||
|
# test stays independent of the machine it runs on.
|
||||||
|
OMARCHY_PCI_DEVICES_PATH="$tmp_dir/devices" bash -uc "
|
||||||
|
FILES=()
|
||||||
|
XKBLAYOUT=us
|
||||||
|
$modules_decl
|
||||||
|
source '$hooks_conf'
|
||||||
|
echo \"\${HOOKS[*]}\"
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
nvidia_modules="nvidia nvidia_modeset nvidia_uvm nvidia_drm"
|
||||||
|
|
||||||
|
write_pci_devices
|
||||||
|
with_kms=$(resolved_hooks "")
|
||||||
|
without_kms=${with_kms/ kms / }
|
||||||
|
|
||||||
|
[[ $with_kms == *" kms "* ]] ||
|
||||||
|
fail "baseline HOOKS contains the kms hook" "actual: $with_kms"
|
||||||
|
pass "baseline HOOKS contains the kms hook"
|
||||||
|
|
||||||
|
assert_hooks() {
|
||||||
|
local description="$1" modules="$2" expected="$3"
|
||||||
|
local actual
|
||||||
|
actual=$(resolved_hooks "$modules")
|
||||||
|
|
||||||
|
[[ $actual == "$expected" ]] ||
|
||||||
|
fail "$description" "expected: $expected"$'\n'"actual: $actual"
|
||||||
|
pass "$description"
|
||||||
|
}
|
||||||
|
|
||||||
|
# NVIDIA RTX class display controller.
|
||||||
|
write_pci_devices 0x10de:0x030000
|
||||||
|
assert_hooks "nvidia-only system with early nvidia_drm drops only kms" \
|
||||||
|
"$nvidia_modules" "$without_kms"
|
||||||
|
assert_hooks "nvidia-only system without early nvidia_drm keeps kms" \
|
||||||
|
"" "$with_kms"
|
||||||
|
assert_hooks "unset MODULES under set -u keeps kms without erroring" \
|
||||||
|
"unset" "$with_kms"
|
||||||
|
|
||||||
|
# AMD integrated graphics next to an NVIDIA 3D controller.
|
||||||
|
write_pci_devices 0x1002:0x030000 0x10de:0x030200
|
||||||
|
assert_hooks "hybrid system keeps kms for the iGPU" \
|
||||||
|
"$nvidia_modules" "$with_kms"
|
||||||
|
|
||||||
|
# NVIDIA audio function only: no display controller found.
|
||||||
|
write_pci_devices 0x10de:0x040300
|
||||||
|
assert_hooks "no display controller found keeps kms" \
|
||||||
|
"$nvidia_modules" "$with_kms"
|
||||||
|
|
||||||
|
write_pci_devices
|
||||||
|
assert_hooks "empty PCI tree keeps kms" \
|
||||||
|
"$nvidia_modules" "$with_kms"
|
||||||
|
|
||||||
|
# A device directory missing its class/vendor attributes must not error, and
|
||||||
|
# counts as inconclusive: it could be another GPU, so kms stays.
|
||||||
|
write_pci_devices
|
||||||
|
mkdir -p "$tmp_dir/devices/0000:00:00.0"
|
||||||
|
assert_hooks "unreadable PCI device keeps kms" \
|
||||||
|
"$nvidia_modules" "$with_kms"
|
||||||
|
|
||||||
|
# Even next to a readable NVIDIA GPU — the unreadable device may be the iGPU.
|
||||||
|
write_pci_devices 0x10de:0x030000
|
||||||
|
mkdir -p "$tmp_dir/devices/0000:01:00.0"
|
||||||
|
assert_hooks "unreadable device beside an NVIDIA GPU keeps kms" \
|
||||||
|
"$nvidia_modules" "$with_kms"
|
||||||
Executable
+131
@@ -0,0 +1,131 @@
|
|||||||
|
#!/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"
|
||||||
Reference in New Issue
Block a user