diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index c184762..9a82cc6 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -28,6 +28,7 @@ import collections import copy import os import os.path +import re import subprocess import sys import threading @@ -462,6 +463,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): self.kernel, self.vm, 'kernel', self.vm.app.default_kernel, allow_default=True, allow_none=True) + self.kernel.currentIndexChanged.connect(self.kernel_changed) else: self.kernel_groupbox.setVisible(False) @@ -479,6 +481,8 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): (lambda vm: vm.klass == 'DispVM'), allow_default=True, allow_none=True) + self.update_virt_mode_list() + def __apply_advanced_tab__(self): msg = [] @@ -518,12 +522,105 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): except Exception as ex: msg.append(str(ex)) + try: + if self.virt_mode.currentIndex() != self.virt_mode_idx: + self.vm.virt_mode = self.selected_virt_mode() + self.anything_changed = True + except Exception as ex: + msg.append(str(ex)) + return msg def boot_from_cdrom_button_pressed(self): self.save_and_apply() subprocess.check_call(['qubes-vm-boot-from-device', self.vm.name]) + def selected_virt_mode(self): + return self.virt_mode_list[self.virt_mode.currentIndex()] + + def virt_mode_changed(self, new_idx): + self.update_pv_warning() + self.update_pvh_dont_support_devs() + self.update_pvh_kernel_version_warning() + + def update_pv_warning(self): + if self.selected_virt_mode() == 'pv': + self.pv_warning.show() + else: + self.pv_warning.hide() + + def update_virt_mode_list(self): + choices = ['hvm', 'pv'] + + if hasattr(self, 'dev_list'): + devs_attached = self.dev_list.selected_list.count() != 0 + else: + devs_attached = len(list(self.vm.devices['pci'].persistent())) != 0 + + if devs_attached: + self.pvh_mode_hidden.show() + else: + choices.insert(0, 'pvh') + self.pvh_mode_hidden.hide() + + if hasattr(self, 'virt_mode_list'): + old_mode = self.selected_virt_mode() + self.virt_mode.currentIndexChanged.disconnect() + else: + old_mode = None + + self.virt_mode.clear() + + # XXX: Hardcoded default value. + self.virt_mode_list, self.virt_mode_idx = utils.prepare_choice( + self.virt_mode, self.vm, 'virt_mode', choices, 'hvm', + allow_default=True) + + if old_mode is not None: + self.virt_mode.setCurrentIndex(self.virt_mode_list.index(old_mode)) + + self.virt_mode.currentIndexChanged.connect(self.virt_mode_changed) + + self.update_pv_warning() + self.update_pvh_kernel_version_warning() + + def update_pvh_kernel_version_warning(self): + if self.selected_virt_mode() != 'pvh': + self.pvh_kernel_version_warning.hide() + return + + kernel = self.kernel_list[self.kernel.currentIndex()] + + if self.pvh_kernel_version_ok(kernel): + self.pvh_kernel_version_warning.hide() + else: + self.pvh_kernel_version_warning.show() + + def kernel_changed(self): + self.update_pvh_kernel_version_warning() + + def pvh_kernel_version_ok(self, name): + # There are nearly no limitaions on kernel names (only file system and + # general qvm-prefs rules). So we just look if we see something which + # looks like a version number. It's just a warning to help the user + # anyways. + + if name is None: + return False + + if name is qubesadmin.DEFAULT: + name = self.vm.app.default_kernel + + m = re.search('(\d+)\.(\d+)', name) + + if m is None: + return False + + x = int(m.group(1)) + y = int(m.group(2)) + + return (x, y) >= (4, 11) + ######## devices tab def __init_devices_tab__(self): self.dev_list = multiselectwidget.MultiSelectWidget(self) @@ -566,6 +663,8 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): self.dev_list.setEnabled(True) self.turn_off_vm_to_modify_devs.setVisible(False) + self.update_pvh_dont_support_devs() + def __apply_devices_tab__(self): msg = [] @@ -621,6 +720,16 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): self.dmm_warning_adv.hide() self.dmm_warning_dev.hide() + self.update_virt_mode_list() + + def update_pvh_dont_support_devs(self): + if self.selected_virt_mode() == 'pvh': + self.dev_list.setEnabled(False) + self.pvh_dont_support_devs.setVisible(True) + else: + self.dev_list.setEnabled(True) + self.pvh_dont_support_devs.setVisible(False) + ######## applications tab def refresh_apps_in_vm(self, t_monitor): diff --git a/ui/settingsdlg.ui b/ui/settingsdlg.ui index ee24036..b4e913f 100644 --- a/ui/settingsdlg.ui +++ b/ui/settingsdlg.ui @@ -409,7 +409,7 @@ QFormLayout::AllNonFixedFieldsGrow - + Kernel: @@ -422,14 +422,31 @@ - + + + + + 75 + true + true + + + + PVH mode requires Linux 4.11 or newer. + + + true + + + + Kernel opts: - + @@ -447,6 +464,87 @@ + + + + QLayout::SetDefaultConstraint + + + 0 + + + + + true + + + + 0 + 0 + + + + Virtualization + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Mode: + + + virt_mode + + + + + + + + + + + 75 + true + true + + + + color:rgb(255, 0, 0) + + + Using PV mode exposes more hypervisor attack surface! + + + true + + + + + + + + 75 + true + true + + + + PVH mode is hidden since it doesn't support PCI passthrough. + + + true + + + + + + + + @@ -894,6 +992,20 @@ + + + + + 75 + true + true + + + + Currently PVH VMs don't support PCI passthrough. Select another virtualization mode if you want to add PCI devices + + +