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