2025-10-19 20:23:55 -07:00
|
|
|
#![doc = include_str!("../README.md")]
|
2025-10-01 16:45:04 -07:00
|
|
|
#![feature(uefi_std)]
|
2025-10-04 23:12:01 -07:00
|
|
|
|
2025-10-11 14:35:29 -07:00
|
|
|
use crate::context::{RootContext, SproutContext};
|
2025-10-14 12:47:33 -07:00
|
|
|
use crate::phases::phase;
|
2025-10-20 18:17:29 -07:00
|
|
|
use anyhow::{Context, Result};
|
2025-10-05 00:09:53 -07:00
|
|
|
use log::info;
|
2025-10-13 00:55:11 -07:00
|
|
|
use std::collections::BTreeMap;
|
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
use uefi::proto::device_path::LoadedImageDevicePath;
|
|
|
|
|
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
|
2025-10-04 23:12:01 -07:00
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
/// actions: Code that can be configured and executed by Sprout.
|
2025-10-04 23:12:01 -07:00
|
|
|
pub mod actions;
|
2025-10-20 00:06:46 -07:00
|
|
|
|
|
|
|
|
/// config: Sprout configuration mechanism.
|
2025-10-01 18:25:49 -07:00
|
|
|
pub mod config;
|
2025-10-20 00:06:46 -07:00
|
|
|
|
|
|
|
|
/// context: Stored values that can be cheaply forked and cloned.
|
2025-10-04 23:12:01 -07:00
|
|
|
pub mod context;
|
2025-10-20 00:06:46 -07:00
|
|
|
|
|
|
|
|
/// drivers: EFI drivers to load and provide extra functionality.
|
2025-10-12 22:39:56 -07:00
|
|
|
pub mod drivers;
|
2025-10-20 00:06:46 -07:00
|
|
|
|
|
|
|
|
/// entries: Boot menu entries that have a title and can execute actions.
|
2025-10-14 12:47:33 -07:00
|
|
|
pub mod entries;
|
2025-10-20 00:06:46 -07:00
|
|
|
|
|
|
|
|
/// extractors: Runtime code that can extract values into the Sprout context.
|
2025-10-13 00:55:11 -07:00
|
|
|
pub mod extractors;
|
2025-10-20 00:06:46 -07:00
|
|
|
|
|
|
|
|
/// generators: Runtime code that can generate entries with specific values.
|
2025-10-04 23:12:01 -07:00
|
|
|
pub mod generators;
|
2025-10-20 00:06:46 -07:00
|
|
|
|
|
|
|
|
/// phases: Hooks into specific parts of the boot process.
|
2025-10-14 12:47:33 -07:00
|
|
|
pub mod phases;
|
2025-10-20 00:06:46 -07:00
|
|
|
|
|
|
|
|
/// setup: Code that initializes the UEFI environment for Sprout.
|
2025-10-01 16:45:04 -07:00
|
|
|
pub mod setup;
|
2025-10-20 00:06:46 -07:00
|
|
|
|
2025-10-20 18:17:29 -07:00
|
|
|
/// options: Parse the options of the Sprout executable.
|
|
|
|
|
pub mod options;
|
|
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
/// utils: Utility functions that are used by other parts of Sprout.
|
2025-10-01 21:30:43 -07:00
|
|
|
pub mod utils;
|
2025-10-01 16:45:04 -07:00
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
/// 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.
|
2025-10-11 14:35:29 -07:00
|
|
|
fn main() -> Result<()> {
|
2025-10-19 20:23:55 -07:00
|
|
|
// Initialize the basic UEFI environment.
|
2025-10-11 14:35:29 -07:00
|
|
|
setup::init()?;
|
2025-10-01 18:25:49 -07:00
|
|
|
|
2025-10-20 18:17:29 -07:00
|
|
|
// Parse the options to the sprout executable.
|
|
|
|
|
let options = options::parse().context("unable to parse options")?;
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Load the configuration of sprout.
|
|
|
|
|
// At this point, the configuration has been validated and the specified
|
|
|
|
|
// version is checked to ensure compatibility.
|
2025-10-20 18:17:29 -07:00
|
|
|
let config = config::loader::load(&options)?;
|
2025-10-05 00:09:53 -07:00
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Load the root context.
|
|
|
|
|
// This is done in a block to ensure the release of the LoadedImageDevicePath protocol.
|
2025-10-13 00:55:11 -07:00
|
|
|
let mut root = {
|
|
|
|
|
let current_image_device_path_protocol = uefi::boot::open_protocol_exclusive::<
|
|
|
|
|
LoadedImageDevicePath,
|
|
|
|
|
>(uefi::boot::image_handle())
|
2025-10-14 12:47:33 -07:00
|
|
|
.context("unable to get loaded image device path")?;
|
2025-10-13 00:55:11 -07:00
|
|
|
let loaded_image_path = current_image_device_path_protocol.deref().to_boxed();
|
|
|
|
|
info!(
|
|
|
|
|
"loaded image path: {}",
|
|
|
|
|
loaded_image_path.to_string(DisplayOnly(false), AllowShortcuts(false))?
|
|
|
|
|
);
|
2025-10-20 18:17:29 -07:00
|
|
|
RootContext::new(loaded_image_path, options)
|
2025-10-13 00:55:11 -07:00
|
|
|
};
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Insert the configuration actions into the root context.
|
2025-10-04 23:12:01 -07:00
|
|
|
root.actions_mut().extend(config.actions.clone());
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Create a new sprout context with the root context.
|
2025-10-11 14:35:29 -07:00
|
|
|
let mut context = SproutContext::new(root);
|
2025-10-19 20:23:55 -07:00
|
|
|
|
|
|
|
|
// Insert the configuration values into the sprout context.
|
2025-10-04 23:12:01 -07:00
|
|
|
context.insert(&config.values);
|
2025-10-19 20:23:55 -07:00
|
|
|
|
|
|
|
|
// Freeze the sprout context so it can be shared and cheaply cloned.
|
2025-10-04 23:12:01 -07:00
|
|
|
let context = context.freeze();
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Execute the early phase.
|
2025-10-14 12:47:33 -07:00
|
|
|
phase(context.clone(), &config.phases.early).context("unable to execute early phase")?;
|
2025-10-13 16:23:08 -07:00
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Load all configured drivers.
|
2025-10-14 12:47:33 -07:00
|
|
|
drivers::load(context.clone(), &config.drivers).context("unable to load drivers")?;
|
2025-10-12 22:39:56 -07:00
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Run all the extractors declared in the configuration.
|
2025-10-13 00:55:11 -07:00
|
|
|
let mut extracted = BTreeMap::new();
|
|
|
|
|
for (name, extractor) in &config.extractors {
|
|
|
|
|
let value = extractors::extract(context.clone(), extractor)
|
2025-10-14 12:47:33 -07:00
|
|
|
.context(format!("unable to extract value {}", name))?;
|
2025-10-13 00:55:11 -07:00
|
|
|
info!("extracted value {}: {}", name, value);
|
|
|
|
|
extracted.insert(name.clone(), value);
|
|
|
|
|
}
|
|
|
|
|
let mut context = context.fork();
|
2025-10-19 20:23:55 -07:00
|
|
|
// Insert the extracted values into the sprout context.
|
2025-10-13 00:55:11 -07:00
|
|
|
context.insert(&extracted);
|
|
|
|
|
let context = context.freeze();
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Execute the late phase.
|
2025-10-14 12:47:33 -07:00
|
|
|
phase(context.clone(), &config.phases.startup).context("unable to execute startup phase")?;
|
2025-10-04 23:12:01 -07:00
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
let mut staged_entries = Vec::new();
|
2025-10-04 23:12:01 -07:00
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Insert all the static entries from the configuration into the entry list.
|
2025-10-04 23:12:01 -07:00
|
|
|
for (_name, entry) in config.entries {
|
2025-10-19 20:23:55 -07:00
|
|
|
// Associate the main context with the static entry.
|
|
|
|
|
staged_entries.push((context.clone(), entry));
|
2025-10-04 23:12:01 -07:00
|
|
|
}
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Run all the generators declared in the configuration.
|
2025-10-04 23:12:01 -07:00
|
|
|
for (_name, generator) in config.generators {
|
|
|
|
|
let context = context.fork().freeze();
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Add all the entries generated by the generator to the entry list.
|
|
|
|
|
// The generator specifies the context associated with the entry.
|
2025-10-11 14:35:29 -07:00
|
|
|
for entry in generators::generate(context.clone(), &generator)? {
|
2025-10-19 20:23:55 -07:00
|
|
|
staged_entries.push(entry);
|
2025-10-04 23:12:01 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Build a list of all the final boot entries.
|
2025-10-04 23:33:23 -07:00
|
|
|
let mut final_entries = Vec::new();
|
2025-10-19 20:23:55 -07:00
|
|
|
for (context, entry) in staged_entries {
|
2025-10-04 23:12:01 -07:00
|
|
|
let mut context = context.fork();
|
2025-10-19 20:23:55 -07:00
|
|
|
// Insert the values from the entry configuration into the
|
|
|
|
|
// sprout context to use with the entry itself.
|
2025-10-04 23:12:01 -07:00
|
|
|
context.insert(&entry.values);
|
|
|
|
|
let context = context.finalize().freeze();
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Insert the entry configuration into final boot entries with the extended context.
|
2025-10-04 23:33:23 -07:00
|
|
|
final_entries.push((context, entry));
|
2025-10-04 23:12:01 -07:00
|
|
|
}
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// TODO(azenla): Implement boot menu here.
|
|
|
|
|
// For now, we just print all of the entries.
|
2025-10-05 00:09:53 -07:00
|
|
|
info!("entries:");
|
2025-10-04 23:33:23 -07:00
|
|
|
for (index, (context, entry)) in final_entries.iter().enumerate() {
|
|
|
|
|
let title = context.stamp(&entry.title);
|
2025-10-05 00:09:53 -07:00
|
|
|
info!(" entry {}: {}", index + 1, title);
|
2025-10-04 23:33:23 -07:00
|
|
|
}
|
|
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Execute the late phase.
|
2025-10-14 12:47:33 -07:00
|
|
|
phase(context.clone(), &config.phases.late).context("unable to execute late phase")?;
|
2025-10-13 16:23:08 -07:00
|
|
|
|
2025-10-20 18:17:29 -07:00
|
|
|
// Use the boot option if possible, otherwise pick the first entry.
|
|
|
|
|
let (context, entry) = if let Some(ref boot) = context.root().options().boot {
|
|
|
|
|
final_entries
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|(_context, entry)| &entry.title == boot)
|
|
|
|
|
.context(format!("unable to find entry: {boot}"))?
|
|
|
|
|
} else {
|
|
|
|
|
final_entries.first().context("no entries found")?
|
2025-10-19 02:25:55 -07:00
|
|
|
};
|
2025-10-04 23:33:23 -07:00
|
|
|
|
2025-10-19 20:23:55 -07:00
|
|
|
// Execute all the actions for the selected entry.
|
2025-10-04 23:33:23 -07:00
|
|
|
for action in &entry.actions {
|
2025-10-05 00:09:53 -07:00
|
|
|
let action = context.stamp(action);
|
2025-10-11 14:35:29 -07:00
|
|
|
actions::execute(context.clone(), &action)
|
2025-10-14 12:47:33 -07:00
|
|
|
.context(format!("unable to execute action '{}'", action))?;
|
2025-10-01 21:30:43 -07:00
|
|
|
}
|
2025-10-19 20:23:55 -07:00
|
|
|
|
|
|
|
|
// Sprout doesn't necessarily guarantee anything was booted.
|
|
|
|
|
// If we reach here, we will exit back to whoever called us.
|
2025-10-11 14:35:29 -07:00
|
|
|
Ok(())
|
2025-10-01 16:45:04 -07:00
|
|
|
}
|