Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tools: auto-attach crowtty when running x86 kernel #339

Merged
merged 19 commits into from
Oct 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ repository = "https://github.com/tosc-rs/mnemos"
homepage = "https://mnemos.dev"
license = "MIT OR Apache-2.0"

[workspace.dependencies]
miette = "7.2"

### profile settings ###

[profile.release]
Expand Down
10 changes: 7 additions & 3 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,18 @@ flash-c3 board *espflash-args: (_get-cargo-command "espflash" "cargo-espflash")
{{ espflash-args }}

# build a bootable x86_64 disk image, using rust-osdev/bootloader.
build-x86 *args='': (run-x86 "build " + args)
build-x86 *args='': (_x86-bootimager "build" args)

# run an x86_64 MnemOS image in QEMU
run-x86 *args='':
run-x86 *args='': (_x86-bootimager "run" args)

# helper recipe to invoke the x86 bootimage builder, used by both build-x86 and
# run-x86.
_x86-bootimager cmd *args='':
{{ _cargo }} run --package {{ _x86_pkg }} \
--target=x86_64-unknown-none \
--features=bootloader_api \
-- {{ args }}
-- {{cmd}} {{ args }}

# run crowtty (a host serial multiplexer, log viewer, and pseudo-keyboard)
crowtty *FLAGS:
Expand Down
3 changes: 3 additions & 0 deletions tools/crowtty/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,6 @@ default-features = false
version = "0.3.17"
default-features = false
features = ["std"]

[dependencies.miette]
workspace = true
140 changes: 140 additions & 0 deletions tools/crowtty/src/connection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use std::{
fmt,
io::{self, Read, Write},
net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream},
path::PathBuf,
time::Duration,
};

/// Unfortunately, the `serialport` crate seems to have some issues on M-series Macs.
///
/// For these hosts, we use a patched version of the crate that has some hacky
/// fixes applied that seem to resolve the issue.
///
/// Context: <https://github.com/serialport/serialport-rs/issues/49>
mod serial {
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
pub use serialport_macos_hack::*;

#[cfg(not(all(target_arch = "aarch64", target_os = "macos")))]
pub use serialport_regular::*;
}

use serial::SerialPort;

/// An active connection to a SerMux target.
#[derive(Debug)]
pub enum Connection {
Serial(Box<dyn SerialPort>),
Tcp(TcpStream),
}

/// Describes a SerMux target to connect to.
#[derive(Debug, clap::Subcommand)]
pub enum Connect {
/// open listener on IP:PORT
Tcp {
/// IP address to connect to. This defaults to localhost.
#[clap(long, default_value_t = Self::DEFAULT_IP)]
ip: IpAddr,
/// TCP port to connect to (usually 9999 for melpomene)
#[arg(default_value_t = 9999)]
port: u16,
},
/// open listener on PATH
Serial {
/// path to the serial port device (usually /dev/ttyUSBx for hw)
path: PathBuf,

/// baud rate (usually 115200 for hw)
#[arg(default_value_t = Self::DEFAULT_BAUD_RATE)]
baud: u32,
},
}

impl Write for Connection {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
match self {
Self::Serial(s) => s.write(buf),
Self::Tcp(t) => t.write(buf),
}
}

fn flush(&mut self) -> std::io::Result<()> {
match self {
Self::Serial(s) => s.flush(),
Self::Tcp(t) => t.flush(),
}
}
}

impl Read for Connection {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match self {
Self::Serial(s) => s.read(buf),
Self::Tcp(t) => t.read(buf),
}
}
}

impl Connection {
pub fn log_tag(&self) -> crate::LogTag {
match self {
Self::Serial(_) => crate::LogTag::serial(),
Self::Tcp(_) => crate::LogTag::tcp(),
}
}
}

impl Connect {
pub const DEFAULT_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
pub const DEFAULT_TCP_PORT: u16 = 9999;
pub const DEFAULT_BAUD_RATE: u32 = 115200;
const READ_TIMEOUT: Duration = Duration::from_millis(10);

pub const fn new_tcp(port: u16) -> Self {
Connect::Tcp {
ip: Self::DEFAULT_IP,
port,
}
}

pub const fn default_tcp() -> Self {
Connect::new_tcp(Self::DEFAULT_TCP_PORT)
}

pub fn new_serial(path: impl Into<PathBuf>) -> Self {
Connect::Serial {
path: path.into(),
baud: Self::DEFAULT_BAUD_RATE,
}
}

pub fn connect(&self) -> io::Result<Connection> {
match *self {
Self::Tcp { ip, port } => {
let addr = SocketAddr::from((ip, port));
let sock = TcpStream::connect(addr)?;
sock.set_read_timeout(Some(Self::READ_TIMEOUT))?;
Ok(Connection::Tcp(sock))
}
Self::Serial { ref path, baud } => {
let path = path.to_str().ok_or_else(|| {
// TODO(eliza): should probably just use `Utf8PathBuf` here...
io::Error::new(io::ErrorKind::InvalidInput, "path is not UTF-8")
})?;
let port = serial::new(path, baud).timeout(Self::READ_TIMEOUT).open()?;
Ok(Connection::Serial(port))
}
}
}
}

impl fmt::Display for Connect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Tcp { ip, port } => write!(f, "{ip}:{port}"),
Self::Serial { path, baud } => write!(f, "{} (@ {baud})", path.display()),
}
}
}
Loading
Loading