xref: /aosp_15_r20/external/gsc-utils/rust/explain_ap_ro_verification_status/src/main.rs (revision 4f2df630800bdcf1d4f0decf95d8a1cb87344f5f)
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //! Explains AP RO verification status codes from plain integers.
5 //!
6 //! AP RO verification result integers take two forms:
7 //!
8 //! - A `u32` derived from `ApRoVerificationResult` in capsules.
9 //!   This puts a `VerifyError` in the top 8 bits, and stores extra
10 //!   detail in the lower 24 bits. This is what is found in the
11 //!   Platform.TPM.ExpandedApRoVerificationStatus UMA metric.
12 //!
13 //! - A `u8` derived from `ApRoVerificationTpmvStatus`, in syscalls.
14 //!   This is the output of `gsctool -B` and found in the
15 //!   Platform.Ti50.ARVStatus metric. It follows a different set of
16 //!   statuses from Cr50, but is in the same namespace.
17 
18 use ap_ro_errs::{
19     ApRoVerificationResult, ApRoVerificationTpmvStatus, BadValue, CryptoAlgorithmSource,
20     DigestLocation, InternalErrorSource, SignatureLocation, VerifyError, VerifyErrorCode,
21     VersionMismatchSource,
22 };
23 use std::fmt::Display;
24 
25 #[derive(Debug)]
26 enum ExplainError {
27     WrongArgCount,
28     InvalidArg,
29 }
30 
31 impl ExplainError {
to_str(self) -> &'static str32     fn to_str(self) -> &'static str {
33         match self {
34             ExplainError::WrongArgCount => "Expected a single arg, hex or decimal",
35             ExplainError::InvalidArg => {
36                 "Unrecognized argument format, expected a single hex or decimal argument"
37             }
38         }
39     }
40 }
41 
parse_arg(mut arg: &str) -> Result<u32, ExplainError>42 fn parse_arg(mut arg: &str) -> Result<u32, ExplainError> {
43     if let Some(stripped) = arg.strip_prefix("0x").or_else(|| arg.strip_prefix('x')) {
44         arg = stripped;
45     } else {
46         // First try to parse as base-10, unless there's a hex prefix.
47         if let Ok(out) = arg.parse() {
48             return Ok(out);
49         } else if let Ok(out) = arg.parse::<i32>() {
50             // Try parse negative numbers, and if successful, cast to u32
51             return Ok(out as u32);
52         }
53     }
54     // If parsing as base-10 failed, try base-16.
55     u32::from_str_radix(arg, 16).map_err(|_| ExplainError::InvalidArg)
56 }
57 
get_code_from_args() -> Result<u32, ExplainError>58 fn get_code_from_args() -> Result<u32, ExplainError> {
59     let mut args = std::env::args();
60     let _app_name = args.next();
61     let Some(out) = args.next() else {
62         return Err(ExplainError::WrongArgCount);
63     };
64     if args.next().is_some() {
65         return Err(ExplainError::WrongArgCount);
66     }
67     parse_arg(&out)
68 }
69 
expected_less_detail(detail: &[u8], at: &str)70 fn expected_less_detail(detail: &[u8], at: &str) {
71     println!(
72         "This tool expects the expanded status to have no detail in the {at},\n\
73         but instead it has {detail:#02x?}.\n\
74         Consider updating this tool."
75     );
76 }
77 
explain_code(code: u32)78 fn explain_code(code: u32) {
79     /// Bit that indicates that AP RO verification has succeeded at least once on device.
80     const LATCH_SUCCESS: u32 = 1 << 31;
81 
82     /// Bit that indicates that the AllowUnverifiedRo CCD capability was enabled on device at the
83     /// time that the extended status was queried.
84     const ALLOW_UNVERIFIED_RO_ENABLED: u32 = 1 << 30;
85 
86     /// Bit that indicates that the AllowUnverifiedRo CCD capability was set to never at the
87     /// time that the extended status was queried.
88     const ALLOW_UNVERIFIED_RO_NEVER: u32 = 1 << 29;
89 
90     println!("Code: 0x{code:08x}");
91     if let Ok(bottom) = u8::try_from(code) {
92         println!("This is likely a status code from TPM");
93         let text = ApRoVerificationTpmvStatus::from_u8(bottom)
94             .map_or("Unknown to this tool", |x| x.to_str());
95         println!("Description: {text}");
96         return;
97     }
98     if code == ApRoVerificationResult::from(Ok(())).into() {
99         println!("It is a SUCCESS status");
100         return;
101     }
102     let result_code =
103         code & !LATCH_SUCCESS & !ALLOW_UNVERIFIED_RO_ENABLED & !ALLOW_UNVERIFIED_RO_NEVER;
104     // Remove latch value before trying to parse the result
105     let result = ApRoVerificationResult::from(result_code);
106     let err = Result::from(result).expect_err("Already checked for success.");
107     if err == VerifyError::Internal(InternalErrorSource::CouldNotDeserialize, 0) {
108         println!("The code is not recognized by this tool");
109         return;
110     }
111     println!("This is likely an expanded error code");
112     if code & LATCH_SUCCESS != 0 {
113         println!("THIS ERROR OCURRED AFTER SUCCESS WAS LATCHED!")
114     }
115     if code & ALLOW_UNVERIFIED_RO_ENABLED != 0 {
116         println!("The system had the `AllowUnverifiedRo` capability enabled at query time")
117     }
118     if code & ALLOW_UNVERIFIED_RO_NEVER != 0 {
119         println!("The system had the `AllowUnverifiedRo` capability set to Never at query time")
120     }
121     let code = err.code();
122     let detail = err.detail();
123 
124     // TODO(kupiakos): Instead of displaying `Debug` for codes, display the
125     //                 contents of the docstrings.
126     //                 This would be best done with a proc macro on the enums.
127 
128     println!("Top level code: {:?} ({})", code, code as u8);
129     match code {
130         VerifyErrorCode::TooBig
131         | VerifyErrorCode::InconsistentGscvd
132         | VerifyErrorCode::InconsistentKeyblock
133         | VerifyErrorCode::MissingGscvd
134         | VerifyErrorCode::SettingNotProvisioned
135         | VerifyErrorCode::OutOfMemory
136         | VerifyErrorCode::InconsistentKey
137         | VerifyErrorCode::SpiRead
138         | VerifyErrorCode::NonZeroGbbFlags
139         | VerifyErrorCode::WrongRootKey => {
140             if let [0, 0, 0] = detail {
141                 println!("This expanded status carries no extra detail as expected.");
142             } else {
143                 expected_less_detail(&detail, "bottom 24 bits");
144             }
145         }
146 
147         VerifyErrorCode::FailedVerification => {
148             let top = detail[0];
149             if top == 0 {
150                 println!("Failed due to signature verification failure.");
151                 let sig_location = detail[1];
152                 if let Some(sig_location) = SignatureLocation::from_u8(sig_location) {
153                     println!("Where was the failing signature: {sig_location:?}");
154                 } else {
155                     println!("Unrecognized signature location ({sig_location})");
156                 }
157 
158                 if detail[2] != 0 {
159                     expected_less_detail(&detail[2..], "bottom 8 bits");
160                 }
161                 return;
162             }
163             println!("Failed due to a digest mismatch with the contents in AP flash.");
164             let num_root_key_hashes = (top >> 4) - 1;
165             println!(
166                 "The Ti50 image this came from had {num_root_key_hashes} hardcoded root key hashes."
167             );
168             let digest_location = top & 0x0F;
169             if let Some(digest_location) = DigestLocation::from_u8(digest_location) {
170                 println!("Which digest failed: {digest_location:?}");
171             } else {
172                 println!("Unrecognized digest location ({digest_location})");
173             }
174             let [_, got, expected] = detail;
175             println!("The first octet of the calculated hash: 0x{got:02x}");
176             println!("The first octet of the expected hash:   0x{expected:02x}");
177         }
178         VerifyErrorCode::VersionMismatch => {
179             let [source, none0, none1] = detail;
180             if none0 != 0 || none1 != 0 {
181                 expected_less_detail(&[none0, none1], "bottom 16 bits");
182             }
183             if let Some(known) = VersionMismatchSource::from_u8(source) {
184                 println!("Version mismatch source: {:?}", known);
185             } else {
186                 println!("Unknown version mismatch source ({})", source);
187             }
188         }
189         VerifyErrorCode::FailedStatusRegister1
190         | VerifyErrorCode::FailedStatusRegister2
191         | VerifyErrorCode::FailedStatusRegister3 => {
192             let register_index = match code {
193                 VerifyErrorCode::FailedStatusRegister1 => 1,
194                 VerifyErrorCode::FailedStatusRegister2 => 2,
195                 VerifyErrorCode::FailedStatusRegister3 => 3,
196                 _ => unreachable!(),
197             };
198             let [got, value, mask] = detail;
199             println!("Status Register {register_index} did not have an expected value.");
200             println!("The flash chip on the system returned 0x{got:02x}");
201             println!("The provisioned value is: 0x{value:02x}");
202             println!("The provisioned mask is:  0x{mask:02x}");
203             // Mask of 0 is a special case for an error getting provisioned value
204             if mask == 0 {
205                 if let Some(e) = BadValue::from_u8(value) {
206                     println!(
207                         "Error getting provisioned value due to it being {}",
208                         e.to_string()
209                     );
210                 } else {
211                     println!("Unknown error getting provision value: {value}");
212                 }
213             } else if got & mask == value & mask {
214                 println!("This doesn't make sense, because it should have passed.");
215             } else {
216                 println!(
217                     "It failed because 0x{got:02x} & 0x{mask:02x} != 0x{value:02x} & 0x{mask:02x}"
218                 );
219             }
220         }
221         VerifyErrorCode::UnsupportedCryptoAlgorithm => {
222             let [source, hi, lo] = detail;
223             let unknown_code = u16::from_be_bytes([hi, lo]);
224             println!("An unknown crypto algorithm was encountered: {unknown_code}");
225             if let Some(source) = CryptoAlgorithmSource::from_u8(source) {
226                 println!("Location of unknown crypto algorithm: {source:?}");
227             } else {
228                 println!("Unknown crypto algorithm location ({source})");
229             }
230         }
231         VerifyErrorCode::Internal => {
232             let [source, hi, lo] = detail;
233             let Some(source) = InternalErrorSource::from_u8(source) else {
234                 println!("Unknown internal error source ({source})");
235                 return;
236             };
237             let code = u16::from_be_bytes([hi, lo]);
238             match source {
239                 InternalErrorSource::Kernel => {
240                     println!("Internal error caused by the kernel");
241                     let code_str = match code {
242                         1 => Some("FAIL"),
243                         2 => Some("BUSY"),
244                         3 => Some("ALREADY"),
245                         4 => Some("OFF"),
246                         5 => Some("RESERVE"),
247                         6 => Some("INVAL"),
248                         7 => Some("SIZE"),
249                         8 => Some("CANCEL"),
250                         9 => Some("NOMEM"),
251                         10 => Some("NOSUPPORT"),
252                         11 => Some("NODEVICE"),
253                         12 => Some("UNINSTALLED"),
254                         13 => Some("NOACK"),
255                         _ => None,
256                     };
257                     if let Some(code_str) = code_str {
258                         println!("Kernel error code {code} ({code_str})");
259                     } else {
260                         println!("Unknown kernel error code {code}");
261                     }
262                 }
263                 InternalErrorSource::Crypto => {
264                     println!("Error caused by the crypto engine");
265                     let code = code as i16 as i32;
266                     println!("Raw code: {code}");
267                     println!("TODO: print out friendlier status, requires `enum_as` rework");
268                 }
269                 InternalErrorSource::VerifyFlashRanges => {
270                     println!("Error occurred while verifying flash ranges");
271                 }
272                 InternalErrorSource::CouldNotDeserialize => {
273                     unreachable!("Handled by \"code is not recognized by this tool\"")
274                 }
275             }
276         }
277         VerifyErrorCode::BoardIdMismatch => {
278             let [got_byte, got_nibble_expected_nibble, expected_byte] = detail;
279             let got = (got_byte as u32) << 4 | (got_nibble_expected_nibble as u32) >> 4;
280             let expected =
281                 ((got_nibble_expected_nibble & 0x0f) as u32) << 8 | (expected_byte as u32);
282 
283             struct Display3BoardIdNibbles(u32);
284             impl Display for Display3BoardIdNibbles {
285                 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286                     use core::fmt::Write as _;
287 
288                     write!(f, "{:03x} (first char: ", self.0)?;
289                     for b in std::ascii::escape_default((self.0 >> 4) as u8) {
290                         f.write_char(b.into())?;
291                     }
292                     if self.0 == 0x5a5 {
293                         write!(f, ", probably ZZCR")?;
294                     }
295                     write!(f, ")")
296                 }
297             }
298 
299             println!("There was a mismatch between the board ID in the GSCVD and on the board.");
300             println!("The detailed code carries the top 12 bits of the ID on AP flash and GSC.");
301             println!("In GSCVD: {}", Display3BoardIdNibbles(got));
302             println!("On GSC:   {}", Display3BoardIdNibbles(expected));
303         }
304     }
305 }
306 
main()307 fn main() {
308     if let Err(e) = get_code_from_args().map(explain_code) {
309         println!("{}", e.to_str());
310     }
311 }
312