2025-11-03 02:04:21 -05:00
|
|
|
use alloc::string::{String, ToString};
|
2025-10-21 19:12:16 -07:00
|
|
|
use anyhow::{Context, Result, bail};
|
2025-11-14 20:45:44 -08:00
|
|
|
use core::ptr::null_mut;
|
|
|
|
|
use jaarg::alloc::ParseMapResult;
|
|
|
|
|
use jaarg::{
|
|
|
|
|
ErrorUsageWriter, ErrorUsageWriterContext, HelpWriter, HelpWriterContext, Opt, Opts,
|
|
|
|
|
StandardErrorUsageWriter, StandardFullHelpWriter,
|
|
|
|
|
};
|
|
|
|
|
use log::{error, info};
|
|
|
|
|
use uefi_raw::Status;
|
2025-10-20 18:17:29 -07:00
|
|
|
|
|
|
|
|
/// Default configuration file path.
|
|
|
|
|
const DEFAULT_CONFIG_PATH: &str = "\\sprout.toml";
|
|
|
|
|
|
|
|
|
|
/// The parsed options of sprout.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct SproutOptions {
|
2025-10-27 02:38:40 -04:00
|
|
|
/// Configures Sprout automatically based on the environment.
|
|
|
|
|
pub autoconfigure: bool,
|
2025-10-20 18:17:29 -07:00
|
|
|
/// Path to a configuration file to load.
|
|
|
|
|
pub config: String,
|
|
|
|
|
/// Entry to boot without showing the boot menu.
|
|
|
|
|
pub boot: Option<String>,
|
2025-10-26 23:59:50 -04:00
|
|
|
/// Force display of the boot menu.
|
|
|
|
|
pub force_menu: bool,
|
|
|
|
|
/// The timeout for the boot menu in seconds.
|
|
|
|
|
pub menu_timeout: Option<u64>,
|
2025-10-20 18:17:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The default Sprout options.
|
|
|
|
|
impl Default for SproutOptions {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
2025-10-27 02:38:40 -04:00
|
|
|
autoconfigure: false,
|
2025-10-20 18:17:29 -07:00
|
|
|
config: DEFAULT_CONFIG_PATH.to_string(),
|
|
|
|
|
boot: None,
|
2025-10-26 23:59:50 -04:00
|
|
|
force_menu: false,
|
|
|
|
|
menu_timeout: None,
|
2025-10-20 18:17:29 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-21 19:12:16 -07:00
|
|
|
|
|
|
|
|
/// The options parser mechanism for Sprout.
|
2025-11-14 20:45:44 -08:00
|
|
|
impl SproutOptions {
|
|
|
|
|
/// Produces [SproutOptions] from the arguments provided by the UEFI core.
|
|
|
|
|
/// Internally we utilize the `jaarg` argument parser which has excellent no_std support.
|
|
|
|
|
pub fn parse() -> Result<Self> {
|
|
|
|
|
// All the options for the Sprout executable.
|
|
|
|
|
const OPTIONS: Opts<&str> = Opts::new(&[
|
|
|
|
|
Opt::help_flag("help", &["--help"]).help_text("Display Sprout Help"),
|
|
|
|
|
Opt::flag("autoconfigure", &["--autoconfigure"])
|
|
|
|
|
.help_text("Enable Sprout autoconfiguration"),
|
|
|
|
|
Opt::value("config", &["--config"], "PATH")
|
|
|
|
|
.help_text("Path to Sprout configuration file"),
|
|
|
|
|
Opt::value("boot", &["--boot"], "ENTRY").help_text("Entry to boot, bypassing the menu"),
|
|
|
|
|
Opt::flag("force-menu", &["--force-menu"]).help_text("Force showing the boot menu"),
|
|
|
|
|
Opt::value("menu-timeout", &["--menu-timeout"], "TIMEOUT")
|
|
|
|
|
.help_text("Boot menu timeout, in seconds"),
|
|
|
|
|
]);
|
2025-10-21 19:12:16 -07:00
|
|
|
|
2025-11-14 20:45:44 -08:00
|
|
|
// Acquire the arguments as determined by the UEFI core.
|
|
|
|
|
let args = eficore::env::args()?;
|
|
|
|
|
|
|
|
|
|
// Parse the OPTIONS into a map using jaarg.
|
|
|
|
|
let parsed = match OPTIONS.parse_map(
|
|
|
|
|
"sprout",
|
|
|
|
|
args.iter(),
|
|
|
|
|
|program_name| {
|
|
|
|
|
let ctx = HelpWriterContext {
|
|
|
|
|
options: &OPTIONS,
|
|
|
|
|
program_name,
|
|
|
|
|
};
|
|
|
|
|
info!("{}", StandardFullHelpWriter::new(ctx));
|
|
|
|
|
},
|
|
|
|
|
|program_name, error| {
|
|
|
|
|
let ctx = ErrorUsageWriterContext {
|
|
|
|
|
options: &OPTIONS,
|
|
|
|
|
program_name,
|
|
|
|
|
error,
|
|
|
|
|
};
|
|
|
|
|
error!("{}", StandardErrorUsageWriter::new(ctx));
|
|
|
|
|
},
|
|
|
|
|
) {
|
|
|
|
|
ParseMapResult::Map(map) => map,
|
|
|
|
|
ParseMapResult::ExitSuccess => unsafe {
|
|
|
|
|
uefi::boot::exit(uefi::boot::image_handle(), Status::SUCCESS, 0, null_mut());
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
ParseMapResult::ExitFailure => unsafe {
|
|
|
|
|
uefi::boot::exit(uefi::boot::image_handle(), Status::ABORTED, 0, null_mut());
|
|
|
|
|
},
|
|
|
|
|
};
|
2025-10-21 19:12:16 -07:00
|
|
|
|
|
|
|
|
// Use the default value of sprout options and have the raw options be parsed into it.
|
|
|
|
|
let mut result = Self::default();
|
|
|
|
|
|
2025-11-14 20:45:44 -08:00
|
|
|
for (key, value) in parsed {
|
|
|
|
|
match key {
|
2025-10-27 02:38:40 -04:00
|
|
|
"autoconfigure" => {
|
|
|
|
|
// Enable autoconfiguration.
|
|
|
|
|
result.autoconfigure = true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-21 19:12:16 -07:00
|
|
|
"config" => {
|
|
|
|
|
// The configuration file to load.
|
2025-11-14 20:45:44 -08:00
|
|
|
result.config = value;
|
2025-10-21 19:12:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"boot" => {
|
|
|
|
|
// The entry to boot.
|
2025-11-14 20:45:44 -08:00
|
|
|
result.boot = Some(value);
|
2025-10-21 19:12:16 -07:00
|
|
|
}
|
|
|
|
|
|
2025-10-26 23:59:50 -04:00
|
|
|
"force-menu" => {
|
|
|
|
|
// Force showing of the boot menu.
|
|
|
|
|
result.force_menu = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"menu-timeout" => {
|
|
|
|
|
// The timeout for the boot menu in seconds.
|
|
|
|
|
let value = value
|
|
|
|
|
.parse::<u64>()
|
|
|
|
|
.context("menu-timeout must be a number")?;
|
|
|
|
|
result.menu_timeout = Some(value);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-21 19:12:16 -07:00
|
|
|
_ => bail!("unknown option: --{key}"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(result)
|
|
|
|
|
}
|
|
|
|
|
}
|