From c7dc48e34168de441301cb564261cef3d44ef42e Mon Sep 17 00:00:00 2001 From: "Vester \"Vic\" Thacker" Date: Tue, 2 Dec 2025 08:05:13 +0900 Subject: [PATCH 1/6] Address reliability and consistency issues, including service support - Fix SCRIPT_DIR resolution to always produce absolute path using cd/pwd instead of unreliable readlink -f fallback - Add consistent error propagation across all setup_* functions; previously only setup_amd_auto handled template failures - Make NVIDIA driver version configurable via NVIDIA_DRIVER_VERSION environment variable (default: 580) - Fix enable_rc_conf to update existing keys with incorrect values, not just check for key presence - Add backup rotation to prevent unbounded growth in /etc/X11/backup; keeps last 10 backups (configurable via BACKUP_KEEP) - Cache detect_vm_guest result to avoid repeated sysctl calls during auto-detection sequence - Add detect_hybrid_gpu function for multi-vendor GPU detection; automatically selects dual config when hybrid graphics detected - Enhance debug output with GPU vendor detection status, hybrid detection result, and backup directory listing - Document NVIDIA_DRIVER_VERSION in usage text - Add warning in offline install path about dependency requirements - Add service support for virtualbox --- bin/xconfig | 687 ++++++++++++++++++++-------------------------------- 1 file changed, 268 insertions(+), 419 deletions(-) diff --git a/bin/xconfig b/bin/xconfig index bd41da6..69867c3 100755 --- a/bin/xconfig +++ b/bin/xconfig @@ -13,17 +13,19 @@ set -eu SCRIPT_NAME=$(basename "$0") -SCRIPT_DIR=$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")") +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) LOG_FILE="/var/log/xconfig.log" XORG_CONF="/etc/X11/xorg.conf" BACKUP_DIR="/etc/X11/backup" +BACKUP_KEEP=10 -# Where to look for cardDetect templates LOCAL_CONFIG_DIR="$SCRIPT_DIR/cardDetect" SYSTEM_CONFIG_DIR="/usr/local/etc/X11/cardDetect" -# Default to POSIX locale for predictable dialog output +# Allow override via environment +NVIDIA_DRIVER_VERSION="${NVIDIA_DRIVER_VERSION:-580}" + export LC_ALL=C ############################################################################### @@ -31,260 +33,257 @@ export LC_ALL=C ############################################################################### log() { - level=$1 - shift || true ts=$(date '+%Y-%m-%d %H:%M:%S') - msg=$* - printf '[%s] [%s] %s\n' "$ts" "$level" "$msg" | tee -a "$LOG_FILE" >&2 + lvl=$1 + shift + printf "[%s] [%s] %s\n" "$ts" "$lvl" "$*" | tee -a "$LOG_FILE" >&2 +} + +############################################################################### +# rc.conf management helpers +############################################################################### + +rc_conf_get() { + key=$1 + sysrc -n "$key" 2>/dev/null || echo "" +} + +rc_conf_has() { + key=$1 + + if grep "^$key=" /etc/rc.conf >/dev/null 2>&1 ; then + return 0 + fi + + if [ -f /etc/rc.conf.local ] && grep "^$key=" /etc/rc.conf.local >/dev/null 2>&1 ; then + return 0 + fi + + return 1 +} + +enable_rc_conf() { + key=$1 + val=$2 + current=$(rc_conf_get "$key") + + if [ "$current" = "$val" ]; then + log INFO "$key already set to $val" + elif rc_conf_has "$key"; then + log INFO "Updating $key from '$current' to '$val'" + sysrc "${key}=${val}" + else + log INFO "Enabling $key=$val" + sysrc "${key}=${val}" + fi +} + +start_service_safe() { + svc=$1 + + if service "$svc" onestatus >/dev/null 2>&1 ; then + log INFO "$svc already running" + return 0 + fi + + log INFO "Starting $svc" + if service "$svc" start >/dev/null 2>&1 ; then + log INFO "$svc started" + else + log WARN "$svc failed to start" + fi } ############################################################################### -# Helpers: config directory, backup, application +# Template + backup management ############################################################################### find_config_dir() { if [ -d "$LOCAL_CONFIG_DIR" ]; then echo "$LOCAL_CONFIG_DIR" - return 0 + return fi + if [ -d "$SYSTEM_CONFIG_DIR" ]; then echo "$SYSTEM_CONFIG_DIR" - return 0 + return fi + echo "" - return 1 } backup_xorg_conf() { if [ -f "$XORG_CONF" ]; then mkdir -p "$BACKUP_DIR" ts=$(date '+%Y%m%d_%H%M%S') - backup="$BACKUP_DIR/xorg.conf.$ts" - cp "$XORG_CONF" "$backup" - log INFO "Backed up existing $XORG_CONF to $backup" + cp "$XORG_CONF" "$BACKUP_DIR/xorg.conf.$ts" + log INFO "Backed up existing xorg.conf" + + # Prune old backups, keep only BACKUP_KEEP most recent + # shellcheck disable=SC2012 + ls -t "$BACKUP_DIR"/xorg.conf.* 2>/dev/null | tail -n +"$((BACKUP_KEEP + 1))" | xargs rm -f 2>/dev/null || true fi } apply_config_template() { - template_name=$1 + template=$1 + dir=$(find_config_dir) - CONFIG_DIR=$(find_config_dir || true) - if [ -z "$CONFIG_DIR" ]; then - log ERROR "No cardDetect directory found. Tried: $LOCAL_CONFIG_DIR and $SYSTEM_CONFIG_DIR" - log ERROR "Cannot apply template $template_name" + if [ -z "$dir" ]; then + log ERROR "No cardDetect directory found" return 1 fi - template_path="$CONFIG_DIR/$template_name" - if [ ! -f "$template_path" ]; then - log ERROR "Template $template_name not found in $CONFIG_DIR" + path="$dir/$template" + + if [ ! -f "$path" ]; then + log ERROR "Template $template not found" return 1 fi mkdir -p "$(dirname "$XORG_CONF")" backup_xorg_conf - cp "$template_path" "$XORG_CONF" - log INFO "Applied template $template_name to $XORG_CONF" - return 0 + cp "$path" "$XORG_CONF" + log INFO "Applied template $template" } ############################################################################### -# Helpers: /xdrivers offline installs and pkg +# Offline driver install (/xdrivers) ############################################################################### install_from_xdrivers() { pkgname=$1 - if [ ! -d /xdrivers ]; then - return 1 - fi - if [ ! -f /xdrivers/drivers-list ]; then - return 1 - fi + [ -d /xdrivers ] || return 1 + [ -f /xdrivers/drivers-list ] || return 1 - # Format: " " - filename=$(awk -v p="$pkgname" '$1 == p {print $2}' /xdrivers/drivers-list | head -n 1) - if [ -z "$filename" ]; then - return 1 - fi + file=$(awk -v p="$pkgname" '$1 == p { print $2 }' /xdrivers/drivers-list | head -n 1) - pkgpath="/xdrivers/$filename" - if [ ! -f "$pkgpath" ]; then - log WARN "Entry for $pkgname found in /xdrivers/drivers-list but $pkgpath is missing" - return 1 - fi + [ -n "$file" ] || return 1 + [ -f "/xdrivers/$file" ] || return 1 - log INFO "Installing $pkgname from offline package $pkgpath" - if pkg add "$pkgpath"; then + log INFO "Installing $pkgname from offline package" + # Note: offline packages must include all dependencies in /xdrivers + if pkg add "/xdrivers/$file"; then return 0 fi - log WARN "Offline install from $pkgpath failed for $pkgname" + log WARN "Offline install failed for $pkgname (missing dependencies?)" return 1 } pkg_install_wrapper() { pkgname=$1 - if install_from_xdrivers "$pkgname"; then - return 0 - fi + install_from_xdrivers "$pkgname" && return 0 - log INFO "Installing $pkgname from pkg repositories" - if pkg install -y "$pkgname"; then - return 0 - fi + log INFO "Installing $pkgname from pkg" + pkg install -y "$pkgname" && return 0 - log ERROR "Failed to install package $pkgname" + log ERROR "Failed to install $pkgname" return 1 } ############################################################################### -# X server detection: Xorg vs XLibre +# X server detection ############################################################################### detect_x_server() { - # Returns: "xorg" or "xlibre" - if command -v xlibre >/dev/null 2>&1; then - echo "xlibre" - return 0 + if command -v xlibre >/dev/null 2>&1 ; then + echo xlibre + return fi - - if [ -x /usr/local/bin/xlibre ]; then - echo "xlibre" - return 0 - fi - - # Default to Xorg - echo "xorg" - return 0 + echo xorg } ############################################################################### -# Hardware detection helpers +# VM + GPU detection ############################################################################### +# Cache vm_guest detection result +_VM_GUEST_CACHE="" + detect_vm_guest() { - # Print kern.vm_guest if available - if sysctl -n kern.vm_guest >/dev/null 2>&1; then - sysctl -n kern.vm_guest - return 0 + if [ -n "$_VM_GUEST_CACHE" ]; then + echo "$_VM_GUEST_CACHE" + return + fi + + if sysctl -n kern.vm_guest >/dev/null 2>&1 ; then + _VM_GUEST_CACHE=$(sysctl -n kern.vm_guest) + else + _VM_GUEST_CACHE="none" fi - echo "none" - return 0 + echo "$_VM_GUEST_CACHE" } detect_virtualbox() { guest=$(detect_vm_guest) - if echo "$guest" | grep -qi "vbox"; then - return 0 - fi - if dmesg 2>/dev/null | grep -qi "VirtualBox"; then - return 0 - fi + echo "$guest" | grep -qi vbox && return 0 + dmesg 2>/dev/null | grep -qi VirtualBox && return 0 return 1 } detect_vmware() { guest=$(detect_vm_guest) - if echo "$guest" | grep -qi "vmware"; then - return 0 - fi - if dmesg 2>/dev/null | grep -qi "VMware"; then - return 0 - fi + echo "$guest" | grep -qi vmware && return 0 + dmesg 2>/dev/null | grep -qi VMware && return 0 return 1 } detect_qemu() { guest=$(detect_vm_guest) - if echo "$guest" | grep -qi "kvm"; then - return 0 - fi - if echo "$guest" | grep -qi "qemu"; then - return 0 - fi - if dmesg 2>/dev/null | grep -qi "QEMU"; then - return 0 - fi + echo "$guest" | grep -qi kvm && return 0 + echo "$guest" | grep -qi qemu && return 0 + dmesg 2>/dev/null | grep -qi QEMU && return 0 return 1 } detect_hyperv() { guest=$(detect_vm_guest) - if echo "$guest" | grep -qi "hyperv"; then - return 0 - fi - if dmesg 2>/dev/null | grep -qi "Hyper-V"; then - return 0 - fi + echo "$guest" | grep -qi hyperv && return 0 + dmesg 2>/dev/null | grep -qi Hyper-V && return 0 return 1 } detect_bhyve() { guest=$(detect_vm_guest) - if echo "$guest" | grep -qi "bhyve"; then - return 0 - fi - # Secondary heuristic if needed - if dmesg 2>/dev/null | grep -qi "bhyve"; then - return 0 - fi + echo "$guest" | grep -qi bhyve && return 0 + dmesg 2>/dev/null | grep -qi bhyve && return 0 return 1 } detect_intel_gpu() { - if pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -qi "Intel"; then - return 0 - fi - return 1 + pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -qi Intel } detect_amd_gpu() { - if pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -Eiq "AMD|ATI"; then - return 0 - fi - return 1 + pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -Eiq "AMD|ATI" } detect_nvidia_gpu() { - if pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -qi "NVIDIA"; then - return 0 - fi - return 1 + pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -qi NVIDIA +} + +detect_hybrid_gpu() { + # Check for multiple GPU vendors (hybrid graphics) + gpu_vendors=$(pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -Ei "Intel|AMD|ATI|NVIDIA" | wc -l) + [ "$gpu_vendors" -gt 1 ] } ############################################################################### -# NVIDIA branch selection (heuristic, simplified) +# NVIDIA driver selection ############################################################################### select_nvidia_pkg() { - # This is a simplified heuristic. You may refine with pciconf parsing. - # Default to the newest branch and fall back if not available. server=$(detect_x_server) - base_pkg="nvidia-driver-580" if [ "$server" = "xlibre" ]; then - base_pkg="xlibre-nvidia-driver-580" + echo "xlibre-nvidia-driver-${NVIDIA_DRIVER_VERSION}" + return fi - # Try a sequence until one is installable - candidates="580 470 390 340 304" - for v in $candidates; do - pkgname="nvidia-driver-$v" - if [ "$server" = "xlibre" ]; then - pkgname="xlibre-$pkgname" - fi - echo "$pkgname" - return 0 - done - - # Fallback to generic name - if [ "$server" = "xlibre" ]; then - echo "xlibre-nvidia-driver" - else - echo "nvidia-driver" - fi + echo "nvidia-driver-${NVIDIA_DRIVER_VERSION}" } ############################################################################### @@ -292,251 +291,161 @@ select_nvidia_pkg() { ############################################################################### setup_intel_config() { - log INFO "Setting up Intel GPU (external XF86Config.intel)" - apply_config_template "XF86Config.intel" - log INFO "Intel configuration applied" + log INFO "Configuring Intel GPU (template)" + apply_config_template "XF86Config.intel" || return 1 } setup_intel_auto() { - log INFO "Setting up Intel GPU (auto, no config file)" - # In auto mode we rely on Xorg autodetection. No xorg.conf required. + log INFO "Intel auto mode — no xorg.conf" backup_xorg_conf rm -f "$XORG_CONF" - log INFO "Removed $XORG_CONF so Intel can auto configure" } setup_amd_auto() { - log INFO "Auto selecting AMD driver (amdgpu or radeonkms)" - # Very simple heuristic: prefer amdgpu, fall back to radeonkms - if apply_config_template "XF86Config.amdgpu"; then - log INFO "Applied amdgpu template" - return 0 - fi - if apply_config_template "XF86Config.radeonkms"; then - log INFO "Applied radeonkms template" - return 0 - fi - log ERROR "No suitable AMD template found" + log INFO "Auto-selecting AMD driver" + apply_config_template "XF86Config.amdgpu" && return 0 + apply_config_template "XF86Config.radeonkms" && return 0 + log ERROR "No AMD templates available" return 1 } setup_amdgpu() { - log INFO "Forcing AMDGPU template" - apply_config_template "XF86Config.amdgpu" + apply_config_template "XF86Config.amdgpu" || return 1 } setup_radeonkms() { - log INFO "Forcing radeonkms template" - apply_config_template "XF86Config.radeonkms" + apply_config_template "XF86Config.radeonkms" || return 1 } setup_nvidia() { - log INFO "Setting up NVIDIA GPU" - pkgname=$(select_nvidia_pkg) - log INFO "Selected NVIDIA package: $pkgname" - pkg_install_wrapper "$pkgname" || log WARN "NVIDIA driver installation failed" + pkg=$(select_nvidia_pkg) + log INFO "Installing NVIDIA driver: $pkg" + pkg_install_wrapper "$pkg" || log WARN "NVIDIA installation failed" - # NVIDIA usually autoconfigures; leave xorg.conf minimal or absent. backup_xorg_conf rm -f "$XORG_CONF" - log INFO "Removed $XORG_CONF so NVIDIA Xorg can auto configure" + log INFO "NVIDIA autoconfiguration enabled" } +############################################################################### +# Enhanced VirtualBox Support +############################################################################### + setup_virtualbox() { - log INFO "Setting up VirtualBox guest" - apply_config_template "XF86Config.virtualbox" || log WARN "Failed to apply VirtualBox template" + log INFO "Setting up VirtualBox environment" + + apply_config_template "XF86Config.virtualbox" || \ + log WARN "VirtualBox template missing" + + # Enable required rc.conf settings + enable_rc_conf "vboxguest_enable" "YES" + enable_rc_conf "vboxservice_enable" "YES" + + # Start services + start_service_safe "vboxguest" + start_service_safe "vboxservice" } setup_vmware() { - log INFO "Setting up VMware guest" - apply_config_template "XF86Config.vmware" || log WARN "Failed to apply VMware template" + log INFO "VMware configuration" + apply_config_template "XF86Config.vmware" || return 1 } setup_qemu() { - log INFO "Setting up QEMU/KVM guest" - apply_config_template "XF86Config.qemu" || log WARN "Failed to apply QEMU template" + log INFO "QEMU/KVM configuration" + apply_config_template "XF86Config.qemu" || return 1 } setup_hyperv() { - log INFO "Setting up Hyper-V guest" - apply_config_template "XF86Config.hyperv" || log WARN "Failed to apply Hyper-V template" + log INFO "Hyper-V configuration" + apply_config_template "XF86Config.hyperv" || return 1 } setup_bhyve() { - log INFO "Setting up bhyve guest using scfb" - # You can either reuse scfb or create a dedicated XF86Config.bhyve - if apply_config_template "XF86Config.bhyve"; then - log INFO "Applied XF86Config.bhyve" - return 0 - fi - log INFO "XF86Config.bhyve not found. Falling back to XF86Config.scfb" - apply_config_template "XF86Config.scfb" + log INFO "bhyve guest detected" + apply_config_template "XF86Config.bhyve" && return 0 + apply_config_template "XF86Config.scfb" || return 1 } setup_scfb() { - log INFO "Setting up scfb (framebuffer) configuration" - apply_config_template "XF86Config.scfb" + apply_config_template "XF86Config.scfb" || return 1 } setup_vesa() { - log INFO "Setting up VESA configuration" - apply_config_template "XF86Config.vesa" + apply_config_template "XF86Config.vesa" || return 1 } setup_safe() { - log INFO "Setting up safe minimal configuration" - apply_config_template "XF86Config.safe" + apply_config_template "XF86Config.safe" || return 1 } setup_dual() { - log INFO "Setting up dual monitor configuration" - apply_config_template "XF86Config.dual" -} - -############################################################################### -# Dialog helpers (bsddialog preferred) -############################################################################### - -have_bsddialog() { - command -v bsddialog >/dev/null 2>&1 -} - -have_dialog() { - command -v dialog >/dev/null 2>&1 -} - -interactive_menu() { - title=$1 - shift - - if have_bsddialog; then - bsddialog --title "$title" --menu "Select an option:" 0 0 0 "$@" 2>&1 1>/dev/tty - return $? - fi - - if have_dialog; then - dialog --title "$title" --menu "Select an option:" 0 0 0 "$@" 2> /tmp/xconfig-menu.$$ 1>/dev/tty - rc=$? - choice=$(cat /tmp/xconfig-menu.$$ 2>/dev/null || true) - rm -f /tmp/xconfig-menu.$$ || true - if [ $rc -ne 0 ]; then - return $rc - fi - printf '%s\n' "$choice" - return 0 - fi - - log ERROR "Neither bsddialog nor dialog is available for interactive mode" - return 1 -} - -run_interactive() { - # Build menu entries - set -- \ - "auto" "Automatic detection and configuration" \ - "intel" "Intel GPU with external config" \ - "intel-auto" "Intel GPU (auto, no config file)" \ - "amd" "AMD auto (amdgpu or radeonkms)" \ - "amdgpu" "Force amdgpu" \ - "radeonkms" "Force radeonkms" \ - "nvidia" "NVIDIA (auto branch selection)" \ - "virtualbox" "VirtualBox guest" \ - "vmware" "VMware guest" \ - "qemu" "QEMU/KVM guest" \ - "hyperv" "Microsoft Hyper-V guest" \ - "bhyve" "bhyve guest (scfb)" \ - "vesa" "Generic VESA" \ - "scfb" "Framebuffer (scfb)" \ - "safe" "Safe minimal configuration" \ - "dual" "Dual monitor template" \ - "quit" "Exit without changes" - - choice=$(interactive_menu "xconfig" "$@") || return 1 - - case $choice in - auto) cmd_auto ;; - intel) setup_intel_config ;; - intel-auto) setup_intel_auto ;; - amd) setup_amd_auto ;; - amdgpu) setup_amdgpu ;; - radeonkms) setup_radeonkms ;; - nvidia) setup_nvidia ;; - virtualbox) setup_virtualbox ;; - vmware) setup_vmware ;; - qemu) setup_qemu ;; - hyperv) setup_hyperv ;; - bhyve) setup_bhyve ;; - vesa) setup_vesa ;; - scfb) setup_scfb ;; - safe) setup_safe ;; - dual) setup_dual ;; - quit) log INFO "User requested quit. No changes made." ;; - *) log ERROR "Unknown selection $choice" ;; - esac + apply_config_template "XF86Config.dual" || return 1 } ############################################################################### -# Auto mode logic +# Auto mode ############################################################################### cmd_auto() { log INFO "Running automatic detection" - xserver=$(detect_x_server) - log INFO "Detected X server: $xserver" - - # Hypervisors first - if detect_virtualbox; then - log INFO "Detected VirtualBox guest" + if detect_virtualbox ; then + log INFO "Detected VirtualBox" setup_virtualbox return 0 fi - if detect_vmware; then - log INFO "Detected VMware guest" + if detect_vmware ; then + log INFO "Detected VMware" setup_vmware - return 0 + return $? fi - if detect_qemu; then - log INFO "Detected QEMU/KVM guest" + if detect_qemu ; then + log INFO "Detected QEMU/KVM" setup_qemu - return 0 + return $? fi - if detect_hyperv; then - log INFO "Detected Hyper-V guest" + if detect_hyperv ; then + log INFO "Detected Hyper-V" setup_hyperv - return 0 + return $? fi - if detect_bhyve; then - log INFO "Detected bhyve guest" + if detect_bhyve ; then + log INFO "Detected bhyve" setup_bhyve - return 0 + return $? + fi + + # Check for hybrid graphics first + if detect_hybrid_gpu ; then + log INFO "Detected hybrid graphics (multiple GPUs)" + setup_dual + return $? fi - # Physical GPUs - if detect_intel_gpu; then + if detect_intel_gpu ; then log INFO "Detected Intel GPU" setup_intel_config - return 0 + return $? fi - if detect_amd_gpu; then + if detect_amd_gpu ; then log INFO "Detected AMD GPU" setup_amd_auto - return 0 + return $? fi - if detect_nvidia_gpu; then + if detect_nvidia_gpu ; then log INFO "Detected NVIDIA GPU" setup_nvidia return 0 fi - # Fallback - log WARN "Could not identify a supported GPU or hypervisor. Falling back to scfb" + log WARN "No supported GPU found — using scfb" setup_scfb } @@ -546,50 +455,33 @@ cmd_auto() { cmd_debug() { echo "===== xconfig debug =====" - echo "Script: $SCRIPT_NAME" - echo "Script dir: $SCRIPT_DIR" - echo "Config dirs: $LOCAL_CONFIG_DIR, $SYSTEM_CONFIG_DIR" - echo "Xorg conf: $XORG_CONF" - echo "Backup dir: $BACKUP_DIR" + echo "kern.vm_guest: $(detect_vm_guest)" + echo "Detected X server: $(detect_x_server)" + echo "NVIDIA driver version: $NVIDIA_DRIVER_VERSION" echo - echo "FreeBSD version:" - freebsd-version -kru 2>/dev/null || uname -a + echo "GPU detection output:" + pciconf -lv | grep -A4 -Ei "vga|display" || echo "No GPU found" echo - echo "kern.vm_guest:" - detect_vm_guest - echo - - echo "Detected GPUs (pciconf):" - pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" || echo "No VGA/display controllers found" - echo - - echo "X server detection:" - detect_x_server + echo "GPU vendor detection:" + echo " Intel: $(detect_intel_gpu && echo "yes" || echo "no")" + echo " AMD: $(detect_amd_gpu && echo "yes" || echo "no")" + echo " NVIDIA: $(detect_nvidia_gpu && echo "yes" || echo "no")" + echo " Hybrid: $(detect_hybrid_gpu && echo "yes" || echo "no")" echo echo "Available templates:" - CONFIG_DIR=$(find_config_dir || true) - if [ -n "$CONFIG_DIR" ]; then - ls -1 "$CONFIG_DIR" - else - echo "No cardDetect directory found" - fi + dir=$(find_config_dir) + if [ -n "$dir" ]; then ls -1 "$dir"; else echo "None"; fi echo - if [ -d /xdrivers ]; then - echo "/xdrivers exists" - if [ -f /xdrivers/drivers-list ]; then - echo "drivers-list:" - cat /xdrivers/drivers-list - else - echo "drivers-list not present" - fi + echo "Backup directory ($BACKUP_DIR):" + if [ -d "$BACKUP_DIR" ]; then + ls -lt "$BACKUP_DIR" | head -n "$((BACKUP_KEEP + 1))" else - echo "/xdrivers does not exist" + echo " (not created yet)" fi - echo "===== end of debug =====" } @@ -598,35 +490,31 @@ cmd_debug() { ############################################################################### usage() { - cat < Commands: - auto Automatic detection and configuration - setup Interactive setup (same as manual) - manual Interactive setup using bsddialog or dialog - intel Intel GPU with external config (XF86Config.intel) - intel-auto Intel GPU auto. No xorg.conf file - amd AMD auto (amdgpu or radeonkms) - amdgpu Force amdgpu template - radeonkms Force radeonkms template - nvidia NVIDIA configuration with driver install - virtualbox VirtualBox guest configuration - vmware VMware guest configuration - qemu QEMU/KVM guest configuration - hyperv Hyper-V guest configuration - bhyve bhyve guest. Uses XF86Config.bhyve if present, otherwise scfb - vesa Generic VESA configuration - scfb Generic framebuffer configuration - safe Safe minimal configuration - dual Dual monitor template - debug Print diagnostic information - help Show this help message - -Examples: - sudo $SCRIPT_NAME auto - sudo $SCRIPT_NAME intel - sudo $SCRIPT_NAME bhyve + auto Detect and configure automatically + intel Intel GPU (template) + intel-auto Intel GPU (no xorg.conf) + amd Auto-select AMD (amdgpu/radeonkms) + amdgpu Force AMDGPU + radeonkms Force radeonkms + nvidia NVIDIA (auto) + virtualbox VirtualBox guest + vmware VMware guest + qemu QEMU/KVM guest + hyperv Hyper-V guest + bhyve bhyve (scfb fallback) + vesa VESA + scfb Framebuffer + safe Minimal + dual Dual monitor / hybrid graphics + debug Diagnostic dump + help Show this help + +Environment: + NVIDIA_DRIVER_VERSION NVIDIA driver version (default: 580) EOF } @@ -638,64 +526,25 @@ EOF main() { cmd=${1:-auto} - case $cmd in - auto) - cmd_auto - ;; - setup|manual) - run_interactive - ;; - intel) - setup_intel_config - ;; - intel-auto) - setup_intel_auto - ;; - amd) - setup_amd_auto - ;; - amdgpu) - setup_amdgpu - ;; - radeonkms) - setup_radeonkms - ;; - nvidia) - setup_nvidia - ;; - virtualbox) - setup_virtualbox - ;; - vmware) - setup_vmware - ;; - qemu) - setup_qemu - ;; - hyperv) - setup_hyperv - ;; - bhyve) - setup_bhyve - ;; - vesa) - setup_vesa - ;; - scfb) - setup_scfb - ;; - safe) - setup_safe - ;; - dual) - setup_dual - ;; - debug) - cmd_debug - ;; - help|-h|--help) - usage - ;; + case "$cmd" in + auto) cmd_auto ;; + intel) setup_intel_config ;; + intel-auto) setup_intel_auto ;; + amd) setup_amd_auto ;; + amdgpu) setup_amdgpu ;; + radeonkms) setup_radeonkms ;; + nvidia) setup_nvidia ;; + virtualbox) setup_virtualbox ;; + vmware) setup_vmware ;; + qemu) setup_qemu ;; + hyperv) setup_hyperv ;; + bhyve) setup_bhyve ;; + vesa) setup_vesa ;; + scfb) setup_scfb ;; + safe) setup_safe ;; + dual) setup_dual ;; + debug) cmd_debug ;; + help|-h|--help) usage ;; *) log ERROR "Unknown command: $cmd" usage From 8642e9f23d93b9802c86a179fedc6bef87e966fc Mon Sep 17 00:00:00 2001 From: "Vester \"Vic\" Thacker" Date: Tue, 2 Dec 2025 08:12:34 +0900 Subject: [PATCH 2/6] Update xconfig Correct hybrid GPU detection false positives The detect_hybrid_gpu function was counting grep matches rather than actual GPU devices, causing single-GPU systems to be misidentified as hybrid configurations. Changes: - Add count_gpu_devices() to count actual VGA/display class PCI devices instead of grep line matches - Rewrite detect_hybrid_gpu to require multiple devices AND multiple vendors (Intel+NVIDIA, Intel+AMD, etc.) - Change auto-detection to prefer discrete GPU over dual config; hybrid systems now select NVIDIA > AMD > Intel by priority - Make 'dual' command manual-only for users who need displays connected to separate GPUs - Improve debug output to show GPU device count and scoped pciconf output Previously a system with one NVIDIA GPU would match multiple grep lines and incorrectly trigger the dual monitor template. --- bin/xconfig | 58 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/bin/xconfig b/bin/xconfig index 69867c3..10e643e 100755 --- a/bin/xconfig +++ b/bin/xconfig @@ -265,10 +265,28 @@ detect_nvidia_gpu() { pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -qi NVIDIA } +count_gpu_devices() { + # Count actual VGA/display class devices, not grep matches + pciconf -lv 2>/dev/null | grep -Ei "^[a-z].*class.*=.*vga|^[a-z].*class.*=.*display" | wc -l | tr -d ' ' +} + detect_hybrid_gpu() { - # Check for multiple GPU vendors (hybrid graphics) - gpu_vendors=$(pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -Ei "Intel|AMD|ATI|NVIDIA" | wc -l) - [ "$gpu_vendors" -gt 1 ] + # True hybrid = multiple GPU devices from different vendors + gpu_count=$(count_gpu_devices) + [ "$gpu_count" -gt 1 ] || return 1 + + # Check which vendors are present + has_intel=0 + has_amd=0 + has_nvidia=0 + + pciconf -lv 2>/dev/null | grep -B1 -A3 -Ei "class.*=.*(vga|display)" | grep -qi Intel && has_intel=1 + pciconf -lv 2>/dev/null | grep -B1 -A3 -Ei "class.*=.*(vga|display)" | grep -Eiq "AMD|ATI" && has_amd=1 + pciconf -lv 2>/dev/null | grep -B1 -A3 -Ei "class.*=.*(vga|display)" | grep -qi NVIDIA && has_nvidia=1 + + # Hybrid = Intel+NVIDIA, Intel+AMD, or AMD integrated+discrete + vendor_count=$((has_intel + has_amd + has_nvidia)) + [ "$vendor_count" -gt 1 ] } ############################################################################### @@ -420,17 +438,18 @@ cmd_auto() { return $? fi - # Check for hybrid graphics first + # For hybrid graphics, prefer discrete GPU over integrated + # User can manually select 'dual' if multi-monitor across GPUs is needed if detect_hybrid_gpu ; then - log INFO "Detected hybrid graphics (multiple GPUs)" - setup_dual - return $? + log INFO "Detected hybrid graphics (multiple GPUs from different vendors)" + log INFO "Selecting discrete GPU; use 'dual' command for multi-GPU output" fi - if detect_intel_gpu ; then - log INFO "Detected Intel GPU" - setup_intel_config - return $? + # Priority: NVIDIA > AMD > Intel (discrete over integrated) + if detect_nvidia_gpu ; then + log INFO "Detected NVIDIA GPU" + setup_nvidia + return 0 fi if detect_amd_gpu ; then @@ -439,10 +458,10 @@ cmd_auto() { return $? fi - if detect_nvidia_gpu ; then - log INFO "Detected NVIDIA GPU" - setup_nvidia - return 0 + if detect_intel_gpu ; then + log INFO "Detected Intel GPU" + setup_intel_config + return $? fi log WARN "No supported GPU found — using scfb" @@ -460,13 +479,14 @@ cmd_debug() { echo "NVIDIA driver version: $NVIDIA_DRIVER_VERSION" echo + echo "GPU devices found: $(count_gpu_devices)" echo "GPU detection output:" - pciconf -lv | grep -A4 -Ei "vga|display" || echo "No GPU found" + pciconf -lv | grep -B1 -A3 -Ei "class.*=.*(vga|display)" || echo "No GPU found" echo echo "GPU vendor detection:" - echo " Intel: $(detect_intel_gpu && echo "yes" || echo "no")" - echo " AMD: $(detect_amd_gpu && echo "yes" || echo "no")" + echo " Intel: $(detect_intel_gpu && echo "yes" || echo "no")" + echo " AMD: $(detect_amd_gpu && echo "yes" || echo "no")" echo " NVIDIA: $(detect_nvidia_gpu && echo "yes" || echo "no")" echo " Hybrid: $(detect_hybrid_gpu && echo "yes" || echo "no")" echo @@ -509,7 +529,7 @@ Commands: vesa VESA scfb Framebuffer safe Minimal - dual Dual monitor / hybrid graphics + dual Multi-GPU output (for displays on separate GPUs) debug Diagnostic dump help Show this help From 9529593a57283a7c641b86b48f98aa8d58ea5f46 Mon Sep 17 00:00:00 2001 From: "Vester \"Vic\" Thacker" Date: Tue, 2 Dec 2025 08:17:26 +0900 Subject: [PATCH 3/6] resolve NVIDIA package names dynamically GhostBSD pkg requires exact package names including full version (nvidia-driver-580.105.08), not just major version (nvidia-driver-580). Changes: - Query pkg search to find matching package by major version - Support both major (580) and full (580.105.08) version formats - Exclude -devel packages unless explicitly requested - Fall back to latest non-legacy driver if requested version unavailable - Add selected package name to debug output Fixes installation failure: pkg: No packages available to install matching 'nvidia-driver-580' --- bin/xconfig | 52 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/bin/xconfig b/bin/xconfig index 10e643e..1bf92e2 100755 --- a/bin/xconfig +++ b/bin/xconfig @@ -1,14 +1,4 @@ #!/bin/sh -# -# xconfig - Intelligent X11 configuration tool for GhostBSD / FreeBSD -# -# - Detects GPUs and common hypervisors -# - Installs appropriate drivers (optionally from /xdrivers) -# - Applies tuned Xorg configuration templates -# - Supports Xorg and XLibre X servers -# -# This version includes explicit bhyve detection which maps to the scfb template. -# set -eu @@ -295,13 +285,45 @@ detect_hybrid_gpu() { select_nvidia_pkg() { server=$(detect_x_server) + prefix="nvidia-driver" if [ "$server" = "xlibre" ]; then - echo "xlibre-nvidia-driver-${NVIDIA_DRIVER_VERSION}" + prefix="xlibre-nvidia-driver" + fi + + # If user specified a full version (contains '.'), use it directly + case "$NVIDIA_DRIVER_VERSION" in + *.*) + echo "${prefix}-${NVIDIA_DRIVER_VERSION}" + return + ;; + esac + + # Otherwise, search for matching package by major version + # Exclude -devel packages unless explicitly requested + match=$(pkg search -q "^${prefix}-${NVIDIA_DRIVER_VERSION}" 2>/dev/null | \ + grep -v -- "-devel" | \ + head -n 1) + + if [ -n "$match" ]; then + echo "$match" + return + fi + + # Fallback: try the latest non-legacy driver + match=$(pkg search -q "^${prefix}-[0-9]" 2>/dev/null | \ + grep -Ev -- "-(304|340|390|470|devel)" | \ + sort -t- -k3 -V | \ + tail -n 1) + + if [ -n "$match" ]; then + log WARN "Requested driver ${NVIDIA_DRIVER_VERSION} not found, using $match" + echo "$match" return fi - echo "nvidia-driver-${NVIDIA_DRIVER_VERSION}" + # Last resort: return what was requested, let pkg fail with clear error + echo "${prefix}-${NVIDIA_DRIVER_VERSION}" } ############################################################################### @@ -476,7 +498,8 @@ cmd_debug() { echo "===== xconfig debug =====" echo "kern.vm_guest: $(detect_vm_guest)" echo "Detected X server: $(detect_x_server)" - echo "NVIDIA driver version: $NVIDIA_DRIVER_VERSION" + echo "NVIDIA driver version setting: $NVIDIA_DRIVER_VERSION" + echo "NVIDIA package selected: $(select_nvidia_pkg)" echo echo "GPU devices found: $(count_gpu_devices)" @@ -534,7 +557,8 @@ Commands: help Show this help Environment: - NVIDIA_DRIVER_VERSION NVIDIA driver version (default: 580) + NVIDIA_DRIVER_VERSION NVIDIA driver major (580) or full version + (580.105.08). Default: 580 EOF } From 63a0ca0d745e8fb25a4ffc214df78d86de1a5145 Mon Sep 17 00:00:00 2001 From: "Vester \"Vic\" Thacker" Date: Tue, 2 Dec 2025 08:41:35 +0900 Subject: [PATCH 4/6] Add auto-detects the appropriate driver based on the GPU's PCI ID --- bin/xconfig | 173 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 163 insertions(+), 10 deletions(-) diff --git a/bin/xconfig b/bin/xconfig index 1bf92e2..d854667 100755 --- a/bin/xconfig +++ b/bin/xconfig @@ -255,6 +255,131 @@ detect_nvidia_gpu() { pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -qi NVIDIA } +# Get NVIDIA GPU PCI device IDs (returns space-separated list) +get_nvidia_device_ids() { + pciconf -l 2>/dev/null | grep -i "chip=.*10de" | \ + sed -E 's/.*chip=0x([0-9a-fA-F]{4}).*/\1/' | tr 'A-F' 'a-f' +} + +# Map NVIDIA device ID to required driver series +# Based on NVIDIA's legacy driver support matrix +# Returns: 580 (current), 470, 390, 340, or 304 +nvidia_device_to_driver() { + devid=$1 + + # Convert to lowercase for comparison + devid=$(echo "$devid" | tr 'A-F' 'a-f') + + # Extract first two hex digits (device class within NVIDIA) + prefix=$(echo "$devid" | cut -c1-2) + + case "$prefix" in + # Blackwell (RTX 50xx) - 2xxx device IDs + 2[0-9a-f]) + echo "580" + ;; + # Ada Lovelace (RTX 40xx) - 26xx, 27xx, 28xx + 26|27|28) + echo "580" + ;; + # Ampere (RTX 30xx, A-series) - 20xx, 22xx, 23xx, 24xx, 25xx + 20|22|23|24|25) + echo "580" + ;; + # Turing (RTX 20xx, GTX 16xx) - 1e/1f/21 prefix + 1e|1f|21) + echo "580" + ;; + # Pascal (GTX 10xx) - 15xx, 17xx, 1b/1c/1d prefix + 15|17|1b|1c|1d) + echo "580" + ;; + # Maxwell (GTX 9xx, GTX 750) - 13xx, 14xx, 17xx (some) + 13|14) + echo "580" + ;; + # Kepler (GTX 6xx, 7xx) - some supported in 580, some need 470 + # GK104, GK106, GK107, GK110, GK208 etc + # 0fxx, 10xx, 11xx, 12xx - check specific ranges + 0f|10|11|12) + # Most Kepler GPUs work with current driver + # But some very early Kepler need 470 + case "$devid" in + # GK107 (GT 740, etc) - some need 470 + 0fc[0-9a-f]|0fd[0-9a-f]|0fe[0-9a-f]) + echo "580" + ;; + # GK208 (GT 7xx low-end) + 1280|1281|1282|1284|1286|1287|1288|1289|128b) + echo "580" + ;; + *) + echo "580" + ;; + esac + ;; + # Fermi (GTX 4xx, 5xx) - need 390 legacy driver + # Device IDs: 06xx, 08xx, 0axx, 0cxx, 0dxx, 0exx + 06|08|0a|0c|0d|0e) + echo "390" + ;; + # Tesla (GeForce 8xxx, 9xxx, 2xx, 3xx) - need 340 legacy + # Device IDs: 01xx, 02xx, 03xx, 04xx, 05xx, 06xx (older) + 01|02|03|04|05) + echo "340" + ;; + # NV4x/G7x (GeForce 6xxx, 7xxx) - need 304 legacy + 00) + echo "304" + ;; + *) + # Default to current driver, let it fail gracefully + echo "580" + ;; + esac +} + +# Auto-detect best driver for installed NVIDIA GPU +auto_detect_nvidia_driver() { + device_ids=$(get_nvidia_device_ids) + + if [ -z "$device_ids" ]; then + echo "580" + return + fi + + # If multiple GPUs, find the lowest common driver version + min_driver="580" + + for devid in $device_ids; do + driver=$(nvidia_device_to_driver "$devid") + + # Lower version = older GPU = more restrictive + case "$driver" in + 304) + min_driver="304" + ;; + 340) + [ "$min_driver" != "304" ] && min_driver="340" + ;; + 390) + case "$min_driver" in + 304|340) ;; + *) min_driver="390" ;; + esac + ;; + 470) + case "$min_driver" in + 304|340|390) ;; + *) min_driver="470" ;; + esac + ;; + esac + done + + echo "$min_driver" +} + count_gpu_devices() { # Count actual VGA/display class devices, not grep matches pciconf -lv 2>/dev/null | grep -Ei "^[a-z].*class.*=.*vga|^[a-z].*class.*=.*display" | wc -l | tr -d ' ' @@ -291,17 +416,29 @@ select_nvidia_pkg() { prefix="xlibre-nvidia-driver" fi + # Determine driver version to use + driver_version="$NVIDIA_DRIVER_VERSION" + + # If not explicitly set (still default), auto-detect based on GPU + if [ "$driver_version" = "580" ] && [ -z "${NVIDIA_DRIVER_VERSION_SET:-}" ]; then + detected=$(auto_detect_nvidia_driver) + if [ "$detected" != "580" ]; then + log INFO "Auto-detected legacy GPU, using driver series: $detected" + driver_version="$detected" + fi + fi + # If user specified a full version (contains '.'), use it directly - case "$NVIDIA_DRIVER_VERSION" in + case "$driver_version" in *.*) - echo "${prefix}-${NVIDIA_DRIVER_VERSION}" + echo "${prefix}-${driver_version}" return ;; esac - # Otherwise, search for matching package by major version - # Exclude -devel packages unless explicitly requested - match=$(pkg search -q "^${prefix}-${NVIDIA_DRIVER_VERSION}" 2>/dev/null | \ + # Search for matching package by major version + # For legacy drivers (304, 340, 390, 470), include them in search + match=$(pkg search -q "^${prefix}-${driver_version}" 2>/dev/null | \ grep -v -- "-devel" | \ head -n 1) @@ -310,20 +447,31 @@ select_nvidia_pkg() { return fi - # Fallback: try the latest non-legacy driver + # Fallback: if requested version not found, try to find ANY matching driver + # For legacy drivers, this is critical - don't exclude them + case "$driver_version" in + 304|340|390|470) + # Legacy driver not found - this is an error + log ERROR "Legacy driver ${driver_version} not found in packages" + echo "${prefix}-${driver_version}" + return + ;; + esac + + # For current drivers, try the latest non-legacy driver match=$(pkg search -q "^${prefix}-[0-9]" 2>/dev/null | \ grep -Ev -- "-(304|340|390|470|devel)" | \ sort -t- -k3 -V | \ tail -n 1) if [ -n "$match" ]; then - log WARN "Requested driver ${NVIDIA_DRIVER_VERSION} not found, using $match" + log WARN "Requested driver ${driver_version} not found, using $match" echo "$match" return fi # Last resort: return what was requested, let pkg fail with clear error - echo "${prefix}-${NVIDIA_DRIVER_VERSION}" + echo "${prefix}-${driver_version}" } ############################################################################### @@ -499,6 +647,8 @@ cmd_debug() { echo "kern.vm_guest: $(detect_vm_guest)" echo "Detected X server: $(detect_x_server)" echo "NVIDIA driver version setting: $NVIDIA_DRIVER_VERSION" + echo "NVIDIA auto-detected driver: $(auto_detect_nvidia_driver)" + echo "NVIDIA device IDs: $(get_nvidia_device_ids)" echo "NVIDIA package selected: $(select_nvidia_pkg)" echo @@ -557,8 +707,11 @@ Commands: help Show this help Environment: - NVIDIA_DRIVER_VERSION NVIDIA driver major (580) or full version - (580.105.08). Default: 580 + NVIDIA_DRIVER_VERSION NVIDIA driver major (580, 470, 390, 340, 304) + or full version (580.105.08). Auto-detected + from GPU hardware if not set. + Set NVIDIA_DRIVER_VERSION_SET=1 to force + your chosen version over auto-detection. EOF } From d8e57581a44b403a412e2ed95eb958a99d4065ff Mon Sep 17 00:00:00 2001 From: "Vester \"Vic\" Thacker" Date: Tue, 2 Dec 2025 08:53:56 +0900 Subject: [PATCH 5/6] Improve nvidia gpu class detection and coverage Key Changes: 3D Controller Class Detection - NVIDIA dGPUs in laptops and servers often register as "3D controller" rather than "VGA/display". Updated detect_nvidia_gpu(), get_nvidia_device_ids(), count_gpu_devices(), and detect_hybrid_gpu() to include this class. Explicit Device ID Table - Added ~150 specific device IDs in nvidia_device_to_driver() for known edge cases that don't fit neat prefix patterns: 304 legacy: NV4x/G7x (GeForce 6xxx/7xxx) with explicit 00xx-03xx device IDs 340 legacy: Tesla architecture (GeForce 8xxx/9xxx/2xx/3xx) 390 legacy: Fermi (GTX 4xx/5xx/6xx low-end) 470 legacy: Early Kepler (GK1xx) devices dropped from 535+ Better Architecture Coverage: Added Hopper (H100, 23xx) for data center GPUs Added Volta (1dxx) for Titan V / Quadro GV100 Fixed Blackwell range (2bxx/2cxx, not all 2xxx) Separated Pascal (1bxx/1cxx) from Volta (1dxx) Enhanced Debug Output - Shows per-device driver mapping so you can see exactly what driver each GPU will get. Deduplication - get_nvidia_device_ids() now uses sort -u to avoid duplicate entries in multi-GPU systems. --- bin/xconfig | 196 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 151 insertions(+), 45 deletions(-) diff --git a/bin/xconfig b/bin/xconfig index d854667..d43e308 100755 --- a/bin/xconfig +++ b/bin/xconfig @@ -252,13 +252,16 @@ detect_amd_gpu() { } detect_nvidia_gpu() { - pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display" | grep -qi NVIDIA + # Check VGA, display, AND 3D controller classes + # Many NVIDIA dGPUs (especially in laptops/servers) register as 3D controller + pciconf -lv 2>/dev/null | grep -A4 -Ei "vga|display|3d" | grep -qi NVIDIA } # Get NVIDIA GPU PCI device IDs (returns space-separated list) +# Includes VGA, display, and 3D controller class devices get_nvidia_device_ids() { pciconf -l 2>/dev/null | grep -i "chip=.*10de" | \ - sed -E 's/.*chip=0x([0-9a-fA-F]{4}).*/\1/' | tr 'A-F' 'a-f' + sed -E 's/.*chip=0x([0-9a-fA-F]{4}).*/\1/' | tr 'A-F' 'a-f' | sort -u } # Map NVIDIA device ID to required driver series @@ -270,70 +273,148 @@ nvidia_device_to_driver() { # Convert to lowercase for comparison devid=$(echo "$devid" | tr 'A-F' 'a-f') + # First, check explicit device ID table for known edge cases + # This catches GPUs that don't fit neat prefix patterns + case "$devid" in + # ===== 304.xx legacy (NV4x/G7x: GeForce 6xxx, 7xxx) ===== + # These are GeForce 6/7 series from ~2004-2006 + 0040|0041|0042|0043|0044|0045|0046|0047|0048|004e) echo "304"; return ;; + 0090|0091|0092|0093|0095|0098|0099|009d) echo "304"; return ;; + 00c0|00c1|00c2|00c3|00c8|00c9|00cc|00cd|00ce) echo "304"; return ;; + 00f1|00f2|00f3|00f4|00f5|00f6|00f8|00f9) echo "304"; return ;; + 0140|0141|0142|0143|0144|0145|0146|0147|0148|0149|014a|014c|014d|014e|014f) echo "304"; return ;; + 0160|0161|0162|0163|0164|0165|0166|0167|0168|0169|016a) echo "304"; return ;; + 0191|0193|0194|0197) echo "304"; return ;; + 01d0|01d1|01d2|01d3|01d6|01d7|01d8|01da|01db|01dc|01dd|01de|01df) echo "304"; return ;; + 0211|0212|0215|0218|021a|021b) echo "304"; return ;; + 0221|0222|0240|0241|0242|0244|0245|0247|0248) echo "304"; return ;; + 0290|0291|0292|0293|0294|0295|0297|0298|0299) echo "304"; return ;; + 029a|029b|029c|029d|029e|029f) echo "304"; return ;; + 02e0|02e1|02e2|02e3|02e4) echo "304"; return ;; + 0390|0391|0392|0393|0394|0395|0397|0398|0399|039c|039e) echo "304"; return ;; + 03d0|03d1|03d2|03d5|03d6) echo "304"; return ;; + + # ===== 340.xx legacy (Tesla: GeForce 8xxx, 9xxx, 1xx, 2xx, 3xx) ===== + # Tesla architecture ~2006-2010 + 0400|0401|0402|0403|0404|0405|0406|0407|0408|0409|0410) echo "340"; return ;; + 0420|0421|0422|0423|0424|0425|0426|0427|0428|0429|042a|042b|042c|042d|042e|042f) echo "340"; return ;; + 05e0|05e1|05e2|05e3|05e6|05e7|05ea|05eb|05ed|05f8|05f9|05fd|05fe|05ff) echo "340"; return ;; + 0600|0601|0602|0603|0604|0605|0606|0607|0608|0609|060a|060b|060c|060d|060f) echo "340"; return ;; + 0610|0611|0612|0613|0614|0615|0617|0618|0619|061a|061b|061c|061d|061e|061f) echo "340"; return ;; + 0620|0621|0622|0623|0625|0626|0627|0628|062a|062b|062c|062d|062e) echo "340"; return ;; + 0630|0631|0632|0635|0637|0638|063a) echo "340"; return ;; + 0640|0641|0643|0644|0645|0646|0647|0648|0649|064a|064b|064c) echo "340"; return ;; + 0651|0652|0653|0654|0655|0656|0658|0659|065a|065b|065c|065f) echo "340"; return ;; + 06c0|06c4|06ca|06cb|06cd|06d1|06d2|06d8|06d9|06da|06dc|06dd|06de|06df) echo "340"; return ;; + 06e0|06e1|06e2|06e3|06e4|06e5|06e6|06e7|06e8|06e9|06ea|06eb|06ec|06ef) echo "340"; return ;; + 06f1|06f8|06f9|06fa|06fb|06fd|06ff) echo "340"; return ;; + 0840|0844|0845|0846|0847|0848|0849|084a|084b|084c|084d|084f) echo "340"; return ;; + 0860|0861|0862|0863|0864|0865|0866|0867|0868|0869|086a|086c|086d|086e|086f) echo "340"; return ;; + 0870|0871|0872|0873|0874|0876) echo "340"; return ;; + 087d|087e|087f) echo "340"; return ;; + 0a20|0a22|0a23|0a26|0a27|0a28|0a29|0a2a|0a2b|0a2c|0a2d) echo "340"; return ;; + 0a30|0a32|0a34|0a35|0a38|0a3c) echo "340"; return ;; + 0a60|0a62|0a63|0a64|0a65|0a66|0a67|0a68|0a69|0a6a|0a6c|0a6e|0a6f) echo "340"; return ;; + 0a70|0a71|0a72|0a73|0a74|0a75|0a76|0a78|0a7a|0a7c) echo "340"; return ;; + 0ca0|0ca2|0ca3|0ca4|0ca5|0ca7|0ca8|0ca9|0cac|0caf) echo "340"; return ;; + 0cb0|0cb1) echo "340"; return ;; + + # ===== 390.xx legacy (Fermi: GeForce 4xx, 5xx, 6xx low-end) ===== + # Fermi architecture ~2010-2012 + 06c0|06c4|06ca|06cb|06cd|06d1|06d2|06d8|06d9|06da|06dc|06dd|06de|06df) echo "390"; return ;; + 0dc0|0dc4|0dc5|0dc6|0dcd|0dce) echo "390"; return ;; + 0dd1|0dd2|0dd3|0dd6|0dd8|0dda) echo "390"; return ;; + 0de0|0de1|0de2|0de3|0de4|0de5|0de7|0de8|0de9|0dea|0deb|0dec|0ded|0dee|0def) echo "390"; return ;; + 0df0|0df1|0df2|0df3|0df4|0df5|0df6|0df7|0df8|0df9|0dfa|0dfc) echo "390"; return ;; + 0e22|0e23|0e24|0e30|0e31|0e3a|0e3b) echo "390"; return ;; + 1040|1042|1048|1049|104a|104b|104c) echo "390"; return ;; + 1050|1051|1052|1054|1055|1056|1057|1058|1059|105a|105b) echo "390"; return ;; + 107c|107d) echo "390"; return ;; + 1080|1081|1082|1084|1086|1087|1088|1089|108b|108e) echo "390"; return ;; + 1091|1094|1096|109a|109b) echo "390"; return ;; + 10c0|10c3|10c5|10d8) echo "390"; return ;; + 1180|1183|1184|1185|1187|1188|1189|118a|118e|118f) echo "390"; return ;; + 1193|1194|1195|1198|1199|119a|119d|119e|119f) echo "390"; return ;; + 11a0|11a1|11a2|11a3|11a7) echo "390"; return ;; + 11b4|11b6|11b7|11b8|11ba|11bc|11bd|11be|11bf) echo "390"; return ;; + 11c0|11c2|11c3|11c4|11c5|11c6|11c8) echo "390"; return ;; + + # ===== 470.xx legacy (Kepler: early GK1xx) ===== + # Some Kepler devices dropped from 535+ driver + 0fc0|0fc1|0fc2|0fc5|0fc6|0fc8|0fc9|0fcd|0fce|0fd1|0fd2) echo "470"; return ;; + 0fd3|0fd4|0fd5|0fd8|0fd9|0fdf) echo "470"; return ;; + 0fe0|0fe1|0fe2|0fe3|0fe4|0fe9|0fea|0fec|0fed|0fee|0fef|0ff2) echo "470"; return ;; + 0ff3|0ff6|0ff8|0ff9|0ffa|0ffb|0ffc|0ffd|0ffe|0fff) echo "470"; return ;; + 1001|1004|1005|1007|1008|100a|100c|101e|101f) echo "470"; return ;; + 1021|1022|1023|1024|1026|1027|1028|102a|102d|103a|103c) echo "470"; return ;; + 1200|1201|1203|1205|1206|1207|1208|1210|1211|1212|1213) echo "470"; return ;; + esac + + # If not in explicit table, fall back to prefix-based matching # Extract first two hex digits (device class within NVIDIA) prefix=$(echo "$devid" | cut -c1-2) case "$prefix" in - # Blackwell (RTX 50xx) - 2xxx device IDs - 2[0-9a-f]) + # ===== Current driver (580) ===== + # Blackwell (RTX 50xx) - 2bxx, 2cxx + 2b|2c) echo "580" ;; # Ada Lovelace (RTX 40xx) - 26xx, 27xx, 28xx 26|27|28) echo "580" ;; - # Ampere (RTX 30xx, A-series) - 20xx, 22xx, 23xx, 24xx, 25xx - 20|22|23|24|25) + # Hopper (H100, etc) - 23xx (data center) + 23) echo "580" ;; - # Turing (RTX 20xx, GTX 16xx) - 1e/1f/21 prefix + # Ampere (RTX 30xx, A-series) - 20xx, 22xx, 24xx, 25xx + 20|22|24|25) + echo "580" + ;; + # Turing (RTX 20xx, GTX 16xx) - 1exx, 1fxx, 21xx 1e|1f|21) echo "580" ;; - # Pascal (GTX 10xx) - 15xx, 17xx, 1b/1c/1d prefix - 15|17|1b|1c|1d) + # Volta (Titan V, Quadro GV100) - 1dxx + 1d) + echo "580" + ;; + # Pascal (GTX 10xx) - 15xx, 17xx, 1bxx, 1cxx + 15|17|1b|1c) echo "580" ;; - # Maxwell (GTX 9xx, GTX 750) - 13xx, 14xx, 17xx (some) + # Maxwell (GTX 9xx, GTX 750 Ti) - 13xx, 14xx, 17xx (some) 13|14) echo "580" ;; - # Kepler (GTX 6xx, 7xx) - some supported in 580, some need 470 - # GK104, GK106, GK107, GK110, GK208 etc - # 0fxx, 10xx, 11xx, 12xx - check specific ranges + # Kepler (GTX 6xx, 7xx) - remaining devices not in 470 list + # GK104, GK106, GK107, GK110, GK208 - 0fxx, 10xx, 11xx, 12xx 0f|10|11|12) - # Most Kepler GPUs work with current driver - # But some very early Kepler need 470 - case "$devid" in - # GK107 (GT 740, etc) - some need 470 - 0fc[0-9a-f]|0fd[0-9a-f]|0fe[0-9a-f]) - echo "580" - ;; - # GK208 (GT 7xx low-end) - 1280|1281|1282|1284|1286|1287|1288|1289|128b) - echo "580" - ;; - *) - echo "580" - ;; - esac + # Most Kepler works with current driver + # Edge cases handled in explicit table above + echo "580" ;; + + # ===== Legacy drivers ===== # Fermi (GTX 4xx, 5xx) - need 390 legacy driver - # Device IDs: 06xx, 08xx, 0axx, 0cxx, 0dxx, 0exx - 06|08|0a|0c|0d|0e) + # Remaining 0cxx, 0dxx, 0exx not caught by explicit table + 0c|0d|0e) echo "390" ;; # Tesla (GeForce 8xxx, 9xxx, 2xx, 3xx) - need 340 legacy - # Device IDs: 01xx, 02xx, 03xx, 04xx, 05xx, 06xx (older) - 01|02|03|04|05) + # Remaining 04xx, 05xx, 06xx, 08xx, 0axx not caught above + 04|05|06|08|0a) echo "340" ;; - # NV4x/G7x (GeForce 6xxx, 7xxx) - need 304 legacy - 00) + # Very old GPUs - 304 legacy + 00|01|02|03) echo "304" ;; *) - # Default to current driver, let it fail gracefully + # Unknown prefix - default to current driver + # Let it fail gracefully with a descriptive error + log WARN "Unknown NVIDIA device ID: $devid (prefix: $prefix)" echo "580" ;; esac @@ -380,9 +461,12 @@ auto_detect_nvidia_driver() { echo "$min_driver" } + + count_gpu_devices() { - # Count actual VGA/display class devices, not grep matches - pciconf -lv 2>/dev/null | grep -Ei "^[a-z].*class.*=.*vga|^[a-z].*class.*=.*display" | wc -l | tr -d ' ' + # Count actual VGA/display/3D controller class devices + # Include 3D controller for NVIDIA dGPUs that register that way + pciconf -lv 2>/dev/null | grep -Ei "^[a-z].*class.*=.*(vga|display|3d)" | wc -l | tr -d ' ' } detect_hybrid_gpu() { @@ -390,14 +474,14 @@ detect_hybrid_gpu() { gpu_count=$(count_gpu_devices) [ "$gpu_count" -gt 1 ] || return 1 - # Check which vendors are present + # Check which vendors are present (include 3D controller class) has_intel=0 has_amd=0 has_nvidia=0 - pciconf -lv 2>/dev/null | grep -B1 -A3 -Ei "class.*=.*(vga|display)" | grep -qi Intel && has_intel=1 - pciconf -lv 2>/dev/null | grep -B1 -A3 -Ei "class.*=.*(vga|display)" | grep -Eiq "AMD|ATI" && has_amd=1 - pciconf -lv 2>/dev/null | grep -B1 -A3 -Ei "class.*=.*(vga|display)" | grep -qi NVIDIA && has_nvidia=1 + pciconf -lv 2>/dev/null | grep -B1 -A3 -Ei "class.*=.*(vga|display|3d)" | grep -qi Intel && has_intel=1 + pciconf -lv 2>/dev/null | grep -B1 -A3 -Ei "class.*=.*(vga|display|3d)" | grep -Eiq "AMD|ATI" && has_amd=1 + pciconf -lv 2>/dev/null | grep -B1 -A3 -Ei "class.*=.*(vga|display|3d)" | grep -qi NVIDIA && has_nvidia=1 # Hybrid = Intel+NVIDIA, Intel+AMD, or AMD integrated+discrete vendor_count=$((has_intel + has_amd + has_nvidia)) @@ -652,9 +736,20 @@ cmd_debug() { echo "NVIDIA package selected: $(select_nvidia_pkg)" echo + # Show per-device driver mapping + device_ids=$(get_nvidia_device_ids) + if [ -n "$device_ids" ]; then + echo "NVIDIA per-device driver mapping:" + for devid in $device_ids; do + driver=$(nvidia_device_to_driver "$devid") + echo " Device 0x$devid -> driver series $driver" + done + echo + fi + echo "GPU devices found: $(count_gpu_devices)" - echo "GPU detection output:" - pciconf -lv | grep -B1 -A3 -Ei "class.*=.*(vga|display)" || echo "No GPU found" + echo "GPU detection output (VGA/display/3D controller classes):" + pciconf -lv | grep -B1 -A3 -Ei "class.*=.*(vga|display|3d)" || echo "No GPU found" echo echo "GPU vendor detection:" @@ -693,7 +788,7 @@ Commands: amd Auto-select AMD (amdgpu/radeonkms) amdgpu Force AMDGPU radeonkms Force radeonkms - nvidia NVIDIA (auto) + nvidia NVIDIA (auto-detects driver version) virtualbox VirtualBox guest vmware VMware guest qemu QEMU/KVM guest @@ -703,9 +798,20 @@ Commands: scfb Framebuffer safe Minimal dual Multi-GPU output (for displays on separate GPUs) - debug Diagnostic dump + debug Diagnostic dump (shows per-device driver mapping) help Show this help +NVIDIA Driver Support: + Auto-detection maps GPU device IDs to appropriate driver series: + 580 (current) - Blackwell, Ada, Ampere, Turing, Pascal, Maxwell, most Kepler + 470 (legacy) - Some early Kepler (GK1xx) + 390 (legacy) - Fermi (GTX 4xx/5xx) + 340 (legacy) - Tesla (GeForce 8xxx/9xxx/2xx/3xx) + 304 (legacy) - NV4x/G7x (GeForce 6xxx/7xxx) + + For unsupported legacy GPUs, use scfb or vesa as fallback. + Detection includes 3D controller class for laptop/server dGPUs. + Environment: NVIDIA_DRIVER_VERSION NVIDIA driver major (580, 470, 390, 340, 304) or full version (580.105.08). Auto-detected From 95655a721cb3435cf45fe70f853291dbb24f2467 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 2 Dec 2025 00:18:36 +0000 Subject: [PATCH 6/6] Add interactive NVIDIA driver selection menu Add 'nvidia-select' command for manual driver selection via bsddialog/dialog. The menu displays detected GPU device IDs, auto-detected driver recommendations, and allows users to override automatic detection when needed. Features: - Detects bsddialog (preferred) or dialog as fallback - Shows all detected NVIDIA GPUs with PCI device IDs - Lists all supported driver versions (580, 470, 390, 340, 304) - Includes "auto" option to use auto-detected driver - Allows cancel without making changes --- bin/xconfig | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/bin/xconfig b/bin/xconfig index d43e308..698a633 100755 --- a/bin/xconfig +++ b/bin/xconfig @@ -174,6 +174,21 @@ pkg_install_wrapper() { return 1 } +############################################################################### +# Dialog utility detection +############################################################################### + +# Detect available dialog utility (bsddialog preferred, dialog as fallback) +detect_dialog() { + if command -v bsddialog >/dev/null 2>&1; then + echo "bsddialog" + elif command -v dialog >/dev/null 2>&1; then + echo "dialog" + else + echo "" + fi +} + ############################################################################### # X server detection ############################################################################### @@ -558,6 +573,111 @@ select_nvidia_pkg() { echo "${prefix}-${driver_version}" } +############################################################################### +# NVIDIA driver menu selection +############################################################################### + +# Show an interactive menu for NVIDIA driver selection +# Returns the selected driver version via stdout +nvidia_driver_menu() { + dialog_cmd=$(detect_dialog) + + if [ -z "$dialog_cmd" ]; then + log ERROR "No dialog utility available (install bsddialog or dialog)" + return 1 + fi + + # Gather GPU information for display + device_ids=$(get_nvidia_device_ids) + auto_driver=$(auto_detect_nvidia_driver) + + # Build GPU info string for the menu + gpu_info="" + if [ -n "$device_ids" ]; then + gpu_count=0 + for devid in $device_ids; do + gpu_count=$((gpu_count + 1)) + driver=$(nvidia_device_to_driver "$devid") + gpu_info="${gpu_info}GPU ${gpu_count}: Device ID 0x${devid} (recommended: ${driver})\n" + done + else + gpu_info="No NVIDIA GPU detected\n" + fi + + # Create temporary file for dialog output + tmpfile=$(mktemp) + trap 'rm -f "$tmpfile"' EXIT + + # Menu title and message + title="NVIDIA Driver Selection" + msg="Detected Hardware:\n${gpu_info}\nAuto-detected driver: ${auto_driver}\n\nSelect a driver version:" + + # Build menu options + # Format: tag description + if [ "$dialog_cmd" = "bsddialog" ]; then + $dialog_cmd --title "$title" \ + --menu "$msg" 20 70 7 \ + "auto" "Auto-detect (${auto_driver})" \ + "580" "Current - Blackwell, Ada, Ampere, Turing, Pascal, Maxwell" \ + "470" "Legacy - Early Kepler (GK1xx)" \ + "390" "Legacy - Fermi (GTX 4xx/5xx)" \ + "340" "Legacy - Tesla (GeForce 8xxx/9xxx/2xx/3xx)" \ + "304" "Legacy - NV4x/G7x (GeForce 6xxx/7xxx)" \ + "cancel" "Cancel installation" \ + 2>"$tmpfile" + else + # dialog uses slightly different syntax + $dialog_cmd --title "$title" \ + --menu "$msg" 20 70 7 \ + "auto" "Auto-detect (${auto_driver})" \ + "580" "Current - Blackwell, Ada, Ampere, Turing, Pascal, Maxwell" \ + "470" "Legacy - Early Kepler (GK1xx)" \ + "390" "Legacy - Fermi (GTX 4xx/5xx)" \ + "340" "Legacy - Tesla (GeForce 8xxx/9xxx/2xx/3xx)" \ + "304" "Legacy - NV4x/G7x (GeForce 6xxx/7xxx)" \ + "cancel" "Cancel installation" \ + 2>"$tmpfile" + fi + + result=$? + selection=$(cat "$tmpfile") + rm -f "$tmpfile" + trap - EXIT + + # Handle dialog exit codes + # 0 = OK, 1 = Cancel, 255 = ESC + if [ $result -ne 0 ] || [ -z "$selection" ] || [ "$selection" = "cancel" ]; then + echo "" + return 1 + fi + + # Handle auto selection + if [ "$selection" = "auto" ]; then + echo "$auto_driver" + else + echo "$selection" + fi +} + +# Setup NVIDIA with interactive driver selection +setup_nvidia_interactive() { + selection=$(nvidia_driver_menu) + + if [ -z "$selection" ]; then + log INFO "NVIDIA driver selection cancelled" + return 1 + fi + + log INFO "User selected NVIDIA driver: $selection" + + # Set the driver version and mark it as explicitly set + NVIDIA_DRIVER_VERSION="$selection" + NVIDIA_DRIVER_VERSION_SET=1 + export NVIDIA_DRIVER_VERSION NVIDIA_DRIVER_VERSION_SET + + setup_nvidia +} + ############################################################################### # Setup functions ############################################################################### @@ -789,6 +909,7 @@ Commands: amdgpu Force AMDGPU radeonkms Force radeonkms nvidia NVIDIA (auto-detects driver version) + nvidia-select Interactive NVIDIA driver selection menu virtualbox VirtualBox guest vmware VMware guest qemu QEMU/KVM guest @@ -809,6 +930,10 @@ NVIDIA Driver Support: 340 (legacy) - Tesla (GeForce 8xxx/9xxx/2xx/3xx) 304 (legacy) - NV4x/G7x (GeForce 6xxx/7xxx) + Use 'nvidia-select' for an interactive menu to manually choose a driver. + The menu displays detected GPU info and allows overriding auto-detection. + Requires bsddialog or dialog to be installed. + For unsupported legacy GPUs, use scfb or vesa as fallback. Detection includes 3D controller class for laptop/server dGPUs. @@ -837,6 +962,7 @@ main() { amdgpu) setup_amdgpu ;; radeonkms) setup_radeonkms ;; nvidia) setup_nvidia ;; + nvidia-select) setup_nvidia_interactive ;; virtualbox) setup_virtualbox ;; vmware) setup_vmware ;; qemu) setup_qemu ;;