From 6aa1377e16851422ae98c2905f040a97d2f8c707 Mon Sep 17 00:00:00 2001 From: Rick Peters Date: Thu, 1 Oct 2026 12:48:56 +0200 Subject: [PATCH] fix: NVIDIA fix on Limine edits the existing cmdline line; early modules only when every kernel has them Found by running it in a VM: the appended += line became literal text on the kernel command line, and a kernel without the modules made limine-mkinitcpio skip its boot entry. --- CHANGELOG.md | 1 + TECHNICAL.md | 7 ++++++- lib/nvidia.sh | 30 +++++++++++++++++++++++------- tests/nvidia-test.sh | 7 +++++++ 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 615ddd7..309b639 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ one per merged pull request. - **feat: the SteamOS conversion fixes a corrupted gaming mode screen with an NVIDIA GPU: nvidia-drm.modeset=1 and fbdev=1 on the kernel command line, NVIDIA modules in the initramfs (lib/nvidia.sh)** - **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** ## 2.9.6 - 2026-09-30 diff --git a/TECHNICAL.md b/TECHNICAL.md index c04f9ce..0d8d160 100644 --- a/TECHNICAL.md +++ b/TECHNICAL.md @@ -216,7 +216,12 @@ its driver installed (`lib/nvidia.sh`), the SteamOS conversion adds systemd-boot or GRUB; the file is backed up first) and `/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf` (`MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)`), then rebuilds the initramfs and boot -entries. Only what's missing is changed; it applies after a reboot. Turning +entries. The early-load drop-in is only written when every installed kernel +has the NVIDIA modules (mkinitcpio fails on a missing module, and +`limine-mkinitcpio` then skips that kernel's boot entry, parameters included). +On Limine the parameters go into the existing `KERNEL_CMDLINE[default]="..."` +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 the conversion off removes both. Untested on hardware so far. ## HDMI-CEC diff --git a/lib/nvidia.sh b/lib/nvidia.sh index c7d0cb9..ad0b89e 100644 --- a/lib/nvidia.sh +++ b/lib/nvidia.sh @@ -9,7 +9,6 @@ NVIDIA_PARAMS="nvidia-drm.modeset=1 nvidia-drm.fbdev=1" NVIDIA_INITRAMFS_CONF=/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf -NVIDIA_MARK="# steamify-nvidia" # The DRM devices in sysfs; the tests point this at a fake tree. NVIDIA_DRM_DIR=${NVIDIA_DRM_DIR:-/sys/class/drm} @@ -46,6 +45,19 @@ nvidia_initramfs_ok() { [[ -f "$NVIDIA_INITRAMFS_CONF" ]] || grep -qE '^MODULES=.*nvidia_drm' /etc/mkinitcpio.conf 2>/dev/null } +nvidia_modules_everywhere() { + # 0 when every installed kernel has the nvidia_drm module: mkinitcpio fails + # on a MODULES entry a kernel lacks, and limine-mkinitcpio then skips that + # 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 + [[ -f "$k/pkgbase" || -d "$k/kernel" || -d "$k/updates" || -d "$k/extramodules" ]] || continue + find "$k" -name 'nvidia-drm.ko*' -print -quit 2>/dev/null | grep -q . || return 1 + found=1 + done + [[ $found == 1 ]] +} + nvidia_rebuild_boot() { local f; f="$(nvidia_boot_file)" info "Rebuilding the initramfs and boot entries..." @@ -68,7 +80,7 @@ nvidia_enable() { backup_file "$f" case "$f" in */limine) - printf 'KERNEL_CMDLINE[default]+=" %s" %s\n' "$NVIDIA_PARAMS" "$NVIDIA_MARK" | sudo tee -a "$f" >/dev/null ;; + sudo sed -i -E "s/^(KERNEL_CMDLINE\[default\]=\"[^\"]*)\"/\1 $NVIDIA_PARAMS\"/" "$f" ;; */sdboot-manage.conf) sudo sed -i -E "s/^(LINUX_OPTIONS=\"[^\"]*)\"/\1 $NVIDIA_PARAMS\"/" "$f" ;; */grub) @@ -77,13 +89,20 @@ nvidia_enable() { changed=1 fi fi - if ! nvidia_initramfs_ok; then + if ! nvidia_initramfs_ok && ! nvidia_modules_everywhere; then + warn "Not every installed kernel has the NVIDIA modules: skipping loading them early (the kernel parameters still apply)." + elif ! nvidia_initramfs_ok; then info "Loading the NVIDIA modules early (initramfs)..." printf 'MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)\n' | sudo install -Dm644 /dev/stdin "$NVIDIA_INITRAMFS_CONF" && changed=1 fi if [[ $changed == 1 ]]; then nvidia_rebuild_boot || { err "Rebuilding the boot entries failed."; return 1; } + if [[ "$f" == */limine && -f "${NVIDIA_LIMINE_CONF:-/boot/limine.conf}" ]] && + ! grep -q 'nvidia-drm.modeset=1' "${NVIDIA_LIMINE_CONF:-/boot/limine.conf}"; then + err "The boot entries don't have the NVIDIA parameters: the initramfs build failed (see above)." + return 1 + fi ok "NVIDIA gaming mode fix applied; it takes effect after a reboot." fi return 0 @@ -92,10 +111,7 @@ nvidia_enable() { nvidia_disable() { local f changed=0; f="$(nvidia_boot_file)" if [[ -n "$f" ]] && grep -q "$NVIDIA_PARAMS" "$f" 2>/dev/null; then - case "$f" in - */limine) sudo sed -i "/$NVIDIA_MARK\$/d" "$f" ;; - *) sudo sed -i "s/ \?$NVIDIA_PARAMS//" "$f" ;; - esac + sudo sed -i "s/ \?$NVIDIA_PARAMS//" "$f" changed=1 fi [[ -f "$NVIDIA_INITRAMFS_CONF" ]] && { sudo rm -f "$NVIDIA_INITRAMFS_CONF"; changed=1; } diff --git a/tests/nvidia-test.sh b/tests/nvidia-test.sh index 2a9a0d4..ff35623 100644 --- a/tests/nvidia-test.sh +++ b/tests/nvidia-test.sh @@ -19,6 +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 0x2c02 > "$T/drm/card1/device/device" 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" +NVIDIA_MODULES_DIR="$T/mods" FAKE_DRIVER=1; check "finds the NVIDIA card as card1 beside an iGPU" nvidia_present FAKE_DRIVER=0; check "skips when the driver isn't installed" '! nvidia_present' @@ -43,6 +45,11 @@ for loader in limine sdboot grub; do check "$loader: disable restores the file and drops the drop-in" '[[ "$(cat "$F")" == "$ORIG" && ! -f "$NVIDIA_INITRAMFS_CONF" ]]' done +# 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" +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"' +rm -rf "$T/mods/6.6.0-lts" FAKE_DRIVER=0; printf 'LINUX_OPTIONS="quiet"\n' > "$T/sdboot"; F="$T/sdboot"; REBUILDS=0; nvidia_enable check "no NVIDIA driver: nothing touched" '[[ "$(cat "$F")" == "LINUX_OPTIONS=\"quiet\"" && $REBUILDS == 0 ]]' exit $fail