2026-10-04 10:18:54 -07:00
|
|
|
use alloc::boxed::Box;
|
2025-10-27 02:38:40 -04:00
|
|
|
use anyhow::{Context, Result};
|
2025-11-02 23:28:31 -05:00
|
|
|
use edera_sprout_config::RootConfiguration;
|
2026-09-26 18:19:55 -07:00
|
|
|
use log::warn;
|
2026-10-04 10:18:54 -07:00
|
|
|
use uefi::Handle;
|
2025-10-27 04:28:25 -04:00
|
|
|
use uefi::fs::FileSystem;
|
2025-10-27 02:38:40 -04:00
|
|
|
use uefi::proto::device_path::DevicePath;
|
2026-10-04 10:18:54 -07:00
|
|
|
use uefi::proto::loaded_image::LoadedImage;
|
2025-10-27 02:38:40 -04:00
|
|
|
use uefi::proto::media::fs::SimpleFileSystem;
|
|
|
|
|
|
2025-10-27 15:41:29 -04:00
|
|
|
/// bls: autodetect and configure BLS-enabled filesystems.
|
2025-10-27 04:28:25 -04:00
|
|
|
pub mod bls;
|
2025-10-27 02:38:40 -04:00
|
|
|
|
2025-10-27 15:41:29 -04:00
|
|
|
/// linux: autodetect and configure Linux kernels.
|
|
|
|
|
/// This autoconfiguration module should not be activated
|
|
|
|
|
/// on BLS-enabled filesystems as it may make duplicate entries.
|
|
|
|
|
pub mod linux;
|
|
|
|
|
|
2025-10-27 19:47:21 -04:00
|
|
|
/// windows: autodetect and configure Windows boot configurations.
|
|
|
|
|
pub mod windows;
|
|
|
|
|
|
2026-10-04 10:18:54 -07:00
|
|
|
/// Find the filesystems that systemd-boot reads BLS entries from: the partition that Sprout
|
|
|
|
|
/// was loaded from, and the extended boot loader partition of the same disk.
|
|
|
|
|
fn strict_bls_sources(loaded_image_path: &DevicePath) -> (Option<Handle>, Option<Handle>) {
|
|
|
|
|
let own = eficore::handle::open_shared::<LoadedImage>(uefi::boot::image_handle())
|
|
|
|
|
.ok()
|
|
|
|
|
.and_then(|image| image.device());
|
|
|
|
|
let xbootldr = match eficore::xbootldr::find_xbootldr(loaded_image_path) {
|
|
|
|
|
Ok(Some(path)) => uefi::boot::locate_device_path::<SimpleFileSystem>(&mut &*path).ok(),
|
|
|
|
|
Ok(None) => None,
|
|
|
|
|
Err(error) => {
|
|
|
|
|
warn!("unable to find the xbootldr partition: {:#}", error);
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
(own, xbootldr)
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-27 02:38:40 -04:00
|
|
|
/// Generate a [RootConfiguration] based on the environment.
|
|
|
|
|
/// Intakes a `config` to use as the basis of the autoconfiguration.
|
2026-10-04 10:18:54 -07:00
|
|
|
/// With `strict`, only the partition Sprout was loaded from and the extended boot loader
|
|
|
|
|
/// partition of its disk are read for BLS entries, as one generator, like systemd-boot does.
|
|
|
|
|
pub fn autoconfigure(
|
|
|
|
|
config: &mut RootConfiguration,
|
|
|
|
|
strict: bool,
|
|
|
|
|
loaded_image_path: &DevicePath,
|
|
|
|
|
) -> Result<()> {
|
2025-10-27 02:38:40 -04:00
|
|
|
// Find all the filesystems that are on the system.
|
|
|
|
|
let filesystem_handles =
|
|
|
|
|
uefi::boot::find_handles::<SimpleFileSystem>().context("unable to scan filesystems")?;
|
|
|
|
|
|
2026-10-04 10:18:54 -07:00
|
|
|
// The partitions that BLS entries are read from in strict mode.
|
|
|
|
|
let (own_handle, xbootldr_handle) = if strict {
|
|
|
|
|
strict_bls_sources(loaded_image_path)
|
|
|
|
|
} else {
|
|
|
|
|
(None, None)
|
|
|
|
|
};
|
|
|
|
|
// The device path of the partition of Sprout, which names the strict generator.
|
|
|
|
|
let mut own_root: Option<Box<DevicePath>> = None;
|
|
|
|
|
// Whether either of the partitions has BLS entries, in strict mode.
|
|
|
|
|
let mut strict_found = false;
|
|
|
|
|
|
2025-10-27 02:38:40 -04:00
|
|
|
// For each filesystem that was detected, scan it for supported autoconfig mechanisms.
|
|
|
|
|
for handle in filesystem_handles {
|
|
|
|
|
// Acquire the device path root for the filesystem.
|
2026-09-26 18:19:55 -07:00
|
|
|
// A filesystem that can't be opened is skipped, as it should not prevent
|
|
|
|
|
// autoconfiguring the other filesystems.
|
|
|
|
|
let root = match uefi::boot::open_protocol_exclusive::<DevicePath>(handle) {
|
|
|
|
|
Ok(root) => root.to_boxed(),
|
|
|
|
|
Err(error) => {
|
|
|
|
|
warn!("skipping filesystem, unable to get its root: {}", error);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-10-27 02:38:40 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Open the filesystem that was detected.
|
2026-09-26 18:19:55 -07:00
|
|
|
let filesystem = match uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(handle) {
|
|
|
|
|
Ok(filesystem) => filesystem,
|
|
|
|
|
Err(error) => {
|
|
|
|
|
warn!("skipping filesystem, unable to open it: {}", error);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-10-27 02:38:40 -04:00
|
|
|
|
|
|
|
|
// Trade the filesystem protocol for the uefi filesystem helper.
|
|
|
|
|
let mut filesystem = FileSystem::new(filesystem);
|
|
|
|
|
|
2026-09-26 20:17:50 -07:00
|
|
|
// A scan that fails is skipped, as a filesystem that can't be read should not
|
|
|
|
|
// prevent autoconfiguring the other filesystems.
|
|
|
|
|
|
2026-10-04 10:18:54 -07:00
|
|
|
// Scan the filesystem for BLS supported configurations. In strict mode, only the
|
|
|
|
|
// partitions that systemd-boot reads get entries, and they get a single generator.
|
|
|
|
|
// The other filesystems that have BLS are still not scanned for Linux configurations.
|
|
|
|
|
let is_source = Some(handle) == own_handle || Some(handle) == xbootldr_handle;
|
|
|
|
|
if strict && Some(handle) == own_handle {
|
|
|
|
|
own_root = Some(root.to_boxed());
|
|
|
|
|
}
|
|
|
|
|
let bls_found = if strict {
|
|
|
|
|
match bls::detect(&mut filesystem) {
|
|
|
|
|
Ok(found) => {
|
|
|
|
|
strict_found |= found && is_source;
|
|
|
|
|
found
|
|
|
|
|
}
|
|
|
|
|
Err(error) => {
|
|
|
|
|
warn!(
|
|
|
|
|
"unable to scan filesystem for bls configurations: {:#}",
|
|
|
|
|
error
|
|
|
|
|
);
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
match bls::scan(&mut filesystem, &root, config) {
|
|
|
|
|
Ok(found) => found,
|
|
|
|
|
Err(error) => {
|
|
|
|
|
warn!(
|
|
|
|
|
"unable to scan filesystem for bls configurations: {:#}",
|
|
|
|
|
error
|
|
|
|
|
);
|
|
|
|
|
false
|
|
|
|
|
}
|
2026-09-26 20:17:50 -07:00
|
|
|
}
|
|
|
|
|
};
|
2025-10-27 15:41:29 -04:00
|
|
|
|
|
|
|
|
// If BLS was not found, scan for Linux configurations.
|
2026-09-26 20:17:50 -07:00
|
|
|
if !bls_found && let Err(error) = linux::scan(&mut filesystem, &root, config) {
|
|
|
|
|
warn!(
|
|
|
|
|
"unable to scan filesystem for linux configurations: {:#}",
|
|
|
|
|
error
|
|
|
|
|
);
|
2025-10-27 15:41:29 -04:00
|
|
|
}
|
2025-10-27 19:47:21 -04:00
|
|
|
|
|
|
|
|
// Always look for Windows configurations.
|
2026-09-26 20:17:50 -07:00
|
|
|
if let Err(error) = windows::scan(&mut filesystem, &root, config) {
|
|
|
|
|
warn!(
|
|
|
|
|
"unable to scan filesystem for windows configurations: {:#}",
|
|
|
|
|
error
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-10-27 02:38:40 -04:00
|
|
|
}
|
|
|
|
|
|
2026-10-04 10:18:54 -07:00
|
|
|
// In strict mode, one generator reads both partitions, with the root of Sprout's partition.
|
2026-10-04 10:41:47 -07:00
|
|
|
if strict && strict_found {
|
|
|
|
|
match own_root {
|
|
|
|
|
Some(root) => {
|
|
|
|
|
if let Err(error) = bls::add_generator(config, &root, true) {
|
|
|
|
|
warn!("unable to add the bls generator: {:#}", error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None => warn!(
|
|
|
|
|
"found BLS entries, but not the partition that Sprout was loaded from, \
|
|
|
|
|
so none are used in strict mode"
|
|
|
|
|
),
|
|
|
|
|
}
|
2026-10-04 10:18:54 -07:00
|
|
|
}
|
|
|
|
|
|
2025-10-27 02:38:40 -04:00
|
|
|
Ok(())
|
|
|
|
|
}
|