Support `/dev/ttyS0` and `console=ttyS0`

This commit is contained in:
Ruihan Li 2025-12-22 23:04:58 +08:00 committed by Tate, Hongliang Tian
parent 000ad53c9f
commit 3c959bf0a0
2 changed files with 80 additions and 1 deletions

View File

@ -16,7 +16,7 @@ use crate::{
registry::char,
tty::{
Tty,
n_tty::{VtDriver, hvc0_device},
n_tty::{VtDriver, hvc0_device, serial0_device},
},
},
fs::{
@ -114,6 +114,7 @@ impl SystemConsole {
let device = match console_name {
"tty0" => Some(Arc::new(Tty0Device) as _),
"ttyS0" => serial0_device().cloned().map(|device| device as _),
"hvc0" => hvc0_device().cloned().map(|device| device as _),
_ => None,
};

View File

@ -63,6 +63,50 @@ impl TtyDriver for VtDriver {
fn on_termios_change(&self, _old_termios: &CTermios, _new_termios: &CTermios) {}
}
/// The driver for serial devices.
#[derive(Clone)]
pub struct SerialDriver {
console: Arc<dyn AnyConsoleDevice>,
}
impl SerialDriver {
const MINOR_ID_BASE: u32 = 64;
}
impl TtyDriver for SerialDriver {
// Reference: <https://elixir.bootlin.com/linux/v6.17/source/include/uapi/linux/major.h#L18>.
const DEVICE_MAJOR_ID: u32 = 4;
fn devtmpfs_path(&self, index: u32) -> Option<String> {
Some(format!("ttyS{}", index - Self::MINOR_ID_BASE))
}
fn open(tty: Arc<Tty<Self>>) -> Result<Box<dyn FileIo>> {
Ok(Box::new(TtyFile(tty)))
}
fn push_output(&self, chs: &[u8]) -> Result<usize> {
self.console.send(chs);
Ok(chs.len())
}
fn echo_callback(&self) -> impl FnMut(&[u8]) + '_ {
|chs| self.console.send(chs)
}
fn can_push(&self) -> bool {
true
}
fn notify_input(&self) {}
fn console(&self) -> Option<&dyn AnyConsoleDevice> {
Some(&*self.console)
}
fn on_termios_change(&self, _old_termios: &CTermios, _new_termios: &CTermios) {}
}
/// The driver for hypervisor console devices.
#[derive(Clone)]
pub struct HvcDriver {
@ -145,6 +189,8 @@ impl<D: TtyDriver> FileIo for TtyFile<D> {
static TTY1: Once<Arc<Tty<VtDriver>>> = Once::new();
static SERIAL0: Once<Arc<Tty<SerialDriver>>> = Once::new();
static HVC0: Once<Arc<Tty<HvcDriver>>> = Once::new();
/// Returns the `tty1` device.
@ -156,6 +202,13 @@ pub fn tty1_device() -> &'static Arc<Tty<VtDriver>> {
TTY1.get().unwrap()
}
/// Returns the `ttyS0` device.
///
/// Returns `None` if the device is not found nor initialized.
pub fn serial0_device() -> Option<&'static Arc<Tty<SerialDriver>>> {
SERIAL0.get()
}
/// Returns the `hvc0` device.
///
/// Returns `None` if the device is not found nor initialized.
@ -190,6 +243,31 @@ pub(super) fn init_in_first_process() -> Result<()> {
},
)));
// Initialize the `ttyS0` device if the serial console is available.
let serial_console = devices
.iter()
.find(|(name, _)| name.as_str() == aster_uart::CONSOLE_NAME)
.map(|(_, device)| device.clone());
if let Some(serial_console) = serial_console {
let driver = SerialDriver {
console: serial_console.clone(),
};
let serial0 = Tty::new(SerialDriver::MINOR_ID_BASE, driver);
SERIAL0.call_once(|| serial0.clone());
char::register(serial0.clone())?;
serial_console.register_callback(Box::leak(Box::new(
move |mut reader: VmReader<Infallible>| {
let mut chs = vec![0u8; reader.remain()];
reader.read(&mut VmWriter::from(chs.as_mut_slice()));
let _ = serial0.push_input(chs.as_slice());
},
)));
}
// Initialize the `hvc0` device if the virtio console is available.
let virtio_console = devices