Nicholas Bishop | e46db29 | 2022-09-07 13:08:05 -0400 | [diff] [blame] | 1 | // Copyright 2022 The ChromiumOS Authors |
Nicholas Bishop | 2ba4ab4 | 2022-05-17 17:18:14 -0400 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 5 | use crate::arch::Arch; |
| 6 | use crate::build_mode::BuildMode; |
Nicholas Bishop | 6784f71 | 2021-10-21 13:43:47 -0400 | [diff] [blame] | 7 | use crate::package::Package; |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 8 | use crate::qemu::OvmfPaths; |
Nicholas Bishop | 14ad615 | 2022-08-19 15:23:36 -0400 | [diff] [blame] | 9 | use crate::secure_boot::SecureBootKeyPaths; |
| 10 | use crate::vboot::VbootKeyPaths; |
Nicholas Bishop | b26a16d | 2022-09-01 17:04:39 -0400 | [diff] [blame] | 11 | use anyhow::Result; |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 12 | use camino::{Utf8Path, Utf8PathBuf}; |
Nicholas Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 13 | use fs_err as fs; |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 14 | use serde::Deserialize; |
Nicholas Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 15 | |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 16 | #[derive(Debug, Deserialize, PartialEq)] |
| 17 | #[serde(deny_unknown_fields)] |
Nicholas Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 18 | pub struct Config { |
Nicholas Bishop | be51f4b | 2021-08-04 19:58:48 -0400 | [diff] [blame] | 19 | enable_verbose_logging: bool, |
Nicholas Bishop | 8d2f0c2 | 2021-08-04 20:05:09 -0400 | [diff] [blame] | 20 | disk_path: Utf8PathBuf, |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 21 | |
| 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 Bishop | be51f4b | 2021-08-04 19:58:48 -0400 | [diff] [blame] | 25 | repo: Utf8PathBuf, |
Nicholas Bishop | 1d3f684 | 2022-10-05 14:40:18 -0400 | [diff] [blame^] | 26 | |
| 27 | // Deprecated field. Keep in the config to avoid breaking existing |
| 28 | // workspaces. |
| 29 | use_test_key: Option<bool>, |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 30 | } |
| 31 | |
| 32 | /// Path of the config file relative to the repo root directory. |
| 33 | pub fn config_path(repo_root: &Utf8Path) -> Utf8PathBuf { |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 34 | repo_root.join("crdyboot.toml") |
Nicholas Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 35 | } |
| 36 | |
| 37 | impl Config { |
Nicholas Bishop | b26a16d | 2022-09-01 17:04:39 -0400 | [diff] [blame] | 38 | pub fn load(repo_root: &Utf8Path) -> Result<Config> { |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 39 | let src = fs::read_to_string(config_path(repo_root))?; |
Nicholas Bishop | b26a16d | 2022-09-01 17:04:39 -0400 | [diff] [blame] | 40 | Config::parse(&src, repo_root) |
Nicholas Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 41 | } |
| 42 | |
Nicholas Bishop | b26a16d | 2022-09-01 17:04:39 -0400 | [diff] [blame] | 43 | fn parse(src: &str, repo: &Utf8Path) -> Result<Config> { |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 44 | let mut config: Self = toml::de::from_str(src)?; |
| 45 | config.repo = repo.into(); |
Nicholas Bishop | b26a16d | 2022-09-01 17:04:39 -0400 | [diff] [blame] | 46 | Ok(config) |
Nicholas Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 47 | } |
Nicholas Bishop | bd47b9c | 2021-08-04 22:59:13 +0000 | [diff] [blame] | 48 | |
Nicholas Bishop | 6784f71 | 2021-10-21 13:43:47 -0400 | [diff] [blame] | 49 | /// 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 Bishop | bd47b9c | 2021-08-04 22:59:13 +0000 | [diff] [blame] | 53 | let mut features = Vec::new(); |
Nicholas Bishop | 6784f71 | 2021-10-21 13:43:47 -0400 | [diff] [blame] | 54 | |
| 55 | match package { |
| 56 | Crdyboot => { |
| 57 | if self.enable_verbose_logging { |
| 58 | features.push("verbose"); |
| 59 | } |
Nicholas Bishop | 6784f71 | 2021-10-21 13:43:47 -0400 | [diff] [blame] | 60 | } |
Nicholas Bishop | 1cfc8b9 | 2022-06-22 16:21:43 -0400 | [diff] [blame] | 61 | Enroller | Libcrdy | Vboot | Xtask => {} |
Nicholas Bishop | bd47b9c | 2021-08-04 22:59:13 +0000 | [diff] [blame] | 62 | } |
Nicholas Bishop | 6784f71 | 2021-10-21 13:43:47 -0400 | [diff] [blame] | 63 | |
Nicholas Bishop | bd47b9c | 2021-08-04 22:59:13 +0000 | [diff] [blame] | 64 | features |
| 65 | } |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 66 | |
Nicholas Bishop | be51f4b | 2021-08-04 19:58:48 -0400 | [diff] [blame] | 67 | pub fn repo_path(&self) -> &Utf8Path { |
| 68 | &self.repo |
| 69 | } |
| 70 | |
Nicholas Bishop | 2e2b702 | 2021-08-24 17:54:52 -0400 | [diff] [blame] | 71 | /// Get the build output directory. |
| 72 | pub fn target_path(&self) -> Utf8PathBuf { |
| 73 | self.repo.join("target") |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 74 | } |
| 75 | |
Nicholas Bishop | 6b89657 | 2022-05-02 13:45:46 -0400 | [diff] [blame] | 76 | /// 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 Bishop | 8f9a9d8 | 2022-10-05 14:08:13 -0400 | [diff] [blame] | 81 | pub fn target_exec_path(&self, arch: Arch, exe: EfiExe) -> Utf8PathBuf { |
Nicholas Bishop | 6b89657 | 2022-05-02 13:45:46 -0400 | [diff] [blame] | 82 | self.target_path() |
| 83 | .join(arch.uefi_target()) |
| 84 | .join(self.build_mode().dir_name()) |
Nicholas Bishop | 8f9a9d8 | 2022-10-05 14:08:13 -0400 | [diff] [blame] | 85 | .join(exe.as_str()) |
Nicholas Bishop | 6b89657 | 2022-05-02 13:45:46 -0400 | [diff] [blame] | 86 | } |
| 87 | |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 88 | pub fn workspace_path(&self) -> Utf8PathBuf { |
| 89 | self.repo.join("workspace") |
| 90 | } |
| 91 | |
Nicholas Bishop | 3de3faf | 2022-05-18 14:29:46 -0400 | [diff] [blame] | 92 | /// 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 Bishop | b26a16d | 2022-09-01 17:04:39 -0400 | [diff] [blame] | 110 | pub fn write_setup_version(&self, version: u32) -> Result<()> { |
| 111 | Ok(fs::write( |
| 112 | self.setup_version_path(), |
| 113 | format!("{}\n", version), |
| 114 | )?) |
Nicholas Bishop | 3de3faf | 2022-05-18 14:29:46 -0400 | [diff] [blame] | 115 | } |
| 116 | |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 117 | 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 Bishop | a2a3486 | 2022-08-19 15:54:12 -0400 | [diff] [blame] | 125 | fn vboot_devkeys_path(&self) -> Utf8PathBuf { |
| 126 | self.vboot_reference_path().join("tests/devkeys") |
| 127 | } |
| 128 | |
Nicholas Bishop | 8d2f0c2 | 2021-08-04 20:05:09 -0400 | [diff] [blame] | 129 | pub fn disk_path(&self) -> &Utf8Path { |
| 130 | &self.disk_path |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 131 | } |
| 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 Bishop | a8fab19 | 2022-05-06 12:06:43 -0400 | [diff] [blame] | 138 | self.workspace_path().join("vboot_test_disk.bin") |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 139 | } |
| 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 Bishop | f558609 | 2022-05-18 16:00:35 -0400 | [diff] [blame] | 149 | /// Key used to sign the kernel keyblock which contains the public |
| 150 | /// part of the kernel_data_key. |
Nicholas Bishop | f1ca17e | 2022-08-19 15:17:17 -0400 | [diff] [blame] | 151 | pub fn kernel_key_paths(&self) -> VbootKeyPaths { |
Nicholas Bishop | a2a3486 | 2022-08-19 15:54:12 -0400 | [diff] [blame] | 152 | 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 Bishop | f558609 | 2022-05-18 16:00:35 -0400 | [diff] [blame] | 158 | } |
| 159 | |
| 160 | /// Key used to sign the kernel data. |
Nicholas Bishop | f1ca17e | 2022-08-19 15:17:17 -0400 | [diff] [blame] | 161 | pub fn kernel_data_key_paths(&self) -> VbootKeyPaths { |
Nicholas Bishop | a2a3486 | 2022-08-19 15:54:12 -0400 | [diff] [blame] | 162 | 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 Bishop | f558609 | 2022-05-18 16:00:35 -0400 | [diff] [blame] | 168 | } |
| 169 | |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 170 | /// This cert will be enrolled as the PK, first KEK, and first DB |
| 171 | /// entry. The private key is used to sign shim. |
Nicholas Bishop | f1ca17e | 2022-08-19 15:17:17 -0400 | [diff] [blame] | 172 | pub fn secure_boot_root_key_paths(&self) -> SecureBootKeyPaths { |
| 173 | SecureBootKeyPaths::new( |
| 174 | self.workspace_path().join("secure_boot_root_key"), |
| 175 | ) |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 176 | } |
| 177 | |
| 178 | /// This cert is embedded in shim and the private key is used to |
| 179 | /// sign crdyboot. |
Nicholas Bishop | f1ca17e | 2022-08-19 15:17:17 -0400 | [diff] [blame] | 180 | pub fn secure_boot_shim_key_paths(&self) -> SecureBootKeyPaths { |
| 181 | SecureBootKeyPaths::new( |
| 182 | self.workspace_path().join("secure_boot_shim_key"), |
| 183 | ) |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 184 | } |
| 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 Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 193 | } |
| 194 | |
Nicholas Bishop | 8f9a9d8 | 2022-10-05 14:08:13 -0400 | [diff] [blame] | 195 | #[derive(Clone, Copy)] |
| 196 | pub enum EfiExe { |
| 197 | Crdyboot, |
Nicholas Bishop | 7ceed94 | 2022-10-05 14:16:25 -0400 | [diff] [blame] | 198 | CrdybootWithPubkey, |
Nicholas Bishop | 8f9a9d8 | 2022-10-05 14:08:13 -0400 | [diff] [blame] | 199 | Enroller, |
| 200 | } |
| 201 | |
| 202 | impl EfiExe { |
| 203 | fn as_str(self) -> &'static str { |
| 204 | match self { |
| 205 | EfiExe::Crdyboot => "crdyboot.efi", |
Nicholas Bishop | 7ceed94 | 2022-10-05 14:16:25 -0400 | [diff] [blame] | 206 | EfiExe::CrdybootWithPubkey => "crdyboot_with_pubkey.efi", |
Nicholas Bishop | 8f9a9d8 | 2022-10-05 14:08:13 -0400 | [diff] [blame] | 207 | EfiExe::Enroller => "enroller.efi", |
| 208 | } |
| 209 | } |
| 210 | } |
| 211 | |
Nicholas Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 212 | #[cfg(test)] |
| 213 | mod tests { |
| 214 | use super::*; |
| 215 | |
| 216 | #[test] |
Nicholas Bishop | b26a16d | 2022-09-01 17:04:39 -0400 | [diff] [blame] | 217 | fn test_parse() -> Result<()> { |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 218 | let repo = &Utf8PathBuf::new(); |
Nicholas Bishop | 22168e4 | 2021-08-04 19:55:01 -0400 | [diff] [blame] | 219 | |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 220 | // Default config parses OK. |
| 221 | let default_cfg = include_str!("../default.toml"); |
| 222 | Config::parse(default_cfg, repo)?; |
Nicholas Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 223 | |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 224 | // 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 Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 227 | |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 228 | // Partial config is invalid. |
Nicholas Bishop | 1d3f684 | 2022-10-05 14:40:18 -0400 | [diff] [blame^] | 229 | let partial = default_cfg.replace("enable_verbose_logging = true", ""); |
Nicholas Bishop | e0be89e | 2022-05-03 09:31:16 -0400 | [diff] [blame] | 230 | assert!(Config::parse(&partial, repo).is_err()); |
Nicholas Bishop | b26a16d | 2022-09-01 17:04:39 -0400 | [diff] [blame] | 231 | |
| 232 | Ok(()) |
Nicholas Bishop | 7528a99 | 2021-08-03 16:58:31 -0400 | [diff] [blame] | 233 | } |
| 234 | } |