diff --git a/crates/boot/src/actions/chainload.rs b/crates/boot/src/actions/chainload.rs index 2d056a7..87cf877 100644 --- a/crates/boot/src/actions/chainload.rs +++ b/crates/boot/src/actions/chainload.rs @@ -12,8 +12,8 @@ use eficore::loader::{ImageLoadRequest, ImageLoader}; use eficore::media_loader::MediaLoaderHandle; use eficore::media_loader::constants::linux::LINUX_EFI_INITRD_MEDIA_GUID; use log::warn; -use uefi::CString16; use uefi::proto::loaded_image::LoadedImage; +use uefi::{CString16, Handle}; /// Read the initrd at the stamped `path` relative to the sprout image. /// Provides [None] if the path refers to the root of a filesystem rather than a file. @@ -34,6 +34,20 @@ pub fn read_initrd(context: &Rc, path: &str) -> Result); + +impl Drop for UnloadGuard { + fn drop(&mut self) { + if let Some(handle) = self.0.take() + && let Err(error) = uefi::boot::unload_image(handle) + { + warn!("unable to unload image: {}", error); + } + } +} + /// Executes the chainload action using the specified `configuration` inside the provided `context`. pub fn chainload(context: Rc, configuration: &ChainloadConfiguration) -> Result<()> { // The initrd can be provided as either a single initrd or a chain, but not both. @@ -58,6 +72,9 @@ pub fn chainload(context: Rc, configuration: &ChainloadConfigurat // It will determine if the image needs to be loaded via the shim or can be loaded directly. let image = ImageLoader::load(request)?; + // Unload the image if an error occurs before it is started. + let mut unload_guard = UnloadGuard(Some(*image.handle())); + // Stamp and combine the options to pass to the image. let options = combine_options(context.stamp_iter(configuration.options.iter())); @@ -139,6 +156,10 @@ pub fn chainload(context: Rc, configuration: &ChainloadConfigurat // This will perform operations like clearing the screen. before_handoff(&context).context("unable to execute before handoff hook")?; + // The image is about to be started, so it is no longer ours to unload. + // Once started, the image is unloaded by the firmware when it exits. + unload_guard.0 = None; + // Start the loaded image. // This call might return, or it may pass full control to another image that will never return. // Capture the result to ensure we can return an error if the image fails to start, but only