blob: cef182440596c743d7db30a610bd90929632d2ee [file] [log] [blame]
// Copyright lowRISC contributors.
// 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::path::PathBuf;
use std::rc::Rc;
use crate::bootstrap::BootstrapOptions;
use crate::io::emu::Emulator;
use crate::io::gpio::{GpioMonitoring, GpioPin};
use crate::io::i2c::Bus;
use crate::io::spi::Target;
use crate::io::uart::Uart;
pub mod common;
pub mod cw310;
pub mod hyperdebug;
pub mod nexus;
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(Serialize, Deserialize)]
pub struct Capability: u32 {
const NONE = 0x00000000;
const UART = 0x00000001;
const SPI = 0x00000002;
const GPIO = 0x00000004;
const I2C = 0x00000008;
const PROXY = 0x00000010;
const EMULATOR = 0x00000020;
const GPIO_MONITORING = 0x00000040; // Logic analyzer functionality
}
}
/// 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 }
}
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 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 [`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())
}
}
/// Methods available only on the Proxy implementation of the Transport trait.
pub trait ProxyOps {
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<()>;
}
/// 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,
}
pub enum Progress {
/// Declares that a stage of the operation is starting, giving the total number of bytes to be
/// processed in this stage. If only a single stage, `name` may be the empty string.
Stage { name: String, total: u32 },
/// Informs about progress within the current state, `pos` is absolute number of bytes so far.
Progress { pos: u32 },
}
/// 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: Option<Box<dyn Fn(Progress) + 'a>>,
}
/// 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(())
}
}