From d3974018d8d3fb751eaf769c139f81e20fff83c5 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Mon, 6 Apr 2026 17:16:26 +0200 Subject: [PATCH] add_controller_nvme_vm.sh --- scripts/global/vm_storage_helpers.sh | 129 ++++++++++++++++++++++ scripts/menus/create_vm_menu.sh | 2 + scripts/storage/add_controller_nvme_vm.sh | 110 ++++++++++++++++-- scripts/vm/disk_selector.sh | 27 ++++- scripts/vm/synology.sh | 48 +++++++- scripts/vm/vm_creator.sh | 26 ++++- scripts/vm/zimaos.sh | 49 +++++++- 7 files changed, 370 insertions(+), 21 deletions(-) diff --git a/scripts/global/vm_storage_helpers.sh b/scripts/global/vm_storage_helpers.sh index cf9e95f2..641fd2f3 100644 --- a/scripts/global/vm_storage_helpers.sh +++ b/scripts/global/vm_storage_helpers.sh @@ -137,6 +137,135 @@ function _vm_is_q35() { [[ "$machine_line" == *q35* ]] } +function _vm_storage_enable_iommu_cmdline() { + local cpu_vendor iommu_param + cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}') + + if [[ "$cpu_vendor" == "GenuineIntel" ]]; then + iommu_param="intel_iommu=on" + elif [[ "$cpu_vendor" == "AuthenticAMD" ]]; then + iommu_param="amd_iommu=on" + else + return 1 + fi + + local cmdline_file="/etc/kernel/cmdline" + local grub_file="/etc/default/grub" + + if [[ -f "$cmdline_file" ]] && grep -qE 'root=ZFS=|root=ZFS/' "$cmdline_file" 2>/dev/null; then + if ! grep -q "$iommu_param" "$cmdline_file"; then + cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)" + sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file" + proxmox-boot-tool refresh >/dev/null 2>&1 || true + fi + elif [[ -f "$grub_file" ]]; then + if ! grep -q "$iommu_param" "$grub_file"; then + cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)" + sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file" + update-grub >/dev/null 2>&1 || true + fi + else + return 1 + fi + + return 0 +} + +function _vm_storage_ensure_iommu_or_offer() { + if declare -F _pci_is_iommu_active >/dev/null 2>&1 && _pci_is_iommu_active; then + return 0 + fi + + if grep -qE 'intel_iommu=on|amd_iommu=on' /proc/cmdline 2>/dev/null && \ + [[ -d /sys/kernel/iommu_groups ]] && \ + [[ -n "$(ls /sys/kernel/iommu_groups/ 2>/dev/null)" ]]; then + return 0 + fi + + local prompt + prompt="$(translate "IOMMU is not active on this system.")\n\n" + prompt+="$(translate "Controller/NVMe passthrough to VMs requires IOMMU enabled in BIOS/UEFI and kernel.")\n\n" + prompt+="$(translate "Do you want to enable IOMMU now?")\n\n" + prompt+="$(translate "A host reboot is required after this change.")" + + whiptail --title "IOMMU Required" --yesno "$prompt" 14 78 + [[ $? -ne 0 ]] && return 1 + + if ! _vm_storage_enable_iommu_cmdline; then + whiptail --title "IOMMU" --msgbox \ +"$(translate "Failed to configure IOMMU automatically.")\n\n$(translate "Please configure it manually and reboot.")" \ + 10 72 + return 1 + fi + + local reboot_policy="${VM_STORAGE_IOMMU_REBOOT_POLICY:-ask_now}" + if [[ "$reboot_policy" == "defer" ]]; then + VM_STORAGE_IOMMU_PENDING_REBOOT=1 + export VM_STORAGE_IOMMU_PENDING_REBOOT + whiptail --title "Reboot Required" --msgbox \ +"$(translate "IOMMU configured successfully.")\n\n$(translate "Continue the VM wizard and reboot the host at the end.")\n\n$(translate "Controller/NVMe passthrough will be available after reboot.")" \ + 12 78 + return 1 + fi + + if whiptail --title "Reboot Required" --yesno \ +"$(translate "IOMMU configured successfully.")\n\n$(translate "Do you want to reboot now?")" 10 68; then + reboot + else + whiptail --title "Reboot Required" --msgbox \ +"$(translate "Please reboot manually and run the passthrough step again.")" 9 68 + fi + + return 1 +} + +function _vm_storage_confirm_controller_passthrough_risk() { + local vmid="${1:-}" + local vm_name="${2:-}" + local title="${3:-Controller + NVMe}" + local vm_label="" + if [[ -n "$vmid" ]]; then + vm_label="$vmid" + [[ -n "$vm_name" ]] && vm_label="${vm_label} (${vm_name})" + fi + + local msg + msg="$(translate "Important compatibility notice")\n\n" + msg+="$(translate "Not all motherboards support physical Controller/NVMe passthrough to VMs reliably, especially systems with old platforms or limited BIOS/UEFI firmware.")\n\n" + msg+="$(translate "On some systems, the VM may fail to start or the host may freeze when the VM boots.")\n\n" + + local reinforce_limited_firmware="no" + local bios_date bios_year current_year cpu_model + bios_date=$(cat /sys/class/dmi/id/bios_date 2>/dev/null) + bios_year=$(echo "$bios_date" | grep -oE '[0-9]{4}' | tail -n1) + current_year=$(date +%Y 2>/dev/null) + if [[ -n "$bios_year" && -n "$current_year" ]]; then + if (( current_year - bios_year >= 7 )); then + reinforce_limited_firmware="yes" + fi + fi + cpu_model=$(grep -m1 'model name' /proc/cpuinfo 2>/dev/null | cut -d: -f2- | xargs) + if echo "$cpu_model" | grep -qiE 'J4[0-9]{3}|J3[0-9]{3}|N4[0-9]{3}|N3[0-9]{3}|Apollo Lake'; then + reinforce_limited_firmware="yes" + fi + + if [[ "$reinforce_limited_firmware" == "yes" ]]; then + msg+="$(translate "Detected risk factor: this host may use an older or limited firmware platform, which increases passthrough instability risk.")\n\n" + fi + + if [[ -n "$vm_label" ]]; then + msg+="$(translate "Target VM"): ${vm_label}\n\n" + fi + msg+="$(translate "If this happens after assignment"):\n" + msg+=" - $(translate "Power cycle the host if it is frozen.")\n" + msg+=" - $(translate "Remove the hostpci controller/NVMe entries from the VM config file.")\n" + msg+=" /etc/pve/qemu-server/${vmid:-}.conf\n" + msg+=" - $(translate "Start the VM again without that passthrough device.")\n\n" + msg+="$(translate "Do you want to continue with this assignment?")" + + whiptail --title "$title" --yesno "$msg" 21 96 +} + function _shorten_text() { local text="$1" local max_len="${2:-42}" diff --git a/scripts/menus/create_vm_menu.sh b/scripts/menus/create_vm_menu.sh index 60526f84..da45bcff 100644 --- a/scripts/menus/create_vm_menu.sh +++ b/scripts/menus/create_vm_menu.sh @@ -68,6 +68,7 @@ function header_info() { VM_WIZARD_CAPTURE_FILE="" VM_WIZARD_CAPTURE_ACTIVE=0 +VM_STORAGE_IOMMU_PENDING_REBOOT=0 function start_vm_wizard_capture() { [[ "${VM_WIZARD_CAPTURE_ACTIVE:-0}" -eq 1 ]] && return 0 @@ -132,6 +133,7 @@ function start_vm_configuration() { while true; do + VM_STORAGE_IOMMU_PENDING_REBOOT=0 OS_TYPE=$(dialog --backtitle "ProxMenux" \ --title "$(translate "Select System Type")" \ --menu "\n$(translate "Choose the type of virtual system to install:")" 20 70 10 \ diff --git a/scripts/storage/add_controller_nvme_vm.sh b/scripts/storage/add_controller_nvme_vm.sh index bb899f75..3b43d48d 100755 --- a/scripts/storage/add_controller_nvme_vm.sh +++ b/scripts/storage/add_controller_nvme_vm.sh @@ -56,6 +56,100 @@ set_title() { msg_title "$(translate "Add Controller or NVMe PCIe to VM")" } +enable_iommu_cmdline() { + local cpu_vendor iommu_param + cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}') + + if [[ "$cpu_vendor" == "GenuineIntel" ]]; then + iommu_param="intel_iommu=on" + msg_info "$(translate "Intel CPU detected")" + elif [[ "$cpu_vendor" == "AuthenticAMD" ]]; then + iommu_param="amd_iommu=on" + msg_info "$(translate "AMD CPU detected")" + else + msg_error "$(translate "Unknown CPU vendor. Cannot determine IOMMU parameter.")" + return 1 + fi + + local cmdline_file="/etc/kernel/cmdline" + local grub_file="/etc/default/grub" + + if [[ -f "$cmdline_file" ]] && grep -qE 'root=ZFS=|root=ZFS/' "$cmdline_file" 2>/dev/null; then + if ! grep -q "$iommu_param" "$cmdline_file"; then + cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)" + sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file" + proxmox-boot-tool refresh >/dev/null 2>&1 || true + msg_ok "$(translate "IOMMU parameters added to /etc/kernel/cmdline")" + else + msg_ok "$(translate "IOMMU already configured in /etc/kernel/cmdline")" + fi + elif [[ -f "$grub_file" ]]; then + if ! grep -q "$iommu_param" "$grub_file"; then + cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)" + sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file" + update-grub >/dev/null 2>&1 || true + msg_ok "$(translate "IOMMU parameters added to GRUB")" + else + msg_ok "$(translate "IOMMU already configured in GRUB")" + fi + else + msg_error "$(translate "Neither /etc/kernel/cmdline nor /etc/default/grub found.")" + return 1 + fi +} + +check_iommu_or_offer_enable() { + if declare -F _pci_is_iommu_active >/dev/null 2>&1 && _pci_is_iommu_active; then + return 0 + fi + + if grep -qE 'intel_iommu=on|amd_iommu=on' /proc/cmdline 2>/dev/null && \ + [[ -d /sys/kernel/iommu_groups ]] && \ + [[ -n "$(ls /sys/kernel/iommu_groups/ 2>/dev/null)" ]]; then + return 0 + fi + + local msg + msg="\n$(translate "IOMMU is not active on this system.")\n\n" + msg+="$(translate "Controller/NVMe passthrough to VMs requires IOMMU to be enabled in the kernel.")\n\n" + msg+="$(translate "Do you want to enable IOMMU now?")\n\n" + msg+="$(translate "Note: A system reboot will be required after enabling IOMMU.")\n" + msg+="$(translate "You must run this option again after rebooting.")" + + dialog --backtitle "ProxMenux" \ + --title "$(translate "IOMMU Required")" \ + --yesno "$msg" 15 74 + local response=$? + clear + + [[ $response -ne 0 ]] && return 1 + + set_title + msg_title "$(translate "Enabling IOMMU")" + echo + if ! enable_iommu_cmdline; then + echo + msg_error "$(translate "Failed to configure IOMMU automatically.")" + msg_success "$(translate "Press Enter to continue...")" + read -r + return 1 + fi + + echo + msg_success "$(translate "IOMMU configured. Reboot required before using Controller/NVMe passthrough.")" + echo + if whiptail --title "$(translate "Reboot Required")" \ + --yesno "$(translate "Do you want to reboot now?")" 10 64; then + msg_warn "$(translate "Rebooting the system...")" + reboot + else + msg_info2 "$(translate "Please reboot manually and run this option again.")" + msg_success "$(translate "Press Enter to continue...")" + read -r + fi + return 1 +} + select_target_vm() { local -a vm_menu=() local line vmid vmname vmstatus vm_machine status_label @@ -71,7 +165,6 @@ select_target_vm() { status_label="${vmstatus}, ${vm_machine}" vm_menu+=("$vmid" "${vmname} [${status_label}]") done < <(qm list 2>/dev/null) - if [[ ${#vm_menu[@]} -eq 0 ]]; then dialog --backtitle "ProxMenux" \ --title "$(translate "Add Controller or NVMe PCIe to VM")" \ @@ -107,14 +200,7 @@ validate_vm_requirements() { return 1 fi - if declare -F _pci_is_iommu_active >/dev/null 2>&1; then - if ! _pci_is_iommu_active; then - dialog --backtitle "ProxMenux" --colors \ - --title "$(translate "IOMMU Required")" \ - --msgbox "\n\Zb\Z1$(translate "IOMMU is not active on this host.")\Zn\n\n$(translate "PCIe passthrough requires IOMMU enabled in kernel and firmware.")\n\n$(translate "Enable IOMMU, reboot the host, and run again.")" 12 80 - return 1 - fi - fi + check_iommu_or_offer_enable || return 1 return 0 } @@ -236,6 +322,12 @@ select_controller_nvme() { return 1 fi + if declare -F _vm_storage_confirm_controller_passthrough_risk >/dev/null 2>&1; then + if ! _vm_storage_confirm_controller_passthrough_risk "$SELECTED_VMID" "$SELECTED_VM_NAME" "$(translate "Controller + NVMe")"; then + return 1 + fi + fi + return 0 } diff --git a/scripts/vm/disk_selector.sh b/scripts/vm/disk_selector.sh index 68daa72d..ec5cc89a 100644 --- a/scripts/vm/disk_selector.sh +++ b/scripts/vm/disk_selector.sh @@ -175,7 +175,7 @@ function select_virtual_disk() { fi local DISK_SIZE - cleanup + stop_spinner DISK_SIZE=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "System Disk Size (GB)")" 8 58 32 --title "VIRTUAL DISK" --cancel-button Cancel 3>&1 1>&2 2>&3) if [ $? -ne 0 ]; then return 0 @@ -229,7 +229,7 @@ function select_import_disk() { done < <(lsblk -dn -e 7,11 -o PATH) if [[ "${#FREE_DISKS[@]}" -eq 0 ]]; then - cleanup + stop_spinner whiptail --title "Error" --msgbox "$(translate "No importable disks available. System disks and protected disks are hidden.")" 9 70 return 1 fi @@ -265,6 +265,21 @@ function select_passthrough_disk() { } function select_controller_nvme() { + local VM_STORAGE_IOMMU_REBOOT_POLICY="defer" + + if declare -F _vm_storage_ensure_iommu_or_offer >/dev/null 2>&1; then + if ! _vm_storage_ensure_iommu_or_offer; then + return 1 + fi + elif declare -F _pci_is_iommu_active >/dev/null 2>&1; then + if ! _pci_is_iommu_active; then + whiptail --title "Controller + NVMe" --msgbox \ +"$(translate "IOMMU is not active on this host.")\n\n$(translate "Controller/NVMe passthrough requires IOMMU enabled in BIOS/UEFI and kernel.")\n\n$(translate "Enable IOMMU, reboot the host, and try again.")" \ + 14 90 + return 1 + fi + fi + msg_info "$(translate "Detecting PCI storage controllers and NVMe devices...")" _refresh_host_storage_cache @@ -384,6 +399,14 @@ function select_controller_nvme() { return 0 fi + if declare -F _vm_storage_confirm_controller_passthrough_risk >/dev/null 2>&1; then + local vm_name_for_notice="${HN:-}" + if ! _vm_storage_confirm_controller_passthrough_risk "${VMID:-}" "$vm_name_for_notice" "Controller + NVMe"; then + CONTROLLER_NVME_PCIS=() + return 1 + fi + fi + export CONTROLLER_NVME_PCIS return 0 } diff --git a/scripts/vm/synology.sh b/scripts/vm/synology.sh index 75f57983..e3bcced3 100644 --- a/scripts/vm/synology.sh +++ b/scripts/vm/synology.sh @@ -79,6 +79,7 @@ WIZARD_ADD_GPU="no" WIZARD_GPU_RESULT="not_requested" VM_WIZARD_CAPTURE_FILE="" VM_WIZARD_CAPTURE_ACTIVE=0 +VM_STORAGE_IOMMU_PENDING_REBOOT=0 @@ -599,6 +600,7 @@ function select_import_disk() { fi done < <(lsblk -dn -e 7,11 -o PATH) + stop_spinner if [[ ${#FREE_DISKS[@]} -eq 0 ]]; then whiptail --title "Error" --msgbox "$(translate "No importable disks available. System disks and protected disks are hidden.")" 9 70 return 1 @@ -618,7 +620,23 @@ function select_import_disk() { } function select_controller_nvme() { + local VM_STORAGE_IOMMU_REBOOT_POLICY="defer" + + if declare -F _vm_storage_ensure_iommu_or_offer >/dev/null 2>&1; then + if ! _vm_storage_ensure_iommu_or_offer; then + return 1 + fi + elif declare -F _pci_is_iommu_active >/dev/null 2>&1; then + if ! _pci_is_iommu_active; then + whiptail --title "Controller + NVMe" --msgbox \ +"$(translate "IOMMU is not active on this host.")\n\n$(translate "Controller/NVMe passthrough requires IOMMU enabled in BIOS/UEFI and kernel.")\n\n$(translate "Enable IOMMU, reboot the host, and try again.")" \ + 14 90 + return 1 + fi + fi + msg_info "$(translate "Detecting PCI storage controllers and NVMe devices...")" + _refresh_host_storage_cache local menu_items=() @@ -721,6 +739,15 @@ function select_controller_nvme() { for pci in $(echo "$selected" | tr -d '"'); do CONTROLLER_NVME_PCIS+=("$pci") done + + if [[ ${#CONTROLLER_NVME_PCIS[@]} -gt 0 ]] && declare -F _vm_storage_confirm_controller_passthrough_risk >/dev/null 2>&1; then + local vm_name_for_notice="${HN:-$NAME}" + if ! _vm_storage_confirm_controller_passthrough_risk "${VMID:-}" "$vm_name_for_notice" "Controller + NVMe"; then + CONTROLLER_NVME_PCIS=() + return 1 + fi + fi + export CONTROLLER_NVME_PCIS } @@ -1056,9 +1083,12 @@ function select_storage_volume() { function create_vm() { # Create the VM - qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1${BIOS_TYPE}${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ + if ! qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1${BIOS_TYPE}${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags proxmenux,nas -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci \ - -serial0 socket + -serial0 socket; then + msg_error "Failed to create base VM. Check VM ID and host configuration." + return 1 + fi msg_ok "Create a $NAME" @@ -1279,7 +1309,15 @@ if [[ ${#EFFECTIVE_IMPORT_DISKS[@]} -gt 0 ]]; then fi if [[ ${#CONTROLLER_NVME_PCIS[@]} -gt 0 ]]; then - if ! _vm_is_q35 "$VMID"; then + if declare -F _pci_is_iommu_active >/dev/null 2>&1 && ! _pci_is_iommu_active; then + if [[ "${VM_STORAGE_IOMMU_PENDING_REBOOT:-0}" == "1" ]]; then + msg_warn "$(translate "IOMMU was configured during this wizard and a reboot is pending.")" + msg_warn "$(translate "Controller + NVMe assignment is postponed until after host reboot.")" + else + msg_error "$(translate "IOMMU is not active. Skipping Controller + NVMe assignment.")" + ERROR_FLAG=true + fi + elif ! _vm_is_q35 "$VMID"; then msg_error "$(translate "Controller + NVMe passthrough requires machine type q35. Skipping controller assignment.")" ERROR_FLAG=true else @@ -1440,6 +1478,10 @@ if [[ "$WIZARD_GPU_RESULT" == "applied" ]]; then echo -e "${TAB}• $(translate "First complete DSM setup and verify Web UI/SSH access.")" echo -e "${TAB}• $(translate "Then change the VM display to none (vga: none) when the system is stable.")" fi +if [[ "${VM_STORAGE_IOMMU_PENDING_REBOOT:-0}" == "1" ]]; then + msg_warn "$(translate "IOMMU was enabled during this wizard. Reboot the host to apply it.")" + echo -e "${TAB}$(translate "After reboot, run: Storage -> Add Controller or NVMe PCIe to VM, and select VM") ${VMID}." +fi echo -e #msg_success "$(translate "Press Enter to return to the main menu...")" diff --git a/scripts/vm/vm_creator.sh b/scripts/vm/vm_creator.sh index 464ade43..22f10816 100644 --- a/scripts/vm/vm_creator.sh +++ b/scripts/vm/vm_creator.sh @@ -60,6 +60,7 @@ fi load_language initialize_cache +VM_STORAGE_IOMMU_PENDING_REBOOT="${VM_STORAGE_IOMMU_PENDING_REBOOT:-0}" # ========================================================== # Mont ISOs @@ -284,7 +285,7 @@ function create_vm() { # $( [[ -n "$SERIAL_PORT" ]] && echo "-serial0 $SERIAL_PORT" ) >/dev/null 2>&1 -qm create "$VMID" \ +if ! qm create "$VMID" \ -agent 1${MACHINE:+ $MACHINE} \ -localtime 1${BIOS_TYPE:+ $BIOS_TYPE}${CPU_TYPE:+ $CPU_TYPE} \ -cores "$CORE_COUNT" \ @@ -295,7 +296,10 @@ qm create "$VMID" \ -ostype "$GUEST_OS_TYPE" \ -scsihw virtio-scsi-pci \ $( [[ -n "$SERIAL_PORT" ]] && echo "-serial0 $SERIAL_PORT" ) \ - >/dev/null 2>&1 + >/dev/null 2>&1; then + msg_error "$(translate "Failed to create base VM. Check VM ID and host configuration.")" + return 1 +fi if [[ "$OS_TYPE" == "2" ]]; then qm set "$VMID" -tablet 1 >/dev/null 2>&1 @@ -465,7 +469,14 @@ fi fi if [[ ${#CONTROLLER_NVME_PCIS[@]} -gt 0 ]]; then - if ! _vm_is_q35 "$VMID"; then + if declare -F _pci_is_iommu_active >/dev/null 2>&1 && ! _pci_is_iommu_active; then + if [[ "${VM_STORAGE_IOMMU_PENDING_REBOOT:-0}" == "1" ]]; then + msg_warn "$(translate "IOMMU was configured during this wizard and a reboot is pending.")" + msg_warn "$(translate "Controller + NVMe assignment is postponed until after host reboot.")" + else + msg_error "$(translate "IOMMU is not active. Skipping Controller + NVMe assignment.")" + fi + elif ! _vm_is_q35 "$VMID"; then msg_error "$(translate "Controller + NVMe passthrough requires machine type q35. Skipping controller assignment.")" else local hostpci_idx=0 @@ -737,6 +748,10 @@ if [[ "${WIZARD_ADD_GPU:-no}" == "yes" ]]; then fi echo -e fi + if [[ "${VM_STORAGE_IOMMU_PENDING_REBOOT:-0}" == "1" ]]; then + msg_warn "$(translate "IOMMU was enabled during this wizard. Reboot the host to apply it.")" + echo -e "${TAB}$(translate "After reboot, run: Storage -> Add Controller or NVMe PCIe to VM, and select VM") ${VMID}." + fi msg_success "$(translate "Press Enter to return to the main menu...")" read -r bash "$LOCAL_SCRIPTS/menus/create_vm_menu.sh" @@ -761,6 +776,11 @@ elif [[ "$OS_TYPE" == "3" ]]; then echo -e fi +if [[ "${VM_STORAGE_IOMMU_PENDING_REBOOT:-0}" == "1" ]]; then + msg_warn "$(translate "IOMMU was enabled during this wizard. Reboot the host to apply it.")" + echo -e "${TAB}$(translate "After reboot, run: Storage -> Add Controller or NVMe PCIe to VM, and select VM") ${VMID}." +fi + msg_success "$(translate "Press Enter to return to the main menu...")" read -r bash "$LOCAL_SCRIPTS/menus/create_vm_menu.sh" diff --git a/scripts/vm/zimaos.sh b/scripts/vm/zimaos.sh index 30ae5bb4..a1cd8543 100644 --- a/scripts/vm/zimaos.sh +++ b/scripts/vm/zimaos.sh @@ -72,6 +72,7 @@ WIZARD_ADD_GPU="no" WIZARD_GPU_RESULT="not_requested" VM_WIZARD_CAPTURE_FILE="" VM_WIZARD_CAPTURE_ACTIVE=0 +VM_STORAGE_IOMMU_PENDING_REBOOT=0 @@ -613,7 +614,7 @@ function select_import_disk() { FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF") fi done < <(lsblk -dn -e 7,11 -o PATH) - + stop_spinner if [[ ${#FREE_DISKS[@]} -eq 0 ]]; then whiptail --title "Error" --msgbox "$(translate "No importable disks available. System disks and protected disks are hidden.")" 9 70 return 1 @@ -633,7 +634,23 @@ function select_import_disk() { } function select_controller_nvme() { + local VM_STORAGE_IOMMU_REBOOT_POLICY="defer" + + if declare -F _vm_storage_ensure_iommu_or_offer >/dev/null 2>&1; then + if ! _vm_storage_ensure_iommu_or_offer; then + return 1 + fi + elif declare -F _pci_is_iommu_active >/dev/null 2>&1; then + if ! _pci_is_iommu_active; then + whiptail --title "Controller + NVMe" --msgbox \ +"$(translate "IOMMU is not active on this host.")\n\n$(translate "Controller/NVMe passthrough requires IOMMU enabled in BIOS/UEFI and kernel.")\n\n$(translate "Enable IOMMU, reboot the host, and try again.")" \ + 14 90 + return 1 + fi + fi + msg_info "$(translate "Detecting PCI storage controllers and NVMe devices...")" + _refresh_host_storage_cache local menu_items=() @@ -736,6 +753,15 @@ function select_controller_nvme() { for pci in $(echo "$selected" | tr -d '"'); do CONTROLLER_NVME_PCIS+=("$pci") done + + if [[ ${#CONTROLLER_NVME_PCIS[@]} -gt 0 ]] && declare -F _vm_storage_confirm_controller_passthrough_risk >/dev/null 2>&1; then + local vm_name_for_notice="${HN:-$NAME}" + if ! _vm_storage_confirm_controller_passthrough_risk "${VMID:-}" "$vm_name_for_notice" "Controller + NVMe"; then + CONTROLLER_NVME_PCIS=() + return 1 + fi + fi + export CONTROLLER_NVME_PCIS } @@ -1092,9 +1118,12 @@ function select_storage_volume() { function create_vm() { # Create the VM - qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1${BIOS_TYPE}${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ + if ! qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1${BIOS_TYPE}${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags proxmenux,nas -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci \ - -serial0 socket + -serial0 socket; then + msg_error "Failed to create base VM. Check VM ID and host configuration." + return 1 + fi msg_ok "Create a $NAME" BOOT_ORDER_LIST=() # Array to store boot order for all disks @@ -1294,7 +1323,15 @@ function create_vm() { fi if [[ ${#CONTROLLER_NVME_PCIS[@]} -gt 0 ]]; then - if ! _vm_is_q35 "$VMID"; then + if declare -F _pci_is_iommu_active >/dev/null 2>&1 && ! _pci_is_iommu_active; then + if [[ "${VM_STORAGE_IOMMU_PENDING_REBOOT:-0}" == "1" ]]; then + msg_warn "$(translate "IOMMU was configured during this wizard and a reboot is pending.")" + msg_warn "$(translate "Controller + NVMe assignment is postponed until after host reboot.")" + else + msg_error "$(translate "IOMMU is not active. Skipping Controller + NVMe assignment.")" + ERROR_FLAG=true + fi + elif ! _vm_is_q35 "$VMID"; then msg_error "$(translate "Controller + NVMe passthrough requires machine type q35. Skipping controller assignment.")" ERROR_FLAG=true else @@ -1466,6 +1503,10 @@ else echo -e "${TAB}• $(translate "First complete ZimaOS setup and verify remote access (web/SSH).")" echo -e "${TAB}• $(translate "Then change the VM display to none (vga: none) when the system is stable.")" fi + if [[ "${VM_STORAGE_IOMMU_PENDING_REBOOT:-0}" == "1" ]]; then + msg_warn "$(translate "IOMMU was enabled during this wizard. Reboot the host to apply it.")" + echo -e "${TAB}$(translate "After reboot, run: Storage -> Add Controller or NVMe PCIe to VM, and select VM") ${VMID}." + fi echo -e