| // 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 std::str::SplitAsciiWhitespace; | |
| use std::time::SystemTime; | |
| use log::error; | |
| use log::info; | |
| use crate::context::Context; | |
| use crate::error::HwsecError; | |
| use crate::gsc::run_gsctool_cmd; | |
| use crate::gsc::run_metrics_client; | |
| use crate::gsc::GSC_METRICS_PREFIX; | |
| // This should match the definitions in Cr50 firmware. | |
| const CR50_FE_LOG_NVMEM: u64 = 5; | |
| const CR50_FE_LOG_AP_RO_VERIFICATION: u64 = 9; | |
| const CR50_APROF_CHECK_TRIGGERED: u64 = 3; | |
| // This should match the definitions in Cr50 firmware. | |
| const TI50_FE_LOG_NVMEM: u64 = 3; | |
| const TI50_FE_LOG_AP_RO_VERIFICATION: u64 = 7; | |
| // Offsets used to expand nvmem and apro verification FLOG codes. | |
| const NVMEM_MALLOC: u64 = 200; | |
| const APRO_OFFSET: u64 = 160; | |
| // Events are reported in 1 byte. | |
| const EVENT_ID_MAX: u64 = 255; | |
| const EVENT_IGNORE_ENTRY: u64 = EVENT_ID_MAX + 1; | |
| pub fn read_prev_timestamp_from_file( | |
| ctx: &mut impl Context, | |
| file_path: &str, | |
| ) -> Result<u64, HwsecError> { | |
| if !ctx.path_exists(file_path) { | |
| info!("{} not found, creating.", file_path); | |
| match ctx.write_contents_to_file(file_path, b"0") { | |
| Ok(_) => return Ok(0), | |
| Err(_) => return Err(HwsecError::FileError), | |
| } | |
| } | |
| let file_string = ctx.read_file_to_string(file_path)?; | |
| file_string | |
| .parse::<u64>() | |
| .map_err(|_| HwsecError::InternalError) | |
| } | |
| pub fn update_timestamp_file( | |
| ctx: &mut impl Context, | |
| new_stamp: u64, | |
| file_path: &str, | |
| ) -> Result<(), HwsecError> { | |
| ctx.write_contents_to_file(file_path, &new_stamp.to_ne_bytes()) | |
| } | |
| pub fn set_log_file_time_base(ctx: &mut impl Context) -> Result<(), HwsecError> { | |
| let epoch_secs = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { | |
| Ok(epoch) => epoch.as_secs(), | |
| Err(_) => return Err(HwsecError::SystemTimeError), | |
| }; | |
| let gsctool_result = run_gsctool_cmd(ctx, vec!["--any", "--tstamp", &epoch_secs.to_string()])?; | |
| if !gsctool_result.status.success() { | |
| error!("Failed to set Cr50 flash log time base to {}", epoch_secs); | |
| return Err(HwsecError::GsctoolError( | |
| gsctool_result.status.code().unwrap(), | |
| )); | |
| } | |
| info!("Set Cr50 flash log base time to {}", epoch_secs); | |
| Ok(()) | |
| } | |
| fn get_next_u64_from_iterator( | |
| iter: &mut SplitAsciiWhitespace, | |
| radix: u32, | |
| ) -> Result<u64, HwsecError> { | |
| match iter.next() { | |
| None => { | |
| error!("Failed to parse gsctool log line"); | |
| Err(HwsecError::InternalError) | |
| } | |
| Some(s) => u64::from_str_radix(s, radix).map_err(|_| { | |
| error!("Failed to parse gsctool log line"); | |
| HwsecError::InternalError | |
| }), | |
| } | |
| } | |
| #[cfg(feature = "ti50_onboard")] | |
| fn expand_cr50_event_id(_event_id: u64, _fe_log_event_id: u64) -> bool { | |
| return false; | |
| } | |
| #[cfg(feature = "cr50_onboard")] | |
| fn expand_cr50_event_id(event_id: u64, fe_log_event_id: u64) -> bool { | |
| return event_id == fe_log_event_id; | |
| } | |
| #[cfg(feature = "ti50_onboard")] | |
| fn expand_ti50_event_id(event_id: u64, fe_log_event_id: u64) -> bool { | |
| return event_id == fe_log_event_id; | |
| } | |
| #[cfg(feature = "cr50_onboard")] | |
| fn expand_ti50_event_id(_event_id: u64, _fe_log_event_id: u64) -> bool { | |
| return false; | |
| } | |
| // The output from "gsctool -a -M -L 0" may look as follows: | |
| // 1:00 | |
| // 1623743076:09 00 | |
| // 1623743077:09 02 | |
| // 1623743085:09 00 | |
| // 1623743086:09 01 | |
| // 1666170902:09 00 | |
| // 1666170905:09 02 | |
| fn parse_timestamp_and_event_id_from_log_entry(line: &str) -> Result<(u64, u64), HwsecError> { | |
| let binding = line.trim().replace(':', " "); | |
| let mut parts = binding.split_ascii_whitespace(); | |
| let stamp: u64 = get_next_u64_from_iterator(&mut parts, 10)?; | |
| let mut event_id: u64 = get_next_u64_from_iterator(&mut parts, 16)?; | |
| let mut offset: u64 = 0; | |
| if event_id > EVENT_ID_MAX { | |
| return Err(HwsecError::InternalError); | |
| } | |
| // Events that expand the first byte. | |
| if expand_cr50_event_id(event_id, CR50_FE_LOG_NVMEM) | |
| || expand_ti50_event_id(event_id, TI50_FE_LOG_NVMEM) | |
| { | |
| offset = NVMEM_MALLOC; | |
| } else if expand_ti50_event_id(event_id, TI50_FE_LOG_AP_RO_VERIFICATION) { | |
| offset = APRO_OFFSET; | |
| } | |
| if offset != 0 { | |
| // If there's a non-zero offset, then return the expand the result using the | |
| // event offset and add the first byte of the payload. | |
| // ex: if event_id is 05 on a cr50 device, which is CR50_FE_LOG_NVMEM, | |
| // then adopt '200 + the first byte of payload' as an new event_id, as | |
| // defined as enum Cr50FlashLogs in | |
| // event_id=05, payload[0]=00, then new event id is 200, which is | |
| // labeled as 'Nvmem Malloc'. | |
| // https://chromium.googlesource.com/chromium/src/+//main:tools/metrics/ | |
| // histograms/enums.xml. | |
| let payload_0: u64 = get_next_u64_from_iterator(&mut parts, 16)?; | |
| event_id = offset + payload_0; | |
| } | |
| // Cr50 AP RO verification events require special handling, because it logs | |
| // non-critical AP RO verification events. Handle it separately from the | |
| // other events that process the first byte. | |
| if expand_cr50_event_id(event_id, CR50_FE_LOG_AP_RO_VERIFICATION) { | |
| // If event_id is 09, which is CR50_FE_LOG_AP_RO_VERIFICATION, then adopt | |
| // '160 + the first byte of payload' as an new event_id, as defined as | |
| // enum Cr50FlashLogs in | |
| // https://chromium.googlesource.com/chromium/src/+//main:tools/metrics/ | |
| // histograms/enums.xml. | |
| // For example, event_id=09, payload[0]=03, then new event id is 163, which | |
| // is labeled as 'AP RO Triggered'. | |
| let payload_0: u64 = get_next_u64_from_iterator(&mut parts, 16)?; | |
| // Ignore the codes less than CHECK_TRIGGERED. They're normal. Verification | |
| // did not run. | |
| if payload_0 < CR50_APROF_CHECK_TRIGGERED { | |
| event_id = EVENT_IGNORE_ENTRY; | |
| } else { | |
| event_id = APRO_OFFSET + payload_0; | |
| } | |
| } | |
| Ok((stamp, event_id)) | |
| } | |
| pub fn gsc_flash_log(ctx: &mut impl Context, prev_stamp: u64) -> Result<u64, (HwsecError, u64)> { | |
| let Ok(gsctool_result) = run_gsctool_cmd( | |
| ctx, | |
| vec!["--any", "--machine", "--flog", &prev_stamp.to_string()], | |
| ) else { | |
| return Err((HwsecError::GsctoolError(1), 0)); | |
| }; | |
| if !gsctool_result.status.success() { | |
| error!("Failed to get flash log entries"); | |
| return Err(( | |
| HwsecError::GsctoolError(gsctool_result.status.code().unwrap()), | |
| 0, | |
| )); | |
| } | |
| let Ok(content) = std::str::from_utf8(&gsctool_result.stdout) else { | |
| return Err((HwsecError::GsctoolResponseBadFormatError, 0)); | |
| }; | |
| let mut new_stamp: u64 = 0; | |
| for entry in content.lines() { | |
| let Ok((stamp, event_id)) = parse_timestamp_and_event_id_from_log_entry(entry) else { | |
| return Err((HwsecError::InternalError, new_stamp)); | |
| }; | |
| if event_id > EVENT_ID_MAX { | |
| continue; | |
| } | |
| let Ok(metrics_client_result) = run_metrics_client( | |
| ctx, | |
| vec![ | |
| "-s", | |
| &format!("{}.FlashLog", GSC_METRICS_PREFIX), | |
| &format!("0x{:02x}", event_id), | |
| ], | |
| ) else { | |
| return Err((HwsecError::MetricsClientFailureError, new_stamp)); | |
| }; | |
| if metrics_client_result.status.code().unwrap() == 0 { | |
| new_stamp = stamp; | |
| } else { | |
| error!( | |
| "Failed to log event {} at timestamp {}", | |
| event_id, new_stamp | |
| ); | |
| return Err((HwsecError::InternalError, new_stamp)); | |
| } | |
| } | |
| Ok(new_stamp) | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::parse_timestamp_and_event_id_from_log_entry; | |
| use crate::context::mock::MockContext; | |
| use crate::context::Context; | |
| use crate::error::HwsecError; | |
| use crate::gsc::gsc_flash_log; | |
| const PREV_STAMP: u64 = 0; | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_ok() { | |
| let line: &str = &format!("{:>10}:00", 1); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| assert_eq!(result, Ok((1, 0))); | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_ok_hex_event() { | |
| let line: &str = "100:0A"; | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| assert_eq!(result, Ok((100, 10))); | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_expand_cr50_nvmem() { | |
| use super::CR50_FE_LOG_NVMEM; | |
| use super::NVMEM_MALLOC; | |
| let line: &str = &format!("{:>10}:{:02x} 00", 1, CR50_FE_LOG_NVMEM); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| if cfg!(feature = "cr50_onboard") { | |
| assert_eq!(result, Ok((1, NVMEM_MALLOC))); | |
| } else { | |
| assert_eq!(result, Ok((1, CR50_FE_LOG_NVMEM))); | |
| } | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_expand_ti50_nvmem() { | |
| use super::NVMEM_MALLOC; | |
| use super::TI50_FE_LOG_NVMEM; | |
| let line: &str = &format!("{:>10}:{:02x} 00", 1, TI50_FE_LOG_NVMEM); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| if cfg!(feature = "ti50_onboard") { | |
| assert_eq!(result, Ok((1, NVMEM_MALLOC))); | |
| } else { | |
| assert_eq!(result, Ok((1, TI50_FE_LOG_NVMEM))); | |
| } | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_expand_cr50_apro() { | |
| use super::APRO_OFFSET; | |
| use super::CR50_APROF_CHECK_TRIGGERED; | |
| use super::CR50_FE_LOG_AP_RO_VERIFICATION; | |
| let line: &str = &format!( | |
| "{:>10}:{:02x} {:02x}", | |
| 1, CR50_FE_LOG_AP_RO_VERIFICATION, CR50_APROF_CHECK_TRIGGERED | |
| ); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| if cfg!(feature = "cr50_onboard") { | |
| assert_eq!(result, Ok((1, APRO_OFFSET + CR50_APROF_CHECK_TRIGGERED))); | |
| } else { | |
| assert_eq!(result, Ok((1, CR50_FE_LOG_AP_RO_VERIFICATION))); | |
| } | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_expand_cr50_ignored_apro() { | |
| use super::CR50_FE_LOG_AP_RO_VERIFICATION; | |
| use super::EVENT_IGNORE_ENTRY; | |
| let line: &str = &format!("{:>10}:{:02x} 02", 1, CR50_FE_LOG_AP_RO_VERIFICATION); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| if cfg!(feature = "cr50_onboard") { | |
| assert_eq!(result, Ok((1, EVENT_IGNORE_ENTRY))); | |
| } else { | |
| assert_eq!(result, Ok((1, CR50_FE_LOG_AP_RO_VERIFICATION))); | |
| } | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_expand_ti50_apro() { | |
| use super::APRO_OFFSET; | |
| use super::CR50_APROF_CHECK_TRIGGERED; | |
| use super::TI50_FE_LOG_AP_RO_VERIFICATION; | |
| let line: &str = &format!( | |
| "{:>10}:{:02x} {:02x}", | |
| 1, TI50_FE_LOG_AP_RO_VERIFICATION, CR50_APROF_CHECK_TRIGGERED | |
| ); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| if cfg!(feature = "ti50_onboard") { | |
| assert_eq!(result, Ok((1, APRO_OFFSET + CR50_APROF_CHECK_TRIGGERED))); | |
| } else { | |
| assert_eq!(result, Ok((1, TI50_FE_LOG_AP_RO_VERIFICATION))); | |
| } | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_expand_ti50_apro_ok() { | |
| use super::APRO_OFFSET; | |
| use super::TI50_FE_LOG_AP_RO_VERIFICATION; | |
| // Cr50 reporting drops ap ro verification events less than 3. Make sure | |
| // Ti50 doesn't. | |
| let line: &str = &format!("{:>10}:{:02x} 00", 1, TI50_FE_LOG_AP_RO_VERIFICATION); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| if cfg!(feature = "ti50_onboard") { | |
| assert_eq!(result, Ok((1, APRO_OFFSET))); | |
| } else { | |
| assert_eq!(result, Ok((1, TI50_FE_LOG_AP_RO_VERIFICATION))); | |
| } | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_not_integer() { | |
| let line: &str = "TEST"; | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| assert_eq!(result, Err(HwsecError::InternalError)); | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_hex_timestamp() { | |
| let line: &str = "A:00"; | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| assert_eq!(result, Err(HwsecError::InternalError)); | |
| } | |
| #[test] | |
| fn test_parse_large_event_id() { | |
| use super::EVENT_ID_MAX; | |
| let line: &str = &format!("{:>10}:{:02x}", 1, EVENT_ID_MAX + 1); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| assert_eq!(result, Err(HwsecError::InternalError)); | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_entry_missing_event_id() { | |
| let line: &str = &format!("{:>10}", 1); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| assert_eq!(result, Err(HwsecError::InternalError)); | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_cr50_nvmem_missing_payload_0() { | |
| use super::CR50_FE_LOG_NVMEM; | |
| let line: &str = &format!("{:>10}:{:02x}", 1, CR50_FE_LOG_NVMEM); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| if cfg!(feature = "cr50_onboard") { | |
| assert_eq!(result, Err(HwsecError::InternalError)); | |
| } else { | |
| assert_eq!(result, Ok((1, CR50_FE_LOG_NVMEM))); | |
| } | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_ti50_nvmem_missing_payload_0() { | |
| use super::TI50_FE_LOG_NVMEM; | |
| let line: &str = &format!("{:>10}:{:02x}", 1, TI50_FE_LOG_NVMEM); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| if cfg!(feature = "ti50_onboard") { | |
| assert_eq!(result, Err(HwsecError::InternalError)); | |
| } else { | |
| assert_eq!(result, Ok((1, TI50_FE_LOG_NVMEM))); | |
| } | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_cr50_apro_missing_payload_0() { | |
| use super::CR50_FE_LOG_AP_RO_VERIFICATION; | |
| let line: &str = &format!("{:>10}:{:02x}", 1, CR50_FE_LOG_AP_RO_VERIFICATION); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| if cfg!(feature = "cr50_onboard") { | |
| assert_eq!(result, Err(HwsecError::InternalError)); | |
| } else { | |
| assert_eq!(result, Ok((1, CR50_FE_LOG_AP_RO_VERIFICATION))); | |
| } | |
| } | |
| #[test] | |
| fn test_parse_timestamp_and_event_id_from_log_ti50_apro_missing_payload_0() { | |
| use super::TI50_FE_LOG_AP_RO_VERIFICATION; | |
| let line: &str = &format!("{:>10}:{:02x}", 1, TI50_FE_LOG_AP_RO_VERIFICATION); | |
| let result = parse_timestamp_and_event_id_from_log_entry(line); | |
| if cfg!(feature = "ti50_onboard") { | |
| assert_eq!(result, Err(HwsecError::InternalError)); | |
| } else { | |
| assert_eq!(result, Ok((1, TI50_FE_LOG_AP_RO_VERIFICATION))); | |
| } | |
| } | |
| #[test] | |
| fn test_gsc_flash_log_empty_flash_log() { | |
| let mut mock_ctx = MockContext::new(); | |
| mock_ctx.cmd_runner().add_gsctool_interaction( | |
| vec!["--any", "--machine", "--flog", "0"], | |
| 0, | |
| "", | |
| "", | |
| ); | |
| let result = gsc_flash_log(&mut mock_ctx, PREV_STAMP); | |
| assert_eq!(result, Ok(0)); | |
| } | |
| #[test] | |
| fn test_gsc_flash_log_multiple_lines_flash_log() { | |
| use super::APRO_OFFSET; | |
| use super::CR50_APROF_CHECK_TRIGGERED; | |
| use super::CR50_FE_LOG_AP_RO_VERIFICATION; | |
| let mut mock_ctx = MockContext::new(); | |
| mock_ctx.cmd_runner().add_gsctool_interaction( | |
| vec!["--any", "--machine", "--flog", "0"], | |
| 0, | |
| &format!( | |
| "{:>10}:00\n{:>10}:{:02x} {:02x}\n{:>10}:{:02x} {:02x}", | |
| 1, | |
| 2, | |
| CR50_FE_LOG_AP_RO_VERIFICATION, | |
| CR50_APROF_CHECK_TRIGGERED, | |
| 3, | |
| CR50_FE_LOG_AP_RO_VERIFICATION, | |
| CR50_APROF_CHECK_TRIGGERED | |
| ), | |
| "", | |
| ); | |
| mock_ctx.cmd_runner().add_metrics_client_expectation(0); | |
| let mut expected_event: u64 = CR50_FE_LOG_AP_RO_VERIFICATION; | |
| if cfg!(feature = "cr50_onboard") { | |
| expected_event = APRO_OFFSET + CR50_APROF_CHECK_TRIGGERED; | |
| } | |
| for _ in 0..2 { | |
| mock_ctx | |
| .cmd_runner() | |
| .add_metrics_client_expectation(expected_event); | |
| } | |
| let result = gsc_flash_log(&mut mock_ctx, PREV_STAMP); | |
| assert_eq!(result, Ok(3)); | |
| } | |
| #[test] | |
| fn test_gsc_flash_log_event_too_large() { | |
| use super::EVENT_ID_MAX; | |
| let last_good_timestamp: u64 = 75; | |
| let mut mock_ctx = MockContext::new(); | |
| mock_ctx.cmd_runner().add_gsctool_interaction( | |
| vec!["--any", "--machine", "--flog", "0"], | |
| 0, | |
| &format!( | |
| "{:>10}:00\n{:>10}:{:02x}\n{:>10}:{:02x}\n{:>10}:{:02x}", | |
| 1, | |
| 2, | |
| EVENT_ID_MAX - 1, | |
| last_good_timestamp, | |
| EVENT_ID_MAX, | |
| 4, | |
| EVENT_ID_MAX + 1 | |
| ), | |
| "", | |
| ); | |
| mock_ctx.cmd_runner().add_metrics_client_expectation(0); | |
| mock_ctx | |
| .cmd_runner() | |
| .add_metrics_client_expectation(EVENT_ID_MAX - 1); | |
| mock_ctx | |
| .cmd_runner() | |
| .add_metrics_client_expectation(EVENT_ID_MAX); | |
| let result = gsc_flash_log(&mut mock_ctx, PREV_STAMP); | |
| assert_eq!( | |
| result, | |
| Err((HwsecError::InternalError, last_good_timestamp)) | |
| ); | |
| } | |
| #[test] | |
| fn test_gsc_flash_log_multiple_lines_cr50_ignore_apro() { | |
| use super::CR50_APROF_CHECK_TRIGGERED; | |
| use super::CR50_FE_LOG_AP_RO_VERIFICATION; | |
| let mut mock_ctx = MockContext::new(); | |
| mock_ctx.cmd_runner().add_gsctool_interaction( | |
| vec!["--any", "--machine", "--flog", "0"], | |
| 0, | |
| &format!( | |
| "{:>10}:00\n{:>10}:{:02x} {:02x}", | |
| 1, | |
| 2, | |
| CR50_FE_LOG_AP_RO_VERIFICATION, | |
| CR50_APROF_CHECK_TRIGGERED - 1 | |
| ), | |
| "", | |
| ); | |
| mock_ctx.cmd_runner().add_metrics_client_expectation(0); | |
| let mut num_cmds: u64 = 1; | |
| if !cfg!(feature = "cr50_onboard") { | |
| num_cmds = 2; | |
| mock_ctx | |
| .cmd_runner() | |
| .add_metrics_client_expectation(CR50_FE_LOG_AP_RO_VERIFICATION); | |
| } | |
| let result = gsc_flash_log(&mut mock_ctx, PREV_STAMP); | |
| assert_eq!(result, Ok(num_cmds)); | |
| } | |
| #[test] | |
| fn test_gsc_flash_log_multiple_lines_ti50_apro() { | |
| use super::APRO_OFFSET; | |
| use super::CR50_APROF_CHECK_TRIGGERED; | |
| use super::TI50_FE_LOG_AP_RO_VERIFICATION; | |
| let mut mock_ctx = MockContext::new(); | |
| mock_ctx.cmd_runner().add_gsctool_interaction( | |
| vec!["--any", "--machine", "--flog", "0"], | |
| 0, | |
| &format!( | |
| "{:>10}:00\n{:>10}:{:02x} {:02x}\n{:>10}:{:02x} {:02x}", | |
| 1, | |
| 2, | |
| TI50_FE_LOG_AP_RO_VERIFICATION, | |
| CR50_APROF_CHECK_TRIGGERED - 1, | |
| 3, | |
| TI50_FE_LOG_AP_RO_VERIFICATION, | |
| CR50_APROF_CHECK_TRIGGERED | |
| ), | |
| "", | |
| ); | |
| mock_ctx.cmd_runner().add_metrics_client_expectation(0); | |
| if cfg!(feature = "ti50_onboard") { | |
| mock_ctx | |
| .cmd_runner() | |
| .add_metrics_client_expectation(APRO_OFFSET + CR50_APROF_CHECK_TRIGGERED - 1); | |
| mock_ctx | |
| .cmd_runner() | |
| .add_metrics_client_expectation(APRO_OFFSET + CR50_APROF_CHECK_TRIGGERED); | |
| } else { | |
| for _ in 0..2 { | |
| mock_ctx | |
| .cmd_runner() | |
| .add_metrics_client_expectation(TI50_FE_LOG_AP_RO_VERIFICATION); | |
| } | |
| } | |
| let result = gsc_flash_log(&mut mock_ctx, PREV_STAMP); | |
| assert_eq!(result, Ok(3)); | |
| } | |
| #[test] | |
| fn test_gsc_flash_log_event_gsctool_error() { | |
| let mut mock_ctx = MockContext::new(); | |
| mock_ctx.cmd_runner().add_gsctool_interaction( | |
| vec!["--any", "--machine", "--flog", "0"], | |
| 1, | |
| "", | |
| "", | |
| ); | |
| let result = gsc_flash_log(&mut mock_ctx, PREV_STAMP); | |
| assert_eq!(result, Err((HwsecError::GsctoolError(1), 0))); | |
| } | |
| #[test] | |
| fn test_read_prev_timestamp_from_file_ok() { | |
| use super::read_prev_timestamp_from_file; | |
| let mut mock_ctx = MockContext::new(); | |
| mock_ctx.create_path("mock_file_path"); | |
| assert_eq!( | |
| mock_ctx.write_contents_to_file("mock_file_path", b"1"), | |
| Ok(()) | |
| ); | |
| let result = read_prev_timestamp_from_file(&mut mock_ctx, "mock_file_path"); | |
| assert_eq!(result, Ok(1)); | |
| } | |
| #[test] | |
| fn test_read_prev_timestamp_from_file_not_exist() { | |
| use super::read_prev_timestamp_from_file; | |
| let mut mock_ctx = MockContext::new(); | |
| let result = read_prev_timestamp_from_file(&mut mock_ctx, "mock_file_path"); | |
| assert_eq!(result, Ok(0)); | |
| } | |
| #[test] | |
| fn test_read_prev_timestamp_from_file_multiple_lines() { | |
| use super::read_prev_timestamp_from_file; | |
| let mut mock_ctx = MockContext::new(); | |
| mock_ctx.create_path("mock_file_path"); | |
| assert_eq!( | |
| mock_ctx.write_contents_to_file("mock_file_path", b"1\n2"), | |
| Ok(()) | |
| ); | |
| let result = read_prev_timestamp_from_file(&mut mock_ctx, "mock_file_path"); | |
| assert_eq!(result, Err(HwsecError::InternalError)); | |
| } | |
| #[test] | |
| fn test_read_prev_timestamp_from_file_not_u64() { | |
| use super::read_prev_timestamp_from_file; | |
| let mut mock_ctx = MockContext::new(); | |
| mock_ctx.create_path("mock_file_path"); | |
| assert_eq!( | |
| mock_ctx.write_contents_to_file("mock_file_path", b"test"), | |
| Ok(()) | |
| ); | |
| let result = read_prev_timestamp_from_file(&mut mock_ctx, "mock_file_path"); | |
| assert_eq!(result, Err(HwsecError::InternalError)); | |
| } | |
| } |