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
+
+
+