diff --git a/README.md b/README.md index 28321a2..9a89e6d 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,16 @@ arch entry needs `url` **and exactly one** of `sha256` or `ed25519`. `args_url` content is verified against `ed25519` (the same release key as the payload) and used verbatim, trimmed, as the load-options string. +**Args model.** `args` / `args_url` set the booted EFI program's UEFI **LoadOptions** — +the generic way stage0 parameterizes whatever EFI image it chain-loads. They come **only** +from this metadata (or the signed URL); stage0 never forwards its own firmware/shell +invocation arguments to stage1. For a **Linux UKI** stage1, the kernel command line is +baked into the signed, measured UKI and is authoritative: under Secure Boot the systemd +stub **ignores** LoadOptions, so `args` cannot alter the UKI cmdline (and a replace would +also escape PCR 14). Production runs Secure Boot on; configure a UKI-based stage1 through +its `_stage2` document, not the kernel cmdline. A non-UKI EFI stage1 may read these +LoadOptions as its arguments. + ### Embedded metadata (self-contained `netboot.efi`) The `_stage1` document can be embedded in stage0's PE before Authenticode diff --git a/crates/stage0/src/config.rs b/crates/stage0/src/config.rs index 608810c..dc2360a 100644 --- a/crates/stage0/src/config.rs +++ b/crates/stage0/src/config.rs @@ -5,6 +5,15 @@ //! Mirrors `stage1`'s per-arch `{url, sha256}` structure (plus optional `args`) //! but under a distinct `_stage1` key, so a UEFI payload is never confused with //! a Linux `_stage2` binary in the same document. +//! +//! `args` (and the signed `args_url`) set the booted EFI program's UEFI *LoadOptions* -- +//! the generic way stage0 parameterizes whatever EFI image it chain-loads. They are +//! sourced ONLY from this metadata (or the signed URL); stage0 never forwards its own +//! firmware/shell invocation arguments to stage1. For a Linux UKI stage1 specifically, +//! the kernel command line is baked into the signed, measured UKI and is authoritative: +//! under Secure Boot the stub ignores LoadOptions, so `_stage1.args` cannot alter the UKI +//! cmdline (operator config for a UKI flows through `_stage2`, not the kernel cmdline). +//! A non-UKI EFI stage1 is free to read these LoadOptions as its arguments. use alloc::string::String; use alloc::vec::Vec; @@ -41,6 +50,9 @@ pub struct UserData { #[derive(Debug, Deserialize)] pub struct Stage1Config { + /// Inline UEFI LoadOptions for the booted stage1 EFI image (a non-UKI stage1 reads + /// these as its args). Overridden by the signed `args_url`. See the module docs for + /// how a Linux UKI treats these (baked cmdline wins; ignored under Secure Boot). #[serde(default)] pub args: Option>, // Exactly one of these is read per build (see `for_this_arch`); the other diff --git a/crates/stage0/src/main.rs b/crates/stage0/src/main.rs index 2cd98c6..cc90b22 100644 --- a/crates/stage0/src/main.rs +++ b/crates/stage0/src/main.rs @@ -264,6 +264,14 @@ fn fetch_signed_args( /// Set the loaded image's load options from the final `opts` string (UCS-2). /// Returns the backing [`CString16`], which the caller must keep alive until /// `start_image`. +/// +/// `opts` is sourced only from the metadata `_stage1.args` or the signed `args_url` +/// (see `run`); stage0 never reads or forwards its own firmware/shell invocation +/// arguments to the child. For a Linux UKI child these LoadOptions would be the kernel +/// command line, but the UKI bakes + measures its own `.cmdline` and the stub ignores +/// LoadOptions under Secure Boot -- so on a UKI in production this has no effect, by +/// design (operator config for a UKI flows through `_stage2`). A non-UKI EFI stage1 +/// reads these as its arguments. fn set_load_options(image: Handle, opts: Option<&str>) -> Option { let opts = opts?; if opts.is_empty() {