diff --git a/sw/host/opentitanlib/src/io/uart.rs b/sw/host/opentitanlib/src/io/uart.rs index b9b830d5a26e2..ba319865ab0d7 100644 --- a/sw/host/opentitanlib/src/io/uart.rs +++ b/sw/host/opentitanlib/src/io/uart.rs @@ -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 { + 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; diff --git a/sw/host/opentitanlib/src/proxy/handler.rs b/sw/host/opentitanlib/src/proxy/handler.rs index 6301b57484c9d..a52b29c338312 100644 --- a/sw/host/opentitanlib/src/proxy/handler.rs +++ b/sw/host/opentitanlib/src/proxy/handler.rs @@ -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, diff --git a/sw/host/opentitanlib/src/proxy/protocol.rs b/sw/host/opentitanlib/src/proxy/protocol.rs index f1f164a9a6175..da47687351f83 100644 --- a/sw/host/opentitanlib/src/proxy/protocol.rs +++ b/sw/host/opentitanlib/src/proxy/protocol.rs @@ -183,6 +183,7 @@ pub enum UartRequest { rate: u32, }, SetParity(Parity), + GetDevicePath, Read { timeout_millis: Option, len: u32, @@ -199,6 +200,7 @@ pub enum UartResponse { GetBaudrate { rate: u32 }, SetBaudrate, SetParity, + GetDevicePath { path: String }, Read { data: Vec }, Write, SupportsNonblockingRead { has_support: bool }, diff --git a/sw/host/opentitanlib/src/transport/common/uart.rs b/sw/host/opentitanlib/src/transport/common/uart.rs index b58e375753fc4..fcd4ba6dd5e6a 100644 --- a/sw/host/opentitanlib/src/transport/common/uart.rs +++ b/sw/host/opentitanlib/src/transport/common/uart.rs @@ -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, port: RefCell, rxbuf: RefCell>, @@ -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(), @@ -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(), @@ -136,6 +139,10 @@ impl Uart for SerialPortUart { Ok(()) } + fn get_device_path(&self) -> Result { + 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 { diff --git a/sw/host/opentitanlib/src/transport/hyperdebug/uart.rs b/sw/host/opentitanlib/src/transport/hyperdebug/uart.rs index 0c2403b596841..647094529bedd 100644 --- a/sw/host/opentitanlib/src/transport/hyperdebug/uart.rs +++ b/sw/host/opentitanlib/src/transport/hyperdebug/uart.rs @@ -93,6 +93,10 @@ impl Uart for HyperdebugUart { self.serial_port.set_flow_control(flow_control) } + fn get_device_path(&self) -> Result { + self.serial_port.get_device_path() + } + fn read(&self, buf: &mut [u8]) -> Result { self.serial_port.read(buf) } diff --git a/sw/host/opentitanlib/src/transport/proxy/uart.rs b/sw/host/opentitanlib/src/transport/proxy/uart.rs index 0cb1448b0a688..8dd2c155957ea 100644 --- a/sw/host/opentitanlib/src/transport/proxy/uart.rs +++ b/sw/host/opentitanlib/src/transport/proxy/uart.rs @@ -87,6 +87,13 @@ impl Uart for ProxyUart { } } + fn get_device_path(&self) -> Result { + 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 { diff --git a/sw/host/opentitantool/BUILD b/sw/host/opentitantool/BUILD index 94a8115b955aa..79aa0e38f4ec1 100644 --- a/sw/host/opentitantool/BUILD +++ b/sw/host/opentitantool/BUILD @@ -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", diff --git a/sw/host/opentitantool/src/command/mod.rs b/sw/host/opentitantool/src/command/mod.rs index 8452c37d96c49..06a594c59cfad 100644 --- a/sw/host/opentitantool/src/command/mod.rs +++ b/sw/host/opentitantool/src/command/mod.rs @@ -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; diff --git a/sw/host/opentitantool/src/command/uart.rs b/sw/host/opentitantool/src/command/uart.rs new file mode 100644 index 0000000000000..05076886d2244 --- /dev/null +++ b/sw/host/opentitantool/src/command/uart.rs @@ -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>> { + let context = context.downcast_ref::().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>> { + // 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) + } +} diff --git a/sw/host/opentitantool/src/main.rs b/sw/host/opentitantool/src/main.rs index 65350f4c3b073..30c5b7452c5e2 100644 --- a/sw/host/opentitantool/src/main.rs +++ b/sw/host/opentitantool/src/main.rs @@ -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),