blob: 8bddc2edc2ac38a496373a3b91d4e870e6369b42 [file] [log] [blame]
Nicholas Bishope46db292022-09-07 13:08:05 -04001// Copyright 2022 The ChromiumOS Authors
Nicholas Bishop2ba4ab42022-05-17 17:18:14 -04002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Nicholas Bishop22168e42021-08-04 19:55:01 -04005use crate::arch::Arch;
6use crate::build_mode::BuildMode;
Nicholas Bishop6784f712021-10-21 13:43:47 -04007use crate::package::Package;
Nicholas Bishop22168e42021-08-04 19:55:01 -04008use crate::qemu::OvmfPaths;
Nicholas Bishop14ad6152022-08-19 15:23:36 -04009use crate::secure_boot::SecureBootKeyPaths;
10use crate::vboot::VbootKeyPaths;
Nicholas Bishopb26a16d2022-09-01 17:04:39 -040011use anyhow::Result;
Nicholas Bishop22168e42021-08-04 19:55:01 -040012use camino::{Utf8Path, Utf8PathBuf};
Nicholas Bishop7528a992021-08-03 16:58:31 -040013use fs_err as fs;
Nicholas Bishope0be89e2022-05-03 09:31:16 -040014use serde::Deserialize;
Nicholas Bishop7528a992021-08-03 16:58:31 -040015
Nicholas Bishope0be89e2022-05-03 09:31:16 -040016#[derive(Debug, Deserialize, PartialEq)]
17#[serde(deny_unknown_fields)]
Nicholas Bishop7528a992021-08-03 16:58:31 -040018pub struct Config {
Nicholas Bishopbe51f4b2021-08-04 19:58:48 -040019 enable_verbose_logging: bool,
Nicholas Bishop8d2f0c22021-08-04 20:05:09 -040020 disk_path: Utf8PathBuf,
Nicholas Bishope0be89e2022-05-03 09:31:16 -040021
22 /// Absolute path of the crdyboot repo. This is passed in to
23 /// [`Config::load`], not part of the input config file.
24 #[serde(skip)]
Nicholas Bishopbe51f4b2021-08-04 19:58:48 -040025 repo: Utf8PathBuf,
Nicholas Bishop1d3f6842022-10-05 14:40:18 -040026
27 // Deprecated field. Keep in the config to avoid breaking existing
28 // workspaces.
29 use_test_key: Option<bool>,
Nicholas Bishop22168e42021-08-04 19:55:01 -040030}
31
32/// Path of the config file relative to the repo root directory.
33pub fn config_path(repo_root: &Utf8Path) -> Utf8PathBuf {
Nicholas Bishope0be89e2022-05-03 09:31:16 -040034 repo_root.join("crdyboot.toml")
Nicholas Bishop7528a992021-08-03 16:58:31 -040035}
36
37impl Config {
Nicholas Bishopb26a16d2022-09-01 17:04:39 -040038 pub fn load(repo_root: &Utf8Path) -> Result<Config> {
Nicholas Bishope0be89e2022-05-03 09:31:16 -040039 let src = fs::read_to_string(config_path(repo_root))?;
Nicholas Bishopb26a16d2022-09-01 17:04:39 -040040 Config::parse(&src, repo_root)
Nicholas Bishop7528a992021-08-03 16:58:31 -040041 }
42
Nicholas Bishopb26a16d2022-09-01 17:04:39 -040043 fn parse(src: &str, repo: &Utf8Path) -> Result<Config> {
Nicholas Bishope0be89e2022-05-03 09:31:16 -040044 let mut config: Self = toml::de::from_str(src)?;
45 config.repo = repo.into();
Nicholas Bishopb26a16d2022-09-01 17:04:39 -040046 Ok(config)
Nicholas Bishop7528a992021-08-03 16:58:31 -040047 }
Nicholas Bishopbd47b9c2021-08-04 22:59:13 +000048
Nicholas Bishop6784f712021-10-21 13:43:47 -040049 /// Get all cargo features to enable while building a package.
50 pub fn get_package_features(&self, package: Package) -> Vec<&'static str> {
51 use Package::*;
52
Nicholas Bishopbd47b9c2021-08-04 22:59:13 +000053 let mut features = Vec::new();
Nicholas Bishop6784f712021-10-21 13:43:47 -040054
55 match package {
56 Crdyboot => {
57 if self.enable_verbose_logging {
58 features.push("verbose");
59 }
Nicholas Bishop6784f712021-10-21 13:43:47 -040060 }
Nicholas Bishop1cfc8b92022-06-22 16:21:43 -040061 Enroller | Libcrdy | Vboot | Xtask => {}
Nicholas Bishopbd47b9c2021-08-04 22:59:13 +000062 }
Nicholas Bishop6784f712021-10-21 13:43:47 -040063
Nicholas Bishopbd47b9c2021-08-04 22:59:13 +000064 features
65 }
Nicholas Bishop22168e42021-08-04 19:55:01 -040066
Nicholas Bishopbe51f4b2021-08-04 19:58:48 -040067 pub fn repo_path(&self) -> &Utf8Path {
68 &self.repo
69 }
70
Nicholas Bishop2e2b7022021-08-24 17:54:52 -040071 /// Get the build output directory.
72 pub fn target_path(&self) -> Utf8PathBuf {
73 self.repo.join("target")
Nicholas Bishop22168e42021-08-04 19:55:01 -040074 }
75
Nicholas Bishop6b896572022-05-02 13:45:46 -040076 /// Get the path of an EFI executable in the build output.
77 ///
78 /// For example, this might return a path like:
79 ///
80 /// <repo>/target/x86_64-unknown-uefi/release/enroller.efi
Nicholas Bishop8f9a9d82022-10-05 14:08:13 -040081 pub fn target_exec_path(&self, arch: Arch, exe: EfiExe) -> Utf8PathBuf {
Nicholas Bishop6b896572022-05-02 13:45:46 -040082 self.target_path()
83 .join(arch.uefi_target())
84 .join(self.build_mode().dir_name())
Nicholas Bishop8f9a9d82022-10-05 14:08:13 -040085 .join(exe.as_str())
Nicholas Bishop6b896572022-05-02 13:45:46 -040086 }
87
Nicholas Bishop22168e42021-08-04 19:55:01 -040088 pub fn workspace_path(&self) -> Utf8PathBuf {
89 self.repo.join("workspace")
90 }
91
Nicholas Bishop3de3faf2022-05-18 14:29:46 -040092 /// Path of the setup-version file in the workspace. This file is
93 /// used to automatically re-run the setup operations when needed.
94 fn setup_version_path(&self) -> Utf8PathBuf {
95 self.workspace_path().join("setup_version")
96 }
97
98 /// Read the current setup version. Returns version 0 if any error
99 /// occurs (such as the version file not existing).
100 pub fn read_setup_version(&self) -> u32 {
101 let default = 0;
102 if let Ok(version) = fs::read_to_string(self.setup_version_path()) {
103 version.trim().parse().unwrap_or(default)
104 } else {
105 default
106 }
107 }
108
109 /// Write out the setup-version file.
Nicholas Bishopb26a16d2022-09-01 17:04:39 -0400110 pub fn write_setup_version(&self, version: u32) -> Result<()> {
111 Ok(fs::write(
112 self.setup_version_path(),
113 format!("{}\n", version),
114 )?)
Nicholas Bishop3de3faf2022-05-18 14:29:46 -0400115 }
116
Nicholas Bishop22168e42021-08-04 19:55:01 -0400117 pub fn vboot_reference_path(&self) -> Utf8PathBuf {
118 self.repo.join("third_party/vboot_reference")
119 }
120
121 pub fn futility_executable_path(&self) -> Utf8PathBuf {
122 self.vboot_reference_path().join("build/futility/futility")
123 }
124
Nicholas Bishopa2a34862022-08-19 15:54:12 -0400125 fn vboot_devkeys_path(&self) -> Utf8PathBuf {
126 self.vboot_reference_path().join("tests/devkeys")
127 }
128
Nicholas Bishop8d2f0c22021-08-04 20:05:09 -0400129 pub fn disk_path(&self) -> &Utf8Path {
130 &self.disk_path
Nicholas Bishop22168e42021-08-04 19:55:01 -0400131 }
132
133 pub fn enroller_disk_path(&self) -> Utf8PathBuf {
134 self.workspace_path().join("enroller.bin")
135 }
136
137 pub fn vboot_test_disk_path(&self) -> Utf8PathBuf {
Nicholas Bishopa8fab192022-05-06 12:06:43 -0400138 self.workspace_path().join("vboot_test_disk.bin")
Nicholas Bishop22168e42021-08-04 19:55:01 -0400139 }
140
141 pub fn ovmf_paths(&self, arch: Arch) -> OvmfPaths {
142 let subdir = match arch {
143 Arch::Ia32 => "uefi32",
144 Arch::X64 => "uefi64",
145 };
146 OvmfPaths::new(self.workspace_path().join(subdir))
147 }
148
Nicholas Bishopf5586092022-05-18 16:00:35 -0400149 /// Key used to sign the kernel keyblock which contains the public
150 /// part of the kernel_data_key.
Nicholas Bishopf1ca17e2022-08-19 15:17:17 -0400151 pub fn kernel_key_paths(&self) -> VbootKeyPaths {
Nicholas Bishopa2a34862022-08-19 15:54:12 -0400152 let devkeys = self.vboot_devkeys_path();
153 VbootKeyPaths {
154 vbprivk: devkeys.join("kernel_subkey.vbprivk"),
155 vbpubk: devkeys.join("kernel_subkey.vbpubk"),
156 keyblock: None,
157 }
Nicholas Bishopf5586092022-05-18 16:00:35 -0400158 }
159
160 /// Key used to sign the kernel data.
Nicholas Bishopf1ca17e2022-08-19 15:17:17 -0400161 pub fn kernel_data_key_paths(&self) -> VbootKeyPaths {
Nicholas Bishopa2a34862022-08-19 15:54:12 -0400162 let devkeys = self.vboot_devkeys_path();
163 VbootKeyPaths {
164 vbprivk: devkeys.join("kernel_data_key.vbprivk"),
165 vbpubk: devkeys.join("kernel_data_key.vbpubk"),
166 keyblock: Some(devkeys.join("kernel.keyblock")),
167 }
Nicholas Bishopf5586092022-05-18 16:00:35 -0400168 }
169
Nicholas Bishop22168e42021-08-04 19:55:01 -0400170 /// This cert will be enrolled as the PK, first KEK, and first DB
171 /// entry. The private key is used to sign shim.
Nicholas Bishopf1ca17e2022-08-19 15:17:17 -0400172 pub fn secure_boot_root_key_paths(&self) -> SecureBootKeyPaths {
173 SecureBootKeyPaths::new(
174 self.workspace_path().join("secure_boot_root_key"),
175 )
Nicholas Bishop22168e42021-08-04 19:55:01 -0400176 }
177
178 /// This cert is embedded in shim and the private key is used to
179 /// sign crdyboot.
Nicholas Bishopf1ca17e2022-08-19 15:17:17 -0400180 pub fn secure_boot_shim_key_paths(&self) -> SecureBootKeyPaths {
181 SecureBootKeyPaths::new(
182 self.workspace_path().join("secure_boot_shim_key"),
183 )
Nicholas Bishop22168e42021-08-04 19:55:01 -0400184 }
185
186 pub fn shim_build_path(&self) -> Utf8PathBuf {
187 self.workspace_path().join("shim_build")
188 }
189
190 pub fn build_mode(&self) -> BuildMode {
191 BuildMode::Release
192 }
Nicholas Bishop7528a992021-08-03 16:58:31 -0400193}
194
Nicholas Bishop8f9a9d82022-10-05 14:08:13 -0400195#[derive(Clone, Copy)]
196pub enum EfiExe {
197 Crdyboot,
Nicholas Bishop7ceed942022-10-05 14:16:25 -0400198 CrdybootWithPubkey,
Nicholas Bishop8f9a9d82022-10-05 14:08:13 -0400199 Enroller,
200}
201
202impl EfiExe {
203 fn as_str(self) -> &'static str {
204 match self {
205 EfiExe::Crdyboot => "crdyboot.efi",
Nicholas Bishop7ceed942022-10-05 14:16:25 -0400206 EfiExe::CrdybootWithPubkey => "crdyboot_with_pubkey.efi",
Nicholas Bishop8f9a9d82022-10-05 14:08:13 -0400207 EfiExe::Enroller => "enroller.efi",
208 }
209 }
210}
211
Nicholas Bishop7528a992021-08-03 16:58:31 -0400212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
Nicholas Bishopb26a16d2022-09-01 17:04:39 -0400217 fn test_parse() -> Result<()> {
Nicholas Bishope0be89e2022-05-03 09:31:16 -0400218 let repo = &Utf8PathBuf::new();
Nicholas Bishop22168e42021-08-04 19:55:01 -0400219
Nicholas Bishope0be89e2022-05-03 09:31:16 -0400220 // Default config parses OK.
221 let default_cfg = include_str!("../default.toml");
222 Config::parse(default_cfg, repo)?;
Nicholas Bishop7528a992021-08-03 16:58:31 -0400223
Nicholas Bishope0be89e2022-05-03 09:31:16 -0400224 // Config with unknown key is invalid.
225 let unknown_key = format!("{}\n unknown_key = true", default_cfg);
226 assert!(Config::parse(&unknown_key, repo).is_err());
Nicholas Bishop7528a992021-08-03 16:58:31 -0400227
Nicholas Bishope0be89e2022-05-03 09:31:16 -0400228 // Partial config is invalid.
Nicholas Bishop1d3f6842022-10-05 14:40:18 -0400229 let partial = default_cfg.replace("enable_verbose_logging = true", "");
Nicholas Bishope0be89e2022-05-03 09:31:16 -0400230 assert!(Config::parse(&partial, repo).is_err());
Nicholas Bishopb26a16d2022-09-01 17:04:39 -0400231
232 Ok(())
Nicholas Bishop7528a992021-08-03 16:58:31 -0400233 }
234}