summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/display_driver_helper461
1 files changed, 461 insertions, 0 deletions
diff --git a/tools/display_driver_helper b/tools/display_driver_helper
new file mode 100644
index 0000000..b4aa290
--- /dev/null
+++ b/tools/display_driver_helper
@@ -0,0 +1,461 @@
+#!/bin/sh
+#
+# Display driver helper
+#
+# Copyright (c) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi>
+#
+# - Load drivers for specified modaliases, skipping disabled display drivers
+# that would cause conflicts (KMS vs. vesa, KMS vs. proprietary).
+# - Get information about enabled driver modules
+# - Check that the loaded modules are correct
+#
+# Licensed under terms of GPLv2 or later.
+#
+# When updating, check:
+# - the variables below
+# - check_driver function
+# - check_dkms_status function
+#
+
+echo "$(date) $*" >> /dev/tmp7
+exec 2>>/dev/tmp7
+set -x
+
+export LC_ALL=C
+
+KMS_DRIVERS="i915 radeon nouveau"
+# module names at run-time (hence nvidia instead of nvidia*):
+KNOWN_MODULES="i915|radeon|nouveau|fglrx|nvidia"
+
+XORG_i915="intel"
+CONFLICTS_i915=""
+
+XORG_nouveau="nouveau"
+CONFLICTS_nouveau="nv nvidia"
+
+XORG_radeon="ati radeon"
+CONFLICTS_radeon="fglrx"
+
+# Note: no /usr
+# See end of script for descriptions of global variables.
+check_driver() {
+ local name="$1"
+ case "$name" in
+ i915)
+ # implicitely loaded by X.org
+ check_xorg $name 0 || return 1
+ IS_KMS=1
+ ;;
+ radeon)
+ # implicitely loaded by X.org
+ check_xorg $name 0 || return 1
+ IS_KMS=1
+ # radeon needs to loaded before X server
+ NEEDS_LOAD_NOW=1
+ ;;
+ nouveau)
+ # these KMS drivers require an explicit directive in xorg.conf
+ check_xorg $name 1 || return 1
+ IS_KMS=1
+ ;;
+ fglrx)
+ check_xorg fglrx 1 || return 1
+ check_dkms fglrx || return 1
+ ;;
+ nvidia)
+ # manually installed driver or a call from check_loaded()
+ UNSURE=1
+ check_xorg nvidia 1 || return 1
+ ;;
+ nvidiafb)
+ # this is only reached if nvidiafb is manually unblacklisted
+ return 2
+ ;;
+ nvidia*)
+ [ "$name" = "nvidia_current" ] && name=nvidia-current
+ # there are multiple co-installable driver versions, so check
+ # the active alternative as well
+ check_gl /etc/$name/ld.so.conf || return 1
+ check_xorg nvidia 1 || return 1
+ check_dkms $name || return 1
+ ;;
+ *)
+ # unknown, will be loaded only if no known drivers were found
+ return 2
+ ;;
+ esac
+ return 0
+}
+
+# Return success if there is no new pending DKMS build (needed to disallow
+# speedboot on very early boot).
+# Previously failed build or a missing module is counted as no pending build.
+# Note: no /usr
+check_dkms_status() {
+ [ -e /etc/alternatives/gl_conf ] || return 0
+ # return fast for non-DKMS
+ check_gl /etc/ld.so.conf.d/GL/standard.conf && return 0
+
+ local active="$(ls -l /etc/alternatives/gl_conf | awk '{ print $NF }')"
+
+ local modname=
+
+ case $active in
+ /etc/nvidia*/ld.so.conf)
+ modname="nvidia${active#*nvidia}"
+ modname="${modname%/*}"
+ ;;
+ /etc/ld.so.conf.d/GL/ati.conf)
+ modname="fglrx"
+ ;;
+ *)
+ # Unknown DKMS-looking driver,
+ # allow speedboot.
+ return 0
+ ;;
+ esac
+ check_dkms "$modname" 1
+}
+
+# Check if all loaded kernel modules have correct xorg.conf
+check_loaded() {
+ for module in $(grep -oE "^($KNOWN_MODULES) " /proc/modules); do
+ check_driver "$module" || return 1
+ done
+ return 0
+}
+
+# Check that specified DKMS driver is not queued for build for the current
+# kernel. Used to check if we 1) should disable speedboot for this boot
+# (--check-dkms-status), and 2) if should should load the currently
+# existing driver (--load). Doing otherwise might cause us to load a wrong old
+# version of the driver that had been installed using e.g. binary DKMS
+# packages.
+# Note: no /usr
+check_dkms() {
+ local driver="$1"
+ local force="$2"
+
+ # If called from DKMS itself or we are not in rc.sysinit anymore,
+ # there are no pending builds.
+ if [ -z "$force" ]; then
+ [ "$DKMS_AUTOLOAD_MODULE" = "$driver" ] && return 0
+ [ -z "$STARTUP" ] && [ ! -f "/dev/.in_sysinit" ] && return 0
+ fi
+
+ local found=
+ local uname_r="$(uname -r)"
+
+ for dir in /var/lib/dkms/$driver/*; do
+ [ -e "$dir" ] || return 0 # no module, no build; or no /var
+ [ -L "$dir" ] && continue # not a module version
+ found=1 # module version found
+ [ -e "$dir/$uname_r" ] && return 0
+ [ -e "/var/lib/dkms-binary/$driver/$(basename "$dir")/$uname_r" ] && return 0
+
+ if [ -e "$dir/build/make.log" ]; then
+ # Build has failed for some kernel, check if it is this one.
+ # If so, there is no point in returning 1.
+ grep -q "^DKMS make\.log.* $uname_r " && return 0
+ fi
+ done
+
+ # if module versions were found but none were built for this kernel, return 1
+ [ -n "$found" ] && return 1 || return 0
+}
+
+# Note: no /usr
+check_gl() {
+ local alt_inode="$(stat -L -c%i $1 2>/dev/null)"
+ [ -n "$alt_inode" ] || return 1
+ [ -n "$GL_INODE" ] || GL_INODE="$(stat -L -c%i /etc/alternatives/gl_conf 2>/dev/null)"
+ [ "$alt_inode" = "$GL_INODE" ] || return 1
+ return 0
+}
+
+# Note: no /usr
+get_xorg_drivers() {
+ if [ -z "$XORG_DRIVERS" ]; then
+ XORG_DRIVERS="$(cat /etc/X11/xorg.conf /etc/X11/xorg.conf.d/*.conf 2>/dev/null |
+ awk -F'"' -vORS=' ' -vIGNORECASE=1 '
+ /^[[:space:]]+*section[[:space:]]+"device"/ { device=1 }
+ /endsection/ { device=0 }
+ /^[[:space:]]*driver[[:space:]]*".*"/ { if (device) drivers[$2]=$2 }
+ END { for (driver in drivers) print driver }
+ ')"
+ [ -n "$XORG_DRIVERS" ] || XORG_DRIVERS="-"
+ fi
+}
+
+# Note: no /usr
+# parameter 1: xorg driver
+# parameter 2: 1 the check if the driver is explicitely enabled
+# 0 means that check only for conflicts
+check_xorg() {
+ local driver="$1"
+ local explicit_only="$2"
+
+ eval local xorg_drivers=\"\$XORG_$driver\"
+ eval local conflicts=\"\$CONFLICTS_$driver\"
+
+ get_xorg_drivers
+
+ conflict_found=
+ for enabled_driver in $XORG_DRIVERS; do
+ for xorg_driver in $xorg_drivers; do
+ [ "$enabled_driver" = "$xorg_driver" ] && return 0
+ done
+
+ # if the X.org driver can be loaded implicitely, check that
+ # there are no conflicting drivers that override the driver
+ if [ "$explicit_only" = "0" -a -z "$conflict_found" ]; then
+ for conflict in vesa $conflicts; do
+ if [ "$enabled_driver" = "$conflict" ]; then
+ conflict_found=1
+ continue 2
+ # continue loop to check for an explicit load
+ fi
+ done
+ fi
+ done
+
+ # in case of a conflict, do not load the module
+ [ -n "$conflict_found" ] && return 1
+
+ # no driver is selected - don't load if explicit_only is 1
+ [ "$explicit_only" = "1" ] && return 1
+
+ # implicit load allowed; still don't load if no xorg.conf (i.e. live cd)
+ [ -e "/etc/X11/xorg.conf" ]
+}
+
+# Load the driver for the specified modalias, if configured.
+# Note: no /usr
+load_driver() {
+ local modulename
+ local load_default=1
+
+ # NOTE: UPDATE when module-init-tools is upgraded to get better performance
+ # modprobe has -R option:
+ #for modulename in $(/home/anssi/module-init-tools-3.12/build/modprobe -Rq "$1"); do
+ # modprobe does not have -R option:
+ for mod in $(/sbin/modprobe -biqvn "$1"); do
+ [ "$mod" = "insmod" ] && continue
+ modulename="${mod##*/}"
+ modulename="${modulename%%.*}"
+
+ check_driver "$modulename"
+ case $? in
+ 1) # a driver which needs handling by this script matches
+ # the modalias, but was not configured - do not run
+ # the generic modprobe if no other drivers are
+ # configured either
+ load_default=
+ continue
+ ;;
+ 2) continue
+ ;;
+ esac
+
+ if [ -n "$IS_KMS" ]; then
+ grep -q "^$modulename " /proc/modules && return 0
+ echo "$modulename" > /dev/.late_kms 2>/dev/null
+ # If NEEDS_LOAD_NOW is not set and plymouth is running,
+ # skip loading the driver to avoid quitting plymouth.
+ # The driver will be loaded later by X server itself.
+ [ -z "$NEEDS_LOAD_NOW" ] && /bin/plymouth --ping 2>/dev/null && return 0
+ /bin/plymouth quit 2>/dev/null
+ fi
+ /sbin/modprobe -b "$modulename" && return 0
+ done
+
+ # no specially handled modules were loaded, so load all modules normally
+ # unless $load_default was set above
+ [ -z "$load_default" ] || /sbin/modprobe -b "$1"
+}
+
+is_kms_allowed() {
+ for driver in $KMS_DRIVERS; do
+ # Check all drivers for conflicts only.
+ check_xorg $driver 0 || return 1
+ done
+ return 0
+}
+
+get_initrd_kms_drivers() {
+ local initrd="$1"
+
+ local kms_drivers="$(echo "$KMS_DRIVERS" | tr " " "|")"
+ zcat "$initrd" | cpio -t --quiet | sed -nr "s,.*/($kms_drivers)\.ko.*$,\1,p"
+}
+
+# Check that the initrd doesn't contain disabled modules
+check_initrd() {
+ local initrd="$1"
+ local initrd_drivers="$(get_initrd_kms_drivers "$initrd")"
+ for driver in $initrd_drivers; do
+ check_driver "$driver" || return 1
+ done
+ for driver2 in $(get_active_kms_drivers); do
+ for driver in $initrd_drivers; do
+ [ "$driver" = "$driver2" ] && continue 2
+ done
+ # An enabled module for present hardware was not in initrd
+ return 1
+ done
+ return 0
+}
+
+get_active_kms_drivers() {
+ local kms_drivers=
+ for device in $(grep -l 0x03 /sys/bus/pci/devices/0000\:0*/class); do
+ [ -e "$device" ] || continue
+ device="$(dirname $device)"
+ [ -f "$device/modalias" ] || continue
+ modalias="$(cat "$device/modalias")"
+ for mod in $(/sbin/modprobe --first-time -biqvn "$modalias" 2>&1); do
+ modulename="${mod##*/}"
+ modulename="${modulename%%.*}"
+ IS_KMS=
+ check_driver "$modulename" || continue
+ [ -n "$IS_KMS" ] && echo $modulename
+ done
+ done
+}
+
+usage() {
+ cat <<EOF
+Usage: $0 action [arguments]
+
+Known actions:
+
+ --load MODALIAS
+ Load drivers matching MODALIAS, checking that they are enabled and
+ configured.
+
+ --load-dkms-autoload MODNAME MODALIAS
+ Same as --load, but assume MODNAME is built and correct so that
+ checking dkms status is unnecessary.
+
+ --is-disabled MODNAME
+ Checks whether the driver corresponding to MODNAME is disabled (e.g.
+ a conflicting driver is configured, etc.). Unknown MODNAMEs are
+ considered not disabled.
+
+ --is-enabled-kms MODNAME
+ Checks whether the driver corresponding to MODNAME is enabled and
+ MODNAME is a known KMS module. Note that drivers may be enabled even
+ if there is no such hardware. This just checks that there are
+ no conflicting drivers in use etc.
+
+ --is-kms-allowed
+ Checks whether it is ok to load KMS drivers in initrd. This returns
+ a failure when a conflicting driver is set up (vesa or a proprietary
+ one).
+
+ --get-all-kms-drivers
+ Get a list of the known KMS drivers.
+
+ --get-active-kms-drivers
+ Get a list of the known KMS drivers which are enabled and the hardware
+ is present.
+
+ --get-initrd-kms-drivers INITRD
+ Get a list of the known KMS drivers in initrd INITRD.
+
+ --check-dkms-status
+ Checks if there are no pending DKMS builds for the currently enabled
+ drivers.
+
+ --check-loaded
+ Checks that there are no disabled drivers loaded.
+
+ --check-speedboot
+ Does --check-dkms-status and --check-loaded.
+
+ --check-loaded-strict
+ As --check-loaded, and consider ambigious cases (e.g. nvidia where
+ we can't detect if the loaded driver has the correct version) as
+ failure.
+
+ --check-initrd INITRD
+ Check that INITRD doesn't contain disabled KMS drivers.
+EOF
+}
+
+# clear global variables
+
+# cache for check_gl()
+GL_INODE=
+
+# cache for check_xorg()
+XORG_DRIVERS=
+
+# The driver is a KMS enabled driver. This will cause the script to quit
+# plymouth when a driver is loaded by --load and NEEDS_LOAD_NOW below is set.
+# This is done as plymouth is still attached to the default framebuffer (the
+# issues caused by not doing this don't seem to be fatal, though, but the
+# display may be lost completely until plymouth eventually stops).
+# There is no option in plymouth to "reload" a driver, it expects any KMS
+# driver to be loaded be before starting it.
+IS_KMS=
+
+# This KMS driver needs to be loaded before X server starts, so load it now
+# even if we have to shut down plymouth (see above).
+NEEDS_LOAD_NOW=
+
+# dkms module that was built when calling from DKMS
+DKMS_AUTOLOAD_MODULE=
+
+# Set by check_loaded() when it can't be sure that the correct driver is loaded
+# (e.g. in case of the multiple proprietary nvidia drivers which all identify as
+# "nvidia" in loaded modules list).
+UNSURE=
+
+case "$1" in
+--load)
+ load_driver "$2"
+ ;;
+--load-dkms-autoload)
+ DKMS_AUTOLOAD_MODULE="$2"
+ load_driver "$3"
+ ;;
+--is-disabled)
+ check_driver "$2"
+ [ $? -eq 1 ]
+ # unknown (2) are not considered disabled :)
+ ;;
+--is-enabled-kms)
+ check_driver "$2" && [ -n "$IS_KMS" ]
+ ;;
+--is-kms-allowed)
+ is_kms_allowed
+ ;;
+--check-dkms-status)
+ check_dkms_status
+ ;;
+--get-all-kms-drivers)
+ echo $KMS_DRIVERS
+ ;;
+--get-active-kms-drivers)
+ get_active_kms_drivers
+ ;;
+--get-initrd-kms-drivers)
+ get_initrd_kms_drivers "$2"
+ ;;
+--check-initrd)
+ check_initrd "$2"
+ ;;
+--check-loaded)
+ check_loaded
+ ;;
+--check-loaded-strict)
+ check_loaded && [ -z "$UNSURE" ]
+ ;;
+--check-speedboot)
+ check_dkms_status && check_loaded
+ ;;
+*)
+ usage
+ ;;
+esac