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;
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
15// Bring in the auto-generated sources.
16include!(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}
25
26impl<T, U> ConsoleSend<T> for U
27where
28    T: ConsoleDevice + ?Sized,
29    U: Serialize,
30{
31    fn send(&self, device: &T) -> Result<String> {
32        let s = serde_json::to_string(self)?;
33        log::info!("Sending: {}", s);
34        device.write(s.as_bytes())?;
35        Ok(s)
36    }
37
38    fn send_with_crc(&self, device: &T) -> Result<String> {
39        let s = serde_json::to_string(self)?;
40        log::info!("Sending: {}", s);
41        device.write(s.as_bytes())?;
42        let actual_crc = OttfCrc {
43            crc: Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(s.as_bytes()),
44        };
45        let crc_s = actual_crc.send(device)?;
46        Ok(s + &crc_s)
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 device: &dyn ConsoleDevice = if quiet { &device } else { &device.logged() };
69        let result = device.wait_for_line(
70            PassFail(
71                regex!(r"RESP_OK:(.*) CRC:([0-9]+)"),
72                regex!(r"RESP_ERR:(.*) CRC:([0-9]+)"),
73            ),
74            timeout,
75        )?;
76        println!();
77        match result {
78            PassFailResult::Pass(cap) => {
79                let json_str = &cap[1];
80                let crc_str = &cap[2];
81                check_crc(json_str, crc_str)?;
82                Ok(serde_json::from_str::<Self>(json_str)?)
83            }
84            PassFailResult::Fail(cap) => {
85                let json_str = &cap[1];
86                let crc_str = &cap[2];
87                check_crc(json_str, crc_str)?;
88                Err(serde_json::from_str::<Status>(json_str)?)?
89            }
90        }
91    }
92}
93
94fn check_crc(json_str: &str, crc_str: &str) -> Result<()> {
95    let crc = crc_str.parse::<u32>()?;
96    let actual_crc = Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(json_str.as_bytes());
97    if crc != actual_crc {
98        return Err(
99            ConsoleError::GenericError("CRC didn't match received json body.".into()).into(),
100        );
101    }
102    Ok(())
103}