feat: NVIDIA modules in the initramfs only while every kernel has them, decided again by a pacman hook at every kernel change

This commit is contained in:
theupriser committed 2026-10-01 14:42:06 +02:00
1 parent 68d28b95ed
commit 30b315e678
7 files changed
+103 -23

No files matched your search

+1
View File
@@ -11,6 +11,7 @@ one per merged pull request.
- **test: tests/nvidia-test.sh runs the NVIDIA fix against a fake RTX 5080 beside an iGPU, a stub modinfo and temp copies of the Limine, systemd-boot and GRUB files (no root, VM or hardware); NVIDIA_DRM_DIR points detection at a fake sysfs tree** - **test: tests/nvidia-test.sh runs the NVIDIA fix against a fake RTX 5080 beside an iGPU, a stub modinfo and temp copies of the Limine, systemd-boot and GRUB files (no root, VM or hardware); NVIDIA_DRM_DIR points detection at a fake sysfs tree**
- **fix: NVIDIA fix on Limine edits the KERNEL_CMDLINE[default] line (an appended += line ended up as literal text on the kernel command line, found in a VM); the modules are only loaded early when every installed kernel has them, and a Limine boot entry without the parameters is an error** - **fix: NVIDIA fix on Limine edits the KERNEL_CMDLINE[default] line (an appended += line ended up as literal text on the kernel command line, found in a VM); the modules are only loaded early when every installed kernel has them, and a Limine boot entry without the parameters is an error**
- **test: tests/nvidia-hardware-test.sh for a real NVIDIA PC: check (read-only state of the detection, the live nvidia_drm modeset/fbdev, versions), apply, and visual (was the gaming mode picture clean), with a report file to paste** - **test: tests/nvidia-hardware-test.sh for a real NVIDIA PC: check (read-only state of the detection, the live nvidia_drm modeset/fbdev, versions), apply, and visual (was the gaming mode picture clean), with a report file to paste**
- **fix: the NVIDIA modules are loaded early only while every installed kernel has them, decided again at every kernel or driver change by a pacman hook (steamify-nvidia-initramfs): a later kernel without the modules (another kernel, a failed DKMS build, a downgrade) no longer makes its initramfs and Limine boot entry fail**
## 2.9.6 - 2026-09-30 ## 2.9.6 - 2026-09-30
+9 -4
View File
@@ -216,13 +216,18 @@ its driver installed (`lib/nvidia.sh`), the SteamOS conversion adds
systemd-boot or GRUB; the file is backed up first) and systemd-boot or GRUB; the file is backed up first) and
`/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf` (`MODULES+=(nvidia `/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf` (`MODULES+=(nvidia
nvidia_modeset nvidia_uvm nvidia_drm)`), then rebuilds the initramfs and boot nvidia_modeset nvidia_uvm nvidia_drm)`), then rebuilds the initramfs and boot
entries. The early-load drop-in is only written when every installed kernel entries. The early-load drop-in is only there while every installed kernel has the
has the NVIDIA modules (mkinitcpio fails on a missing module, and NVIDIA modules (mkinitcpio fails on a missing module, and `limine-mkinitcpio`
`limine-mkinitcpio` then skips that kernel's boot entry, parameters included). then skips that kernel's boot entry, parameters included). That is decided again
at every kernel or driver change by a pacman hook
(`/etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook`, after DKMS builds,
before the initramfs is built) running `/usr/local/libexec/steamify-nvidia-initramfs`:
another kernel without the modules, a failed DKMS build or a downgrade removes
the drop-in, and it comes back once every kernel has them again.
On Limine the parameters go into the existing `KERNEL_CMDLINE[default]="..."` On Limine the parameters go into the existing `KERNEL_CMDLINE[default]="..."`
line: an extra appended `+=` line is pasted into the command line as text. line: an extra appended `+=` line is pasted into the command line as text.
Only what's missing is changed; it applies after a reboot. Turning Only what's missing is changed; it applies after a reboot. Turning
the conversion off removes both. `tests/nvidia-test.sh` tests it against a fake GPU the conversion off removes both, the hook and its script. `tests/nvidia-test.sh` tests it against a fake GPU
and fake boot loader files; `tests/nvidia-hardware-test.sh check|apply|visual` is for a and fake boot loader files; `tests/nvidia-hardware-test.sh check|apply|visual` is for a
real NVIDIA PC (read-only check, the real apply, and a prompt for whether the picture real NVIDIA PC (read-only check, the real apply, and a prompt for whether the picture
was clean), and writes `~/steamify-nvidia-report.txt`. was clean), and writes `~/steamify-nvidia-report.txt`.
+22 -15
View File
@@ -9,6 +9,9 @@
NVIDIA_PARAMS="nvidia-drm.modeset=1 nvidia-drm.fbdev=1" NVIDIA_PARAMS="nvidia-drm.modeset=1 nvidia-drm.fbdev=1"
NVIDIA_INITRAMFS_CONF=/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf NVIDIA_INITRAMFS_CONF=/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf
# The script and pacman hook that keep the initramfs file right at every kernel change.
NVIDIA_SCRIPT=${NVIDIA_SCRIPT:-/usr/local/libexec/steamify-nvidia-initramfs}
NVIDIA_HOOK=${NVIDIA_HOOK:-/etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook}
# The DRM devices in sysfs; the tests point this at a fake tree. # The DRM devices in sysfs; the tests point this at a fake tree.
NVIDIA_DRM_DIR=${NVIDIA_DRM_DIR:-/sys/class/drm} NVIDIA_DRM_DIR=${NVIDIA_DRM_DIR:-/sys/class/drm}
@@ -46,16 +49,14 @@ nvidia_initramfs_ok() {
} }
nvidia_modules_everywhere() { nvidia_modules_everywhere() {
# 0 when every installed kernel has the nvidia_drm module: mkinitcpio fails # 0 when every installed kernel has the nvidia_drm module (the script's own check).
# on a MODULES entry a kernel lacks, and limine-mkinitcpio then skips that patch_file steamify-nvidia-initramfs.sh | NVIDIA_MODULES_DIR="${NVIDIA_MODULES_DIR:-/usr/lib/modules}" bash -s check
# kernel's initramfs and boot entry, so the parameters never reach it. }
local k found=0
for k in "${NVIDIA_MODULES_DIR:-/usr/lib/modules}"/*/; do nvidia_hook_install() {
[[ -f "$k/pkgbase" || -d "$k/kernel" || -d "$k/updates" || -d "$k/extramodules" ]] || continue patch_file steamify-nvidia-initramfs.sh | sudo install -Dm755 /dev/stdin "$NVIDIA_SCRIPT" &&
find "$k" -name 'nvidia-drm.ko*' -print -quit 2>/dev/null | grep -q . || return 1 patch_file steamify-nvidia-initramfs.hook | fill SCRIPT="$NVIDIA_SCRIPT" |
found=1 sudo install -Dm644 /dev/stdin "$NVIDIA_HOOK"
done
[[ $found == 1 ]]
} }
nvidia_rebuild_boot() { nvidia_rebuild_boot() {
@@ -107,12 +108,17 @@ nvidia_enable() {
changed=1 changed=1
fi fi
fi fi
if ! nvidia_initramfs_ok && ! nvidia_modules_everywhere; then nvidia_hook_install || { err "Installing the NVIDIA initramfs hook failed."; return 1; }
warn "Not every installed kernel has the NVIDIA modules: skipping loading them early (the kernel parameters still apply)." local had=0; [[ -f "$NVIDIA_INITRAMFS_CONF" ]] && had=1
elif ! nvidia_initramfs_ok; then sudo env NVIDIA_INITRAMFS_CONF="$NVIDIA_INITRAMFS_CONF" NVIDIA_MODULES_DIR="${NVIDIA_MODULES_DIR:-/usr/lib/modules}" "$NVIDIA_SCRIPT" sync
if [[ -f "$NVIDIA_INITRAMFS_CONF" && $had == 0 ]]; then
info "Loading the NVIDIA modules early (initramfs)..." info "Loading the NVIDIA modules early (initramfs)..."
printf 'MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)\n' | changed=1
sudo install -Dm644 /dev/stdin "$NVIDIA_INITRAMFS_CONF" && changed=1 elif [[ ! -f "$NVIDIA_INITRAMFS_CONF" && $had == 1 ]]; then
warn "A kernel without the NVIDIA modules is installed: not loading them early any more (rebuilding the initramfs without them)."
changed=1
elif [[ ! -f "$NVIDIA_INITRAMFS_CONF" ]] && ! nvidia_initramfs_ok; then
warn "Not every installed kernel has the NVIDIA modules: not loading them early for now (the kernel parameters still apply; this is checked again at every kernel update)."
fi fi
if [[ $changed == 1 ]]; then if [[ $changed == 1 ]]; then
nvidia_rebuild_boot || { err "Rebuilding the boot entries failed."; return 1; } nvidia_rebuild_boot || { err "Rebuilding the boot entries failed."; return 1; }
@@ -132,6 +138,7 @@ nvidia_disable() {
changed=1 changed=1
fi fi
[[ -f "$NVIDIA_INITRAMFS_CONF" ]] && { sudo rm -f "$NVIDIA_INITRAMFS_CONF"; changed=1; } [[ -f "$NVIDIA_INITRAMFS_CONF" ]] && { sudo rm -f "$NVIDIA_INITRAMFS_CONF"; changed=1; }
sudo rm -f "$NVIDIA_HOOK" "$NVIDIA_SCRIPT"
[[ $changed == 1 ]] && nvidia_rebuild_boot [[ $changed == 1 ]] && nvidia_rebuild_boot
return 0 return 0
} }
+1
View File
@@ -14,4 +14,5 @@ read and reviewed on their own. `lib/*.sh` reads them with `patch_file
| `steam-shortcuts.py` | `lib/steam-game.sh` (Add as non-Steam game) | Reads and writes Steam's binary `shortcuts.vdf` (find, add or update, remove) | | `steam-shortcuts.py` | `lib/steam-game.sh` (Add as non-Steam game) | Reads and writes Steam's binary `shortcuts.vdf` (find, add or update, remove) |
| `steamify-notifier.py` | `lib/update-notifier.sh` (Update notifications) | The daily check: notification and tray icon when there's a new release | | `steamify-notifier.py` | `lib/update-notifier.sh` (Update notifications) | The daily check: notification and tray icon when there's a new release |
| `steamify-os-release.sh`, `steamify-os-release.hook` | `lib/login-manager.sh` (SteamOS conversion) | What Steam's System settings show besides the OS name (which stays CachyOS's): `VERSION_ID=steamos-X.Y`, `VERSION_CODENAME=steam-machine`, `VARIANT`/`VARIANT_ID` Steamify with its version, in `/etc/os-release`; the pacman hook runs it again after cachyos-hooks (`fill SCRIPT=... VERSION=... STEAMOS=...`) | | `steamify-os-release.sh`, `steamify-os-release.hook` | `lib/login-manager.sh` (SteamOS conversion) | What Steam's System settings show besides the OS name (which stays CachyOS's): `VERSION_ID=steamos-X.Y`, `VERSION_CODENAME=steam-machine`, `VARIANT`/`VARIANT_ID` Steamify with its version, in `/etc/os-release`; the pacman hook runs it again after cachyos-hooks (`fill SCRIPT=... VERSION=... STEAMOS=...`) |
| `steamify-nvidia-initramfs.sh`, `steamify-nvidia-initramfs.hook` | `lib/nvidia.sh` (SteamOS conversion, NVIDIA fix) | The pacman hook (before the initramfs is built, after DKMS) and its script: the NVIDIA modules go into the initramfs only while every installed kernel has them, decided again at every kernel or driver change (`fill SCRIPT=...`) |
| `cros-ec-cec-single-port.patch` | `lib/cec.sh` (HDMI-CEC) | Makes Valve's Steam Machine CEC driver find amdgpu's HDMI port | | `cros-ec-cec-single-port.patch` | `lib/cec.sh` (HDMI-CEC) | Makes Valve's Steam Machine CEC driver find amdgpu's HDMI port |
+17
View File
@@ -0,0 +1,17 @@
# Installed by Steamify CachyOS (SteamOS conversion, NVIDIA fix): decides
# again whether the NVIDIA modules go into the initramfs, after DKMS has built
# its modules (71-dkms-install) and before the initramfs is built
# (90-mkinitcpio-install); hooks run in file name order.
[Trigger]
Operation = Install
Operation = Upgrade
Operation = Remove
Type = Path
Target = usr/lib/modules/*/vmlinuz
Target = usr/lib/modules/*/extramodules/nvidia*
Target = usr/lib/modules/*/updates/*
[Action]
Description = Steamify: NVIDIA modules in the initramfs only if every kernel has them
When = PostTransaction
Exec = @SCRIPT@ sync
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# Installed by Steamify CachyOS (SteamOS conversion, NVIDIA fix), run by the
# pacman hook steamify-nvidia-initramfs.hook before the initramfs is built.
# The NVIDIA modules go into the initramfs (early KMS) only while EVERY
# installed kernel has them: mkinitcpio fails on a module a kernel lacks, and
# limine-mkinitcpio then skips that kernel's boot entry, kernel parameters
# included. Decided again at every kernel or driver change, so a new kernel
# without the modules (another kernel, a failed DKMS build, a downgrade) gets
# a working initramfs and the file comes back once it has them.
# steamify-nvidia-initramfs [sync|check]
# sync (default) write or remove the drop-in to match
# check exit status 0 when every installed kernel has nvidia_drm
conf="${NVIDIA_INITRAMFS_CONF:-/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf}"
mods="${NVIDIA_MODULES_DIR:-/usr/lib/modules}"
everywhere() {
local k found=0
for k in "$mods"/*/; do
[[ -f "$k/vmlinuz" || -f "$k/pkgbase" ]] || continue # an installed kernel, not a leftover folder
find "$k" -name 'nvidia-drm.ko*' -print -quit 2>/dev/null | grep -q . || return 1
found=1
done
[[ $found == 1 ]]
}
case "${1:-sync}" in
check) everywhere ;;
sync)
if everywhere; then
[[ -f "$conf" ]] || { mkdir -p "$(dirname "$conf")" &&
echo 'MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)' > "$conf"; }
else
rm -f "$conf"
fi ;;
*) echo "usage: $0 [sync|check]" >&2; exit 2 ;;
esac
+17 -4
View File
@@ -7,7 +7,7 @@ T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT
fail=0 fail=0
check() { if eval "$2"; then echo "ok $1"; else echo "FAIL $1"; fail=1; fi; } check() { if eval "$2"; then echo "ok $1"; else echo "FAIL $1"; fail=1; fi; }
source lib/common.sh; source lib/hdmi-refresh.sh; source lib/nvidia.sh SCRIPT_DIR="$PWD"; source lib/common.sh; source lib/hdmi-refresh.sh; source lib/nvidia.sh
sudo() { "$@"; } sudo() { "$@"; }
info() { :; }; ok() { :; }; warn() { :; }; err() { echo "ERR $*"; } info() { :; }; ok() { :; }; warn() { :; }; err() { echo "ERR $*"; }
backup_file() { [[ -f "$1.bak" ]] || cp "$1" "$1.bak"; } backup_file() { [[ -f "$1.bak" ]] || cp "$1" "$1.bak"; }
@@ -19,7 +19,8 @@ echo 0x8086 > "$T/drm/card0/device/vendor"; echo 0x030000 > "$T/drm/card0/device
echo 0x10de > "$T/drm/card1/device/vendor"; echo 0x030000 > "$T/drm/card1/device/class" echo 0x10de > "$T/drm/card1/device/vendor"; echo 0x030000 > "$T/drm/card1/device/class"
echo 0x2c02 > "$T/drm/card1/device/device" echo 0x2c02 > "$T/drm/card1/device/device"
NVIDIA_DRM_DIR="$T/drm" NVIDIA_DRM_DIR="$T/drm"
mkdir -p "$T/mods/6.1.0-cachyos/kernel"; : > "$T/mods/6.1.0-cachyos/kernel/nvidia-drm.ko.zst" mkdir -p "$T/mods/6.1.0-cachyos/kernel"; : > "$T/mods/6.1.0-cachyos/kernel/nvidia-drm.ko.zst"; : > "$T/mods/6.1.0-cachyos/pkgbase"
NVIDIA_SCRIPT="$T/libexec/steamify-nvidia-initramfs"; NVIDIA_HOOK="$T/hooks/85-steamify-nvidia-initramfs.hook"
NVIDIA_MODULES_DIR="$T/mods" NVIDIA_MODULES_DIR="$T/mods"
NVIDIA_LIMINE_CONF="$T/none/limine.conf"; NVIDIA_SDBOOT_DIR="$T/none/entries"; NVIDIA_GRUB_CFG="$T/none/grub.cfg" # not generated here: not checked NVIDIA_LIMINE_CONF="$T/none/limine.conf"; NVIDIA_SDBOOT_DIR="$T/none/entries"; NVIDIA_GRUB_CFG="$T/none/grub.cfg" # not generated here: not checked
@@ -39,15 +40,27 @@ for loader in limine sdboot grub; do
nvidia_enable nvidia_enable
check "$loader: parameters added" 'grep -q "nvidia-drm.modeset=1" "$F" && grep -q "nvidia-drm.fbdev=1" "$F"' check "$loader: parameters added" 'grep -q "nvidia-drm.modeset=1" "$F" && grep -q "nvidia-drm.fbdev=1" "$F"'
check "$loader: initramfs drop-in written" 'grep -q nvidia_drm "$NVIDIA_INITRAMFS_CONF"' check "$loader: initramfs drop-in written" 'grep -q nvidia_drm "$NVIDIA_INITRAMFS_CONF"'
check "$loader: script and pacman hook installed" '[[ -x "$NVIDIA_SCRIPT" ]] && grep -q "^Exec = $NVIDIA_SCRIPT sync" "$NVIDIA_HOOK"'
check "$loader: backup made, rebuilt once" '[[ -f "$F.bak" && $REBUILDS == 1 ]]' check "$loader: backup made, rebuilt once" '[[ -f "$F.bak" && $REBUILDS == 1 ]]'
SUM="$(cat "$F")"; nvidia_enable SUM="$(cat "$F")"; nvidia_enable
check "$loader: second run changes nothing" '[[ "$(cat "$F")" == "$SUM" && $REBUILDS == 1 ]]' check "$loader: second run changes nothing" '[[ "$(cat "$F")" == "$SUM" && $REBUILDS == 1 ]]'
nvidia_disable nvidia_disable
check "$loader: disable restores the file and drops the drop-in" '[[ "$(cat "$F")" == "$ORIG" && ! -f "$NVIDIA_INITRAMFS_CONF" ]]' check "$loader: disable restores the file, drops the drop-in, script and hook" '[[ "$(cat "$F")" == "$ORIG" && ! -f "$NVIDIA_INITRAMFS_CONF" && ! -e "$NVIDIA_SCRIPT" && ! -e "$NVIDIA_HOOK" ]]'
done done
# The pacman hook's script decides again at every kernel change.
syncit() { NVIDIA_INITRAMFS_CONF="$NVIDIA_INITRAMFS_CONF" NVIDIA_MODULES_DIR="$NVIDIA_MODULES_DIR" bash "$NVIDIA_SCRIPT" sync; }
nvidia_hook_install; rm -f "$NVIDIA_INITRAMFS_CONF"
syncit; check "hook script: every kernel has the modules -> drop-in written" '[[ -f "$NVIDIA_INITRAMFS_CONF" ]]'
mkdir -p "$T/mods/6.6.0-lts/kernel"; : > "$T/mods/6.6.0-lts/pkgbase"
syncit; check "hook script: a new kernel without modules -> drop-in removed" '[[ ! -f "$NVIDIA_INITRAMFS_CONF" ]]'
: > "$T/mods/6.6.0-lts/kernel/nvidia-drm.ko.zst"
syncit; check "hook script: the modules arrive (DKMS built) -> drop-in back" '[[ -f "$NVIDIA_INITRAMFS_CONF" ]]'
rm -rf "$T/mods/6.6.0-lts"; mkdir -p "$T/mods/5.0.0-leftover"; : > "$T/mods/5.0.0-leftover/modules.dep"
syncit; check "hook script: a leftover folder of a removed kernel is ignored" '[[ -f "$NVIDIA_INITRAMFS_CONF" ]]'
rm -rf "$T/mods/5.0.0-leftover"; nvidia_disable
# A second kernel without the NVIDIA modules: no early-load drop-in, parameters still set. # A second kernel without the NVIDIA modules: no early-load drop-in, parameters still set.
mkdir -p "$T/mods/6.6.0-lts/kernel"; F="$T/sdboot-manage.conf"; printf 'LINUX_OPTIONS="quiet"\n' > "$F" mkdir -p "$T/mods/6.6.0-lts/kernel"; : > "$T/mods/6.6.0-lts/pkgbase"; F="$T/sdboot-manage.conf"; printf 'LINUX_OPTIONS="quiet"\n' > "$F"
nvidia_boot_file() { echo "$F"; }; rm -f "$NVIDIA_INITRAMFS_CONF"; REBUILDS=0; nvidia_enable nvidia_boot_file() { echo "$F"; }; rm -f "$NVIDIA_INITRAMFS_CONF"; REBUILDS=0; nvidia_enable
check "kernel without nvidia modules: no drop-in, parameters set" '[[ ! -f "$NVIDIA_INITRAMFS_CONF" ]] && grep -q "nvidia-drm.fbdev=1" "$F"' check "kernel without nvidia modules: no drop-in, parameters set" '[[ ! -f "$NVIDIA_INITRAMFS_CONF" ]] && grep -q "nvidia-drm.fbdev=1" "$F"'
rm -rf "$T/mods/6.6.0-lts" rm -rf "$T/mods/6.6.0-lts"