Skip to content

Commit

Permalink
Supporting Grub2-BLS (#708)
Browse files Browse the repository at this point in the history
* supporting Grub2-BLS
  • Loading branch information
schubi2 authored Jan 13, 2025
1 parent 5a649f8 commit 4e2183b
Show file tree
Hide file tree
Showing 35 changed files with 1,505 additions and 1,417 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1,490 changes: 446 additions & 1,044 deletions doc/bootloader_backend.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions package/yast2-bootloader.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Thu Jan 9 11:26:59 UTC 2025 - Stefan Schubert <schubi@suse.de>

- Added grub2-bls support (jsc#PED-10703).
- 5.0.14

-------------------------------------------------------------------
Fri Dec 20 10:26:41 UTC 2024 - Josef Reidinger <jreidinger@suse.com>

Expand Down
2 changes: 1 addition & 1 deletion package/yast2-bootloader.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 14 additions & 12 deletions src/lib/bootloader/autoyast_converter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
105 changes: 105 additions & 0 deletions src/lib/bootloader/bls.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# 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
extend Yast::I18n

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

def self.menu_timeout
begin
output = Yast::Execute.on_target!(SDBOOTUTIL, "get-timeout", stdout: :capture).to_i
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
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

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
67 changes: 67 additions & 0 deletions src/lib/bootloader/bls_sections.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

require "json"
require "yast"
require "yast2/execute"
require "bootloader/bls"

Yast.import "Misc"

module Bootloader
# Represents available sections and handling of default BLS boot entry
class BlsSections
include Yast::Logger

# @return [Array<String>] 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?

Bls.write_default_menu(@default)
end

def read
@data = read_entries
@all = @data.map { |e| e["title"] }
@default = Bls.default_menu
end

private

# @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
12 changes: 12 additions & 0 deletions src/lib/bootloader/bootloader_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
21 changes: 20 additions & 1 deletion src/lib/bootloader/bootloader_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "bootloader/none_bootloader"
require "bootloader/grub2"
require "bootloader/grub2efi"
require "bootloader/grub2bls"
require "bootloader/systemdboot"
require "bootloader/exceptions"

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -108,15 +114,28 @@ 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?)
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

Expand Down
2 changes: 1 addition & 1 deletion src/lib/bootloader/config_dialog.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 4e2183b

Please sign in to comment.