blob: 8bddc2edc2ac38a496373a3b91d4e870e6369b42 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::arch::Arch;
use crate::build_mode::BuildMode;
use crate::package::Package;
use crate::qemu::OvmfPaths;
use crate::secure_boot::SecureBootKeyPaths;
use crate::vboot::VbootKeyPaths;
use anyhow::Result;
use camino::{Utf8Path, Utf8PathBuf};
use fs_err as fs;
use serde::Deserialize;
#[derive(Debug, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Config {
enable_verbose_logging: bool,
disk_path: Utf8PathBuf,
/// Absolute path of the crdyboot repo. This is passed in to
/// [`Config::load`], not part of the input config file.
#[serde(skip)]
repo: Utf8PathBuf,
// Deprecated field. Keep in the config to avoid breaking existing
// workspaces.
use_test_key: Option<bool>,
}
/// Path of the config file relative to the repo root directory.
pub fn config_path(repo_root: &Utf8Path) -> Utf8PathBuf {
repo_root.join("crdyboot.toml")
}
impl Config {
pub fn load(repo_root: &Utf8Path) -> Result<Config> {
let src = fs::read_to_string(config_path(repo_root))?;
Config::parse(&src, repo_root)
}
fn parse(src: &str, repo: &Utf8Path) -> Result<Config> {
let mut config: Self = toml::de::from_str(src)?;
config.repo = repo.into();
Ok(config)
}
/// Get all cargo features to enable while building a package.
pub fn get_package_features(&self, package: Package) -> Vec<&'static str> {
use Package::*;
let mut features = Vec::new();
match package {
Crdyboot => {
if self.enable_verbose_logging {
features.push("verbose");
}
}
Enroller | Libcrdy | Vboot | Xtask => {}
}
features
}
pub fn repo_path(&self) -> &Utf8Path {
&self.repo
}
/// Get the build output directory.
pub fn target_path(&self) -> Utf8PathBuf {
self.repo.join("target")
}
/// Get the path of an EFI executable in the build output.
///
/// For example, this might return a path like:
///
/// <repo>/target/x86_64-unknown-uefi/release/enroller.efi
pub fn target_exec_path(&self, arch: Arch, exe: EfiExe) -> Utf8PathBuf {
self.target_path()
.join(arch.uefi_target())
.join(self.build_mode().dir_name())
.join(exe.as_str())
}
pub fn workspace_path(&self) -> Utf8PathBuf {
self.repo.join("workspace")
}
/// Path of the setup-version file in the workspace. This file is
/// used to automatically re-run the setup operations when needed.
fn setup_version_path(&self) -> Utf8PathBuf {
self.workspace_path().join("setup_version")
}
/// Read the current setup version. Returns version 0 if any error
/// occurs (such as the version file not existing).
pub fn read_setup_version(&self) -> u32 {
let default = 0;
if let Ok(version) = fs::read_to_string(self.setup_version_path()) {
version.trim().parse().unwrap_or(default)
} else {
default
}
}
/// Write out the setup-version file.
pub fn write_setup_version(&self, version: u32) -> Result<()> {
Ok(fs::write(
self.setup_version_path(),
format!("{}\n", version),
)?)
}
pub fn vboot_reference_path(&self) -> Utf8PathBuf {
self.repo.join("third_party/vboot_reference")
}
pub fn futility_executable_path(&self) -> Utf8PathBuf {
self.vboot_reference_path().join("build/futility/futility")
}
fn vboot_devkeys_path(&self) -> Utf8PathBuf {
self.vboot_reference_path().join("tests/devkeys")
}
pub fn disk_path(&self) -> &Utf8Path {
&self.disk_path
}
pub fn enroller_disk_path(&self) -> Utf8PathBuf {
self.workspace_path().join("enroller.bin")
}
pub fn vboot_test_disk_path(&self) -> Utf8PathBuf {
self.workspace_path().join("vboot_test_disk.bin")
}
pub fn ovmf_paths(&self, arch: Arch) -> OvmfPaths {
let subdir = match arch {
Arch::Ia32 => "uefi32",
Arch::X64 => "uefi64",
};
OvmfPaths::new(self.workspace_path().join(subdir))
}
/// Key used to sign the kernel keyblock which contains the public
/// part of the kernel_data_key.
pub fn kernel_key_paths(&self) -> VbootKeyPaths {
let devkeys = self.vboot_devkeys_path();
VbootKeyPaths {
vbprivk: devkeys.join("kernel_subkey.vbprivk"),
vbpubk: devkeys.join("kernel_subkey.vbpubk"),
keyblock: None,
}
}
/// Key used to sign the kernel data.
pub fn kernel_data_key_paths(&self) -> VbootKeyPaths {
let devkeys = self.vboot_devkeys_path();
VbootKeyPaths {
vbprivk: devkeys.join("kernel_data_key.vbprivk"),
vbpubk: devkeys.join("kernel_data_key.vbpubk"),
keyblock: Some(devkeys.join("kernel.keyblock")),
}
}
/// This cert will be enrolled as the PK, first KEK, and first DB
/// entry. The private key is used to sign shim.
pub fn secure_boot_root_key_paths(&self) -> SecureBootKeyPaths {
SecureBootKeyPaths::new(
self.workspace_path().join("secure_boot_root_key"),
)
}
/// This cert is embedded in shim and the private key is used to
/// sign crdyboot.
pub fn secure_boot_shim_key_paths(&self) -> SecureBootKeyPaths {
SecureBootKeyPaths::new(
self.workspace_path().join("secure_boot_shim_key"),
)
}
pub fn shim_build_path(&self) -> Utf8PathBuf {
self.workspace_path().join("shim_build")
}
pub fn build_mode(&self) -> BuildMode {
BuildMode::Release
}
}
#[derive(Clone, Copy)]
pub enum EfiExe {
Crdyboot,
CrdybootWithPubkey,
Enroller,
}
impl EfiExe {
fn as_str(self) -> &'static str {
match self {
EfiExe::Crdyboot => "crdyboot.efi",
EfiExe::CrdybootWithPubkey => "crdyboot_with_pubkey.efi",
EfiExe::Enroller => "enroller.efi",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse() -> Result<()> {
let repo = &Utf8PathBuf::new();
// Default config parses OK.
let default_cfg = include_str!("../default.toml");
Config::parse(default_cfg, repo)?;
// Config with unknown key is invalid.
let unknown_key = format!("{}\n unknown_key = true", default_cfg);
assert!(Config::parse(&unknown_key, repo).is_err());
// Partial config is invalid.
let partial = default_cfg.replace("enable_verbose_logging = true", "");
assert!(Config::parse(&partial, repo).is_err());
Ok(())
}
}