blob: d1d03d6da1d959a193f5bc942f10aad710219247 [file] [log] [blame] [edit]
// 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::collections::VecDeque;
use std::fmt::Write;
use crate::command_runner::CommandRunner;
use crate::gsc::RmaSnBits;
use crate::gsc::GSCTOOL_CMD_NAME;
use crate::output::HwsecOutput;
use crate::output::HwsecStatus;
use crate::tpm2::tests::split_into_hex_strtok;
use crate::tpm2::BoardID;
// For any member variable x in MockCommandInput:
// x = Some(_) means that we would check the correspondence;
// otherwise, we don't really care about its exact value.
// To know more, take a glance at impl CommandRunner for MockCommandRunner.
pub struct MockCommandInput {
pub cmd_name: Option<String>,
pub args: Option<Vec<String>>,
}
impl MockCommandInput {
pub fn new(cmd_name: &str, args: Vec<&str>) -> Self {
Self {
cmd_name: Some(cmd_name.to_owned()),
args: Some(args.iter().map(|&s| s.into()).collect()),
}
}
}
pub struct MockCommandOutput {
pub result: Result<HwsecOutput, std::io::Error>,
}
impl MockCommandOutput {
pub fn new(exit_status: i32, out: &str, err: &str) -> Self {
Self {
result: Ok(HwsecOutput {
status: HwsecStatus::from_raw(exit_status),
stdout: out.to_owned().as_bytes().to_vec(),
stderr: err.to_owned().as_bytes().to_vec(),
}),
}
}
}
pub struct MockCommandRunner {
expectations: VecDeque<(MockCommandInput, MockCommandOutput)>,
}
impl Default for MockCommandRunner {
fn default() -> Self {
Self::new()
}
}
fn u8_slice_to_upper_hex_string(bytes: &[u8]) -> String {
let mut s = String::with_capacity(bytes.len() * 2);
for &b in bytes {
write!(&mut s, "{:02X}", b).unwrap();
}
s
}
impl MockCommandRunner {
pub fn new() -> Self {
MockCommandRunner {
expectations: VecDeque::new(),
}
}
pub fn add_expectation(&mut self, inp: MockCommandInput, out: MockCommandOutput) {
self.expectations.push_back((inp, out));
}
pub fn set_trunksd_running(&mut self, status: bool) {
self.add_expectation(
MockCommandInput::new("status", vec!["trunksd"]),
MockCommandOutput::new(
0,
if status {
"trunksd start/running, process 17302"
} else {
"trunksd stop/waiting"
},
"",
),
);
}
pub fn add_tpm_interaction(
&mut self,
cmd_name: &str,
flag: Vec<&str>,
hex_str_tokens: Vec<&str>,
exit_status: i32,
out: &str,
err: &str,
) {
self.add_expectation(
MockCommandInput::new(cmd_name, [&flag[..], &hex_str_tokens[..]].concat()),
MockCommandOutput::new(exit_status, out, err),
);
}
pub fn add_gsctool_interaction(
&mut self,
mut flag: Vec<&str>,
exit_status: i32,
out: &str,
err: &str,
) {
if cfg!(feature = "ti50_onboard") {
flag.push("--dauntless");
}
self.add_expectation(
MockCommandInput::new(GSCTOOL_CMD_NAME, flag),
MockCommandOutput::new(exit_status, out, err),
);
}
pub fn add_metrics_client_expectation(&mut self, event_id: u64) {
use crate::gsc::GSC_METRICS_PREFIX;
self.add_expectation(
MockCommandInput::new(
"metrics_client",
vec![
"-s",
&format!("{}.FlashLog", GSC_METRICS_PREFIX),
&format!("0x{:02x}", event_id),
],
),
MockCommandOutput::new(0, "", ""),
);
}
pub fn add_successful_generic_read_board_id_interaction(&mut self, board_id: BoardID) {
self.add_tpm_interaction(
"trunks_send",
vec!["--raw"],
split_into_hex_strtok(
"80 02 00 00 00 23 00 00 \
01 4e 01 3f ff 00 01 3f \
ff 00 00 00 00 09 40 00 \
00 09 00 00 00 00 00 00 \
0c 00 00",
),
0,
&format!(
"800200000021000000000000000E000C{:08X}{:08X}{:08X}0000010000",
board_id.part_1.swap_bytes(),
board_id.part_2.swap_bytes(),
board_id.flag.swap_bytes(),
),
"",
);
}
pub fn add_successful_generic_read_board_id_arbitary_interaction(&mut self) {
// Use this when not in want of specifying board id.
// Reading board id with mock context
// after calling this function with would return
// BoardID {
// part_1: 0x4d524646,
// part_2: 0xb2adb9b9,
// flag: 0x00007f7f
// }
self.add_successful_generic_read_board_id_interaction(BoardID {
part_1: 0x4d524646,
part_2: 0xb2adb9b9,
flag: 0x00007f7f,
});
}
pub fn add_successful_gsctool_read_board_id_interaction(&mut self, board_id: BoardID) {
self.add_gsctool_interaction(
vec!["--any", "--board_id"],
0,
&format!(
"finding_device 18d1:5014\n\
Found device.\n\
found interface 3 endpoint 4, chunk_len 64\n\
READY\n\
-------\n\
Board ID space: {:08x}:{:08x}:{:08x}\n",
board_id.part_1, board_id.part_2, board_id.flag
),
"",
);
}
pub fn add_successful_gsctool_read_board_id_arbitary_interaction(&mut self) {
// Use this when not in want of specifying a board id.
// Reading board id with mock context
// after calling this function with would return
// BoardID {
// part_1: 0x43425559,
// part_2: 0xbcbdaaa6,
// flag: 0x00007f80
// }
self.add_successful_gsctool_read_board_id_interaction(BoardID {
part_1: 0x43425559,
part_2: 0xbcbdaaa6,
flag: 0x00007f80,
});
}
pub fn add_successful_gsc_get_name_arbitary_interaction(&mut self) {
self.add_successful_gsctool_read_board_id_arbitary_interaction();
}
pub fn add_successful_gsc_read_rma_sn_bits_interaction_non_generic_tpm2(
&mut self,
rma_sn_bits: RmaSnBits,
) {
self.set_trunksd_running(true);
self.add_tpm_interaction(
"trunks_send",
vec!["--raw"],
split_into_hex_strtok(
"80 02 00 00 00 23 00 00 \
01 4e 01 3f ff 01 01 3f \
ff 01 00 00 00 09 40 00 \
00 09 00 00 00 00 00 00 \
10 00 00",
),
0,
&format!(
"80020000002500000000000000120010{}{:02X}{}0000010000",
u8_slice_to_upper_hex_string(&rma_sn_bits.sn_data_version),
rma_sn_bits.rma_status,
u8_slice_to_upper_hex_string(&rma_sn_bits.sn_bits),
),
"",
);
}
pub fn add_successful_gsc_read_rma_sn_bits_arbitary_interaction(&mut self) {
// Use this when not in want of specifying rma sn bits.
// Reading rma sn bits with mock context
// after calling this function with would return
// RmaSnBits {
// sn_data_version: [0x0f, 0xff, 0xff],
// rma_status: 0xff,
// sn_bits: [0x87, 0x7f, 0x50, 0xd2, 0x08, 0xec, 0x89, 0xe9, 0xc1, 0x69, 0x1f, 0x54],
// }
self.add_successful_gsc_read_rma_sn_bits_interaction_non_generic_tpm2(RmaSnBits {
sn_data_version: [0x0f, 0xff, 0xff],
rma_status: 0xff,
sn_bits: [
0x87, 0x7f, 0x50, 0xd2, 0x08, 0xec, 0x89, 0xe9, 0xc1, 0x69, 0x1f, 0x54,
],
});
}
}
impl CommandRunner for MockCommandRunner {
fn run(&mut self, cmd_name: &str, args: Vec<&str>) -> Result<HwsecOutput, std::io::Error> {
assert!(
!self.expectations.is_empty(),
"Failed to pop front from queue -- it's empty!"
);
let io_pair = self.expectations.pop_front().unwrap();
let inp = io_pair.0;
let out = io_pair.1;
if let Some(inp_name) = inp.cmd_name {
assert_eq!(cmd_name, inp_name);
}
if let Some(inp_args) = inp.args {
assert_eq!(args, inp_args);
}
out.result
}
fn output(&mut self, cmd_name: &str, args: Vec<&str>) -> Result<String, std::io::Error> {
let run_result = self.run(cmd_name, args)?;
Ok(String::from_utf8_lossy(&run_result.stdout).to_string())
}
}
impl Drop for MockCommandRunner {
fn drop(&mut self) {
assert!(self.expectations.is_empty());
}
}