diff --git a/crates/boot/src/actions/chainload.rs b/crates/boot/src/actions/chainload.rs index f89a5b3..1753384 100644 --- a/crates/boot/src/actions/chainload.rs +++ b/crates/boot/src/actions/chainload.rs @@ -5,7 +5,7 @@ use alloc::rc::Rc; use alloc::vec::Vec; use anyhow::{Context, Result, bail}; use edera_sprout_config::actions::chainload::ChainloadConfiguration; -use edera_sprout_parsing::{combine_options, empty_is_none}; +use edera_sprout_parsing::{append_initrd, combine_options, empty_is_none}; use eficore::bootloader_interface::BootloaderInterface; use eficore::loader::source::ImageSource; use eficore::loader::{ImageLoadRequest, ImageLoader}; @@ -112,7 +112,7 @@ pub fn chainload(context: Rc, configuration: &ChainloadConfigurat continue; }; match initrd.as_mut() { - Some(initrd) => initrd.extend_from_slice(&content), + Some(initrd) => append_initrd(initrd, &content), None => initrd = Some(content), } } diff --git a/crates/boot/src/actions/edera.rs b/crates/boot/src/actions/edera.rs index 2fe8abf..d031d8d 100644 --- a/crates/boot/src/actions/edera.rs +++ b/crates/boot/src/actions/edera.rs @@ -5,7 +5,7 @@ use alloc::{format, vec}; use anyhow::{Context, Result, bail}; use edera_sprout_config::actions::chainload::ChainloadConfiguration; use edera_sprout_config::actions::edera::EderaConfiguration; -use edera_sprout_parsing::{build_xen_config, combine_options, empty_is_none}; +use edera_sprout_parsing::{append_initrd, build_xen_config, combine_options, empty_is_none}; use eficore::media_loader::{ MediaLoaderHandle, constants::xen::{ @@ -94,7 +94,10 @@ pub fn edera(context: Rc, configuration: &EderaConfiguration) -> .chain(configuration.initrd_chain.iter()) { if let Some(p) = empty_is_none(Some(p)) { - initrd_bytes.extend(read_loader_payload(&context, "initrd", p)?); + append_initrd( + &mut initrd_bytes, + &read_loader_payload(&context, "initrd", p)?, + ); } } PlatformTpm::log_event( diff --git a/crates/parsing/src/lib.rs b/crates/parsing/src/lib.rs index 2ac51b8..c93380d 100644 --- a/crates/parsing/src/lib.rs +++ b/crates/parsing/src/lib.rs @@ -90,6 +90,18 @@ pub fn combine_options>(options: impl Iterator) -> Strin .join(" ") } +/// The alignment of each initrd when initrds are concatenated. +const INITRD_ALIGNMENT: usize = 4; + +/// Append the `initrd` to the concatenated `initrds`, padding with zeros so that it starts +/// on a 4-byte boundary. Linux only finds a cpio archive at a 4-byte aligned offset when +/// unpacking concatenated initrds, as GRUB and systemd-boot pad them the same way. +pub fn append_initrd(initrds: &mut Vec, initrd: &[u8]) { + let padded = initrds.len().next_multiple_of(INITRD_ALIGNMENT); + initrds.resize(padded, 0); + initrds.extend_from_slice(initrd); +} + /// Produce a unique hash for the input. /// This uses SHA-256, which is unique enough but relatively short. pub fn unique_hash(input: &str) -> String { @@ -358,6 +370,24 @@ mod tests { assert!(config.ends_with('\n')); } + #[test] + fn append_initrd_pads_to_four_bytes() { + let mut initrds = Vec::new(); + append_initrd(&mut initrds, b"abcde"); + assert_eq!(initrds, b"abcde"); + append_initrd(&mut initrds, b"fg"); + assert_eq!(initrds, b"abcde\0\0\0fg"); + append_initrd(&mut initrds, b"h"); + assert_eq!(initrds, b"abcde\0\0\0fg\0\0h"); + } + + #[test] + fn append_initrd_aligned_needs_no_padding() { + let mut initrds = b"abcd".to_vec(); + append_initrd(&mut initrds, b"efgh"); + assert_eq!(initrds, b"abcdefgh"); + } + #[test] fn kernel_prefix_exact_match() { assert_eq!(