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
20 changes: 19 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,11 @@ jobs:
test_os: ${{ fromJson(needs.compute-ci-level.outputs.integration_os_matrix) }}
variant: [ostree, composefs]
filesystem: ["ext4", "xfs"]
bootloader: ["grub", "systemd"]
# TODO: Remove "grub" once "grub-cc" is stable
bootloader: ["grub", "grub-cc", "systemd"]
boot_type: ["bls", "uki"]
seal_state: ["sealed", "unsealed"]

exclude:
# https://github.com/bootc-dev/bootc/issues/1812
- test_os: centos-9
Expand All @@ -245,6 +247,22 @@ jobs:
- variant: ostree
bootloader: systemd

# For now only have grub-cc tests in F44
Comment thread
Johan-Liebert1 marked this conversation as resolved.
- test_os: fedora-45
bootloader: grub-cc
- test_os: fedora-43
bootloader: grub-cc
- test_os: centos-9
bootloader: grub-cc
- test_os: centos-10
bootloader: grub-cc
# Not in ostree
- variant: ostree
bootloader: grub-cc
# Not yet "sealed"
- bootloader: grub-cc
seal_state: sealed

runs-on: ubuntu-24.04

steps:
Expand Down
43 changes: 43 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,37 @@ RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
/run/packaging/enable-compose-repos
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp /usr/libexec/bootc-base-imagectl build-rootfs --manifest=standard /target-rootfs

# Get the latest GrubCC binary
FROM quay.io/fedora/eln:latest AS grub-cc-download
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp <<EOF
set -eux

# There isn't a generic grub2-efi-cc package that DNF can automatically resolve
case "$(uname -m)" in
x86_64)
dnf download grub2-efi-x64-cc
;;
aarch64)
dnf download grub2-efi-aa64-cc
;;
*)
echo "Unsupported architecture: $(uname -m)" >&2
exit 1
;;
esac

mv ./*.rpm grub-cc.rpm

EOF

FROM scratch as fetch
COPY --from=target-base /target-rootfs/ /
COPY --from=grub-cc-download /grub-cc.rpm /var/grub-cc.rpm
# SKIP_CONFIGS=1 skips LBIs, test kargs, and install configs (for FCOS testing)
ARG SKIP_CONFIGS
ARG boot_type
ARG seal_state
ARG bootloader
# All network-fetching operations: package installs from distro repos, Copr, Koji.
# Separated so `just build-fetch --target=fetch` can be retried independently on
# transient network failures without re-running the configuration phase.
Expand Down Expand Up @@ -89,6 +114,24 @@ RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
if [[ ${#pkgs_to_install[@]} -gt 0 ]]; then
dnf install -y "${pkgs_to_install[@]}"
fi

# Currently dnf installs grub-cc at /usr/lib/efi/grub2/1:2.12-60.eln156/EFI/eln/cc/grubx64-cc.efi
# which is less than ideal because:
# - the "cc" subdirectory
# - no support for installing grub-cc in bootupd
#
# So we move the binary to /usr/lib/grub-cc/grub-cc.efi so we have a predictale location from which
# we can copy the EFI binary to the ESP
if [[ "$bootloader" == "grub-cc" ]]; then
mkdir /var/grub-cc
rpm2archive /var/grub-cc.rpm | tar -xvz -C /var/grub-cc
Comment thread
Johan-Liebert1 marked this conversation as resolved.
file=$(find /var/grub-cc -name '*.efi')
mkdir -p /usr/lib/grub-cc
cp "$file" /usr/lib/grub-cc/grub-cc.efi
fi
Comment thread
Johan-Liebert1 marked this conversation as resolved.

rm -rvf /var/grub-cc
rm -rvf /var/grub-cc.rpm
EOF

# Note we don't do any customization here yet
Expand Down
10 changes: 4 additions & 6 deletions crates/lib/src/bootc_composefs/backwards_compat/bcompat_boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
TYPE1_ENT_PATH_STAGED, UKI_NAME_PREFIX, USER_CFG_STAGED,
},
parsers::bls_config::{BLSConfig, BLSConfigType},
spec::Bootloader,
spec::BootloaderKind,
store::Storage,
};
use anyhow::{Context, Result};
Expand Down Expand Up @@ -323,8 +323,8 @@ pub(crate) async fn prepend_custom_prefix(
handle_bls_conf(storage, cfs_cmdline, boot_dir, false)?;
}

BootType::Uki => match bootloader {
Bootloader::Grub => {
BootType::Uki => match bootloader.kind()? {
BootloaderKind::GRUBClassic => {
let esp = storage.require_esp()?;

let mut buf = String::new();
Expand Down Expand Up @@ -384,11 +384,9 @@ pub(crate) async fn prepend_custom_prefix(
rename_exchange_user_cfg(&grub_dir)?;
}

Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
handle_bls_conf(storage, cfs_cmdline, boot_dir, true)?;
}

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

Expand Down
62 changes: 51 additions & 11 deletions crates/lib/src/bootc_composefs/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ use crate::bootc_composefs::status::ComposefsCmdline;
use crate::bootc_kargs::compute_new_kargs;
use crate::composefs_consts::{TYPE1_BOOT_DIR_PREFIX, TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED};
use crate::parsers::bls_config::{BLSConfig, BLSConfigType};
use crate::spec::BootloaderKind;
use crate::task::Task;
use crate::{bootc_composefs::repo::open_composefs_repo, store::Storage};
use crate::{bootc_composefs::status::get_sorted_grub_uki_boot_entries, install::PostFetchState};
Expand Down Expand Up @@ -582,8 +583,8 @@ pub(crate) fn setup_composefs_bls_boot(

compute_new_kargs(mounted_erofs, current_root, &mut cmdline_refs)?;

let (entry_paths, _tmpdir_guard) = match bootloader {
Bootloader::Grub => {
let (entry_paths, _tmpdir_guard) = match bootloader.kind()? {
BootloaderKind::GRUBClassic => {
let root = Dir::open_ambient_dir(&root_path, ambient_authority())
.context("Opening root path")?;

Expand All @@ -607,7 +608,7 @@ pub(crate) fn setup_composefs_bls_boot(
)
}

Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
let efi_mount = mount_esp(&esp_device).context("Mounting ESP")?;

let mounted_efi = Utf8PathBuf::from(efi_mount.dir.path().as_str()?);
Expand All @@ -622,8 +623,6 @@ 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 @@ -1164,14 +1163,14 @@ pub(crate) fn setup_composefs_uki_boot(

let boot_digest = uki_info.boot_digest.clone();

match bootloader {
Bootloader::Grub => {
match bootloader.kind()? {
BootloaderKind::GRUBClassic => {
write_grub_uki_menuentry(root_path, &setup_type, uki_info.boot_label, id, &esp_device)?
}

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

Bootloader::None => unreachable!("Checked at install time"),
BootloaderKind::BLSCompatible => {
write_systemd_uki_config(&esp_mount.fd, &setup_type, uki_info, id)?
}
};

Ok(boot_digest)
Expand Down Expand Up @@ -1369,13 +1368,54 @@ pub(crate) async fn setup_composefs_boot(
&root_setup.device_info.require_single_root()?,
boot_uuid,
)?;
} else if postfetch.detected_bootloader == Bootloader::Grub {
} else if matches!(
postfetch.detected_bootloader,
Bootloader::Grub | Bootloader::GrubCC
) {
crate::bootloader::install_via_bootupd(
&root_setup.device_info,
&root_setup.physical_root_path,
&state.config_opts,
None,
)?;

// FIXME: Remove this hack once we have support in bootupd

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but it'd be clearer if this was in the conditional above right? Wouldn't bootupd bail in this scenario as is now?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, bootupd will fail if it's only grub-cc, but another hack is that we don't remove grub from the Dockerfile currently. We keep it alongside the grub-cc binary. So bootupd installs grub, shim etc and afterwards we replace the grubx64.efi with grub-cc.efi. It's not clean

if matches!(postfetch.detected_bootloader, Bootloader::GrubCC) {
root_setup
.physical_root
.remove_dir_all("boot/grub2")
.context("removing grub2")?;
Comment thread
Johan-Liebert1 marked this conversation as resolved.

let (os_id, ..) = parse_os_release(mounted_root.dir())?
.ok_or_else(|| anyhow::anyhow!("Failed to parse os-release"))?;

let dir = format!("EFI/{os_id}");

// Files are in EFI/<os-name>/
let efis_dir = mounted_root
.open_esp_dir()
.context("opening esp")?
.open_dir(&dir)
.with_context(|| format!("Opening {dir}"))?;

efis_dir
.remove_file_optional("bootuuid.cfg")
.context("Removing bootuuid.cfg")?;
efis_dir
.remove_file_optional("grub.cfg")
.context("Removing grub.cfg")?;

let final_name = match std::env::consts::ARCH {
"x86_64" => "grubx64.efi",
"aarch64" => "grubaa64-cc.efi",
arch => anyhow::bail!("GrubCC not supported for: {arch}"),
};

mounted_root
.dir()
.copy("usr/lib/grub-cc/grub-cc.efi", &efis_dir, final_name)
.context("Copying grub-cc binary")?;
}
} else {
crate::bootloader::install_systemd_boot(
&mounted_root,
Expand Down
10 changes: 4 additions & 6 deletions crates/lib/src/bootc_composefs/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED, USER_CFG_STAGED,
},
parsers::bls_config::{BLSConfigType, parse_bls_config},
spec::{BootEntry, Bootloader, DeploymentEntry},
spec::{BootEntry, BootloaderKind, DeploymentEntry},
status::Slot,
store::{BootedComposefs, Storage},
};
Expand Down Expand Up @@ -145,20 +145,18 @@ fn delete_depl_boot_entries(
) -> Result<()> {
let boot_dir = storage.require_boot_dir()?;

match deployment.deployment.bootloader {
Bootloader::Grub => match deployment.deployment.boot_type {
match deployment.deployment.bootloader.kind()? {
BootloaderKind::GRUBClassic => match deployment.deployment.boot_type {
BootType::Bls => delete_type1_conf_file(deployment, boot_dir, deleting_staged),
BootType::Uki => {
remove_grub_menucfg_entry(&deployment.deployment.verity, boot_dir, deleting_staged)
}
},

Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
// For Systemd UKI as well, we use .conf files
delete_type1_conf_file(deployment, boot_dir, deleting_staged)
}

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

Expand Down
10 changes: 4 additions & 6 deletions crates/lib/src/bootc_composefs/finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::bootc_composefs::gc::{GCOpts, composefs_gc};
use crate::bootc_composefs::rollback::{rename_exchange_bls_entries, rename_exchange_user_cfg};
use crate::bootc_composefs::status::get_composefs_status;
use crate::composefs_consts::STATE_DIR_ABS;
use crate::spec::Bootloader;
use crate::spec::BootloaderKind;
use crate::store::{BootedComposefs, Storage};
use anyhow::{Context, Result};
use bootc_initramfs_setup::mount_composefs_image;
Expand Down Expand Up @@ -131,21 +131,19 @@ pub(crate) async fn composefs_backend_finalize(

let boot_dir = storage.require_boot_dir()?;

match booted_composefs.bootloader {
Bootloader::Grub => match staged_composefs.boot_type {
match booted_composefs.bootloader.kind()? {
BootloaderKind::GRUBClassic => match staged_composefs.boot_type {
BootType::Bls => {
let entries_dir = boot_dir.open_dir("loader")?;
rename_exchange_bls_entries(&entries_dir)?;
}
BootType::Uki => finalize_staged_grub_uki(boot_dir)?,
},

Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
let entries_dir = boot_dir.open_dir("loader")?;
rename_exchange_bls_entries(&entries_dir)?;
}

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

// Now that we have successfully updated bootloader entires, we can GC the unreferenced ones
Expand Down
10 changes: 4 additions & 6 deletions crates/lib/src/bootc_composefs/rollback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::composefs_consts::{
COMPOSEFS_STAGED_DEPLOYMENT_FNAME, COMPOSEFS_TRANSIENT_STATE_DIR, TYPE1_ENT_PATH_STAGED,
};
use crate::deploy::ROLLBACK_JOURNAL_ID;
use crate::spec::{Bootloader, Host};
use crate::spec::{Bootloader, BootloaderKind, Host};
use crate::store::{BootedComposefs, Storage};
use crate::{
bootc_composefs::{boot::get_efi_uuid_source, status::get_sorted_grub_uki_boot_entries},
Expand Down Expand Up @@ -252,8 +252,8 @@ pub(crate) async fn composefs_rollback(

let boot_dir = storage.require_boot_dir()?;

match &rollback_entry.bootloader {
Bootloader::Grub => match rollback_entry.boot_type {
match &rollback_entry.bootloader.kind()? {
BootloaderKind::GRUBClassic => match rollback_entry.boot_type {
BootType::Bls => {
rollback_composefs_entries(&host, boot_dir, rollback_entry.bootloader.clone())?;
}
Expand All @@ -262,12 +262,10 @@ pub(crate) async fn composefs_rollback(
}
},

Bootloader::Systemd => {
BootloaderKind::BLSCompatible => {
// We use BLS entries for systemd UKI as well
rollback_composefs_entries(&host, boot_dir, rollback_entry.bootloader.clone())?;
}

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

if reverting {
Expand Down
Loading
Loading