feat: rewrite console init logic

This commit is contained in:
Paul Pan 2024-03-24 19:12:04 +08:00
parent 284e12348d
commit 5032711022
22 changed files with 291 additions and 228 deletions

View File

@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features] [features]
default = ["arch_riscv64", "board_virt", "log_color", "frame_freelist"] default = ["arch_riscv64", "board_virt", "log_color"]
arch_riscv64 = [] arch_riscv64 = []
arch_riscv32 = [] arch_riscv32 = []
@ -16,9 +16,6 @@ board_virt = []
log_color = [] log_color = []
frame_bitmap = []
frame_freelist = []
[profile.dev] [profile.dev]
panic = "abort" panic = "abort"

View File

@ -1,6 +1,6 @@
.section .text.trap .section .text.trap
.extern trap_handler .extern kernel_trap_handler
.align 4 .align 4
.global trap_entry .global trap_entry
@ -64,10 +64,10 @@ __trap_from_user:
beqz t2, __trap_from_user_next beqz t2, __trap_from_user_next
__trap_from_kernel_next: __trap_from_kernel_next:
# goto trap_handler # goto kernel_trap_handler
mv a0, sp mv a0, sp
la ra, __trap_return la ra, __trap_return
j trap_handler j kernel_trap_handler
__trap_from_user_next: __trap_from_user_next:
# we come from switch_to_user, which saved callee-saved registers # we come from switch_to_user, which saved callee-saved registers
@ -87,7 +87,7 @@ __trap_from_user_next:
LD_SP s10, 10 LD_SP s10, 10
LD_SP s11, 11 LD_SP s11, 11
LD_SP ra, 12 LD_SP ra, 12
LD_SP tp, 13 # hartid LD_SP tp, 13 # TLS
addi sp, sp, 14*XLENB addi sp, sp, 14*XLENB
# return from switch_to_user # return from switch_to_user
ret ret
@ -110,7 +110,7 @@ switch_to_user:
SD_SP s10, 10 SD_SP s10, 10
SD_SP s11, 11 SD_SP s11, 11
SD_SP ra, 12 SD_SP ra, 12
SD_SP tp, 13 # not callee-saved, but used in kernel to save hartid SD_SP tp, 13 # not callee-saved, but used in kernel to save TLS
# switch to user context # switch to user context
mv t0, sp mv t0, sp

View File

@ -0,0 +1,32 @@
use crate::plat::console::{set_console, ConsoleDevice, ConsoleDriver};
pub struct EarlyConsole;
impl ConsoleDevice for EarlyConsole {
fn read(&mut self) -> u8 {
loop {
if let Some(ch) = self.try_read() {
return ch;
}
}
}
fn try_read(&mut self) -> Option<u8> {
let mut buf = [0u8; 1];
let ret = sbi_rt::console_read(sbi_rt::Physical::new(1, buf.as_mut_ptr() as _, 0));
ret.ok()
.and_then(|len| if len == 0 { None } else { Some(buf[0]) })
}
fn write(&mut self, ch: u8) {
sbi_rt::console_write_byte(ch);
}
fn write_str(&mut self, s: &str) {
sbi_rt::console_write(sbi_rt::Physical::new(s.len(), s.as_ptr() as _, 0));
}
}
pub fn init_early_console() {
set_console(ConsoleDriver::EarlyConsole(EarlyConsole));
}

View File

@ -1,28 +0,0 @@
use crate::plat::io::{Printer, RawConsole, Reader};
impl Printer for RawConsole {
#[inline]
fn put_char(c: char) {
sbi_rt::console_write_byte(c as u8);
}
#[inline]
fn put_str(s: &str) {
sbi_rt::console_write(sbi_rt::Physical::new(s.len(), s.as_ptr() as _, 0));
}
}
impl Reader for RawConsole {
#[inline]
fn get_char() -> char {
let mut buf = [0u8; 1];
loop {
let ret = sbi_rt::console_read(sbi_rt::Physical::new(1, buf.as_mut_ptr() as _, 0));
if let Some(len) = ret.ok()
&& len == 1
{
return buf[0] as char;
}
}
}
}

View File

@ -1,5 +1,4 @@
pub const BOOT_HART_ID: usize = 0;
pub const TIMER_TICKS: u64 = 100_000; // 100ms pub const TIMER_TICKS: u64 = 100_000; // 100ms
mod io; pub mod console;
mod lowlevel; mod lowlevel;

View File

@ -0,0 +1,21 @@
use crate::plat::console::{set_console, ConsoleDevice, ConsoleDriver};
pub struct EarlyConsole;
impl ConsoleDevice for EarlyConsole {
fn read(&mut self) -> u8 {
let uart = super::UART0_BASE as *mut u8;
let line_sts = super::UART0_LSR as *mut u8;
while unsafe { line_sts.read_volatile() } & 0x01 == 0 {}
unsafe { uart.read_volatile() }
}
fn write(&mut self, ch: u8) {
let uart = super::UART0_BASE as *mut u8;
unsafe { uart.write_volatile(ch) }
}
}
pub fn init_early_console() {
set_console(ConsoleDriver::EarlyConsole(EarlyConsole));
}

View File

@ -1,4 +1,3 @@
pub const BOOT_HART_ID: usize = 0;
pub const TIMER_TICKS: u64 = 100_000; // 100ms pub const TIMER_TICKS: u64 = 100_000; // 100ms
// devices // devices
@ -6,6 +5,5 @@ pub const UART0_BASE: usize = 0x1000_0000;
pub const UART0_LSR: usize = 0x1000_0005; pub const UART0_LSR: usize = 0x1000_0005;
pub const TEST_DEVICE: *mut u32 = 0x10_0000 as *mut u32; pub const TEST_DEVICE: *mut u32 = 0x10_0000 as *mut u32;
pub mod console;
mod lowlevel; mod lowlevel;
mod printer;
mod reader;

View File

@ -1,19 +0,0 @@
use crate::plat::io::{Printer, RawConsole};
// Theoretically, we should wait until "THR Empty" bit (1<<5 in LSR) is set before writing to UART.
impl Printer for RawConsole {
#[inline]
fn put_char(c: char) {
let uart = super::UART0_BASE as *mut char;
unsafe { uart.write_volatile(c) }
}
#[inline]
fn put_str(s: &str) {
let uart = super::UART0_BASE as *mut char;
for c in s.chars() {
unsafe { uart.write_volatile(c) }
}
}
}

View File

@ -1,11 +0,0 @@
use crate::plat::io::{RawConsole, Reader};
impl Reader for RawConsole {
#[inline]
fn get_char() -> char {
let uart = super::UART0_BASE as *mut char;
let line_sts = super::UART0_LSR as *mut u8;
while unsafe { line_sts.read_volatile() } & 0x01 == 0 {}
unsafe { uart.read_volatile() }
}
}

View File

@ -1,5 +1,5 @@
use super::board::console::init_early_console;
use super::layout::{__boot_stack_end, __bss_end, __tbss_end, __tbss_start}; use super::layout::{__boot_stack_end, __bss_end, __tbss_end, __tbss_start};
use super::tls::set_hart_id;
#[naked] #[naked]
#[no_mangle] #[no_mangle]
@ -27,10 +27,10 @@ unsafe extern "C" fn _start(hart_id: usize, device_tree_addr: usize) -> ! {
} }
extern "C" fn pre_main(hart_id: usize, device_tree_addr: usize) -> ! { extern "C" fn pre_main(hart_id: usize, device_tree_addr: usize) -> ! {
// TODO: multiboot
zero_bss(); zero_bss();
init_early_console();
// TODO: initialize page table // TODO: initialize page table
crate::entry::rust_main(hart_id, device_tree_addr); crate::entry::rust_main(hart_id, device_tree_addr);

View File

@ -14,8 +14,8 @@ extern "C" {
static __data_end: ExternSymbol; static __data_end: ExternSymbol;
static __bss_start: ExternSymbol; static __bss_start: ExternSymbol;
pub(crate) static __boot_stack_end: ExternSymbol; pub static __boot_stack_end: ExternSymbol;
pub(crate) static __bss_end: ExternSymbol; pub static __bss_end: ExternSymbol;
static __tss_start: ExternSymbol; static __tss_start: ExternSymbol;
static __tss_end: ExternSymbol; static __tss_end: ExternSymbol;
@ -23,6 +23,6 @@ extern "C" {
static __tdata_start: ExternSymbol; static __tdata_start: ExternSymbol;
static __tdata_end: ExternSymbol; static __tdata_end: ExternSymbol;
pub(crate) static __tbss_start: ExternSymbol; pub static __tbss_start: ExternSymbol;
pub(crate) static __tbss_end: ExternSymbol; pub static __tbss_end: ExternSymbol;
} }

View File

@ -1,7 +1,5 @@
use cfg_if::cfg_if; use cfg_if::cfg_if;
pub use board::BOOT_HART_ID;
cfg_if! { cfg_if! {
if #[cfg(feature = "board_virt")] { if #[cfg(feature = "board_virt")] {
#[path = "board/virt/mod.rs"] #[path = "board/virt/mod.rs"]
@ -12,6 +10,9 @@ cfg_if! {
} }
} }
// Console: plat/console.rs
pub use board::console::EarlyConsole;
mod entry; mod entry;
pub mod layout; pub mod layout;
mod lowlevel; mod lowlevel;

View File

@ -1,3 +1,4 @@
use crate::plat::console::CONSOLE;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use log::trace; use log::trace;
use riscv::register::scause::{Interrupt as I, Trap as T}; use riscv::register::scause::{Interrupt as I, Trap as T};
@ -23,7 +24,13 @@ extern "C" {
} }
#[no_mangle] #[no_mangle]
extern "C" fn trap_handler(_tf: &mut TrapContext) { extern "C" fn kernel_trap_handler(tf: &mut TrapContext) {
// TODO: consider redesigning this, should we allow trap in kernel?
unsafe { CONSOLE.force_unlock() }
trap_handler(tf, true)
}
fn trap_handler(_tf: &mut TrapContext, _from_kernel: bool) {
let scause = riscv::register::scause::read(); let scause = riscv::register::scause::read();
trace!( trace!(
"[Interrupt] cpu@{} scause: {:?}", "[Interrupt] cpu@{} scause: {:?}",

11
kernel/src/drivers/mod.rs Normal file
View File

@ -0,0 +1,11 @@
// This will only implement some basic drivers (PLIC, UART, ...) to boot the kernel.
// For the rest of the drivers, we will implement them in root server.
pub mod serial;
use fdt::node::FdtNode;
pub trait Driver {
fn compatible() -> &'static [&'static str];
fn setup(_: FdtNode) -> Self;
}

View File

@ -0,0 +1,6 @@
use crate::drivers::Driver;
use crate::plat::console::ConsoleDevice;
pub trait SerialDriver: Driver + ConsoleDevice {}
pub mod uart16550;

View File

@ -0,0 +1,36 @@
use crate::drivers::serial::SerialDriver;
use crate::drivers::Driver;
use crate::plat::console::ConsoleDevice;
use fdt::node::FdtNode;
use uart_16550::MmioSerialPort;
pub struct Uart16550
where Uart16550: SerialDriver
{
port: MmioSerialPort,
}
impl Driver for Uart16550 {
fn compatible() -> &'static [&'static str] {
&["ns16550", "ns16550a"]
}
fn setup(fdt: FdtNode) -> Self {
let addr = fdt.reg().unwrap().next().unwrap().starting_address;
let mut serial_port = unsafe { MmioSerialPort::new(addr as usize) };
serial_port.init();
Uart16550 { port: serial_port }
}
}
impl ConsoleDevice for Uart16550 {
fn read(&mut self) -> u8 {
self.port.receive()
}
fn write(&mut self, byte: u8) {
self.port.send(byte);
}
}
impl SerialDriver for Uart16550 {}

View File

@ -1,9 +1,8 @@
use core::cell::Cell; use core::cell::Cell;
use fdt::Fdt; use fdt::Fdt;
use log::{debug, error, info}; use log::{debug, error, info, warn};
use crate::arch::BOOT_HART_ID; use crate::plat::console::{set_console, ConsoleDevice, ConsoleDriver, CONSOLE};
use crate::plat::io::{RawConsole, Reader};
use crate::plat::lowlevel::{Hardware, LowLevel}; use crate::plat::lowlevel::{Hardware, LowLevel};
use crate::plat::timer::{Timer, TimerOps}; use crate::plat::timer::{Timer, TimerOps};
use crate::plat::trap::{Trap, TrapOps}; use crate::plat::trap::{Trap, TrapOps};
@ -14,21 +13,17 @@ pub static HART_ID: Cell<usize> = Cell::new(0);
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_main(hart_id: usize, device_tree_addr: usize) -> ! { pub extern "C" fn rust_main(hart_id: usize, device_tree_addr: usize) -> ! {
HART_ID.set(hart_id); HART_ID.set(hart_id);
crate::logging::init(); crate::logging::init();
info!("Kernel Started");
let fdt = unsafe { Fdt::from_ptr(device_tree_addr as *const u8).unwrap() };
setup_console(&fdt);
#[cfg(test)] #[cfg(test)]
{
info!("Running tests...");
crate::test_main(); crate::test_main();
// test_main will exit
info!("[rust_main] Kernel Started");
info!("hart_id = {}", hart_id);
debug!("device_tree_addr = {:#x}", device_tree_addr);
if hart_id != BOOT_HART_ID {
error!("No SMP support now, halting...");
Hardware::halt();
} }
// TODO: setup and start scheduler // TODO: setup and start scheduler
@ -40,12 +35,13 @@ pub extern "C" fn rust_main(hart_id: usize, device_tree_addr: usize) -> ! {
Hardware::enable_interrupt(); Hardware::enable_interrupt();
loop { loop {
let ch = RawConsole::get_char(); if let Some(ch) = CONSOLE.lock().try_read() {
debug!("Key: {}", ch); debug!("Key: {}", ch);
if ch == 'q' { if ch == b'q' {
break; break;
} }
} }
}
// --- end --- // --- end ---
@ -53,15 +49,14 @@ pub extern "C" fn rust_main(hart_id: usize, device_tree_addr: usize) -> ! {
Hardware::shutdown(true); Hardware::shutdown(true);
} }
pub fn print_hello() { fn setup_console(fdt: &Fdt) {
const SERIAL_PORT_BASE_ADDRESS: usize = 0x1000_0000; // NOTE: ignore stdin: both stdin and stdout will go through stdout device
const HELLO: &[u8] = b"Hello World!\n"; match fdt
.chosen()
use uart_16550::MmioSerialPort; .stdout()
let mut serial_port = unsafe { MmioSerialPort::new(SERIAL_PORT_BASE_ADDRESS) }; .and_then(|stdout| ConsoleDriver::new(stdout))
serial_port.init(); {
None => warn!("No stdout device found or no compatible drivers found, falling back to platform default"),
for &byte in HELLO { Some(driver) => set_console(driver),
serial_port.send(byte);
} }
} }

View File

@ -1,9 +1,8 @@
use crate::entry::HART_ID;
use crate::plat::console::CONSOLE;
use core::fmt::Write; use core::fmt::Write;
use log::{LevelFilter, Log, Metadata, Record}; use log::{LevelFilter, Log, Metadata, Record};
use crate::plat::io::RawConsole;
struct SimpleLogger; struct SimpleLogger;
impl Log for SimpleLogger { impl Log for SimpleLogger {
@ -29,14 +28,12 @@ impl Log for SimpleLogger {
("", "") ("", "")
}; };
RawConsole CONSOLE
.lock()
.write_fmt(format_args!( .write_fmt(format_args!(
"{color_prefix}[{}] [{}] {}{color_reset}\n", "{color_prefix}[{}][HART{}] {}{color_reset}\n",
record.level(), record.level(),
record HART_ID.get(),
.module_path_static()
.or_else(|| record.module_path())
.unwrap_or("<n/a>"),
record.args(), record.args(),
)) ))
.unwrap(); .unwrap();

View File

@ -4,7 +4,6 @@
// Features // Features
#![feature(asm_const)] #![feature(asm_const)]
#![feature(extern_types)] #![feature(extern_types)]
#![feature(let_chains)]
#![feature(naked_functions)] #![feature(naked_functions)]
#![feature(panic_info_message)] #![feature(panic_info_message)]
#![feature(stmt_expr_attributes)] #![feature(stmt_expr_attributes)]
@ -17,26 +16,14 @@
extern crate static_assertions; extern crate static_assertions;
// arch mod arch;
pub mod arch; mod drivers;
mod entry;
// rust language runtime mod lang;
pub mod lang; mod logging;
mod objects;
// entrypoint mod plat;
pub mod entry; mod utils;
// plat
pub mod plat;
// object
pub mod objects;
// logging
pub mod logging;
// utils
pub mod utils;
// test infrastructure // test infrastructure
#[cfg(test)] #[cfg(test)]

120
kernel/src/plat/console.rs Normal file
View File

@ -0,0 +1,120 @@
use crate::arch::EarlyConsole;
use crate::drivers::serial::uart16550::Uart16550;
use crate::drivers::Driver;
use fdt::node::FdtNode;
use spin::Mutex;
macro_rules! create_console_driver {
(($($dynamic_driver:ident),+), ($($static_driver:ident),+)) => {
// A VERY DIRTY hack to work around dynamic dispatch
pub enum ConsoleDriver {
$($dynamic_driver($dynamic_driver),)+
$($static_driver($static_driver),)+
}
impl ConsoleDriver {
pub fn new(fdt: FdtNode) -> Option<Self> {
if let Some(compatible) = fdt.compatible() {
$(
if compatible
.all()
.any(|s| $dynamic_driver::compatible().contains(&s))
{
return Some(ConsoleDriver::$dynamic_driver($dynamic_driver::setup(fdt)))
}
)+
}
None
}
}
impl ConsoleDevice for ConsoleDriver {
fn read(&mut self) -> u8 {
match self {
$(ConsoleDriver::$dynamic_driver(driver) => driver.read(),)+
$(ConsoleDriver::$static_driver(driver) => driver.read(),)+
}
}
fn try_read(&mut self) -> Option<u8> {
match self {
$(ConsoleDriver::$dynamic_driver(driver) => driver.try_read(),)+
$(ConsoleDriver::$static_driver(driver) => driver.try_read(),)+
}
}
fn write(&mut self, n: u8) {
match self {
$(ConsoleDriver::$dynamic_driver(driver) => driver.write(n),)+
$(ConsoleDriver::$static_driver(driver) => driver.write(n),)+
}
}
fn write_str(&mut self, s: &str) {
match self {
$(ConsoleDriver::$dynamic_driver(driver) => driver.write_str(s),)+
$(ConsoleDriver::$static_driver(driver) => driver.write_str(s),)+
}
}
}
};
}
create_console_driver!((Uart16550), (SilenceConsole, EarlyConsole));
impl core::fmt::Write for ConsoleDriver {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
ConsoleDevice::write_str(self, s);
Ok(())
}
}
pub struct SilenceConsole;
impl ConsoleDevice for SilenceConsole {
fn read(&mut self) -> u8 {
0
}
fn write(&mut self, _: u8) {}
}
pub trait ConsoleDevice {
/// Blocking read
fn read(&mut self) -> u8;
/// Non-blocking read
fn try_read(&mut self) -> Option<u8> {
Some(self.read())
}
/// Write a byte
fn write(&mut self, _: u8);
/// Write a str
fn write_str(&mut self, s: &str) {
for byte in s.bytes() {
self.write(byte);
}
}
}
pub static CONSOLE: Mutex<ConsoleDriver> =
Mutex::new(ConsoleDriver::SilenceConsole(SilenceConsole));
pub fn set_console(driver: ConsoleDriver) {
*CONSOLE.lock() = driver;
}
#[cfg(test)]
mod tests {
use super::*;
use core::fmt::Write;
#[test_case]
fn print_with_console() {
CONSOLE.lock().write(b'A');
CONSOLE
.lock()
.write_fmt(format_args!("B {}\n", 0x42))
.unwrap();
}
}

View File

@ -1,86 +0,0 @@
pub struct RawConsole
where RawConsole: Printer + Reader;
pub trait Printer: core::fmt::Write {
fn put_char(c: char);
fn put_str(s: &str) {
s.chars().for_each(Self::put_char);
}
fn put_line(s: &str) {
Self::put_str(s);
Self::put_char('\n');
}
fn put_num(mut num: usize) {
if num == 0 {
Self::put_char('0');
return;
}
let digits = "0123456789".as_bytes();
let mut buf = [0u8; 10];
let mut i = 0;
while num != 0 {
buf[i] = digits[num % 10];
num /= 10;
i += 1;
}
buf[..i]
.iter()
.rev()
.for_each(|c| Self::put_char(*c as char));
}
fn put_hex(mut num: usize) {
if num == 0 {
Self::put_str("0x0");
return;
}
let alphabet = "0123456789abcdef".as_bytes();
let mut buf = [0u8; 8];
let mut i = 0;
while num != 0 {
buf[i] = alphabet[num % 16];
num /= 16;
i += 1;
}
Self::put_str("0x");
buf[..i]
.iter()
.rev()
.for_each(|c| Self::put_char(*c as char));
}
}
impl core::fmt::Write for RawConsole {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
Self::put_str(s);
Ok(())
}
}
pub trait Reader {
fn get_char() -> char;
}
#[cfg(test)]
mod tests {
use super::*;
#[test_case]
fn print_with_console() {
RawConsole::put_char('A');
RawConsole::put_str("BC");
RawConsole::put_line("DEF");
RawConsole::put_num(123);
RawConsole::put_hex(0xdeadbeef);
RawConsole::put_line("");
}
}

View File

@ -1,4 +1,4 @@
pub mod io; pub mod console;
pub mod lowlevel; pub mod lowlevel;
pub mod timer; pub mod timer;
pub mod trap; pub mod trap;