chore(code): sbat section generator build tool

This commit is contained in:
2025-11-03 22:37:06 -05:00
parent 632781abbf
commit 9a803ad355
10 changed files with 111 additions and 68 deletions

12
crates/build/Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "edera-sprout-build"
description = "Sprout Build Tools"
license.workspace = true
version.workspace = true
homepage.workspace = true
repository.workspace = true
edition.workspace = true
[lib]
name = "edera_sprout_build"
path = "src/lib.rs"

76
crates/build/src/lib.rs Normal file
View File

@@ -0,0 +1,76 @@
use std::path::PathBuf;
use std::{env, fs};
/// Block size of the sbat section.
const SBAT_BLOCK_SIZE: usize = 512;
/// Template contents for the sbat.generated.rs file.
const SBAT_RS_TEMPLATE: &str = include_str!("sbat.template.rs");
/// Pad with zeros the given `data` to a multiple of `block_size`.
fn block_pad(data: &mut Vec<u8>, block_size: usize) {
let needed = data.len().div_ceil(block_size).max(1) * block_size;
if needed != data.len() {
data.resize(needed, 0);
}
}
/// Generate an .sbat link section module. This should be coupled with including the sbat module in
/// the crate that intends to embed the sbat section.
/// We intake a sbat.template.csv file in the calling crate and output a sbat.dat
/// which is included by a generated sbat.generated.rs file.
pub fn generate_sbat_module() {
// Notify Cargo that if the version changes, we need to regenerate the sbat.out file.
println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION");
// The version of the package.
let version = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION not set");
// The output directory to place the sbat.csv into.
let output_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set"));
// The output path to the sbat.out file.
let out_file = output_dir.join("sbat.out");
// The output path to the sbat.generated.rs file.
let rs_file = output_dir.join("sbat.generated.rs");
// The path to the root of the crate.
let crate_root =
PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"));
// The path to the sbat.template.tsv file is in the source directory of the crate.
let sbat_template_file = crate_root.join("src/sbat.csv");
// Notify Cargo that if sbat.csv changes, we need to regenerate the sbat.out file.
println!(
"cargo:rerun-if-changed={}",
sbat_template_file
.to_str()
.expect("unable to convert sbat template path file to a string")
);
// Read the sbat.csv template file.
let sbat_template =
fs::read_to_string(&sbat_template_file).expect("unable to read sbat.csv file");
// Replace the version placeholder in the template with the actual version.
let sbat = sbat_template.replace("{version}", &version);
// Encode the sbat.csv as bytes.
let mut encoded = sbat.as_bytes().to_vec();
// Pad the sbat.csv to the required block size.
block_pad(&mut encoded, SBAT_BLOCK_SIZE);
// Write the sbat.out file to the output directory.
fs::write(&out_file, &encoded).expect("unable to write sbat.out");
// Generate the contents of the sbat.generated.rs file.
// The size must tbe size of the encoded sbat.out file.
let sbat_rs = SBAT_RS_TEMPLATE.replace("{size}", &encoded.len().to_string());
// Write the sbat.generated.rs file to the output directory.
fs::write(&rs_file, sbat_rs).expect("unable to write sbat.generated.rs");
}

View File

@@ -0,0 +1,6 @@
/// Define the SBAT attestation by including the sbat.csv file.
/// See this document for more details: https://github.com/rhboot/shim/blob/main/SBAT.md
/// NOTE: This data must be aligned by 512 bytes.
#[used]
#[unsafe(link_section = ".sbat")]
static SBAT: [u8; {size}] = *include_bytes!(concat!(env!("OUT_DIR"), "/sbat.out"));

View File

@@ -9,7 +9,6 @@ edition.workspace = true
[dependencies]
anyhow.workspace = true
bitflags.workspace = true
edera-sprout-config.path = "../config"
edera-sprout-eficore.path = "../eficore"
hex.workspace = true
@@ -20,6 +19,9 @@ log.workspace = true
uefi.workspace = true
uefi-raw.workspace = true
[build-dependencies]
edera-sprout-build.path = "../build"
[[bin]]
name = "sprout"
path = "src/main.rs"

View File

@@ -1,57 +1,7 @@
use std::path::PathBuf;
use std::{env, fs};
use edera_sprout_build::generate_sbat_module;
/// The size of the sbat.csv file.
const SBAT_SIZE: usize = 512;
/// Generate the sbat.csv for the .sbat link section.
///
/// We intake a sbat.template.tsv and output a sbat.csv which is included by src/sbat.rs
fn generate_sbat_csv() {
// Notify Cargo that if the Sprout version changes, we need to regenerate the sbat.csv.
println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION");
// The version of the sprout crate.
let sprout_version = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION not set");
// The output directory to place the sbat.csv into.
let output_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set"));
// The output path to the sbat.csv.
let output_file = output_dir.join("sbat.csv");
// The path to the root of the sprout crate.
let sprout_root =
PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"));
// The path to the sbat.template.tsv file is in the source directory of the sprout crate.
let template_path = sprout_root.join("src/sbat.template.csv");
// Read the sbat.csv template file.
let template = fs::read_to_string(&template_path).expect("unable to read template file");
// Replace the version placeholder in the template with the actual version.
let sbat = template.replace("{version}", &sprout_version);
// Encode the sbat.csv as bytes.
let mut encoded = sbat.as_bytes().to_vec();
if encoded.len() > SBAT_SIZE {
panic!("sbat.csv is too large");
}
// Pad the sbat.csv to the required size.
while encoded.len() < SBAT_SIZE {
encoded.push(0);
}
// Write the sbat.csv to the output directory.
fs::write(&output_file, encoded).expect("unable to write sbat.csv");
}
/// Build script entry point.
/// Right now, all we need to do is generate the sbat.csv file.
/// Build script entry point for Sprout.
fn main() {
// Generate the sbat.csv file.
generate_sbat_csv();
// Generate the sbat.generated.rs file.
generate_sbat_module();
}

View File

@@ -1,11 +1,2 @@
/// SBAT must be aligned by 512 bytes.
const SBAT_SIZE: usize = 512;
/// Define the SBAT attestation by including the sbat.csv file.
/// See this document for more details: https://github.com/rhboot/shim/blob/main/SBAT.md
/// NOTE: Alignment can't be enforced by an attribute, so instead the alignment is currently
/// enforced by the SBAT_SIZE being 512. The build.rs will ensure that the sbat.csv is padded.
/// This code will not compile if the sbat.csv is a different size than SBAT_SIZE.
#[used]
#[unsafe(link_section = ".sbat")]
static SBAT: [u8; SBAT_SIZE] = *include_bytes!(concat!(env!("OUT_DIR"), "/sbat.csv"));
// Include the generated sbat section in this file.
include!(concat!(env!("OUT_DIR"), "/sbat.generated.rs"));