diff --git a/package/yast2-bootloader.changes b/package/yast2-bootloader.changes index 67d55d0e2..3ffbe393d 100644 --- a/package/yast2-bootloader.changes +++ b/package/yast2-bootloader.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Apr 26 12:27:34 UTC 2019 - jreidinger + +- renamed "smt" to "cpu_mitigations", improved naming and help + (bsc#1098559) +- 4.1.24 + ------------------------------------------------------------------- Mon Mar 25 15:45:54 CET 2019 - schubi@suse.de diff --git a/package/yast2-bootloader.spec b/package/yast2-bootloader.spec index d08ae954c..928168732 100644 --- a/package/yast2-bootloader.spec +++ b/package/yast2-bootloader.spec @@ -17,7 +17,7 @@ Name: yast2-bootloader -Version: 4.1.23 +Version: 4.1.24 Release: 0 BuildRoot: %{_tmppath}/%{name}-%{version}-build diff --git a/src/autoyast-rnc/bootloader.rnc b/src/autoyast-rnc/bootloader.rnc index 2c2ecf238..4b62d1cab 100644 --- a/src/autoyast-rnc/bootloader.rnc +++ b/src/autoyast-rnc/bootloader.rnc @@ -71,7 +71,7 @@ bl_global = boot_extended? & boot_mbr? & stage1_dev? & - smt? & + cpu_mitigations? & element vgamode { text }? } @@ -86,7 +86,7 @@ boot_root = element boot_root { "true" | "false" } boot_boot = element boot_boot { "true" | "false" } boot_extended = element boot_extended { "true" | "false" } boot_mbr = element boot_mbr { "true" | "false" } -smt = element smt { "true" | "false" } +cpu_mitigations = element cpu_mitigations { "nosmt", "auto", "off", "manual" } sections = element sections { diff --git a/src/lib/bootloader/autoyast_converter.rb b/src/lib/bootloader/autoyast_converter.rb index d0c8a4c1b..e02ca69d8 100644 --- a/src/lib/bootloader/autoyast_converter.rb +++ b/src/lib/bootloader/autoyast_converter.rb @@ -1,6 +1,7 @@ require "yast" require "bootloader/bootloader_factory" +require "bootloader/cpu_mitigations" Yast.import "BootStorage" Yast.import "Arch" @@ -37,7 +38,8 @@ def import(data) # always nil pmbr as autoyast does not support it yet, # so use nil to always use proposed value (bsc#1081967) bootloader.pmbr_action = nil - bootloader.smt = data["global"]["smt"] == "true" unless data["global"]["smt"].nil? + cpu_mitigations = data["global"]["cpu_mitigations"] + bootloader.cpu_mitigations = CpuMitigations.from_string(cpu_mitigations) if cpu_mitigations # TODO: import Initrd log.warn "autoyast profile contain sections which won't be processed" if data["sections"] @@ -57,7 +59,7 @@ def export(config) global = res["global"] export_grub2(global, config) if config.name == "grub2" export_default(global, config.grub_default) - res["global"]["smt"] = config.smt ? "true" : "false" + res["global"]["cpu_mitigations"] = config.cpu_mitigations.value.to_s # Do not export device map as device name are very unpredictable and is used only as # work-around when automatic ones do not work for what-ever reasons ( it can really safe # your day in L3 ) @@ -79,7 +81,7 @@ def import_grub2(data, bootloader) end def import_default(data, default) - # import first kernel params as smt can later modify it + # import first kernel params as cpu_mitigations can later modify it DEFAULT_KERNEL_PARAMS_MAPPING.each do |key, method| val = data["global"][key] next unless val diff --git a/src/lib/bootloader/config_dialog.rb b/src/lib/bootloader/config_dialog.rb index 45c78bcf1..f22400144 100644 --- a/src/lib/bootloader/config_dialog.rb +++ b/src/lib/bootloader/config_dialog.rb @@ -17,6 +17,11 @@ class ConfigDialog include Yast::I18n include Yast::UIShortcuts + # param initial_tab [:boot_code|:kernel|:bootloader] initial tab when dialog open + def initialize(initial_tab: :boot_code) + @initial_tab = initial_tab + end + def run guarded_run rescue ::Bootloader::BrokenConfiguration, ::Bootloader::UnsupportedOption => e @@ -63,13 +68,6 @@ def guarded_run end # F#300779: end - if BootloaderFactory.current.is_a?(NoneBootloader) - contents = VBox(LoaderTypeWidget.new) - else - tabs = CWM::Tabs.new(BootCodeTab.new, KernelTab.new, BootloaderTab.new) - contents = VBox(tabs) - end - Yast::CWM.show( contents, caption: _("Boot Loader Settings"), @@ -79,5 +77,22 @@ def guarded_run skip_store_for: [:redraw] ) end + + def contents + return VBox(LoaderTypeWidget.new) if BootloaderFactory.current.is_a?(NoneBootloader) + + boot_code_tab = BootCodeTab.new + kernel_tab = KernelTab.new + bootloader_tab = BootloaderTab.new + case @initial_tab + when :boot_code then boot_code_tab.initial = true + when :kernel then kernel_tab.initial = true + when :bootloader then bootloader_tab.initial = true + else + raise "unknown initial tab #{@initial_tab.inspect}" + end + + VBox(CWM::Tabs.new(boot_code_tab, kernel_tab, bootloader_tab)) + end end end diff --git a/src/lib/bootloader/cpu_mitigations.rb b/src/lib/bootloader/cpu_mitigations.rb new file mode 100644 index 000000000..05f1aaf69 --- /dev/null +++ b/src/lib/bootloader/cpu_mitigations.rb @@ -0,0 +1,75 @@ +require "yast" + +require "cfa/matcher" +require "cfa/placer" + +module Bootloader + # Specialized class to handle CPU mitigation settings. + # @see https://www.suse.com/support/kb/doc/?id=7023836 + class CpuMitigations + include Yast::Logger + include Yast::I18n + extend Yast::I18n + KERNEL_MAPPING = { + nosmt: "auto,nosmt", + auto: "auto", + off: "off", + manual: nil + }.freeze + + HUMAN_MAPPING = { + nosmt: N_("Auto + No SMT"), + auto: N_("Auto"), + off: N_("Off"), + manual: N_("Manually") + }.freeze + + attr_reader :value + + def initialize(value) + textdomain "bootloader" + + @value = value + end + + # Note: order of ALL is used also in UI as order of combobox. + ALL = KERNEL_MAPPING.keys.map { |k| CpuMitigations.new(k) } + DEFAULT = CpuMitigations.new(:auto) + + def self.from_kernel_params(kernel_params) + log.info "kernel params #{kernel_params.inspect}" + param = kernel_params.parameter("mitigations") + log.info "mitigation param #{param.inspect}" + param = nil if param == false + reverse_mapping = KERNEL_MAPPING.invert + raise "Unknown mitigations value #{param.inspect}" if !reverse_mapping.key?(param) + + new(reverse_mapping[param]) + end + + def self.from_string(string) + raise "Unknown mitigations value #{string.inspect}" unless KERNEL_MAPPING.key?(string.to_sym) + + new(string.to_sym) + end + + def to_human_string + _(HUMAN_MAPPING[value]) + end + + def kernel_value + KERNEL_MAPPING[value] or raise "Invalid value #{value.inspect}" + end + + def modify_kernel_params(kernel_params) + matcher = CFA::Matcher.new(key: "mitigations") + + kernel_params.remove_parameter(matcher) + if value != :manual + # TODO: fix cfa_grub2 with replace placer + kernel_params.add_parameter("mitigations", kernel_value) + log.info "replacing old config with #{kernel_value}: #{kernel_params.inspect}" + end + end + end +end diff --git a/src/lib/bootloader/grub2_widgets.rb b/src/lib/bootloader/grub2_widgets.rb index 45a42870c..faeb5f8fc 100644 --- a/src/lib/bootloader/grub2_widgets.rb +++ b/src/lib/bootloader/grub2_widgets.rb @@ -3,6 +3,7 @@ require "bootloader/generic_widgets" require "bootloader/device_map_dialog" require "bootloader/serial_console" +require "bootloader/cpu_mitigations" require "cfa/matcher" Yast.import "BootStorage" @@ -113,7 +114,7 @@ def store end # Represents decision if smt is enabled - class Smt < CWM::CheckBox + class CpuMitigationsWidget < CWM::ComboBox include Grub2Widget def initialize @@ -121,22 +122,45 @@ def initialize end def label - _("Disable Simultaneous &Multithreading") + _("CPU Mitigations") + end + + def items + ::Bootloader::CpuMitigations::ALL.map do |m| + [m.value, m.to_human_string] + end end def help _( - "

Disable Simultaneous Multithreading
\n" \ - "To disable sharing physical cores by more virtual ones." + "

CPU Mitigations
\n" \ + "The option selects which default settings should be used for CPU " \ + "side channels mitigations. A highlevel description is on our Technical Information " \ + "Document TID 7023836. Following options are available:

" ) end def init - self.value = !grub2.smt + self.value = grub2.cpu_mitigations.value end def store - grub2.smt = !value + grub2.cpu_mitigations = ::Bootloader::CpuMitigations.new(value) end end @@ -240,7 +264,7 @@ def help end def init - self.value = grub_default.kernel_params.serialize.gsub(/nosmt/, "") + self.value = grub_default.kernel_params.serialize.gsub(/mitigations=\S+/, "") end def store @@ -872,11 +896,10 @@ def label def contents console_widget = Yast::Arch.s390 ? CWM::Empty.new("console") : ConsoleWidget.new - smt_widget = Yast::Arch.x86_64 ? MarginBox(1, 0.5, Smt.new) : CWM::Empty.new("smt") VBox( VSpacing(1), MarginBox(1, 0.5, KernelAppendWidget.new), - Left(smt_widget), + MarginBox(1, 0.5, Left(CpuMitigationsWidget.new)), MarginBox(1, 0.5, console_widget), VStretch() ) @@ -887,10 +910,6 @@ def contents class BootCodeTab < CWM::Tab include Grub2Widget - def initialize - self.initial = true - end - def label textdomain "bootloader" diff --git a/src/lib/bootloader/grub2base.rb b/src/lib/bootloader/grub2base.rb index e9beadd02..888d5cdbb 100644 --- a/src/lib/bootloader/grub2base.rb +++ b/src/lib/bootloader/grub2base.rb @@ -54,7 +54,7 @@ def initialize @grub_default = ::CFA::Grub2::Default.new @sections = ::Bootloader::Sections.new @pmbr_action = :nothing - @smt = nil # nil means not set explicitly, otherwise boolean + @explicit_cpu_mitigations = false end # general functions @@ -75,24 +75,18 @@ def pmbr_setup(*devices) end end - def smt - !grub_default.kernel_params.parameter("nosmt") + def cpu_mitigations + CpuMitigations.from_kernel_params(grub_default.kernel_params) end - def explicit_smt - @smt + def explicit_cpu_mitigations + @explicit_cpu_mitigations ? cpu_mitigations : nil end - def smt=(value) - log.info "setting smt to #{value}" - @smt = value - - if value - matcher = CFA::Matcher.new(key: "nosmt") - grub_default.kernel_params.remove_parameter(matcher) - elsif !grub_default.kernel_params.parameter("nosmt") - grub_default.kernel_params.add_parameter("nosmt", true) - end + def cpu_mitigations=(value) + log.info "setting mitigations to #{value}" + @explicit_cpu_mitigations = true + value.modify_kernel_params(grub_default.kernel_params) end def read @@ -219,28 +213,37 @@ def merge_grub_default(other) log.info "before merge other #{other_default.inspect}" KERNEL_FLAVORS_METHODS.each do |method| - other_params = other_default.public_send(method) - default_params = default.public_send(method) - next if other_params.empty? - - default_serialize = default_params.serialize - # handle specially noresume as it should lead to remove all other resume - default_serialize.gsub!(/resume=\S+/, "") if other_params.parameter("noresume") - - new_kernel_params = default_serialize + " " + other_params.serialize - - default_params.replace(new_kernel_params) + merge_kernel_params(method, other_default) end merge_attributes(default, other_default) - # explicitly set smt - self.smt = other.explicit_smt unless other.explicit_smt.nil? - log.info "smt after merge #{smt}" + # explicitly set mitigations means overwrite of our + if other.explicit_cpu_mitigations + log.info "merging cpu_mitigations" + self.cpu_mitigations = other.cpu_mitigations + end + log.info "mitigations after merge #{cpu_mitigations}" log.info "after merge default #{default.inspect}" end + def merge_kernel_params(method, other_default) + other_params = other_default.public_send(method) + default_params = grub_default.public_send(method) + return if other_params.empty? + + default_serialize = default_params.serialize + # handle specially noresume as it should lead to remove all other resume + default_serialize.gsub!(/resume=\S+/, "") if other_params.parameter("noresume") + # prevent double cpu_mitigations params + default_serialize.gsub!(/mitigations=\S+/, "") if other_params.parameter("mitigations") + + new_kernel_params = default_serialize + " " + other_params.serialize + + default_params.replace(new_kernel_params) + end + def merge_attributes(default, other) # string attributes [:serial_console, :timeout, :hidden_timeout, :distributor, @@ -338,4 +341,5 @@ def propose_encrypted grub_default.cryptodisk.value = !!Yast::BootStorage.encrypted_boot? end end + # rubocop:enable Metrics/ClassLength end diff --git a/src/modules/BootArch.rb b/src/modules/BootArch.rb index 3a236dbc5..60d71133d 100644 --- a/src/modules/BootArch.rb +++ b/src/modules/BootArch.rb @@ -19,6 +19,8 @@ # require "yast" +require "bootloader/cpu_mitigations" + module Yast class BootArchClass < Module include Yast::Logger @@ -37,7 +39,8 @@ def main # from installation to running kernel on s390 (bsc#1086665) S390_WHITELIST = [ /net\.ifnames=\S*/, - /fips=\S*/ + /fips=\S*/, + /mitigations=\S*/ ].freeze # Get parameters for the default kernel @@ -54,8 +57,8 @@ def DefaultKernelParams(resume) if Arch.i386 || Arch.x86_64 || Arch.aarch64 || Arch.ppc ret = kernel_cmdline ret << " resume=#{resume}" unless resume.empty? - ret << propose_smt if Arch.x86_64 ret << " #{features}" unless features.empty? + ret << propose_cpu_mitigations ret << " quiet" return ret elsif Arch.s390 @@ -70,11 +73,12 @@ def DefaultKernelParams(resume) parameters << " #{Regexp.last_match(0)}" if kernel_cmdline =~ pattern end + parameters << propose_cpu_mitigations parameters << " resume=#{resume}" unless resume.empty? return parameters else log.warn "Default kernel parameters not defined" - return kernel_cmdline + return kernel_cmdline + propose_cpu_mitigations end end @@ -84,28 +88,24 @@ def ResumeAvailable Arch.i386 || Arch.x86_64 || Arch.s390 end - SMT_DEFAULT = true - def smt_settings - linuxrc_value = Yast::Linuxrc.value_for("disablesmt") - product_value = ProductFeatures.GetBooleanFeatureWithFallback("globals", "smt", SMT_DEFAULT) - log.info "smt settings: linuxrc #{linuxrc_value.inspect} product #{product_value.inspect}" - # linuxrc cmdline - return linuxrc_value == "0" if !linuxrc_value.nil? + def propose_cpu_mitigations + linuxrc_value = Yast::Linuxrc.value_for("mitigations") + log.info "linuxrc mitigations #{linuxrc_value.inspect}" + return "" unless linuxrc_value.nil? # linuxrc already has mitigations + product_value = ProductFeatures.GetStringFeature("globals", "cpu_mitigations") + log.info "cpu mitigations in product: #{product_value.inspect}" - # product features - product_value + mitigations = if product_value.empty? + ::Bootloader::CpuMitigations::DEFAULT + else + ::Bootloader::CpuMitigations.from_string(product_value) + end + # no value for manual mitigations + mitigations.kernel_value ? " mitigations=#{mitigations.kernel_value}" : "" end publish :function => :DefaultKernelParams, :type => "string (string)" publish :function => :ResumeAvailable, :type => "boolean ()" - - private - - DISABLE_SMT = " nosmt".freeze - - def propose_smt - smt_settings ? "" : DISABLE_SMT - end end BootArch = BootArchClass.new diff --git a/test/autoyast_converter_test.rb b/test/autoyast_converter_test.rb index cd40f70a9..dda354c4b 100644 --- a/test/autoyast_converter_test.rb +++ b/test/autoyast_converter_test.rb @@ -116,13 +116,13 @@ bootloader.trusted_boot = true expected_export = { - "append" => "verbose nomodeset", - "terminal" => "gfxterm", - "os_prober" => "true", - "hiddenmenu" => "true", - "timeout" => 10, - "trusted_grub" => "true", - "smt" => "true" + "append" => "verbose nomodeset", + "terminal" => "gfxterm", + "os_prober" => "true", + "hiddenmenu" => "true", + "timeout" => 10, + "trusted_grub" => "true", + "cpu_mitigations" => "manual" } expect(subject.export(bootloader)["global"]).to eq expected_export diff --git a/test/boot_arch_test.rb b/test/boot_arch_test.rb index 66aa51be5..7f218912b 100644 --- a/test/boot_arch_test.rb +++ b/test/boot_arch_test.rb @@ -5,6 +5,11 @@ describe Yast::BootArch do subject { Yast::BootArch } + before do + allow(Yast::ProductFeatures).to receive(:GetStringFeature) + .and_return("") + end + def stub_arch(arch) Yast.import "Arch" @@ -50,7 +55,8 @@ def stub_arch(arch) end it "adds additional parameters from Product file" do - allow(Yast::ProductFeatures).to receive(:GetStringFeature).and_return("console=ttyS0") + allow(Yast::ProductFeatures).to receive(:GetStringFeature) + .with("globals", "additional_kernel_parameters").and_return("console=ttyS0") expect(subject.DefaultKernelParams("/dev/sda2")).to include("console=ttyS0") end @@ -74,7 +80,8 @@ def stub_arch(arch) end it "adds additional parameters from Product file" do - allow(Yast::ProductFeatures).to receive(:GetStringFeature).and_return("console=ttyS0") + allow(Yast::ProductFeatures).to receive(:GetStringFeature) + .with("globals", "additional_kernel_parameters").and_return("console=ttyS0") expect(subject.DefaultKernelParams("/dev/sda2")).to include("console=ttyS0") end @@ -122,9 +129,12 @@ def stub_arch(arch) it "returns parameters from current command line" do allow(Yast::Kernel).to receive(:GetCmdLine).and_return("console=ttyS0") # just to test that it do not add product features - allow(Yast::ProductFeatures).to receive(:GetStringFeature).and_return("console=ttyS1") + allow(Yast::ProductFeatures).to receive(:GetStringFeature) + .with("globals", "additional_kernel_parameters").and_return("console=ttyS1") - expect(subject.DefaultKernelParams("/dev/sda2")).to eq "console=ttyS0 resume=/dev/sda2 console=ttyS1 quiet" + expect(subject.DefaultKernelParams("/dev/sda2")).to eq( + "console=ttyS0 resume=/dev/sda2 console=ttyS1 mitigations=auto quiet" + ) end it "adds \"quiet\" parameter" do diff --git a/test/grub2_efi_test.rb b/test/grub2_efi_test.rb index b6c6c889e..a16aee0b6 100644 --- a/test/grub2_efi_test.rb +++ b/test/grub2_efi_test.rb @@ -3,6 +3,12 @@ require "bootloader/grub2efi" describe Bootloader::Grub2EFI do + subject do + sub = described_class.new + allow(sub).to receive(:cpu_mitigations).and_return(::Bootloader::CpuMitigations.new(:manual)) + sub + end + before do allow(::CFA::Grub2::Default).to receive(:new).and_return(double("GrubDefault").as_null_object) allow(::CFA::Grub2::GrubCfg).to receive(:new).and_return(double("GrubCfg").as_null_object) diff --git a/test/grub2_test.rb b/test/grub2_test.rb index a3e2b1937..1e1cbb91b 100644 --- a/test/grub2_test.rb +++ b/test/grub2_test.rb @@ -3,6 +3,12 @@ require "bootloader/grub2" describe Bootloader::Grub2 do + subject do + sub = described_class.new + allow(sub).to receive(:cpu_mitigations).and_return(::Bootloader::CpuMitigations.new(:manual)) + sub + end + before do allow(::CFA::Grub2::Default).to receive(:new).and_return(double("GrubDefault").as_null_object) allow(::CFA::Grub2::GrubCfg).to receive(:new).and_return(double("GrubCfg").as_null_object) diff --git a/test/grub2base_test.rb b/test/grub2base_test.rb index 2f0c502db..6dd1f7785 100644 --- a/test/grub2base_test.rb +++ b/test/grub2base_test.rb @@ -3,6 +3,11 @@ require "bootloader/grub2base" describe Bootloader::Grub2Base do + before do + allow(Yast::ProductFeatures).to receive(:GetStringFeature) + .and_return("") + end + describe "#read" do before do allow(::CFA::Grub2::Default).to receive(:new).and_return(double("GrubDefault", loaded?: false, load: nil, save: nil))