Skip to content

Commit

Permalink
Merge pull request #4368 from jlinton/allow_systemd_boot_as_bootloader
Browse files Browse the repository at this point in the history
Provide a command line option to install systemd-boot rather than grub2 on x86_64 and arm64
  • Loading branch information
poncovka committed Mar 10, 2023
2 parents 8753613 + a9e0559 commit 227afca
Show file tree
Hide file tree
Showing 18 changed files with 310 additions and 18 deletions.
10 changes: 10 additions & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,13 @@ Below is a list of pure community features, their community maintainers, and mai
* Description:

``Enable boot of the installed system from a BTRFS subvolume.``

systemd-boot as a bootloader
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* Origin: https://github.com/rhinstaller/anaconda/pull/4368
* Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2135531
* Maintainer: Jeremy Linton <jeremy.linton@arm.com>
* Description:

``Enable boot using systemd-boot rather than grub2.``
2 changes: 1 addition & 1 deletion anaconda.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Source0: https://github.com/rhinstaller/%{name}/releases/download/%{name}-%{vers
%define libxklavierver 5.4
%define mehver 0.23-1
%define nmver 1.0
%define pykickstartver 3.44-1
%define pykickstartver 3.45-1
%define pypartedver 2.5-2
%define pythonblivetver 1:3.6.0-1
%define rpmver 4.15.0
Expand Down
1 change: 1 addition & 0 deletions data/anaconda.conf
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ selinux = -1
#
# DEFAULT Choose the type by platform.
# EXTLINUX Use extlinux as the bootloader.
# SDBOOT Use systemd-boot as the bootloader.
#
type = DEFAULT

Expand Down
5 changes: 5 additions & 0 deletions data/anaconda_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ Use extlinux as the bootloader. Note that there's no attempt to validate
that this will work for your platform or anything; it assumes that if you
ask for it, you want to try.

sdboot
Use systemd-boot as the bootloader. Note that there's no attempt to validate
that this will work for your platform or anything; it assumes that if you
ask for it, you want to try.

nombr

If nombr is specified the grub2 bootloader will be installed but the
Expand Down
9 changes: 9 additions & 0 deletions docs/boot-options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,15 @@ Use extlinux as the bootloader. Note that there's no attempt to validate that
this will work for your platform or anything; it assumes that if you ask for it,
you want to try.

.. inst.sdboot:
inst.sdboot
^^^^^^^^^^^^^

Use systemd-boot as the bootloader. Note that there's no attempt to validate that
this will work for your platform or anything; it assumes that if you ask for it,
you want to try.

.. inst.leavebootorder:
inst.leavebootorder
Expand Down
20 changes: 20 additions & 0 deletions docs/release-notes/systemd-boot.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
:Type: Kickstart Installation
:Summary: Install an image using systemd-boot rather than grub (#2135531)

:Description:
With this release, systemd-boot can be selected as an alternative boot
loader for testing and development purposes.

This can be done with 'inst.sdboot' from the grub/kernel command
line or with '--sdboot' in a kickstart file as part of the
bootloader command. The resulting machine should be free of grub,
shim, and grubby packages, with all the boot files on the EFI
System Partition (ESP). This may mean that it is wise to dedicate
the space previously allocated for /boot to the ESP in order to
assure that future kernel upgrades will have sufficient space.

For more information, refer to the anaconda and systemd-boot documentation.

:Links:
- https://bugzilla.redhat.com/show_bug.cgi?id=2135531
- https://github.com/rhinstaller/anaconda/pull/4368
2 changes: 1 addition & 1 deletion dracut/parse-kickstart
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ from pykickstart.commands.mediacheck import FC4_MediaCheck as MediaCheck
from pykickstart.commands.driverdisk import F14_DriverDisk as DriverDisk
from pykickstart.commands.network import F38_Network as Network
from pykickstart.commands.displaymode import F26_DisplayMode as DisplayMode
from pykickstart.commands.bootloader import F34_Bootloader as Bootloader
from pykickstart.commands.bootloader import F39_Bootloader as Bootloader

# Default logging: none
log = logging.getLogger('parse-kickstart').addHandler(logging.NullHandler())
Expand Down
2 changes: 2 additions & 0 deletions pyanaconda/argument_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,8 @@ def __call__(self, parser, namespace, values, option_string=None):
help=help_parser.help_text("noeject"))
ap.add_argument("--extlinux", action="store_true", default=False,
help=help_parser.help_text("extlinux"))
ap.add_argument("--sdboot", action="store_true", default=False,
help=help_parser.help_text("sdboot"))
ap.add_argument("--nombr", action="store_true", default=False,
help=help_parser.help_text("nombr"))
ap.add_argument("--mpathfriendlynames", dest="multipath_friendly_names", action="store_true",
Expand Down
2 changes: 2 additions & 0 deletions pyanaconda/core/configuration/anaconda.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,8 @@ def set_from_opts(self, opts):
# Set the bootloader type.
if opts.extlinux:
self.bootloader._set_option("type", BootloaderType.EXTLINUX.value)
if opts.sdboot:
self.bootloader._set_option("type", BootloaderType.SDBOOT.value)

# Set the boot loader flags.
self.bootloader._set_option("nonibft_iscsi_boot", opts.nonibftiscsiboot)
Expand Down
4 changes: 3 additions & 1 deletion pyanaconda/core/configuration/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@

class BootloaderType(Enum):
"""Type of the bootloader."""
DEFAULT = "DEFAULT"
DEFAULT = "DEFAULT"
EXTLINUX = "EXTLINUX"
SDBOOT = "SDBOOT"


class BootloaderSection(Section):
Expand All @@ -38,6 +39,7 @@ def type(self):
DEFAULT Choose the type by platform.
EXTLINUX Use extlinux as the bootloader.
SDBOOT Use systemd-boot as the bootloader.
:return: an instance of BootloaderType
"""
Expand Down
2 changes: 1 addition & 1 deletion pyanaconda/core/kickstart/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from pykickstart.commands.authselect import F28_Authselect as Authselect
from pykickstart.commands.autopart import F38_AutoPart as AutoPart
from pykickstart.commands.autostep import F34_AutoStep as AutoStep
from pykickstart.commands.bootloader import F34_Bootloader as Bootloader
from pykickstart.commands.bootloader import F39_Bootloader as Bootloader
from pykickstart.commands.btrfs import F23_BTRFS as BTRFS
from pykickstart.commands.cdrom import FC3_Cdrom as Cdrom
from pykickstart.commands.clearpart import F28_ClearPart as ClearPart
Expand Down
5 changes: 5 additions & 0 deletions pyanaconda/modules/storage/bootloader/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ class BootLoader(object):
image_label_attr = "label"
encryption_support = False
stage2_is_valid_stage1 = False
stage2_required = True

# requirements for stage2 devices
stage2_device = None
Expand Down Expand Up @@ -645,6 +646,10 @@ def is_valid_stage2_device(self, device, linux=True, non_linux=False):

log.debug("Is %s a valid stage2 target device?", device.name)

if not self.stage2_required:
log.debug("stage2 not required")
return True

if device.protected:
valid = False

Expand Down
7 changes: 7 additions & 0 deletions pyanaconda/modules/storage/bootloader/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ def _set_module_from_kickstart(self, data):
if data.bootloader.extlinux:
self.set_default_type(BootloaderType.EXTLINUX)

if data.bootloader.sdboot:
self.set_default_type(BootloaderType.SDBOOT)

if data.bootloader.bootDrive:
self.set_drive(data.bootloader.bootDrive)

Expand Down Expand Up @@ -184,6 +187,9 @@ def setup_kickstart(self, data):
if self.get_default_type() == BootloaderType.EXTLINUX:
data.bootloader.extlinux = True

if self.get_default_type() == BootloaderType.SDBOOT:
data.bootloader.sdboot = True

if self.bootloader_mode == BootloaderMode.DISABLED:
data.bootloader.disabled = True
data.bootloader.location = "none"
Expand Down Expand Up @@ -503,6 +509,7 @@ def generate_initramfs_with_tasks(self, payload_type, kernel_versions):
"""
return [
RecreateInitrdsTask(
storage=self.storage,
payload_type=payload_type,
kernel_versions=kernel_versions,
sysroot=conf.target.system_root
Expand Down
87 changes: 75 additions & 12 deletions pyanaconda/modules/storage/bootloader/efi.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@

from pyanaconda.modules.storage.bootloader.base import BootLoaderError
from pyanaconda.modules.storage.bootloader.grub2 import GRUB2
from pyanaconda.modules.storage.bootloader.systemd import SystemdBoot
from pyanaconda.core import util
from pyanaconda.core.kernel import kernel_arguments
from pyanaconda.core.i18n import _
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.core.kernel import kernel_arguments
from pyanaconda.core.path import join_paths
from pyanaconda.product import productName

from pyanaconda.anaconda_loggers import get_module_logger
log = get_module_logger(__name__)

__all__ = ["EFIBase", "EFIGRUB", "Aarch64EFIGRUB", "ArmEFIGRUB", "MacEFIGRUB"]
__all__ = ["EFIBase", "EFIGRUB", "Aarch64EFIGRUB", "ArmEFIGRUB", "MacEFIGRUB", "Aarch64EFISystemdBoot", "X64EFISystemdBoot"]


class EFIBase(object):
Expand All @@ -42,6 +45,16 @@ def efi_config_dir(self):
def _efi_config_dir(self):
return "efi/EFI/{}".format(conf.bootloader.efi_dir)

def get_fw_platform_size(self):
try:
with open("/sys/firmware/efi/fw_platform_size", "r") as f:
value = f.readline().strip()
except OSError:
log.info("Reading /sys/firmware/efi/fw_platform_size failed, "
"defaulting to 64-bit install.")
value = '64'
return value

def efibootmgr(self, *args, **kwargs):
if not conf.target.is_hardware:
log.info("Skipping efibootmgr for image/directory install.")
Expand Down Expand Up @@ -134,7 +147,7 @@ def install(self, args=None):
class EFIGRUB(EFIBase, GRUB2):
"""EFI GRUBv2"""
_packages32 = [ "grub2-efi-ia32", "shim-ia32" ]
_packages_common = [ "efibootmgr", "grub2-tools" ]
_packages_common = ["efibootmgr", "grub2-tools", "grub2-tools-extra", "grubby" ]
stage2_is_valid_stage1 = False
stage2_bootable = False

Expand All @@ -144,14 +157,7 @@ def __init__(self):
super().__init__()
self._packages64 = [ "grub2-efi-x64", "shim-x64" ]

try:
f = open("/sys/firmware/efi/fw_platform_size", "r")
value = f.readline().strip()
except OSError:
log.info("Reading /sys/firmware/efi/fw_platform_size failed, "
"defaulting to 64-bit install.")
value = '64'
if value == '32':
if self.get_fw_platform_size() == '32':
self._is_32bit_firmware = True

@property
Expand Down Expand Up @@ -197,13 +203,70 @@ def write_config(self):
super().write_config()


class EFISystemdBoot(EFIBase, SystemdBoot):
"""EFI Systemd-boot"""
_packages_common = ["efibootmgr", "systemd-udev", "systemd-boot", "sdubby"]
_packages64 = []

def __init__(self):
super().__init__()

if self.get_fw_platform_size() == '32':
# not supported try a different bootloader
log.error("efi.py: systemd-boot is not supported on 32-bit platforms")
raise BootLoaderError(_("Systemd-boot is not supported on this platform"))

@property
def packages(self):
return self._packages64 + self._packages_common

@property
def efi_config_file(self):
""" Full path to EFI configuration file. """
return join_paths(self.efi_config_dir, self._config_file)

def write_config(self):
""" Write the config settings to config file (ex: grub.cfg) not needed for systemd. """
config_path = join_paths(conf.target.system_root, self.efi_config_file)

log.info("efi.py: (systemd) write_config systemd : %s ", config_path)

super().write_config()

def install(self, args=None):
log.info("efi.py: (systemd) install")
# force the resolution order, we don't want to:
# efibootmgr remove old "fedora"
# or use efiboot mgr to install a new one
# lets just use `bootctl install` directly.
# which will fix the efi boot variables too.
SystemdBoot.install(self)


class Aarch64EFIGRUB(EFIGRUB):
_serial_consoles = ["ttyAMA", "ttyS"]
_efi_binary = "\\shimaa64.efi"

def __init__(self):
super().__init__()
self._packages64 = ["grub2-efi-aa64", "shim-aa64"]
self._packages64 = ["grub2-efi-aa64", "shim-aa64", "grub2-efi-aa64-cdboot"]


class Aarch64EFISystemdBoot(EFISystemdBoot):
_serial_consoles = ["ttyAMA", "ttyS"]
_efi_binary = "\\systemd-bootaa64.efi"

def __init__(self):
super().__init__()
self._packages64 = []

class X64EFISystemdBoot(EFISystemdBoot):
_efi_binary = "\\systemd-bootx64.efi"

def __init__(self):
super().__init__()
self._packages64 = []



class ArmEFIGRUB(EFIGRUB):
Expand Down
10 changes: 10 additions & 0 deletions pyanaconda/modules/storage/bootloader/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def get_class_by_name(cls, name):
Supported values:
EXTLINUX
SDBOOT
:param name: a boot loader name or None
:return: a boot loader class or None
Expand All @@ -90,6 +91,15 @@ def get_class_by_name(cls, name):
from pyanaconda.modules.storage.bootloader.extlinux import EXTLINUX
return EXTLINUX

if name == "SDBOOT":
platform_class = platform.platform.__class__
if platform_class is platform.Aarch64EFI:
from pyanaconda.modules.storage.bootloader.efi import Aarch64EFISystemdBoot
return Aarch64EFISystemdBoot
if platform_class is platform.EFI:
from pyanaconda.modules.storage.bootloader.efi import X64EFISystemdBoot
return X64EFISystemdBoot

return None

@classmethod
Expand Down
8 changes: 6 additions & 2 deletions pyanaconda/modules/storage/bootloader/installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from blivet.devices import BTRFSDevice
from pyanaconda.core.constants import PAYLOAD_TYPE_RPM_OSTREE, PAYLOAD_LIVE_TYPES
from pyanaconda.modules.storage.bootloader import BootLoaderError

from pyanaconda.modules.storage.bootloader.systemd import SystemdBoot
from pyanaconda.core.util import execWithRedirect
from pyanaconda.modules.common.errors.installation import BootloaderInstallationError
from pyanaconda.modules.storage.constants import BootloaderMode
Expand Down Expand Up @@ -173,9 +173,10 @@ def run(self):
class RecreateInitrdsTask(Task):
"""Installation task that recreates the initrds."""

def __init__(self, payload_type, kernel_versions, sysroot):
def __init__(self, storage, payload_type, kernel_versions, sysroot):
"""Create a new task."""
super().__init__()
self._storage = storage
self._payload_type = payload_type
self._versions = kernel_versions
self._sysroot = sysroot
Expand All @@ -192,6 +193,9 @@ def run(self):
if self._payload_type == PAYLOAD_TYPE_RPM_OSTREE:
log.debug("Don't regenerate initramfs on rpm-ostree systems.")
return
if isinstance(self._storage.bootloader, SystemdBoot):
log.debug("Don't regenerate initramfs on systemd-boot systems.")
return

recreate_initrds(
sysroot=self._sysroot,
Expand Down
Loading

0 comments on commit 227afca

Please sign in to comment.