mirror of
https://github.com/edera-dev/sprout.git
synced 2026-11-04 18:42:21 +00:00
feat(boot): read only the boot partitions for BLS in strict mode
In strict mode autoconfigure reads BLS entries from the partition Sprout was loaded from and the extended boot loader partition of its disk, with one generator, and no longer makes entries from other disks. Other filesystems that have BLS are still not scanned for Linux kernels.
This commit is contained in:
1 parent
ca01353df4
commit
14463d5776
3 files changed
+128
-37
No files matched your search
@@ -1,8 +1,11 @@
|
|||||||
|
use alloc::boxed::Box;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use edera_sprout_config::RootConfiguration;
|
use edera_sprout_config::RootConfiguration;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
use uefi::Handle;
|
||||||
use uefi::fs::FileSystem;
|
use uefi::fs::FileSystem;
|
||||||
use uefi::proto::device_path::DevicePath;
|
use uefi::proto::device_path::DevicePath;
|
||||||
|
use uefi::proto::loaded_image::LoadedImage;
|
||||||
use uefi::proto::media::fs::SimpleFileSystem;
|
use uefi::proto::media::fs::SimpleFileSystem;
|
||||||
|
|
||||||
/// bls: autodetect and configure BLS-enabled filesystems.
|
/// bls: autodetect and configure BLS-enabled filesystems.
|
||||||
@@ -16,13 +19,47 @@ pub mod linux;
|
|||||||
/// windows: autodetect and configure Windows boot configurations.
|
/// windows: autodetect and configure Windows boot configurations.
|
||||||
pub mod windows;
|
pub mod windows;
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate a [RootConfiguration] based on the environment.
|
/// Generate a [RootConfiguration] based on the environment.
|
||||||
/// Intakes a `config` to use as the basis of the autoconfiguration.
|
/// Intakes a `config` to use as the basis of the autoconfiguration.
|
||||||
pub fn autoconfigure(config: &mut RootConfiguration) -> Result<()> {
|
/// 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<()> {
|
||||||
// Find all the filesystems that are on the system.
|
// Find all the filesystems that are on the system.
|
||||||
let filesystem_handles =
|
let filesystem_handles =
|
||||||
uefi::boot::find_handles::<SimpleFileSystem>().context("unable to scan filesystems")?;
|
uefi::boot::find_handles::<SimpleFileSystem>().context("unable to scan filesystems")?;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
// For each filesystem that was detected, scan it for supported autoconfig mechanisms.
|
// For each filesystem that was detected, scan it for supported autoconfig mechanisms.
|
||||||
for handle in filesystem_handles {
|
for handle in filesystem_handles {
|
||||||
// Acquire the device path root for the filesystem.
|
// Acquire the device path root for the filesystem.
|
||||||
@@ -51,15 +88,37 @@ pub fn autoconfigure(config: &mut RootConfiguration) -> Result<()> {
|
|||||||
// A scan that fails is skipped, as a filesystem that can't be read should not
|
// A scan that fails is skipped, as a filesystem that can't be read should not
|
||||||
// prevent autoconfiguring the other filesystems.
|
// prevent autoconfiguring the other filesystems.
|
||||||
|
|
||||||
// Scan the filesystem for BLS supported configurations.
|
// Scan the filesystem for BLS supported configurations. In strict mode, only the
|
||||||
let bls_found = match bls::scan(&mut filesystem, &root, config) {
|
// partitions that systemd-boot reads get entries, and they get a single generator.
|
||||||
Ok(found) => found,
|
// The other filesystems that have BLS are still not scanned for Linux configurations.
|
||||||
Err(error) => {
|
let is_source = Some(handle) == own_handle || Some(handle) == xbootldr_handle;
|
||||||
warn!(
|
if strict && Some(handle) == own_handle {
|
||||||
"unable to scan filesystem for bls configurations: {:#}",
|
own_root = Some(root.to_boxed());
|
||||||
error
|
}
|
||||||
);
|
let bls_found = if strict {
|
||||||
false
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -80,5 +139,14 @@ pub fn autoconfigure(config: &mut RootConfiguration) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In strict mode, one generator reads both partitions, with the root of Sprout's partition.
|
||||||
|
if strict
|
||||||
|
&& strict_found
|
||||||
|
&& let Some(root) = own_root
|
||||||
|
&& let Err(error) = bls::add_generator(config, &root, true)
|
||||||
|
{
|
||||||
|
warn!("unable to add the bls generator: {:#}", error);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -18,12 +18,8 @@ use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
|
|||||||
/// by the BLS generator to chainload entries.
|
/// by the BLS generator to chainload entries.
|
||||||
const BLS_CHAINLOAD_ACTION_PREFIX: &str = "bls-chainload-";
|
const BLS_CHAINLOAD_ACTION_PREFIX: &str = "bls-chainload-";
|
||||||
|
|
||||||
/// Scan the specified `filesystem` for BLS configurations.
|
/// Detect whether the `filesystem` has a BLS supported configuration.
|
||||||
pub fn scan(
|
pub fn detect(filesystem: &mut FileSystem) -> Result<bool> {
|
||||||
filesystem: &mut FileSystem,
|
|
||||||
root: &DevicePath,
|
|
||||||
config: &mut RootConfiguration,
|
|
||||||
) -> Result<bool> {
|
|
||||||
// BLS has a loader.conf file that can specify its own auto-entries mechanism.
|
// BLS has a loader.conf file that can specify its own auto-entries mechanism.
|
||||||
let bls_loader_conf_path = Path::new(cstr16!("\\loader\\loader.conf"));
|
let bls_loader_conf_path = Path::new(cstr16!("\\loader\\loader.conf"));
|
||||||
// BLS also has an entries directory that can specify explicit entries.
|
// BLS also has an entries directory that can specify explicit entries.
|
||||||
@@ -31,17 +27,6 @@ pub fn scan(
|
|||||||
// BLS Type #2 entries are unified kernel images in the EFI/Linux directory.
|
// BLS Type #2 entries are unified kernel images in the EFI/Linux directory.
|
||||||
let bls_uki_path = Path::new(cstr16!("\\EFI\\Linux"));
|
let bls_uki_path = Path::new(cstr16!("\\EFI\\Linux"));
|
||||||
|
|
||||||
// Convert the device path root to a string we can use in the configuration.
|
|
||||||
let mut root = root
|
|
||||||
.to_string16(DisplayOnly(false), AllowShortcuts(false))
|
|
||||||
.context("unable to convert device root to string")?
|
|
||||||
.to_string();
|
|
||||||
// Add a trailing forward-slash to the root to ensure the device root is completed.
|
|
||||||
root.push('/');
|
|
||||||
|
|
||||||
// Generate a unique hash of the root path.
|
|
||||||
let root_unique_hash = unique_hash(&root);
|
|
||||||
|
|
||||||
// Whether we have a loader.conf file.
|
// Whether we have a loader.conf file.
|
||||||
let has_loader_conf = filesystem
|
let has_loader_conf = filesystem
|
||||||
.try_exists(bls_loader_conf_path)
|
.try_exists(bls_loader_conf_path)
|
||||||
@@ -86,9 +71,26 @@ pub fn scan(
|
|||||||
// Detect if a BLS supported configuration is on this filesystem.
|
// Detect if a BLS supported configuration is on this filesystem.
|
||||||
// We check loader.conf, the entries directory and unified kernel images, as only one of
|
// We check loader.conf, the entries directory and unified kernel images, as only one of
|
||||||
// them is required.
|
// them is required.
|
||||||
if !(has_loader_conf || has_entries_dir || has_ukis) {
|
Ok(has_loader_conf || has_entries_dir || has_ukis)
|
||||||
return Ok(false);
|
}
|
||||||
}
|
|
||||||
|
/// Add the BLS generator and its action for the partition with the device path `root`.
|
||||||
|
/// In `strict` mode, the generator also reads the extended boot loader partition of the disk.
|
||||||
|
pub fn add_generator(
|
||||||
|
config: &mut RootConfiguration,
|
||||||
|
root: &DevicePath,
|
||||||
|
strict: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
// Convert the device path root to a string we can use in the configuration.
|
||||||
|
let mut root = root
|
||||||
|
.to_string16(DisplayOnly(false), AllowShortcuts(false))
|
||||||
|
.context("unable to convert device root to string")?
|
||||||
|
.to_string();
|
||||||
|
// Add a trailing forward-slash to the root to ensure the device root is completed.
|
||||||
|
root.push('/');
|
||||||
|
|
||||||
|
// Generate a unique hash of the root path.
|
||||||
|
let root_unique_hash = unique_hash(&root);
|
||||||
|
|
||||||
// Generate a unique name for the BLS chainload action.
|
// Generate a unique name for the BLS chainload action.
|
||||||
let chainload_action_name = format!("{}{}", BLS_CHAINLOAD_ACTION_PREFIX, root_unique_hash,);
|
let chainload_action_name = format!("{}{}", BLS_CHAINLOAD_ACTION_PREFIX, root_unique_hash,);
|
||||||
@@ -103,8 +105,9 @@ pub fn scan(
|
|||||||
path: format!("{}\\loader", root),
|
path: format!("{}\\loader", root),
|
||||||
uki_path: None,
|
uki_path: None,
|
||||||
// Every filesystem is scanned, including the Extended Boot Loader Partition, which
|
// Every filesystem is scanned, including the Extended Boot Loader Partition, which
|
||||||
// gets a generator of its own.
|
// gets a generator of its own, unless strict mode reads it with the generator of the
|
||||||
xbootldr: false,
|
// partition that Sprout was loaded from.
|
||||||
|
xbootldr: strict,
|
||||||
pin_names: true,
|
pin_names: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -121,15 +124,18 @@ pub fn scan(
|
|||||||
// BLS will provide these values to us.
|
// BLS will provide these values to us.
|
||||||
// Every initrd slot is chained in order. Unused slots stamp to the root of the
|
// Every initrd slot is chained in order. Unused slots stamp to the root of the
|
||||||
// filesystem, which the chainload action skips.
|
// filesystem, which the chainload action skips.
|
||||||
|
// The files of an entry are on the partition of the entry, which is the root of the
|
||||||
|
// generator for most entries, and the extended boot loader partition for some in strict mode.
|
||||||
|
let entry_root = if strict { "$entry-root" } else { &root };
|
||||||
let chainload = ChainloadConfiguration {
|
let chainload = ChainloadConfiguration {
|
||||||
path: format!("{}\\$chainload", root),
|
path: format!("{}\\$chainload", entry_root),
|
||||||
options: vec!["$options".to_string()],
|
options: vec!["$options".to_string()],
|
||||||
linux_initrd: None,
|
linux_initrd: None,
|
||||||
linux_initrd_chain: (0..BLS_INITRD_SLOTS)
|
linux_initrd_chain: (0..BLS_INITRD_SLOTS)
|
||||||
.map(|slot| format!("{}\\$initrd-{}", root, slot))
|
.map(|slot| format!("{}\\$initrd-{}", entry_root, slot))
|
||||||
.collect(),
|
.collect(),
|
||||||
// An unset devicetree stamps to the root of the filesystem, which the action skips.
|
// An unset devicetree stamps to the root of the filesystem, which the action skips.
|
||||||
devicetree: Some(format!("{}\\$devicetree", root)),
|
devicetree: Some(format!("{}\\$devicetree", entry_root)),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Insert the chainload action into the configuration.
|
// Insert the chainload action into the configuration.
|
||||||
@@ -141,6 +147,18 @@ pub fn scan(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// We had a BLS supported configuration, so return true.
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scan the specified `filesystem` for BLS configurations.
|
||||||
|
pub fn scan(
|
||||||
|
filesystem: &mut FileSystem,
|
||||||
|
root: &DevicePath,
|
||||||
|
config: &mut RootConfiguration,
|
||||||
|
) -> Result<bool> {
|
||||||
|
if !detect(filesystem)? {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
add_generator(config, root, false)?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
@@ -230,7 +230,12 @@ fn run(reboot_on_error: &mut bool) -> Result<()> {
|
|||||||
// If --autoconfigure is specified or the loaded configuration has autoconfigure enabled,
|
// If --autoconfigure is specified or the loaded configuration has autoconfigure enabled,
|
||||||
// trigger the autoconfiguration mechanism.
|
// trigger the autoconfiguration mechanism.
|
||||||
if context.root().options().autoconfigure || config.options.autoconfigure {
|
if context.root().options().autoconfigure || config.options.autoconfigure {
|
||||||
autoconfigure::autoconfigure(&mut config).context("unable to autoconfigure")?;
|
autoconfigure::autoconfigure(
|
||||||
|
&mut config,
|
||||||
|
context.root().options().bls_strict_mode,
|
||||||
|
context.root().loaded_image_path()?,
|
||||||
|
)
|
||||||
|
.context("unable to autoconfigure")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unload the context so that it can be modified.
|
// Unload the context so that it can be modified.
|
||||||
|
|||||||
Reference in new issue
Block a user