diff --git a/ui/details.ui b/ui/details.ui index 8b3f939e..5fd3f3e4 100644 --- a/ui/details.ui +++ b/ui/details.ui @@ -2122,6 +2122,21 @@ 1 + + + Enable available CPU security flaw mitigations + True + True + False + True + + + + 0 + 2 + 2 + + diff --git a/virtManager/details.py b/virtManager/details.py index 10398b56..4e4e8439 100644 --- a/virtManager/details.py +++ b/virtManager/details.py @@ -537,6 +537,7 @@ class vmmDetails(vmmGObjectUI): "on_cpu_maxvcpus_changed": self.config_maxvcpus_changed, "on_cpu_model_changed": lambda *x: self.config_cpu_model_changed(x), "on_cpu_copy_host_clicked": self.on_cpu_copy_host_clicked, + "on_cpu_secure_toggled": self.on_cpu_secure_toggled, "on_cpu_cores_changed": self.config_cpu_topology_changed, "on_cpu_sockets_changed": self.config_cpu_topology_changed, "on_cpu_threads_changed": self.config_cpu_topology_changed, @@ -1732,6 +1733,11 @@ class vmmDetails(vmmGObjectUI): def on_cpu_copy_host_clicked(self, src): uiutil.set_grid_row_visible( self.widget("cpu-model"), not src.get_active()) + uiutil.set_grid_row_visible( + self.widget("cpu-secure"), not src.get_active()) + self.enable_apply(EDIT_CPU) + + def on_cpu_secure_toggled(self, ignore): self.enable_apply(EDIT_CPU) def config_cpu_model_changed(self, ignore): @@ -2032,6 +2038,7 @@ class vmmDetails(vmmGObjectUI): if self.edited(EDIT_CPU): kwargs["model"] = self.get_config_cpu_model() + kwargs["secure"] = self.widget("cpu-secure").get_active() if self.edited(EDIT_TOPOLOGY): do_top = self.widget("cpu-topology-enable").get_active() @@ -2605,6 +2612,11 @@ class vmmDetails(vmmGObjectUI): n1, n2 = self.vm.network_traffic_vectors() self.network_traffic_graph.set_property("data_array", n1 + n2) + def _cpu_secure_is_available(self): + domcaps = self.vm.get_domain_capabilities() + features = domcaps.get_cpu_security_features() + return self.vm.get_xmlobj().os.is_x86() and len(features) > 0 + def refresh_config_cpu(self): # Set topology first, because it impacts maxvcpus values cpu = self.vm.get_cpu_config() @@ -2658,6 +2670,15 @@ class vmmDetails(vmmGObjectUI): self.widget("cpu-copy-host").set_active(bool(is_host)) self.on_cpu_copy_host_clicked(self.widget("cpu-copy-host")) + if not self._cpu_secure_is_available(): + self.widget("cpu-secure").set_sensitive(False) + self.widget("cpu-secure").set_tooltip_text( + "No security features to copy, the host is missing " + "security patches or the host CPU is not vulnerable.") + + cpu.check_security_features(self.vm.get_xmlobj()) + self.widget("cpu-secure").set_active(cpu.secure) + def refresh_config_memory(self): host_mem_widget = self.widget("state-host-memory") host_mem = self.vm.conn.host_memory_size() // 1024 diff --git a/virtManager/domain.py b/virtManager/domain.py index 2134bfbc..f4f03c7a 100644 --- a/virtManager/domain.py +++ b/virtManager/domain.py @@ -442,7 +442,7 @@ class vmmDomain(vmmLibvirtObject): self._redefine_xmlobj(xmlobj) def define_cpu(self, vcpus=_SENTINEL, maxvcpus=_SENTINEL, - model=_SENTINEL, sockets=_SENTINEL, + model=_SENTINEL, secure=_SENTINEL, sockets=_SENTINEL, cores=_SENTINEL, threads=_SENTINEL): guest = self._make_xmlobj_to_define() @@ -456,7 +456,8 @@ class vmmDomain(vmmLibvirtObject): guest.cpu.cores = cores guest.cpu.threads = threads - if model != _SENTINEL: + if secure != _SENTINEL or model != _SENTINEL: + guest.cpu.secure = secure if model in guest.cpu.SPECIAL_MODES: guest.cpu.set_special_mode(guest, model) else: diff --git a/virtinst/domain/cpu.py b/virtinst/domain/cpu.py index bf553449..45fdbd3a 100644 --- a/virtinst/domain/cpu.py +++ b/virtinst/domain/cpu.py @@ -122,6 +122,36 @@ class DomainCpu(XMLBuilder): if not exists: self.add_feature(feature) + def check_security_features(self, guest): + """ + Since 'secure' property is not exported into the domain XML + we might need to refresh its state. + """ + domcaps = guest.lookup_domcaps() + features = domcaps.get_cpu_security_features() + + if len(features) == 0: + self.secure = False + return + + for feature in features: + exists = False + for f in self.features: + if f.name == feature and f.policy == "require": + exists = True + break + if not exists: + self.secure = False + return + + def _remove_security_features(self, guest): + domcaps = guest.lookup_domcaps() + for feature in domcaps.get_cpu_security_features(): + for f in self.features: + if f.name == feature and f.policy == "require": + self.remove_child(f) + break + def set_model(self, guest, val): logging.debug("setting cpu model %s", val) if val: