diff --git a/crates/boot/src/actions/edera.rs b/crates/boot/src/actions/edera.rs index 363b8a4..578744e 100644 --- a/crates/boot/src/actions/edera.rs +++ b/crates/boot/src/actions/edera.rs @@ -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; @@ -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, 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::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, +fn register_media_loader_bytes( guid: Guid, what: &str, - path: &str, + bytes: Vec, ) -> Result { - // 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, what: &str, path: &str) -> Result> { 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, 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. diff --git a/crates/eficore/src/platform/tpm.rs b/crates/eficore/src/platform/tpm.rs index 88ee913..5ea04e7 100644 --- a/crates/eficore/src/platform/tpm.rs +++ b/crates/eficore/src/platform/tpm.rs @@ -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> {