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