Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 24 additions & 11 deletions crates/lib/src/bootc_composefs/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ use serde::{Deserialize, Serialize};
use crate::bootc_composefs::state::{get_booted_bls, write_composefs_state};
use crate::bootc_composefs::status::ComposefsCmdline;
use crate::bootc_kargs::compute_new_kargs;
use crate::bootloader::bootupd_supports_bootloader_flag;
use crate::composefs_consts::{TYPE1_BOOT_DIR_PREFIX, TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED};
use crate::parsers::bls_config::{BLSConfig, BLSConfigType, EFIKey};
use crate::spec::BootloaderKind;
Expand Down Expand Up @@ -1455,32 +1456,44 @@ pub(crate) async fn setup_composefs_boot(
.or(root_setup.rootfs_uuid.as_deref())
.ok_or_else(|| anyhow!("No uuid for boot/root"))?;

let bootupd_chroot_target = Utf8Path::from_path(mounted_root.root_path())
.ok_or_else(|| anyhow!("composefs tmpdir path is not valid UTF-8"))?;

// Like the ostree backend, bind the physical root's real /boot (an
// ordinary ext4/xfs/... directory, not yet populated with kernels at
// this point) into the chroot. This gives bootupd both a correct
// filesystem to inspect for `--write-uuid` (rather than the ESP,
// which is otherwise mounted at the composefs root's own /boot) and
// an empty `boot/efi` directory for its EFI component to discover
// and mount the real ESP into, exactly as it would on ostree.
let bind_boot_path = root_setup.physical_root_path.join("boot");

if cfg!(target_arch = "s390x") {
// TODO: Integrate s390x support into install_via_bootupd
crate::bootloader::install_via_zipl(
&root_setup.device_info.require_single_root()?,
boot_uuid,
)?;
} else if bootupd_supports_bootloader_flag(Some(bootupd_chroot_target))? {
crate::bootloader::install_via_bootupd(
&root_setup.device_info,
&root_setup.physical_root_path,
&state.config_opts,
Some(bootupd_chroot_target),
Some(bind_boot_path.as_path()),
Some(postfetch.detected_bootloader),
)?;
} else if matches!(
postfetch.detected_bootloader,
Bootloader::Grub | Bootloader::GrubCC
) {
let chroot_target = Utf8Path::from_path(mounted_root.root_path())
.ok_or_else(|| anyhow!("composefs tmpdir path is not valid UTF-8"))?;
// Like the ostree backend, bind the physical root's real /boot (an
// ordinary ext4/xfs/... directory, not yet populated with kernels at
// this point) into the chroot. This gives bootupd both a correct
// filesystem to inspect for `--write-uuid` (rather than the ESP,
// which is otherwise mounted at the composefs root's own /boot) and
// an empty `boot/efi` directory for its EFI component to discover
// and mount the real ESP into, exactly as it would on ostree.
let bind_boot_path = root_setup.physical_root_path.join("boot");
crate::bootloader::install_via_bootupd(
&root_setup.device_info,
&root_setup.physical_root_path,
&state.config_opts,
Some(chroot_target),
Some(bootupd_chroot_target),
Some(bind_boot_path.as_path()),
None,
)?;

// FIXME: Remove this hack once we have support in bootupd
Expand Down
51 changes: 44 additions & 7 deletions crates/lib/src/bootloader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use fn_error_context::context;
use bootc_mount as mount;

use crate::bootc_composefs::boot::{MountedImageRoot, SecurebootKeys};
use crate::spec::Bootloader;
use crate::utils;

/// The name of the mountpoint for efi (as a subdirectory of /boot, or at the toplevel)
Expand Down Expand Up @@ -95,15 +96,17 @@ pub(crate) fn supports_bootupd(root: &Dir) -> Result<bool> {
Ok(r)
}

/// Check whether the target bootupd supports `--filesystem`.
///
/// Runs `bootupctl backend install --help` and looks for `--filesystem` in the
/// output. When `chroot_target` is set the command runs inside a chroot
/// (via [`ChrootCmd`]) so we probe the binary from the target image.
fn bootupd_supports_filesystem(chroot_target: Option<&Utf8Path>) -> Result<bool> {
fn bootupd_install_help(chroot_target: Option<&Utf8Path>) -> Result<String> {
static STATUS: std::sync::OnceLock<String> = std::sync::OnceLock::new();

if let Some(s) = STATUS.get() {
return Ok(s.clone());
};

let help_args = ["bootupctl", "backend", "install", "--help"];

let output = if let Some(target_root) = chroot_target {
ChrootCmd::new(target_root)
ChrootCmd::new(&target_root)
.set_default_path()
.run_get_string(help_args)?
} else {
Expand All @@ -113,6 +116,17 @@ fn bootupd_supports_filesystem(chroot_target: Option<&Utf8Path>) -> Result<bool>
.run_get_string()?
};

Ok(STATUS.get_or_init(|| output).to_string())
}

/// Check whether the target bootupd supports `--filesystem`.
///
/// Runs `bootupctl backend install --help` and looks for `--filesystem` in the
/// output. When `chroot_target` is set the command runs inside a chroot
/// (via [`ChrootCmd`]) so we probe the binary from the target image.
fn bootupd_supports_filesystem(chroot_target: Option<&Utf8Path>) -> Result<bool> {
let output = bootupd_install_help(chroot_target)?;

let use_filesystem = output.contains("--filesystem");

if use_filesystem {
Expand All @@ -124,6 +138,23 @@ fn bootupd_supports_filesystem(chroot_target: Option<&Utf8Path>) -> Result<bool>
Ok(use_filesystem)
}

/// Check whether the target bootupd supports `--bootloader` for installs
/// Caches the result of the first call; callers must use the same chroot_target
#[context("Checking if bootupd supports bootloader flag")]
pub(crate) fn bootupd_supports_bootloader_flag(chroot_target: Option<&Utf8Path>) -> Result<bool> {
let output = bootupd_install_help(chroot_target)?;

let supports_bootloader = output.contains("--bootloader");

if supports_bootloader {
tracing::debug!("bootupd supports --bootloader");
} else {
tracing::debug!("bootupd does not support --bootloader");
}

Ok(supports_bootloader)
}

/// Install the bootloader via bootupd.
///
/// When the target bootupd supports `--filesystem` we pass it pointing at a
Expand Down Expand Up @@ -151,6 +182,7 @@ pub(crate) fn install_via_bootupd(
configopts: &crate::install::InstallConfigOpts,
chroot_target: Option<&Utf8Path>,
bind_boot_path: Option<&Utf8Path>,
bootloader: Option<Bootloader>,
) -> Result<()> {
let verbose = std::env::var_os("BOOTC_BOOTLOADER_DEBUG").map(|_| "-vvvv");
// bootc defaults to only targeting the platform boot method.
Expand Down Expand Up @@ -184,6 +216,11 @@ pub(crate) fn install_via_bootupd(
bootupd_args.extend(opts.iter().copied());
}

if let Some(b) = bootloader {
bootupd_args.push("--bootloader");
bootupd_args.push(b.as_str());
};

// When the target bootupd lacks --filesystem support, fall back to the
// legacy --device flag. For --device we need the whole-disk device path
// (e.g. /dev/vda), not a partition (e.g. /dev/vda3), so resolve the
Expand Down
1 change: 1 addition & 0 deletions crates/lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1880,6 +1880,7 @@ async fn install_with_sysroot(
&state.config_opts,
Some(chroot_target.as_path()),
Some(bind_boot_path.as_path()),
None,
)?;
}
Bootloader::Systemd | Bootloader::GrubCC => {
Expand Down
14 changes: 9 additions & 5 deletions crates/lib/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,16 +255,20 @@ pub enum BootloaderKind {
GRUBClassic,
}

impl Display for Bootloader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let string = match self {
impl Bootloader {
pub fn as_str(self) -> &'static str {
match self {
Bootloader::Grub => "grub",
Bootloader::GrubCC => "grub-cc",
Bootloader::Systemd => "systemd",
Bootloader::None => "none",
};
}
}
}

write!(f, "{}", string)
impl Display for Bootloader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}

Expand Down
Loading