From 009d051a0011e2cf4be2eb6071de3e4e99bee140 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 14 Nov 2024 08:17:27 +0100 Subject: [PATCH 01/46] supporting Grub2-BLS --- README.md | 1 + doc/bootloader_backend.svg | 1490 +++++++--------------- package/yast2-bootloader.changes | 6 + package/yast2-bootloader.spec | 2 +- src/lib/bootloader/autoyast_converter.rb | 26 +- src/lib/bootloader/bls_sections.rb | 95 ++ src/lib/bootloader/bootloader_base.rb | 12 + src/lib/bootloader/bootloader_factory.rb | 10 + src/lib/bootloader/config_dialog.rb | 2 +- src/lib/bootloader/generic_widgets.rb | 18 +- src/lib/bootloader/grub2_widgets.rb | 55 +- src/lib/bootloader/grub2base.rb | 8 - src/lib/bootloader/grub2bls.rb | 193 +++ src/lib/bootloader/sysconfig.rb | 2 +- src/lib/bootloader/systemdboot.rb | 14 +- src/lib/bootloader/systeminfo.rb | 2 +- src/modules/BootSupportCheck.rb | 4 +- test/bls_sections_test.rb | 76 ++ test/boot_support_test.rb | 7 + test/bootloader_factory_test.rb | 8 +- test/grub2_bls_test.rb | 166 +++ 21 files changed, 1082 insertions(+), 1115 deletions(-) create mode 100644 src/lib/bootloader/bls_sections.rb create mode 100644 src/lib/bootloader/grub2bls.rb create mode 100755 test/bls_sections_test.rb create mode 100644 test/grub2_bls_test.rb diff --git a/README.md b/README.md index 8ca3e2166..79081195b 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ that holds and also can propose the bootloader implementation. So now let's expl - [GRUB2](https://www.rubydoc.info/github/yast/yast-bootloader/master/Bootloader/Grub2) for legacy booting or emulated grub2 boot like s390x. - [GRUB2-EFI](https://www.rubydoc.info/github/yast/yast-bootloader/master/Bootloader/Grub2EFI) for EFI variant of GRUB2 bootloader +- [GRUB2-BLS](https://www.rubydoc.info/github/yast/yast-bootloader/master/Bootloader/Grub2Bls) bootloader based on Boot Loader Specification(BLS) (for EFI only) - [systemd-boot](https://www.rubydoc.info/github/yast/yast-bootloader/master/Bootloader/SystemdBoot) systemd bootloader (for EFI only) - [None](https://www.rubydoc.info/github/yast/yast-bootloader/master/Bootloader/NoneBootloader) when YaST does not manage booting - [GRUB2 base](https://www.rubydoc.info/github/yast/yast-bootloader/master/Bootloader/Grub2Base) is the shared functionality for both GRUB2 implementations diff --git a/doc/bootloader_backend.svg b/doc/bootloader_backend.svg index aa013103e..5f14779c4 100644 --- a/doc/bootloader_backend.svg +++ b/doc/bootloader_backend.svg @@ -1,1044 +1,446 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - systemd-boot - - - - - + + Created with Fabric.js 5.2.4 + + + Layer 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + systemd-boot + + + + + + + + + + GRUB2-BLS + + + + + + + + + + + + + + + + + + + + sdbootutil + + + + + + \ No newline at end of file diff --git a/package/yast2-bootloader.changes b/package/yast2-bootloader.changes index 7c29d46f9..ad8abf0d3 100644 --- a/package/yast2-bootloader.changes +++ b/package/yast2-bootloader.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Nov 13 16:28:04 UTC 2024 - Stefan Schubert + +- Added grub2-bls support (jsc#PED-10703). +- 5.0.13 + ------------------------------------------------------------------- Tue Nov 5 09:57:58 UTC 2024 - Josef Reidinger diff --git a/package/yast2-bootloader.spec b/package/yast2-bootloader.spec index b434dfc1a..7602e9dfe 100644 --- a/package/yast2-bootloader.spec +++ b/package/yast2-bootloader.spec @@ -17,7 +17,7 @@ Name: yast2-bootloader -Version: 5.0.12 +Version: 5.0.13 Release: 0 Summary: YaST2 - Bootloader Configuration License: GPL-2.0-or-later diff --git a/src/lib/bootloader/autoyast_converter.rb b/src/lib/bootloader/autoyast_converter.rb index bbb698ccc..3dcae73ce 100644 --- a/src/lib/bootloader/autoyast_converter.rb +++ b/src/lib/bootloader/autoyast_converter.rb @@ -34,16 +34,18 @@ def import(data) return bootloader if bootloader.name == "none" case bootloader.name - when "grub2", "grub2-efi" - import_grub2(data, bootloader) - import_grub2efi(data, bootloader) - import_stage1(data, bootloader) + when "grub2", "grub2-efi", "grub2-bls" + if ["grub2", "grub2-efi"].include?(bootloader.name) + import_grub2(data, bootloader) + import_grub2efi(data, bootloader) + import_stage1(data, bootloader) + import_device_map(data, bootloader) + import_password(data, bootloader) + # always nil pmbr as autoyast does not support it yet, + # so use nil to always use proposed value (bsc#1081967) + bootloader.pmbr_action = nil + end import_default(data, bootloader.grub_default) - import_device_map(data, bootloader) - import_password(data, bootloader) - # always nil pmbr as autoyast does not support it yet, - # so use nil to always use proposed value (bsc#1081967) - bootloader.pmbr_action = nil cpu_mitigations = data.global.cpu_mitigations if cpu_mitigations bootloader.cpu_mitigations = CpuMitigations.from_string(cpu_mitigations) @@ -72,18 +74,18 @@ def export(config) res["global"] = {} case config.name - when "grub2", "grub2-efi" + when "grub2", "grub2-efi", "grub2-bls" global = res["global"] export_grub2(global, config) if config.name == "grub2" export_grub2efi(global, config) if config.name == "grub2-efi" + export_password(global, config.password) if ["grub2", "grub2-efi"].include?(config.name) export_default(global, config.grub_default) - export_password(global, config.password) res["global"]["cpu_mitigations"] = config.cpu_mitigations.value.to_s when "systemd-boot" res["global"]["timeout"] = config.menu_timeout res["global"]["secure_boot"] = config.secure_boot else - raise UnsupportedBootloader, bootloader.name + raise UnsupportedBootloader, config.name end # 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 diff --git a/src/lib/bootloader/bls_sections.rb b/src/lib/bootloader/bls_sections.rb new file mode 100644 index 000000000..b934bb8a9 --- /dev/null +++ b/src/lib/bootloader/bls_sections.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require "json" +require "yast" +require "yast2/execute" + +Yast.import "Misc" + +module Bootloader + # Represents available sections and handling of default BLS boot entry + class BlsSections + include Yast::Logger + + # @return [Array] list of all available boot titles + # or an empty array + attr_reader :all + + # @return [String] title of default boot section. + attr_reader :default + + def initialize + @all = [] + @default = "" + end + + # Sets default section internally. + # @param [String] value of new boot title to boot + # @note to write it to system use #write later + def default=(value) + log.info "set new default to '#{value.inspect}'" + + # empty value mean no default specified + if !all.empty? && !all.include?(value) && !value.empty? + log.warn "Invalid value #{value} trying to set as default. Fallback to default" + value = "" + end + + @default = value + end + + # writes default to system making it persistent + def write + return if @default.empty? + + write_default + end + + def read + @data = read_entries + @all = @data.map { |e| e["title"] } + @default = read_default + end + + private + + OS_RELEASE_PATH = "/etc/os-release" + + def grubenv_path + str = Yast::Misc.CustomSysconfigRead("ID_LIKE", "openSUSE", + OS_RELEASE_PATH) + os = str.split.first + File.join("/boot/efi/EFI/", os, "/grubenv") + end + + # @return [String] return default boot as string or "" if not set + # or something goes wrong + def read_default + Yast::Misc.CustomSysconfigRead("default", "", + grubenv_path) + end + + # write default entry + def write_default + ret = Yast::Execute.on_target("/usr/bin/sdbootutil", + "set-default", @default, + allowed_exitstatus: [0, 1]) + + return unless ret != 0 + + # fallback directly over grub2-editenv + Yast::Execute.on_target("/usr/bin/grub2-editenv", grubenv_path, + "set", "default=" + @default) + end + + # @return [Array] return array of entries or [] + def read_entries + output = Yast::Execute.on_target( + "/usr/bin/bootctl", "--json=short", "list", stdout: :capture + ) + return [] if output.nil? + + JSON.parse(output) + end + end +end diff --git a/src/lib/bootloader/bootloader_base.rb b/src/lib/bootloader/bootloader_base.rb index 7268dd55c..b01230467 100644 --- a/src/lib/bootloader/bootloader_base.rb +++ b/src/lib/bootloader/bootloader_base.rb @@ -11,7 +11,11 @@ module Bootloader # Represents base for all kinds of bootloaders class BootloaderBase + include Yast::I18n + def initialize + textdomain "bootloader" + @read = false @proposed = false @initial_sysconfig = Sysconfig.from_system @@ -113,5 +117,13 @@ def include_kexec_tools_package? def restore_initial_sysconfig @initial_sysconfig.write end + + def status_string(status) + if status + _("enabled") + else + _("disabled") + end + end end end diff --git a/src/lib/bootloader/bootloader_factory.rb b/src/lib/bootloader/bootloader_factory.rb index f63b324a3..9e39df361 100644 --- a/src/lib/bootloader/bootloader_factory.rb +++ b/src/lib/bootloader/bootloader_factory.rb @@ -5,6 +5,7 @@ require "bootloader/none_bootloader" require "bootloader/grub2" require "bootloader/grub2efi" +require "bootloader/grub2bls" require "bootloader/systemdboot" require "bootloader/exceptions" @@ -24,6 +25,7 @@ class BootloaderFactory # Keyword used in autoyast for default bootloader used for given system. DEFAULT_KEYWORD = "default" SYSTEMDBOOT = "systemd-boot" + GRUB2BLS = "grub2-bls" class << self include Yast::Logger @@ -57,6 +59,7 @@ def supported_names if Yast::Mode.config # default means bootloader use what it think is the best result = BootloaderFactory::SUPPORTED_BOOTLOADERS.clone + result << GRUB2BLS if use_grub2_bls? result << SYSTEMDBOOT if use_systemd_boot? result << DEFAULT_KEYWORD return result @@ -72,6 +75,7 @@ def supported_names # grub2 everywhere except aarch64 or riscv64 ret << "grub2" unless Systeminfo.efi_mandatory? ret << "grub2-efi" if Systeminfo.efi_supported? + ret << GRUB2BLS if use_grub2_bls? ret << SYSTEMDBOOT if use_systemd_boot? ret << "none" # avoid double entry for selected one @@ -89,6 +93,8 @@ def bootloader_by_name(name) @cached_bootloaders["grub2-efi"] ||= Grub2EFI.new when "systemd-boot" @cached_bootloaders["systemd-boot"] ||= SystemdBoot.new + when "grub2-bls" + @cached_bootloaders["grub2-bls"] ||= Grub2Bls.new when "none" @cached_bootloaders["none"] ||= NoneBootloader.new when String @@ -108,6 +114,10 @@ def use_systemd_boot? (Yast::Arch.x86_64 || Yast::Arch.aarch64) # only these architectures are supported. end + def use_grub2_bls? + (Yast::Arch.x86_64 || Yast::Arch.aarch64) # only these architectures are supported. + end + def grub2_efi_installable? Systeminfo.efi_mandatory? || ((Yast::Arch.x86_64 || Yast::Arch.i386) && Systeminfo.efi?) diff --git a/src/lib/bootloader/config_dialog.rb b/src/lib/bootloader/config_dialog.rb index e2d5a697d..52ccfdc29 100644 --- a/src/lib/bootloader/config_dialog.rb +++ b/src/lib/bootloader/config_dialog.rb @@ -93,7 +93,7 @@ def contents boot_code_tab = ::Bootloader::SystemdBootWidget::BootCodeTab.new kernel_tab = ::Bootloader::SystemdBootWidget::KernelTab.new bootloader_tab = ::Bootloader::SystemdBootWidget::BootloaderTab.new - else + else # grub2, grub2-efi, grub2-bls boot_code_tab = ::Bootloader::Grub2Widget::BootCodeTab.new kernel_tab = ::Bootloader::Grub2Widget::KernelTab.new bootloader_tab = ::Bootloader::Grub2Widget::BootloaderTab.new diff --git a/src/lib/bootloader/generic_widgets.rb b/src/lib/bootloader/generic_widgets.rb index f3c938124..5709dba8a 100644 --- a/src/lib/bootloader/generic_widgets.rb +++ b/src/lib/bootloader/generic_widgets.rb @@ -43,6 +43,7 @@ def localized_names(name) names = { "grub2" => _("GRUB2"), "grub2-efi" => _("GRUB2 for EFI"), + "grub2-bls" => _("GRUB2 with BLS"), # Translators: option in combo box when bootloader is not managed by yast2 "systemd-boot" => _("Systemd Boot"), "none" => _("Not Managed"), @@ -52,8 +53,6 @@ def localized_names(name) names[name] or raise "Unknown supported bootloader '#{name}'" end - # rubocop:disable Metrics/MethodLength - # It will be reduced again if systemd-boot is not anymore in beta phase. def handle old_bl = BootloaderFactory.current.name new_bl = value @@ -73,20 +72,6 @@ def handle return :redraw if !Yast::Popup.ContinueCancel(popup_msg) end - if new_bl == "systemd-boot" - # popup - Continue/Cancel - popup_msg = _( - "\n" \ - "Systemd-boot support is currently work in progress and\n" \ - "may not work as expected. Use at your own risk.\n" \ - "\n" \ - "Currently we do not provide official maintenance or support.\n" \ - "Proceed?\n" - ) - - return :redraw if !Yast::Popup.ContinueCancel(popup_msg) - end - if !Yast::Stage.initial && (old_bl == "systemd-boot") Yast::Popup.Warning(_( "Switching from systemd-boot to another bootloader\n" \ @@ -101,7 +86,6 @@ def handle :redraw end - # rubocop:enable Metrics/MethodLength def help _( "

Boot Loader\n" \ diff --git a/src/lib/bootloader/grub2_widgets.rb b/src/lib/bootloader/grub2_widgets.rb index 056126623..74802f268 100644 --- a/src/lib/bootloader/grub2_widgets.rb +++ b/src/lib/bootloader/grub2_widgets.rb @@ -78,7 +78,9 @@ def init end def store - if @hidden_menu_widget.checked? + if @hidden_menu_widget.is_a?(CWM::Empty) + grub_default.timeout = value.to_s + elsif @hidden_menu_widget.checked? grub_default.hidden_timeout = value.to_s grub_default.timeout = "0" else @@ -352,7 +354,8 @@ def store end def validate - return true if Yast::Mode.config || !value || grub2.name == "grub2-efi" + return true if Yast::Mode.config || !value || ["grub2-efi", + "grub2-bls"].include?(grub2.name) tpm_files = Dir.glob("/sys/**/pcrs") if !tpm_files.empty? && !File.read(tpm_files[0], 1).nil? @@ -970,6 +973,8 @@ def handle # represents Tab with kernel related configuration class KernelTab < CWM::Tab + include Grub2Helper + def label textdomain "bootloader" @@ -977,7 +982,11 @@ def label end def contents - console_widget = Yast::Arch.s390 ? CWM::Empty.new("console") : ConsoleWidget.new + console_widget = if Yast::Arch.s390 || grub2.name == "grub2-bls" + CWM::Empty.new("console") + else + ConsoleWidget.new + end VBox( VSpacing(1), MarginBox(1, 0.5, KernelAppendWidget.new), @@ -1055,34 +1064,37 @@ def loader_location_widget? end def generic_mbr_widget? - (Yast::Arch.x86_64 || Yast::Arch.i386) && grub2.name != "grub2-efi" + (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", "grub2-bls"].include?(grub2.name) end def secure_boot_widget? - Systeminfo.secure_boot_available?(grub2.name) + Systeminfo.secure_boot_available?(grub2.name) && grub2.name != "grub2-bls" end def trusted_boot_widget? - Systeminfo.trusted_boot_available?(grub2.name) + Systeminfo.trusted_boot_available?(grub2.name) && grub2.name != "grub2-bls" end def update_nvram_widget? - Systeminfo.nvram_available?(grub2.name) + Systeminfo.nvram_available?(grub2.name) && grub2.name != "grub2-bls" end def pmbr_widget? (Yast::Arch.x86_64 || Yast::Arch.i386) && - Yast::BootStorage.gpt_boot_disk? + Yast::BootStorage.gpt_boot_disk? && + grub2.name != "grub2-bls" end def device_map_button? - (Yast::Arch.x86_64 || Yast::Arch.i386) && grub2.name != "grub2-efi" + (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", "grub2-bls"].include?(grub2.name) end end # Represents bootloader specific options like its timeout, # default section or password protection class BootloaderTab < CWM::Tab + include Grub2Helper + def label textdomain "bootloader" @@ -1090,31 +1102,44 @@ def label end def contents - hiden_menu_widget = HiddenMenuWidget.new + hidden_menu_widget = if grub2.name == "grub2-bls" + CWM::Empty.new("hidden_menu") + else + HiddenMenuWidget.new + end VBox( VSpacing(2), HBox( HSpacing(1), - TimeoutWidget.new(hiden_menu_widget), + TimeoutWidget.new(hidden_menu_widget), HSpacing(1), VBox( os_prober_widget, VSpacing(1), - Left(hiden_menu_widget) + Left(hidden_menu_widget) ), HSpacing(1) ), VSpacing(1), - MarginBox(1, 1, DefaultSectionWidget.new), - MarginBox(1, 1, GrubPasswordWidget.new), + MarginBox(1, 1, MinWidth(1, DefaultSectionWidget.new)), + MarginBox(1, 1, grub_password_widget), VStretch() ) end private + def grub_password_widget + if grub2.name == "grub2-bls" + CWM::Empty.new("password_widget") + else + GrubPasswordWidget.new + end + end + def os_prober_widget - if OsProber.available? # Checks !Arch.s390 and if package is available + if OsProber.available? && # Checks !Arch.s390 and if package is available + grub2.name != "grub2-bls" Left(OSProberWidget.new) else CWM::Empty.new("os_prober") diff --git a/src/lib/bootloader/grub2base.rb b/src/lib/bootloader/grub2base.rb index 788b95120..83caa8419 100644 --- a/src/lib/bootloader/grub2base.rb +++ b/src/lib/bootloader/grub2base.rb @@ -435,14 +435,6 @@ def update_nvram_summary "#{_("Update NVRAM:")} #{status_string(update_nvram)} #{link}" end - - def status_string(status) - if status - _("enabled") - else - _("disabled") - end - end end # rubocop:enable Metrics/ClassLength end diff --git a/src/lib/bootloader/grub2bls.rb b/src/lib/bootloader/grub2bls.rb new file mode 100644 index 000000000..37dcb4bae --- /dev/null +++ b/src/lib/bootloader/grub2bls.rb @@ -0,0 +1,193 @@ +# frozen_string_literal: true + +require "yast" +require "bootloader/bootloader_base" +require "bootloader/bls_sections" + +Yast.import "Arch" +Yast.import "Report" +Yast.import "Stage" +Yast.import "Misc" + +module Bootloader + # Represents grub2 bls bootloader with efi target + class Grub2Bls < Grub2Base + include Yast::Logger + include Yast::I18n + + attr_reader :sections + + CMDLINE = "/etc/kernel/cmdline" + + def initialize + super + textdomain "bootloader" + + @sections = ::Bootloader::BlsSections.new + @is_read = false + @is_proposed = false + end + + # Display bootloader summary + # @return a list of summary lines + def summary(*) + [ + Yast::Builtins.sformat( + _("Boot Loader Type: %1"), + "GRUB2 BLS" + ) + ] + end + + # @return bootloader name + def name + "grub2-bls" + end + + # reads configuration from target disk + def read + read_menu_timeout + @sections.read + lines = "" + filename = File.join(Yast::Installation.destdir, CMDLINE) + if File.exist?(filename) + File.open(filename).each do |line| + lines = + line + end + end + grub_default.kernel_params.replace(lines) + log.info "kernel params: #{grub_default.kernel_params}" + log.info "bls sections: #{@sections.all}" + log.info "bls default: #{@sections.default}" + @is_read = true # flag that settings has been read + end + + # @return true if configuration is already read + def read? + @is_read + end + + # Proposes new configuration + def propose + log.info("Propose settings...") + if grub_default.kernel_params.empty? + kernel_line = Yast::BootArch.DefaultKernelParams(Yast::BootStorage.propose_resume) + grub_default.kernel_params.replace(kernel_line) + end + grub_default.timeout = Yast::ProductFeatures.GetIntegerFeature("globals", "boot_timeout").to_i + @is_proposed = true + end + + # @return true if configuration is already proposed + def proposed? + @is_proposed + end + + # writes configuration to target disk + def write(*) + install_bootloader if Yast::Stage.initial # while new installation only (currently) + create_menu_entries + install_bootloader + @sections.write + write_menu_timeout + # writing kernel parameter to /etc/kernel/cmdline + File.open(File.join(Yast::Installation.destdir, CMDLINE), "w+") do |fw| + fw.puts(grub_default.kernel_params.serialize) + end + end + + # merges other bootloader configuration into this one. + # It have to be same bootloader type. + # rubocop:disable Metrics/AbcSize + def merge(other) + raise "Invalid merge argument #{other.name} for #{name}" if name != other.name + + log.info "merging: timeout: #{grub_default.timeout}=>#{other.grub_default.timeout}" + log.info " mitigations: #{cpu_mitigations.to_human_string}=>" \ + "#{other.cpu_mitigations.to_human_string}" + log.info " grub_default.kernel_params: #{grub_default.kernel_params.serialize}=>" \ + "#{other.grub_default.kernel_params.serialize}" + + merge_sections(other) + merge_grub_default(other) + + log.info "merging result: timeout: #{grub_default.timeout}" + log.info " mitigations: #{cpu_mitigations.to_human_string}" + log.info " kernel_params: #{grub_default.kernel_params.serialize}" + end + # rubocop:enable Metrics/AbcSize + + # @return [Array] packages required to configure given bootloader + def packages + res = super + res << ("grub2-" + Yast::Arch.architecture + "-efi-bls") + res << "sdbootutil" + res << "grub2" + res + end + + private + + SDBOOTUTIL = "/usr/bin/sdbootutil" + OS_RELEASE_PATH = "/etc/os-release" + + def grubenv_path + str = Yast::Misc.CustomSysconfigRead("ID_LIKE", "openSUSE", + OS_RELEASE_PATH) + os = str.split.first + File.join("/boot/efi/EFI/", os, "/grubenv") + end + + # @return [String] return default boot as string or "" if not set + # or something goes wrong + def read_menu_timeout + grub_default.timeout = Yast::Misc.CustomSysconfigRead("timeout", "", + grubenv_path) + log.info "Boot timeout: #{grub_default.timeout}" + end + + def write_menu_timeout + ret = Yast::Execute.on_target(SDBOOTUTIL, + "set-timeout", + grub_default.timeout, + allowed_exitstatus: [0, 1]) + + return unless ret != 0 + + # fallback directly over grub2-editenv + Yast::Execute.on_target("/usr/bin/grub2-editenv", grubenv_path, + "set", "timeout=#{grub_default.timeout}") + end + + def merge_sections(other) + return if !other.sections.default || other.sections.default.empty? + + @sections.default = other.sections.default + end + + def create_menu_entries + Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", "add-all-kernels") + rescue Cheetah::ExecutionFailed => e + Yast::Report.Error( + format(_( + "Cannot create grub2-bls menu entry:\n" \ + "Command `%{command}`.\n" \ + "Error output: %{stderr}" + ), command: e.commands.inspect, stderr: e.stderr) + ) + end + + def install_bootloader + Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", + "install") + rescue Cheetah::ExecutionFailed => e + Yast::Report.Error( + format(_( + "Cannot install grub2-bls bootloader:\n" \ + "Command `%{command}`.\n" \ + "Error output: %{stderr}" + ), command: e.commands.inspect, stderr: e.stderr) + ) + end + end +end diff --git a/src/lib/bootloader/sysconfig.rb b/src/lib/bootloader/sysconfig.rb index 772bc7341..9b4da9d77 100644 --- a/src/lib/bootloader/sysconfig.rb +++ b/src/lib/bootloader/sysconfig.rb @@ -61,7 +61,7 @@ def pre_write bootloader: "\n" \ "## Path:\tSystem/Bootloader\n" \ "## Description:\tBootloader configuration\n" \ - "## Type:\tlist(grub,grub2,grub2-efi,systemd-boot,none)\n" \ + "## Type:\tlist(grub,grub2,grub2-efi,grub2-bls,systemd-boot,none)\n" \ "## Default:\tgrub2\n" \ "#\n" \ "# Type of bootloader in use.\n" \ diff --git a/src/lib/bootloader/systemdboot.rb b/src/lib/bootloader/systemdboot.rb index b525afa5c..9235c671e 100644 --- a/src/lib/bootloader/systemdboot.rb +++ b/src/lib/bootloader/systemdboot.rb @@ -130,14 +130,6 @@ def propose self.secure_boot = Systeminfo.secure_boot_supported? end - def status_string(status) - if status - _("enabled") - else - _("disabled") - end - end - # Secure boot setting shown in summary screen. # sdbootutil intialize secure boot if shim has been installed. # @@ -199,7 +191,7 @@ def write_sysconfig(prewrite: false) SDBOOTUTIL = "/usr/bin/sdbootutil" - def create_menu_entries + def write_kernel_parameter # writing kernel parameter to /etc/kernel/cmdline File.open(File.join(Yast::Installation.destdir, CMDLINE), "w+") do |fw| if Yast::Stage.initial # while new installation only @@ -208,6 +200,10 @@ def create_menu_entries fw.puts(kernel_params.serialize) end end + end + + def create_menu_entries + write_kernel_parameter begin Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", "add-all-kernels") diff --git a/src/lib/bootloader/systeminfo.rb b/src/lib/bootloader/systeminfo.rb index 28fc8ecd7..9dc7ead26 100644 --- a/src/lib/bootloader/systeminfo.rb +++ b/src/lib/bootloader/systeminfo.rb @@ -89,7 +89,7 @@ def trusted_boot_available?(bootloader_name) # param bootloader_name [String] bootloader name # @return [Boolean] true if UEFI will be used for booting with this bootloader def efi_used?(bootloader_name) - ["grub2-efi", "systemd-boot"].include?(bootloader_name) + ["grub2-efi", "systemd-boot", "grub2-bls"].include?(bootloader_name) end # Check if UEFI is available on this system. diff --git a/src/modules/BootSupportCheck.rb b/src/modules/BootSupportCheck.rb index 748c97d18..5faa8c9bd 100644 --- a/src/modules/BootSupportCheck.rb +++ b/src/modules/BootSupportCheck.rb @@ -83,11 +83,11 @@ def correct_loader_type(type) # grub2 is sooo cool... return true if type == "grub2" && !::Bootloader::Systeminfo.efi_mandatory? - if (Arch.i386 || Arch.x86_64) && ["grub2-efi", "systemd-boot"].include?(type) && efi? + if (Arch.i386 || Arch.x86_64) && ["grub2-efi", "grub2-bls", "systemd-boot"].include?(type) && efi? return true end - if ["grub2-efi", "systemd-boot"].include?(type) && ::Bootloader::Systeminfo.efi_mandatory? + if ["grub2-efi", "grub2-bls", "systemd-boot"].include?(type) && ::Bootloader::Systeminfo.efi_mandatory? return true end diff --git a/test/bls_sections_test.rb b/test/bls_sections_test.rb new file mode 100755 index 000000000..08a7530b6 --- /dev/null +++ b/test/bls_sections_test.rb @@ -0,0 +1,76 @@ +#! /usr/bin/env rspec --format doc +# frozen_string_literal: true + +require_relative "./test_helper" + +require "bootloader/bls_sections" +require "cfa/memory_file" + +describe Bootloader::BlsSections do + + before do + allow(Yast::Misc).to receive(:CustomSysconfigRead) + .with("ID_LIKE", "openSUSE", "/etc/os-release") + .and_return("openSUSE") + end + + describe "#read" do + before do + allow(Yast::Execute).to receive(:on_target) + .with("/usr/bin/bootctl", "--json=short", "list", stdout: :capture) + .and_return("[{\"title\" : \"openSUSE Tumbleweed\", \"isDefault\" : true }," \ + "{\"title\" : \"Snapper: *openSUSE Tumbleweed 20241107\", \"isDefault\" : false}]") + allow(Yast::Misc).to receive(:CustomSysconfigRead) + .with("default", "", "/boot/efi/EFI/openSUSE/grubenv") + .and_return("openSUSE Tumbleweed") + subject.read + end + + it "returns list of all available sections" do + expect(subject.all).to eq(["openSUSE Tumbleweed", "Snapper: *openSUSE Tumbleweed 20241107"]) + end + + it "reads default menu entry" do + expect(subject.default).to eq("openSUSE Tumbleweed") + end + end + + describe "#default=" do + before do + allow(Yast::Execute).to receive(:on_target) + .with("/usr/bin/bootctl", "--json=short", "list", stdout: :capture) + .and_return("[{\"title\" : \"openSUSE Tumbleweed\", \"isDefault\" : true }," \ + "{\"title\" : \"Snapper: *openSUSE Tumbleweed 20241107\", \"isDefault\" : false}]") + allow(Yast::Misc).to receive(:CustomSysconfigRead) + .with("default", "", "/boot/efi/EFI/openSUSE/grubenv") + .and_return("openSUSE Tumbleweed") + subject.read + end + it "sets new value for default" do + subject.default = "Snapper: *openSUSE Tumbleweed 20241107" + expect(subject.default).to eq "Snapper: *openSUSE Tumbleweed 20241107" + end + + it "sets default to empty if section do not exists" do + subject.default = "non-exist" + expect(subject.default).to eq "" + end + end + + describe "#write" do + it "writes default value if set" do + subject.default = "Snapper: *openSUSE Tumbleweed 20241107" + expect(Yast::Execute).to receive(:on_target) + .with("/usr/bin/sdbootutil", "set-default", subject.default, { :allowed_exitstatus=>[0, 1] }) + subject.write + end + + it "does not write default value if not set" do + subject.default = "" + expect(Yast::Execute).to_not receive(:on_target) + .with("/usr/bin/sdbootutil", "set-default", subject.default, { :allowed_exitstatus=>[0, 1] }) + subject.write + end + + end +end diff --git a/test/boot_support_test.rb b/test/boot_support_test.rb index 3949bdcc2..6b58f1f40 100644 --- a/test/boot_support_test.rb +++ b/test/boot_support_test.rb @@ -42,6 +42,13 @@ expect(subject.SystemSupported).to eq false end + it "returns false if grub2-bls is used and UEFI is not supported" do + Bootloader::BootloaderFactory.current_name = "grub2-bls" + allow(subject).to receive(:efi?).and_return(false) + + expect(subject.SystemSupported).to eq false + end + it "returns false if systemd-boot is used and UEFI is not supported" do Bootloader::BootloaderFactory.current_name = "systemd-boot" allow(subject).to receive(:efi?).and_return(false) diff --git a/test/bootloader_factory_test.rb b/test/bootloader_factory_test.rb index 7eede3bca..e36883e70 100644 --- a/test/bootloader_factory_test.rb +++ b/test/bootloader_factory_test.rb @@ -38,7 +38,7 @@ allow(Yast::ProductFeatures).to receive(:GetBooleanFeature).with("globals", "enable_systemd_boot").and_return(true) end it "returns systemd-boot in the list" do - expect(Bootloader::BootloaderFactory.supported_names).to eq ["grub2", "grub2-efi", "systemd-boot", "none"] + expect(Bootloader::BootloaderFactory.supported_names).to eq ["grub2", "grub2-efi", "grub2-bls", "systemd-boot", "none"] end end context "product does not support systemd-boot" do @@ -46,7 +46,7 @@ allow(Yast::ProductFeatures).to receive(:GetBooleanFeature).with("globals", "enable_systemd_boot").and_return(false) end it "does not include systemd-boot in the list" do - expect(Bootloader::BootloaderFactory.supported_names).to eq ["grub2", "grub2-efi", "none"] + expect(Bootloader::BootloaderFactory.supported_names).to eq ["grub2", "grub2-efi", "grub2-bls", "none"] end end end @@ -61,7 +61,7 @@ allow(Yast::ProductFeatures).to receive(:GetBooleanFeature).with("globals", "enable_systemd_boot").and_return(true) end it "does not include grub2 in the list" do - expect(Bootloader::BootloaderFactory.supported_names).to eq ["grub2-efi", "systemd-boot", "none"] + expect(Bootloader::BootloaderFactory.supported_names).to eq ["grub2-efi", "grub2-bls", "systemd-boot", "none"] end end context "product does not support systemd-boot" do @@ -69,7 +69,7 @@ allow(Yast::ProductFeatures).to receive(:GetBooleanFeature).with("globals", "enable_systemd_boot").and_return(false) end it "does not include systemd-boot and grub2 in the list" do - expect(Bootloader::BootloaderFactory.supported_names).to eq ["grub2-efi", "none"] + expect(Bootloader::BootloaderFactory.supported_names).to eq ["grub2-efi", "grub2-bls", "none"] end end end diff --git a/test/grub2_bls_test.rb b/test/grub2_bls_test.rb new file mode 100644 index 000000000..a8bf6f4fe --- /dev/null +++ b/test/grub2_bls_test.rb @@ -0,0 +1,166 @@ +# frozen_string_literal: true + +require_relative "test_helper" + +require "bootloader/grub2bls" + +describe Bootloader::Grub2Bls do + subject do + sub = described_class.new + sub + end + + let(:destdir) { File.expand_path("data/", __dir__) } + let(:cmdline_content) { "splash=silent quiet security=apparmor mitigations=off" } + + before do + allow(Yast::Arch).to receive(:architecture).and_return("x86_64") + end + + describe "#read" do + before do + allow(Yast::Misc).to receive(:CustomSysconfigRead) + .with("ID_LIKE", "openSUSE", "/etc/os-release") + .and_return("openSUSE") + allow(Yast::Misc).to receive(:CustomSysconfigRead) + .with("timeout", "", "/boot/efi/EFI/openSUSE/grubenv") + .and_return("10") + allow(Yast::Misc).to receive(:CustomSysconfigRead) + .with("default", "", "/boot/efi/EFI/openSUSE/grubenv") + .and_return("") + allow(Yast::Installation).to receive(:destdir).and_return(destdir) + end + + it "reads menu timeout" do + subject.read + + expect(subject.grub_default.timeout).to eq "10" + end + + it "reads entries from /etc/kernel/cmdline" do + subject.read + + expect(subject.cpu_mitigations.to_human_string).to eq "Off" + expect(subject.grub_default.kernel_params.serialize).to eq cmdline_content + end + end + + describe "#write" do + before do + allow(Yast::Stage).to receive(:initial).and_return(false) + allow(Yast::Installation).to receive(:destdir).and_return(destdir) + subject.grub_default.kernel_params.replace(cmdline_content) + subject.grub_default.timeout = 10 + end + + it "installs the bootloader" do + allow(Yast::Execute).to receive(:on_target) + .with("/usr/bin/sdbootutil", "set-timeout", + subject.grub_default.timeout, + allowed_exitstatus: [0, 1]) + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "set-default", subject.sections.default) + + # install bootloader + expect(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "--verbose", "install") + + # create menu entries + expect(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") + + subject.write + end + + it "writes kernel cmdline" do + allow(Yast::Execute).to receive(:on_target) + .with("/usr/bin/sdbootutil", "set-timeout", + subject.grub_default.timeout, + allowed_exitstatus: [0, 1]) + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "set-default", subject.sections.default) + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "--verbose", "install") + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") + + subject.write + # Checking written kernel parameters + subject.read + expect(subject.cpu_mitigations.to_human_string).to eq "Off" + expect(subject.grub_default.kernel_params.serialize).to include cmdline_content + end + + it "saves menu timeout" do + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "set-default", subject.sections.default) + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "--verbose", "install") + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") + + # Saving menu timeout + expect(Yast::Execute).to receive(:on_target) + .with("/usr/bin/sdbootutil", "set-timeout", + subject.grub_default.timeout, + allowed_exitstatus: [0, 1]) + subject.write + end + end + + describe "#packages" do + it "adds grub2* and sdbootutil packages" do + allow(Yast::Arch).to receive(:architecture).and_return("x86_64") + allow(Yast::Package).to receive(:Available).with("os-prober").and_return(true) + expect(subject.packages).to include("grub2-" + Yast::Arch.architecture + "-efi-bls") + expect(subject.packages).to include("sdbootutil") + expect(subject.packages).to include("grub2") + end + end + + describe "#summary" do + it "returns line with boot loader type specified" do + expect(subject.summary).to include("Boot Loader Type: GRUB2 BLS") + end + + end + + describe "#merge" do + it "overwrite mitigations and menu timeout if specified in merged one" do + other_cmdline = "splash=silent quiet mitigations=auto" + other = described_class.new + other.grub_default.timeout = 12 + other.grub_default.kernel_params.replace(other_cmdline) + + subject.grub_default.timeout = 10 + subject.grub_default.kernel_params.replace(cmdline_content) + + subject.merge(other) + + expect(subject.grub_default.timeout).to eq 12 + expect(subject.cpu_mitigations.to_human_string).to eq "Auto" + expect(subject.grub_default.kernel_params.serialize).to include "security=apparmor splash=silent quiet mitigations=auto" + end + end + + describe "#propose" do + before do + allow(Yast::BootStorage).to receive(:available_swap_partitions).and_return({}) + end + + it "proposes timeout to product/role default" do + allow(Yast::ProductFeatures).to receive(:GetIntegerFeature) + .with("globals", "boot_timeout").and_return(2) + subject.propose + + expect(subject.grub_default.timeout).to eq 2 + end + + it "proposes kernel cmdline" do + expect(Yast::BootArch).to receive(:DefaultKernelParams).and_return(cmdline_content) + + subject.propose + expect(subject.grub_default.kernel_params.serialize).to eq cmdline_content + end + end +end From ee16502c69204061126155b3d98bdabacb71a797 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 14 Nov 2024 14:36:41 +0100 Subject: [PATCH 02/46] no fallbacks --- src/lib/bootloader/bls_sections.rb | 12 +++--------- src/lib/bootloader/grub2bls.rb | 14 +++----------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/lib/bootloader/bls_sections.rb b/src/lib/bootloader/bls_sections.rb index b934bb8a9..1bcd41c8d 100644 --- a/src/lib/bootloader/bls_sections.rb +++ b/src/lib/bootloader/bls_sections.rb @@ -71,15 +71,9 @@ def read_default # write default entry def write_default - ret = Yast::Execute.on_target("/usr/bin/sdbootutil", - "set-default", @default, - allowed_exitstatus: [0, 1]) - - return unless ret != 0 - - # fallback directly over grub2-editenv - Yast::Execute.on_target("/usr/bin/grub2-editenv", grubenv_path, - "set", "default=" + @default) + # Execute.on_target can return nil if call failed. It shows users error popup, but bootloader + # can continue with not selected default section. + Yast::Execute.on_target("/usr/bin/sdbootutil", "set-default", @default) end # @return [Array] return array of entries or [] diff --git a/src/lib/bootloader/grub2bls.rb b/src/lib/bootloader/grub2bls.rb index 37dcb4bae..9c061280e 100644 --- a/src/lib/bootloader/grub2bls.rb +++ b/src/lib/bootloader/grub2bls.rb @@ -122,7 +122,6 @@ def packages res = super res << ("grub2-" + Yast::Arch.architecture + "-efi-bls") res << "sdbootutil" - res << "grub2" res end @@ -147,16 +146,9 @@ def read_menu_timeout end def write_menu_timeout - ret = Yast::Execute.on_target(SDBOOTUTIL, - "set-timeout", - grub_default.timeout, - allowed_exitstatus: [0, 1]) - - return unless ret != 0 - - # fallback directly over grub2-editenv - Yast::Execute.on_target("/usr/bin/grub2-editenv", grubenv_path, - "set", "timeout=#{grub_default.timeout}") + # Execute.on_target can return nil if call failed. It shows users error popup, but bootloader + # can continue with not selected default section. + Yast::Execute.on_target(SDBOOTUTIL, "set-timeout", grub_default.timeout) end def merge_sections(other) From c6392827b85524d3fcf2f93c2bc4ca8ba106d7f0 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 14 Nov 2024 15:03:13 +0100 Subject: [PATCH 03/46] fixed testcases --- test/bls_sections_test.rb | 4 ++-- test/grub2_bls_test.rb | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/test/bls_sections_test.rb b/test/bls_sections_test.rb index 08a7530b6..e5791dea3 100755 --- a/test/bls_sections_test.rb +++ b/test/bls_sections_test.rb @@ -61,14 +61,14 @@ it "writes default value if set" do subject.default = "Snapper: *openSUSE Tumbleweed 20241107" expect(Yast::Execute).to receive(:on_target) - .with("/usr/bin/sdbootutil", "set-default", subject.default, { :allowed_exitstatus=>[0, 1] }) + .with("/usr/bin/sdbootutil", "set-default", subject.default) subject.write end it "does not write default value if not set" do subject.default = "" expect(Yast::Execute).to_not receive(:on_target) - .with("/usr/bin/sdbootutil", "set-default", subject.default, { :allowed_exitstatus=>[0, 1] }) + .with("/usr/bin/sdbootutil", "set-default", subject.default) subject.write end diff --git a/test/grub2_bls_test.rb b/test/grub2_bls_test.rb index a8bf6f4fe..fcd9d511e 100644 --- a/test/grub2_bls_test.rb +++ b/test/grub2_bls_test.rb @@ -56,8 +56,7 @@ it "installs the bootloader" do allow(Yast::Execute).to receive(:on_target) .with("/usr/bin/sdbootutil", "set-timeout", - subject.grub_default.timeout, - allowed_exitstatus: [0, 1]) + subject.grub_default.timeout) allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "set-default", subject.sections.default) @@ -75,8 +74,7 @@ it "writes kernel cmdline" do allow(Yast::Execute).to receive(:on_target) .with("/usr/bin/sdbootutil", "set-timeout", - subject.grub_default.timeout, - allowed_exitstatus: [0, 1]) + subject.grub_default.timeout) allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "set-default", subject.sections.default) allow(Yast::Execute).to receive(:on_target!) @@ -102,8 +100,7 @@ # Saving menu timeout expect(Yast::Execute).to receive(:on_target) .with("/usr/bin/sdbootutil", "set-timeout", - subject.grub_default.timeout, - allowed_exitstatus: [0, 1]) + subject.grub_default.timeout) subject.write end end From 3111016c21db043632a361c9d4fec558542f7375 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 14 Nov 2024 15:07:36 +0100 Subject: [PATCH 04/46] fixed testcases --- test/grub2_bls_test.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/grub2_bls_test.rb b/test/grub2_bls_test.rb index fcd9d511e..6620901fd 100644 --- a/test/grub2_bls_test.rb +++ b/test/grub2_bls_test.rb @@ -111,7 +111,6 @@ allow(Yast::Package).to receive(:Available).with("os-prober").and_return(true) expect(subject.packages).to include("grub2-" + Yast::Arch.architecture + "-efi-bls") expect(subject.packages).to include("sdbootutil") - expect(subject.packages).to include("grub2") end end From 953b4c87a3d787a3b963e5c2a8eb71fe42997b2f Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 14 Nov 2024 17:51:43 +0100 Subject: [PATCH 05/46] evaluate grub2-bls architectures --- src/lib/bootloader/grub2bls.rb | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/lib/bootloader/grub2bls.rb b/src/lib/bootloader/grub2bls.rb index 9c061280e..531a547a6 100644 --- a/src/lib/bootloader/grub2bls.rb +++ b/src/lib/bootloader/grub2bls.rb @@ -120,7 +120,7 @@ def merge(other) # @return [Array] packages required to configure given bootloader def packages res = super - res << ("grub2-" + Yast::Arch.architecture + "-efi-bls") + res << ("grub2-" + grub2bls_architecture + "-efi-bls") res << "sdbootutil" res end @@ -130,6 +130,33 @@ def packages SDBOOTUTIL = "/usr/bin/sdbootutil" OS_RELEASE_PATH = "/etc/os-release" + def grub2bls_architecture + arch = Yast::Arch.architecture + table = { "x86_64" => "x86_64", + "amd64" => "x86_64", + "sparc" => "sparc64", + "mipsel" => "mipsel", + "mips64el" => "mipsel", + "mips" => "mips", + "mips64" => "mips", + "loongarch64" => "loongarch64" } + ret = table[arch] + if ret.empty? + ret = if arch.start_with?("arm") + "arm" + elsif arch.start_with?("aarch64") + "arm64" + elsif arch.start_with?("riscv32") + "riscv32" + elsif arch.start_with?("riscv64") + "riscv64" + else + arch # fallback, but useful ? + end + end + ret + end + def grubenv_path str = Yast::Misc.CustomSysconfigRead("ID_LIKE", "openSUSE", OS_RELEASE_PATH) From 96d5b8b6d6532f776b195ba738570124823441c8 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 14 Nov 2024 18:00:58 +0100 Subject: [PATCH 06/46] evaluate grub2-bls architectures --- test/grub2_bls_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/grub2_bls_test.rb b/test/grub2_bls_test.rb index 6620901fd..4059184fa 100644 --- a/test/grub2_bls_test.rb +++ b/test/grub2_bls_test.rb @@ -106,7 +106,7 @@ end describe "#packages" do - it "adds grub2* and sdbootutil packages" do + it "adds grub2--efi-bls and sdbootutil packages" do allow(Yast::Arch).to receive(:architecture).and_return("x86_64") allow(Yast::Package).to receive(:Available).with("os-prober").and_return(true) expect(subject.packages).to include("grub2-" + Yast::Arch.architecture + "-efi-bls") From 6fb8f2db40dd52794bf07ff658649795b7a40123 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 15 Nov 2024 12:48:05 +0100 Subject: [PATCH 07/46] cleanup --- src/lib/bootloader/bls.rb | 57 ++++++++++++++++++++++++++++++ src/lib/bootloader/bls_sections.rb | 10 ++---- src/lib/bootloader/grub2bls.rb | 41 ++++----------------- src/lib/bootloader/systemdboot.rb | 50 +++----------------------- 4 files changed, 70 insertions(+), 88 deletions(-) create mode 100644 src/lib/bootloader/bls.rb diff --git a/src/lib/bootloader/bls.rb b/src/lib/bootloader/bls.rb new file mode 100644 index 000000000..535c5eac9 --- /dev/null +++ b/src/lib/bootloader/bls.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require "fileutils" +require "yast" +require "bootloader/sysconfig" +require "bootloader/cpu_mitigations" +require "cfa/grub2/default" + +Yast.import "Report" + +module Bootloader + # Represents bls compatile system calls which can be used + # e.g. by grub2-bls and systemd-boot + class Bls + include Yast::Logger + include Yast::I18n + + SDBOOTUTIL = "/usr/bin/sdbootutil" + + def self.create_menu_entries + Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", "add-all-kernels") + rescue Cheetah::ExecutionFailed => e + Yast::Report.Error( + format(_( + "Cannot create boot menu entry:\n" \ + "Command `%{command}`.\n" \ + "Error output: %{stderr}" + ), command: e.commands.inspect, stderr: e.stderr) + ) + end + + def self.install_bootloader + Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", + "install") + rescue Cheetah::ExecutionFailed => e + Yast::Report.Error( + format(_( + "Cannot install bootloader:\n" \ + "Command `%{command}`.\n" \ + "Error output: %{stderr}" + ), command: e.commands.inspect, stderr: e.stderr) + ) + end + + def self.write_menu_timeout(timeout) + Yast::Execute.on_target(SDBOOTUTIL, "set-timeout", timeout) + rescue Cheetah::ExecutionFailed => e + Yast::Report.Error( + format(_( + "Cannot write boot menu timeout:\n" \ + "Command `%{command}`.\n" \ + "Error output: %{stderr}" + ), command: e.commands.inspect, stderr: e.stderr) + ) + end + end +end diff --git a/src/lib/bootloader/bls_sections.rb b/src/lib/bootloader/bls_sections.rb index 1bcd41c8d..b0077258d 100644 --- a/src/lib/bootloader/bls_sections.rb +++ b/src/lib/bootloader/bls_sections.rb @@ -3,6 +3,7 @@ require "json" require "yast" require "yast2/execute" +require "bootloader/bls" Yast.import "Misc" @@ -42,7 +43,7 @@ def default=(value) def write return if @default.empty? - write_default + Bls.write_default_menu(@default) end def read @@ -69,13 +70,6 @@ def read_default grubenv_path) end - # write default entry - def write_default - # Execute.on_target can return nil if call failed. It shows users error popup, but bootloader - # can continue with not selected default section. - Yast::Execute.on_target("/usr/bin/sdbootutil", "set-default", @default) - end - # @return [Array] return array of entries or [] def read_entries output = Yast::Execute.on_target( diff --git a/src/lib/bootloader/grub2bls.rb b/src/lib/bootloader/grub2bls.rb index 531a547a6..e26a4d2d7 100644 --- a/src/lib/bootloader/grub2bls.rb +++ b/src/lib/bootloader/grub2bls.rb @@ -2,6 +2,7 @@ require "yast" require "bootloader/bootloader_base" +require "bootloader/bls" require "bootloader/bls_sections" Yast.import "Arch" @@ -85,11 +86,12 @@ def proposed? # writes configuration to target disk def write(*) - install_bootloader if Yast::Stage.initial # while new installation only (currently) - create_menu_entries - install_bootloader + Bls.install_bootloader if Yast::Stage.initial # while new installation only (currently) + Bls.create_menu_entries + Bls.install_bootloader @sections.write - write_menu_timeout + Bls.write_menu_timeout(grub_default.timeout) + # writing kernel parameter to /etc/kernel/cmdline File.open(File.join(Yast::Installation.destdir, CMDLINE), "w+") do |fw| fw.puts(grub_default.kernel_params.serialize) @@ -172,41 +174,10 @@ def read_menu_timeout log.info "Boot timeout: #{grub_default.timeout}" end - def write_menu_timeout - # Execute.on_target can return nil if call failed. It shows users error popup, but bootloader - # can continue with not selected default section. - Yast::Execute.on_target(SDBOOTUTIL, "set-timeout", grub_default.timeout) - end - def merge_sections(other) return if !other.sections.default || other.sections.default.empty? @sections.default = other.sections.default end - - def create_menu_entries - Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", "add-all-kernels") - rescue Cheetah::ExecutionFailed => e - Yast::Report.Error( - format(_( - "Cannot create grub2-bls menu entry:\n" \ - "Command `%{command}`.\n" \ - "Error output: %{stderr}" - ), command: e.commands.inspect, stderr: e.stderr) - ) - end - - def install_bootloader - Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", - "install") - rescue Cheetah::ExecutionFailed => e - Yast::Report.Error( - format(_( - "Cannot install grub2-bls bootloader:\n" \ - "Command `%{command}`.\n" \ - "Error output: %{stderr}" - ), command: e.commands.inspect, stderr: e.stderr) - ) - end end end diff --git a/src/lib/bootloader/systemdboot.rb b/src/lib/bootloader/systemdboot.rb index 9235c671e..b2e8db7e8 100644 --- a/src/lib/bootloader/systemdboot.rb +++ b/src/lib/bootloader/systemdboot.rb @@ -4,6 +4,7 @@ require "yast" require "bootloader/sysconfig" require "bootloader/cpu_mitigations" +require "bootloader/bls" require "cfa/systemd_boot" require "cfa/grub2/default" @@ -112,9 +113,10 @@ def read def write(etc_only: false) super log.info("Writing settings...") - install_bootloader if Yast::Stage.initial # while new installation only (currently) - create_menu_entries - write_menu_timeout + Bls.install_bootloader if Yast::Stage.initial # while new installation only (currently) + write_kernel_parameter + Bls.create_menu_entries + Bls.write_menu_timeout true end @@ -189,8 +191,6 @@ def write_sysconfig(prewrite: false) private - SDBOOTUTIL = "/usr/bin/sdbootutil" - def write_kernel_parameter # writing kernel parameter to /etc/kernel/cmdline File.open(File.join(Yast::Installation.destdir, CMDLINE), "w+") do |fw| @@ -202,22 +202,6 @@ def write_kernel_parameter end end - def create_menu_entries - write_kernel_parameter - - begin - Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", "add-all-kernels") - rescue Cheetah::ExecutionFailed => e - Yast::Report.Error( - format(_( - "Cannot create systemd-boot menu entry:\n" \ - "Command `%{command}`.\n" \ - "Error output: %{stderr}" - ), command: e.commands.inspect, stderr: e.stderr) - ) - end - end - def read_menu_timeout config = CFA::SystemdBoot.load return unless config.menu_timeout @@ -228,29 +212,5 @@ def read_menu_timeout config.menu_timeout.to_i end end - - def write_menu_timeout - config = CFA::SystemdBoot.load - config.menu_timeout = if menu_timeout == -1 - "menu-force" - else - menu_timeout.to_s - end - config.save - end - - def install_bootloader - Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", - "install") - rescue Cheetah::ExecutionFailed => e - Yast::Report.Error( - format(_( - "Cannot install systemd bootloader:\n" \ - "Command `%{command}`.\n" \ - "Error output: %{stderr}" - ), command: e.commands.inspect, stderr: e.stderr) - ) - nil - end end end From 6f3a891061ec7d7cbd60577e919dcb1ff7fb7400 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 15 Nov 2024 12:53:41 +0100 Subject: [PATCH 08/46] cleanup --- src/lib/bootloader/bls.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/bootloader/bls.rb b/src/lib/bootloader/bls.rb index 535c5eac9..95bd93744 100644 --- a/src/lib/bootloader/bls.rb +++ b/src/lib/bootloader/bls.rb @@ -53,5 +53,17 @@ def self.write_menu_timeout(timeout) ), command: e.commands.inspect, stderr: e.stderr) ) end + + def self.write_default_menu(default) + Yast::Execute.on_target(SDBOOTUTIL, "set-default", default) + rescue Cheetah::ExecutionFailed => e + Yast::Report.Error( + format(_( + "Cannot write default boot menu entry:\n" \ + "Command `%{command}`.\n" \ + "Error output: %{stderr}" + ), command: e.commands.inspect, stderr: e.stderr) + ) + end end end From 008cd0312a64f230ee971ff0bfb62a6497a6d0d4 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Mon, 18 Nov 2024 15:02:13 +0100 Subject: [PATCH 09/46] fixed timeout --- src/lib/bootloader/systemdboot.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/bootloader/systemdboot.rb b/src/lib/bootloader/systemdboot.rb index b2e8db7e8..6d426dd84 100644 --- a/src/lib/bootloader/systemdboot.rb +++ b/src/lib/bootloader/systemdboot.rb @@ -116,7 +116,7 @@ def write(etc_only: false) Bls.install_bootloader if Yast::Stage.initial # while new installation only (currently) write_kernel_parameter Bls.create_menu_entries - Bls.write_menu_timeout + Bls.write_menu_timeout(menu_timeout) true end From 672aa02a85cc967450c9214cfaabb79ff6686156 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Mon, 18 Nov 2024 15:46:37 +0100 Subject: [PATCH 10/46] no bls switching --- src/lib/bootloader/generic_widgets.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/bootloader/generic_widgets.rb b/src/lib/bootloader/generic_widgets.rb index 5709dba8a..5fb0512a3 100644 --- a/src/lib/bootloader/generic_widgets.rb +++ b/src/lib/bootloader/generic_widgets.rb @@ -72,11 +72,11 @@ def handle return :redraw if !Yast::Popup.ContinueCancel(popup_msg) end - if !Yast::Stage.initial && (old_bl == "systemd-boot") - Yast::Popup.Warning(_( - "Switching from systemd-boot to another bootloader\n" \ + if !Yast::Stage.initial && ["systemd-boot", "grub2-bls"].include?(old_bl) + Yast::Popup.Warning(format(_( + "Switching from %s to another bootloader\n" \ "is currently not supported.\n" - )) + ), old_bl)) return :redraw end From 5da518caf80f2c9140d7649762406841b4c837ef Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 19 Nov 2024 13:25:58 +0100 Subject: [PATCH 11/46] set textdomain --- src/lib/bootloader/bls.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/bootloader/bls.rb b/src/lib/bootloader/bls.rb index 95bd93744..fca9b1541 100644 --- a/src/lib/bootloader/bls.rb +++ b/src/lib/bootloader/bls.rb @@ -17,9 +17,14 @@ class Bls SDBOOTUTIL = "/usr/bin/sdbootutil" + def initialize + textdomain "bootloader" + end + def self.create_menu_entries Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", "add-all-kernels") rescue Cheetah::ExecutionFailed => e + textdomain "bootloader" Yast::Report.Error( format(_( "Cannot create boot menu entry:\n" \ From 74405e7cb84ee6558bb399c770ae33fb82930da2 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 19 Nov 2024 13:33:07 +0100 Subject: [PATCH 12/46] set textdomain --- src/lib/bootloader/bls.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/bootloader/bls.rb b/src/lib/bootloader/bls.rb index fca9b1541..fde64f666 100644 --- a/src/lib/bootloader/bls.rb +++ b/src/lib/bootloader/bls.rb @@ -13,7 +13,7 @@ module Bootloader # e.g. by grub2-bls and systemd-boot class Bls include Yast::Logger - include Yast::I18n + extend Yast::I18n SDBOOTUTIL = "/usr/bin/sdbootutil" @@ -24,7 +24,6 @@ def initialize def self.create_menu_entries Yast::Execute.on_target!(SDBOOTUTIL, "--verbose", "add-all-kernels") rescue Cheetah::ExecutionFailed => e - textdomain "bootloader" Yast::Report.Error( format(_( "Cannot create boot menu entry:\n" \ From c3e3c782aa687ff440f466127c8094bfbd80ba09 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 19 Nov 2024 13:46:37 +0100 Subject: [PATCH 13/46] switching not allowed --- src/lib/bootloader/generic_widgets.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/bootloader/generic_widgets.rb b/src/lib/bootloader/generic_widgets.rb index 5fb0512a3..080cb327e 100644 --- a/src/lib/bootloader/generic_widgets.rb +++ b/src/lib/bootloader/generic_widgets.rb @@ -80,6 +80,14 @@ def handle return :redraw end + if !Yast::Stage.initial && ["systemd-boot", "grub2-bls"].include?(new_bl) + Yast::Popup.Warning(format(_( + "Switching to bootloader %s \n" \ + "is currently not supported.\n" + ), new_bl)) + return :redraw + end + BootloaderFactory.current_name = new_bl BootloaderFactory.current.propose From ea93f6a387204e634a648d5e2b3401f46d82a260 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 26 Nov 2024 09:37:00 +0100 Subject: [PATCH 14/46] taking get-* from sdbootutil --- src/lib/bootloader/bls.rb | 32 ++++++++++++++++++++++++++++++ src/lib/bootloader/bls_sections.rb | 9 +-------- src/lib/bootloader/grub2bls.rb | 13 ++---------- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/lib/bootloader/bls.rb b/src/lib/bootloader/bls.rb index fde64f666..a5993fdc1 100644 --- a/src/lib/bootloader/bls.rb +++ b/src/lib/bootloader/bls.rb @@ -58,6 +58,22 @@ def self.write_menu_timeout(timeout) ) end + def self.menu_timeout + begin + output = Yast::Execute.on_target(SDBOOTUTIL, "get-timeout", stdout: :capture) + rescue Cheetah::ExecutionFailed => e + Yast::Report.Error( + format(_( + "Cannot read boot menu timeout:\n" \ + "Command `%{command}`.\n" \ + "Error output: %{stderr}" + ), command: e.commands.inspect, stderr: e.stderr) + ) + output = -1 + end + output + end + def self.write_default_menu(default) Yast::Execute.on_target(SDBOOTUTIL, "set-default", default) rescue Cheetah::ExecutionFailed => e @@ -69,5 +85,21 @@ def self.write_default_menu(default) ), command: e.commands.inspect, stderr: e.stderr) ) end + + def self.default_menu + begin + output = Yast::Execute.on_target(SDBOOTUTIL, "get-default", stdout: :capture) + rescue Cheetah::ExecutionFailed => e + Yast::Report.Error( + format(_( + "Cannot read default menu:\n" \ + "Command `%{command}`.\n" \ + "Error output: %{stderr}" + ), command: e.commands.inspect, stderr: e.stderr) + ) + output = "" + end + output + end end end diff --git a/src/lib/bootloader/bls_sections.rb b/src/lib/bootloader/bls_sections.rb index b0077258d..b2886e2dd 100644 --- a/src/lib/bootloader/bls_sections.rb +++ b/src/lib/bootloader/bls_sections.rb @@ -49,7 +49,7 @@ def write def read @data = read_entries @all = @data.map { |e| e["title"] } - @default = read_default + @default = Bls.default_menu end private @@ -63,13 +63,6 @@ def grubenv_path File.join("/boot/efi/EFI/", os, "/grubenv") end - # @return [String] return default boot as string or "" if not set - # or something goes wrong - def read_default - Yast::Misc.CustomSysconfigRead("default", "", - grubenv_path) - end - # @return [Array] return array of entries or [] def read_entries output = Yast::Execute.on_target( diff --git a/src/lib/bootloader/grub2bls.rb b/src/lib/bootloader/grub2bls.rb index e26a4d2d7..fee5b4688 100644 --- a/src/lib/bootloader/grub2bls.rb +++ b/src/lib/bootloader/grub2bls.rb @@ -8,7 +8,6 @@ Yast.import "Arch" Yast.import "Report" Yast.import "Stage" -Yast.import "Misc" module Bootloader # Represents grub2 bls bootloader with efi target @@ -47,8 +46,8 @@ def name # reads configuration from target disk def read - read_menu_timeout @sections.read + read_menu_timeout lines = "" filename = File.join(Yast::Installation.destdir, CMDLINE) if File.exist?(filename) @@ -159,18 +158,10 @@ def grub2bls_architecture ret end - def grubenv_path - str = Yast::Misc.CustomSysconfigRead("ID_LIKE", "openSUSE", - OS_RELEASE_PATH) - os = str.split.first - File.join("/boot/efi/EFI/", os, "/grubenv") - end - # @return [String] return default boot as string or "" if not set # or something goes wrong def read_menu_timeout - grub_default.timeout = Yast::Misc.CustomSysconfigRead("timeout", "", - grubenv_path) + grub_default.timeout = @sections.menu_timeout log.info "Boot timeout: #{grub_default.timeout}" end From 011839c733786912bb9fa4c03316249633ce8763 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 26 Nov 2024 10:18:00 +0100 Subject: [PATCH 15/46] using sdbootutil in systemd-boot --- src/lib/bootloader/systemdboot.rb | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/lib/bootloader/systemdboot.rb b/src/lib/bootloader/systemdboot.rb index 6d426dd84..b8a9cfeba 100644 --- a/src/lib/bootloader/systemdboot.rb +++ b/src/lib/bootloader/systemdboot.rb @@ -96,7 +96,7 @@ def cpu_mitigations=(value) def read super - read_menu_timeout + self.menu_timeout = Bls.menu_timeout self.secure_boot = Systeminfo.secure_boot_active? lines = "" @@ -202,15 +202,5 @@ def write_kernel_parameter end end - def read_menu_timeout - config = CFA::SystemdBoot.load - return unless config.menu_timeout - - self.menu_timeout = if config.menu_timeout == "menu-force" - -1 - else - config.menu_timeout.to_i - end - end end end From 556a3db35a85fb0463403c9dc9a7893b69ec5623 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 26 Nov 2024 10:46:43 +0100 Subject: [PATCH 16/46] timeout to integer --- src/lib/bootloader/bls.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/bootloader/bls.rb b/src/lib/bootloader/bls.rb index a5993fdc1..372126276 100644 --- a/src/lib/bootloader/bls.rb +++ b/src/lib/bootloader/bls.rb @@ -60,7 +60,7 @@ def self.write_menu_timeout(timeout) def self.menu_timeout begin - output = Yast::Execute.on_target(SDBOOTUTIL, "get-timeout", stdout: :capture) + output = Yast::Execute.on_target(SDBOOTUTIL, "get-timeout", stdout: :capture).to_i rescue Cheetah::ExecutionFailed => e Yast::Report.Error( format(_( From c88b4683a70f1f168250605641a237390bd61489 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 26 Nov 2024 10:57:58 +0100 Subject: [PATCH 17/46] not needed CFS::systemdboot --- src/lib/cfa/systemd_boot.rb | 100 ------------------------------------ 1 file changed, 100 deletions(-) delete mode 100644 src/lib/cfa/systemd_boot.rb diff --git a/src/lib/cfa/systemd_boot.rb b/src/lib/cfa/systemd_boot.rb deleted file mode 100644 index 2105346cc..000000000 --- a/src/lib/cfa/systemd_boot.rb +++ /dev/null @@ -1,100 +0,0 @@ -# frozen_string_literal: true - -# Copyright (c) [2023] SUSE LLC -# -# All Rights Reserved. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of version 2 of the GNU General Public License as published -# by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, contact SUSE LLC. -# -# To contact SUSE LLC about this file by physical or electronic mail, you may -# find current contact information at www.suse.com. - -require "yast" -require "cfa/base_model" -require "yast2/target_file" -require "yast2/execute" - -module CFA - # CFA based class to handle systemd-boot configuration file - # - # @example Reading a value - # file = CFA::SystemdBoot.new - # file.load - # file.menu_timeout #=> 10 - # - # @example Writing a value - # file = CFA::SystemdBoot.new - # file.menu_timeout = 5 - # file.save - # - # @example Loading shortcut - # file = CFA::SystemdBoot.load - # file.menu_timeout #=> 10 - class SystemdBoot < BaseModel - extend Yast::Logger - include Yast::Logger - - attributes( - menu_timeout: "timeout", - console_mode: "console_mode", - default: "default" - ) - - # Instantiates and loads a file when possible - # - # This method is basically a shortcut to instantiate and load the content in just one call. - # - # @param file_handler [#read,#write] something able to read/write a string (like File) - # @param file_path [String] File path - # @return [SystemdBoot] File with the already loaded content - def self.load(file_handler: Yast::TargetFile, file_path: PATH) - file = new(file_path: file_path, file_handler: file_handler) - file.tap(&:load) - rescue Errno::ENOENT - log.info("#{file_path} couldn't be loaded. Probably the file does not exist yet.") - - file - end - - # Constructor - # - # @param file_handler [#read,#write] something able to read/write a string (like File) - # @param file_path [String] File path - # - # @see CFA::BaseModel#initialize - def initialize(file_handler: Yast::TargetFile, file_path: PATH) - super(AugeasParser.new(LENS), file_path, file_handler: file_handler) - end - - def save - directory = File.dirname(@file_path) - if !Yast::FileUtils.IsDirectory(directory) - Yast::Execute.on_target("/usr/bin/mkdir", "--parents", - directory) - end - super - rescue Errno::EACCES - log.info("Permission denied when writting to #{@file_path}") - false - end - - # Default path to the systemd-boot config file - PATH = "/boot/efi/loader/loader.conf" - private_constant :PATH - - # The lens to be used by Augeas parser - # - LENS = "spacevars.lns" - private_constant :LENS - end -end From e450381b0817ecab791dcd769f5eab7c1ac4cd1a Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 26 Nov 2024 11:00:02 +0100 Subject: [PATCH 18/46] not needed CFS::systemdboot --- src/lib/bootloader/systemdboot.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/bootloader/systemdboot.rb b/src/lib/bootloader/systemdboot.rb index b8a9cfeba..1f9a4c020 100644 --- a/src/lib/bootloader/systemdboot.rb +++ b/src/lib/bootloader/systemdboot.rb @@ -5,7 +5,6 @@ require "bootloader/sysconfig" require "bootloader/cpu_mitigations" require "bootloader/bls" -require "cfa/systemd_boot" require "cfa/grub2/default" Yast.import "Report" From c7b49e8f180a2d59576b6bb04dcf74bf5f6ae86d Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 26 Nov 2024 11:22:30 +0100 Subject: [PATCH 19/46] not needed CFS::systemdboot --- src/lib/bootloader/systemdboot.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/bootloader/systemdboot.rb b/src/lib/bootloader/systemdboot.rb index 1f9a4c020..b65b4e4b8 100644 --- a/src/lib/bootloader/systemdboot.rb +++ b/src/lib/bootloader/systemdboot.rb @@ -200,6 +200,5 @@ def write_kernel_parameter end end end - end end From 184d2612e4ccaccc9b50b0a1a8fb3e67888669a4 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 26 Nov 2024 13:42:39 +0100 Subject: [PATCH 20/46] fixed testcases --- src/lib/bootloader/bls_sections.rb | 9 --------- src/lib/bootloader/grub2bls.rb | 10 ++-------- test/bls_sections_test.rb | 7 ++----- test/data/etc/kernel/cmdline | 2 +- test/grub2_bls_test.rb | 12 +++--------- test/systemdboot_test.rb | 6 ++---- 6 files changed, 10 insertions(+), 36 deletions(-) diff --git a/src/lib/bootloader/bls_sections.rb b/src/lib/bootloader/bls_sections.rb index b2886e2dd..808f6935c 100644 --- a/src/lib/bootloader/bls_sections.rb +++ b/src/lib/bootloader/bls_sections.rb @@ -54,15 +54,6 @@ def read private - OS_RELEASE_PATH = "/etc/os-release" - - def grubenv_path - str = Yast::Misc.CustomSysconfigRead("ID_LIKE", "openSUSE", - OS_RELEASE_PATH) - os = str.split.first - File.join("/boot/efi/EFI/", os, "/grubenv") - end - # @return [Array] return array of entries or [] def read_entries output = Yast::Execute.on_target( diff --git a/src/lib/bootloader/grub2bls.rb b/src/lib/bootloader/grub2bls.rb index fee5b4688..ea7778a09 100644 --- a/src/lib/bootloader/grub2bls.rb +++ b/src/lib/bootloader/grub2bls.rb @@ -47,7 +47,8 @@ def name # reads configuration from target disk def read @sections.read - read_menu_timeout + grub_default.timeout = Bls.menu_timeout + log.info "Boot timeout: #{grub_default.timeout}" lines = "" filename = File.join(Yast::Installation.destdir, CMDLINE) if File.exist?(filename) @@ -158,13 +159,6 @@ def grub2bls_architecture ret end - # @return [String] return default boot as string or "" if not set - # or something goes wrong - def read_menu_timeout - grub_default.timeout = @sections.menu_timeout - log.info "Boot timeout: #{grub_default.timeout}" - end - def merge_sections(other) return if !other.sections.default || other.sections.default.empty? diff --git a/test/bls_sections_test.rb b/test/bls_sections_test.rb index e5791dea3..a9d44201f 100755 --- a/test/bls_sections_test.rb +++ b/test/bls_sections_test.rb @@ -20,8 +20,8 @@ .with("/usr/bin/bootctl", "--json=short", "list", stdout: :capture) .and_return("[{\"title\" : \"openSUSE Tumbleweed\", \"isDefault\" : true }," \ "{\"title\" : \"Snapper: *openSUSE Tumbleweed 20241107\", \"isDefault\" : false}]") - allow(Yast::Misc).to receive(:CustomSysconfigRead) - .with("default", "", "/boot/efi/EFI/openSUSE/grubenv") + allow(Yast::Execute).to receive(:on_target) + .with("/usr/bin/sdbootutil", "get-default", stdout: :capture) .and_return("openSUSE Tumbleweed") subject.read end @@ -41,9 +41,6 @@ .with("/usr/bin/bootctl", "--json=short", "list", stdout: :capture) .and_return("[{\"title\" : \"openSUSE Tumbleweed\", \"isDefault\" : true }," \ "{\"title\" : \"Snapper: *openSUSE Tumbleweed 20241107\", \"isDefault\" : false}]") - allow(Yast::Misc).to receive(:CustomSysconfigRead) - .with("default", "", "/boot/efi/EFI/openSUSE/grubenv") - .and_return("openSUSE Tumbleweed") subject.read end it "sets new value for default" do diff --git a/test/data/etc/kernel/cmdline b/test/data/etc/kernel/cmdline index 3535cd4cd..ad8a94d05 100644 --- a/test/data/etc/kernel/cmdline +++ b/test/data/etc/kernel/cmdline @@ -1 +1 @@ -splash=silent quiet security=apparmor mitigations=off +root=/dev/sda3 splash=silent quiet security=apparmor mitigations=off diff --git a/test/grub2_bls_test.rb b/test/grub2_bls_test.rb index 4059184fa..d6c692eb7 100644 --- a/test/grub2_bls_test.rb +++ b/test/grub2_bls_test.rb @@ -19,22 +19,16 @@ describe "#read" do before do - allow(Yast::Misc).to receive(:CustomSysconfigRead) - .with("ID_LIKE", "openSUSE", "/etc/os-release") - .and_return("openSUSE") - allow(Yast::Misc).to receive(:CustomSysconfigRead) - .with("timeout", "", "/boot/efi/EFI/openSUSE/grubenv") + allow(Yast::Execute).to receive(:on_target) + .with("/usr/bin/sdbootutil", "get-timeout", stdout: :capture) .and_return("10") - allow(Yast::Misc).to receive(:CustomSysconfigRead) - .with("default", "", "/boot/efi/EFI/openSUSE/grubenv") - .and_return("") allow(Yast::Installation).to receive(:destdir).and_return(destdir) end it "reads menu timeout" do subject.read - expect(subject.grub_default.timeout).to eq "10" + expect(subject.grub_default.timeout).to eq 10 end it "reads entries from /etc/kernel/cmdline" do diff --git a/test/systemdboot_test.rb b/test/systemdboot_test.rb index fc755c056..4b0598c2f 100644 --- a/test/systemdboot_test.rb +++ b/test/systemdboot_test.rb @@ -51,7 +51,6 @@ it "installs the bootloader" do allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") - allow_any_instance_of(CFA::SystemdBoot).to receive(:save) # install bootloader expect(Yast::Execute).to receive(:on_target!) @@ -65,7 +64,6 @@ .with("/usr/bin/sdbootutil", "--verbose", "install") allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") - allow_any_instance_of(CFA::SystemdBoot).to receive(:save) subject.write # Checking written kernel parameters @@ -77,7 +75,6 @@ it "creates menu entries" do allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "--verbose", "install") - allow_any_instance_of(CFA::SystemdBoot).to receive(:save) # create menu entries expect(Yast::Execute).to receive(:on_target!) @@ -93,7 +90,8 @@ .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") # Saving menu timeout - expect_any_instance_of(CFA::SystemdBoot).to receive(:save) + allow(Yast::Execute).to receive(:on_target) + .with("/usr/bin/sdbootutil", "set-timeout", 10, stdout: :capture) subject.write end From e83fe23d43e77838bb8d0a398263fea59126171a Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 26 Nov 2024 14:53:31 +0100 Subject: [PATCH 21/46] fixed testcase --- test/data/etc/kernel/cmdline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data/etc/kernel/cmdline b/test/data/etc/kernel/cmdline index ad8a94d05..3535cd4cd 100644 --- a/test/data/etc/kernel/cmdline +++ b/test/data/etc/kernel/cmdline @@ -1 +1 @@ -root=/dev/sda3 splash=silent quiet security=apparmor mitigations=off +splash=silent quiet security=apparmor mitigations=off From b631d2450b76afb67df9e3f57aa9c5ecdf7e48c1 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 26 Nov 2024 17:12:12 +0100 Subject: [PATCH 22/46] added translation comments --- src/lib/bootloader/generic_widgets.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/bootloader/generic_widgets.rb b/src/lib/bootloader/generic_widgets.rb index 080cb327e..87113fefd 100644 --- a/src/lib/bootloader/generic_widgets.rb +++ b/src/lib/bootloader/generic_widgets.rb @@ -43,9 +43,10 @@ def localized_names(name) names = { "grub2" => _("GRUB2"), "grub2-efi" => _("GRUB2 for EFI"), + # Translators: Using Boot Loader Specification (BLS) snippets. "grub2-bls" => _("GRUB2 with BLS"), - # Translators: option in combo box when bootloader is not managed by yast2 "systemd-boot" => _("Systemd Boot"), + # Translators: option in combo box when bootloader is not managed by yast2 "none" => _("Not Managed"), "default" => _("Default") } From fac93835763b7296a4efc8c0e59d8a2f51b9ac2c Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 10:16:53 +0100 Subject: [PATCH 23/46] cleanup --- src/lib/bootloader/grub2_widgets.rb | 51 +++++++++++----------- src/lib/bootloader/os_prober.rb | 8 ++-- src/lib/bootloader/systeminfo.rb | 67 +++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/src/lib/bootloader/grub2_widgets.rb b/src/lib/bootloader/grub2_widgets.rb index 74802f268..8a98e105d 100644 --- a/src/lib/bootloader/grub2_widgets.rb +++ b/src/lib/bootloader/grub2_widgets.rb @@ -11,7 +11,6 @@ require "bootloader/device_path" require "cfa/matcher" -Yast.import "BootStorage" Yast.import "Initrd" Yast.import "Label" Yast.import "Report" @@ -982,11 +981,6 @@ def label end def contents - console_widget = if Yast::Arch.s390 || grub2.name == "grub2-bls" - CWM::Empty.new("console") - else - ConsoleWidget.new - end VBox( VSpacing(1), MarginBox(1, 0.5, KernelAppendWidget.new), @@ -1060,33 +1054,31 @@ def horizontal_margin end def loader_location_widget? - (Yast::Arch.x86_64 || Yast::Arch.i386 || Yast::Arch.ppc) && grub2.name == "grub2" + Systeminfo.loader_location_available?(grub2.name) end def generic_mbr_widget? - (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", "grub2-bls"].include?(grub2.name) + Systeminfo.generic_mbr_available?(grub2.name) end def secure_boot_widget? - Systeminfo.secure_boot_available?(grub2.name) && grub2.name != "grub2-bls" + Systeminfo.secure_boot_available?(grub2.name) end def trusted_boot_widget? - Systeminfo.trusted_boot_available?(grub2.name) && grub2.name != "grub2-bls" + Systeminfo.trusted_boot_available?(grub2.name) end def update_nvram_widget? - Systeminfo.nvram_available?(grub2.name) && grub2.name != "grub2-bls" + Systeminfo.nvram_available?(grub2.name) end def pmbr_widget? - (Yast::Arch.x86_64 || Yast::Arch.i386) && - Yast::BootStorage.gpt_boot_disk? && - grub2.name != "grub2-bls" + Systeminfo.pmbr_available?(grub2.name) end def device_map_button? - (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", "grub2-bls"].include?(grub2.name) + Systeminfo.device_map?(grub2.name) end end @@ -1102,11 +1094,6 @@ def label end def contents - hidden_menu_widget = if grub2.name == "grub2-bls" - CWM::Empty.new("hidden_menu") - else - HiddenMenuWidget.new - end VBox( VSpacing(2), HBox( @@ -1130,16 +1117,32 @@ def contents private def grub_password_widget - if grub2.name == "grub2-bls" + if Systeminfo.password_supported?(grub2.name) + GrubPasswordWidget.new + else CWM::Empty.new("password_widget") + end + end + + def console_widget + if console_supported?(grub2.name) + ConsoleWidget.new else - GrubPasswordWidget.new + CWM::Empty.new("console") + end + end + + def hidden_menu_widget + if hiding_menu_supported?(grub2.name) + HiddenMenuWidget.new + else + CWM::Empty.new("hidden_menu") end end def os_prober_widget - if OsProber.available? && # Checks !Arch.s390 and if package is available - grub2.name != "grub2-bls" + # Checks !Arch.s390, not grub2-bls and if package is available + if OsProber.available?(grub2.name) Left(OSProberWidget.new) else CWM::Empty.new("os_prober") diff --git a/src/lib/bootloader/os_prober.rb b/src/lib/bootloader/os_prober.rb index b79875615..64d519a65 100644 --- a/src/lib/bootloader/os_prober.rb +++ b/src/lib/bootloader/os_prober.rb @@ -13,10 +13,10 @@ def package_name "os-prober" end - # Check if os-prober is supported on this architecture and if the package - # is available - def available? - arch_supported? && package_available? + # Check if os-prober is supported on this architecture + # no grub2-bls bootloader and if the package is available + def available?(_bootloader) + arch_supported? && package_available? && grub2.name != "grub2-bls" end # Check if the os-prober package is available for installation diff --git a/src/lib/bootloader/systeminfo.rb b/src/lib/bootloader/systeminfo.rb index 9dc7ead26..b2b30e96c 100644 --- a/src/lib/bootloader/systeminfo.rb +++ b/src/lib/bootloader/systeminfo.rb @@ -7,6 +7,7 @@ require "yast2/execute" Yast.import "Arch" +Yast.import "BootStorage" module Bootloader # Provide system and architecture dependent information @@ -45,10 +46,47 @@ def secure_boot_available?(bootloader_name) return false if efi_arch == "i386" # no shim neither secure boot support for 32 bit arm nor riscv64 (bsc#1229070) return false if Yast::Arch.arm || Yast::Arch.riscv64 + # not for grub2-bls + return false if bootloader_name == "grub2-bls" efi_used?(bootloader_name) || s390_secure_boot_available? || ppc_secure_boot_available? end + # Check if mbr configurable with a bootloader. + # + # @param bootloader_name [String] bootloader name + # @return [Boolean] true if available with this bootloader + def generic_mbr_available?(_bootloader_name) + (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", "grub2-bls"].include?(grub2.name) + end + + # Check if loader location is configurable with a bootloader. + # + # @param bootloader_name [String] bootloader name + # @return [Boolean] true if available with this bootloader + def loader_location_available?(_bootloader_name) + (Yast::Arch.x86_64 || Yast::Arch.i386 || Yast::Arch.ppc) && grub2.name == "grub2" + end + + # Check if pmbr is configurable with a bootloader. + # + # @param bootloader_name [String] bootloader name + # @return [Boolean] true if available with this bootloader + def pmbr_available?(_bootloader_name) + (Yast::Arch.x86_64 || Yast::Arch.i386) && + Yast::BootStorage.gpt_boot_disk? && + grub2.name != "grub2-bls" + end + + # Check if setting device map is available. + # + # @param bootloader_name [String] bootloader name + # @return [Boolean] true if available with this bootloader + def device_map?(_bootloader_name) + (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", + "grub2-bls"].include?(grub2.name) + end + # Check current trusted boot state. # # ATM this just returns the config file setting. @@ -62,6 +100,9 @@ def trusted_boot_active? # Check if the system is expected to have nvram - ie. update_nvram_active? makes a difference def nvram_available?(bootloader_name = nil) + # not for grub2-bls + return false if bootloader_name == "grub2-bls" + (bootloader_name ? efi_used?(bootloader_name) : efi_supported?) || Yast::Arch.ppc end @@ -76,6 +117,8 @@ def update_nvram_active? def trusted_boot_available?(bootloader_name) # TPM availability is must have return false unless File.exist?("/dev/tpm0") + # not for grub2-bls + return false if bootloader_name == "grub2-bls" # for details about grub2 efi trusted boot support see FATE#315831 ( @@ -108,6 +151,30 @@ def efi_mandatory? Yast::Arch.aarch64 || Yast::Arch.arm || Yast::Arch.riscv64 end + # Check if console settings are supported + # + # param bootloader_name [String] bootloader name + # @return [Boolean] true if supported + def console_supported?(_bootloader_name) + !Yast::Arch.s390 && grub2.name != "grub2-bls" + end + + # Check if hiding menu are supported + # + # param bootloader_name [String] bootloader name + # @return [Boolean] true if supported + def hiding_menu_supported?(_bootloader_name) + grub2.name != "grub2-bls" + end + + # Check if setting password is supported + # + # param bootloader_name [String] bootloader name + # @return [Boolean] true if supported + def password_supported?(_bootloader_name) + grub2.name != "grub2-bls" + end + # Check if shim-install should be used instead of grub2-install. # # param bootloader_name [String] bootloader name From 18cd79da46105fa9966db1369ceb7c488430a169 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 10:34:31 +0100 Subject: [PATCH 24/46] cleanup --- src/lib/bootloader/grub2base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/bootloader/grub2base.rb b/src/lib/bootloader/grub2base.rb index 83caa8419..f6d4f0344 100644 --- a/src/lib/bootloader/grub2base.rb +++ b/src/lib/bootloader/grub2base.rb @@ -189,7 +189,7 @@ def packages # # @return [Boolean] true if the os-prober package should be included; false otherwise. def include_os_prober_package? - OsProber.available? + OsProber.arch_supported? && OsProber.package_available? end def enable_serial_console(console_arg_string) From 5a2e0a114e5a3b97bbb68750bca9f683f10f9a51 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 11:00:58 +0100 Subject: [PATCH 25/46] cleanup --- src/lib/bootloader/grub2_widgets.rb | 36 ++++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/lib/bootloader/grub2_widgets.rb b/src/lib/bootloader/grub2_widgets.rb index 8a98e105d..5df2d248a 100644 --- a/src/lib/bootloader/grub2_widgets.rb +++ b/src/lib/bootloader/grub2_widgets.rb @@ -87,6 +87,15 @@ def store grub_default.timeout = value.to_s end end + + private + def hidden_menu_widget + if Systeminfo.hiding_menu_supported?(grub2.name) + HiddenMenuWidget.new + else + CWM::Empty.new("hidden_menu") + end + end end # Represents decision if bootloader need activated partition @@ -989,6 +998,17 @@ def contents VStretch() ) end + + private + + def console_widget + if Systeminfo.console_supported?(grub2.name) + ConsoleWidget.new + else + CWM::Empty.new("console") + end + end + end # Represent tab with options related to stage1 location and bootloader type @@ -1124,22 +1144,6 @@ def grub_password_widget end end - def console_widget - if console_supported?(grub2.name) - ConsoleWidget.new - else - CWM::Empty.new("console") - end - end - - def hidden_menu_widget - if hiding_menu_supported?(grub2.name) - HiddenMenuWidget.new - else - CWM::Empty.new("hidden_menu") - end - end - def os_prober_widget # Checks !Arch.s390, not grub2-bls and if package is available if OsProber.available?(grub2.name) From 2b7dce0e23f32adb0910f00f7321202cbd19b51a Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 11:02:33 +0100 Subject: [PATCH 26/46] cleanup --- src/lib/bootloader/grub2_widgets.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/bootloader/grub2_widgets.rb b/src/lib/bootloader/grub2_widgets.rb index 5df2d248a..9dbd1d7bb 100644 --- a/src/lib/bootloader/grub2_widgets.rb +++ b/src/lib/bootloader/grub2_widgets.rb @@ -88,14 +88,6 @@ def store end end - private - def hidden_menu_widget - if Systeminfo.hiding_menu_supported?(grub2.name) - HiddenMenuWidget.new - else - CWM::Empty.new("hidden_menu") - end - end end # Represents decision if bootloader need activated partition @@ -1144,6 +1136,14 @@ def grub_password_widget end end + def hidden_menu_widget + if Systeminfo.hiding_menu_supported?(grub2.name) + HiddenMenuWidget.new + else + CWM::Empty.new("hidden_menu") + end + end + def os_prober_widget # Checks !Arch.s390, not grub2-bls and if package is available if OsProber.available?(grub2.name) From c148516290a0f73e86bad831bc46c7cc8b9a829f Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 11:13:50 +0100 Subject: [PATCH 27/46] cleanup --- src/lib/bootloader/grub2_widgets.rb | 10 ++++------ src/lib/bootloader/systeminfo.rb | 29 +++++++++++++++-------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/lib/bootloader/grub2_widgets.rb b/src/lib/bootloader/grub2_widgets.rb index 9dbd1d7bb..6f4c41e86 100644 --- a/src/lib/bootloader/grub2_widgets.rb +++ b/src/lib/bootloader/grub2_widgets.rb @@ -87,7 +87,6 @@ def store grub_default.timeout = value.to_s end end - end # Represents decision if bootloader need activated partition @@ -990,9 +989,9 @@ def contents VStretch() ) end - + private - + def console_widget if Systeminfo.console_supported?(grub2.name) ConsoleWidget.new @@ -1000,7 +999,6 @@ def console_widget CWM::Empty.new("console") end end - end # Represent tab with options related to stage1 location and bootloader type @@ -1142,8 +1140,8 @@ def hidden_menu_widget else CWM::Empty.new("hidden_menu") end - end - + end + def os_prober_widget # Checks !Arch.s390, not grub2-bls and if package is available if OsProber.available?(grub2.name) diff --git a/src/lib/bootloader/systeminfo.rb b/src/lib/bootloader/systeminfo.rb index b2b30e96c..dbf79b46f 100644 --- a/src/lib/bootloader/systeminfo.rb +++ b/src/lib/bootloader/systeminfo.rb @@ -56,35 +56,36 @@ def secure_boot_available?(bootloader_name) # # @param bootloader_name [String] bootloader name # @return [Boolean] true if available with this bootloader - def generic_mbr_available?(_bootloader_name) - (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", "grub2-bls"].include?(grub2.name) + def generic_mbr_available?(bootloader_name) + (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", + "grub2-bls"].include?(bootloader_name) end # Check if loader location is configurable with a bootloader. # # @param bootloader_name [String] bootloader name # @return [Boolean] true if available with this bootloader - def loader_location_available?(_bootloader_name) - (Yast::Arch.x86_64 || Yast::Arch.i386 || Yast::Arch.ppc) && grub2.name == "grub2" + def loader_location_available?(bootloader_name) + (Yast::Arch.x86_64 || Yast::Arch.i386 || Yast::Arch.ppc) && bootloader_name == "grub2" end # Check if pmbr is configurable with a bootloader. # # @param bootloader_name [String] bootloader name # @return [Boolean] true if available with this bootloader - def pmbr_available?(_bootloader_name) + def pmbr_available?(bootloader_name) (Yast::Arch.x86_64 || Yast::Arch.i386) && Yast::BootStorage.gpt_boot_disk? && - grub2.name != "grub2-bls" + bootloader_name != "grub2-bls" end # Check if setting device map is available. # # @param bootloader_name [String] bootloader name # @return [Boolean] true if available with this bootloader - def device_map?(_bootloader_name) + def device_map?(bootloader_name) (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", - "grub2-bls"].include?(grub2.name) + "grub2-bls"].include?(bootloader_name) end # Check current trusted boot state. @@ -155,24 +156,24 @@ def efi_mandatory? # # param bootloader_name [String] bootloader name # @return [Boolean] true if supported - def console_supported?(_bootloader_name) - !Yast::Arch.s390 && grub2.name != "grub2-bls" + def console_supported?(bootloader_name) + !Yast::Arch.s390 && bootloader_name != "grub2-bls" end # Check if hiding menu are supported # # param bootloader_name [String] bootloader name # @return [Boolean] true if supported - def hiding_menu_supported?(_bootloader_name) - grub2.name != "grub2-bls" + def hiding_menu_supported?(bootloader_name) + bootloader_name != "grub2-bls" end # Check if setting password is supported # # param bootloader_name [String] bootloader name # @return [Boolean] true if supported - def password_supported?(_bootloader_name) - grub2.name != "grub2-bls" + def password_supported?(bootloader_name) + bootloader_name != "grub2-bls" end # Check if shim-install should be used instead of grub2-install. From dec55e0bbb4ac6e218b148477d0028335b2bc799 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 11:37:41 +0100 Subject: [PATCH 28/46] cleanup --- src/lib/bootloader/os_prober.rb | 4 ++-- test/os_prober_test.rb | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/lib/bootloader/os_prober.rb b/src/lib/bootloader/os_prober.rb index 64d519a65..51903e266 100644 --- a/src/lib/bootloader/os_prober.rb +++ b/src/lib/bootloader/os_prober.rb @@ -15,8 +15,8 @@ def package_name # Check if os-prober is supported on this architecture # no grub2-bls bootloader and if the package is available - def available?(_bootloader) - arch_supported? && package_available? && grub2.name != "grub2-bls" + def available?(bootloader) + arch_supported? && package_available? && bootloader != "grub2-bls" end # Check if the os-prober package is available for installation diff --git a/test/os_prober_test.rb b/test/os_prober_test.rb index f9f57b949..5da7a9026 100644 --- a/test/os_prober_test.rb +++ b/test/os_prober_test.rb @@ -45,7 +45,7 @@ end it "os-prober is available" do - expect(subject.available?).to eq true + expect(subject.available?("grub2")).to eq true end end @@ -55,9 +55,20 @@ end it "os-prober is not available" do - expect(subject.available?).to eq false + expect(subject.available?("grub2")).to eq false end end + + context "if grub2-bls bootloader" do + before do + allow(Yast::Package).to receive(:Available).and_return(true) + end + + it "os-prober is not available for that bootloader" do + expect(subject.available?("grub2-bls")).to eq false + end + end + end end end From d18e621244770bb2709f1d194d9f6e3e16417617 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 12:02:12 +0100 Subject: [PATCH 29/46] cleanup --- src/lib/bootloader/grub2bls.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib/bootloader/grub2bls.rb b/src/lib/bootloader/grub2bls.rb index ea7778a09..b8faf1cbb 100644 --- a/src/lib/bootloader/grub2bls.rb +++ b/src/lib/bootloader/grub2bls.rb @@ -129,9 +129,6 @@ def packages private - SDBOOTUTIL = "/usr/bin/sdbootutil" - OS_RELEASE_PATH = "/etc/os-release" - def grub2bls_architecture arch = Yast::Arch.architecture table = { "x86_64" => "x86_64", From 351119046243870d404a9d600084c4877293a6d7 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 12:48:23 +0100 Subject: [PATCH 30/46] fixed Execute.on_target --- src/lib/bootloader/bls.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/bootloader/bls.rb b/src/lib/bootloader/bls.rb index 372126276..e47d54045 100644 --- a/src/lib/bootloader/bls.rb +++ b/src/lib/bootloader/bls.rb @@ -47,7 +47,7 @@ def self.install_bootloader end def self.write_menu_timeout(timeout) - Yast::Execute.on_target(SDBOOTUTIL, "set-timeout", timeout) + Yast::Execute.on_target!(SDBOOTUTIL, "set-timeout", timeout) rescue Cheetah::ExecutionFailed => e Yast::Report.Error( format(_( @@ -60,7 +60,7 @@ def self.write_menu_timeout(timeout) def self.menu_timeout begin - output = Yast::Execute.on_target(SDBOOTUTIL, "get-timeout", stdout: :capture).to_i + output = Yast::Execute.on_target!(SDBOOTUTIL, "get-timeout", stdout: :capture).to_i rescue Cheetah::ExecutionFailed => e Yast::Report.Error( format(_( @@ -75,7 +75,7 @@ def self.menu_timeout end def self.write_default_menu(default) - Yast::Execute.on_target(SDBOOTUTIL, "set-default", default) + Yast::Execute.on_target!(SDBOOTUTIL, "set-default", default) rescue Cheetah::ExecutionFailed => e Yast::Report.Error( format(_( @@ -88,7 +88,7 @@ def self.write_default_menu(default) def self.default_menu begin - output = Yast::Execute.on_target(SDBOOTUTIL, "get-default", stdout: :capture) + output = Yast::Execute.on_target!(SDBOOTUTIL, "get-default", stdout: :capture) rescue Cheetah::ExecutionFailed => e Yast::Report.Error( format(_( From 9a340a9f44ce674d01a3a742138e9799b5e4acec Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 13:43:30 +0100 Subject: [PATCH 31/46] fixed testcase --- test/bls_sections_test.rb | 31 +++++++++++-------------------- test/grub2_bls_test.rb | 17 +++++++++++++---- test/systemdboot_test.rb | 27 +++++++++++++++++++++------ 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/test/bls_sections_test.rb b/test/bls_sections_test.rb index a9d44201f..8de0ffa44 100755 --- a/test/bls_sections_test.rb +++ b/test/bls_sections_test.rb @@ -12,20 +12,18 @@ allow(Yast::Misc).to receive(:CustomSysconfigRead) .with("ID_LIKE", "openSUSE", "/etc/os-release") .and_return("openSUSE") + allow(Yast::Execute).to receive(:on_target) + .with("/usr/bin/bootctl", "--json=short", "list", stdout: :capture) + .and_return("[{\"title\" : \"openSUSE Tumbleweed\", \"isDefault\" : true }," \ + "{\"title\" : \"Snapper: *openSUSE Tumbleweed 20241107\", \"isDefault\" : false}]") + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "get-default", stdout: :capture) + .and_return("openSUSE Tumbleweed") + + subject.read end describe "#read" do - before do - allow(Yast::Execute).to receive(:on_target) - .with("/usr/bin/bootctl", "--json=short", "list", stdout: :capture) - .and_return("[{\"title\" : \"openSUSE Tumbleweed\", \"isDefault\" : true }," \ - "{\"title\" : \"Snapper: *openSUSE Tumbleweed 20241107\", \"isDefault\" : false}]") - allow(Yast::Execute).to receive(:on_target) - .with("/usr/bin/sdbootutil", "get-default", stdout: :capture) - .and_return("openSUSE Tumbleweed") - subject.read - end - it "returns list of all available sections" do expect(subject.all).to eq(["openSUSE Tumbleweed", "Snapper: *openSUSE Tumbleweed 20241107"]) end @@ -36,13 +34,6 @@ end describe "#default=" do - before do - allow(Yast::Execute).to receive(:on_target) - .with("/usr/bin/bootctl", "--json=short", "list", stdout: :capture) - .and_return("[{\"title\" : \"openSUSE Tumbleweed\", \"isDefault\" : true }," \ - "{\"title\" : \"Snapper: *openSUSE Tumbleweed 20241107\", \"isDefault\" : false}]") - subject.read - end it "sets new value for default" do subject.default = "Snapper: *openSUSE Tumbleweed 20241107" expect(subject.default).to eq "Snapper: *openSUSE Tumbleweed 20241107" @@ -57,14 +48,14 @@ describe "#write" do it "writes default value if set" do subject.default = "Snapper: *openSUSE Tumbleweed 20241107" - expect(Yast::Execute).to receive(:on_target) + expect(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "set-default", subject.default) subject.write end it "does not write default value if not set" do subject.default = "" - expect(Yast::Execute).to_not receive(:on_target) + expect(Yast::Execute).to_not receive(:on_target!) .with("/usr/bin/sdbootutil", "set-default", subject.default) subject.write end diff --git a/test/grub2_bls_test.rb b/test/grub2_bls_test.rb index d6c692eb7..5585e01f9 100644 --- a/test/grub2_bls_test.rb +++ b/test/grub2_bls_test.rb @@ -15,11 +15,14 @@ before do allow(Yast::Arch).to receive(:architecture).and_return("x86_64") + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "get-default", stdout: :capture) + .and_return("openSUSE Tumbleweed") end describe "#read" do before do - allow(Yast::Execute).to receive(:on_target) + allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "get-timeout", stdout: :capture) .and_return("10") allow(Yast::Installation).to receive(:destdir).and_return(destdir) @@ -48,7 +51,7 @@ end it "installs the bootloader" do - allow(Yast::Execute).to receive(:on_target) + allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "set-timeout", subject.grub_default.timeout) allow(Yast::Execute).to receive(:on_target!) @@ -66,7 +69,13 @@ end it "writes kernel cmdline" do - allow(Yast::Execute).to receive(:on_target) + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "get-default", stdout: :capture) + .and_return("openSUSE Tumbleweed") + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "get-timeout", stdout: :capture) + .and_return(10) + allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "set-timeout", subject.grub_default.timeout) allow(Yast::Execute).to receive(:on_target!) @@ -92,7 +101,7 @@ .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") # Saving menu timeout - expect(Yast::Execute).to receive(:on_target) + expect(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "set-timeout", subject.grub_default.timeout) subject.write diff --git a/test/systemdboot_test.rb b/test/systemdboot_test.rb index 4b0598c2f..8a91a565b 100644 --- a/test/systemdboot_test.rb +++ b/test/systemdboot_test.rb @@ -17,6 +17,12 @@ allow(Yast::BootStorage).to receive(:available_swap_partitions).and_return([]) allow(Yast::Arch).to receive(:architecture).and_return("x86_64") allow(Yast::Package).to receive(:Available).and_return(true) + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "get-timeout", stdout: :capture) + .and_return(10) + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "set-timeout", + subject.menu_timeout) end describe "#read" do @@ -49,6 +55,9 @@ end it "installs the bootloader" do + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "set-timeout", + subject.menu_timeout) allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") @@ -60,6 +69,9 @@ end it "writes kernel cmdline" do + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "set-timeout", + subject.menu_timeout) allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "--verbose", "install") allow(Yast::Execute).to receive(:on_target!) @@ -73,6 +85,9 @@ end it "creates menu entries" do + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "set-timeout", + subject.menu_timeout) allow(Yast::Execute).to receive(:on_target!) .with("/usr/bin/sdbootutil", "--verbose", "install") @@ -84,14 +99,14 @@ end it "saves menu timeout" do - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "install") - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") # Saving menu timeout - allow(Yast::Execute).to receive(:on_target) - .with("/usr/bin/sdbootutil", "set-timeout", 10, stdout: :capture) + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") + allow(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "--verbose", "install") + expect(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "set-timeout", subject.menu_timeout) subject.write end From 369f44f6ada2b5bb7ce79d358ca52290156faf02 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 18:27:30 +0100 Subject: [PATCH 32/46] pmbr for bls --- src/lib/bootloader/grub2.rb | 4 +- src/lib/bootloader/grub2_widgets.rb | 3 +- src/lib/bootloader/grub2base.rb | 16 ------ src/lib/bootloader/grub2efi.rb | 29 +---------- src/lib/bootloader/pmbr.rb | 79 +++++++++++++++++++++++++++++ src/lib/bootloader/systeminfo.rb | 10 ---- 6 files changed, 85 insertions(+), 56 deletions(-) create mode 100644 src/lib/bootloader/pmbr.rb diff --git a/src/lib/bootloader/grub2.rb b/src/lib/bootloader/grub2.rb index 587c931c6..dfe79454e 100644 --- a/src/lib/bootloader/grub2.rb +++ b/src/lib/bootloader/grub2.rb @@ -8,6 +8,7 @@ require "bootloader/stage1" require "bootloader/grub_install" require "bootloader/systeminfo" +require "bootloader/pmbr" Yast.import "Arch" Yast.import "BootStorage" @@ -59,9 +60,8 @@ def write(etc_only: false) device_map.write if (Yast::Arch.x86_64 || Yast::Arch.i386) && !etc_only - # TODO: own class handling PBMR # set it only for gpt disk bsc#1008092 - pmbr_setup(*::Yast::BootStorage.gpt_disks(stage1.devices)) + Pmbr.write(pmbr_action, "grub2") # powernv must not call grub2-install (bnc#970582) if !Yast::Arch.board_powernv diff --git a/src/lib/bootloader/grub2_widgets.rb b/src/lib/bootloader/grub2_widgets.rb index 6f4c41e86..794556c68 100644 --- a/src/lib/bootloader/grub2_widgets.rb +++ b/src/lib/bootloader/grub2_widgets.rb @@ -9,6 +9,7 @@ require "bootloader/systeminfo" require "bootloader/os_prober" require "bootloader/device_path" +require "bootloader/pmbr" require "cfa/matcher" Yast.import "Initrd" @@ -1084,7 +1085,7 @@ def update_nvram_widget? end def pmbr_widget? - Systeminfo.pmbr_available?(grub2.name) + Pmbr.available? end def device_map_button? diff --git a/src/lib/bootloader/grub2base.rb b/src/lib/bootloader/grub2base.rb index f6d4f0344..a98aa0a5d 100644 --- a/src/lib/bootloader/grub2base.rb +++ b/src/lib/bootloader/grub2base.rb @@ -79,22 +79,6 @@ def initialize # general functions - # set pmbr flags on boot disks - # TODO: move it to own place - def pmbr_setup(*devices) - return if @pmbr_action == :nothing - - action_parted = case @pmbr_action - when :add then "on" - when :remove then "off" - else raise "invalid action #{action}" - end - - devices.each do |dev| - Yast::Execute.locally("/usr/sbin/parted", "-s", dev, "disk_set", "pmbr_boot", action_parted) - end - end - def cpu_mitigations CpuMitigations.from_kernel_params(grub_default.kernel_params) end diff --git a/src/lib/bootloader/grub2efi.rb b/src/lib/bootloader/grub2efi.rb index 91234934b..96c6286ba 100644 --- a/src/lib/bootloader/grub2efi.rb +++ b/src/lib/bootloader/grub2efi.rb @@ -4,6 +4,7 @@ require "bootloader/grub2base" require "bootloader/grub_install" require "bootloader/sysconfig" +require "bootloader/pmbr" require "y2storage" Yast.import "Arch" @@ -27,7 +28,7 @@ def write(etc_only: false) # super have to called as first as grub install require some config written in ancestor super - pmbr_write if pmbr_action + Pmbr.write(pmbr_action, "grub2-efi") unless etc_only @grub_install.execute(secure_boot: secure_boot, trusted_boot: trusted_boot, @@ -99,31 +100,5 @@ def write_sysconfig(prewrite: false) update_nvram: update_nvram) prewrite ? sysconfig.pre_write : sysconfig.write end - - private - - # Filesystems in the staging (planned) devicegraph - # - # @return [Y2Storage::FilesystemsList] - def filesystems - staging = Y2Storage::StorageManager.instance.staging - staging.filesystems - end - - # write pmbr flags - def pmbr_write - fs = filesystems - efi_partition = fs.find { |f| f.mount_path == "/boot/efi" } - efi_partition ||= fs.find { |f| f.mount_path == "/boot" } - efi_partition ||= fs.find { |f| f.mount_path == "/" } - - raise "could not find boot partiton" unless efi_partition - - disks = Yast::BootStorage.stage1_disks_for(efi_partition) - # set only gpt disks - disks.select! { |disk| disk.gpt? } - - pmbr_setup(*disks.map(&:name)) - end end end diff --git a/src/lib/bootloader/pmbr.rb b/src/lib/bootloader/pmbr.rb new file mode 100644 index 000000000..ce8c44d9c --- /dev/null +++ b/src/lib/bootloader/pmbr.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require "yast" +require "y2storage" + +Yast.import "Arch" +Yast.import "BootStorage" + +module Bootloader + # Helper methods for PMBR + class Pmbr + class << self + def available? + (Yast::Arch.x86_64 || Yast::Arch.i386) && + Yast::BootStorage.gpt_boot_disk? + end + + def write(action, boot_loader) + if ["grub2-efi", "grub2-bls", "systemd-boot"].include?(boot_loader) + write_efi(action) + else # grub2 + write_none_efi(action) + end + end + end + + private + + # Filesystems in the staging (planned) devicegraph + # + # @return [Y2Storage::FilesystemsList] + def filesystems + staging = Y2Storage::StorageManager.instance.staging + staging.filesystems + end + + def write_none_efi(action) + stage1 = Stage1.new + begin + stage1.read + rescue Errno::ENOENT + # grub_installdevice is not part of grub2 rpm, so it doesn't need to exist. + # In such case ignore exception and use empty stage1 + log.info "grub_installdevice does not exist. Using empty one." + stage1 = Stage1.new + end + pmbr_setup(*::Yast::BootStorage.gpt_disks(stage1.devices), action) + end + + def write_efi(action) + fs = filesystems + efi_partition = fs.find { |f| f.mount_path == "/boot/efi" } + efi_partition ||= fs.find { |f| f.mount_path == "/boot" } + efi_partition ||= fs.find { |f| f.mount_path == "/" } + + raise "could not find boot partiton" unless efi_partition + + disks = Yast::BootStorage.stage1_disks_for(efi_partition) + # set only gpt disks + disks.select! { |disk| disk.gpt? } + pmbr_setup(*disks.map(&:name), action) + end + + # set pmbr flags on boot disks + def pmbr_setup(*devices, action) + return if action == :nothing + + action_parted = case action + when :add then "on" + when :remove then "off" + else raise "invalid action #{action}" + end + + devices.each do |dev| + Yast::Execute.locally("/usr/sbin/parted", "-s", dev, "disk_set", "pmbr_boot", action_parted) + end + end + end +end diff --git a/src/lib/bootloader/systeminfo.rb b/src/lib/bootloader/systeminfo.rb index dbf79b46f..7c779d21b 100644 --- a/src/lib/bootloader/systeminfo.rb +++ b/src/lib/bootloader/systeminfo.rb @@ -69,16 +69,6 @@ def loader_location_available?(bootloader_name) (Yast::Arch.x86_64 || Yast::Arch.i386 || Yast::Arch.ppc) && bootloader_name == "grub2" end - # Check if pmbr is configurable with a bootloader. - # - # @param bootloader_name [String] bootloader name - # @return [Boolean] true if available with this bootloader - def pmbr_available?(bootloader_name) - (Yast::Arch.x86_64 || Yast::Arch.i386) && - Yast::BootStorage.gpt_boot_disk? && - bootloader_name != "grub2-bls" - end - # Check if setting device map is available. # # @param bootloader_name [String] bootloader name From fc00345809be81d2984390e4fe57301f9781f323 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 27 Nov 2024 18:51:25 +0100 Subject: [PATCH 33/46] pmbr for bls --- src/lib/bootloader/pmbr.rb | 81 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/lib/bootloader/pmbr.rb b/src/lib/bootloader/pmbr.rb index ce8c44d9c..d1de6dabb 100644 --- a/src/lib/bootloader/pmbr.rb +++ b/src/lib/bootloader/pmbr.rb @@ -22,57 +22,58 @@ def write(action, boot_loader) write_none_efi(action) end end - end - private + private - # Filesystems in the staging (planned) devicegraph - # - # @return [Y2Storage::FilesystemsList] - def filesystems - staging = Y2Storage::StorageManager.instance.staging - staging.filesystems - end + # Filesystems in the staging (planned) devicegraph + # + # @return [Y2Storage::FilesystemsList] + def filesystems + staging = Y2Storage::StorageManager.instance.staging + staging.filesystems + end - def write_none_efi(action) - stage1 = Stage1.new - begin - stage1.read - rescue Errno::ENOENT - # grub_installdevice is not part of grub2 rpm, so it doesn't need to exist. - # In such case ignore exception and use empty stage1 - log.info "grub_installdevice does not exist. Using empty one." + def write_none_efi(action) stage1 = Stage1.new + begin + stage1.read + rescue Errno::ENOENT + # grub_installdevice is not part of grub2 rpm, so it doesn't need to exist. + # In such case ignore exception and use empty stage1 + log.info "grub_installdevice does not exist. Using empty one." + stage1 = Stage1.new + end + pmbr_setup(*::Yast::BootStorage.gpt_disks(stage1.devices), action) end - pmbr_setup(*::Yast::BootStorage.gpt_disks(stage1.devices), action) - end - def write_efi(action) - fs = filesystems - efi_partition = fs.find { |f| f.mount_path == "/boot/efi" } - efi_partition ||= fs.find { |f| f.mount_path == "/boot" } - efi_partition ||= fs.find { |f| f.mount_path == "/" } + def write_efi(action) + fs = filesystems + efi_partition = fs.find { |f| f.mount_path == "/boot/efi" } + efi_partition ||= fs.find { |f| f.mount_path == "/boot" } + efi_partition ||= fs.find { |f| f.mount_path == "/" } - raise "could not find boot partiton" unless efi_partition + raise "could not find boot partiton" unless efi_partition - disks = Yast::BootStorage.stage1_disks_for(efi_partition) - # set only gpt disks - disks.select! { |disk| disk.gpt? } - pmbr_setup(*disks.map(&:name), action) - end + disks = Yast::BootStorage.stage1_disks_for(efi_partition) + # set only gpt disks + disks.select! { |disk| disk.gpt? } + pmbr_setup(*disks.map(&:name), action) + end - # set pmbr flags on boot disks - def pmbr_setup(*devices, action) - return if action == :nothing + # set pmbr flags on boot disks + def pmbr_setup(*devices, action) + return if action == :nothing - action_parted = case action - when :add then "on" - when :remove then "off" - else raise "invalid action #{action}" - end + action_parted = case action + when :add then "on" + when :remove then "off" + else raise "invalid action #{action}" + end - devices.each do |dev| - Yast::Execute.locally("/usr/sbin/parted", "-s", dev, "disk_set", "pmbr_boot", action_parted) + devices.each do |dev| + Yast::Execute.locally("/usr/sbin/parted", "-s", dev, "disk_set", "pmbr_boot", + action_parted) + end end end end From 5789f8d6a92624cde48fc9593cd45ae4470b5799 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 28 Nov 2024 13:09:47 +0100 Subject: [PATCH 34/46] fixed testcase --- src/lib/bootloader/pmbr.rb | 2 ++ test/grub2_efi_test.rb | 4 ++-- test/grub2_test.rb | 12 +++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/lib/bootloader/pmbr.rb b/src/lib/bootloader/pmbr.rb index d1de6dabb..eda623998 100644 --- a/src/lib/bootloader/pmbr.rb +++ b/src/lib/bootloader/pmbr.rb @@ -10,6 +10,8 @@ module Bootloader # Helper methods for PMBR class Pmbr class << self + include Yast::Logger + def available? (Yast::Arch.x86_64 || Yast::Arch.i386) && Yast::BootStorage.gpt_boot_disk? diff --git a/test/grub2_efi_test.rb b/test/grub2_efi_test.rb index fedfae7bb..87d1ee127 100644 --- a/test/grub2_efi_test.rb +++ b/test/grub2_efi_test.rb @@ -38,8 +38,8 @@ it "setups protective mbr to real disks containing /boot/efi" do subject.pmbr_action = :add allow(Yast::BootStorage).to receive(:gpt_boot_disk?).and_return(true) - - expect(subject).to receive(:pmbr_setup).with("/dev/sda") + expect(Yast::Execute).to receive(:locally) + .with("/usr/sbin/parted", "-s", "/dev/sda", "disk_set", "pmbr_boot", "on") subject.write end diff --git a/test/grub2_test.rb b/test/grub2_test.rb index 903f2cae1..cad1288cf 100644 --- a/test/grub2_test.rb +++ b/test/grub2_test.rb @@ -45,7 +45,7 @@ end describe "write" do - let(:stage1) { double(Bootloader::Stage1, devices: [], generic_mbr?: false, write: nil) } + let(:stage1) { double(Bootloader::Stage1, devices: [], generic_mbr?: false, write: nil, read: nil) } before do allow(Bootloader::Stage1).to receive(:new).and_return(stage1) @@ -56,7 +56,7 @@ end it "writes stage1 location" do - stage1 = double(Bootloader::Stage1, devices: [], generic_mbr?: false) + stage1 = double(Bootloader::Stage1, devices: [], generic_mbr?: false, read: nil) expect(stage1).to receive(:write) allow(Bootloader::Stage1).to receive(:new).and_return(stage1) @@ -64,19 +64,21 @@ end it "changes pmbr flag as specified in pmbr_action for all boot devices with gpt label" do - stage1 = double(Bootloader::Stage1, devices: ["/dev/sda", "/dev/sdb1"], generic_mbr?: false, write: nil) + stage1 = double(Bootloader::Stage1, devices: ["/dev/sda", "/dev/sdb1"], generic_mbr?: false, write: nil, read: nil) allow(Bootloader::Stage1).to receive(:new).and_return(stage1) allow(Yast::BootStorage).to receive(:gpt_boot_disk?).and_return(true) devicegraph_stub("msdos_and_gpt.yaml") - expect(subject).to receive(:pmbr_setup).with("/dev/sdb") + expect(Yast::Execute).to receive(:locally) + .with("/usr/sbin/parted", "-s", "/dev/sdb", "disk_set", "pmbr_boot", "on") + subject.pmbr_action = :add subject.write end it "runs grub2-install for all configured stage1 locations on non-transactional systems" do - stage1 = double(Bootloader::Stage1, devices: ["/dev/sda", "/dev/sdb1"], generic_mbr?: false, write: nil) + stage1 = double(Bootloader::Stage1, devices: ["/dev/sda", "/dev/sdb1"], generic_mbr?: false, write: nil, read: nil) allow(Bootloader::Stage1).to receive(:new).and_return(stage1) grub2_install = double(Bootloader::GrubInstall) From 054b256b1f62a6436eb734bc14ab95a4d918550b Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 28 Nov 2024 13:34:00 +0100 Subject: [PATCH 35/46] cleanup --- src/lib/bootloader/grub2.rb | 2 +- src/lib/bootloader/grub2efi.rb | 2 +- src/lib/bootloader/pmbr.rb | 41 +++++++++------------------------- test/grub2_test.rb | 8 +++---- 4 files changed, 17 insertions(+), 36 deletions(-) diff --git a/src/lib/bootloader/grub2.rb b/src/lib/bootloader/grub2.rb index dfe79454e..b4c6685f3 100644 --- a/src/lib/bootloader/grub2.rb +++ b/src/lib/bootloader/grub2.rb @@ -61,7 +61,7 @@ def write(etc_only: false) device_map.write if (Yast::Arch.x86_64 || Yast::Arch.i386) && !etc_only # set it only for gpt disk bsc#1008092 - Pmbr.write(pmbr_action, "grub2") + Pmbr.write_none_efi(pmbr_action, stage1) # powernv must not call grub2-install (bnc#970582) if !Yast::Arch.board_powernv diff --git a/src/lib/bootloader/grub2efi.rb b/src/lib/bootloader/grub2efi.rb index 96c6286ba..aa3b52808 100644 --- a/src/lib/bootloader/grub2efi.rb +++ b/src/lib/bootloader/grub2efi.rb @@ -28,7 +28,7 @@ def write(etc_only: false) # super have to called as first as grub install require some config written in ancestor super - Pmbr.write(pmbr_action, "grub2-efi") + Pmbr.write_efi(pmbr_action) unless etc_only @grub_install.execute(secure_boot: secure_boot, trusted_boot: trusted_boot, diff --git a/src/lib/bootloader/pmbr.rb b/src/lib/bootloader/pmbr.rb index eda623998..a7902249d 100644 --- a/src/lib/bootloader/pmbr.rb +++ b/src/lib/bootloader/pmbr.rb @@ -10,41 +10,12 @@ module Bootloader # Helper methods for PMBR class Pmbr class << self - include Yast::Logger - def available? (Yast::Arch.x86_64 || Yast::Arch.i386) && Yast::BootStorage.gpt_boot_disk? end - def write(action, boot_loader) - if ["grub2-efi", "grub2-bls", "systemd-boot"].include?(boot_loader) - write_efi(action) - else # grub2 - write_none_efi(action) - end - end - - private - - # Filesystems in the staging (planned) devicegraph - # - # @return [Y2Storage::FilesystemsList] - def filesystems - staging = Y2Storage::StorageManager.instance.staging - staging.filesystems - end - - def write_none_efi(action) - stage1 = Stage1.new - begin - stage1.read - rescue Errno::ENOENT - # grub_installdevice is not part of grub2 rpm, so it doesn't need to exist. - # In such case ignore exception and use empty stage1 - log.info "grub_installdevice does not exist. Using empty one." - stage1 = Stage1.new - end + def write_none_efi(action, stage1) pmbr_setup(*::Yast::BootStorage.gpt_disks(stage1.devices), action) end @@ -62,6 +33,16 @@ def write_efi(action) pmbr_setup(*disks.map(&:name), action) end + private + + # Filesystems in the staging (planned) devicegraph + # + # @return [Y2Storage::FilesystemsList] + def filesystems + staging = Y2Storage::StorageManager.instance.staging + staging.filesystems + end + # set pmbr flags on boot disks def pmbr_setup(*devices, action) return if action == :nothing diff --git a/test/grub2_test.rb b/test/grub2_test.rb index cad1288cf..8069dce7c 100644 --- a/test/grub2_test.rb +++ b/test/grub2_test.rb @@ -45,7 +45,7 @@ end describe "write" do - let(:stage1) { double(Bootloader::Stage1, devices: [], generic_mbr?: false, write: nil, read: nil) } + let(:stage1) { double(Bootloader::Stage1, devices: [], generic_mbr?: false, write: nil) } before do allow(Bootloader::Stage1).to receive(:new).and_return(stage1) @@ -56,7 +56,7 @@ end it "writes stage1 location" do - stage1 = double(Bootloader::Stage1, devices: [], generic_mbr?: false, read: nil) + stage1 = double(Bootloader::Stage1, devices: [], generic_mbr?: false) expect(stage1).to receive(:write) allow(Bootloader::Stage1).to receive(:new).and_return(stage1) @@ -64,7 +64,7 @@ end it "changes pmbr flag as specified in pmbr_action for all boot devices with gpt label" do - stage1 = double(Bootloader::Stage1, devices: ["/dev/sda", "/dev/sdb1"], generic_mbr?: false, write: nil, read: nil) + stage1 = double(Bootloader::Stage1, devices: ["/dev/sda", "/dev/sdb1"], generic_mbr?: false, write: nil) allow(Bootloader::Stage1).to receive(:new).and_return(stage1) allow(Yast::BootStorage).to receive(:gpt_boot_disk?).and_return(true) @@ -78,7 +78,7 @@ end it "runs grub2-install for all configured stage1 locations on non-transactional systems" do - stage1 = double(Bootloader::Stage1, devices: ["/dev/sda", "/dev/sdb1"], generic_mbr?: false, write: nil, read: nil) + stage1 = double(Bootloader::Stage1, devices: ["/dev/sda", "/dev/sdb1"], generic_mbr?: false, write: nil) allow(Bootloader::Stage1).to receive(:new).and_return(stage1) grub2_install = double(Bootloader::GrubInstall) From bbc87d5d52dcc1e4c1618f54f7ee20bbb70655f7 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 28 Nov 2024 14:08:08 +0100 Subject: [PATCH 36/46] activate pmbr for bls --- src/lib/bootloader/grub2bls.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/bootloader/grub2bls.rb b/src/lib/bootloader/grub2bls.rb index b8faf1cbb..8281124b1 100644 --- a/src/lib/bootloader/grub2bls.rb +++ b/src/lib/bootloader/grub2bls.rb @@ -77,6 +77,8 @@ def propose end grub_default.timeout = Yast::ProductFeatures.GetIntegerFeature("globals", "boot_timeout").to_i @is_proposed = true + # for UEFI always remove PMBR flag on disk (bnc#872054) + self.pmbr_action = :remove end # @return true if configuration is already proposed @@ -96,6 +98,8 @@ def write(*) File.open(File.join(Yast::Installation.destdir, CMDLINE), "w+") do |fw| fw.puts(grub_default.kernel_params.serialize) end + + Pmbr.write_efi(pmbr_action) end # merges other bootloader configuration into this one. From 078bde49a03fcdb3c7b42de6c9992bc862547a41 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 28 Nov 2024 17:16:21 +0100 Subject: [PATCH 37/46] cleanup testcases --- test/bls_sections_test.rb | 13 +++--- test/bls_test.rb | 59 ++++++++++++++++++++++++++ test/grub2_bls_test.rb | 88 +++++++++++++++++++++------------------ test/systemdboot_test.rb | 55 ++++++++++-------------- 4 files changed, 134 insertions(+), 81 deletions(-) create mode 100644 test/bls_test.rb diff --git a/test/bls_sections_test.rb b/test/bls_sections_test.rb index 8de0ffa44..8c3c4c4fc 100755 --- a/test/bls_sections_test.rb +++ b/test/bls_sections_test.rb @@ -4,6 +4,7 @@ require_relative "./test_helper" require "bootloader/bls_sections" +require "bootloader/bls" require "cfa/memory_file" describe Bootloader::BlsSections do @@ -16,8 +17,7 @@ .with("/usr/bin/bootctl", "--json=short", "list", stdout: :capture) .and_return("[{\"title\" : \"openSUSE Tumbleweed\", \"isDefault\" : true }," \ "{\"title\" : \"Snapper: *openSUSE Tumbleweed 20241107\", \"isDefault\" : false}]") - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "get-default", stdout: :capture) + allow(Bootloader::Bls).to receive(:default_menu) .and_return("openSUSE Tumbleweed") subject.read @@ -48,15 +48,16 @@ describe "#write" do it "writes default value if set" do subject.default = "Snapper: *openSUSE Tumbleweed 20241107" - expect(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-default", subject.default) + expect(Bootloader::Bls).to receive(:write_default_menu) + .with(subject.default) + subject.write end it "does not write default value if not set" do subject.default = "" - expect(Yast::Execute).to_not receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-default", subject.default) + expect(Bootloader::Bls).to_not receive(:write_default_menu) + subject.write end diff --git a/test/bls_test.rb b/test/bls_test.rb new file mode 100644 index 000000000..9ed0a8343 --- /dev/null +++ b/test/bls_test.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require_relative "test_helper" + +describe Bootloader::Bls do + subject = described_class + + describe "#create_menu_entries" do + it "calls sdbootutil add-all-kernels" do + expect(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") + subject.create_menu_entries + end + end + + describe "#install_bootloader" do + it "calls sdbootutil install" do + expect(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "--verbose", "install") + subject.install_bootloader + end + end + + describe "#write_menu_timeout" do + it "calls sdbootutil set-timeout" do + expect(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "set-timeout", + 10) + subject.write_menu_timeout(10) + end + end + + describe "#menu_timeout" do + it "calls sdbootutil get-timeout" do + expect(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "get-timeout", stdout: :capture) + .and_return(10) + expect(subject.menu_timeout).to eq 10 + end + end + + describe "#write_default_menu" do + it "calls sdbootutil set-default" do + expect(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "set-default", "openSUSE") + subject.write_default_menu("openSUSE") + end + end + + describe "#default_menu" do + it "calls sdbootutil get-default" do + expect(Yast::Execute).to receive(:on_target!) + .with("/usr/bin/sdbootutil", "get-default", stdout: :capture) + .and_return("openSUSE") + expect(subject.default_menu).to eq "openSUSE" + end + end + +end diff --git a/test/grub2_bls_test.rb b/test/grub2_bls_test.rb index 5585e01f9..d6ec4acd1 100644 --- a/test/grub2_bls_test.rb +++ b/test/grub2_bls_test.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require_relative "test_helper" - +require "bootloader/bls" require "bootloader/grub2bls" describe Bootloader::Grub2Bls do @@ -15,16 +15,14 @@ before do allow(Yast::Arch).to receive(:architecture).and_return("x86_64") - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "get-default", stdout: :capture) - .and_return("openSUSE Tumbleweed") + allow(Bootloader::Bls).to receive(:default_menu) + .and_return(subject.sections.default) end describe "#read" do before do - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "get-timeout", stdout: :capture) - .and_return("10") + allow(Bootloader::Bls).to receive(:menu_timeout) + .and_return(10) allow(Yast::Installation).to receive(:destdir).and_return(destdir) end @@ -50,40 +48,52 @@ subject.grub_default.timeout = 10 end + it "setups protective mbr to real disks containing /boot/efi" do + subject.pmbr_action = :add + allow(Bootloader::Bls).to receive(:default_menu) + .and_return(subject.sections.default) + allow(Bootloader::Bls).to receive(:write_default_menu) + .with(subject.sections.default) + allow(Bootloader::Bls).to receive(:menu_timeout) + .and_return(subject.grub_default.timeout) + allow(Bootloader::Bls).to receive(:write_menu_timeout) + .with(subject.grub_default.timeout) + allow(Bootloader::Bls).to receive(:create_menu_entries) + allow(Bootloader::Bls).to receive(:install_bootloader) + allow(Yast::BootStorage).to receive(:gpt_boot_disk?).and_return(true) + + expect(Yast::Execute).to receive(:locally) + .with("/usr/sbin/parted", "-s", "/dev/sda", "disk_set", "pmbr_boot", "on") + + subject.write + end + it "installs the bootloader" do - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-timeout", - subject.grub_default.timeout) - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-default", subject.sections.default) + allow(Bootloader::Bls).to receive(:write_default_menu) + .with(subject.sections.default) + allow(Bootloader::Bls).to receive(:write_menu_timeout) + .with(subject.grub_default.timeout) # install bootloader - expect(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "install") + expect(Bootloader::Bls).to receive(:install_bootloader) # create menu entries - expect(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") + expect(Bootloader::Bls).to receive(:create_menu_entries) subject.write end it "writes kernel cmdline" do - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "get-default", stdout: :capture) - .and_return("openSUSE Tumbleweed") - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "get-timeout", stdout: :capture) - .and_return(10) - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-timeout", - subject.grub_default.timeout) - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-default", subject.sections.default) - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "install") - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") + allow(Bootloader::Bls).to receive(:default_menu) + .and_return(subject.sections.default) + allow(Bootloader::Bls).to receive(:write_default_menu) + .with(subject.sections.default) + allow(Bootloader::Bls).to receive(:menu_timeout) + .and_return(subject.grub_default.timeout) + allow(Bootloader::Bls).to receive(:write_menu_timeout) + .with(subject.grub_default.timeout) + allow(Bootloader::Bls).to receive(:create_menu_entries) + allow(Bootloader::Bls).to receive(:install_bootloader) subject.write # Checking written kernel parameters @@ -93,17 +103,13 @@ end it "saves menu timeout" do - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-default", subject.sections.default) - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "install") - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") - + allow(Bootloader::Bls).to receive(:create_menu_entries) + allow(Bootloader::Bls).to receive(:install_bootloader) + allow(Bootloader::Bls).to receive(:write_default_menu) + .with(subject.sections.default) # Saving menu timeout - expect(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-timeout", - subject.grub_default.timeout) + expect(Bootloader::Bls).to receive(:write_menu_timeout) + .with(subject.grub_default.timeout) subject.write end end diff --git a/test/systemdboot_test.rb b/test/systemdboot_test.rb index 8a91a565b..28fb9d65f 100644 --- a/test/systemdboot_test.rb +++ b/test/systemdboot_test.rb @@ -2,6 +2,7 @@ require_relative "test_helper" +require "bootloader/bls" require "bootloader/systemdboot" describe Bootloader::SystemdBoot do @@ -17,12 +18,10 @@ allow(Yast::BootStorage).to receive(:available_swap_partitions).and_return([]) allow(Yast::Arch).to receive(:architecture).and_return("x86_64") allow(Yast::Package).to receive(:Available).and_return(true) - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "get-timeout", stdout: :capture) - .and_return(10) - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-timeout", - subject.menu_timeout) + allow(Bootloader::Bls).to receive(:menu_timeout) + .and_return(subject.menu_timeout) + allow(Bootloader::Bls).to receive(:write_menu_timeout) + .with(subject.menu_timeout) end describe "#read" do @@ -55,27 +54,20 @@ end it "installs the bootloader" do - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-timeout", - subject.menu_timeout) - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") - + allow(Bootloader::Bls).to receive(:write_menu_timeout) + .with(subject.menu_timeout) + allow(Bootloader::Bls).to receive(:create_menu_entries) # install bootloader - expect(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "install") + expect(Bootloader::Bls).to receive(:install_bootloader) subject.write end it "writes kernel cmdline" do - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-timeout", - subject.menu_timeout) - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "install") - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") + allow(Bootloader::Bls).to receive(:write_menu_timeout) + .with(subject.menu_timeout) + allow(Bootloader::Bls).to receive(:create_menu_entries) + allow(Bootloader::Bls).to receive(:install_bootloader) subject.write # Checking written kernel parameters @@ -85,28 +77,23 @@ end it "creates menu entries" do - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-timeout", - subject.menu_timeout) - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "install") + allow(Bootloader::Bls).to receive(:write_menu_timeout) + .with(subject.menu_timeout) + allow(Bootloader::Bls).to receive(:install_bootloader) # create menu entries - expect(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") + expect(Bootloader::Bls).to receive(:create_menu_entries) subject.write end it "saves menu timeout" do + allow(Bootloader::Bls).to receive(:create_menu_entries) + allow(Bootloader::Bls).to receive(:install_bootloader) # Saving menu timeout - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "add-all-kernels") - allow(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "--verbose", "install") - expect(Yast::Execute).to receive(:on_target!) - .with("/usr/bin/sdbootutil", "set-timeout", subject.menu_timeout) + expect(Bootloader::Bls).to receive(:write_menu_timeout) + .with(subject.menu_timeout) subject.write end From 8c241ec03d116868260f6b0f5e2018ba09e22a1f Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 29 Nov 2024 10:46:24 +0100 Subject: [PATCH 38/46] merging pmbr --- src/lib/bootloader/grub2bls.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/bootloader/grub2bls.rb b/src/lib/bootloader/grub2bls.rb index 8281124b1..2b0e9ab78 100644 --- a/src/lib/bootloader/grub2bls.rb +++ b/src/lib/bootloader/grub2bls.rb @@ -111,15 +111,20 @@ def merge(other) log.info "merging: timeout: #{grub_default.timeout}=>#{other.grub_default.timeout}" log.info " mitigations: #{cpu_mitigations.to_human_string}=>" \ "#{other.cpu_mitigations.to_human_string}" + log.info " pmbr_action: #{pmbr_action}=>#{other.pmbr_action}" + log.info " grub_default.kernel_params: #{grub_default.kernel_params.serialize}=>" \ + "#{other.grub_default.kernel_params.serialize}" log.info " grub_default.kernel_params: #{grub_default.kernel_params.serialize}=>" \ "#{other.grub_default.kernel_params.serialize}" merge_sections(other) merge_grub_default(other) + merge_pmbr_action(other) log.info "merging result: timeout: #{grub_default.timeout}" log.info " mitigations: #{cpu_mitigations.to_human_string}" log.info " kernel_params: #{grub_default.kernel_params.serialize}" + log.info " pmbr_action: #{pmbr_action}" end # rubocop:enable Metrics/AbcSize From 0f8a518f3cfb3dff1696b13190e737df9e119796 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 29 Nov 2024 13:55:06 +0100 Subject: [PATCH 39/46] making PMBRWidget general --- src/lib/bootloader/generic_widgets.rb | 53 +++++++++++++++++++++++++++ src/lib/bootloader/grub2_widgets.rb | 42 --------------------- 2 files changed, 53 insertions(+), 42 deletions(-) diff --git a/src/lib/bootloader/generic_widgets.rb b/src/lib/bootloader/generic_widgets.rb index 87113fefd..baf1c46c1 100644 --- a/src/lib/bootloader/generic_widgets.rb +++ b/src/lib/bootloader/generic_widgets.rb @@ -164,6 +164,59 @@ def store end end + # Represents Protective MBR action + class PMBRWidget < CWM::ComboBox + include Grub2Helper + + def initialize + textdomain "bootloader" + + super + end + + def label + _("&Protective MBR flag") + end + + def help + _( + "

Protective MBR flag is expert only settings, that is needed " \ + "only on exotic hardware. For details see Protective MBR in GPT disks. " \ + "Do not touch if you are not sure.

" + ) + end + + def init + current_bl = ::Bootloader::BootloaderFactory.current + if current_bl.respond_to?(:pmbr_action) + self.value = current_bl.pmbr_action + else + log.error("Bootloader #{current_bl} does not support PMBR.") + disable + end + end + + def items + [ + # TRANSLATORS: set flag on disk + [:add, _("set")], + # TRANSLATORS: remove flag from disk + [:remove, _("remove")], + # TRANSLATORS: do not change flag on disk + [:nothing, _("do not change")] + ] + end + + def store + current_bl = ::Bootloader::BootloaderFactory.current + if current_bl.respond_to?(:pmbr_action) + current_bl.pmbr_action = value + else + log.error("Bootloader #{current_bl} does not support PMBR.") + end + end + end + # represents kernel command line class KernelAppendWidget < CWM::InputField def initialize diff --git a/src/lib/bootloader/grub2_widgets.rb b/src/lib/bootloader/grub2_widgets.rb index 794556c68..0afba8ef7 100644 --- a/src/lib/bootloader/grub2_widgets.rb +++ b/src/lib/bootloader/grub2_widgets.rb @@ -210,48 +210,6 @@ def store end end - # Represents Protective MBR action - class PMBRWidget < CWM::ComboBox - include Grub2Helper - - def initialize - textdomain "bootloader" - - super - end - - def label - _("&Protective MBR flag") - end - - def help - _( - "

Protective MBR flag is expert only settings, that is needed " \ - "only on exotic hardware. For details see Protective MBR in GPT disks. " \ - "Do not touch if you are not sure.

" - ) - end - - def init - self.value = grub2.pmbr_action - end - - def items - [ - # TRANSLATORS: set flag on disk - [:add, _("set")], - # TRANSLATORS: remove flag from disk - [:remove, _("remove")], - # TRANSLATORS: do not change flag on disk - [:nothing, _("do not change")] - ] - end - - def store - grub2.pmbr_action = value - end - end - # Represents switcher for secure boot on EFI class SecureBootWidget < CWM::CheckBox include Grub2Helper From 88e8aeb8273235a6dbca95bd9ca430f58adf3a87 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 29 Nov 2024 13:57:39 +0100 Subject: [PATCH 40/46] making PMBRWidget general --- src/lib/bootloader/generic_widgets.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/bootloader/generic_widgets.rb b/src/lib/bootloader/generic_widgets.rb index baf1c46c1..203b81c1f 100644 --- a/src/lib/bootloader/generic_widgets.rb +++ b/src/lib/bootloader/generic_widgets.rb @@ -166,8 +166,6 @@ def store # Represents Protective MBR action class PMBRWidget < CWM::ComboBox - include Grub2Helper - def initialize textdomain "bootloader" From d71a689a861ff6c6b08168dd206c8eb61e72a9c5 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 29 Nov 2024 15:22:42 +0100 Subject: [PATCH 41/46] add pmbr to systemd-boot --- src/lib/bootloader/systemdboot.rb | 11 +++++++++++ src/lib/bootloader/systemdboot_widgets.rb | 13 +++++++++++++ test/data/etc/kernel/cmdline | 2 +- test/systemdboot_test.rb | 13 +++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/lib/bootloader/systemdboot.rb b/src/lib/bootloader/systemdboot.rb index b65b4e4b8..140dc879d 100644 --- a/src/lib/bootloader/systemdboot.rb +++ b/src/lib/bootloader/systemdboot.rb @@ -29,6 +29,10 @@ class SystemdBoot < BootloaderBase # @return [Boolean] current secure boot setting attr_accessor :secure_boot + # @!attribute pmbr_action + # @return [:remove, :add, :nothing] + attr_accessor :pmbr_action + def initialize super @@ -37,6 +41,7 @@ def initialize # like grub2 in order to be compatible with all calls. @kernel_container = ::CFA::Grub2::Default.new @explicit_cpu_mitigations = false + @pmbr_action = :nothing end def kernel_params @@ -49,11 +54,13 @@ def merge(other) log.info " secure_boot: #{secure_boot}=>#{other.secure_boot}" log.info " mitigations: #{cpu_mitigations.to_human_string}=>" \ "#{other.cpu_mitigations.to_human_string}" + log.info " pmbr_action: #{pmbr_action}=>#{other.pmbr_action}" log.info " kernel_params: #{kernel_params.serialize}=>" \ "#{other.kernel_params.serialize}" super self.menu_timeout = other.menu_timeout unless other.menu_timeout.nil? self.secure_boot = other.secure_boot unless other.secure_boot.nil? + self.pmbr_action = other.pmbr_action if other.pmbr_action kernel_serialize = kernel_params.serialize # handle specially noresume as it should lead to remove all other resume @@ -75,6 +82,7 @@ def merge(other) log.info " secure_boot: #{secure_boot}" log.info " mitigations: #{cpu_mitigations.to_human_string}" log.info " kernel_params: #{kernel_params.serialize}" + log.info " pmbr_action: #{pmbr_action}" end # rubocop:enable Metrics/AbcSize @@ -116,6 +124,7 @@ def write(etc_only: false) write_kernel_parameter Bls.create_menu_entries Bls.write_menu_timeout(menu_timeout) + Pmbr.write_efi(pmbr_action) true end @@ -129,6 +138,8 @@ def propose end self.menu_timeout = Yast::ProductFeatures.GetIntegerFeature("globals", "boot_timeout").to_i self.secure_boot = Systeminfo.secure_boot_supported? + # for UEFI always remove PMBR flag on disk (bnc#872054) + self.pmbr_action = :remove end # Secure boot setting shown in summary screen. diff --git a/src/lib/bootloader/systemdboot_widgets.rb b/src/lib/bootloader/systemdboot_widgets.rb index e921ccfd0..6d891ba75 100644 --- a/src/lib/bootloader/systemdboot_widgets.rb +++ b/src/lib/bootloader/systemdboot_widgets.rb @@ -3,6 +3,7 @@ require "yast" require "bootloader/generic_widgets" require "bootloader/systeminfo" +require "bootloader/pmbr" Yast.import "UI" Yast.import "Arch" @@ -142,6 +143,8 @@ def contents ), VSpacing(1), *widgets, + VSpacing(1), + pmbr_widget, VStretch() ) end @@ -160,6 +163,12 @@ def widgets end end + def pmbr_widget + return Empty() unless pmbr_widget? + + MarginBox(1, 0, Left(PMBRWidget.new)) + end + def horizontal_margin @horizontal_margin ||= Yast::UI.TextMode ? 1 : 1.5 end @@ -167,6 +176,10 @@ def horizontal_margin def secure_boot_widget? Systeminfo.secure_boot_available?(systemdboot.name) end + + def pmbr_widget? + Pmbr.available? + end end # Represents bootloader specific options like its timeout, diff --git a/test/data/etc/kernel/cmdline b/test/data/etc/kernel/cmdline index 3535cd4cd..ad8a94d05 100644 --- a/test/data/etc/kernel/cmdline +++ b/test/data/etc/kernel/cmdline @@ -1 +1 @@ -splash=silent quiet security=apparmor mitigations=off +root=/dev/sda3 splash=silent quiet security=apparmor mitigations=off diff --git a/test/systemdboot_test.rb b/test/systemdboot_test.rb index 28fb9d65f..001422d98 100644 --- a/test/systemdboot_test.rb +++ b/test/systemdboot_test.rb @@ -63,6 +63,19 @@ subject.write end + it "setups protective mbr to real disks containing /boot/efi" do + subject.pmbr_action = :add + allow(Bootloader::Bls).to receive(:write_menu_timeout) + .with(subject.menu_timeout) + allow(Bootloader::Bls).to receive(:create_menu_entries) + allow(Bootloader::Bls).to receive(:install_bootloader) + allow(Yast::BootStorage).to receive(:gpt_boot_disk?).and_return(true) + + expect(Yast::Execute).to receive(:locally) + .with("/usr/sbin/parted", "-s", "/dev/sda", "disk_set", "pmbr_boot", "on") + subject.write + end + it "writes kernel cmdline" do allow(Bootloader::Bls).to receive(:write_menu_timeout) .with(subject.menu_timeout) From b936d0e6d7dcf0413b56a1da013433ba891a68f4 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 29 Nov 2024 16:07:53 +0100 Subject: [PATCH 42/46] fixed testcase --- test/generic_widgets_test.rb | 25 +++++++++++++++++++++++++ test/grub2_widgets_test.rb | 26 -------------------------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/test/generic_widgets_test.rb b/test/generic_widgets_test.rb index b74c28a94..cd497728e 100755 --- a/test/generic_widgets_test.rb +++ b/test/generic_widgets_test.rb @@ -15,3 +15,28 @@ include_examples "CWM::ComboBox" end + +describe Bootloader::PMBRWidget do + before do + Bootloader::BootloaderFactory.clear_cache + Bootloader::BootloaderFactory.current_name = "grub2-bls" + end + + it "is initialized to pmbr action" do + Bootloader::BootloaderFactory.current.pmbr_action = :add + expect(subject).to receive(:value=).with(:add) + + subject.init + end + + it "stores pmbr action" do + expect(subject).to receive(:value).and_return(:remove) + subject.store + + expect(Bootloader::BootloaderFactory.current.pmbr_action).to eq :remove + end + + it "offer set, remove and no action options" do + expect(subject.items.size).to eq 3 + end +end diff --git a/test/grub2_widgets_test.rb b/test/grub2_widgets_test.rb index 7f40371ac..e639d6e65 100644 --- a/test/grub2_widgets_test.rb +++ b/test/grub2_widgets_test.rb @@ -238,32 +238,6 @@ def stub_widget_value(id, value) end end -describe Bootloader::Grub2Widget::PMBRWidget do - before do - assign_bootloader - end - - it_behaves_like "labeled widget" - - it "is initialized to pmbr action" do - bootloader.pmbr_action = :add - expect(subject).to receive(:value=).with(:add) - - subject.init - end - - it "stores pmbr action" do - expect(subject).to receive(:value).and_return(:remove) - subject.store - - expect(bootloader.pmbr_action).to eq :remove - end - - it "offer set, remove and no action options" do - expect(subject.items.size).to eq 3 - end -end - describe Bootloader::Grub2Widget::SecureBootWidget do before do assign_bootloader("grub2-efi") From aad7340df1e449aaf0a83a29b39731a1f3703b65 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 29 Nov 2024 16:10:40 +0100 Subject: [PATCH 43/46] fixed testcase --- test/data/etc/kernel/cmdline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data/etc/kernel/cmdline b/test/data/etc/kernel/cmdline index ad8a94d05..3535cd4cd 100644 --- a/test/data/etc/kernel/cmdline +++ b/test/data/etc/kernel/cmdline @@ -1 +1 @@ -root=/dev/sda3 splash=silent quiet security=apparmor mitigations=off +splash=silent quiet security=apparmor mitigations=off From 9dd4b63c62b7bee82baa9bca861335b273ea59b3 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 17 Dec 2024 17:25:53 +0100 Subject: [PATCH 44/46] fixed proposal --- src/lib/bootloader/bootloader_factory.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/lib/bootloader/bootloader_factory.rb b/src/lib/bootloader/bootloader_factory.rb index 9e39df361..55cfa6dcf 100644 --- a/src/lib/bootloader/bootloader_factory.rb +++ b/src/lib/bootloader/bootloader_factory.rb @@ -123,10 +123,19 @@ def grub2_efi_installable? ((Yast::Arch.x86_64 || Yast::Arch.i386) && Systeminfo.efi?) end + def bls_installable? + ((Yast::Arch.x86_64 || Yast::Arch.i386) && Systeminfo.efi?) + end + def proposed_name prefered_bootloader = Yast::ProductFeatures.GetStringFeature("globals", "prefered_bootloader") - if supported_names.include?(prefered_bootloader) && prefered_bootloader != "grub2-efi" + if supported_names.include?(prefered_bootloader) && + ["grub2-efi", "systemd-boot", "grub2-bls"].include?(prefered_bootloader) + return prefered_bootloader + end + + if ["systemd-boot", "grub2-bls"].include?(prefered_bootloader) && bls_installable? return prefered_bootloader end From eba3bdf83c989b62031a38fba445f362a8994f35 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Tue, 17 Dec 2024 17:30:52 +0100 Subject: [PATCH 45/46] fixed proposal --- src/lib/bootloader/bootloader_factory.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/bootloader/bootloader_factory.rb b/src/lib/bootloader/bootloader_factory.rb index 55cfa6dcf..2bb108d2c 100644 --- a/src/lib/bootloader/bootloader_factory.rb +++ b/src/lib/bootloader/bootloader_factory.rb @@ -131,7 +131,7 @@ def proposed_name prefered_bootloader = Yast::ProductFeatures.GetStringFeature("globals", "prefered_bootloader") if supported_names.include?(prefered_bootloader) && - ["grub2-efi", "systemd-boot", "grub2-bls"].include?(prefered_bootloader) + !["grub2-efi", "systemd-boot", "grub2-bls"].include?(prefered_bootloader) return prefered_bootloader end From b19dd581a2a71b701c2c5958fe4c1e5eae6fa117 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 9 Jan 2025 12:27:54 +0100 Subject: [PATCH 46/46] packaging --- package/yast2-bootloader.changes | 6 ++++++ package/yast2-bootloader.spec | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package/yast2-bootloader.changes b/package/yast2-bootloader.changes index b29371870..0e772795d 100644 --- a/package/yast2-bootloader.changes +++ b/package/yast2-bootloader.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Thu Jan 9 11:26:59 UTC 2025 - Stefan Schubert + +- Added grub2-bls support (jsc#PED-10703). +- 5.0.14 + ------------------------------------------------------------------- Fri Dec 20 10:26:41 UTC 2024 - Josef Reidinger diff --git a/package/yast2-bootloader.spec b/package/yast2-bootloader.spec index 7602e9dfe..ffebaadb7 100644 --- a/package/yast2-bootloader.spec +++ b/package/yast2-bootloader.spec @@ -17,7 +17,7 @@ Name: yast2-bootloader -Version: 5.0.13 +Version: 5.0.14 Release: 0 Summary: YaST2 - Bootloader Configuration License: GPL-2.0-or-later