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