From c6f7412df0996ec10dabfbda0e508df33ddd1435 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Mon, 13 Oct 2025 01:54:03 -0700 Subject: [PATCH] add initial bls support --- src/generators.rs | 6 +++ src/generators/bls.rs | 82 +++++++++++++++++++++++++++++++++ src/generators/bls/entry.rs | 91 +++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 src/generators/bls.rs create mode 100644 src/generators/bls/entry.rs diff --git a/src/generators.rs b/src/generators.rs index 478e409..4770468 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1,17 +1,21 @@ use crate::config::EntryDeclaration; use crate::context::SproutContext; +use crate::generators::bls::BlsConfiguration; use crate::generators::matrix::MatrixConfiguration; use anyhow::Result; use anyhow::bail; use serde::{Deserialize, Serialize}; use std::rc::Rc; +pub mod bls; pub mod matrix; #[derive(Serialize, Deserialize, Default, Clone)] pub struct GeneratorDeclaration { #[serde(default)] pub matrix: Option, + #[serde(default)] + pub bls: Option, } pub fn generate( @@ -20,6 +24,8 @@ pub fn generate( ) -> Result, EntryDeclaration)>> { if let Some(matrix) = &generator.matrix { matrix::generate(context, matrix) + } else if let Some(bls) = &generator.bls { + bls::generate(context, bls) } else { bail!("unknown generator configuration"); } diff --git a/src/generators/bls.rs b/src/generators/bls.rs new file mode 100644 index 0000000..03392e2 --- /dev/null +++ b/src/generators/bls.rs @@ -0,0 +1,82 @@ +mod entry; + +use crate::config::EntryDeclaration; +use crate::context::SproutContext; +use crate::generators::bls::entry::BlsEntry; +use crate::utils; +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::rc::Rc; +use std::str::FromStr; +use uefi::CString16; +use uefi::fs::{FileSystem, Path}; +use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly}; +use uefi::proto::media::fs::SimpleFileSystem; + +#[derive(Serialize, Deserialize, Default, Clone)] +pub struct BlsConfiguration { + pub entry: EntryDeclaration, + #[serde(default = "default_bls_path")] + pub path: String, +} + +fn default_bls_path() -> String { + "\\loader\\entries".to_string() +} + +pub fn generate( + context: Rc, + bls: &BlsConfiguration, +) -> Result, EntryDeclaration)>> { + let mut entries = Vec::new(); + + let path = context.stamp(&bls.path); + let resolved = utils::resolve_path(context.root().loaded_image_path()?, &path) + .context("failed to resolve bls path")?; + let fs = uefi::boot::open_protocol_exclusive::(resolved.filesystem_handle) + .context("failed to open bls filesystem")?; + let mut fs = FileSystem::new(fs); + let sub_text_path = resolved + .sub_path + .to_string(DisplayOnly(false), AllowShortcuts(false)) + .context("failed to convert subpath to string")?; + let entries_path = Path::new(&sub_text_path); + + let entries_iter = fs + .read_dir(entries_path) + .context("failed to read bls entries")?; + + for entry in entries_iter { + let entry = entry?; + if !entry.is_regular_file() { + continue; + } + let name = entry.file_name().to_string(); + if !name.ends_with(".conf") { + continue; + } + + let full_entry_path = CString16::try_from(format!("{}\\{}", sub_text_path, name).as_str()) + .context("failed to construct full entry path")?; + let full_entry_path = Path::new(&full_entry_path); + let content = fs + .read(full_entry_path) + .context("failed to read bls file")?; + let content = String::from_utf8(content).context("failed to read bls entry as utf8")?; + let entry = BlsEntry::from_str(&content).context("failed to parse bls entry")?; + + if !entry.is_valid() { + continue; + } + + let mut context = context.fork(); + context.set("title", entry.title().unwrap_or(name)); + context.set("chainload", entry.chainload_path().unwrap_or_default()); + context.set("options", entry.options().unwrap_or_default()); + context.set("initrd", entry.initrd_path().unwrap_or_default()); + + entries.push((context.freeze(), bls.entry.clone())); + } + + Ok(entries) +} diff --git a/src/generators/bls/entry.rs b/src/generators/bls/entry.rs new file mode 100644 index 0000000..2cc704a --- /dev/null +++ b/src/generators/bls/entry.rs @@ -0,0 +1,91 @@ +use anyhow::{Error, Result}; +use std::str::FromStr; + +#[derive(Default, Debug, Clone)] +pub struct BlsEntry { + pub title: Option, + pub options: Option, + pub linux: Option, + pub initrd: Option, + pub efi: Option, +} + +impl FromStr for BlsEntry { + type Err = Error; + + fn from_str(input: &str) -> Result { + let mut title: Option = None; + let mut options: Option = None; + let mut linux: Option = None; + let mut initrd: Option = None; + let mut efi: Option = None; + + for line in input.lines() { + let line = line.trim(); + let Some((key, value)) = line.split_once(" ") else { + continue; + }; + + match key { + "title" => { + title = Some(value.trim().to_string()); + } + + "options" => { + options = Some(value.trim().to_string()); + } + + "linux" => { + linux = Some(value.trim().to_string()); + } + + "initrd" => { + initrd = Some(value.trim().to_string()); + } + + "efi" => { + efi = Some(value.trim().to_string()); + } + + _ => { + continue; + } + } + } + + Ok(BlsEntry { + title, + options, + linux, + initrd, + efi, + }) + } +} + +impl BlsEntry { + pub fn is_valid(&self) -> bool { + self.linux.is_some() || self.efi.is_some() + } + + pub fn chainload_path(&self) -> Option { + self.linux + .clone() + .or(self.efi.clone()) + .map(|path| path.replace("/", "\\").trim_start_matches("\\").to_string()) + } + + pub fn initrd_path(&self) -> Option { + self.initrd + .clone() + .map(|path| path.replace("/", "\\").trim_start_matches("\\").to_string()) + } + + pub fn options(&self) -> Option { + self.options.clone() + } + + pub fn title(&self) -> Option { + self.title.clone() + } +}