Log on tty (serial and vga)

This commit is contained in:
2026-03-22 16:00:41 +01:00
parent 15ecefb5fb
commit f966a1239e
9 changed files with 129 additions and 96 deletions

View File

@@ -1,26 +1,26 @@
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::io;
#[path = "unsupported.rs"] #[path = "unsupported.rs"]
mod unsupported; 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 Stdout;
// pub struct Stdin; pub struct Stdin;
impl Stdout { impl Stdout {
pub const fn new() -> Self { pub const fn new() -> Self {
Self Self
} }
} }
// impl Stdin { impl Stdin {
// pub const fn new() -> Self { pub const fn new() -> Self {
// Self Self
// } }
// } }
impl io::Write for Stdout { impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe { Ok(write(1, buf) as usize) } Ok(write(1, buf) as usize)
} }
fn flush(&mut self) -> io::Result<()> { fn flush(&mut self) -> io::Result<()> {
@@ -29,12 +29,11 @@ impl io::Write for Stdout {
} }
} }
// impl io::Read for Stdin { impl io::Read for Stdin {
// fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
// Ok(0) Ok(read(0, buf) as usize)
// // unsafe { Ok(read(0, buf) as usize) } }
// } }
// }
pub fn panic_output() -> Option<impl io::Write> { pub fn panic_output() -> Option<impl io::Write> {
// Todo, use Stderr // Todo, use Stderr

View File

@@ -9,18 +9,7 @@ use log::info;
use shared::syscall::SysCall; use shared::syscall::SysCall;
use crate::{ use crate::{
boot::sbi::{ExtensionID, TimerFunctionID}, 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
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,
}; };
use core::{alloc::Layout, arch::naked_asm, time::Duration}; use core::{alloc::Layout, arch::naked_asm, time::Duration};
@@ -185,6 +174,7 @@ unsafe extern "C" fn supervisor_trap_handler(
let buf = let buf =
unsafe { core::slice::from_raw_parts_mut(a2 as *mut u8, a3 as usize) }; unsafe { core::slice::from_raw_parts_mut(a2 as *mut u8, a3 as usize) };
let mut scheduler = SCHEDULER.lock(); let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process(); let current_process = scheduler.get_current_process();
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap(); let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();

View File

@@ -2,8 +2,12 @@
//! //!
//! Provides a lightweight logger implementation routing to UART and helper //! Provides a lightweight logger implementation routing to UART and helper
//! macros for printing from kernel code. //! 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::{Level, Metadata, Record};
use log::{LevelFilter, SetLoggerError}; use log::{LevelFilter, SetLoggerError};
@@ -14,11 +18,15 @@ use crate::uart::write_uart;
/// Accepts any type that implements `AsRef<str>` to avoid unnecessary /// Accepts any type that implements `AsRef<str>` to avoid unnecessary
/// allocations at call sites. /// allocations at call sites.
pub(crate) fn print<T: AsRef<str>>(content: T) { pub(crate) fn print<T: AsRef<str>>(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. /// Logger implementation that routes kernel log records to the UART-based console.
struct Logger; pub struct Logger;
impl log::Log for Logger { impl log::Log for Logger {
fn enabled(&self, metadata: &Metadata) -> bool { fn enabled(&self, metadata: &Metadata) -> bool {
@@ -27,24 +35,25 @@ impl log::Log for Logger {
fn log(&self, record: &Record) { fn log(&self, record: &Record) {
if self.enabled(record.metadata()) { if self.enabled(record.metadata()) {
if let Some((file, line)) = record.file().zip(record.line()) { let to_print = if let Some((file, line)) = record.file().zip(record.line()) {
println!( format!(
"[{}] at {}:{} - {}", "[{}] at {}:{} - {}\n",
record.level(), record.level(),
file, file,
line, line,
record.args() record.args()
); )
} else { } else {
println!("[{}] - {}", record.level(), record.args()); format!("[{}] - {}\n", record.level(), record.args())
} };
print!("{to_print}");
} }
} }
fn flush(&self) {} fn flush(&self) {}
} }
static LOGGER: Logger = Logger; pub static LOGGER: Logger = Logger;
/// Initialize the kernel logger and set the default level. /// Initialize the kernel logger and set the default level.
/// ///
@@ -62,10 +71,9 @@ macro_rules! print {
#[macro_export] #[macro_export]
macro_rules! println { macro_rules! println {
() => { () => {
$crate::print!("\n\r") $crate::print!("\n")
}; };
($($args:expr),*) => {{ ($($args:expr),*) => {{
$crate::print!($($args),*); $crate::print!("{}\n", alloc::format!($($args),*));
$crate::println!();
}}; }};
} }

View File

@@ -94,33 +94,33 @@ pub const fn map_keycode(code: u16, state: &KeyboardState) -> KeyType {
} else { } else {
if state.alt_gr_modifier { if state.alt_gr_modifier {
match code { match code {
// KEY_1 => KeyType::Ascii('—'), // KEY_1 => KeyType::Ascii('—'),
KEY_2 => KeyType::Ascii('<'), KEY_2 => KeyType::Ascii('<'),
KEY_3 => KeyType::Ascii('>'), KEY_3 => KeyType::Ascii('>'),
KEY_4 => KeyType::Ascii('['), KEY_4 => KeyType::Ascii('['),
KEY_5 => KeyType::Ascii(']'), KEY_5 => KeyType::Ascii(']'),
KEY_6 => KeyType::Ascii('^'), KEY_6 => KeyType::Ascii('^'),
KEY_7 => KeyType::Ascii('±'), KEY_7 => KeyType::Ascii('±'),
// KEY_8 => KeyType::Ascii(''), // KEY_8 => KeyType::Ascii(''),
KEY_9 => KeyType::Ascii('÷'), KEY_9 => KeyType::Ascii('÷'),
KEY_0 => KeyType::Ascii('×'), KEY_0 => KeyType::Ascii('×'),
// KEY_MINUS => KeyType::Ascii('≠'), KEY_MINUS => KeyType::Ascii('≠'),
// KEY_EQUAL => KeyType::Ascii('‰'), KEY_EQUAL => KeyType::Ascii('‰'),
KEY_Q => KeyType::Ascii('|'), KEY_Q => KeyType::Ascii('|'),
// KEY_W => KeyType::Ascii(''), // KEY_W => KeyType::Ascii(''),
KEY_E => KeyType::Ascii('&'), KEY_E => KeyType::Ascii('&'),
KEY_R => KeyType::Ascii('œ'), KEY_R => KeyType::Ascii('œ'),
// KEY_T => KeyType::Ascii(''), // KEY_T => KeyType::Ascii(''),
KEY_Y => KeyType::Ascii('¡'), KEY_Y => KeyType::Ascii('¡'),
// KEY_U => KeyType::Ascii(''), // KEY_U => KeyType::Ascii(''),
KEY_I => KeyType::Ascii('ð'), KEY_I => KeyType::Ascii('ð'),
// KEY_O => KeyType::Ascii(''), // KEY_O => KeyType::Ascii(''),
KEY_P => KeyType::Ascii('ij'), KEY_P => KeyType::Ascii('ij'),
KEY_A => KeyType::Ascii('æ'), KEY_A => KeyType::Ascii('æ'),
KEY_S => KeyType::Ascii('ù'), KEY_S => KeyType::Ascii('ù'),
// KEY_D => KeyType::Ascii(''), // KEY_D => KeyType::Ascii(''),
// KEY_F => KeyType::Ascii('€'), // KEY_F => KeyType::Ascii('€'),
// KEY_G => KeyType::Ascii(''), // KEY_G => KeyType::Ascii(''),
KEY_H => KeyType::Ascii('©'), KEY_H => KeyType::Ascii('©'),
KEY_J => KeyType::Ascii('þ'), KEY_J => KeyType::Ascii('þ'),
KEY_K => 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_Z => KeyType::Ascii('\\'),
KEY_X => KeyType::Ascii('{'), KEY_X => KeyType::Ascii('{'),
KEY_C => KeyType::Ascii('}'), KEY_C => KeyType::Ascii('}'),
// KEY_V => KeyType::Ascii('…'), // KEY_V => KeyType::Ascii('…'),
KEY_B => KeyType::Ascii('~'), KEY_B => KeyType::Ascii('~'),
KEY_N => KeyType::Ascii('¿'), KEY_N => KeyType::Ascii('¿'),
// KEY_M => KeyType::Ascii(''), // KEY_M => KeyType::Ascii(''),
KEY_LEFTBRACE => KeyType::Ascii('ə'), KEY_LEFTBRACE => KeyType::Ascii('ə'),
// KEY_RIGHTBRACE => KeyType::Ascii(''), // KEY_RIGHTBRACE => KeyType::Ascii(''),
// KEY_SEMICOLON => KeyType::Ascii(''), // KEY_SEMICOLON => KeyType::Ascii(''),
// KEY_APOSTROPHE => KeyType::Ascii(''), // KEY_APOSTROPHE => KeyType::Ascii(''),
// KEY_COMMA => KeyType::Ascii(''), // KEY_COMMA => KeyType::Ascii(''),
// KEY_DOT => KeyType::Ascii('†'), // KEY_DOT => KeyType::Ascii('†'),
// KEY_SLASH => KeyType::Ascii(''), // KEY_SLASH => KeyType::Ascii(''),
_ => KeyType::Unknown, _ => KeyType::Unknown,
} }
} else { } else {

View File

@@ -1,4 +1,7 @@
use core::cell::{LazyCell, RefCell}; use core::{
cell::{LazyCell, RefCell},
sync::atomic::AtomicBool,
};
use alloc::{boxed::Box, rc::Rc}; use alloc::{boxed::Box, rc::Rc};
use io::{IoBase, Read, Seek, Write}; use io::{IoBase, Read, Seek, Write};
@@ -10,7 +13,12 @@ use crate::{
}; };
pub const TTY_BUFFER_SIZE: usize = 4096; // 4Ko pub const TTY_BUFFER_SIZE: usize = 4096; // 4Ko
pub static mut TTY0: LazyCell<Tty> = LazyCell::new(Tty::new); pub static mut TTY0: LazyCell<Tty> = 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)] #[derive(Debug, Clone)]
pub struct Tty { pub struct Tty {
@@ -25,10 +33,13 @@ impl Tty {
console: RefCell::new(VirtualConsole::new()).into(), console: RefCell::new(VirtualConsole::new()).into(),
} }
} }
pub fn new_node(&'_ self) -> TtyNode<'_> {
TtyNode { tty: self }
}
} }
#[derive(Debug)] #[derive(Debug)]
struct TtyNode<'a> { pub struct TtyNode<'a> {
tty: &'a Tty, tty: &'a Tty,
} }
@@ -40,7 +51,7 @@ impl VirtualFileSystem for Tty {
if !path.is_empty() { if !path.is_empty() {
Err(()) Err(())
} else { } 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<'_> { impl Write for TtyNode<'_> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.tty critical_section::with(|_| {
.console self.tty
.borrow_mut() .console
.write_str(str::from_utf8(buf).unwrap()); .borrow_mut()
.write_str(str::from_utf8(buf).unwrap());
});
Ok(buf.len()) Ok(buf.len())
} }

View File

@@ -4,7 +4,7 @@
//! draw text using an embedded font plate. //! draw text using an embedded font plate.
use log::info; 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 PCI_ECAM_BASE_ADDRESS: *mut u32 = 0x30000000 as *mut _;
const BOCHS_DISPLAY_BASE_ADDRESS: *mut u32 = 0x50000000 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) { pub unsafe fn write_u8_unsafe(offset: usize, value: u8) {
unsafe { *(VGA_ADDRESS.byte_add(offset) as *mut u8) = value } 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 { impl Draw for Vga {

View File

@@ -3,8 +3,9 @@ use io::SeekFrom;
use crate::{ use crate::{
draw::{Color, Draw, FONT_HEIGHT, FONT_WIDTH}, draw::{Color, Draw, FONT_HEIGHT, FONT_WIDTH},
vga::{self, Vga}, uart::write_uart,
virtual_fs::{self, FILE_SYSTEM, VirtualFileSystem}, vga::Vga,
virtual_fs::{self, VGAFileSystem},
}; };
const TAB_SIZE: u64 = 4; const TAB_SIZE: u64 = 4;
@@ -31,11 +32,12 @@ impl VirtualConsole {
pub fn new() -> Self { pub fn new() -> Self {
VirtualConsole { VirtualConsole {
cursor: Cursor::new(), 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) { pub fn write_str(&mut self, s: &str) {
write_uart(s);
s.chars().for_each(|c| self.write_char(c)); 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.x = 0;
self.cursor.y += 1; 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(); self.line_up();
if last_cursor.y > 0 { if last_cursor.y > 0 {
last_cursor.y -= 1; 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) {} fn move_cursor_line(&mut self, _last: Cursor) {}
} }

View File

@@ -75,10 +75,16 @@ pub unsafe fn init_file_system() {
} }
#[derive(Debug)] #[derive(Debug)]
struct VGAFileSystem; pub struct VGAFileSystem;
impl VGAFileSystem {
pub fn open_vga(&self) -> VGAVirtualNode {
VGAVirtualNode { position: 0 }
}
}
#[derive(Debug)] #[derive(Debug)]
struct VGAVirtualNode { pub struct VGAVirtualNode {
position: u64, position: u64,
} }
@@ -103,8 +109,11 @@ impl Seek for VGAVirtualNode {
} }
impl Read for VGAVirtualNode { impl Read for VGAVirtualNode {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, ()> { fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> {
todo!() 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() { if !path.is_empty() {
Err(()) Err(())
} else { } else {
Ok(Box::new(VGAVirtualNode { position: 0 })) Ok(Box::new(self.open_vga()))
} }
} }
} }

View File

@@ -1,34 +1,23 @@
#![allow(unused)] #![allow(unused)]
use core::sync::atomic::AtomicUsize; use core::sync::atomic::AtomicUsize;
use io::{Read as Readio, Write};
use shared::{fs::File, syscall}; use shared::{fs::File, syscall};
use std::io::{Read, Stdin, stdin}; use std::io::{Read, Stdin, Write, stdin, stdout};
static FOO: AtomicUsize = AtomicUsize::new(0);
fn main() { 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); syscall::close(0);
let mut tty = unsafe { File::from_raw_fd(syscall::open("/dev/tty0")) }; let mut tty = unsafe { File::from_raw_fd(syscall::open("/dev/tty0")) };
syscall::close(1); syscall::close(1);
let _ = syscall::open("/dev/tty0"); let _ = syscall::open("/dev/tty0");
println!("test from test_pic"); println!("test from test_pic");
tty.write(input.as_bytes()).unwrap();
syscall::spawn("/usr/bin/shell"); syscall::spawn("/usr/bin/shell");
// panic!("explicit panic"); // panic!("explicit panic");
// std::process::abort(); // std::process::abort();
unsafe {core::arch::asm!("unimp")}; // unsafe {core::arch::asm!("unimp")};
loop { loop {
let mut test = [0; 2]; let mut test = [0; 4];
// let len = stdin().read(&mut test); let len = stdin().read(&mut test).unwrap();
let len = tty.read(&mut test).unwrap(); print!("{}", str::from_utf8(&test[..len as usize]).unwrap());
tty.write(str::from_utf8(&test[..len as usize]).unwrap().as_bytes()) stdout().flush();
.unwrap();
} }
} }