Skip to content

Commit 72c6973

Browse files
authored
Parse uki and aboot files using minimal composefs-boot dependency (#397)
This uses a new feature of composefs-boot to drop all composefs integration dependencies, making it a very small library with parsers for boot artifacts (uki, aboot, os-release files). This allows us to drop the objcopy and android-tools dependency. Signed-off-by: Alexander Larsson <alexl@redhat.com>
1 parent 6e6564c commit 72c6973

8 files changed

Lines changed: 40 additions & 35 deletions

File tree

‎Cargo.lock‎

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎contrib/packaging/bcvk.spec‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@ Requires: podman
1919
Requires: qemu-img
2020
Requires: qemu-kvm
2121
Requires: virtiofsd
22-
# Extraction and initramfs tools are needed for some ephemeral boot images.
23-
Recommends: binutils
22+
# Initramfs tools are needed for some ephemeral boot images.
2423
Recommends: kernel-tools
25-
Recommends: android-tools
2624
# libvirt-client is optional but recommended for 'bcvk libvirt' commands
2725
Recommends: libvirt-client
2826

‎crates/bcvk-qemu/src/qemu.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ pub enum BootMode {
160160
/// Direct kernel boot (fast, testing-focused).
161161
/// Also used for UKI boot after extracting kernel/initramfs from UKI PE sections.
162162
///
163-
/// Note: For UKI images, we extract kernel/initramfs using objcopy rather than
163+
/// Note: For UKI images, we extract kernel/initramfs from PE sections rather than
164164
/// booting the UKI directly via OVMF. This allows us to append bcvk units to
165165
/// the initramfs for /etc overlay and /var setup. The tradeoff is that this
166166
/// breaks the UKI signature chain, so Secure Boot is not supported for

‎crates/integration-tests/src/tests/run_ephemeral.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ integration_test!(test_run_ephemeral_instancetype_invalid);
264264
/// Test that ephemeral VMs can boot from UKI-only images (no separate vmlinuz/initramfs)
265265
///
266266
/// This tests compatibility with bootc images that only ship a Unified Kernel Image,
267-
/// verifying that bcvk can extract kernel/initramfs from the UKI using objcopy.
267+
/// verifying that bcvk can extract kernel/initramfs from the UKI.
268268
fn test_run_ephemeral_uki_only() -> TestResult {
269269
let sh = shell()?;
270270
let base_image = get_test_image();

‎crates/kit/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ bcvk-qemu = { path = "../bcvk-qemu" }
5050
cap-std-ext = { workspace = true }
5151
bootc-mount = { package = "bootc-internal-mount", version = "1.16.14" }
5252
bootc-utils = { package = "bootc-internal-utils", version = "1.16.14" }
53+
composefs-boot = { version = "0.9.4", default-features = false }
5354
rustix = { version = "1", features = ["thread", "net", "fs", "pipe", "system", "process", "mount"] }
5455
vsock = "0.5"
5556
nix = { version = "0.31", features = ["socket"] }

‎crates/kit/src/run_ephemeral.rs‎

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,40 +1428,37 @@ pub(crate) async fn run_impl(opts: RunEphemeralOpts) -> Result<()> {
14281428

14291429
match kernel_info.kind {
14301430
crate::kernel::KernelKind::Uki => {
1431-
require_binary("objcopy")?;
14321431
debug!(
14331432
"Extracting kernel and initramfs from UKI: {:?}",
14341433
kernel_info.kernel_path
14351434
);
1436-
for (section, output) in [(".linux", kernel_mount), (".initrd", initramfs_mount)] {
1437-
Command::new("objcopy")
1438-
.args([
1439-
"--dump-section",
1440-
&format!("{section}={output}"),
1441-
kernel_info.kernel_path.as_str(),
1442-
])
1443-
.run_capture_stderr()
1444-
.map_err(|e| eyre!("Failed to extract {section} from UKI: {e}"))?;
1435+
let mut uki = File::open(&kernel_info.kernel_path).context("opening UKI")?;
1436+
for (section, target) in [(".linux", kernel_mount), (".initrd", initramfs_mount)] {
1437+
// The buffered parser starts at the current position in the image.
1438+
uki.rewind().context("rewinding UKI")?;
1439+
let contents = composefs_boot::uki::get_section_buffered(&mut uki, section)
1440+
.context(format!("extracting {section} from UKI"))?;
1441+
fs::write(target, contents).context(format!("writing {section} from UKI"))?;
1442+
debug!("Extracted {section} from UKI to {target}");
14451443
}
14461444
}
14471445
crate::kernel::KernelKind::AndroidBoot => {
1448-
require_binary("unpack_bootimg")?;
1449-
let unpacked = "/run/qemu/aboot-unpacked";
1450-
fs::create_dir_all(unpacked)?;
1451-
Command::new("unpack_bootimg")
1452-
.args([
1453-
"--boot_img",
1454-
kernel_info.kernel_path.as_str(),
1455-
"--out",
1456-
unpacked,
1457-
])
1458-
.stdout(Stdio::null())
1459-
.run_capture_stderr()
1460-
.map_err(|e| eyre!("Failed to unpack Android boot image: {e}"))?;
1461-
fs::rename(format!("{unpacked}/kernel"), kernel_mount)
1462-
.context("Getting kernel from Android boot image")?;
1463-
fs::rename(format!("{unpacked}/ramdisk"), initramfs_mount)
1464-
.context("Getting ramdisk from Android boot image")?;
1446+
use composefs_boot::android_boot::{AndroidBootImage, Component};
1447+
1448+
let mut image =
1449+
File::open(&kernel_info.kernel_path).context("opening Android boot image")?;
1450+
let header =
1451+
AndroidBootImage::parse(&mut image).context("parsing Android boot image")?;
1452+
for (component, name, target) in [
1453+
(Component::Kernel, "kernel", kernel_mount),
1454+
(Component::Ramdisk, "ramdisk", initramfs_mount),
1455+
] {
1456+
let contents = header
1457+
.component(&mut image, component)
1458+
.with_context(|| format!("extracting {name} from Android boot image"))?;
1459+
fs::write(target, contents)
1460+
.with_context(|| format!("writing {name} from Android boot image"))?;
1461+
}
14651462
}
14661463
crate::kernel::KernelKind::Traditional => {
14671464
let source_initramfs_path = kernel_info

‎docs/src/installation.md‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ For running bcvk:
2222
- virtiofsd
2323
- Podman
2424
- openssh-clients (for libvirt SSH operations)
25-
- binutils (for extracting UKIs, including ukiboot payloads)
26-
- android-tools (for extracting Android boot images)
2725
- kernel-tools (for initramfs bootconfig and ARM64 zstd EFI zboot kernels)
2826

2927
Optional:

‎docs/todo/ephemeral-uefi.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Tracking issue: https://github.com/bootc-dev/bcvk/issues/161
66

77
Ephemeral VMs currently use direct kernel boot via QEMU's `-kernel` and
88
`-initrd` options. For UKI-only images, we extract the kernel and initramfs
9-
from the UKI using `objcopy --dump-section`.
9+
from the UKI using the `composefs-boot` UKI parser.
1010

1111
### Where UKIs Live in bootc Images
1212

0 commit comments

Comments
 (0)