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<()>;
23    fn send_with_crc(&self, device: &T) -> Result<()>;
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<()> {
32        let s = serde_json::to_string(self)?;
33        log::info!("Sending: {}", s);
34        device.write(s.as_bytes())?;
35        Ok(())
36    }
37
38    fn send_with_crc(&self, device: &T) -> Result<()> {
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        actual_crc.send(device)
46    }
47}
48
49pub trait ConsoleRecv<T>
50where
51    T: ConsoleDevice + ?Sized,
52{
53    fn recv(device: &T, timeout: Duration, quiet: bool) -> Result<Self>
54    where
55        Self: Sized;
56}
57
58impl<T, U> ConsoleRecv<T> for U
59where
60    T: ConsoleDevice + ?Sized,
61    U: DeserializeOwned,
62{
63    fn recv(device: &T, timeout: Duration, quiet: bool) -> Result<Self>
64    where
65        Self: Sized,
66    {
67        let device: &dyn ConsoleDevice = if quiet { &device } else { &device.logged() };
68        let result = device.wait_for_line(
69            PassFail(
70                regex!(r"RESP_OK:(.*) CRC:([0-9]+)"),
71                regex!(r"RESP_ERR:(.*) CRC:([0-9]+)"),
72            ),
73            timeout,
74        )?;
75        println!();
76        match result {
77            PassFailResult::Pass(cap) => {
78                let json_str = &cap[1];
79                let crc_str = &cap[2];
80                check_crc(json_str, crc_str)?;
81                Ok(serde_json::from_str::<Self>(json_str)?)
82            }
83            PassFailResult::Fail(cap) => {
84                let json_str = &cap[1];
85                let crc_str = &cap[2];
86                check_crc(json_str, crc_str)?;
87                Err(serde_json::from_str::<Status>(json_str)?)?
88            }
89        }
90    }
91}
92
93fn check_crc(json_str: &str, crc_str: &str) -> Result<()> {
94    let crc = crc_str.parse::<u32>()?;
95    let actual_crc = Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(json_str.as_bytes());
96    if crc != actual_crc {
97        return Err(
98            ConsoleError::GenericError("CRC didn't match received json body.".into()).into(),
99        );
100    }
101    Ok(())
102}