opentitanlib/test_utils/
rpc.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4use 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
16// Bring in the auto-generated sources.
17include!(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}