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
5 changes: 5 additions & 0 deletions crates/lib/src/bootc_composefs/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,8 @@ pub(crate) fn setup_composefs_bls_boot(
Some(efi_mount),
)
}

Bootloader::None => unreachable!("Checked at install time"),
};

let (bls_config, boot_digest, os_id) = match &entry {
Expand Down Expand Up @@ -851,6 +853,7 @@ fn write_pe_to_esp(
let efi_linux_path = mounted_efi.as_ref().join(match bootloader {
Bootloader::Grub => EFI_LINUX,
Bootloader::Systemd => SYSTEMD_UKI_DIR,
Bootloader::None => unreachable!("Checked at install time"),
});

create_dir_all(&efi_linux_path).context("Creating EFI/Linux")?;
Expand Down Expand Up @@ -1163,6 +1166,8 @@ pub(crate) fn setup_composefs_uki_boot(
}

Bootloader::Systemd => write_systemd_uki_config(&esp_mount.fd, &setup_type, uki_info, id)?,

Bootloader::None => unreachable!("Checked at install time"),
};

Ok(boot_digest)
Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/bootc_composefs/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ fn delete_depl_boot_entries(
// For Systemd UKI as well, we use .conf files
delete_type1_entry(deployment, boot_dir, deleting_staged)
}

Bootloader::None => unreachable!("Checked at install time"),
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/bootc_composefs/finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ pub(crate) async fn composefs_backend_finalize(
let entries_dir = boot_dir.open_dir("loader")?;
rename_exchange_bls_entries(&entries_dir)?;
}

Bootloader::None => unreachable!("Checked at install time"),
};

Ok(())
Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/bootc_composefs/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ fn list_bootloader_entries(storage: &Storage) -> Result<Vec<String>> {
.map(|entry| entry.get_verity())
.collect::<Result<Vec<_>, _>>()?
}

Bootloader::None => unreachable!("Checked at install time"),
};

Ok(entries)
Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/bootc_composefs/rollback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ pub(crate) async fn composefs_rollback(
// We use BLS entries for systemd UKI as well
rollback_composefs_entries(boot_dir, rollback_entry.bootloader.clone())?;
}

Bootloader::None => unreachable!("Checked at install time"),
}

if reverting {
Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/bootc_composefs/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,8 @@ pub(crate) async fn composefs_deployment_status_from(

(is_rollback_queued, Some(bls_configs), None)
}

Bootloader::None => unreachable!("Checked at install time"),
};

// Determine rollback deployment by matching extra deployment boot entries against entires read from /boot
Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/bootc_composefs/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ pub(crate) fn validate_update(
},

Bootloader::Systemd => rm_staged_type1_ent(boot_dir)?,

Bootloader::None => unreachable!("Checked at install time"),
}

// Remove state directory
Expand Down
31 changes: 25 additions & 6 deletions crates/lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@ pub(crate) struct InstallConfigOpts {
#[clap(long)]
#[serde(default)]
pub(crate) bootupd_skip_boot_uuid: bool,

/// The bootloader to use.
#[clap(long)]
#[serde(default)]
pub(crate) bootloader: Option<Bootloader>,
}

#[derive(Debug, Default, Clone, clap::Parser, Serialize, Deserialize, PartialEq, Eq)]
Expand All @@ -387,11 +392,6 @@ pub(crate) struct InstallComposefsOpts {
#[serde(default)]
pub(crate) insecure: bool,

/// The bootloader to use.
#[clap(long, requires = "composefs_backend")]
#[serde(default)]
pub(crate) bootloader: Option<Bootloader>,

/// Name of the UKI addons to install without the ".efi.addon" suffix.
/// This option can be provided multiple times if multiple addons are to be installed.
#[clap(long, requires = "composefs_backend")]
Expand Down Expand Up @@ -1590,6 +1590,12 @@ async fn prepare_install(
composefs_options.composefs_backend = true;
}

if composefs_options.composefs_backend
&& matches!(config_opts.bootloader, Some(Bootloader::None))
{
anyhow::bail!("Bootloader set to none is not supported with the composefs backend");
}

// We need to access devices that are set up by the host udev
bootc_mount::ensure_mirrored_host_mount("/dev")?;
// We need to read our own container image (and any logically bound images)
Expand Down Expand Up @@ -1642,10 +1648,20 @@ async fn prepare_install(
.and_then(|b| b.skip_boot_uuid)
.unwrap_or(false);
}

if config_opts.bootloader.is_none() {
config_opts.bootloader = config.bootloader.clone();
}
} else {
tracing::debug!("No install configuration found");
}

if let Some(crate::spec::Bootloader::None) = config_opts.bootloader {
if cfg!(target_arch = "s390x") {
anyhow::bail!("Bootloader set to none is not supported for the s390x architecture");
}
}

// Convert the keyfile to a hashmap because GKeyFile isnt Send for probably bad reasons.
let prepareroot_config = {
let kf = ostree_prepareroot::require_config_from_root(&rootfs)?;
Expand Down Expand Up @@ -1695,7 +1711,7 @@ impl PostFetchState {
// Determine bootloader type for the target system
// Priority: user-specified > bootupd availability > systemd-boot fallback
let detected_bootloader = {
if let Some(bootloader) = state.composefs_options.bootloader.clone() {
if let Some(bootloader) = state.config_opts.bootloader.clone() {
bootloader
} else {
if crate::bootloader::supports_bootupd(d)? {
Expand Down Expand Up @@ -1761,6 +1777,9 @@ async fn install_with_sysroot(
Bootloader::Systemd => {
anyhow::bail!("bootupd is required for ostree-based installs");
}
Bootloader::None => {
tracing::debug!("Skip bootloader installation due set to None");
}
}
}
tracing::debug!("Installed bootloader");
Expand Down
47 changes: 47 additions & 0 deletions crates/lib/src/install/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//!
//! This module handles the TOML configuration file for `bootc install`.

use crate::spec::Bootloader;
use anyhow::{Context, Result};
use clap::ValueEnum;
use fn_error_context::context;
Expand Down Expand Up @@ -97,6 +98,8 @@ pub(crate) struct InstallConfiguration {
pub(crate) boot_mount_spec: Option<String>,
/// Bootupd configuration
pub(crate) bootupd: Option<Bootupd>,
/// Bootloader to use (grub, systemd, none)
pub(crate) bootloader: Option<Bootloader>,
}

fn merge_basic<T>(s: &mut Option<T>, o: Option<T>, _env: &EnvProperties) {
Expand Down Expand Up @@ -180,6 +183,7 @@ impl Mergeable for InstallConfiguration {
merge_basic(&mut self.root_mount_spec, other.root_mount_spec, env);
merge_basic(&mut self.boot_mount_spec, other.boot_mount_spec, env);
self.bootupd.merge(other.bootupd, env);
merge_basic(&mut self.bootloader, other.bootloader, env);
if let Some(other_kargs) = other.kargs {
self.kargs
.get_or_insert_with(Default::default)
Expand Down Expand Up @@ -810,3 +814,46 @@ skip-boot-uuid = false
assert_eq!(install.bootupd.unwrap().skip_boot_uuid.unwrap(), true);
}
}

#[test]
fn test_parse_bootloader() {
let env = EnvProperties {
sys_arch: "x86_64".to_string(),
};

// 1. Test parsing "none"
let c: InstallConfigurationToplevel = toml::from_str(
r##"[install]
bootloader = "none"
"##,
)
.unwrap();
assert_eq!(c.install.unwrap().bootloader, Some(Bootloader::None));

// 2. Test parsing "grub"
let c: InstallConfigurationToplevel = toml::from_str(
r##"[install]
bootloader = "grub"
"##,
)
.unwrap();
assert_eq!(c.install.unwrap().bootloader, Some(Bootloader::Grub));

// 3. Test merging
// Initial config has "systemd"
let mut install: InstallConfiguration = toml::from_str(
r#"bootloader = "systemd"
"#,
)
.unwrap();

// Incoming config has "none"
let other = InstallConfiguration {
bootloader: Some(Bootloader::None),
..Default::default()
};

// Merge should overwrite systemd with none
install.merge(other, &env);
assert_eq!(install.bootloader, Some(Bootloader::None));
}
5 changes: 5 additions & 0 deletions crates/lib/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,19 +185,23 @@ pub struct BootEntryOstree {
#[derive(
clap::ValueEnum, Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema,
)]
#[serde(rename_all = "kebab-case")]
pub enum Bootloader {
/// Use Grub as the bootloader
#[default]
Grub,
/// Use SystemdBoot as the bootloader
Systemd,
/// Don't use a bootloader managed by bootc
None,
}

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

write!(f, "{}", string)
Expand All @@ -211,6 +215,7 @@ impl FromStr for Bootloader {
match value {
"grub" => Ok(Self::Grub),
"systemd" => Ok(Self::Systemd),
"none" => Ok(Self::None),
unrecognized => Err(anyhow::anyhow!("Unrecognized bootloader: '{unrecognized}'")),
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/lib/src/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ impl BootedStorage {
Bootloader::Grub => physical_root.open_dir("boot").context("Opening boot")?,
// NOTE: Handle XBOOTLDR partitions here if and when we use it
Bootloader::Systemd => esp_mount.fd.try_clone().context("Cloning fd")?,
Bootloader::None => unreachable!("Checked at install time"),
};

let storage = Storage {
Expand Down
8 changes: 8 additions & 0 deletions docs/src/bootloaders.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,11 @@ by default (except on s390x).
## s390x

bootc uses `zipl`.

## none

It is possible to skip bootloader installation entirely by using `--bootloader=none` (or `bootloader = "none"` in the [install] section of the config file).

With this option, users can have explicit control over how the boot loading is handled, without bootc or bootupd intervention.

NOTE: none is only supported for the Ostree backend and not for Composefs. It is also not supported for the s390x architecture.
1 change: 1 addition & 0 deletions docs/src/man/bootc-install-to-filesystem.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ is currently expected to be empty by default.
Possible values:
- grub
- systemd
- none

**--uki-addon**=*UKI_ADDON*

Expand Down