mirror of
https://github.com/edera-dev/sprout.git
synced 2025-12-19 13:50:16 +00:00
add initial bls support
This commit is contained in:
@@ -1,17 +1,21 @@
|
|||||||
use crate::config::EntryDeclaration;
|
use crate::config::EntryDeclaration;
|
||||||
use crate::context::SproutContext;
|
use crate::context::SproutContext;
|
||||||
|
use crate::generators::bls::BlsConfiguration;
|
||||||
use crate::generators::matrix::MatrixConfiguration;
|
use crate::generators::matrix::MatrixConfiguration;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub mod bls;
|
||||||
pub mod matrix;
|
pub mod matrix;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default, Clone)]
|
#[derive(Serialize, Deserialize, Default, Clone)]
|
||||||
pub struct GeneratorDeclaration {
|
pub struct GeneratorDeclaration {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub matrix: Option<MatrixConfiguration>,
|
pub matrix: Option<MatrixConfiguration>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub bls: Option<BlsConfiguration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate(
|
pub fn generate(
|
||||||
@@ -20,6 +24,8 @@ pub fn generate(
|
|||||||
) -> Result<Vec<(Rc<SproutContext>, EntryDeclaration)>> {
|
) -> Result<Vec<(Rc<SproutContext>, EntryDeclaration)>> {
|
||||||
if let Some(matrix) = &generator.matrix {
|
if let Some(matrix) = &generator.matrix {
|
||||||
matrix::generate(context, matrix)
|
matrix::generate(context, matrix)
|
||||||
|
} else if let Some(bls) = &generator.bls {
|
||||||
|
bls::generate(context, bls)
|
||||||
} else {
|
} else {
|
||||||
bail!("unknown generator configuration");
|
bail!("unknown generator configuration");
|
||||||
}
|
}
|
||||||
|
|||||||
82
src/generators/bls.rs
Normal file
82
src/generators/bls.rs
Normal file
@@ -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<SproutContext>,
|
||||||
|
bls: &BlsConfiguration,
|
||||||
|
) -> Result<Vec<(Rc<SproutContext>, 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::<SimpleFileSystem>(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)
|
||||||
|
}
|
||||||
91
src/generators/bls/entry.rs
Normal file
91
src/generators/bls/entry.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
use anyhow::{Error, Result};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct BlsEntry {
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub options: Option<String>,
|
||||||
|
pub linux: Option<String>,
|
||||||
|
pub initrd: Option<String>,
|
||||||
|
pub efi: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for BlsEntry {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(input: &str) -> Result<Self> {
|
||||||
|
let mut title: Option<String> = None;
|
||||||
|
let mut options: Option<String> = None;
|
||||||
|
let mut linux: Option<String> = None;
|
||||||
|
let mut initrd: Option<String> = None;
|
||||||
|
let mut efi: Option<String> = 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<String> {
|
||||||
|
self.linux
|
||||||
|
.clone()
|
||||||
|
.or(self.efi.clone())
|
||||||
|
.map(|path| path.replace("/", "\\").trim_start_matches("\\").to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initrd_path(&self) -> Option<String> {
|
||||||
|
self.initrd
|
||||||
|
.clone()
|
||||||
|
.map(|path| path.replace("/", "\\").trim_start_matches("\\").to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn options(&self) -> Option<String> {
|
||||||
|
self.options.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn title(&self) -> Option<String> {
|
||||||
|
self.title.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user