Skip to content

Commit

Permalink
[opentitantool] Add ability to access underlying serial device
Browse files Browse the repository at this point in the history
Some transports/debuggers expose the serial ports in a way that can be
accessed by other programs, without necessarily going through
opentitanlib.

This CL introduces a new `uart` top level command and `os-device` under
it, (in hindsigt, `console` and `rescue` probably beloned under the same
top-level, in order to share some flag processing.)

The idea is that `opentitantool` can be queried, using a set of
configuration file with aliases and command line usb serial number, and
will print the path of the underlying OS device corresponding to the
given port of the given debugger:

```
opentitantool --conf ... --usb-serial ... uart --uart=EC os-device
{
  path: "/dev/ttyUSB6"
}
```

This can be used in cases where processing of output from the OpenTitan
chip takes place in external scripts or programs.  Such cases currently
will have to use an `opentitantool console` process to pass data between
the serial device and its stdin/stdout pipes to the actual script doing
the actual processing, which is wasteful.

Change-Id: Id4dfff38fbc74ec65366d3a7e49a6844ae52c8ee
Signed-off-by: Jes B. Klinke <[email protected]>
  • Loading branch information
jesultra committed Dec 3, 2024
1 parent 2b289b2 commit 5e1d408
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 0 deletions.
6 changes: 6 additions & 0 deletions sw/host/opentitanlib/src/io/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ pub trait Uart {
Ok(())
}

/// Returns `"/dev/ttyUSBn"` or similar OS device path usable by external programs for
/// directly accessing the serial port.
fn get_device_path(&self) -> Result<String> {
Err(TransportError::UnsupportedOperation.into())
}

/// Reads UART receive data into `buf`, returning the number of bytes read.
/// This function _may_ block.
fn read(&self, buf: &mut [u8]) -> Result<usize>;
Expand Down
4 changes: 4 additions & 0 deletions sw/host/opentitanlib/src/proxy/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ impl<'a> TransportCommandHandler<'a> {
instance.set_parity(*parity)?;
Ok(Response::Uart(UartResponse::SetParity))
}
UartRequest::GetDevicePath => {
let path = instance.get_device_path()?;
Ok(Response::Uart(UartResponse::GetDevicePath { path }))
}
UartRequest::Read {
timeout_millis,
len,
Expand Down
2 changes: 2 additions & 0 deletions sw/host/opentitanlib/src/proxy/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ pub enum UartRequest {
rate: u32,
},
SetParity(Parity),
GetDevicePath,
Read {
timeout_millis: Option<u32>,
len: u32,
Expand All @@ -199,6 +200,7 @@ pub enum UartResponse {
GetBaudrate { rate: u32 },
SetBaudrate,
SetParity,
GetDevicePath { path: String },
Read { data: Vec<u8> },
Write,
SupportsNonblockingRead { has_support: bool },
Expand Down
7 changes: 7 additions & 0 deletions sw/host/opentitanlib/src/transport/common/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::util;

/// Implementation of the `Uart` trait on top of a serial device, such as `/dev/ttyUSB0`.
pub struct SerialPortUart {
port_name: String,
flow_control: Cell<FlowControl>,
port: RefCell<TTYPort>,
rxbuf: RefCell<VecDeque<u8>>,
Expand All @@ -38,6 +39,7 @@ impl SerialPortUart {
.map_err(|e| UartError::OpenError(e.to_string()))?;
flock_serial(&port, port_name)?;
Ok(SerialPortUart {
port_name: port_name.to_string(),
flow_control: Cell::new(FlowControl::None),
port: RefCell::new(port),
rxbuf: RefCell::default(),
Expand All @@ -51,6 +53,7 @@ impl SerialPortUart {
.map_err(|e| UartError::OpenError(e.to_string()))?;
flock_serial(&port, port_name)?;
Ok(SerialPortUart {
port_name: port_name.to_string(),
flow_control: Cell::new(FlowControl::None),
port: RefCell::new(port),
rxbuf: RefCell::default(),
Expand Down Expand Up @@ -136,6 +139,10 @@ impl Uart for SerialPortUart {
Ok(())
}

fn get_device_path(&self) -> Result<String> {
Ok(self.port_name.clone())
}

/// Reads UART receive data into `buf`, returning the number of bytes read.
/// The `timeout` may be used to specify a duration to wait for data.
fn read_timeout(&self, buf: &mut [u8], timeout: Duration) -> Result<usize> {
Expand Down
4 changes: 4 additions & 0 deletions sw/host/opentitanlib/src/transport/hyperdebug/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ impl Uart for HyperdebugUart {
self.serial_port.set_flow_control(flow_control)
}

fn get_device_path(&self) -> Result<String> {
self.serial_port.get_device_path()
}

fn read(&self, buf: &mut [u8]) -> Result<usize> {
self.serial_port.read(buf)
}
Expand Down
7 changes: 7 additions & 0 deletions sw/host/opentitanlib/src/transport/proxy/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ impl Uart for ProxyUart {
}
}

fn get_device_path(&self) -> Result<String> {
match self.execute_command(UartRequest::GetDevicePath)? {
UartResponse::GetDevicePath { path } => Ok(path),
_ => bail!(ProxyError::UnexpectedReply()),
}
}

/// Reads UART receive data into `buf`, returning the number of bytes read.
/// This function _may_ block.
fn read(&self, buf: &mut [u8]) -> Result<usize> {
Expand Down
1 change: 1 addition & 0 deletions sw/host/opentitantool/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ rust_binary(
"src/command/status_cmd.rs",
"src/command/tpm.rs",
"src/command/transport.rs",
"src/command/uart.rs",
"src/command/update_usr_access.rs",
"src/command/version.rs",
"src/command/xmodem.rs",
Expand Down
1 change: 1 addition & 0 deletions sw/host/opentitantool/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub mod spx;
pub mod status_cmd;
pub mod tpm;
pub mod transport;
pub mod uart;
pub mod update_usr_access;
pub mod version;
pub mod xmodem;
Expand Down
63 changes: 63 additions & 0 deletions sw/host/opentitantool/src/command/uart.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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 clap::{Args, Subcommand};
use serde_annotate::Annotate;
use std::any::Any;

use opentitanlib::app::command::CommandDispatch;
use opentitanlib::app::TransportWrapper;
use opentitanlib::io::uart::UartParams;

/// Outputs `"/dev/ttyUSBn"` or similar OS device path usable by external programs for directly
/// accessing the given serial port of the debugger.
#[derive(Debug, Args)]
pub struct UartOsDevice {}

#[derive(Debug, serde::Serialize)]
pub struct UartOsDeviceResponse {
path: String,
}

impl CommandDispatch for UartOsDevice {
fn run(
&self,
context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
let context = context.downcast_ref::<UartCommand>().unwrap();
let uart = context.params.create(transport)?;
Ok(Some(Box::new(UartOsDeviceResponse {
path: uart.get_device_path()?,
})))
}
}

/// Commands for interacting with a UART.
#[derive(Debug, Subcommand, CommandDispatch)]
pub enum InternalUartCommand {
OsDevice(UartOsDevice),
}

#[derive(Debug, Args)]
pub struct UartCommand {
#[command(flatten)]
params: UartParams,

#[command(subcommand)]
command: InternalUartCommand,
}

impl CommandDispatch for UartCommand {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
// None of the UART commands care about the prior context, but they do
// care about the `UartParams` parameter in the current node.
self.command.run(self, transport)
}
}
1 change: 1 addition & 0 deletions sw/host/opentitantool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ enum RootCommandHierarchy {
Spx(command::spx::Spx),
#[command(subcommand)]
Transport(command::transport::TransportCommand),
Uart(command::uart::UartCommand),
Version(command::version::Version),
#[command(subcommand)]
Status(command::status_cmd::StatusCommand),
Expand Down

0 comments on commit 5e1d408

Please sign in to comment.