From f966a1239e0d0ff82bc2a09649228c54205ee4e1 Mon Sep 17 00:00:00 2001 From: Julien THILLARD Date: Sun, 22 Mar 2026 16:00:41 +0100 Subject: [PATCH] Log on tty (serial and vga) --- library/std/src/sys/stdio/survos.rs | 29 +++++++++++----------- src/interrupt.rs | 14 ++--------- src/io.rs | 34 ++++++++++++++++---------- src/keymap.rs | 38 ++++++++++++++--------------- src/tty.rs | 29 ++++++++++++++++------ src/vga.rs | 19 ++++++++++++++- src/virtual_console.rs | 20 ++++++++++----- src/virtual_fs.rs | 19 +++++++++++---- user/test_pic/src/main.rs | 23 +++++------------ 9 files changed, 129 insertions(+), 96 deletions(-) diff --git a/library/std/src/sys/stdio/survos.rs b/library/std/src/sys/stdio/survos.rs index 56cce36..64b5bb9 100644 --- a/library/std/src/sys/stdio/survos.rs +++ b/library/std/src/sys/stdio/survos.rs @@ -1,26 +1,26 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::io; #[path = "unsupported.rs"] mod unsupported; -pub use self::unsupported::{STDIN_BUF_SIZE, Stderr, Stdin, is_ebadf}; +pub use self::unsupported::{STDIN_BUF_SIZE, Stderr, is_ebadf}; pub struct Stdout; -// pub struct Stdin; +pub struct Stdin; impl Stdout { pub const fn new() -> Self { Self } } -// impl Stdin { -// pub const fn new() -> Self { -// Self -// } -// } +impl Stdin { + pub const fn new() -> Self { + Self + } +} impl io::Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result { - unsafe { Ok(write(1, buf) as usize) } + Ok(write(1, buf) as usize) } fn flush(&mut self) -> io::Result<()> { @@ -29,12 +29,11 @@ impl io::Write for Stdout { } } -// impl io::Read for Stdin { -// fn read(&mut self, buf: &mut [u8]) -> io::Result { -// Ok(0) -// // unsafe { Ok(read(0, buf) as usize) } -// } -// } +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + Ok(read(0, buf) as usize) + } +} pub fn panic_output() -> Option { // Todo, use Stderr diff --git a/src/interrupt.rs b/src/interrupt.rs index cef3b13..a086d15 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -9,18 +9,7 @@ use log::info; use shared::syscall::SysCall; use crate::{ - boot::sbi::{ExtensionID, TimerFunctionID}, - clear_csr, - drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER}, - process::{ExecutionContext, exit_process, sleep}, - read_csr, - riscv::{disable_interrupt, dump_cpu}, - scheduler::SCHEDULER, - set_csr, syscall, - time::{IRQ_M_EXTERNAL, IRQ_M_TIMER, setup_next_timer_interrupt}, - virtio::input::HANDLING_INTERRUPT, - virtual_fs::{FILE_SYSTEM, VirtualFileSystem}, - write_csr, + boot::sbi::{ExtensionID, TimerFunctionID}, clear_csr, drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER}, println, process::{ExecutionContext, exit_process, sleep}, read_csr, riscv::{disable_interrupt, dump_cpu}, scheduler::SCHEDULER, set_csr, syscall, time::{IRQ_M_EXTERNAL, IRQ_M_TIMER, setup_next_timer_interrupt}, virtio::input::HANDLING_INTERRUPT, virtual_fs::{FILE_SYSTEM, VirtualFileSystem}, write_csr }; use core::{alloc::Layout, arch::naked_asm, time::Duration}; @@ -185,6 +174,7 @@ unsafe extern "C" fn supervisor_trap_handler( let buf = unsafe { core::slice::from_raw_parts_mut(a2 as *mut u8, a3 as usize) }; + let mut scheduler = SCHEDULER.lock(); let current_process = scheduler.get_current_process(); let vnode = current_process.fd_table[fd as usize].as_mut().unwrap(); diff --git a/src/io.rs b/src/io.rs index 978b26c..d877cf2 100644 --- a/src/io.rs +++ b/src/io.rs @@ -2,8 +2,12 @@ //! //! Provides a lightweight logger implementation routing to UART and helper //! macros for printing from kernel code. -use crate::println; +use crate::print; +use crate::tty::{TTY_INITIALIZED, TTY0}; + +use alloc::format; +use io::Write; use log::{Level, Metadata, Record}; use log::{LevelFilter, SetLoggerError}; @@ -14,11 +18,15 @@ use crate::uart::write_uart; /// Accepts any type that implements `AsRef` to avoid unnecessary /// allocations at call sites. pub(crate) fn print>(content: T) { - write_uart(content); + if TTY_INITIALIZED.load(core::sync::atomic::Ordering::Relaxed) { + unsafe { TTY0.new_node().write(content.as_ref().as_bytes()).unwrap() }; + } else { + write_uart(&content); + } } /// Logger implementation that routes kernel log records to the UART-based console. -struct Logger; +pub struct Logger; impl log::Log for Logger { fn enabled(&self, metadata: &Metadata) -> bool { @@ -27,24 +35,25 @@ impl log::Log for Logger { fn log(&self, record: &Record) { if self.enabled(record.metadata()) { - if let Some((file, line)) = record.file().zip(record.line()) { - println!( - "[{}] at {}:{} - {}", + let to_print = if let Some((file, line)) = record.file().zip(record.line()) { + format!( + "[{}] at {}:{} - {}\n", record.level(), file, line, record.args() - ); + ) } else { - println!("[{}] - {}", record.level(), record.args()); - } + format!("[{}] - {}\n", record.level(), record.args()) + }; + print!("{to_print}"); } } fn flush(&self) {} } -static LOGGER: Logger = Logger; +pub static LOGGER: Logger = Logger; /// Initialize the kernel logger and set the default level. /// @@ -62,10 +71,9 @@ macro_rules! print { #[macro_export] macro_rules! println { () => { - $crate::print!("\n\r") + $crate::print!("\n") }; ($($args:expr),*) => {{ - $crate::print!($($args),*); - $crate::println!(); + $crate::print!("{}\n", alloc::format!($($args),*)); }}; } diff --git a/src/keymap.rs b/src/keymap.rs index 91d5d0d..d247a19 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -94,33 +94,33 @@ pub const fn map_keycode(code: u16, state: &KeyboardState) -> KeyType { } else { if state.alt_gr_modifier { match code { - // KEY_1 => KeyType::Ascii('—'), + // KEY_1 => KeyType::Ascii('—'), KEY_2 => KeyType::Ascii('<'), KEY_3 => KeyType::Ascii('>'), KEY_4 => KeyType::Ascii('['), KEY_5 => KeyType::Ascii(']'), KEY_6 => KeyType::Ascii('^'), KEY_7 => KeyType::Ascii('±'), - // KEY_8 => KeyType::Ascii('−'), + // KEY_8 => KeyType::Ascii('−'), KEY_9 => KeyType::Ascii('÷'), KEY_0 => KeyType::Ascii('×'), - // KEY_MINUS => KeyType::Ascii('≠'), - // KEY_EQUAL => KeyType::Ascii('‰'), + KEY_MINUS => KeyType::Ascii('≠'), + KEY_EQUAL => KeyType::Ascii('‰'), KEY_Q => KeyType::Ascii('|'), - // KEY_W => KeyType::Ascii(''), + // KEY_W => KeyType::Ascii(''), KEY_E => KeyType::Ascii('&'), KEY_R => KeyType::Ascii('œ'), - // KEY_T => KeyType::Ascii(''), + // KEY_T => KeyType::Ascii(''), KEY_Y => KeyType::Ascii('¡'), - // KEY_U => KeyType::Ascii(''), + // KEY_U => KeyType::Ascii(''), KEY_I => KeyType::Ascii('ð'), - // KEY_O => KeyType::Ascii(''), + // KEY_O => KeyType::Ascii(''), KEY_P => KeyType::Ascii('ij'), KEY_A => KeyType::Ascii('æ'), KEY_S => KeyType::Ascii('ù'), - // KEY_D => KeyType::Ascii(''), - // KEY_F => KeyType::Ascii('€'), - // KEY_G => KeyType::Ascii('’'), + // KEY_D => KeyType::Ascii(''), + // KEY_F => KeyType::Ascii('€'), + // KEY_G => KeyType::Ascii('’'), KEY_H => KeyType::Ascii('©'), KEY_J => KeyType::Ascii('þ'), KEY_K => KeyType::Ascii('ß'), @@ -128,17 +128,17 @@ pub const fn map_keycode(code: u16, state: &KeyboardState) -> KeyType { KEY_Z => KeyType::Ascii('\\'), KEY_X => KeyType::Ascii('{'), KEY_C => KeyType::Ascii('}'), - // KEY_V => KeyType::Ascii('…'), + // KEY_V => KeyType::Ascii('…'), KEY_B => KeyType::Ascii('~'), KEY_N => KeyType::Ascii('¿'), - // KEY_M => KeyType::Ascii(''), + // KEY_M => KeyType::Ascii(''), KEY_LEFTBRACE => KeyType::Ascii('ə'), - // KEY_RIGHTBRACE => KeyType::Ascii(''), - // KEY_SEMICOLON => KeyType::Ascii(''), - // KEY_APOSTROPHE => KeyType::Ascii(''), - // KEY_COMMA => KeyType::Ascii(''), - // KEY_DOT => KeyType::Ascii('†'), - // KEY_SLASH => KeyType::Ascii(''), + // KEY_RIGHTBRACE => KeyType::Ascii(''), + // KEY_SEMICOLON => KeyType::Ascii(''), + // KEY_APOSTROPHE => KeyType::Ascii(''), + // KEY_COMMA => KeyType::Ascii(''), + // KEY_DOT => KeyType::Ascii('†'), + // KEY_SLASH => KeyType::Ascii(''), _ => KeyType::Unknown, } } else { diff --git a/src/tty.rs b/src/tty.rs index b00426e..d2a9cdd 100644 --- a/src/tty.rs +++ b/src/tty.rs @@ -1,4 +1,7 @@ -use core::cell::{LazyCell, RefCell}; +use core::{ + cell::{LazyCell, RefCell}, + sync::atomic::AtomicBool, +}; use alloc::{boxed::Box, rc::Rc}; use io::{IoBase, Read, Seek, Write}; @@ -10,7 +13,12 @@ use crate::{ }; pub const TTY_BUFFER_SIZE: usize = 4096; // 4Ko -pub static mut TTY0: LazyCell = LazyCell::new(Tty::new); +pub static mut TTY0: LazyCell = LazyCell::new(|| { + let tty = Tty::new(); + TTY_INITIALIZED.store(true, core::sync::atomic::Ordering::Relaxed); + tty +}); +pub static TTY_INITIALIZED: AtomicBool = AtomicBool::new(false); #[derive(Debug, Clone)] pub struct Tty { @@ -25,10 +33,13 @@ impl Tty { console: RefCell::new(VirtualConsole::new()).into(), } } + pub fn new_node(&'_ self) -> TtyNode<'_> { + TtyNode { tty: self } + } } #[derive(Debug)] -struct TtyNode<'a> { +pub struct TtyNode<'a> { tty: &'a Tty, } @@ -40,7 +51,7 @@ impl VirtualFileSystem for Tty { if !path.is_empty() { Err(()) } else { - Ok(Box::new(TtyNode { tty: self })) + Ok(Box::new(self.new_node())) } } } @@ -68,10 +79,12 @@ impl Seek for TtyNode<'_> { impl Write for TtyNode<'_> { fn write(&mut self, buf: &[u8]) -> Result { - self.tty - .console - .borrow_mut() - .write_str(str::from_utf8(buf).unwrap()); + critical_section::with(|_| { + self.tty + .console + .borrow_mut() + .write_str(str::from_utf8(buf).unwrap()); + }); Ok(buf.len()) } diff --git a/src/vga.rs b/src/vga.rs index 212baa1..4858075 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -4,7 +4,7 @@ //! draw text using an embedded font plate. use log::info; -use crate::draw::{Color, Draw}; +use crate::draw::{Color, Draw, FONT_HEIGHT}; const PCI_ECAM_BASE_ADDRESS: *mut u32 = 0x30000000 as *mut _; const BOCHS_DISPLAY_BASE_ADDRESS: *mut u32 = 0x50000000 as *mut _; @@ -58,6 +58,23 @@ impl Vga { pub unsafe fn write_u8_unsafe(offset: usize, value: u8) { unsafe { *(VGA_ADDRESS.byte_add(offset) as *mut u8) = value } } + pub unsafe fn read_u8_unsafe(offset: usize) -> u8 { + unsafe { *(VGA_ADDRESS.byte_add(offset) as *mut u8) } + } + pub fn line_up(&mut self) { + unsafe { + core::ptr::copy( + VGA_ADDRESS.add(self.get_width() * FONT_HEIGHT), + VGA_ADDRESS, + self.get_width() * (self.get_height() - FONT_HEIGHT), + ); + for y in self.get_height() - FONT_HEIGHT..self.get_height() { + for x in 0..self.get_width() { + self.write_pixel_unsafe(x as u16, y as u16, Color::BLACK); + } + } + } + } } impl Draw for Vga { diff --git a/src/virtual_console.rs b/src/virtual_console.rs index 63015e4..1b13070 100644 --- a/src/virtual_console.rs +++ b/src/virtual_console.rs @@ -3,8 +3,9 @@ use io::SeekFrom; use crate::{ draw::{Color, Draw, FONT_HEIGHT, FONT_WIDTH}, - vga::{self, Vga}, - virtual_fs::{self, FILE_SYSTEM, VirtualFileSystem}, + uart::write_uart, + vga::Vga, + virtual_fs::{self, VGAFileSystem}, }; const TAB_SIZE: u64 = 4; @@ -31,11 +32,12 @@ impl VirtualConsole { pub fn new() -> Self { VirtualConsole { cursor: Cursor::new(), - framebuffer: unsafe { FILE_SYSTEM.open("/dev/fb0".as_ref()).unwrap() }, + framebuffer: Box::new(VGAFileSystem.open_vga()), } } pub fn write_str(&mut self, s: &str) { + write_uart(s); s.chars().for_each(|c| self.write_char(c)); } @@ -81,11 +83,11 @@ impl VirtualConsole { } _ => {} } - if self.cursor.x as usize * FONT_WIDTH >= vga::WIDTH { + if self.cursor.x as usize * FONT_WIDTH >= self.get_width() { self.cursor.x = 0; self.cursor.y += 1; } - if self.cursor.y as usize * FONT_HEIGHT >= vga::HEIGHT { + if (self.cursor.y + 1) as usize * FONT_HEIGHT >= self.get_height() { self.line_up(); if last_cursor.y > 0 { last_cursor.y -= 1; @@ -96,7 +98,13 @@ impl VirtualConsole { } } - fn line_up(&mut self) {} + fn line_up(&mut self) { + // TODO avoid calling Vga directly + Vga.line_up(); + if self.cursor.y > 0 { + self.cursor.y -= 1; + } + } fn move_cursor_line(&mut self, _last: Cursor) {} } diff --git a/src/virtual_fs.rs b/src/virtual_fs.rs index b052372..65112d1 100644 --- a/src/virtual_fs.rs +++ b/src/virtual_fs.rs @@ -75,10 +75,16 @@ pub unsafe fn init_file_system() { } #[derive(Debug)] -struct VGAFileSystem; +pub struct VGAFileSystem; + +impl VGAFileSystem { + pub fn open_vga(&self) -> VGAVirtualNode { + VGAVirtualNode { position: 0 } + } +} #[derive(Debug)] -struct VGAVirtualNode { +pub struct VGAVirtualNode { position: u64, } @@ -103,8 +109,11 @@ impl Seek for VGAVirtualNode { } impl Read for VGAVirtualNode { - fn read(&mut self, _buf: &mut [u8]) -> Result { - todo!() + fn read(&mut self, buf: &mut [u8]) -> Result { + buf.iter_mut().for_each(|val| { + *val = unsafe { Vga::read_u8_unsafe(self.position as usize) }; + }); + Ok(buf.len()) } } @@ -128,7 +137,7 @@ impl VirtualFileSystem for VGAFileSystem { if !path.is_empty() { Err(()) } else { - Ok(Box::new(VGAVirtualNode { position: 0 })) + Ok(Box::new(self.open_vga())) } } } diff --git a/user/test_pic/src/main.rs b/user/test_pic/src/main.rs index ab9506c..9fa0c0d 100644 --- a/user/test_pic/src/main.rs +++ b/user/test_pic/src/main.rs @@ -1,34 +1,23 @@ #![allow(unused)] use core::sync::atomic::AtomicUsize; -use io::{Read as Readio, Write}; use shared::{fs::File, syscall}; -use std::io::{Read, Stdin, stdin}; - -static FOO: AtomicUsize = AtomicUsize::new(0); +use std::io::{Read, Stdin, Write, stdin, stdout}; fn main() { - let mut input = String::new(); - input.push('a'); - // let mut file = syscall::open("/dev/fb0"); - // syscall::seek(&mut file, SeekFrom::End(-3)); - // syscall::write(&mut file, &[255; 6400 * 50]); - // syscall::sleep(Duration::from_secs_f64(2.0)); syscall::close(0); let mut tty = unsafe { File::from_raw_fd(syscall::open("/dev/tty0")) }; syscall::close(1); let _ = syscall::open("/dev/tty0"); println!("test from test_pic"); - tty.write(input.as_bytes()).unwrap(); syscall::spawn("/usr/bin/shell"); // panic!("explicit panic"); // std::process::abort(); - unsafe {core::arch::asm!("unimp")}; + // unsafe {core::arch::asm!("unimp")}; loop { - let mut test = [0; 2]; - // let len = stdin().read(&mut test); - let len = tty.read(&mut test).unwrap(); - tty.write(str::from_utf8(&test[..len as usize]).unwrap().as_bytes()) - .unwrap(); + let mut test = [0; 4]; + let len = stdin().read(&mut test).unwrap(); + print!("{}", str::from_utf8(&test[..len as usize]).unwrap()); + stdout().flush(); } }