Skip to content

Commit

Permalink
tools: auto-attach crowtty when running x86 kernel (#339)
Browse files Browse the repository at this point in the history
This commit adds code in the x86 bootimage runner to automatically
attach crowtty to the QEMU virtual serial port when running in QEMU
(unless asked politely not to). Doing this required turning `crowtty`
into a library, so it could be used as a dependency of
`x86_64-bootimager`. 

Since we don't have a working UART driver in the x86 kernel yet (see
#222), the serial port currently only gets us output from the
bootloader, and we can't currently speak SerMux with the running mnemOS
VM. But, I'm gonna go add a serial driver next hopefully.

```console
eliza@theseus ~/Code/mnemos $ just run-x86 --crowtty-verbose
cargo run --package mnemos-x86_64 --target=x86_64-unknown-none --features=bootloader_api -- run --crowtty-verbose
   Compiling regex-automata v0.4.7
   Compiling regex v1.10.6
   Compiling vergen v8.3.2
   Compiling mnemos v0.1.0 (/home/eliza/Code/mnemos/source/kernel)
   Compiling mnemos-x86_64 v0.1.0 (/home/eliza/Code/mnemos/platforms/x86_64/core)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.75s
     Running `cargo run --package mnemos-x86_64-bootimager -- --kernel-bin target/x86_64-unknown-none/debug/bootloader run --crowtty-verbose`
   Compiling cfg-if v1.0.0
   Compiling memchr v2.7.4
   Compiling itoa v1.0.11
   Compiling ryu v1.0.18
   Compiling polling v3.7.3
   Compiling polling v2.8.0
   Compiling getrandom v0.2.15
   Compiling nix v0.23.2
   Compiling nix v0.26.4
   Compiling thread_local v1.1.8
   Compiling tempfile v3.12.0
   Compiling uuid v1.10.0
   Compiling tracing-subscriber v0.3.18
   Compiling async-io v1.13.0
   Compiling async-io v2.3.3
   Compiling gpt v3.1.0
   Compiling object v0.32.2
   Compiling serde_json v1.0.122
   Compiling async-signal v0.2.9
   Compiling async-process v1.8.1
   Compiling serialport v4.5.0
   Compiling bootloader v0.11.7
   Compiling serialport v4.0.1 (https://github.com/metta-systems/serialport-rs?rev=7fec572529ec35b82bd4e3636d897fe2f1c2233f#7fec5725)
   Compiling backtrace v0.3.71
   Compiling backtrace-ext v0.2.1
   Compiling miette v7.2.0
   Compiling crowtty v0.1.0 (/home/eliza/Code/mnemos/tools/crowtty)
   Compiling mnemos-x86_64-bootimager v0.1.0 (/home/eliza/Code/mnemos/tools/x86_64-bootimager)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 11.18s
     Running `target/debug/mnemos-x86_64-bootimager --kernel-bin target/x86_64-unknown-none/debug/bootloader run --crowtty-verbose`
    Assuming direct control over the build!
    Building boot image, boot_mode: UEFI, kernel: target/x86_64-unknown-none/debug/bootloader
    Finished bootable disk image [UEFI] in 118.28ms (/home/eliza/Code/mnemos/target/x86_64-unknown-none/debug/mnemos-x86_64-uefi.img)
     Booting mnemOS VM, qemu: qemu-system-x86_64, args: []
  Connecting crowtty...
[  +0004.098695519s] UART BOOT BdsDxe: failed to load Boot0001 "UEFI QEMU DVD-ROM QM00003 " from PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Master,0x0): Not Found
[  +0004.205543166s] UART BOOT BdsDxe: loading Boot0002 "UEFI QEMU HARDDISK QM00001 " from PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0
[  +0004.209001023s] UART BOOT BdsDxe: starting Boot0002 "UEFI QEMU HARDDISK QM00001 " from PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0
[  +0005.841023599s] UART BOOT INFO : Framebuffer info: FrameBufferInfo { byte_len: 4096000, width: 1280, height: 800, pixel_format: Bgr, bytes_per_pixel: 4, stride: 1280
[  +0005.841309139s] UART BOOT INFO : UEFI bootloader started
[  +0005.841663228s] UART BOOT INFO : Using framebuffer at 0x80000000
[  +0005.842068224s] UART BOOT INFO : Reading configuration from disk was successful
[  +0005.842356148s] UART BOOT INFO : Trying to load ramdisk via Disk
[  +0005.842728632s] UART BOOT INFO : Ramdisk not found
[  +0005.958230026s] UART BOOT INFO : New page table at: PhysFrame[4KiB](0x2000
[  +0005.959142731s] UART BOOT INFO : Elf file loaded at 0x0000000004f98000
[  +0005.960043464s] UART BOOT INFO : virtual_address_offset: 0xffff800000000000
[  +0005.961674828s] UART BOOT INFO : Handling Segment: Ph64(ProgramHeader64 { type_: Ok(Load), flags: Flags(4), offset: 0, virtual_addr: 0, physical_addr: 0, file_size: 3f524, mem_size: 3f524, align: 1000
[  +0005.963159093s] UART BOOT INFO : Handling Segment: Ph64(ProgramHeader64 { type_: Ok(Load), flags: Flags(5), offset: 3f530, virtual_addr: 40530, physical_addr: 40530, file_size: bbab7, mem_size: bbab7, align: 1000
[  +0005.966445476s] UART BOOT INFO : Handling Segment: Ph64(ProgramHeader64 { type_: Ok(Load), flags: Flags(6), offset: fafe8, virtual_addr: fcfe8, physical_addr: fcfe8, file_size: 179d8, mem_size: 18018, align: 1000
[  +0005.966656455s] UART BOOT INFO : Mapping bss section
[  +0005.968165568s] UART BOOT INFO : Handling Segment: Ph64(ProgramHeader64 { type_: Ok(Load), flags: Flags(6), offset: 1129c0, virtual_addr: 1159c0, physical_addr: 1159c0, file_size: 2b40, mem_size: be70, align: 1000
[  +0005.968348984s] UART BOOT INFO : Mapping bss section
[  +0005.976238970s] UART BOOT INFO : Entry point at: 0xffff800000041740
[  +0005.976821020s] UART BOOT INFO : Creating GDT at PhysAddr(0x45000
[  +0005.977080721s] UART BOOT INFO : Map framebuffer
[  +0005.977782628s] UART BOOT INFO : Map physical memory
[  +0005.978220726s] UART BOOT INFO : Map page table recursively
[  +0005.978546963s] UART BOOT INFO : Allocate bootinfo
[  +0005.978853522s] UART BOOT INFO : Create Memory Map
[  +0005.979163138s] UART BOOT INFO : Create bootinfo
[  +0005.979764544s] UART BOOT INFO : Jumping to kernel entry point at VirtAddr(0xffff800000041740
[2 +0005.979812395s] UART KEYB pseudo-keyboard (SerMux port :2) reading from STDIN
asdf
[2 +0011.139810921s] UART KEYB 5B <- "asdf\n"

```
  • Loading branch information
hawkw authored Oct 6, 2024
1 parent ff7610c commit 026d0b8
Show file tree
Hide file tree
Showing 12 changed files with 859 additions and 518 deletions.
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

0 comments on commit 026d0b8

Please sign in to comment.