diff --git a/Cargo.lock b/Cargo.lock index d347cec..5435c0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,7 @@ dependencies = [ "hex", "jaarg", "log", + "serde_ignored", "sha2", "toml", "uefi", @@ -237,6 +238,16 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_ignored" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_spanned" version = "1.1.1" diff --git a/Cargo.toml b/Cargo.toml index c584f8c..d2adb48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,10 @@ version = "1.0.229" default-features = false features = ["alloc", "derive"] +[workspace.dependencies.serde_ignored] +version = "0.1.14" +default-features = false + [workspace.dependencies.sha2] version = "0.11.0" default-features = false diff --git a/crates/boot/Cargo.toml b/crates/boot/Cargo.toml index c86cb09..8db728b 100644 --- a/crates/boot/Cargo.toml +++ b/crates/boot/Cargo.toml @@ -15,6 +15,7 @@ edera-sprout-bls.path = "../bls" edera-sprout-parsing.path = "../parsing" hex.workspace = true jaarg.workspace = true +serde_ignored.workspace = true sha2.workspace = true toml.workspace = true log.workspace = true diff --git a/crates/boot/src/config/loader.rs b/crates/boot/src/config/loader.rs index da45b78..9d219a3 100644 --- a/crates/boot/src/config/loader.rs +++ b/crates/boot/src/config/loader.rs @@ -1,10 +1,14 @@ use crate::options::SproutOptions; +use alloc::format; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use anyhow::{Context, Result, bail}; +use core::fmt::Display; use core::ops::Deref; use edera_sprout_config::{RootConfiguration, latest_version}; use eficore::platform::tpm::PlatformTpm; -use log::info; +use log::{info, warn}; +use serde_ignored::Path; use toml::Value; use uefi::proto::device_path::LoadedImageDevicePath; @@ -35,6 +39,59 @@ fn load_raw_config(options: &SproutOptions) -> Result> { Ok(content) } +/// Format the configuration key `path` with dots, like "actions.linux.chainload.path". +fn key_path(path: &Path) -> String { + // Joins the `parent` path and the `key` with a dot, unless the parent is the root. + fn join(parent: &Path, key: impl Display) -> String { + match parent { + Path::Root => key.to_string(), + parent => format!("{}.{}", key_path(parent), key), + } + } + + match path { + Path::Root => String::new(), + Path::Seq { parent, index } => join(parent, index), + Path::Map { parent, key } => join(parent, key), + // Optional values and wrapper types don't add a key of their own. + Path::Some { parent } + | Path::NewtypeStruct { parent } + | Path::NewtypeVariant { parent } => key_path(parent), + } +} + +/// Warn about each declaration in `config` that configures more than one kind. +/// For example, an action with both chainload and print configured only runs chainload. +fn warn_multiple_kinds(config: &RootConfiguration) { + for (name, action) in &config.actions { + let kinds = [ + action.chainload.is_some(), + action.print.is_some(), + action.edera.is_some(), + ]; + if kinds.iter().filter(|kind| **kind).count() > 1 { + warn!( + "action {} configures more than one action, only one is used", + name + ); + } + } + + for (name, generator) in &config.generators { + let kinds = [ + generator.matrix.is_some(), + generator.bls.is_some(), + generator.list.is_some(), + ]; + if kinds.iter().filter(|kind| **kind).count() > 1 { + warn!( + "generator {} configures more than one generator, only one is used", + name + ); + } + } +} + /// Loads the [RootConfiguration] for Sprout. pub fn load(options: &SproutOptions) -> Result { // Load the raw configuration from the sprout config file. @@ -59,9 +116,15 @@ pub fn load(options: &SproutOptions) -> Result { } // If the version is supported, parse the full configuration. - let config: RootConfiguration = value - .try_into() - .context("unable to parse sprout.toml file")?; + // Keys that aren't part of the configuration, such as misspelled keys, are ignored. + // Warn about each one, as the configuration may not do what was intended. + let config: RootConfiguration = serde_ignored::deserialize(value, |path| { + warn!("ignoring unknown configuration key: {}", key_path(&path)); + }) + .context("unable to parse sprout.toml file")?; + + // Warn about declarations that configure more than one kind, as only one is used. + warn_multiple_kinds(&config); // Return the parsed configuration. Ok(config)