#!/bin/bash # Author: jpaveg # Adapted from: tteck (tteckster) # License: MIT # https://github.com/tteck/Proxmox/raw/main/LICENSE header_info() { clear cat <<"EOF" ____ / __ \_________ _ ______ ___ ____ _ __ / /_/ / ___/ __ \| |/_/ __ `__ \/ __ \| |/_/ / ____/ / / /_/ /> < /_/ /_/ \____/_/|_/_/ /_/ /_/\____/_/|_| ____ __ __ __ / __ \____ ___________/ /_/ /_ _________ __ ______ _/ /_ / /_/ / __ `/ ___/ ___/ __/ __ \/ ___/ __ \/ / / / __ `/ __ \ / ____/ /_/ (__ |__ ) /_/ / / / / / /_/ / /_/ / /_/ / / / / /_/ \__,_/____/____/\__/_/ /_/_/ \____/\__,_/\__, /_/ /_/ /____/ _____ _ __ / ___/__________(_)___ / /_ \__ \/ ___/ ___/ / __ \/ __/ ___/ / /__/ / / / /_/ / /_ /____/\___/_/ /_/ .___/\__/ /_/ EOF } RD=$(echo "\033[01;31m") YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" set -euo pipefail shopt -s inherit_errexit nullglob GRUB_FILE="/etc/default/grub" GRUB_FILE_BACKUP="/etc/default/grub.bak" SYSTEMD_FILE="/etc/kernel/cmdline" SYSTEMD_FILE_BACKUP="/etc/kernel/cmdline.bak" MODULES_FILE="/etc/modules" MODULES_FILE_BACKUP="/etc/modules.bak" CPU_TYPE="unknown" msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } check_cpu_type() { msg_info "Checking CPU Type" # Check the Vendor ID using lscpu vendor_id=$(lscpu | grep "Vendor ID") vendor_id=$(echo $vendor_id | awk '{print $3}') if [ "$vendor_id" == "GenuineIntel" ]; then msg_info "Intel CPU detected" CPU_TYPE="intel" elif [ "$vendor_id" == "AuthenticAMD" ]; then msg_info "AMD CPU detected" CPU_TYPE="amd" else msg_error "Unknown CPU vendor, exiting" exit fi } modify_grub_cmdline() { # Make a backup of the GRUB file sudo cp $GRUB_FILE $GRUB_FILE_BACKUP # Extract the content of GRUB_CMDLINE_LINUX_DEFAULT using sed cmdline_flags=$(sed -n 's/^GRUB_CMDLINE_LINUX_DEFAULT="\(.*\)"/\1/p' "$GRUB_FILE") # Initialize an associative array to hold key-value pairs declare -A kv_pairs # Initialize a string to hold flags flags="" # Iterate over each item in the cmdline_flags for item in $cmdline_flags; do if [[ "$item" != *"="* ]]; then # If it's a flag, add it to the flags string flags="$flags $item" else # If it's a key-value pair, split it and add to associative array IFS='=' read -r key value <<<"$item" kv_pairs["$key"]="$value" fi done # Use whiptail to ask user if they want to enable IOMMU passthrough mode if (whiptail --title "GRUB - IOMMU Passthrough" --yesno "Do you want to enable IOMMU passthrough mode?" 10 60); then kv_pairs["iommu"]="pt" else unset kv_pairs["iommu"] # Remove the key if the user selects "No" fi # Check if CPU_TYPE is Intel before enabling Intel IOMMU if [ "$CPU_TYPE" == "intel" ]; then if (whiptail --title "GRUB - Intel IOMMU" --yesno "Do you want to enable Intel IOMMU?" 10 60); then kv_pairs["intel_iommu"]="on" else unset kv_pairs["intel_iommu"] # Remove the key if the user selects "No" fi fi # Reconstruct the new GRUB_CMDLINE_LINUX_DEFAULT line new_cmdline_flags="$flags" for key in "${!kv_pairs[@]}"; do new_cmdline_flags="$new_cmdline_flags $key=${kv_pairs[$key]}" done # Remove leading space new_cmdline_flags=$(echo "$new_cmdline_flags" | sed 's/^ //') # Set the updated flags and key-value pairs in GRUB file msg_info "Setting GRUB_CMDLINE_LINUX_DEFAULT flags" sudo sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s/\(GRUB_CMDLINE_LINUX_DEFAULT=\).*/\1\"$new_cmdline_flags\"/" $GRUB_FILE # Output the updated GRUB_CMDLINE_LINUX_DEFAULT line for verification msg_ok "Modified /etc/default/grub: GRUB_CMDLINE_LINUX_DEFAULT=\"$new_cmdline_flags\"" # Run update-grub to finish changes msg_info "Running update-grub to finalize changes to cmdline" # sudo update-grub } modify_systemd_boot_cmdline() { # Make a backup of the cmdline file sudo cp "$SYSTEMD_FILE" "$SYSTEMD_FILE_BACKUP" # Extract the content of the cmdline file cmdline_flags=$(cat "$SYSTEMD_FILE") # Initialize an associative array to hold key-value pairs declare -A kv_pairs # Initialize a string to hold flags flags="" # Iterate over each item in the cmdline_flags for item in $cmdline_flags; do if [[ "$item" != *"="* ]]; then # If it's a flag, add it to the flags string flags="$flags $item" else # If it's a key-value pair, split it and add to associative array IFS='=' read -r key value <<<"$item" kv_pairs["$key"]="$value" fi done # Use whiptail to ask user if they want to enable IOMMU passthrough mode if (whiptail --title "SYSTEMD - IOMMU Passthrough" --yesno "Do you want to enable IOMMU passthrough mode?" 10 60); then kv_pairs["iommu"]="pt" else unset kv_pairs["iommu"] # Remove the key if the user selects "No" fi # Check if CPU_TYPE is Intel before enabling Intel IOMMU if [ "$CPU_TYPE" == "intel" ]; then if (whiptail --title "SYSTEMD - Intel IOMMU" --yesno "Do you want to enable Intel IOMMU?" 10 60); then kv_pairs["intel_iommu"]="on" else unset kv_pairs["intel_iommu"] # Remove the key if the user selects "No" fi fi # Reconstruct the new kernel cmdline new_cmdline_flags="$flags" for key in "${!kv_pairs[@]}"; do new_cmdline_flags="$new_cmdline_flags $key=${kv_pairs[$key]}" done # Remove leading space new_cmdline_flags=$(echo "$new_cmdline_flags" | sed 's/^ //') # Set the updated flags and key-value pairs in cmdline file msg_info "Setting kernel cmdline flags" echo -n "$new_cmdline_flags" | sudo tee "$SYSTEMD_FILE" >/dev/null # Output the updated cmdline for verification msg_ok "Modified /etc/kernel/cmdline: \"$new_cmdline_flags\"" # Run proxmox-boot-tool refresh to apply changes msg_info "Running proxmox-boot-tool refresh to apply changes" # sudo proxmox-boot-tool refresh } modify_kernel_modules() { # List of required modules required_modules=("vfio" "vfio_iommu_type1" "vfio_pci") # Check the kernel version KERNEL_VERSION=$(uname -r | cut -d'-' -f1) KERNEL_MAJOR=$(echo "$KERNEL_VERSION" | cut -d'.' -f1) KERNEL_MINOR=$(echo "$KERNEL_VERSION" | cut -d'.' -f2) # Add vfio_virqfd only if the kernel version is older than 6.2 if [ "$KERNEL_MAJOR" -lt 6 ] || { [ "$KERNEL_MAJOR" -eq 6 ] && [ "$KERNEL_MINOR" -lt 2 ]; }; then echo $KERNEL_MAJOR $KERNEL_MINOR $KERNEL_VERSION required_modules+=("vfio_virqfd") fi # Make a backup of the current modules file sudo cp "$MODULES_FILE" "$MODULES_FILE_BACKUP" # Load current modules into an associative array for quick lookup declare -A current_modules if [ -f "$MODULES_FILE" ]; then while IFS= read -r line; do # Skip empty lines and comments [[ "$line" =~ ^#.*$ ]] || [[ -z "$line" ]] && continue current_modules["$line"]=1 done <"$MODULES_FILE" fi # Initialize a flag to track if we need to update the modules file update_needed=false # Check each required module and add it if not present for module in "${required_modules[@]}"; do # Use a default value for the lookup to avoid unbound variable errors if [[ -z "${current_modules[$module]+x}" ]]; then echo "$module" | sudo tee -a "$MODULES_FILE" >/dev/null update_needed=true msg_info "Added $module to $MODULES_FILE" else msg_ok "$module is already present in $MODULES_FILE" fi done # Refresh initramfs if we added any new modules if [ "$update_needed" = true ]; then msg_info "Updating initramfs to apply module changes" sudo update-initramfs -u -k all msg_ok "Initramfs updated successfully" else msg_ok "No changes needed for $MODULES_FILE" fi } # Main start_routines() { header_info check_cpu_type # Display a whiptail menu for the user to choose an option OPTION=$(whiptail --title "Select Command Line Modification" --menu "Choose an option:" 15 60 4 \ "1" "Modify GRUB cmdline" \ "2" "Modify systemd-boot cmdline" \ "3" "Modify both cmdlines" \ "4" "Exit" 3>&1 1>&2 2>&3) # Act based on the user's choice case $OPTION in "1") modify_grub_cmdline ;; "2") modify_systemd_boot_cmdline ;; "3") modify_grub_cmdline || msg_error "There was an error modifying the GRUB cmdline, continuing to next step." modify_systemd_boot_cmdline || msg_error "There was an error modifying the SYSTEMD-BOOT cmdline, continuing to next step." ;; "4") echo "Exiting without making any changes." ;; *) echo "Invalid option. Exiting." ;; esac echo "Debug: Calling modify_kernel_modules" modify_kernel_modules } echo -e "\nThis script will modify your cmdline and kernel flags to enable passthrough & vfio.\n" while true; do read -p "Start the Proxmox VE Passthrough Script (y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) clear exit ;; *) echo "Please answer yes or no." ;; esac done if ! pveversion | grep -Eq "pve-manager/8.[0-2]"; then msg_error "This version of Proxmox Virtual Environment is not supported" echo -e "Requires Proxmox Virtual Environment Version 8.0 or later." echo -e "Exiting..." sleep 2 exit fi start_routines # Notification to the user echo -e "\n\nScript execution complete." # Notify the user about the need to reboot and verify IOMMU echo -e "To apply the changes, please reboot your system." # Provide instructions to verify IOMMU is enabled echo -e "After rebooting, run the following command to ensure IOMMU is enabled:" echo -e " dmesg | grep -e DMAR -e IOMMU -e AMD-Vi" # Inform about the backup files echo -e "\nBackup files have been created for your safety:" echo -e " $GRUB_FILE_BACKUP" echo -e " $SYSTEMD_FILE_BACKUP" echo -e " $MODULES_FILE_BACKUP" # Provide link for further information echo -e "\nFor more information or if you encounter issues, please visit:" echo -e " https://pve.proxmox.com/wiki/PCI(e)_Passthrough"