Take a different approach

Generate module blacklists instead when initramfs is loaded.
This commit is contained in:
Thomas Duckworth
2026-03-21 11:59:40 +11:00
parent 665a204da7
commit 7d709b467f
5 changed files with 65 additions and 39 deletions

View File

@@ -2,8 +2,8 @@
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
# SPDX-FileCopyrightText: 2026 Thomas Duckworth <tduck@filotimoproject.org>
# Necessary for letting udev control what GPUs get which drivers
# using driver_override, so nouveau or nvidia can be dynamically selected.
# Necessary for generating automatic modprobe blacklists before
# drivers load so nouveau or nvidia can be dynamically selected.
# Also gives us some certainty that these are included for kernel modesetting.
build() {
@@ -11,8 +11,12 @@ build() {
add_module "nvidia-drm"
add_module "nvidia-modeset"
add_module "nvidia-uvm"
add_module "nouveau"
add_checked_modules "/drivers/gpu/drm"
add_binary "/usr/lib/initcpio/nvidia-driver-selection"
add_systemd_unit "kde-linux-nvidia-driver-selection.service"
add_symlink "/etc/systemd/system/sysinit.target.wants/kde-linux-nvidia-driver-selection.service" "/usr/lib/systemd/system/kde-linux-nvidia-driver-selection.service"
}

View File

@@ -0,0 +1,42 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
# SPDX-FileCopyrightText: 2026 Thomas Duckworth <tduck@filotimoproject.org>
# Checks sysfs for NVIDIA GPUs and generates a modprobe blacklist dynamically.
mkdir -p /run/modprobe.d
mkdir -p /run/modules-load.d
USE_NVIDIA=0
GPU_FOUND=0
for dev in /sys/bus/pci/devices/*; do
[ -f "$dev/vendor" ] && [ -f "$dev/device" ] && [ -f "$dev/class" ] || continue
vendor=$(cat "$dev/vendor")
class=$(cat "$dev/class")
# 0x10de = NVIDIA. Class 0x03* = Display controllers
if [ "$vendor" = "0x10de" ] && [[ "$class" == 0x03* ]]; then
GPU_FOUND=1
device=$(cat "$dev/device")
dec_id=$(printf "%d" "$device")
# 7680 (0x1e00) is the Turing (NVIDIA driver) cutoff
if [ "$dec_id" -ge 7680 ]; then
USE_NVIDIA=1
fi
fi
done
if [ "$GPU_FOUND" -eq 1 ]; then
if [ "$USE_NVIDIA" -eq 1 ]; then
echo "blacklist nouveau" > /run/modprobe.d/05-dynamic-gpu.conf
modprobe nvidia-drm modeset=1
else
echo "blacklist nvidia" > /run/modprobe.d/05-dynamic-gpu.conf
echo "blacklist nvidia-drm" >> /run/modprobe.d/05-dynamic-gpu.conf
echo "blacklist nvidia-modeset" >> /run/modprobe.d/05-dynamic-gpu.conf
echo "blacklist nvidia-uvm" >> /run/modprobe.d/05-dynamic-gpu.conf
modprobe nouveau
fi
fi

View File

@@ -28,8 +28,8 @@ fi
cat <<- EOF > mkinitcpio.conf
MODULES=(overlay)
BINARIES=()
FILES=(/usr/lib/udev/rules.d/05-nvidia-module-selection.rules) # Determine which driver is used at boot with udev
HOOKS=(base systemd nvidia-module-selection modconf kms keyboard block sd-encrypt filesystems fsck systemd-extension plymouth microcode sd-shutdown)
FILES=()
HOOKS=(base systemd nvidia-driver-selection modconf kms keyboard block sd-encrypt filesystems fsck systemd-extension plymouth microcode sd-shutdown)
EOF
echo "rw \

View File

@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
# SPDX-FileCopyrightText: 2025 Thomas Duckworth <tduck@filotimoproject.org>
[Unit]
Description=Dynamic NVIDIA Driver Selection
DefaultDependencies=no
Before=systemd-udevd.service systemd-modules-load.service
ConditionPathExists=/sys/bus/pci/devices
# This service and binary is included in the initramfs.
[Service]
Type=oneshot
ExecStart=/usr/lib/initcpio/nvidia-driver-selection
RemainAfterExit=yes

View File

@@ -146,40 +146,6 @@ if [ -f /usr/lib/modprobe.d/nvidia-utils.conf ]; then
sed -i '/blacklist nouveau/d' /usr/lib/modprobe.d/nvidia-utils.conf
fi
# Generate a huge udev rule which selects the correct driver for NVIDIA graphics cards.
# This gets included in the initrd - see mkosi.extra/usr/lib/rebuild-efi
PCI_IDS="/usr/share/hwdata/pci.ids"
UDEV_RULE="/usr/lib/udev/rules.d/05-nvidia-module-selection.rules"
echo "# Generated by mkosi.postinst.chroot" > "$UDEV_RULE"
echo "# Static driver overrides for NVIDIA GPUs based on ID ranges." >> "$UDEV_RULE"
gawk '
BEGIN { IGNORECASE = 1 }
# Vendor line, e.g. "10de NVIDIA Corporation".
/^10de\b/ { in_nvidia = 1; next }
# Any other vendor line (4 hex digits at start) disables in_nvidia.
/^[0-9A-Fa-f]{4}\s/ { in_nvidia = 0; next }
# device lines are indented and begin with a 4-hex-digit device id
in_nvidia && /^\s+[0-9A-Fa-f]{4}\b/ {
device_id = gensub(/^\s+([0-9A-Fa-f]{4}).*/, "\\1", "g")
dec_id = strtonum("0x" device_id)
# 0x1e00 (7680) is where Turing architecture begins (RTX 20-series / GTX 16-series).
# This is, currently, the cutoff for support in the open kernel modules.
if (dec_id >= 7680) {
driver = "nvidia"
} else {
driver = "nouveau"
}
# Match only Class 03 (Display Controllers) to avoid breaking Audio/USB/Bridges.
printf "ACTION==\"add\", SUBSYSTEM==\"pci\", ATTR{vendor}==\"0x10de\", ATTR{device}==\"0x%s\", ATTR{class}==\"0x03*\", ATTR{driver_override}=\"%s\"\n", device_id, driver
}
' "$PCI_IDS" >> "$UDEV_RULE"
cd /tmp
/usr/lib/rebuild-efi
mv -v ./*.efi /