From 76a86e196977e5d2e2331d9b87096a74f77bc0af Mon Sep 17 00:00:00 2001 From: Wildan M Date: Fri, 12 Dec 2025 00:13:16 +0700 Subject: [PATCH] Move redoxerd and add aarch64 support --- Cargo.lock | 19 ++++ Cargo.toml | 2 + drivers/redoxerd/Cargo.toml | 14 +++ drivers/redoxerd/src/main.rs | 185 +++++++++++++++++++++++++++++++++++ drivers/redoxerd/src/sys.rs | 50 ++++++++++ 5 files changed, 270 insertions(+) create mode 100644 drivers/redoxerd/Cargo.toml create mode 100644 drivers/redoxerd/src/main.rs create mode 100644 drivers/redoxerd/src/sys.rs diff --git a/Cargo.lock b/Cargo.lock index b850e044a8..99235e2c24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1423,6 +1423,12 @@ dependencies = [ "redox_termios", ] +[[package]] +name = "qemu-exit" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb0fd6580eeed0103c054e3fba2c2618ff476943762f28a645b63b8692b21c9" + [[package]] name = "quote" version = "1.0.42" @@ -1724,6 +1730,19 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" +[[package]] +name = "redoxerd" +version = "0.1.0" +dependencies = [ + "anyhow", + "common", + "libredox", + "qemu-exit", + "redox_event", + "redox_syscall", + "redox_termios", +] + [[package]] name = "regex" version = "1.12.2" diff --git a/Cargo.toml b/Cargo.toml index ea2285e7c2..99e9c039ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,8 @@ members = [ "drivers/net/rtl8168d", "drivers/net/virtio-netd", + "drivers/redoxerd", + "drivers/storage/ahcid", "drivers/storage/bcm2835-sdhcid", "drivers/storage/driver-block", diff --git a/drivers/redoxerd/Cargo.toml b/drivers/redoxerd/Cargo.toml new file mode 100644 index 0000000000..878625912c --- /dev/null +++ b/drivers/redoxerd/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "redoxerd" +version = "0.1.0" +authors = ["Jeremy Soller "] +edition = "2018" + +[dependencies] +anyhow = "1.0" +libredox = "0.1" +redox_event = "0.4.1" +redox_syscall = "0.5" +redox_termios = "0.1" +common = { path = "../common" } +qemu-exit = "3.0.2" diff --git a/drivers/redoxerd/src/main.rs b/drivers/redoxerd/src/main.rs new file mode 100644 index 0000000000..ee39cbe4e7 --- /dev/null +++ b/drivers/redoxerd/src/main.rs @@ -0,0 +1,185 @@ +use anyhow::Context; +use std::fs::{self, OpenOptions}; +use std::io; +use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd}; +use std::process::{Child, Command, ExitStatus, Stdio}; + +mod sys; + +const DEFAULT_COLS: u32 = 80; +const DEFAULT_LINES: u32 = 30; + +pub fn syscall_error(error: syscall::Error) -> io::Error { + io::Error::from_raw_os_error(error.errno) +} + +event::user_data! { + enum EventData { + Pty, + Timer, + } +} + +fn handle( + event_queue: event::EventQueue, + master_fd: RawFd, + timeout_fd: RawFd, + process: &mut Child, +) -> io::Result { + let handle_event = |event: EventData| -> io::Result { + match event { + EventData::Pty => { + let mut packet = [0; 4096]; + loop { + // Read data from PTY master + let count = match syscall::read(master_fd as usize, &mut packet) { + Ok(0) => return Ok(false), + Ok(count) => count, + Err(ref err) if err.errno == syscall::EAGAIN => return Ok(true), + Err(err) => return Err(syscall_error(err)), + }; + + // Write data to stdout + syscall::write(1, &packet[1..count]).map_err(syscall_error)?; + + for i in 1..count { + // Write byte to QEMU debugcon (Bochs compatible) + sys::debug_char(packet[i]); + } + } + } + EventData::Timer => { + let mut timespec = syscall::TimeSpec::default(); + syscall::read(timeout_fd as usize, &mut timespec).map_err(syscall_error)?; + + timespec.tv_sec += 1; + syscall::write(timeout_fd as usize, &mut timespec).map_err(syscall_error)?; + + Ok(true) + } + } + }; + + if handle_event(EventData::Pty)? && handle_event(EventData::Timer)? { + 'events: loop { + match process.try_wait() { + Ok(status_opt) => match status_opt { + Some(status) => return Ok(status), + None => (), + }, + Err(err) => match err.kind() { + io::ErrorKind::WouldBlock => (), + _ => return Err(err), + }, + } + + let event = event_queue.next_event()?; + if !handle_event(event.user_data)? { + break 'events; + } + } + } + + let _ = process.kill(); + process.wait() +} + +fn getpty(columns: u32, lines: u32) -> io::Result<(RawFd, String)> { + let master = syscall::open( + "/scheme/pty", + syscall::O_CLOEXEC | syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, + ) + .map_err(syscall_error)?; + + if let Ok(winsize_fd) = syscall::dup(master, b"winsize") { + let _ = syscall::write( + winsize_fd, + &redox_termios::Winsize { + ws_row: lines as u16, + ws_col: columns as u16, + }, + ); + let _ = syscall::close(winsize_fd); + } + + let mut buf: [u8; 4096] = [0; 4096]; + let count = syscall::fpath(master, &mut buf).map_err(syscall_error)?; + Ok((master as RawFd, unsafe { + String::from_utf8_unchecked(Vec::from(&buf[..count])) + })) +} + +fn inner() -> anyhow::Result<()> { + common::acquire_port_io_rights()?; + + let config = fs::read_to_string("/etc/redoxerd").context("Failed to read /etc/redoxerd")?; + let mut config_lines = config.lines(); + + let (columns, lines) = (DEFAULT_COLS, DEFAULT_LINES); + let (master_fd, pty) = getpty(columns, lines)?; + + let timeout_fd = syscall::open( + "/scheme/time/4", + syscall::O_CLOEXEC | syscall::O_RDWR | syscall::O_NONBLOCK, + ) + .map_err(syscall_error)? as RawFd; + + let event_queue = event::EventQueue::new()?; + event_queue.subscribe(master_fd as usize, EventData::Pty, event::EventFlags::READ)?; + event_queue.subscribe( + timeout_fd as usize, + EventData::Timer, + event::EventFlags::READ, + )?; + + let slave_stdin = OpenOptions::new().read(true).open(&pty)?; + let slave_stdout = OpenOptions::new().write(true).open(&pty)?; + let slave_stderr = OpenOptions::new().write(true).open(&pty)?; + + let Some(name) = config_lines.next() else { + anyhow::bail!("/etc/redoxerd does not specify command"); + }; + let mut command = Command::new(name); + for arg in config_lines { + command.arg(arg); + } + unsafe { + command + .stdin(Stdio::from_raw_fd(slave_stdin.into_raw_fd())) + .stdout(Stdio::from_raw_fd(slave_stdout.into_raw_fd())) + .stderr(Stdio::from_raw_fd(slave_stderr.into_raw_fd())) + .env("COLUMNS", format!("{}", columns)) + .env("LINES", format!("{}", lines)) + .env("TERM", "xterm-256color") + .env("TTY", &pty); + } + + let mut process = command + .spawn() + .with_context(|| format!("Failed to spawn {command:?}"))?; + let status = handle(event_queue, master_fd, timeout_fd, &mut process) + .with_context(|| format!("Failed to run {name}"))?; + if status.success() { + Ok(()) + } else { + anyhow::bail!("{name} failed with {}", status); + } +} + +fn main() { + match inner() { + Ok(()) => { + // Exit with success using qemu device + sys::exit_success(); + } + Err(err) => { + eprintln!("redoxerd: {:#}", err); + + // Wait a bit for the error message to get flushed through the tty subsystem before exiting. + std::thread::sleep(std::time::Duration::from_millis(100)); + + // Exit with error using qemu device + sys::exit_failure(); + } + } +} diff --git a/drivers/redoxerd/src/sys.rs b/drivers/redoxerd/src/sys.rs new file mode 100644 index 0000000000..96ed275614 --- /dev/null +++ b/drivers/redoxerd/src/sys.rs @@ -0,0 +1,50 @@ +pub fn exit_success() { + imp::exit(true); +} + +pub fn exit_failure() { + imp::exit(false); +} + +pub fn debug_char(b: u8) { + let _ = imp::write_debug(b); +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod imp { + use syscall::Io; + use syscall::Pio; + + pub fn exit(success: bool) { + if success { + Pio::::new(0x604).write(0x2000); + Pio::::new(0x501).write(51 / 2); + } else { + Pio::::new(0x501).write(53 / 2); + } + } + + pub fn write_debug(b: u8) -> syscall::Result<()> { + Pio::::new(0xe9).write(b); + Ok(()) + } +} + +#[cfg(target_arch = "aarch64")] +mod imp { + use qemu_exit::QEMUExit; + + pub fn exit(success: bool) { + let q = qemu_exit::AArch64::new(); + if success { + q.exit(51) + } else { + q.exit(53) + } + } + + pub fn write_debug(b: u8) -> syscall::Result<()> { + // TODO + Ok(()) + } +}