diff --git a/Cargo.lock b/Cargo.lock index a1a591a..bc0694e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,8 @@ dependencies = [ "hex", "log", "sha2", + "shlex", + "spin", "toml", "uefi", "uefi-raw", @@ -86,12 +88,6 @@ dependencies = [ "serde", ] -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "generic-array" version = "0.14.9" @@ -102,34 +98,27 @@ dependencies = [ "version_check", ] -[[package]] -name = "hashbrown" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" - [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "indexmap" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" -dependencies = [ - "equivalent", - "hashbrown", -] - [[package]] name = "libc" version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.28" @@ -174,6 +163,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.228" @@ -224,6 +219,21 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + [[package]] name = "syn" version = "2.0.108" @@ -241,12 +251,10 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ - "indexmap", "serde_core", "serde_spanned", "toml_datetime", "toml_parser", - "toml_writer", "winnow", ] @@ -268,12 +276,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "toml_writer" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" - [[package]] name = "typenum" version = "1.19.0" diff --git a/Cargo.toml b/Cargo.toml index a7da504..5e31861 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,16 +13,39 @@ repository = "https://github.com/edera-dev/sprout" edition = "2024" [workspace.dependencies] -anyhow = "1.0.100" bitflags = "2.10.0" -hex = "0.4.3" log = "0.4.28" -serde = "1.0.228" -sha2 = "0.10.9" -toml = "0.9.8" +spin = "0.10.0" uefi = "0.36.0" uefi-raw = "0.12.0" +[workspace.dependencies.anyhow] +version = "1.0.100" +default-features = false + +[workspace.dependencies.hex] +version = "0.4.3" +default-features = false +features = ["alloc"] + +[workspace.dependencies.serde] +version = "1.0.228" +default-features = false +features = ["alloc", "derive"] + +[workspace.dependencies.sha2] +version = "0.10.9" +default-features = false + +[workspace.dependencies.shlex] +version = "1.3.0" +default-features = false + +[workspace.dependencies.toml] +version = "0.9.8" +default-features = false +features = ["serde", "parse"] + # Common build profiles # NOTE: We have to compile everything for opt-level = 2 due to optimization passes # which don't handle the UEFI target properly. diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 63145c1..3bb3862 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -5,11 +5,7 @@ This guide is a work in progress. ## Development Setup You can use any Rust development environment to develop Sprout. - Rustup is recommended as the Rust toolchain manager to manage Rust versions and targets. - -Sprout currently requires Rust nightly to support uefi_std. See [uefi_std](https://doc.rust-lang.org/beta/rustc/platform-support/unknown-uefi.html) for more details. - We currently only support `x86_64-unknown-uefi` and `aarch64-unknown-uefi` targets. To test your changes in QEMU, please run `./hack/dev/boot.sh`, you can specify `x86_64` or `aarch64` diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 412e28c..2381ebe 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -9,7 +9,7 @@ edition.workspace = true [dependencies.serde] workspace = true -features = ["derive"] +default-features = false [lib] name = "edera_sprout_config" diff --git a/crates/config/src/actions/chainload.rs b/crates/config/src/actions/chainload.rs index 44afd99..a06a8fd 100644 --- a/crates/config/src/actions/chainload.rs +++ b/crates/config/src/actions/chainload.rs @@ -1,3 +1,5 @@ +use alloc::string::String; +use alloc::vec::Vec; use serde::{Deserialize, Serialize}; /// The configuration of the chainload action. diff --git a/crates/config/src/actions/edera.rs b/crates/config/src/actions/edera.rs index f4a04e8..6577610 100644 --- a/crates/config/src/actions/edera.rs +++ b/crates/config/src/actions/edera.rs @@ -1,3 +1,5 @@ +use alloc::string::String; +use alloc::vec::Vec; use serde::{Deserialize, Serialize}; /// The configuration of the edera action which boots the Edera hypervisor. diff --git a/crates/config/src/actions/print.rs b/crates/config/src/actions/print.rs index b9f9c5a..4c255d3 100644 --- a/crates/config/src/actions/print.rs +++ b/crates/config/src/actions/print.rs @@ -1,3 +1,4 @@ +use alloc::string::String; use serde::{Deserialize, Serialize}; /// The configuration of the print action. diff --git a/crates/config/src/drivers.rs b/crates/config/src/drivers.rs index dd05ad2..48320be 100644 --- a/crates/config/src/drivers.rs +++ b/crates/config/src/drivers.rs @@ -1,3 +1,4 @@ +use alloc::string::String; use serde::{Deserialize, Serialize}; /// Declares a driver configuration. diff --git a/crates/config/src/entries.rs b/crates/config/src/entries.rs index b9950e7..61dbc23 100644 --- a/crates/config/src/entries.rs +++ b/crates/config/src/entries.rs @@ -1,5 +1,7 @@ +use alloc::collections::BTreeMap; +use alloc::string::String; +use alloc::vec::Vec; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; /// Declares a boot entry to display in the boot menu. /// diff --git a/crates/config/src/extractors/filesystem_device_match.rs b/crates/config/src/extractors/filesystem_device_match.rs index c7f2c30..643150b 100644 --- a/crates/config/src/extractors/filesystem_device_match.rs +++ b/crates/config/src/extractors/filesystem_device_match.rs @@ -1,3 +1,4 @@ +use alloc::string::String; use serde::{Deserialize, Serialize}; /// The filesystem device match extractor. diff --git a/crates/config/src/generators/bls.rs b/crates/config/src/generators/bls.rs index 1f8b671..a632d31 100644 --- a/crates/config/src/generators/bls.rs +++ b/crates/config/src/generators/bls.rs @@ -1,4 +1,5 @@ use crate::entries::EntryDeclaration; +use alloc::string::{String, ToString}; use serde::{Deserialize, Serialize}; /// The default path to the BLS directory. diff --git a/crates/config/src/generators/list.rs b/crates/config/src/generators/list.rs index dd05047..2beb57b 100644 --- a/crates/config/src/generators/list.rs +++ b/crates/config/src/generators/list.rs @@ -1,6 +1,8 @@ use crate::entries::EntryDeclaration; +use alloc::collections::BTreeMap; +use alloc::string::String; +use alloc::vec::Vec; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; /// List generator configuration. /// The list generator produces multiple entries based diff --git a/crates/config/src/generators/matrix.rs b/crates/config/src/generators/matrix.rs index e382bfa..95a8362 100644 --- a/crates/config/src/generators/matrix.rs +++ b/crates/config/src/generators/matrix.rs @@ -1,6 +1,8 @@ use crate::entries::EntryDeclaration; +use alloc::collections::BTreeMap; +use alloc::string::String; +use alloc::vec::Vec; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; /// Matrix generator configuration. /// The matrix generator produces multiple entries based diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 7176527..9d32064 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1,5 +1,7 @@ //! Sprout configuration descriptions. //! This crate provides all the configuration structures for Sprout. +#![no_std] +extern crate alloc; use crate::actions::ActionDeclaration; use crate::drivers::DriverDeclaration; @@ -7,8 +9,9 @@ use crate::entries::EntryDeclaration; use crate::extractors::ExtractorDeclaration; use crate::generators::GeneratorDeclaration; use crate::phases::PhasesConfiguration; +use alloc::collections::BTreeMap; +use alloc::string::String; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; pub mod actions; pub mod drivers; diff --git a/crates/config/src/phases.rs b/crates/config/src/phases.rs index 90bce70..88709d9 100644 --- a/crates/config/src/phases.rs +++ b/crates/config/src/phases.rs @@ -1,5 +1,7 @@ +use alloc::collections::BTreeMap; +use alloc::string::String; +use alloc::vec::Vec; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; /// Configures the various phases of the boot process. /// This allows hooking various phases to run actions. diff --git a/crates/sprout/Cargo.toml b/crates/sprout/Cargo.toml index 8a8dfb8..9c7a885 100644 --- a/crates/sprout/Cargo.toml +++ b/crates/sprout/Cargo.toml @@ -13,12 +13,14 @@ bitflags.workspace = true edera-sprout-config.path = "../config" hex.workspace = true sha2.workspace = true +shlex.workspace = true +spin.workspace = true toml.workspace = true log.workspace = true [dependencies.uefi] workspace = true -features = ["alloc", "logger"] +features = ["alloc", "global_allocator", "logger", "panic_handler"] [dependencies.uefi-raw] workspace = true diff --git a/crates/sprout/src/actions.rs b/crates/sprout/src/actions.rs index 0e8ac4c..c5ba4b7 100644 --- a/crates/sprout/src/actions.rs +++ b/crates/sprout/src/actions.rs @@ -1,6 +1,6 @@ use crate::context::SproutContext; +use alloc::rc::Rc; use anyhow::{Context, Result, bail}; -use std::rc::Rc; /// EFI chainloader action. pub mod chainload; diff --git a/crates/sprout/src/actions/chainload.rs b/crates/sprout/src/actions/chainload.rs index d759b6b..66483f6 100644 --- a/crates/sprout/src/actions/chainload.rs +++ b/crates/sprout/src/actions/chainload.rs @@ -4,10 +4,11 @@ use crate::integrations::shim::{ShimInput, ShimSupport}; use crate::utils; use crate::utils::media_loader::MediaLoaderHandle; use crate::utils::media_loader::constants::linux::LINUX_EFI_INITRD_MEDIA_GUID; +use alloc::boxed::Box; +use alloc::rc::Rc; use anyhow::{Context, Result, bail}; use edera_sprout_config::actions::chainload::ChainloadConfiguration; use log::error; -use std::rc::Rc; use uefi::CString16; use uefi::proto::loaded_image::LoadedImage; diff --git a/crates/sprout/src/actions/edera.rs b/crates/sprout/src/actions/edera.rs index a60377d..467d610 100644 --- a/crates/sprout/src/actions/edera.rs +++ b/crates/sprout/src/actions/edera.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use crate::{ actions, context::SproutContext, @@ -13,6 +11,9 @@ use crate::{ }, }, }; +use alloc::rc::Rc; +use alloc::string::{String, ToString}; +use alloc::{format, vec}; use anyhow::{Context, Result}; use edera_sprout_config::actions::chainload::ChainloadConfiguration; use edera_sprout_config::actions::edera::EderaConfiguration; diff --git a/crates/sprout/src/actions/print.rs b/crates/sprout/src/actions/print.rs index 7903aed..5a43cd9 100644 --- a/crates/sprout/src/actions/print.rs +++ b/crates/sprout/src/actions/print.rs @@ -1,8 +1,8 @@ use crate::context::SproutContext; +use alloc::rc::Rc; use anyhow::Result; use edera_sprout_config::actions::print::PrintConfiguration; use log::info; -use std::rc::Rc; /// Executes the print action with the specified `configuration` inside the provided `context`. pub fn print(context: Rc, configuration: &PrintConfiguration) -> Result<()> { diff --git a/crates/sprout/src/autoconfigure/bls.rs b/crates/sprout/src/autoconfigure/bls.rs index cd47489..95e572c 100644 --- a/crates/sprout/src/autoconfigure/bls.rs +++ b/crates/sprout/src/autoconfigure/bls.rs @@ -1,4 +1,6 @@ use crate::utils; +use alloc::string::ToString; +use alloc::{format, vec}; use anyhow::{Context, Result}; use edera_sprout_config::RootConfiguration; use edera_sprout_config::actions::ActionDeclaration; diff --git a/crates/sprout/src/autoconfigure/linux.rs b/crates/sprout/src/autoconfigure/linux.rs index 52b20d7..7146cf9 100644 --- a/crates/sprout/src/autoconfigure/linux.rs +++ b/crates/sprout/src/autoconfigure/linux.rs @@ -1,5 +1,9 @@ use crate::utils; use crate::utils::vercmp; +use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use alloc::{format, vec}; use anyhow::{Context, Result}; use edera_sprout_config::RootConfiguration; use edera_sprout_config::actions::ActionDeclaration; @@ -7,7 +11,6 @@ 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}; use uefi::proto::device_path::DevicePath; diff --git a/crates/sprout/src/autoconfigure/windows.rs b/crates/sprout/src/autoconfigure/windows.rs index d17844f..bc954d4 100644 --- a/crates/sprout/src/autoconfigure/windows.rs +++ b/crates/sprout/src/autoconfigure/windows.rs @@ -1,4 +1,6 @@ use crate::utils; +use alloc::string::ToString; +use alloc::{format, vec}; use anyhow::{Context, Result}; use edera_sprout_config::RootConfiguration; use edera_sprout_config::actions::ActionDeclaration; diff --git a/crates/sprout/src/config/loader.rs b/crates/sprout/src/config/loader.rs index 86ac595..ec8c83b 100644 --- a/crates/sprout/src/config/loader.rs +++ b/crates/sprout/src/config/loader.rs @@ -1,10 +1,11 @@ use crate::options::SproutOptions; use crate::platform::tpm::PlatformTpm; use crate::utils; +use alloc::vec::Vec; use anyhow::{Context, Result, bail}; +use core::ops::Deref; use edera_sprout_config::{RootConfiguration, latest_version}; use log::info; -use std::ops::Deref; use toml::Value; use uefi::proto::device_path::LoadedImageDevicePath; diff --git a/crates/sprout/src/context.rs b/crates/sprout/src/context.rs index 4ff4882..f8a8b31 100644 --- a/crates/sprout/src/context.rs +++ b/crates/sprout/src/context.rs @@ -1,11 +1,15 @@ use crate::options::SproutOptions; use crate::platform::timer::PlatformTimer; +use alloc::boxed::Box; +use alloc::collections::{BTreeMap, BTreeSet}; +use alloc::format; +use alloc::rc::Rc; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use anyhow::anyhow; use anyhow::{Result, bail}; +use core::cmp::Reverse; use edera_sprout_config::actions::ActionDeclaration; -use std::cmp::Reverse; -use std::collections::{BTreeMap, BTreeSet}; -use std::rc::Rc; use uefi::proto::device_path::DevicePath; /// The maximum number of iterations that can be performed in [SproutContext::finalize]. diff --git a/crates/sprout/src/drivers.rs b/crates/sprout/src/drivers.rs index 7fdaddb..270886e 100644 --- a/crates/sprout/src/drivers.rs +++ b/crates/sprout/src/drivers.rs @@ -1,11 +1,13 @@ use crate::context::SproutContext; use crate::integrations::shim::{ShimInput, ShimSupport}; use crate::utils; +use alloc::collections::BTreeMap; +use alloc::format; +use alloc::rc::Rc; +use alloc::string::String; use anyhow::{Context, Result}; -pub(crate) use edera_sprout_config::drivers::DriverDeclaration; +use edera_sprout_config::drivers::DriverDeclaration; use log::info; -use std::collections::BTreeMap; -use std::rc::Rc; use uefi::boot::SearchType; /// Loads the driver specified by the `driver` declaration. diff --git a/crates/sprout/src/entries.rs b/crates/sprout/src/entries.rs index dfa9e80..8d1a3e2 100644 --- a/crates/sprout/src/entries.rs +++ b/crates/sprout/src/entries.rs @@ -1,6 +1,7 @@ use crate::context::SproutContext; +use alloc::rc::Rc; +use alloc::string::{String, ToString}; use edera_sprout_config::entries::EntryDeclaration; -use std::rc::Rc; /// Represents an entry that is stamped and ready to be booted. #[derive(Clone)] diff --git a/crates/sprout/src/extractors.rs b/crates/sprout/src/extractors.rs index f8d76b9..d27984d 100644 --- a/crates/sprout/src/extractors.rs +++ b/crates/sprout/src/extractors.rs @@ -1,7 +1,8 @@ use crate::context::SproutContext; +use alloc::rc::Rc; +use alloc::string::String; use anyhow::{Result, bail}; use edera_sprout_config::extractors::ExtractorDeclaration; -use std::rc::Rc; /// The filesystem device match extractor. pub mod filesystem_device_match; diff --git a/crates/sprout/src/extractors/filesystem_device_match.rs b/crates/sprout/src/extractors/filesystem_device_match.rs index ed1b557..33911d6 100644 --- a/crates/sprout/src/extractors/filesystem_device_match.rs +++ b/crates/sprout/src/extractors/filesystem_device_match.rs @@ -1,10 +1,11 @@ use crate::context::SproutContext; use crate::utils; +use alloc::rc::Rc; +use alloc::string::String; use anyhow::{Context, Result, anyhow, bail}; +use core::ops::Deref; +use core::str::FromStr; use edera_sprout_config::extractors::filesystem_device_match::FilesystemDeviceMatchExtractor; -use std::ops::Deref; -use std::rc::Rc; -use std::str::FromStr; use uefi::fs::{FileSystem, Path}; use uefi::proto::device_path::DevicePath; use uefi::proto::media::file::{File, FileSystemVolumeLabel}; diff --git a/crates/sprout/src/generators.rs b/crates/sprout/src/generators.rs index b431c0e..3578334 100644 --- a/crates/sprout/src/generators.rs +++ b/crates/sprout/src/generators.rs @@ -1,9 +1,10 @@ use crate::context::SproutContext; use crate::entries::BootableEntry; +use alloc::rc::Rc; +use alloc::vec::Vec; use anyhow::Result; use anyhow::bail; use edera_sprout_config::generators::GeneratorDeclaration; -use std::rc::Rc; /// The BLS generator. pub mod bls; diff --git a/crates/sprout/src/generators/bls.rs b/crates/sprout/src/generators/bls.rs index f6e24ef..91d3d2e 100644 --- a/crates/sprout/src/generators/bls.rs +++ b/crates/sprout/src/generators/bls.rs @@ -3,11 +3,14 @@ use crate::entries::BootableEntry; use crate::generators::bls::entry::BlsEntry; use crate::utils; use crate::utils::vercmp; +use alloc::format; +use alloc::rc::Rc; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use anyhow::{Context, Result}; +use core::cmp::Ordering; +use core::str::FromStr; use edera_sprout_config::generators::bls::BlsConfiguration; -use std::cmp::Ordering; -use std::rc::Rc; -use std::str::FromStr; use uefi::cstr16; use uefi::fs::{FileSystem, PathBuf}; use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly}; diff --git a/crates/sprout/src/generators/bls/entry.rs b/crates/sprout/src/generators/bls/entry.rs index 452f2f9..6cd0825 100644 --- a/crates/sprout/src/generators/bls/entry.rs +++ b/crates/sprout/src/generators/bls/entry.rs @@ -1,5 +1,6 @@ +use alloc::string::{String, ToString}; use anyhow::{Error, Result}; -use std::str::FromStr; +use core::str::FromStr; /// Represents a parsed BLS entry. /// Fields unrelated to Sprout are not included. diff --git a/crates/sprout/src/generators/list.rs b/crates/sprout/src/generators/list.rs index 021598a..290440a 100644 --- a/crates/sprout/src/generators/list.rs +++ b/crates/sprout/src/generators/list.rs @@ -1,8 +1,10 @@ use crate::context::SproutContext; use crate::entries::BootableEntry; +use alloc::rc::Rc; +use alloc::string::ToString; +use alloc::vec::Vec; use anyhow::Result; use edera_sprout_config::generators::list::ListConfiguration; -use std::rc::Rc; /// Generates a set of entries using the specified `list` configuration in the `context`. pub fn generate( diff --git a/crates/sprout/src/generators/matrix.rs b/crates/sprout/src/generators/matrix.rs index 28e2aff..069c185 100644 --- a/crates/sprout/src/generators/matrix.rs +++ b/crates/sprout/src/generators/matrix.rs @@ -1,11 +1,14 @@ use crate::context::SproutContext; use crate::entries::BootableEntry; use crate::generators::list; +use alloc::collections::BTreeMap; +use alloc::rc::Rc; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; use anyhow::Result; use edera_sprout_config::generators::list::ListConfiguration; use edera_sprout_config::generators::matrix::MatrixConfiguration; -use std::collections::BTreeMap; -use std::rc::Rc; /// Builds out multiple generations of `input` based on a matrix style. /// For example, if input is: {"x": ["a", "b"], "y": ["c", "d"]} diff --git a/crates/sprout/src/integrations/bootloader_interface.rs b/crates/sprout/src/integrations/bootloader_interface.rs index 07bd25b..b3cf6a8 100644 --- a/crates/sprout/src/integrations/bootloader_interface.rs +++ b/crates/sprout/src/integrations/bootloader_interface.rs @@ -2,6 +2,9 @@ use crate::integrations::bootloader_interface::bitflags::LoaderFeatures; use crate::platform::timer::PlatformTimer; use crate::utils::device_path_subpath; use crate::utils::variables::{VariableClass, VariableController}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use anyhow::{Context, Result}; use uefi::proto::device_path::DevicePath; use uefi::{Guid, guid}; diff --git a/crates/sprout/src/integrations/shim.rs b/crates/sprout/src/integrations/shim.rs index e85a9a0..21e62b6 100644 --- a/crates/sprout/src/integrations/shim.rs +++ b/crates/sprout/src/integrations/shim.rs @@ -3,10 +3,13 @@ use crate::secure::SecureBoot; use crate::utils; use crate::utils::ResolvedPath; use crate::utils::variables::{VariableClass, VariableController}; +use alloc::boxed::Box; +use alloc::string::ToString; +use alloc::vec::Vec; use anyhow::{Context, Result, anyhow, bail}; +use core::ffi::c_void; +use core::pin::Pin; use log::warn; -use std::ffi::c_void; -use std::pin::Pin; use uefi::Handle; use uefi::boot::LoadImageSource; use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly}; diff --git a/crates/sprout/src/integrations/shim/hook.rs b/crates/sprout/src/integrations/shim/hook.rs index 157bcf5..d8a0876 100644 --- a/crates/sprout/src/integrations/shim/hook.rs +++ b/crates/sprout/src/integrations/shim/hook.rs @@ -1,8 +1,9 @@ use crate::integrations::shim::{ShimInput, ShimSupport, ShimVerificationOutput}; use crate::utils; -use anyhow::{Context, Result, bail}; +use anyhow::{Context, Result}; +use core::slice; use log::warn; -use std::sync::{LazyLock, Mutex}; +use spin::{Lazy, Mutex}; use uefi::proto::device_path::FfiDevicePath; use uefi::proto::unsafe_protocol; use uefi::{Guid, guid}; @@ -45,8 +46,7 @@ struct SecurityHookState { /// Global state for the security hook. /// This is messy, but it is safe given the mutex. -static GLOBAL_HOOK_STATE: LazyLock>> = - LazyLock::new(|| Mutex::new(None)); +static GLOBAL_HOOK_STATE: Lazy>> = Lazy::new(|| Mutex::new(None)); /// Security hook helper. pub struct SecurityHook; @@ -110,24 +110,13 @@ impl SecurityHook { // Verify the input, if it fails, call the original hook. if !Self::verify(input) { // Acquire the global hook state to grab the original hook. - let function = match GLOBAL_HOOK_STATE.lock() { - // We have acquired the lock, so we can find the original hook. - Ok(state) => match state.as_ref() { - // The hook state is available, so we can acquire the original hook. - Some(state) => state.original_hook.file_authentication_state, + let function = match GLOBAL_HOOK_STATE.lock().as_ref() { + // The hook state is available, so we can acquire the original hook. + Some(state) => state.original_hook.file_authentication_state, - // The hook state is not available, so we can't call the original hook. - None => { - warn!("global hook state is not available, unable to call original hook"); - return Status::LOAD_ERROR; - } - }, - - Err(error) => { - warn!( - "unable to acquire global hook state lock to call original hook: {}", - error, - ); + // The hook state is not available, so we can't call the original hook. + None => { + warn!("global hook state is not available, unable to call original hook"); return Status::LOAD_ERROR; } }; @@ -161,7 +150,7 @@ impl SecurityHook { } // Construct a slice out of the file buffer and size. - let buffer = unsafe { std::slice::from_raw_parts(file_buffer, file_size) }; + let buffer = unsafe { slice::from_raw_parts(file_buffer, file_size) }; // Construct a shim input from the path. let input = ShimInput::SecurityHookBuffer(Some(path), buffer); @@ -169,24 +158,13 @@ impl SecurityHook { // Verify the input, if it fails, call the original hook. if !Self::verify(input) { // Acquire the global hook state to grab the original hook. - let function = match GLOBAL_HOOK_STATE.lock() { - // We have acquired the lock, so we can find the original hook. - Ok(state) => match state.as_ref() { - // The hook state is available, so we can acquire the original hook. - Some(state) => state.original_hook2.file_authentication, + let function = match GLOBAL_HOOK_STATE.lock().as_ref() { + // The hook state is available, so we can acquire the original hook. + Some(state) => state.original_hook2.file_authentication, - // The hook state is not available, so we can't call the original hook. - None => { - warn!("global hook state is not available, unable to call original hook"); - return Status::LOAD_ERROR; - } - }, - - Err(error) => { - warn!( - "unable to acquire global hook state lock to call original hook: {}", - error - ); + // The hook state is not available, so we can't call the original hook. + None => { + warn!("global hook state is not available, unable to call original hook"); return Status::LOAD_ERROR; } }; @@ -237,9 +215,7 @@ impl SecurityHook { }; // Acquire the lock to the global state and replace it. - let Ok(mut global_state) = GLOBAL_HOOK_STATE.lock() else { - bail!("unable to acquire global hook state lock"); - }; + let mut global_state = GLOBAL_HOOK_STATE.lock(); global_state.replace(state); // Install the hooks into the UEFI stack. @@ -276,9 +252,7 @@ impl SecurityHook { .context("unable to open security arch2 protocol")?; // Acquire the lock to the global state. - let Ok(mut global_state) = GLOBAL_HOOK_STATE.lock() else { - bail!("unable to acquire global hook state lock"); - }; + let mut global_state = GLOBAL_HOOK_STATE.lock(); // Take the state and replace the original functions. let Some(state) = global_state.take() else { diff --git a/crates/sprout/src/main.rs b/crates/sprout/src/main.rs index 90ad02c..7732bcc 100644 --- a/crates/sprout/src/main.rs +++ b/crates/sprout/src/main.rs @@ -1,8 +1,8 @@ #![doc = include_str!("../README.md")] -#![feature(uefi_std)] +#![no_std] +#![no_main] -/// The delay to wait for when an error occurs in Sprout. -const DELAY_ON_ERROR: Duration = Duration::from_secs(10); +extern crate alloc; use crate::context::{RootContext, SproutContext}; use crate::entries::BootableEntry; @@ -14,13 +14,18 @@ use crate::platform::timer::PlatformTimer; use crate::platform::tpm::PlatformTpm; use crate::secure::SecureBoot; use crate::utils::PartitionGuidForm; +use alloc::collections::BTreeMap; +use alloc::format; +use alloc::string::ToString; +use alloc::vec::Vec; use anyhow::{Context, Result, bail}; +use core::ops::Deref; +use core::time::Duration; use edera_sprout_config::RootConfiguration; use log::{error, info, warn}; -use std::collections::BTreeMap; -use std::ops::Deref; -use std::time::Duration; +use uefi::entry; use uefi::proto::device_path::LoadedImageDevicePath; +use uefi_raw::Status; /// actions: Code that can be configured and executed by Sprout. pub mod actions; @@ -73,6 +78,9 @@ pub mod options; /// utils: Utility functions that are used by other parts of Sprout. pub mod utils; +/// The delay to wait for when an error occurs in Sprout. +const DELAY_ON_ERROR: Duration = Duration::from_secs(10); + /// Run Sprout, returning an error if one occurs. fn run() -> Result<()> { // For safety reasons, we will note that Secure Boot is in beta on Sprout. @@ -373,9 +381,14 @@ fn run() -> Result<()> { /// The main entrypoint of sprout. /// It is possible this function will not return if actions that are executed /// exit boot services or do not return control to sprout. -fn main() -> Result<()> { +#[entry] +fn efi_main() -> Status { // Initialize the basic UEFI environment. - setup::init()?; + // If initialization fails, we will return ABORTED. + if let Err(error) = setup::init() { + error!("unable to initialize environment: {}", error); + return Status::ABORTED; + } // Run Sprout, then handle the error. let result = run(); @@ -387,9 +400,10 @@ fn main() -> Result<()> { } // Sleep to allow the user to read the error. uefi::boot::stall(DELAY_ON_ERROR); + return Status::ABORTED; } // Sprout doesn't necessarily guarantee anything was booted. // If we reach here, we will exit back to whoever called us. - Ok(()) + Status::SUCCESS } diff --git a/crates/sprout/src/menu.rs b/crates/sprout/src/menu.rs index 0487d7e..2ee6ebc 100644 --- a/crates/sprout/src/menu.rs +++ b/crates/sprout/src/menu.rs @@ -1,9 +1,10 @@ use crate::entries::BootableEntry; use crate::integrations::bootloader_interface::BootloaderInterface; use crate::platform::timer::PlatformTimer; +use alloc::vec; use anyhow::{Context, Result, bail}; +use core::time::Duration; use log::{info, warn}; -use std::time::Duration; use uefi::ResultExt; use uefi::boot::TimerTrigger; use uefi::proto::console::text::{Input, Key, ScanCode}; diff --git a/crates/sprout/src/options.rs b/crates/sprout/src/options.rs index 0953ba7..718dd20 100644 --- a/crates/sprout/src/options.rs +++ b/crates/sprout/src/options.rs @@ -1,6 +1,10 @@ use crate::options::parser::{OptionDescription, OptionForm, OptionsRepresentable}; +use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; use anyhow::{Context, Result, bail}; -use std::collections::BTreeMap; + +/// Acquire arguments from UEFI environment. +pub mod env; /// The Sprout options parser. pub mod parser; diff --git a/crates/sprout/src/options/env.rs b/crates/sprout/src/options/env.rs new file mode 100644 index 0000000..261e8cb --- /dev/null +++ b/crates/sprout/src/options/env.rs @@ -0,0 +1,77 @@ +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use anyhow::{Context, Result, bail}; +use uefi::proto::loaded_image::{LoadOptionsError, LoadedImage}; + +/// Loads the command-line arguments passed to Sprout. +pub fn args() -> Result> { + // Acquire the image handle of Sprout. + let handle = uefi::boot::image_handle(); + + // Open the LoadedImage protocol for Sprout. + let loaded_image = uefi::boot::open_protocol_exclusive::(handle) + .context("unable to open loaded image protocol for sprout")?; + + // Load the command-line argument string. + let options = match loaded_image.load_options_as_cstr16() { + // Load options were passed. We will return them for processing. + Ok(options) => options, + + // No load options were passed. We will return an empty vector. + Err(LoadOptionsError::NotSet) => { + return Ok(Vec::new()); + } + + Err(LoadOptionsError::NotAligned) => { + bail!("load options are not properly aligned"); + } + + Err(LoadOptionsError::InvalidString(error)) => { + bail!("load options are not a valid string: {}", error); + } + }; + + // Convert the options to a string. + let options = options.to_string(); + + // Use shlex to parse the options. + // If shlex fails, we will fall back to a simple whitespace split. + let mut args = shlex::split(&options).unwrap_or_else(|| { + options + .split_ascii_whitespace() + .map(|string| string.to_string()) + .collect::>() + }); + + // If there is a first argument, check if it is not an option. + // If it is not, we will assume it is the path to the executable and remove it. + if let Some(arg) = args.first() + && !arg.starts_with('-') + { + args.remove(0); + } + + // Correct firmware that may add invalid arguments at the start. + // Witnessed this on a Dell Precision 5690 when direct booting. + loop { + // Grab the first argument or break. + let Some(arg) = args.first() else { + break; + }; + + // Check if the argument is a valid character. + // If it is not, remove it and continue. + let Some(first_character) = arg.chars().next() else { + break; + }; + + // If the character is not a printable character or a backtick, remove it and continue. + if first_character < 0x1f as char || first_character == '`' { + args.remove(0); + continue; + } + break; + } + + Ok(args) +} diff --git a/crates/sprout/src/options/parser.rs b/crates/sprout/src/options/parser.rs index 574335d..4d07b5e 100644 --- a/crates/sprout/src/options/parser.rs +++ b/crates/sprout/src/options/parser.rs @@ -1,6 +1,10 @@ +use crate::options::env; +use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; use anyhow::{Context, Result, bail}; +use core::ptr::null_mut; use log::info; -use std::collections::BTreeMap; +use uefi_raw::Status; /// The type of option. This disambiguates different behavior /// of how options are handled. @@ -47,23 +51,7 @@ pub trait OptionsRepresentable { // Collect all the arguments to Sprout. // Skip the first argument, which is the path to our executable. - let mut args = std::env::args().skip(1).collect::>(); - - // Correct firmware that may add invalid arguments at the start. - // Witnessed this on a Dell Precision 5690 when direct booting. - loop { - // Grab the first argument or break. - let Some(arg) = args.first() else { - break; - }; - - // If the argument starts with a tilde, remove it. - if arg.starts_with("`") { - args.remove(0); - continue; - } - break; - } + let args = env::args()?; // Represent options as key-value pairs. let mut options = BTreeMap::new(); @@ -77,7 +65,7 @@ pub trait OptionsRepresentable { break; }; - // If the doesn't start with --, that is invalid. + // If the option doesn't start with --, that is invalid. if !option.starts_with("--") { bail!("invalid option: {option}"); } @@ -144,7 +132,9 @@ pub trait OptionsRepresentable { ); } // Exit because the help has been displayed. - std::process::exit(0); + unsafe { + uefi::boot::exit(uefi::boot::image_handle(), Status::SUCCESS, 0, null_mut()); + }; } // Insert the option and the value into the map. diff --git a/crates/sprout/src/phases.rs b/crates/sprout/src/phases.rs index 68c6baa..3a6b14c 100644 --- a/crates/sprout/src/phases.rs +++ b/crates/sprout/src/phases.rs @@ -1,8 +1,9 @@ use crate::actions; use crate::context::SproutContext; +use alloc::format; +use alloc::rc::Rc; use anyhow::{Context, Result}; use edera_sprout_config::phases::PhaseConfiguration; -use std::rc::Rc; /// Executes the specified [phase] of the boot process. /// The value [phase] should be a reference of a specific phase in the [PhasesConfiguration]. diff --git a/crates/sprout/src/platform/timer.rs b/crates/sprout/src/platform/timer.rs index f004dc6..9a8b743 100644 --- a/crates/sprout/src/platform/timer.rs +++ b/crates/sprout/src/platform/timer.rs @@ -1,7 +1,7 @@ // Referenced https://github.com/sheroz/tick_counter (MIT license) as a baseline. // Architecturally modified to support UEFI and remove x86 (32-bit) support. -use std::time::Duration; +use core::time::Duration; /// Support for aarch64 timers. #[cfg(target_arch = "aarch64")] diff --git a/crates/sprout/src/platform/timer/aarch64.rs b/crates/sprout/src/platform/timer/aarch64.rs index 46a57ca..153bd1b 100644 --- a/crates/sprout/src/platform/timer/aarch64.rs +++ b/crates/sprout/src/platform/timer/aarch64.rs @@ -1,5 +1,5 @@ use crate::platform::timer::TickFrequency; -use std::arch::asm; +use core::arch::asm; /// Reads the cntvct_el0 counter and returns the value. pub fn ticks() -> u64 { diff --git a/crates/sprout/src/platform/timer/x86_64.rs b/crates/sprout/src/platform/timer/x86_64.rs index 7736dab..b47567b 100644 --- a/crates/sprout/src/platform/timer/x86_64.rs +++ b/crates/sprout/src/platform/timer/x86_64.rs @@ -1,6 +1,6 @@ use crate::platform::timer::TickFrequency; use core::arch::asm; -use std::time::Duration; +use core::time::Duration; /// We will measure the frequency of the timer based on 1000 microseconds. /// This will result in a call to BS->Stall(1000) in the end. diff --git a/crates/sprout/src/setup.rs b/crates/sprout/src/setup.rs index 127f6cd..45de097 100644 --- a/crates/sprout/src/setup.rs +++ b/crates/sprout/src/setup.rs @@ -1,27 +1,8 @@ use anyhow::{Context, Result}; -use std::os::uefi as uefi_std; /// Initializes the UEFI environment. -/// -/// This fetches the system table and current image handle from uefi_std and injects -/// them into the uefi crate. pub fn init() -> Result<()> { - // Acquire the system table and image handle from the uefi_std environment. - let system_table = uefi_std::env::system_table(); - let image_handle = uefi_std::env::image_handle(); - - // SAFETY: The UEFI variables above come from the Rust std. - // These variables are not-null and calling the uefi crates with these values is validated - // to be corrected by hand. - unsafe { - // Set the system table and image handle. - uefi::table::set_system_table(system_table.as_ptr().cast()); - let handle = uefi::Handle::from_ptr(image_handle.as_ptr().cast()) - .context("unable to resolve image handle")?; - uefi::boot::set_image_handle(handle); - } - - // Initialize the uefi logger mechanism and other helpers. + // Initialize the uefi internals. uefi::helpers::init().context("unable to initialize uefi")?; Ok(()) } diff --git a/crates/sprout/src/utils.rs b/crates/sprout/src/utils.rs index 89b0a2a..62b8fc9 100644 --- a/crates/sprout/src/utils.rs +++ b/crates/sprout/src/utils.rs @@ -1,6 +1,10 @@ +use alloc::borrow::ToOwned; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use anyhow::{Context, Result, bail}; +use core::ops::Deref; use sha2::{Digest, Sha256}; -use std::ops::Deref; use uefi::boot::SearchType; use uefi::fs::{FileSystem, Path}; use uefi::proto::device_path::text::{AllowShortcuts, DevicePathFromText, DisplayOnly}; diff --git a/crates/sprout/src/utils/framebuffer.rs b/crates/sprout/src/utils/framebuffer.rs index 084df36..f5dc3ea 100644 --- a/crates/sprout/src/utils/framebuffer.rs +++ b/crates/sprout/src/utils/framebuffer.rs @@ -1,3 +1,5 @@ +use alloc::vec; +use alloc::vec::Vec; use anyhow::{Context, Result}; use uefi::proto::console::gop::{BltOp, BltPixel, BltRegion, GraphicsOutput}; diff --git a/crates/sprout/src/utils/media_loader.rs b/crates/sprout/src/utils/media_loader.rs index 0b49f2e..6c407b1 100644 --- a/crates/sprout/src/utils/media_loader.rs +++ b/crates/sprout/src/utils/media_loader.rs @@ -1,5 +1,8 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; use anyhow::{Context, Result, bail}; -use std::ffi::c_void; +use core::ffi::c_void; +use core::ptr; use uefi::proto::device_path::DevicePath; use uefi::proto::device_path::build::DevicePathBuilder; use uefi::proto::device_path::build::media::Vendor; @@ -261,8 +264,7 @@ impl MediaLoaderHandle { let protocol = Box::from_raw(self.protocol); // Retrieve a box for the data we passed in. - let slice = - std::ptr::slice_from_raw_parts_mut(protocol.address as *mut u8, protocol.length); + let slice = ptr::slice_from_raw_parts_mut(protocol.address as *mut u8, protocol.length); let data = Box::from_raw(slice); // Drop all the allocations explicitly, as we don't want to leak them. diff --git a/crates/sprout/src/utils/variables.rs b/crates/sprout/src/utils/variables.rs index 22615a5..7dea9cc 100644 --- a/crates/sprout/src/utils/variables.rs +++ b/crates/sprout/src/utils/variables.rs @@ -1,4 +1,7 @@ use crate::utils; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use anyhow::{Context, Result}; use log::warn; use uefi::{CString16, guid}; diff --git a/crates/sprout/src/utils/vercmp.rs b/crates/sprout/src/utils/vercmp.rs index 67c2aa1..c8ead2b 100644 --- a/crates/sprout/src/utils/vercmp.rs +++ b/crates/sprout/src/utils/vercmp.rs @@ -1,5 +1,5 @@ -use std::cmp::Ordering; -use std::iter::Peekable; +use core::cmp::Ordering; +use core::iter::Peekable; /// Handles single character advancement and comparison. macro_rules! handle_single_char { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a962b7a..67dad26 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2025-11-03" -components = ["rustfmt", "rust-std", "clippy"] +channel = "1.91.0" +components = ["rustfmt", "clippy"] targets = ["x86_64-unknown-uefi", "aarch64-unknown-uefi"]