Supervisor mode
This commit is contained in:
3
.gdbinit
3
.gdbinit
@@ -1 +1,4 @@
|
||||
file target/riscv64/debug/kernel-rust
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
57
src/boot.rs
57
src/boot.rs
@@ -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
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 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
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_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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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
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 {}
|
||||
|
||||
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"};
|
||||
|
||||
Reference in New Issue
Block a user