Supervisor mode

This commit is contained in:
2026-01-30 16:22:53 +01:00
parent 7a25e89d4c
commit 53043fd3cd
11 changed files with 457 additions and 108 deletions

View File

@@ -1 +1,4 @@
file target/riscv64/debug/kernel-rust
target remote localhost:1234 target remote localhost:1234
break machine_mode_entry
c

59
ilm.ld
View File

@@ -1,45 +1,36 @@
/* /*
* ld directives the for barmetal RISCV * ld directives the for barmetal RISCV
*/ */
OUTPUT_ARCH( "riscv" ) OUTPUT_ARCH(riscv)
ENTRY(entry)
MEMORY MEMORY {
{ RAM (wxa) : ORIGIN = 0x80000000, LENGTH = 128M
ram (wxa) : ORIGIN = 0x80000000, LENGTH = 128M
} }
SECTIONS { SECTIONS {
/* The kernel starts at 0x80000000 */ /* The kernel starts at 0x80000000 */
. = 0x80000000; . = 0x80000000;
.text : { .text : {
ENTRY(entry) KEEP(*(.text.entry))
KEEP(*(.text.entry))
*(.text.init) *(.text) *(.text.*) *(.text .text.*)
_etext = .; } > RAM
} > ram
.data : { .rodata : {
*(.sdata) *(.sdata.*) *(.rodata .rodata.*)
*(.fini) } > RAM
*(.anno)
*(.rodata) *(.rodata.*)
*(__ex_table)
*(.data) *(.data.*)
_edata = .;
} > ram
.bss : { .data : {
/* On veut un alignement sur 8 octets */ *(.data .data.*)
. = ALIGN(8); } > RAM
__bss_start = .;
*(.sbss) *(.sbss.*) .bss : ALIGN(8) {
*(.bss) *(.bss.*) __bss_start = .;
*(scommon) *(COMMON) *(.bss .bss.*)
. = ALIGN(8); __bss_end = .;
__bss_end = .; } > RAM
PROVIDE(_heap_start = __bss_end + 8);
PROVIDE(_heap_end = ORIGIN(ram) + LENGTH(ram)); _heap_start = ALIGN(8);
} > ram _heap_end = ORIGIN(RAM) + LENGTH(RAM);
_end = .;
} }

View File

@@ -1,11 +1,48 @@
use core::arch::global_asm; use core::arch::naked_asm;
global_asm!( use crate::{
".section .text.entry clear_csr,
.globl entry interrupt::{setup_machine_trap_handler, setup_supervisor_trap_handler},
entry: mret, set_csr, supervisor_mode_entry, write_csr,
la sp, _heap_end };
jal main
loop: pub mod sbi;
j loop"
); #[unsafe(naked)]
#[unsafe(no_mangle)]
pub extern "C" fn entry() {
naked_asm!(
"
la sp, _heap_end
jal machine_mode_entry
1:
j 1b"
)
}
#[unsafe(no_mangle)]
pub extern "C" fn machine_mode_entry() {
unsafe { setup_machine_trap_handler() };
// Set supervisor mode
set_csr!(mstatus, 1 << 11);
clear_csr!(mstatus, 1 << 12);
write_csr!(mepc, supervisor_mode_entry as *const () as u64);
// Allow all R/W/X
write_csr!(pmpaddr0, 0xffff_ffff_ffff_ffffu64);
// NAPOT
write_csr!(pmpcfg0, 0b0000_1111);
// Desactivate paging
write_csr!(satp, 0);
// Delegate timer interrupt
set_csr!(mideleg, 1 << 5);
unsafe {
setup_supervisor_trap_handler();
};
mret!();
}

11
src/boot/sbi.rs Normal file
View File

@@ -0,0 +1,11 @@
#[non_exhaustive]
#[repr(u64)]
pub enum EID {
Time = 0x54494D45,
}
#[non_exhaustive]
#[repr(u64)]
pub enum TimeFID {
SetTimer = 0x0,
}

View File

@@ -1,18 +1,18 @@
use critical_section::RawRestoreState; use critical_section::RawRestoreState;
use crate::riscv::{disable_interrupt, get_interrupt_state, restore_interrupt}; use crate::riscv::{disable_supervisor_interrupt, get_supervisor_interrupt_state, restore_supervisor_interrupt};
struct MyCriticalSection; struct MyCriticalSection;
critical_section::set_impl!(MyCriticalSection); critical_section::set_impl!(MyCriticalSection);
unsafe impl critical_section::Impl for MyCriticalSection { unsafe impl critical_section::Impl for MyCriticalSection {
unsafe fn acquire() -> RawRestoreState { unsafe fn acquire() -> RawRestoreState {
let restore = get_interrupt_state(); let restore = get_supervisor_interrupt_state();
disable_interrupt(); disable_supervisor_interrupt();
restore restore
} }
unsafe fn release(token: RawRestoreState) { unsafe fn release(token: RawRestoreState) {
restore_interrupt(token); restore_supervisor_interrupt(token);
} }
} }

178
src/interrupt.rs Normal file
View File

@@ -0,0 +1,178 @@
use crate::{
boot::sbi::{TimeFID, EID},
clear_csr, generate_trap_handler, read_csr,
riscv::disable_interrupt,
set_csr,
time::{setup_next_timer_interrupt, IRQ_M_TIMER},
write_csr,
};
use core::arch::naked_asm;
use crate::time::{setup_timer_interrupt, timer_interrupt};
#[unsafe(no_mangle)]
unsafe extern "C" fn machine_trap_handler(mcause: u64, mie: u64, mip: u64) {
let mepc = read_csr!(mepc);
let mtval = read_csr!(mtval);
if mcause & (1 << 63) == 0 {
let message = match mcause & !(1 << 63) {
0 => "Instruction address misaligned",
1 => "Instruction access fault",
2 => "Illegal instruction",
3 => "Breakpoint",
4 => "Load address misaligned",
5 => "Load access fault",
6 => "Store/AMO address misaligned",
7 => "Store/AMO access fault",
8 => "Environment call from U-mode",
9 => {
// Environment call from S-mode
let eid: u64;
let fid: u64;
unsafe {
core::arch::asm!(
"mv {}, a7",
"mv {}, a6",
out(reg) eid,
out(reg) fid,
);
}
#[allow(clippy::single_match)]
match eid {
c if c == EID::Time as u64 => match fid {
c if c == TimeFID::SetTimer as u64 => {
clear_csr!(mip, 1 << 5);
setup_next_timer_interrupt();
}
_ => {}
},
_ => {}
}
// Advance mepc to exit the ecall
let mepc = read_csr!(mepc);
write_csr!(mepc, mepc + 4);
return;
}
11 => "Environment call from M-mode",
12 => "Instruction page fault",
13 => "Load page fault",
15 => "Store/AMO page fault",
16 => "Double trap",
18 => "Software check",
19 => "Hardware error",
_ => panic!(
"unknown exception, mcause={}, mie={}, mip={}",
mcause, mie, mip
),
};
disable_interrupt();
panic!("{} at PC=0x{:x}, mtval={}", message, mepc, mtval);
} else {
#[allow(clippy::single_match)]
match mcause & !(1 << 63) {
7 => {
setup_next_timer_interrupt();
set_csr!(mip, 1 << 5);
}
_ => {}
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn supervisor_trap_handler(scause: u64, _sie: u64, _sip: u64) {
#[allow(clippy::single_match)]
match scause & !(1 << 63) {
5 => {
unsafe {
core::arch::asm!(
"ecall",
in("a0") 0,
in("a6") TimeFID::SetTimer as u64,
in("a7") EID::Time as u64,
);
}
timer_interrupt();
}
_ => {}
}
}
pub unsafe fn setup_machine_trap_handler() {
write_csr!(mtvec, _machine_mode_trap);
set_csr!(mie, IRQ_M_TIMER);
}
pub unsafe fn setup_supervisor_trap_handler() {
write_csr!(stvec, _supervisor_mode_trap);
setup_timer_interrupt();
}
generate_trap_handler! {
_machine_mode_trap, machine_trap_handler, m
}
generate_trap_handler! {
_supervisor_mode_trap, supervisor_trap_handler, s
}
#[macro_export]
macro_rules! generate_trap_handler {
($name:ident, $jump_to:ident, $mode:ident) => {
#[unsafe(naked)]
#[unsafe(no_mangle)]
unsafe extern "C" fn $name() {
naked_asm!(
concat!("
addi sp, sp, -128
sd ra, 120(sp)
sd a0, 0(sp)
sd a1, 8(sp)
sd a2, 16(sp)
sd a3, 24(sp)
sd a4, 32(sp)
sd a5, 40(sp)
sd a6, 48(sp)
sd a7, 56(sp)
sd t0, 64(sp)
sd t1, 72(sp)
sd t2, 80(sp)
sd t3, 88(sp)
sd t4, 96(sp)
sd t5, 104(sp)
sd t6, 112(sp)
csrr a0, ", stringify!($mode),"cause
csrr a1, ", stringify!($mode),"ie
csrr a2, ", stringify!($mode),"ip
jal ", stringify!($jump_to), "
ld a0, 0(sp)
ld a1, 8(sp)
ld a2, 16(sp)
ld a3, 24(sp)
ld a4, 32(sp)
ld a5, 40(sp)
ld a6, 48(sp)
ld a7, 56(sp)
ld t0, 64(sp)
ld t1, 72(sp)
ld t2, 80(sp)
ld t3, 88(sp)
ld t4, 96(sp)
ld t5, 104(sp)
ld t6, 112(sp)
ld ra, 120(sp)
addi sp, sp, 128
", stringify!($mode),"ret")
)
}
};
}

View File

@@ -1,13 +1,16 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![allow(static_mut_refs)]
#![feature(riscv_ext_intrinsics)]
use core::arch::asm; use core::arch::riscv64::wfi;
use embedded_alloc::LlffHeap as Heap; use embedded_alloc::LlffHeap as Heap;
use log::info; use log::info;
use crate::{ use crate::{
io::init_log, io::init_log,
riscv::enable_supervisor_interrupt,
vga::{Color, Vga}, vga::{Color, Vga},
}; };
@@ -15,9 +18,11 @@ extern crate alloc;
mod boot; mod boot;
mod critical_section; mod critical_section;
mod interrupt;
mod io; mod io;
mod panic_handler; mod panic_handler;
mod riscv; mod riscv;
mod time;
mod uart; mod uart;
mod vga; mod vga;
@@ -26,17 +31,18 @@ pub const HEAP_SIZE: usize = 40960;
static HEAP: Heap = Heap::empty(); static HEAP: Heap = Heap::empty();
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn main() { pub extern "C" fn supervisor_mode_entry() {
unsafe { unsafe {
embedded_alloc::init!(HEAP, HEAP_SIZE); embedded_alloc::init!(HEAP, HEAP_SIZE);
init_log().unwrap();
Vga::init();
enable_supervisor_interrupt();
} }
init_log().unwrap();
Vga::init();
info!("Hello World !"); info!("Hello World !");
unsafe { Vga::draw_string(10, 10, "Hello World !", Color::WHITE) }; unsafe { Vga::draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
loop { loop {
unsafe { asm!("wfi") } unsafe { wfi() }
} }
} }

View File

@@ -1,31 +1,32 @@
use core::arch::asm; use core::arch::riscv64::wfi;
use alloc::{ use alloc::{format, string::ToString};
format,
string::{String, ToString},
};
use log::error; use log::error;
use crate::vga::{Color, FONT_HEIGHT, Vga}; use crate::vga::{Color, Vga, FONT_HEIGHT};
#[panic_handler] #[panic_handler]
fn panic(panic_info: &core::panic::PanicInfo) -> ! { fn panic(panic_info: &core::panic::PanicInfo) -> ! {
error!("PANIC !"); error!("PANIC !");
let mut panic_message = if let Some(message) = panic_info.message().as_str() { let mut panic_message = panic_info.message().to_string();
message.to_string()
} else {
String::new()
};
if let Some(location) = panic_info.location() { if let Some(location) = panic_info.location() {
panic_message = format!("{panic_message} at {}:{}", location.file(), location.line()); panic_message = format!("{panic_message} at {}:{}", location.file(), location.line());
} }
error!("{panic_message}"); error!("{panic_message}");
Vga::clear_screen(Color::WHITE); Vga::clear_screen(Color::WHITE);
unsafe { Vga::draw_string(0, 0, "PANIC !", Color::BLACK) }; unsafe { Vga::draw_string(0, 0, "PANIC !", Color::BLACK, Color::WHITE) };
unsafe { Vga::draw_string(0, FONT_HEIGHT as u16, panic_message, Color::BLACK) }; unsafe {
Vga::draw_string(
0,
FONT_HEIGHT as u16,
panic_message,
Color::BLACK,
Color::WHITE,
)
};
loop { loop {
unsafe { asm!("wfi") } unsafe { wfi() }
} }
} }

View File

@@ -1,17 +1,40 @@
use core::arch::asm; #![allow(unused)]
const MSTATUS_MIE: usize = 0x8; use core::arch::naked_asm;
use crate::clear_csr;
use crate::read_csr;
use crate::set_csr;
pub struct MStatus;
pub struct SStatus;
impl MStatus {
pub const MIE: usize = 1 << 3;
pub const MPIE: usize = 1 << 7;
}
impl SStatus {
pub const SIE: usize = 1 << 1;
pub const SPIE: usize = 1 << 5;
}
pub fn get_interrupt_state() -> bool { pub fn get_interrupt_state() -> bool {
let res: u64; (read_csr!(mstatus) & MStatus::MIE as u64) != 0
unsafe { asm!("csrr {}, mstatus", out(reg) res) }; }
(res & MSTATUS_MIE as u64) != 0 pub fn get_supervisor_interrupt_state() -> bool {
(read_csr!(sstatus) & SStatus::SIE as u64) != 0
} }
pub fn enable_interrupt() { pub fn enable_interrupt() {
unsafe { asm!("csrs mstatus, {}", in(reg) MSTATUS_MIE) }; set_csr!(mstatus, MStatus::MIE);
}
pub fn enable_supervisor_interrupt() {
set_csr!(sstatus, SStatus::SIE);
} }
pub fn disable_interrupt() { pub fn disable_interrupt() {
unsafe { asm!("csrc mstatus, {}", in(reg) MSTATUS_MIE) }; clear_csr!(mstatus, MStatus::MIE);
}
pub fn disable_supervisor_interrupt() {
clear_csr!(sstatus, SStatus::SIE);
} }
pub fn restore_interrupt(previous_state: bool) { pub fn restore_interrupt(previous_state: bool) {
if previous_state { if previous_state {
@@ -20,3 +43,59 @@ pub fn restore_interrupt(previous_state: bool) {
disable_interrupt(); disable_interrupt();
} }
} }
pub fn restore_supervisor_interrupt(previous_state: bool) {
if previous_state {
enable_supervisor_interrupt();
} else {
disable_supervisor_interrupt();
}
}
#[macro_export]
macro_rules! read_csr {
($name:ident) => {{
let res: u64;
unsafe { core::arch::asm!(concat!("csrr {}, ", stringify!($name)), out(reg) res, options(nomem, nostack)) };
res
}};
}
#[macro_export]
macro_rules! write_csr {
($name:ident, $value:expr) => {
let val = $value;
unsafe { core::arch::asm!(concat!("csrw ", stringify!($name), ", {}"), in(reg) val, options(nomem, nostack)) };
};
}
#[macro_export]
macro_rules! set_csr {
($name:ident, $value:expr) => {
let val = $value;
unsafe { core::arch::asm!(concat!("csrs ", stringify!($name), ", {}"), in(reg) val, options(nomem, nostack)) };
};
}
#[macro_export]
macro_rules! clear_csr {
($name:ident, $value:expr) => {
let val = $value;
unsafe { core::arch::asm!(concat!("csrc ", stringify!($name), ", {}"), in(reg) val, options(nomem, nostack)) };
};
}
#[macro_export]
macro_rules! mret {
() => {
unsafe { core::arch::asm!("mret", options(noreturn)) }
};
}
#[unsafe(naked)]
pub extern "C" fn exit_qemu() {
naked_asm!(
"
li a0, 0x100000
li a1, 0x5555
sw a1, 0(a0)
wfi
"
)
}

66
src/time.rs Normal file
View File

@@ -0,0 +1,66 @@
use core::time::Duration;
use alloc::format;
use crate::{
set_csr,
vga::{Color, Vga, FONT_WIDTH, WIDTH},
};
pub const IRQ_M_TIMER: u8 = 1 << 7;
pub const IRQ_S_TIMER: u8 = 1 << 5;
const CLINT_TIMER_CMP: *mut u64 = 0x02004000 as *mut u64;
const CLINT_TIMER: *const u64 = 0x0200bff8 as *const u64;
const TIMER_FREQUENCY: u64 = 10000000; // 10MHz
const INTERRUPT_FREQUENCY: u64 = 20; // 20Hz
static mut START_TIME: Instant = Instant(0);
pub fn setup_timer_interrupt() {
unsafe { START_TIME = Instant::now() };
set_csr!(sie, IRQ_S_TIMER);
setup_next_timer_interrupt();
}
pub fn setup_next_timer_interrupt() {
unsafe {
core::ptr::write_volatile(
CLINT_TIMER_CMP,
Instant::now().0 + TIMER_FREQUENCY / INTERRUPT_FREQUENCY,
);
}
}
pub fn timer_interrupt() {
let current_time = elapsed_time_since_startup();
let seconds = current_time.as_secs();
let minutes = seconds / 60 % 60;
let hours = seconds / 3600 % 60;
let seconds = seconds % 60;
let formated_time = format!("{:02}:{:02}:{:02}", hours, minutes, seconds);
unsafe {
Vga::draw_string(
(WIDTH - formated_time.len() * FONT_WIDTH) as u16,
0,
formated_time,
Color::WHITE,
Color::BLACK,
)
};
}
pub fn elapsed_time_since_startup() -> Duration {
unsafe { START_TIME.elapsed() }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Instant(u64);
impl Instant {
pub fn now() -> Self {
Instant(unsafe { core::ptr::read_volatile(CLINT_TIMER) })
}
pub fn elapsed(&self) -> Duration {
let now = Self::now();
Duration::from_nanos((now.0 - self.0) * (1_000_000_000 / TIMER_FREQUENCY))
}
}

View File

@@ -30,7 +30,7 @@ impl Color {
pub struct Vga {} pub struct Vga {}
impl Vga { impl Vga {
pub fn init() { pub unsafe fn init() {
for i in 0..32 { for i in 0..32 {
let addr = PCI_ECAM_BASE_ADDRESS.wrapping_byte_add(i << 11); let addr = PCI_ECAM_BASE_ADDRESS.wrapping_byte_add(i << 11);
let header = unsafe { core::ptr::read_volatile(addr) }; let header = unsafe { core::ptr::read_volatile(addr) };
@@ -72,35 +72,6 @@ impl Vga {
unsafe { *VGA_ADDRESS.add(pixel_index) = color } unsafe { *VGA_ADDRESS.add(pixel_index) = color }
} }
/// # Safety
/// `x` must be less than `WIDTH` and `y` must be less than `HEIGHT`
pub unsafe fn draw_char(x: u16, y: u16, c: char, color: Color) {
let c = if (c as u8 > b'~') || ((c as u8) < b' ') {
b'/' - b' '
} else {
c as u8 - b' '
};
// Get char position within font plate
let char_x = (c as usize % 32) * FONT_WIDTH;
let char_y = (c as usize / 32) * FONT_HEIGHT;
for i in 0..(FONT_WIDTH as u16) {
for j in 0..(FONT_HEIGHT as u16) {
let xx = x + i;
let yy = y + j;
if xx < (WIDTH as u16)
&& yy < (HEIGHT as u16)
&& unsafe { Self::font_plate_index(char_x as u16 + i, char_y as u16 + j) }
{
unsafe { Self::write_pixel_unsafe(xx, yy, color) }
}
}
}
}
#[allow(unused)]
pub unsafe fn draw_char_bg(x: u16, y: u16, c: char, color: Color, bg_color: Color) { pub unsafe fn draw_char_bg(x: u16, y: u16, c: char, color: Color, bg_color: Color) {
let c = if (c as u8 > b'~') || ((c as u8) < b' ') { let c = if (c as u8 > b'~') || ((c as u8) < b' ') {
b'/' - b' ' b'/' - b' '
@@ -130,7 +101,13 @@ impl Vga {
/// # Safety /// # Safety
/// The text must have a length that can fit within a `u16` /// The text must have a length that can fit within a `u16`
pub unsafe fn draw_string<T: AsRef<str>>(x: u16, mut y: u16, str: T, color: Color) { pub unsafe fn draw_string<T: AsRef<str>>(
x: u16,
mut y: u16,
str: T,
color: Color,
bg_color: Color,
) {
let mut current_x = x; let mut current_x = x;
str.as_ref().chars().for_each(|c| unsafe { str.as_ref().chars().for_each(|c| unsafe {
match c { match c {
@@ -142,7 +119,7 @@ impl Vga {
current_x = x; current_x = x;
} }
c => { c => {
Self::draw_char(current_x, y, c, color); Self::draw_char_bg(current_x, y, c, color, bg_color);
current_x += FONT_WIDTH as u16; current_x += FONT_WIDTH as u16;
} }
} }
@@ -169,4 +146,4 @@ pub const FONT_HEIGHT: usize = 13;
pub const FONTPLATE_WIDTH: usize = 32 * FONT_WIDTH; pub const FONTPLATE_WIDTH: usize = 32 * FONT_WIDTH;
pub const FONTPLATE_HEIGHT: usize = 3 * FONT_HEIGHT; pub const FONTPLATE_HEIGHT: usize = 3 * FONT_HEIGHT;
pub const FONTPLATE_SIZE: usize = FONTPLATE_WIDTH * FONTPLATE_HEIGHT / 8; pub const FONTPLATE_SIZE: usize = FONTPLATE_WIDTH * FONTPLATE_HEIGHT / 8;
pub const FONTPLATE: [u8; FONTPLATE_SIZE] = include_font_plate! {"assets/fontplate.png"}; pub static FONTPLATE: [u8; FONTPLATE_SIZE] = include_font_plate! {"assets/fontplate.png"};