diff --git a/Cargo.lock b/Cargo.lock index d13a0f6..7c95f59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,15 +70,22 @@ version = "0.0.17" dependencies = [ "anyhow", "bitflags", + "edera-sprout-config", "hex", "log", - "serde", "sha2", "toml", "uefi", "uefi-raw", ] +[[package]] +name = "edera-sprout-config" +version = "0.0.17" +dependencies = [ + "serde", +] + [[package]] name = "equivalent" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index daad5b4..9a26321 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "crates/config", "crates/sprout", ] resolver = "3" diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml new file mode 100644 index 0000000..412e28c --- /dev/null +++ b/crates/config/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "edera-sprout-config" +description = "Sprout Configuration" +license.workspace = true +version.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true + +[dependencies.serde] +workspace = true +features = ["derive"] + +[lib] +name = "edera_sprout_config" diff --git a/crates/config/src/actions.rs b/crates/config/src/actions.rs new file mode 100644 index 0000000..5dcf969 --- /dev/null +++ b/crates/config/src/actions.rs @@ -0,0 +1,32 @@ +use serde::{Deserialize, Serialize}; + +/// Configuration for the chainload action. +pub mod chainload; + +/// Configuration for the edera action. +pub mod edera; + +/// Configuration for the print action. +pub mod print; + +/// Declares an action that sprout can execute. +/// Actions allow configuring sprout's internal runtime mechanisms with values +/// that you can specify via other concepts. +/// +/// Actions are the main work that Sprout gets done, like booting Linux. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct ActionDeclaration { + /// Chainload to another EFI application. + /// This allows you to load any EFI application, either to boot an operating system + /// or to perform more EFI actions and return to sprout. + #[serde(default)] + pub chainload: Option, + /// Print a string to the EFI console. + #[serde(default)] + pub print: Option, + /// Boot the Edera hypervisor and the root operating system. + /// This action is an extension on top of the Xen EFI stub that + /// is specific to Edera. + #[serde(default, rename = "edera")] + pub edera: Option, +} diff --git a/crates/config/src/actions/chainload.rs b/crates/config/src/actions/chainload.rs new file mode 100644 index 0000000..44afd99 --- /dev/null +++ b/crates/config/src/actions/chainload.rs @@ -0,0 +1,19 @@ +use serde::{Deserialize, Serialize}; + +/// The configuration of the chainload action. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct ChainloadConfiguration { + /// The path to the image to chainload. + /// This can be a Linux EFI stub (vmlinuz usually) or a standard EFI executable. + pub path: String, + /// The options to pass to the image. + /// The options are concatenated by a space and then passed to the EFI application. + #[serde(default)] + pub options: Vec, + /// An optional path to a Linux initrd. + /// This uses the [LINUX_EFI_INITRD_MEDIA_GUID] mechanism to load the initrd into the EFI stack. + /// For Linux, you can also use initrd=\path\to\initrd as an option, but this option is + /// generally better and safer as it can support additional load options in the future. + #[serde(default, rename = "linux-initrd")] + pub linux_initrd: Option, +} diff --git a/crates/config/src/actions/edera.rs b/crates/config/src/actions/edera.rs new file mode 100644 index 0000000..f4a04e8 --- /dev/null +++ b/crates/config/src/actions/edera.rs @@ -0,0 +1,21 @@ +use serde::{Deserialize, Serialize}; + +/// The configuration of the edera action which boots the Edera hypervisor. +/// Edera is based on Xen but modified significantly with a Rust stack. +/// Sprout is a component of the Edera stack and provides the boot functionality of Xen. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct EderaConfiguration { + /// The path to the Xen hypervisor EFI image. + pub xen: String, + /// The path to the kernel to boot for dom0. + pub kernel: String, + /// The path to the initrd to load for dom0. + #[serde(default)] + pub initrd: Option, + /// The options to pass to the kernel. + #[serde(default, rename = "kernel-options")] + pub kernel_options: Vec, + /// The options to pass to the Xen hypervisor. + #[serde(default, rename = "xen-options")] + pub xen_options: Vec, +} diff --git a/crates/config/src/actions/print.rs b/crates/config/src/actions/print.rs new file mode 100644 index 0000000..b9f9c5a --- /dev/null +++ b/crates/config/src/actions/print.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +/// The configuration of the print action. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct PrintConfiguration { + /// The text to print to the console. + #[serde(default)] + pub text: String, +} diff --git a/crates/config/src/drivers.rs b/crates/config/src/drivers.rs new file mode 100644 index 0000000..dd05ad2 --- /dev/null +++ b/crates/config/src/drivers.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +/// Declares a driver configuration. +/// Drivers allow extending the functionality of Sprout. +/// Drivers are loaded at runtime and can provide extra functionality like filesystem support. +/// Drivers are loaded by their name, which is used to reference them in other concepts. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct DriverDeclaration { + /// The filesystem path to the driver. + /// This file should be an EFI executable that can be located and executed. + pub path: String, +} diff --git a/crates/config/src/entries.rs b/crates/config/src/entries.rs new file mode 100644 index 0000000..b9950e7 --- /dev/null +++ b/crates/config/src/entries.rs @@ -0,0 +1,19 @@ +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// Declares a boot entry to display in the boot menu. +/// +/// Entries are the user-facing concept of Sprout, making it possible +/// to run a set of actions with a specific context. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct EntryDeclaration { + /// The title of the entry which will be display in the boot menu. + /// This is the pre-stamped value. + pub title: String, + /// The actions to run when the entry is selected. + #[serde(default)] + pub actions: Vec, + /// The values to insert into the context when the entry is selected. + #[serde(default)] + pub values: BTreeMap, +} diff --git a/crates/config/src/extractors.rs b/crates/config/src/extractors.rs new file mode 100644 index 0000000..3eec95e --- /dev/null +++ b/crates/config/src/extractors.rs @@ -0,0 +1,18 @@ +use crate::extractors::filesystem_device_match::FilesystemDeviceMatchExtractor; +use serde::{Deserialize, Serialize}; + +/// Configuration for the filesystem-device-match extractor. +pub mod filesystem_device_match; + +/// Declares an extractor configuration. +/// Extractors allow calculating values at runtime +/// using built-in sprout modules. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct ExtractorDeclaration { + /// The filesystem device match extractor. + /// This extractor finds a filesystem using some search criteria and returns + /// the device root path that can concatenated with subpaths to access files + /// on a particular filesystem. + #[serde(default, rename = "filesystem-device-match")] + pub filesystem_device_match: Option, +} diff --git a/crates/config/src/extractors/filesystem_device_match.rs b/crates/config/src/extractors/filesystem_device_match.rs new file mode 100644 index 0000000..c7f2c30 --- /dev/null +++ b/crates/config/src/extractors/filesystem_device_match.rs @@ -0,0 +1,29 @@ +use serde::{Deserialize, Serialize}; + +/// The filesystem device match extractor. +/// This extractor finds a filesystem using some search criteria and returns +/// the device root path that can concatenated with subpaths to access files +/// on a particular filesystem. +/// The fallback value can be used to provide a value if no match is found. +/// +/// This extractor requires all the criteria to match. If no criteria is provided, +/// an error is returned. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct FilesystemDeviceMatchExtractor { + /// Matches a filesystem that has the specified label. + #[serde(default, rename = "has-label")] + pub has_label: Option, + /// Matches a filesystem that has the specified item. + /// An item is either a directory or file. + #[serde(default, rename = "has-item")] + pub has_item: Option, + /// Matches a filesystem that has the specified partition UUID. + #[serde(default, rename = "has-partition-uuid")] + pub has_partition_uuid: Option, + /// Matches a filesystem that has the specified partition type UUID. + #[serde(default, rename = "has-partition-type-uuid")] + pub has_partition_type_uuid: Option, + /// The fallback value to use if no filesystem matches the criteria. + #[serde(default)] + pub fallback: Option, +} diff --git a/crates/config/src/generators.rs b/crates/config/src/generators.rs new file mode 100644 index 0000000..1cb0930 --- /dev/null +++ b/crates/config/src/generators.rs @@ -0,0 +1,40 @@ +use crate::generators::bls::BlsConfiguration; +use crate::generators::list::ListConfiguration; +use crate::generators::matrix::MatrixConfiguration; +use serde::{Deserialize, Serialize}; + +/// Configuration for the BLS generator. +pub mod bls; + +/// Configuration for the list generator. +pub mod list; + +/// Configuration for the matrix generator. +pub mod matrix; + +/// Declares a generator configuration. +/// Generators allow generating entries at runtime based on a set of data. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct GeneratorDeclaration { + /// Matrix generator configuration. + /// Matrix allows you to specify multiple value-key values as arrays. + /// This allows multiplying the number of entries by any number of possible + /// configuration options. For example, + /// data.x = ["a", "b"] + /// data.y = ["c", "d"] + /// would generate an entry for each of these combinations: + /// x = a, y = c + /// x = a, y = d + /// x = b, y = c + /// x = b, y = d + #[serde(default)] + pub matrix: Option, + /// BLS generator configuration. + /// BLS allows you to pass a filesystem path that contains a set of BLS entries. + /// It will generate a sprout entry for every supported BLS entry. + #[serde(default)] + pub bls: Option, + /// List generator configuration. + /// Allows you to specify a list of values to generate an entry from. + pub list: Option, +} diff --git a/crates/config/src/generators/bls.rs b/crates/config/src/generators/bls.rs new file mode 100644 index 0000000..1f8b671 --- /dev/null +++ b/crates/config/src/generators/bls.rs @@ -0,0 +1,21 @@ +use crate::entries::EntryDeclaration; +use serde::{Deserialize, Serialize}; + +/// The default path to the BLS directory. +const BLS_TEMPLATE_PATH: &str = "\\loader"; + +/// The configuration of the BLS generator. +/// The BLS uses the Bootloader Specification to produce +/// entries from an input template. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct BlsConfiguration { + /// The entry to use for as a template. + pub entry: EntryDeclaration, + /// The path to the BLS directory. + #[serde(default = "default_bls_path")] + pub path: String, +} + +fn default_bls_path() -> String { + BLS_TEMPLATE_PATH.to_string() +} diff --git a/crates/config/src/generators/list.rs b/crates/config/src/generators/list.rs new file mode 100644 index 0000000..dd05047 --- /dev/null +++ b/crates/config/src/generators/list.rs @@ -0,0 +1,16 @@ +use crate::entries::EntryDeclaration; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// List generator configuration. +/// The list generator produces multiple entries based +/// on a set of input maps. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct ListConfiguration { + /// The template entry to use for each generated entry. + #[serde(default)] + pub entry: EntryDeclaration, + /// The values to use as the input for the matrix. + #[serde(default)] + pub values: Vec>, +} diff --git a/crates/config/src/generators/matrix.rs b/crates/config/src/generators/matrix.rs new file mode 100644 index 0000000..e382bfa --- /dev/null +++ b/crates/config/src/generators/matrix.rs @@ -0,0 +1,16 @@ +use crate::entries::EntryDeclaration; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// Matrix generator configuration. +/// The matrix generator produces multiple entries based +/// on input values multiplicatively. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct MatrixConfiguration { + /// The template entry to use for each generated entry. + #[serde(default)] + pub entry: EntryDeclaration, + /// The values to use as the input for the matrix. + #[serde(default)] + pub values: BTreeMap>, +} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs new file mode 100644 index 0000000..7176527 --- /dev/null +++ b/crates/config/src/lib.rs @@ -0,0 +1,97 @@ +//! Sprout configuration descriptions. +//! This crate provides all the configuration structures for Sprout. + +use crate::actions::ActionDeclaration; +use crate::drivers::DriverDeclaration; +use crate::entries::EntryDeclaration; +use crate::extractors::ExtractorDeclaration; +use crate::generators::GeneratorDeclaration; +use crate::phases::PhasesConfiguration; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +pub mod actions; +pub mod drivers; +pub mod entries; +pub mod extractors; +pub mod generators; +pub mod phases; + +/// This is the latest version of the sprout configuration format. +/// This must be incremented when the configuration breaks compatibility. +pub const LATEST_VERSION: u32 = 1; + +/// The default timeout for the boot menu in seconds. +pub const DEFAULT_MENU_TIMEOUT_SECONDS: u64 = 10; + +/// The Sprout configuration format. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct RootConfiguration { + /// The version of the configuration. This should always be declared + /// and be the latest version that is supported. If not specified, it is assumed + /// the configuration is the latest version. + #[serde(default = "latest_version")] + pub version: u32, + /// Default options for Sprout. + #[serde(default)] + pub options: OptionsConfiguration, + /// Values to be inserted into the root sprout context. + #[serde(default)] + pub values: BTreeMap, + /// Drivers to load. + /// These drivers provide extra functionality like filesystem support to Sprout. + /// Each driver has a name which uniquely identifies it inside Sprout. + #[serde(default)] + pub drivers: BTreeMap, + /// Declares the extractors that add values to the sprout context that are calculated + /// at runtime. Each extractor has a name which corresponds to the value it will set + /// inside the sprout context. + #[serde(default)] + pub extractors: BTreeMap, + /// Declares the actions that can execute operations for sprout. + /// Actions are executable modules in sprout that take in specific structured values. + /// Actions are responsible for ensuring that passed strings are stamped to replace values + /// at runtime. + /// Each action has a name that can be referenced by other base concepts like entries. + #[serde(default)] + pub actions: BTreeMap, + /// Declares the entries that are displayed on the boot menu. These entries are static + /// but can still use values from the sprout context. + #[serde(default)] + pub entries: BTreeMap, + /// Declares the generators that are used to generate entries at runtime. + /// Each generator has its own logic for generating entries, but generally they intake + /// a template entry and stamp that template entry over some values determined at runtime. + /// Each generator has an associated name used to differentiate it across sprout. + #[serde(default)] + pub generators: BTreeMap, + /// Configures the various phases of sprout. This allows you to hook into specific parts + /// of the boot process to execute actions, for example, you can show a boot splash during + /// the early phase. + #[serde(default)] + pub phases: PhasesConfiguration, +} + +/// Options configuration for Sprout, used when the corresponding options are not specified. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct OptionsConfiguration { + /// The entry to boot without showing the boot menu. + /// If not specified, a boot menu is shown. + #[serde(rename = "default-entry", default)] + pub default_entry: Option, + /// The timeout of the boot menu. + #[serde(rename = "menu-timeout", default = "default_menu_timeout")] + pub menu_timeout: u64, + /// Enables autoconfiguration of Sprout based on the environment. + #[serde(default)] + pub autoconfigure: bool, +} + +/// Get the latest version of the Sprout configuration format. +pub fn latest_version() -> u32 { + LATEST_VERSION +} + +fn default_menu_timeout() -> u64 { + DEFAULT_MENU_TIMEOUT_SECONDS +} diff --git a/crates/config/src/phases.rs b/crates/config/src/phases.rs new file mode 100644 index 0000000..90bce70 --- /dev/null +++ b/crates/config/src/phases.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// Configures the various phases of the boot process. +/// This allows hooking various phases to run actions. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct PhasesConfiguration { + /// The early phase is run before drivers are loaded. + #[serde(default)] + pub early: Vec, + /// The startup phase is run after drivers are loaded, but before entries are displayed. + #[serde(default)] + pub startup: Vec, + /// The late phase is run after the entry is chosen, but before the actions are executed. + #[serde(default)] + pub late: Vec, +} + +/// Configures a single phase of the boot process. +/// There can be multiple phase configurations that are +/// executed sequentially. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct PhaseConfiguration { + /// The actions to run when the phase is executed. + #[serde(default)] + pub actions: Vec, + /// The values to insert into the context when the phase is executed. + #[serde(default)] + pub values: BTreeMap, +} diff --git a/crates/sprout/Cargo.toml b/crates/sprout/Cargo.toml index 191dee5..8a8dfb8 100644 --- a/crates/sprout/Cargo.toml +++ b/crates/sprout/Cargo.toml @@ -10,15 +10,12 @@ edition.workspace = true [dependencies] anyhow.workspace = true bitflags.workspace = true +edera-sprout-config.path = "../config" hex.workspace = true sha2.workspace = true toml.workspace = true log.workspace = true -[dependencies.serde] -workspace = true -features = ["derive"] - [dependencies.uefi] workspace = true features = ["alloc", "logger"] diff --git a/crates/sprout/src/actions.rs b/crates/sprout/src/actions.rs index af7740a..0e8ac4c 100644 --- a/crates/sprout/src/actions.rs +++ b/crates/sprout/src/actions.rs @@ -1,6 +1,5 @@ use crate::context::SproutContext; use anyhow::{Context, Result, bail}; -use serde::{Deserialize, Serialize}; use std::rc::Rc; /// EFI chainloader action. @@ -10,28 +9,6 @@ pub mod edera; /// EFI console print action. pub mod print; -/// Declares an action that sprout can execute. -/// Actions allow configuring sprout's internal runtime mechanisms with values -/// that you can specify via other concepts. -/// -/// Actions are the main work that Sprout gets done, like booting Linux. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct ActionDeclaration { - /// Chainload to another EFI application. - /// This allows you to load any EFI application, either to boot an operating system - /// or to perform more EFI actions and return to sprout. - #[serde(default)] - pub chainload: Option, - /// Print a string to the EFI console. - #[serde(default)] - pub print: Option, - /// Boot the Edera hypervisor and the root operating system. - /// This action is an extension on top of the Xen EFI stub that - /// is specific to Edera. - #[serde(default, rename = "edera")] - pub edera: Option, -} - /// Execute the action specified by `name` which should be stored in the /// root context of the provided `context`. This function may not return /// if the provided action executes an operating system or an EFI application diff --git a/crates/sprout/src/actions/chainload.rs b/crates/sprout/src/actions/chainload.rs index 43186bf..d759b6b 100644 --- a/crates/sprout/src/actions/chainload.rs +++ b/crates/sprout/src/actions/chainload.rs @@ -5,30 +5,12 @@ use crate::utils; use crate::utils::media_loader::MediaLoaderHandle; use crate::utils::media_loader::constants::linux::LINUX_EFI_INITRD_MEDIA_GUID; use anyhow::{Context, Result, bail}; +use edera_sprout_config::actions::chainload::ChainloadConfiguration; use log::error; -use serde::{Deserialize, Serialize}; use std::rc::Rc; use uefi::CString16; use uefi::proto::loaded_image::LoadedImage; -/// The configuration of the chainload action. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct ChainloadConfiguration { - /// The path to the image to chainload. - /// This can be a Linux EFI stub (vmlinuz usually) or a standard EFI executable. - pub path: String, - /// The options to pass to the image. - /// The options are concatenated by a space and then passed to the EFI application. - #[serde(default)] - pub options: Vec, - /// An optional path to a Linux initrd. - /// This uses the [LINUX_EFI_INITRD_MEDIA_GUID] mechanism to load the initrd into the EFI stack. - /// For Linux, you can also use initrd=\path\to\initrd as an option, but this option is - /// generally better and safer as it can support additional load options in the future. - #[serde(default, rename = "linux-initrd")] - pub linux_initrd: Option, -} - /// Executes the chainload action using the specified `configuration` inside the provided `context`. pub fn chainload(context: Rc, configuration: &ChainloadConfiguration) -> Result<()> { // Retrieve the current image handle of sprout. diff --git a/crates/sprout/src/actions/edera.rs b/crates/sprout/src/actions/edera.rs index b0794de..a60377d 100644 --- a/crates/sprout/src/actions/edera.rs +++ b/crates/sprout/src/actions/edera.rs @@ -1,12 +1,7 @@ use std::rc::Rc; -use anyhow::{Context, Result}; -use log::error; -use serde::{Deserialize, Serialize}; -use uefi::Guid; - use crate::{ - actions::{self, chainload::ChainloadConfiguration}, + actions, context::SproutContext, utils::{ self, @@ -18,26 +13,11 @@ use crate::{ }, }, }; - -/// The configuration of the edera action which boots the Edera hypervisor. -/// Edera is based on Xen but modified significantly with a Rust stack. -/// Sprout is a component of the Edera stack and provides the boot functionality of Xen. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct EderaConfiguration { - /// The path to the Xen hypervisor EFI image. - pub xen: String, - /// The path to the kernel to boot for dom0. - pub kernel: String, - /// The path to the initrd to load for dom0. - #[serde(default)] - pub initrd: Option, - /// The options to pass to the kernel. - #[serde(default, rename = "kernel-options")] - pub kernel_options: Vec, - /// The options to pass to the Xen hypervisor. - #[serde(default, rename = "xen-options")] - pub xen_options: Vec, -} +use anyhow::{Context, Result}; +use edera_sprout_config::actions::chainload::ChainloadConfiguration; +use edera_sprout_config::actions::edera::EderaConfiguration; +use log::error; +use uefi::Guid; /// Builds a configuration string for the Xen EFI stub using the specified `configuration`. fn build_xen_config(context: Rc, configuration: &EderaConfiguration) -> String { diff --git a/crates/sprout/src/actions/print.rs b/crates/sprout/src/actions/print.rs index 1013d46..7903aed 100644 --- a/crates/sprout/src/actions/print.rs +++ b/crates/sprout/src/actions/print.rs @@ -1,17 +1,9 @@ use crate::context::SproutContext; use anyhow::Result; +use edera_sprout_config::actions::print::PrintConfiguration; use log::info; -use serde::{Deserialize, Serialize}; use std::rc::Rc; -/// The configuration of the print action. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct PrintConfiguration { - /// The text to print to the console. - #[serde(default)] - pub text: String, -} - /// Executes the print action with the specified `configuration` inside the provided `context`. pub fn print(context: Rc, configuration: &PrintConfiguration) -> Result<()> { info!("{}", context.stamp(&configuration.text)); diff --git a/crates/sprout/src/autoconfigure.rs b/crates/sprout/src/autoconfigure.rs index 92e17f2..8a1fddf 100644 --- a/crates/sprout/src/autoconfigure.rs +++ b/crates/sprout/src/autoconfigure.rs @@ -1,5 +1,5 @@ -use crate::config::RootConfiguration; use anyhow::{Context, Result}; +use edera_sprout_config::RootConfiguration; use uefi::fs::FileSystem; use uefi::proto::device_path::DevicePath; use uefi::proto::media::fs::SimpleFileSystem; diff --git a/crates/sprout/src/autoconfigure/bls.rs b/crates/sprout/src/autoconfigure/bls.rs index 6104e83..cd47489 100644 --- a/crates/sprout/src/autoconfigure/bls.rs +++ b/crates/sprout/src/autoconfigure/bls.rs @@ -1,11 +1,11 @@ -use crate::actions::ActionDeclaration; -use crate::actions::chainload::ChainloadConfiguration; -use crate::config::RootConfiguration; -use crate::entries::EntryDeclaration; -use crate::generators::GeneratorDeclaration; -use crate::generators::bls::BlsConfiguration; use crate::utils; use anyhow::{Context, Result}; +use edera_sprout_config::RootConfiguration; +use edera_sprout_config::actions::ActionDeclaration; +use edera_sprout_config::actions::chainload::ChainloadConfiguration; +use edera_sprout_config::entries::EntryDeclaration; +use edera_sprout_config::generators::GeneratorDeclaration; +use edera_sprout_config::generators::bls::BlsConfiguration; use uefi::cstr16; use uefi::fs::{FileSystem, Path}; use uefi::proto::device_path::DevicePath; diff --git a/crates/sprout/src/autoconfigure/linux.rs b/crates/sprout/src/autoconfigure/linux.rs index c73300b..52b20d7 100644 --- a/crates/sprout/src/autoconfigure/linux.rs +++ b/crates/sprout/src/autoconfigure/linux.rs @@ -1,12 +1,12 @@ -use crate::actions::ActionDeclaration; -use crate::actions::chainload::ChainloadConfiguration; -use crate::config::RootConfiguration; -use crate::entries::EntryDeclaration; -use crate::generators::GeneratorDeclaration; -use crate::generators::list::ListConfiguration; use crate::utils; use crate::utils::vercmp; use anyhow::{Context, Result}; +use edera_sprout_config::RootConfiguration; +use edera_sprout_config::actions::ActionDeclaration; +use edera_sprout_config::actions::chainload::ChainloadConfiguration; +use edera_sprout_config::entries::EntryDeclaration; +use edera_sprout_config::generators::GeneratorDeclaration; +use edera_sprout_config::generators::list::ListConfiguration; use std::collections::BTreeMap; use uefi::CString16; use uefi::fs::{FileSystem, Path, PathBuf}; diff --git a/crates/sprout/src/autoconfigure/windows.rs b/crates/sprout/src/autoconfigure/windows.rs index 2db8208..d17844f 100644 --- a/crates/sprout/src/autoconfigure/windows.rs +++ b/crates/sprout/src/autoconfigure/windows.rs @@ -1,9 +1,9 @@ -use crate::actions::ActionDeclaration; -use crate::actions::chainload::ChainloadConfiguration; -use crate::config::RootConfiguration; -use crate::entries::EntryDeclaration; use crate::utils; use anyhow::{Context, Result}; +use edera_sprout_config::RootConfiguration; +use edera_sprout_config::actions::ActionDeclaration; +use edera_sprout_config::actions::chainload::ChainloadConfiguration; +use edera_sprout_config::entries::EntryDeclaration; use uefi::CString16; use uefi::fs::{FileSystem, Path}; use uefi::proto::device_path::DevicePath; diff --git a/crates/sprout/src/config.rs b/crates/sprout/src/config.rs index 4b50ead..419d244 100644 --- a/crates/sprout/src/config.rs +++ b/crates/sprout/src/config.rs @@ -1,89 +1,2 @@ -use crate::actions::ActionDeclaration; -use crate::drivers::DriverDeclaration; -use crate::entries::EntryDeclaration; -use crate::extractors::ExtractorDeclaration; -use crate::generators::GeneratorDeclaration; -use crate::phases::PhasesConfiguration; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; - /// The configuration loader mechanisms. pub mod loader; - -/// This is the latest version of the sprout configuration format. -/// This must be incremented when the configuration breaks compatibility. -pub const LATEST_VERSION: u32 = 1; - -/// The default timeout for the boot menu in seconds. -pub const DEFAULT_MENU_TIMEOUT_SECONDS: u64 = 10; - -/// The Sprout configuration format. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct RootConfiguration { - /// The version of the configuration. This should always be declared - /// and be the latest version that is supported. If not specified, it is assumed - /// the configuration is the latest version. - #[serde(default = "latest_version")] - pub version: u32, - /// Default options for Sprout. - #[serde(default)] - pub options: OptionsConfiguration, - /// Values to be inserted into the root sprout context. - #[serde(default)] - pub values: BTreeMap, - /// Drivers to load. - /// These drivers provide extra functionality like filesystem support to Sprout. - /// Each driver has a name which uniquely identifies it inside Sprout. - #[serde(default)] - pub drivers: BTreeMap, - /// Declares the extractors that add values to the sprout context that are calculated - /// at runtime. Each extractor has a name which corresponds to the value it will set - /// inside the sprout context. - #[serde(default)] - pub extractors: BTreeMap, - /// Declares the actions that can execute operations for sprout. - /// Actions are executable modules in sprout that take in specific structured values. - /// Actions are responsible for ensuring that passed strings are stamped to replace values - /// at runtime. - /// Each action has a name that can be referenced by other base concepts like entries. - #[serde(default)] - pub actions: BTreeMap, - /// Declares the entries that are displayed on the boot menu. These entries are static - /// but can still use values from the sprout context. - #[serde(default)] - pub entries: BTreeMap, - /// Declares the generators that are used to generate entries at runtime. - /// Each generator has its own logic for generating entries, but generally they intake - /// a template entry and stamp that template entry over some values determined at runtime. - /// Each generator has an associated name used to differentiate it across sprout. - #[serde(default)] - pub generators: BTreeMap, - /// Configures the various phases of sprout. This allows you to hook into specific parts - /// of the boot process to execute actions, for example, you can show a boot splash during - /// the early phase. - #[serde(default)] - pub phases: PhasesConfiguration, -} - -/// Options configuration for Sprout, used when the corresponding options are not specified. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct OptionsConfiguration { - /// The entry to boot without showing the boot menu. - /// If not specified, a boot menu is shown. - #[serde(rename = "default-entry", default)] - pub default_entry: Option, - /// The timeout of the boot menu. - #[serde(rename = "menu-timeout", default = "default_menu_timeout")] - pub menu_timeout: u64, - /// Enables autoconfiguration of Sprout based on the environment. - #[serde(default)] - pub autoconfigure: bool, -} - -fn latest_version() -> u32 { - LATEST_VERSION -} - -fn default_menu_timeout() -> u64 { - DEFAULT_MENU_TIMEOUT_SECONDS -} diff --git a/crates/sprout/src/config/loader.rs b/crates/sprout/src/config/loader.rs index 4c839b7..86ac595 100644 --- a/crates/sprout/src/config/loader.rs +++ b/crates/sprout/src/config/loader.rs @@ -1,8 +1,8 @@ -use crate::config::{RootConfiguration, latest_version}; use crate::options::SproutOptions; use crate::platform::tpm::PlatformTpm; use crate::utils; use anyhow::{Context, Result, bail}; +use edera_sprout_config::{RootConfiguration, latest_version}; use log::info; use std::ops::Deref; use toml::Value; diff --git a/crates/sprout/src/context.rs b/crates/sprout/src/context.rs index e194f88..4ff4882 100644 --- a/crates/sprout/src/context.rs +++ b/crates/sprout/src/context.rs @@ -1,8 +1,8 @@ -use crate::actions::ActionDeclaration; use crate::options::SproutOptions; use crate::platform::timer::PlatformTimer; use anyhow::anyhow; use anyhow::{Result, bail}; +use edera_sprout_config::actions::ActionDeclaration; use std::cmp::Reverse; use std::collections::{BTreeMap, BTreeSet}; use std::rc::Rc; diff --git a/crates/sprout/src/drivers.rs b/crates/sprout/src/drivers.rs index 8181be3..7fdaddb 100644 --- a/crates/sprout/src/drivers.rs +++ b/crates/sprout/src/drivers.rs @@ -2,23 +2,12 @@ use crate::context::SproutContext; use crate::integrations::shim::{ShimInput, ShimSupport}; use crate::utils; use anyhow::{Context, Result}; +pub(crate) use edera_sprout_config::drivers::DriverDeclaration; use log::info; -use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::rc::Rc; use uefi::boot::SearchType; -/// Declares a driver configuration. -/// Drivers allow extending the functionality of Sprout. -/// Drivers are loaded at runtime and can provide extra functionality like filesystem support. -/// Drivers are loaded by their name, which is used to reference them in other concepts. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct DriverDeclaration { - /// The filesystem path to the driver. - /// This file should be an EFI executable that can be located and executed. - pub path: String, -} - /// Loads the driver specified by the `driver` declaration. fn load_driver(context: Rc, driver: &DriverDeclaration) -> Result<()> { // Acquire the handle and device path of the loaded image. diff --git a/crates/sprout/src/entries.rs b/crates/sprout/src/entries.rs index 757a45f..dfa9e80 100644 --- a/crates/sprout/src/entries.rs +++ b/crates/sprout/src/entries.rs @@ -1,25 +1,7 @@ use crate::context::SproutContext; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; +use edera_sprout_config::entries::EntryDeclaration; use std::rc::Rc; -/// Declares a boot entry to display in the boot menu. -/// -/// Entries are the user-facing concept of Sprout, making it possible -/// to run a set of actions with a specific context. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct EntryDeclaration { - /// The title of the entry which will be display in the boot menu. - /// This is the pre-stamped value. - pub title: String, - /// The actions to run when the entry is selected. - #[serde(default)] - pub actions: Vec, - /// The values to insert into the context when the entry is selected. - #[serde(default)] - pub values: BTreeMap, -} - /// Represents an entry that is stamped and ready to be booted. #[derive(Clone)] pub struct BootableEntry { diff --git a/crates/sprout/src/extractors.rs b/crates/sprout/src/extractors.rs index ae4f325..f8d76b9 100644 --- a/crates/sprout/src/extractors.rs +++ b/crates/sprout/src/extractors.rs @@ -1,25 +1,11 @@ use crate::context::SproutContext; -use crate::extractors::filesystem_device_match::FilesystemDeviceMatchExtractor; use anyhow::{Result, bail}; -use serde::{Deserialize, Serialize}; +use edera_sprout_config::extractors::ExtractorDeclaration; use std::rc::Rc; /// The filesystem device match extractor. pub mod filesystem_device_match; -/// Declares an extractor configuration. -/// Extractors allow calculating values at runtime -/// using built-in sprout modules. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct ExtractorDeclaration { - /// The filesystem device match extractor. - /// This extractor finds a filesystem using some search criteria and returns - /// the device root path that can concatenated with subpaths to access files - /// on a particular filesystem. - #[serde(default, rename = "filesystem-device-match")] - pub filesystem_device_match: Option, -} - /// Extracts the value using the specified `extractor` under the provided `context`. /// The extractor must return a value, and if a value cannot be determined, an error /// should be returned. diff --git a/crates/sprout/src/extractors/filesystem_device_match.rs b/crates/sprout/src/extractors/filesystem_device_match.rs index bbbfaaa..ed1b557 100644 --- a/crates/sprout/src/extractors/filesystem_device_match.rs +++ b/crates/sprout/src/extractors/filesystem_device_match.rs @@ -1,7 +1,7 @@ use crate::context::SproutContext; use crate::utils; use anyhow::{Context, Result, anyhow, bail}; -use serde::{Deserialize, Serialize}; +use edera_sprout_config::extractors::filesystem_device_match::FilesystemDeviceMatchExtractor; use std::ops::Deref; use std::rc::Rc; use std::str::FromStr; @@ -11,34 +11,6 @@ use uefi::proto::media::file::{File, FileSystemVolumeLabel}; use uefi::proto::media::fs::SimpleFileSystem; use uefi::{CString16, Guid}; -/// The filesystem device match extractor. -/// This extractor finds a filesystem using some search criteria and returns -/// the device root path that can concatenated with subpaths to access files -/// on a particular filesystem. -/// The fallback value can be used to provide a value if no match is found. -/// -/// This extractor requires all the criteria to match. If no criteria is provided, -/// an error is returned. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct FilesystemDeviceMatchExtractor { - /// Matches a filesystem that has the specified label. - #[serde(default, rename = "has-label")] - pub has_label: Option, - /// Matches a filesystem that has the specified item. - /// An item is either a directory or file. - #[serde(default, rename = "has-item")] - pub has_item: Option, - /// Matches a filesystem that has the specified partition UUID. - #[serde(default, rename = "has-partition-uuid")] - pub has_partition_uuid: Option, - /// Matches a filesystem that has the specified partition type UUID. - #[serde(default, rename = "has-partition-type-uuid")] - pub has_partition_type_uuid: Option, - /// The fallback value to use if no filesystem matches the criteria. - #[serde(default)] - pub fallback: Option, -} - /// Extract a filesystem device path using the specified `context` and `extractor` configuration. pub fn extract( context: Rc, diff --git a/crates/sprout/src/generators.rs b/crates/sprout/src/generators.rs index 13622a9..b431c0e 100644 --- a/crates/sprout/src/generators.rs +++ b/crates/sprout/src/generators.rs @@ -1,43 +1,18 @@ use crate::context::SproutContext; use crate::entries::BootableEntry; -use crate::generators::bls::BlsConfiguration; -use crate::generators::list::ListConfiguration; -use crate::generators::matrix::MatrixConfiguration; use anyhow::Result; use anyhow::bail; -use serde::{Deserialize, Serialize}; +use edera_sprout_config::generators::GeneratorDeclaration; use std::rc::Rc; +/// The BLS generator. pub mod bls; -pub mod list; -pub mod matrix; -/// Declares a generator configuration. -/// Generators allow generating entries at runtime based on a set of data. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct GeneratorDeclaration { - /// Matrix generator configuration. - /// Matrix allows you to specify multiple value-key values as arrays. - /// This allows multiplying the number of entries by any number of possible - /// configuration options. For example, - /// data.x = ["a", "b"] - /// data.y = ["c", "d"] - /// would generate an entry for each of these combinations: - /// x = a, y = c - /// x = a, y = d - /// x = b, y = c - /// x = b, y = d - #[serde(default)] - pub matrix: Option, - /// BLS generator configuration. - /// BLS allows you to pass a filesystem path that contains a set of BLS entries. - /// It will generate a sprout entry for every supported BLS entry. - #[serde(default)] - pub bls: Option, - /// List generator configuration. - /// Allows you to specify a list of values to generate an entry from. - pub list: Option, -} +/// The list generator. +pub mod list; + +/// The matrix generator. +pub mod matrix; /// Runs the generator specified by the `generator` option. /// It uses the specified `context` as the parent context for diff --git a/crates/sprout/src/generators/bls.rs b/crates/sprout/src/generators/bls.rs index 122a554..f6e24ef 100644 --- a/crates/sprout/src/generators/bls.rs +++ b/crates/sprout/src/generators/bls.rs @@ -1,10 +1,10 @@ use crate::context::SproutContext; -use crate::entries::{BootableEntry, EntryDeclaration}; +use crate::entries::BootableEntry; use crate::generators::bls::entry::BlsEntry; use crate::utils; use crate::utils::vercmp; use anyhow::{Context, Result}; -use serde::{Deserialize, Serialize}; +use edera_sprout_config::generators::bls::BlsConfiguration; use std::cmp::Ordering; use std::rc::Rc; use std::str::FromStr; @@ -16,25 +16,6 @@ use uefi::proto::media::fs::SimpleFileSystem; /// BLS entry parser. mod entry; -/// The default path to the BLS directory. -const BLS_TEMPLATE_PATH: &str = "\\loader"; - -/// The configuration of the BLS generator. -/// The BLS uses the Bootloader Specification to produce -/// entries from an input template. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct BlsConfiguration { - /// The entry to use for as a template. - pub entry: EntryDeclaration, - /// The path to the BLS directory. - #[serde(default = "default_bls_path")] - pub path: String, -} - -fn default_bls_path() -> String { - BLS_TEMPLATE_PATH.to_string() -} - // TODO(azenla): remove this once variable substitution is implemented. /// This function is used to remove the `tuned_initrd` variable from entry values. /// Fedora uses tuned which adds an initrd that shouldn't be used. diff --git a/crates/sprout/src/generators/list.rs b/crates/sprout/src/generators/list.rs index f863e82..021598a 100644 --- a/crates/sprout/src/generators/list.rs +++ b/crates/sprout/src/generators/list.rs @@ -1,23 +1,9 @@ use crate::context::SproutContext; -use crate::entries::{BootableEntry, EntryDeclaration}; +use crate::entries::BootableEntry; use anyhow::Result; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; +use edera_sprout_config::generators::list::ListConfiguration; use std::rc::Rc; -/// List generator configuration. -/// The list generator produces multiple entries based -/// on a set of input maps. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct ListConfiguration { - /// The template entry to use for each generated entry. - #[serde(default)] - pub entry: EntryDeclaration, - /// The values to use as the input for the matrix. - #[serde(default)] - pub values: Vec>, -} - /// Generates a set of entries using the specified `list` configuration in the `context`. pub fn generate( context: Rc, diff --git a/crates/sprout/src/generators/matrix.rs b/crates/sprout/src/generators/matrix.rs index f6150eb..28e2aff 100644 --- a/crates/sprout/src/generators/matrix.rs +++ b/crates/sprout/src/generators/matrix.rs @@ -1,24 +1,12 @@ use crate::context::SproutContext; -use crate::entries::{BootableEntry, EntryDeclaration}; +use crate::entries::BootableEntry; use crate::generators::list; use anyhow::Result; -use serde::{Deserialize, Serialize}; +use edera_sprout_config::generators::list::ListConfiguration; +use edera_sprout_config::generators::matrix::MatrixConfiguration; use std::collections::BTreeMap; use std::rc::Rc; -/// Matrix generator configuration. -/// The matrix generator produces multiple entries based -/// on input values multiplicatively. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct MatrixConfiguration { - /// The template entry to use for each generated entry. - #[serde(default)] - pub entry: EntryDeclaration, - /// The values to use as the input for the matrix. - #[serde(default)] - pub values: BTreeMap>, -} - /// Builds out multiple generations of `input` based on a matrix style. /// For example, if input is: {"x": ["a", "b"], "y": ["c", "d"]} /// It will produce: @@ -61,7 +49,7 @@ pub fn generate( // Use the list generator to generate entries for each combination. list::generate( context, - &list::ListConfiguration { + &ListConfiguration { entry: matrix.entry.clone(), values: combinations, }, diff --git a/crates/sprout/src/main.rs b/crates/sprout/src/main.rs index 78eb702..90ad02c 100644 --- a/crates/sprout/src/main.rs +++ b/crates/sprout/src/main.rs @@ -4,7 +4,6 @@ /// The delay to wait for when an error occurs in Sprout. const DELAY_ON_ERROR: Duration = Duration::from_secs(10); -use crate::config::RootConfiguration; use crate::context::{RootContext, SproutContext}; use crate::entries::BootableEntry; use crate::integrations::bootloader_interface::{BootloaderInterface, BootloaderInterfaceTimeout}; @@ -16,6 +15,7 @@ use crate::platform::tpm::PlatformTpm; use crate::secure::SecureBoot; use crate::utils::PartitionGuidForm; use anyhow::{Context, Result, bail}; +use edera_sprout_config::RootConfiguration; use log::{error, info, warn}; use std::collections::BTreeMap; use std::ops::Deref; diff --git a/crates/sprout/src/phases.rs b/crates/sprout/src/phases.rs index a6303e9..68c6baa 100644 --- a/crates/sprout/src/phases.rs +++ b/crates/sprout/src/phases.rs @@ -1,38 +1,9 @@ use crate::actions; use crate::context::SproutContext; use anyhow::{Context, Result}; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; +use edera_sprout_config::phases::PhaseConfiguration; use std::rc::Rc; -/// Configures the various phases of the boot process. -/// This allows hooking various phases to run actions. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct PhasesConfiguration { - /// The early phase is run before drivers are loaded. - #[serde(default)] - pub early: Vec, - /// The startup phase is run after drivers are loaded, but before entries are displayed. - #[serde(default)] - pub startup: Vec, - /// The late phase is run after the entry is chosen, but before the actions are executed. - #[serde(default)] - pub late: Vec, -} - -/// Configures a single phase of the boot process. -/// There can be multiple phase configurations that are -/// executed sequentially. -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct PhaseConfiguration { - /// The actions to run when the phase is executed. - #[serde(default)] - pub actions: Vec, - /// The values to insert into the context when the phase is executed. - #[serde(default)] - pub values: BTreeMap, -} - /// Executes the specified [phase] of the boot process. /// The value [phase] should be a reference of a specific phase in the [PhasesConfiguration]. /// Any error from the actions is propagated into the [Result] and will interrupt further