Supervisor mode
This commit is contained in:
3
.gdbinit
3
.gdbinit
@@ -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
59
ilm.ld
@@ -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 = .;
|
|
||||||
}
|
}
|
||||||
|
|||||||
57
src/boot.rs
57
src/boot.rs
@@ -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
11
src/boot/sbi.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#[non_exhaustive]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum EID {
|
||||||
|
Time = 0x54494D45,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum TimeFID {
|
||||||
|
SetTimer = 0x0,
|
||||||
|
}
|
||||||
@@ -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
178
src/interrupt.rs
Normal 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")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
18
src/main.rs
18
src/main.rs
@@ -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() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/riscv.rs
93
src/riscv.rs
@@ -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
66
src/time.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/vga.rs
43
src/vga.rs
@@ -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"};
|
||||||
|
|||||||
Reference in New Issue
Block a user