opentitanlib/transport/
mod.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
4
5use anyhow::Result;
6use bitflags::bitflags;
7use serde::{Deserialize, Serialize};
8use std::any::Any;
9use std::collections::HashMap;
10use std::path::PathBuf;
11use std::rc::Rc;
12
13use crate::bootstrap::BootstrapOptions;
14use crate::io::emu::Emulator;
15use crate::io::gpio::{GpioBitbanging, GpioMonitoring, GpioPin};
16use crate::io::i2c::Bus;
17use crate::io::jtag::{JtagChain, JtagParams};
18use crate::io::spi::Target;
19use crate::io::uart::Uart;
20
21pub mod common;
22pub mod ioexpander;
23
24// Export custom error types
25mod errors;
26pub use errors::{TransportError, TransportInterfaceType};
27
28bitflags! {
29    /// A bitmap of capabilities which may be provided by a transport.
30    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
31    #[serde(transparent)]
32    pub struct Capability: u32 {
33        const NONE = 0x00;
34        const UART = 0x01 << 0;
35        const SPI = 0x01 << 1;
36        const GPIO = 0x01 << 2;
37        const I2C = 0x01 << 3;
38        const PROXY = 0x01 << 4;
39        const EMULATOR = 0x01 << 5;
40        const GPIO_MONITORING = 0x01 << 6; // Logic analyzer functionality
41        const JTAG = 0x01 << 7;
42        const UART_NONBLOCKING = 0x01 << 8;
43        const SPI_DUAL = 0x01 << 9;
44        const SPI_QUAD = 0x01 << 10;
45        const GPIO_BITBANGING = 0x01 << 11;
46    }
47}
48
49/// A struct which represents what features a particular Transport instance supports.
50#[derive(Serialize, Deserialize)]
51pub struct Capabilities {
52    capabilities: Capability,
53}
54
55impl Capabilities {
56    /// Create a new Capabilities object representing a provider of
57    /// capabilities specified by `cap`.
58    pub fn new(cap: Capability) -> Self {
59        Self { capabilities: cap }
60    }
61
62    pub fn add(&self, extra: Capability) -> Self {
63        Self {
64            capabilities: self.capabilities | extra,
65        }
66    }
67
68    /// Request the capabilities specified by `cap`.
69    pub fn request(&self, needed: Capability) -> NeededCapabilities {
70        NeededCapabilities {
71            capabilities: self.capabilities,
72            needed,
73        }
74    }
75}
76
77/// A struct which can check that needed capability requirements are met.
78pub struct NeededCapabilities {
79    capabilities: Capability,
80    needed: Capability,
81}
82
83impl NeededCapabilities {
84    /// Checks that the requested capabilities are provided.
85    pub fn ok(&self) -> Result<()> {
86        if self.capabilities & self.needed != self.needed {
87            Err(TransportError::MissingCapabilities(self.needed, self.capabilities).into())
88        } else {
89            Ok(())
90        }
91    }
92}
93
94/// A transport object is a factory for the low-level interfaces provided
95/// by a given communications backend.
96pub trait Transport {
97    /// Returns a `Capabilities` object to check the capabilities of this
98    /// transport object.
99    fn capabilities(&self) -> Result<Capabilities>;
100
101    /// Resets the transport to power-on condition.  That is, pin/uart/spi configuration reverts
102    /// to default, ongoing operations are cancelled, etc.
103    fn apply_default_configuration(&self) -> Result<()> {
104        Ok(())
105    }
106
107    /// Returns a [`JtagChain`] implementation.
108    fn jtag(&self, _opts: &JtagParams) -> Result<Box<dyn JtagChain + '_>> {
109        Err(TransportError::InvalidInterface(TransportInterfaceType::Jtag).into())
110    }
111    /// Returns a SPI [`Target`] implementation.
112    fn spi(&self, _instance: &str) -> Result<Rc<dyn Target>> {
113        Err(TransportError::InvalidInterface(TransportInterfaceType::Spi).into())
114    }
115    /// Returns a I2C [`Bus`] implementation.
116    fn i2c(&self, _instance: &str) -> Result<Rc<dyn Bus>> {
117        Err(TransportError::InvalidInterface(TransportInterfaceType::I2c).into())
118    }
119    /// Returns a [`Uart`] implementation.
120    fn uart(&self, _instance: &str) -> Result<Rc<dyn Uart>> {
121        Err(TransportError::InvalidInterface(TransportInterfaceType::Uart).into())
122    }
123    /// Returns a [`GpioPin`] implementation.
124    fn gpio_pin(&self, _instance: &str) -> Result<Rc<dyn GpioPin>> {
125        Err(TransportError::InvalidInterface(TransportInterfaceType::Gpio).into())
126    }
127    /// Returns a [`GpioMonitoring`] implementation, for logic analyzer functionality.
128    fn gpio_monitoring(&self) -> Result<Rc<dyn GpioMonitoring>> {
129        Err(TransportError::InvalidInterface(TransportInterfaceType::GpioMonitoring).into())
130    }
131    /// Returns a [`GpioBitbanging`] implementation, for timed and synchronized manipulation of
132    /// multiple GPIO pins.
133    fn gpio_bitbanging(&self) -> Result<Rc<dyn GpioBitbanging>> {
134        Err(TransportError::InvalidInterface(TransportInterfaceType::GpioBitbanging).into())
135    }
136    /// Returns a [`Emulator`] implementation.
137    fn emulator(&self) -> Result<&dyn Emulator> {
138        Err(TransportError::InvalidInterface(TransportInterfaceType::Emulator).into())
139    }
140
141    /// Methods available only on FPGA implementations.
142    fn fpga_ops(&self) -> Result<&dyn FpgaOps> {
143        Err(TransportError::InvalidInterface(TransportInterfaceType::FpgaOps).into())
144    }
145
146    /// Methods available only on Proxy implementation.
147    fn proxy_ops(&self) -> Result<&dyn ProxyOps> {
148        Err(TransportError::InvalidInterface(TransportInterfaceType::ProxyOps).into())
149    }
150
151    /// Invoke non-standard functionality of some Transport implementations.
152    fn dispatch(&self, _action: &dyn Any) -> Result<Option<Box<dyn erased_serde::Serialize>>> {
153        Err(TransportError::UnsupportedOperation.into())
154    }
155
156    /// Invoke the provided callback (preferably) without exclusive access.
157    ///
158    /// By default, ownership of `Transport` would imply exclusive access to the underlying device,
159    /// and optimisation can be made assuming no other process would be simultaneously accessing.
160    /// However for long running commands, such as `opentitantool console`, it may be desirable to
161    /// relinquish exclusive access during such comamnd and only re-take exclusive access later.
162    ///
163    /// Transport that does not support such scenario may ignore such request and perform a no-op.
164    fn relinquish_exclusive_access(&self, callback: Box<dyn FnOnce() + '_>) -> Result<()> {
165        callback();
166        Ok(())
167    }
168}
169
170/// Methods available only on FPGA transports.
171pub trait FpgaOps {
172    fn load_bitstream(&self, bitstream: &[u8], progress: &dyn ProgressIndicator) -> Result<()>;
173
174    fn clear_bitstream(&self) -> Result<()>;
175}
176
177/// Methods available only on the Proxy implementation of the Transport trait.
178pub trait ProxyOps {
179    /// Returns a string->string map containing user-defined aspects "provided" by the testbed
180    /// setup.  For instance, whether a SPI flash chip is fitted in the socket, or whether pullup
181    /// resistors are suitable for high-speed I2C.  Most of the time, this information will not
182    /// come from the actual transport layer, but from the TransportWrapper above it.
183    fn provides_map(&self) -> Result<HashMap<String, String>>;
184
185    fn bootstrap(&self, options: &BootstrapOptions, payload: &[u8]) -> Result<()>;
186    fn apply_pin_strapping(&self, strapping_name: &str) -> Result<()>;
187    fn remove_pin_strapping(&self, strapping_name: &str) -> Result<()>;
188
189    /// Applies the default transport init configuration expect with the specify strap applied.
190    fn apply_default_configuration_with_strap(&self, strapping_name: &str) -> Result<()>;
191}
192
193/// Used by Transport implementations dealing with emulated OpenTitan
194/// chips, allowing e.g. more efficient direct means of programming
195/// emulated flash storage.  (As opposed to running an actual
196/// bootloater on the emulated target, which would receive data via
197/// SPI to be flashed.)
198pub struct Bootstrap {
199    pub image_path: PathBuf,
200}
201
202/// Some transports allow dynamically changing which pins are used for JTAG.
203pub struct SetJtagPins {
204    pub tclk: Option<Rc<dyn GpioPin>>,
205    pub tms: Option<Rc<dyn GpioPin>>,
206    pub tdi: Option<Rc<dyn GpioPin>>,
207    pub tdo: Option<Rc<dyn GpioPin>>,
208    pub trst: Option<Rc<dyn GpioPin>>,
209}
210
211pub trait ProgressIndicator {
212    // Begins a new stage, indicating "size" of this stage in bytes.  `name` can be the empty
213    // string, for instance if the operation has only a single stage.
214    fn new_stage(&self, name: &str, total: usize);
215    // Indicates how far towards the previously declared `total`.  Operation will be shown as
216    // complete, once the parameter value to this method equals `total`.
217    fn progress(&self, absolute: usize);
218}
219
220/// Command for Transport::dispatch().
221pub struct UpdateFirmware<'a> {
222    /// The firmware to load into the HyperDebug device, None means load an "official" newest
223    /// release of the firmware for the particular debugger device, assuming that the `Transport`
224    /// trait implementation knows how to download such.
225    pub firmware: Option<Vec<u8>>,
226    /// A progress function to provide user feedback, see details of the `Progress` struct.
227    pub progress: Box<dyn ProgressIndicator + 'a>,
228    /// Should updating be attempted, even if the current firmware version matches that of the
229    /// image to be updated to.
230    pub force: bool,
231}
232
233/// An `EmptyTransport` provides no communications backend.
234pub struct EmptyTransport;
235
236impl Transport for EmptyTransport {
237    fn capabilities(&self) -> Result<Capabilities> {
238        Ok(Capabilities::new(Capability::NONE))
239    }
240}
241
242#[cfg(test)]
243pub mod tests {
244    use super::*;
245
246    #[test]
247    fn test_capabilities_met() -> anyhow::Result<()> {
248        let cap = Capabilities::new(Capability::UART | Capability::SPI);
249        assert!(cap.request(Capability::UART).ok().is_ok());
250        Ok(())
251    }
252
253    #[test]
254    fn test_capabilities_not_met() -> anyhow::Result<()> {
255        let cap = Capabilities::new(Capability::UART | Capability::SPI);
256        assert!(cap.request(Capability::GPIO).ok().is_err());
257        Ok(())
258    }
259}