opentitanlib/test_utils/
rpc.rs1use anyhow::Result;
5use crc::{CRC_32_ISO_HDLC, Crc};
6use serde::Serialize;
7use serde::de::DeserializeOwned;
8use std::time::Duration;
9
10use crate::io::console::ext::{PassFail, PassFailResult};
11use crate::io::console::{ConsoleDevice, ConsoleError, ConsoleExt};
12use crate::regex;
13use crate::test_utils::status::Status;
14
15include!(env!("ottf"));
17
18pub trait ConsoleSend<T>
19where
20 T: ConsoleDevice + ?Sized,
21{
22 fn send(&self, device: &T) -> Result<String>;
23 fn send_with_crc(&self, device: &T) -> Result<String>;
24 fn send_with_padding(&self, device: &T, max_size: usize) -> Result<String>;
25 fn send_with_padding_and_crc(&self, device: &T, max_size: usize, quiet: bool)
26 -> Result<String>;
27}
28
29impl<T, U> ConsoleSend<T> for U
30where
31 T: ConsoleDevice + ?Sized,
32 U: Serialize,
33{
34 fn send(&self, device: &T) -> Result<String> {
35 let s = serde_json::to_string(self)?;
36 log::info!("Sending: {}", s);
37 device.write(s.as_bytes())?;
38 Ok(s)
39 }
40
41 fn send_with_crc(&self, device: &T) -> Result<String> {
42 let s = serde_json::to_string(self)?;
43 log::info!("Sending: {}", s);
44 device.write(s.as_bytes())?;
45 let actual_crc = OttfCrc {
46 crc: Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(s.as_bytes()),
47 };
48 let crc_s = actual_crc.send(device)?;
49 Ok(s + &crc_s)
50 }
51
52 fn send_with_padding(&self, device: &T, max_size: usize) -> Result<String> {
53 let mut s = serde_json::to_string(self)?;
54 let pad_len = max_size - s.len();
55 let pad_str = ' '.to_string().repeat(pad_len);
56 s.insert_str(s.len() - 1, &pad_str);
57 device.write(s.as_bytes())?;
58 Ok(s)
59 }
60
61 fn send_with_padding_and_crc(
62 &self,
63 device: &T,
64 max_size: usize,
65 quiet: bool,
66 ) -> Result<String> {
67 let mut s = serde_json::to_string(self)?;
69 let pad_len = max_size - s.len();
70 let pad_str = ' '.to_string().repeat(pad_len);
71 s.insert_str(s.len() - 1, &pad_str);
72 let actual_crc = OttfCrc {
74 crc: Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(s.as_bytes()),
75 };
76 let max_crc = OttfCrc { crc: u32::MAX };
77 let mut crc_s = serde_json::to_string(&actual_crc)?;
78 let max_crc_s = serde_json::to_string(&max_crc)?;
79 let crc_pad_str = ' '.to_string().repeat(max_crc_s.len() - crc_s.len());
80 crc_s.insert_str(crc_s.len() - 1, &crc_pad_str);
81 if !quiet {
83 log::info!("Sending: {}", s.clone() + &crc_s);
84 }
85 device.write(s.as_bytes())?;
86 device.write(crc_s.as_bytes())?;
87 Ok(s + &crc_s)
88 }
89}
90
91pub trait ConsoleRecv<T>
92where
93 T: ConsoleDevice + ?Sized,
94{
95 fn recv(device: &T, timeout: Duration, quiet: bool, skip_crc: bool) -> Result<Self>
96 where
97 Self: Sized;
98}
99
100impl<T, U> ConsoleRecv<T> for U
101where
102 T: ConsoleDevice + ?Sized,
103 U: DeserializeOwned,
104{
105 fn recv(device: &T, timeout: Duration, quiet: bool, skip_crc: bool) -> Result<Self>
106 where
107 Self: Sized,
108 {
109 let device: &dyn ConsoleDevice = if quiet { &device } else { &device.logged() };
110 let result = device.wait_for_line(
111 PassFail(
112 regex!(r"RESP_OK:(.*) CRC:([0-9]+)"),
113 regex!(r"RESP_ERR:(.*) CRC:([0-9]+)"),
114 ),
115 timeout,
116 )?;
117 println!();
118 match result {
119 PassFailResult::Pass(cap) => {
120 let json_str = &cap[1];
121 let crc_str = &cap[2];
122 if !skip_crc {
123 check_crc(json_str, crc_str)?;
124 }
125 Ok(serde_json::from_str::<Self>(json_str)?)
126 }
127 PassFailResult::Fail(cap) => {
128 let json_str = &cap[1];
129 let crc_str = &cap[2];
130 check_crc(json_str, crc_str)?;
131 Err(serde_json::from_str::<Status>(json_str)?)?
132 }
133 }
134 }
135}
136
137fn check_crc(json_str: &str, crc_str: &str) -> Result<()> {
138 let crc = crc_str.parse::<u32>()?;
139 let actual_crc = Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(json_str.as_bytes());
140 if crc != actual_crc {
141 return Err(
142 ConsoleError::GenericError("CRC didn't match received json body.".into()).into(),
143 );
144 }
145 Ok(())
146}