2025-10-14 22:36:22 -07:00
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
|
|
|
|
use anyhow::{Context, Result};
|
2025-10-18 23:26:05 -07:00
|
|
|
use log::error;
|
2025-10-14 22:36:22 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use uefi::Guid;
|
|
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
|
actions::{self, chainload::ChainloadConfiguration},
|
|
|
|
|
context::SproutContext,
|
|
|
|
|
utils::{
|
|
|
|
|
self,
|
|
|
|
|
media_loader::{
|
|
|
|
|
MediaLoaderHandle,
|
2025-10-18 23:49:00 -07:00
|
|
|
constants::xen::{
|
2025-10-14 22:36:22 -07:00
|
|
|
XEN_EFI_CONFIG_MEDIA_GUID, XEN_EFI_KERNEL_MEDIA_GUID, XEN_EFI_RAMDISK_MEDIA_GUID,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
/// 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.
|
2025-10-14 22:36:22 -07:00
|
|
|
#[derive(Serialize, Deserialize, Default, Clone)]
|
|
|
|
|
pub struct EderaConfiguration {
|
2025-10-20 00:06:46 -07:00
|
|
|
/// The path to the Xen hypervisor EFI image.
|
2025-10-14 22:36:22 -07:00
|
|
|
pub xen: String,
|
2025-10-20 00:06:46 -07:00
|
|
|
/// The path to the kernel to boot for dom0.
|
2025-10-14 22:36:22 -07:00
|
|
|
pub kernel: String,
|
2025-10-20 00:06:46 -07:00
|
|
|
/// The path to the initrd to load for dom0.
|
2025-10-14 22:36:22 -07:00
|
|
|
#[serde(default)]
|
|
|
|
|
pub initrd: Option<String>,
|
2025-10-20 00:06:46 -07:00
|
|
|
/// The options to pass to the kernel.
|
2025-10-14 22:36:22 -07:00
|
|
|
#[serde(default, rename = "kernel-options")]
|
|
|
|
|
pub kernel_options: Vec<String>,
|
2025-10-20 00:06:46 -07:00
|
|
|
/// The options to pass to the Xen hypervisor.
|
2025-10-14 22:36:22 -07:00
|
|
|
#[serde(default, rename = "xen-options")]
|
|
|
|
|
pub xen_options: Vec<String>,
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
/// Builds a configuration string for the Xen EFI stub using the specified `configuration`.
|
2025-10-14 22:36:22 -07:00
|
|
|
fn build_xen_config(configuration: &EderaConfiguration) -> String {
|
2025-10-20 00:06:46 -07:00
|
|
|
// xen config file format is ini-like
|
2025-10-14 22:36:22 -07:00
|
|
|
[
|
2025-10-20 00:06:46 -07:00
|
|
|
// global section
|
2025-10-14 22:36:22 -07:00
|
|
|
"[global]".to_string(),
|
2025-10-20 00:06:46 -07:00
|
|
|
// default configuration section
|
2025-10-14 22:36:22 -07:00
|
|
|
"default=sprout".to_string(),
|
2025-10-20 00:06:46 -07:00
|
|
|
// configuration section for sprout
|
2025-10-14 22:36:22 -07:00
|
|
|
"[sprout]".to_string(),
|
2025-10-20 00:06:46 -07:00
|
|
|
// xen options
|
2025-10-14 22:36:22 -07:00
|
|
|
format!("options={}", configuration.xen_options.join(" ")),
|
2025-10-20 00:06:46 -07:00
|
|
|
// kernel options, stub replaces the kernel path
|
|
|
|
|
// the kernel is provided via media loader
|
2025-10-14 22:36:22 -07:00
|
|
|
format!("kernel=stub {}", configuration.kernel_options.join(" ")),
|
2025-10-20 00:06:46 -07:00
|
|
|
// required or else the last line will be ignored
|
|
|
|
|
"".to_string(),
|
2025-10-14 22:36:22 -07:00
|
|
|
]
|
|
|
|
|
.join("\n")
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
/// Register a media loader for some `text` with the vendor `guid`.
|
|
|
|
|
/// `what` should indicate some identifying value for error messages
|
|
|
|
|
/// like `config` or `kernel`.
|
|
|
|
|
/// Provides a [MediaLoaderHandle] that can be used to unregister the media loader.
|
2025-10-14 22:36:22 -07:00
|
|
|
fn register_media_loader_text(guid: Guid, what: &str, text: String) -> Result<MediaLoaderHandle> {
|
|
|
|
|
MediaLoaderHandle::register(guid, text.as_bytes().to_vec().into_boxed_slice())
|
|
|
|
|
.context(format!("unable to register {} media loader", what)) /* */
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
/// Register a media loader for the file `path` with the vendor `guid`.
|
|
|
|
|
/// `what` should indicate some identifying value for error messages
|
|
|
|
|
/// like `config` or `kernel`.
|
|
|
|
|
/// Provides a [MediaLoaderHandle] that can be used to unregister the media loader.
|
2025-10-14 22:36:22 -07:00
|
|
|
fn register_media_loader_file(
|
|
|
|
|
context: &Rc<SproutContext>,
|
|
|
|
|
guid: Guid,
|
|
|
|
|
what: &str,
|
|
|
|
|
path: &str,
|
|
|
|
|
) -> Result<MediaLoaderHandle> {
|
2025-10-20 00:06:46 -07:00
|
|
|
// Stamp the path to the file.
|
2025-10-14 22:36:22 -07:00
|
|
|
let path = context.stamp(path);
|
2025-10-20 00:06:46 -07:00
|
|
|
// Read the file contents.
|
2025-10-14 22:36:22 -07:00
|
|
|
let content = utils::read_file_contents(context.root().loaded_image_path()?, &path)
|
|
|
|
|
.context(format!("unable to read {} file", what))?;
|
2025-10-20 00:06:46 -07:00
|
|
|
// Register the media loader.
|
2025-10-14 22:36:22 -07:00
|
|
|
let handle = MediaLoaderHandle::register(guid, content.into_boxed_slice())
|
|
|
|
|
.context(format!("unable to register {} media loader", what))?;
|
|
|
|
|
Ok(handle)
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
/// Executes the edera action which will boot the Edera hypervisor with the specified
|
|
|
|
|
/// `configuration` and `context`. This action uses Edera-specific Xen EFI stub functionality.
|
2025-10-14 22:36:22 -07:00
|
|
|
pub fn edera(context: Rc<SproutContext>, configuration: &EderaConfiguration) -> Result<()> {
|
2025-10-20 00:06:46 -07:00
|
|
|
// Build the Xen config file content for this configuration.
|
2025-10-14 22:36:22 -07:00
|
|
|
let config = build_xen_config(configuration);
|
2025-10-20 00:06:46 -07:00
|
|
|
|
|
|
|
|
// Register the media loader for the config.
|
2025-10-18 23:26:05 -07:00
|
|
|
let config = register_media_loader_text(XEN_EFI_CONFIG_MEDIA_GUID, "config", config)
|
|
|
|
|
.context("unable to register config media loader")?;
|
2025-10-20 00:06:46 -07:00
|
|
|
|
|
|
|
|
// Register the media loaders for the kernel.
|
2025-10-14 22:36:22 -07:00
|
|
|
let kernel = register_media_loader_file(
|
|
|
|
|
&context,
|
|
|
|
|
XEN_EFI_KERNEL_MEDIA_GUID,
|
|
|
|
|
"kernel",
|
|
|
|
|
&configuration.kernel,
|
2025-10-18 23:26:05 -07:00
|
|
|
)
|
|
|
|
|
.context("unable to register kernel media loader")?;
|
2025-10-14 22:36:22 -07:00
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
// Create a vector of media loaders to unregister on error.
|
2025-10-14 22:36:22 -07:00
|
|
|
let mut media_loaders = vec![config, kernel];
|
|
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
// Register the initrd if it is provided.
|
2025-10-14 22:36:22 -07:00
|
|
|
if let Some(ref initrd) = configuration.initrd {
|
|
|
|
|
let initrd =
|
2025-10-18 23:26:05 -07:00
|
|
|
register_media_loader_file(&context, XEN_EFI_RAMDISK_MEDIA_GUID, "initrd", initrd)
|
|
|
|
|
.context("unable to register initrd media loader")?;
|
2025-10-14 22:36:22 -07:00
|
|
|
media_loaders.push(initrd);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
// Chainload to the Xen EFI stub.
|
2025-10-14 22:36:22 -07:00
|
|
|
let result = actions::chainload::chainload(
|
|
|
|
|
context.clone(),
|
|
|
|
|
&ChainloadConfiguration {
|
|
|
|
|
path: configuration.xen.clone(),
|
|
|
|
|
options: vec![],
|
|
|
|
|
linux_initrd: None,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.context("unable to chainload to xen");
|
|
|
|
|
|
2025-10-20 00:06:46 -07:00
|
|
|
// Unregister the media loaders on error.
|
2025-10-14 22:36:22 -07:00
|
|
|
for media_loader in media_loaders {
|
2025-10-18 23:26:05 -07:00
|
|
|
if let Err(error) = media_loader.unregister() {
|
|
|
|
|
error!("unable to unregister media loader: {}", error);
|
|
|
|
|
}
|
2025-10-14 22:36:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
}
|