From 14cf513ecff60b1779f92a5f2f8b4305f5764a63 Mon Sep 17 00:00:00 2001 From: Joseph <14274863+jpaveg@users.noreply.github.com> Date: Tue, 27 Aug 2024 16:47:31 -0400 Subject: [PATCH] Create pcie-passthrough.sh create a pcie-passthrough helper script which will modify the relevant files according to the Proxmox documentation here: https://pve.proxmox.com/wiki/PCI(e)_Passthrough --- misc/pcie-passthrough.sh | 336 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 misc/pcie-passthrough.sh diff --git a/misc/pcie-passthrough.sh b/misc/pcie-passthrough.sh new file mode 100644 index 00000000..d6980e22 --- /dev/null +++ b/misc/pcie-passthrough.sh @@ -0,0 +1,336 @@ +#!/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"