Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 69 additions & 47 deletions crates/boot/src/actions/edera.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{actions, context::SproutContext};
use alloc::rc::Rc;
use alloc::string::String;
use alloc::vec::Vec;
use alloc::{format, vec};
use anyhow::{Context, Result};
use edera_sprout_config::actions::chainload::ChainloadConfiguration;
Expand All @@ -12,74 +12,96 @@ use eficore::media_loader::{
XEN_EFI_CONFIG_MEDIA_GUID, XEN_EFI_KERNEL_MEDIA_GUID, XEN_EFI_RAMDISK_MEDIA_GUID,
},
};
use eficore::platform::tpm::PlatformTpm;
use uefi::Guid;

/// Builds a configuration string for the Xen EFI stub using the specified `configuration`.
fn make_xen_config(context: Rc<SproutContext>, configuration: &EderaConfiguration) -> String {
let xen_options = combine_options(context.stamp_iter(configuration.xen_options.iter()));
let kernel_options = combine_options(context.stamp_iter(configuration.kernel_options.iter()));
build_xen_config(&xen_options, &kernel_options)
}

/// 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.
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)) /* */
}

/// Register a media loader for the file `path` with the vendor `guid`.
/// Register a media loader for the provided `bytes` 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.
fn register_media_loader_file(
context: &Rc<SproutContext>,
fn register_media_loader_bytes(
guid: Guid,
what: &str,
path: &str,
bytes: Vec<u8>,
) -> Result<MediaLoaderHandle> {
// Stamp the path to the file.
MediaLoaderHandle::register(guid, bytes.into_boxed_slice())
.context(format!("unable to register {} media loader", what))
}

/// Read the contents of the loader payload at `path` relative to the sprout image.
/// `what` should indicate some identifying value for error messages
/// like `kernel` or `initrd`.
fn read_loader_payload(context: &Rc<SproutContext>, what: &str, path: &str) -> Result<Vec<u8>> {
let path = context.stamp(path);
// Read the file contents.
let content =
eficore::path::read_file_contents(Some(context.root().loaded_image_path()?), &path)
.context(format!("unable to read {} file", what))?;
// Register the media loader.
let handle = MediaLoaderHandle::register(guid, content.into_boxed_slice())
.context(format!("unable to register {} media loader", what))?;
Ok(handle)
eficore::path::read_file_contents(Some(context.root().loaded_image_path()?), &path)
.context(format!("unable to read {} file", what))
}

/// 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.
pub fn edera(context: Rc<SproutContext>, configuration: &EderaConfiguration) -> Result<()> {
// Build the Xen config file content for this configuration.
let config = make_xen_config(context.clone(), configuration);
// Only register the initrd media loader if the user actually configured one.
let xen_opts = combine_options(context.stamp_iter(configuration.xen_options.iter()));
let dom0_args = combine_options(context.stamp_iter(configuration.kernel_options.iter()));

// Measure xen options and dom0 cmdline into PCR 12 in a fixed order.
PlatformTpm::log_event(
PlatformTpm::PCR_KERNEL_PARAMETERS,
xen_opts.as_bytes(),
"sprout: xen options",
)
.context("unable to measure xen options into the TPM")?;
PlatformTpm::log_event(
PlatformTpm::PCR_KERNEL_PARAMETERS,
dom0_args.as_bytes(),
"sprout: dom0 cmdline",
)
.context("unable to measure dom0 cmdline into the TPM")?;

// Build the Xen config text and register it as the config media loader. The
// assembled text is a derived artifact, intentionally not measured.
let config_handle = register_media_loader_bytes(
XEN_EFI_CONFIG_MEDIA_GUID,
"config",
build_xen_config(&xen_opts, &dom0_args).into_bytes(),
)
.context("unable to register config media loader")?;

// Register the media loader for the config.
let config = register_media_loader_text(XEN_EFI_CONFIG_MEDIA_GUID, "config", config)
.context("unable to register config media loader")?;
// Read the dom0 kernel, measure it into PCR 11, then register the kernel
// media loader.
let kernel_bytes = read_loader_payload(&context, "kernel", &configuration.kernel)?;
PlatformTpm::log_event(
PlatformTpm::PCR_KERNEL,
&kernel_bytes,
"sprout: dom0 kernel",
)
.context("unable to measure dom0 kernel into the TPM")?;
let kernel_handle =
register_media_loader_bytes(XEN_EFI_KERNEL_MEDIA_GUID, "kernel", kernel_bytes)
.context("unable to register kernel media loader")?;

// Register the media loaders for the kernel.
let kernel = register_media_loader_file(
&context,
XEN_EFI_KERNEL_MEDIA_GUID,
"kernel",
&configuration.kernel,
// Extend PCR 9 with the initrd bytes (empty when no initrd is
// configured).
let initrd_bytes = match empty_is_none(configuration.initrd.as_ref()) {
Some(p) => read_loader_payload(&context, "initrd", p)?,
None => Vec::new(),
};
PlatformTpm::log_event(
PlatformTpm::PCR_INITRD,
&initrd_bytes,
"sprout: dom0 initrd",
)
.context("unable to register kernel media loader")?;
.context("unable to measure dom0 initrd into the TPM")?;

// Create a vector of media loaders to drop them only after this function completes.
let mut media_loaders = vec![config, kernel];
let mut media_loaders = vec![config_handle, kernel_handle];

// Register the initrd if it is provided.
if let Some(initrd) = empty_is_none(configuration.initrd.as_ref()) {
let initrd =
register_media_loader_file(&context, XEN_EFI_RAMDISK_MEDIA_GUID, "initrd", initrd)
if !initrd_bytes.is_empty() {
let initrd_handle =
register_media_loader_bytes(XEN_EFI_RAMDISK_MEDIA_GUID, "initrd", initrd_bytes)
.context("unable to register initrd media loader")?;
media_loaders.push(initrd);
media_loaders.push(initrd_handle);
}

// Chainload to the Xen EFI stub.
Expand Down
9 changes: 9 additions & 0 deletions crates/eficore/src/platform/tpm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ impl PlatformTpm {
/// The PCR for measuring the bootloader configuration into.
pub const PCR_BOOT_LOADER_CONFIG: PcrIndex = PcrIndex(5);

/// The PCR for measuring the dom0 initrd payload into.
pub const PCR_INITRD: PcrIndex = PcrIndex(9);

/// The PCR for measuring the dom0 kernel payload into.
pub const PCR_KERNEL: PcrIndex = PcrIndex(11);

/// The PCR for measuring kernel command line and xen options into.
pub const PCR_KERNEL_PARAMETERS: PcrIndex = PcrIndex(12);

/// Acquire access to the TPM protocol handle, if possible.
/// Returns None if TPM is not available.
fn protocol() -> Result<Option<TpmProtocolHandle>> {
Expand Down
Loading