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
break machine_mode_entry
c

59
ilm.ld
View File

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

View File

@@ -1,11 +1,48 @@
use core::arch::global_asm;
use core::arch::naked_asm;
global_asm!(
".section .text.entry
.globl entry
entry:
la sp, _heap_end
jal main
loop:
j loop"
);
use crate::{
clear_csr,
interrupt::{setup_machine_trap_handler, setup_supervisor_trap_handler},
mret, set_csr, supervisor_mode_entry, write_csr,
};
pub mod sbi;
#[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 crate::riscv::{disable_interrupt, get_interrupt_state, restore_interrupt};
use crate::riscv::{disable_supervisor_interrupt, get_supervisor_interrupt_state, restore_supervisor_interrupt};
struct MyCriticalSection;
critical_section::set_impl!(MyCriticalSection);
unsafe impl critical_section::Impl for MyCriticalSection {
unsafe fn acquire() -> RawRestoreState {
let restore = get_interrupt_state();
disable_interrupt();
let restore = get_supervisor_interrupt_state();
disable_supervisor_interrupt();
restore
}
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_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 log::info;
use crate::{
io::init_log,
riscv::enable_supervisor_interrupt,
vga::{Color, Vga},
};
@@ -15,9 +18,11 @@ extern crate alloc;
mod boot;
mod critical_section;
mod interrupt;
mod io;
mod panic_handler;
mod riscv;
mod time;
mod uart;
mod vga;
@@ -26,17 +31,18 @@ pub const HEAP_SIZE: usize = 40960;
static HEAP: Heap = Heap::empty();
#[unsafe(no_mangle)]
pub extern "C" fn main() {
pub extern "C" fn supervisor_mode_entry() {
unsafe {
embedded_alloc::init!(HEAP, HEAP_SIZE);
init_log().unwrap();
Vga::init();
enable_supervisor_interrupt();
}
init_log().unwrap();
Vga::init();
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 {
unsafe { asm!("wfi") }
unsafe { wfi() }
}
}

View File

@@ -1,31 +1,32 @@
use core::arch::asm;
use core::arch::riscv64::wfi;
use alloc::{
format,
string::{String, ToString},
};
use alloc::{format, string::ToString};
use log::error;
use crate::vga::{Color, FONT_HEIGHT, Vga};
use crate::vga::{Color, Vga, FONT_HEIGHT};
#[panic_handler]
fn panic(panic_info: &core::panic::PanicInfo) -> ! {
error!("PANIC !");
let mut panic_message = if let Some(message) = panic_info.message().as_str() {
message.to_string()
} else {
String::new()
};
let mut panic_message = panic_info.message().to_string();
if let Some(location) = panic_info.location() {
panic_message = format!("{panic_message} at {}:{}", location.file(), location.line());
}
error!("{panic_message}");
Vga::clear_screen(Color::WHITE);
unsafe { Vga::draw_string(0, 0, "PANIC !", Color::BLACK) };
unsafe { Vga::draw_string(0, FONT_HEIGHT as u16, panic_message, 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,
Color::WHITE,
)
};
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 {
let res: u64;
unsafe { asm!("csrr {}, mstatus", out(reg) res) };
(res & MSTATUS_MIE as u64) != 0
(read_csr!(mstatus) & MStatus::MIE as u64) != 0
}
pub fn get_supervisor_interrupt_state() -> bool {
(read_csr!(sstatus) & SStatus::SIE as u64) != 0
}
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() {
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) {
if previous_state {
@@ -20,3 +43,59 @@ pub fn restore_interrupt(previous_state: bool) {
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 {}
impl Vga {
pub fn init() {
pub unsafe fn init() {
for i in 0..32 {
let addr = PCI_ECAM_BASE_ADDRESS.wrapping_byte_add(i << 11);
let header = unsafe { core::ptr::read_volatile(addr) };
@@ -72,35 +72,6 @@ impl Vga {
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) {
let c = if (c as u8 > b'~') || ((c as u8) < b' ') {
b'/' - b' '
@@ -130,7 +101,13 @@ impl Vga {
/// # Safety
/// 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;
str.as_ref().chars().for_each(|c| unsafe {
match c {
@@ -142,7 +119,7 @@ impl Vga {
current_x = x;
}
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;
}
}
@@ -169,4 +146,4 @@ pub const FONT_HEIGHT: usize = 13;
pub const FONTPLATE_WIDTH: usize = 32 * FONT_WIDTH;
pub const FONTPLATE_HEIGHT: usize = 3 * FONT_HEIGHT;
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"};