opentitanlib/test_utils/
rpc.rs1use anyhow::{Result, anyhow};
5use crc::{CRC_32_ISO_HDLC, Crc};
6use regex::Regex;
7use serde::Serialize;
8use serde::de::DeserializeOwned;
9use std::io::Write;
10use std::time::Duration;
11
12use crate::io::console::{ConsoleDevice, ConsoleError};
13use crate::test_utils::status::Status;
14use crate::uart::console::{ExitStatus, UartConsole};
15
16include!(env!("ottf"));
18
19pub trait ConsoleSend<T>
20where
21 T: ConsoleDevice + ?Sized,
22{
23 fn send(&self, device: &T) -> Result<()>;
24 fn send_with_crc(&self, device: &T) -> Result<()>;
25}
26
27impl<T, U> ConsoleSend<T> for U
28where
29 T: ConsoleDevice + ?Sized,
30 U: Serialize,
31{
32 fn send(&self, device: &T) -> Result<()> {
33 let s = serde_json::to_string(self)?;
34 log::info!("Sending: {}", s);
35 device.console_write(s.as_bytes())?;
36 Ok(())
37 }
38
39 fn send_with_crc(&self, device: &T) -> Result<()> {
40 let s = serde_json::to_string(self)?;
41 log::info!("Sending: {}", s);
42 device.console_write(s.as_bytes())?;
43 let actual_crc = OttfCrc {
44 crc: Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(s.as_bytes()),
45 };
46 actual_crc.send(device)
47 }
48}
49
50pub trait ConsoleRecv<T>
51where
52 T: ConsoleDevice + ?Sized,
53{
54 fn recv(device: &T, timeout: Duration, quiet: bool) -> Result<Self>
55 where
56 Self: Sized;
57}
58
59impl<T, U> ConsoleRecv<T> for U
60where
61 T: ConsoleDevice + ?Sized,
62 U: DeserializeOwned,
63{
64 fn recv(device: &T, timeout: Duration, quiet: bool) -> Result<Self>
65 where
66 Self: Sized,
67 {
68 let mut console = UartConsole {
69 timeout: Some(timeout),
70 timestamp: true,
71 newline: true,
72 exit_success: Some(Regex::new(r"RESP_OK:(.*) CRC:([0-9]+)\n")?),
73 exit_failure: Some(Regex::new(r"RESP_ERR:(.*) CRC:([0-9]+)\n")?),
74 ..Default::default()
75 };
76 let mut stdout = std::io::stdout();
77 let out = if !quiet {
78 let w: &mut dyn Write = &mut stdout;
79 Some(w)
80 } else {
81 None
82 };
83 let result = console.interact(device, None, out)?;
84 println!();
85 match result {
86 ExitStatus::ExitSuccess => {
87 let cap = console
88 .captures(ExitStatus::ExitSuccess)
89 .expect("RESP_OK capture");
90 let json_str = cap.get(1).expect("RESP_OK group").as_str();
91 let crc_str = cap.get(2).expect("CRC group").as_str();
92 check_crc(json_str, crc_str)?;
93 Ok(serde_json::from_str::<Self>(json_str)?)
94 }
95 ExitStatus::ExitFailure => {
96 let cap = console
97 .captures(ExitStatus::ExitFailure)
98 .expect("RESP_ERR capture");
99 let json_str = cap.get(1).expect("RESP_OK group").as_str();
100 let crc_str = cap.get(2).expect("CRC group").as_str();
101 check_crc(json_str, crc_str)?;
102 let err = serde_json::from_str::<Status>(json_str)?;
103 Err(err.into())
104 }
105 ExitStatus::Timeout => Err(ConsoleError::GenericError("Timed Out".into()).into()),
106 _ => Err(anyhow!("Impossible result: {:?}", result)),
107 }
108 }
109}
110
111fn check_crc(json_str: &str, crc_str: &str) -> Result<()> {
112 let crc = crc_str.parse::<u32>()?;
113 let actual_crc = Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(json_str.as_bytes());
114 if crc != actual_crc {
115 return Err(
116 ConsoleError::GenericError("CRC didn't match received json body.".into()).into(),
117 );
118 }
119 Ok(())
120}