Try loading code at runtime

This commit is contained in:
2026-02-13 17:22:23 +01:00
parent a53e11d6dd
commit 27a3847c13
28 changed files with 96 additions and 144 deletions

View File

@@ -1,49 +0,0 @@
use core::arch::naked_asm;
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);
set_csr!(medeleg, 1 << 8);
unsafe {
setup_supervisor_trap_handler();
};
mret!();
}

View File

@@ -1,11 +0,0 @@
#[non_exhaustive]
#[repr(usize)]
pub enum EextensionID {
Time = 0x54494D45,
}
#[non_exhaustive]
#[repr(usize)]
pub enum TimerFunctionID {
SetTimer = 0x0,
}

View File

@@ -1,18 +0,0 @@
use critical_section::RawRestoreState;
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_supervisor_interrupt_state();
disable_supervisor_interrupt();
restore
}
unsafe fn release(token: RawRestoreState) {
restore_supervisor_interrupt(token);
}
}

View File

@@ -1,324 +0,0 @@
use alloc::str;
use log::info;
use crate::{
boot::sbi::{EextensionID, TimerFunctionID},
clear_csr, generate_trap_handler,
process::{sleep, ExecutionContext},
read_csr,
riscv::disable_interrupt,
scheduler::scheduler,
set_csr,
syscall::SysCall,
time::{setup_next_timer_interrupt, IRQ_M_TIMER},
write_csr,
};
use core::{arch::naked_asm, time::Duration};
use crate::time::{setup_timer_interrupt, timer_interrupt};
#[unsafe(no_mangle)]
unsafe extern "C" fn machine_trap_handler(
mcause: u64,
mie: u64,
mip: u64,
interrupt_state: *const MachineInterruptState,
) {
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 = unsafe { (*interrupt_state).a[7] };
let fid = unsafe { (*interrupt_state).a[6] };
#[allow(clippy::single_match)]
match eid {
c if c == EextensionID::Time as usize => match fid {
c if c == TimerFunctionID::SetTimer as usize => {
clear_csr!(mip, 1 << 5);
// setup_next_timer_interrupt();
}
_ => {}
},
_ => panic!("Unhandled SBI call eid={eid}, fid={fid}"),
}
// 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(
interrupt_state: *mut ExecutionContext,
scause: u64,
_sie: u64,
_sip: u64,
) -> *const ExecutionContext {
if scause & (1 << 63) == 0 {
#[allow(clippy::single_match)]
match scause & !(1 << 63) {
8 => {
// Environment call from S-mode
let syscall_u64: u64 = unsafe { (*interrupt_state).a[0] };
let a1: u64 = unsafe { (*interrupt_state).a[1] };
let a2: u64 = unsafe { (*interrupt_state).a[2] };
let syscall: SysCall = syscall_u64.into();
match syscall {
SysCall::NanoSleep => sleep(Duration::new(a1, a2 as u32)),
SysCall::WriteTemp => {
info!("Print from user space : {}", unsafe {
str::from_raw_parts(a1 as *const u8, a2 as usize)
})
}
SysCall::Unimplemented => {
unimplemented!("Syscall {syscall_u64} is not implemented")
}
}
// Advance sepc to exit the ecall
unsafe {
(*interrupt_state).mepc = (*interrupt_state).mepc.byte_add(4);
}
}
_ => {}
}
} else {
#[allow(clippy::single_match)]
match scause & !(1 << 63) {
5 => {
unsafe {
core::arch::asm!(
"ecall",
in("a0") 0,
in("a6") TimerFunctionID::SetTimer as u64,
in("a7") EextensionID::Time as u64,
clobber_abi("system")
);
}
timer_interrupt();
return scheduler(unsafe { *interrupt_state });
}
_ => {}
}
}
interrupt_state
}
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();
}
#[unsafe(naked)]
#[unsafe(no_mangle)]
unsafe extern "C" fn _machine_mode_trap() {
naked_asm!(
"
addi sp, sp, -128
# Store the current frame
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, mcause
csrr a1, mie
csrr a2, mip
mv a3, sp
jal machine_trap_handler
# Restore registers
ld ra, 120(sp)
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)
addi sp, sp, 128
mret"
)
}
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!(
"
mv t0, sp
addi sp, sp, -264
# Store the current frame
sd ra, 0(sp)
sd t0, 8(sp) // sp
sd gp, 16(sp)
sd tp, 24(sp)
sd a0, 32(sp)
sd a1, 40(sp)
sd a2, 48(sp)
sd a3, 56(sp)
sd a4, 64(sp)
sd a5, 72(sp)
sd a6, 80(sp)
sd a7, 88(sp)
sd t0, 96(sp)
sd t1, 104(sp)
sd t2, 112(sp)
sd t3, 120(sp)
sd t4, 128(sp)
sd t5, 136(sp)
sd t6, 144(sp)
sd s0, 152(sp)
sd s1, 160(sp)
sd s2, 168(sp)
sd s3, 176(sp)
sd s4, 184(sp)
sd s5, 192(sp)
sd s6, 200(sp)
sd s7, 208(sp)
sd s8, 216(sp)
sd s9, 224(sp)
sd s10, 232(sp)
sd s11, 240(sp)
csrr t0, sepc
sd t0, 248(sp)
csrr t0, sstatus
sd t0, 256(sp)
mv a0, sp
csrr a1, ",
stringify!($mode),
"cause
csrr a2, ",
stringify!($mode),
"ie
csrr a3, ",
stringify!($mode),
"ip
jal ",
stringify!($jump_to),
"
# Restore registers
ld t0, 248(a0)
csrw sepc, t0
ld t0, 256(a0)
csrw sstatus, t0
ld ra, 0(a0)
ld sp, 8(a0)
ld gp, 16(a0)
ld tp, 24(a0)
ld a1, 40(a0)
ld a2, 48(a0)
ld a3, 56(a0)
ld a4, 64(a0)
ld a5, 72(a0)
ld a6, 80(a0)
ld a7, 88(a0)
ld t0, 96(a0)
ld t1, 104(a0)
ld t2, 112(a0)
ld t3, 120(a0)
ld t4, 128(a0)
ld t5, 136(a0)
ld t6, 144(a0)
ld s0, 152(a0)
ld s1, 160(a0)
ld s2, 168(a0)
ld s3, 176(a0)
ld s4, 184(a0)
ld s5, 192(a0)
ld s6, 200(a0)
ld s7, 208(a0)
ld s8, 216(a0)
ld s9, 224(a0)
ld s10, 232(a0)
ld s11, 240(a0)
ld a0, 32(a0)
",
stringify!($mode),
"ret"
))
}
};
}
#[repr(C)]
struct MachineInterruptState {
pub a: [usize; 8],
pub t: [usize; 7],
pub ra: usize,
}

View File

@@ -1,61 +0,0 @@
use crate::println;
use alloc::format;
use alloc::string::String;
use log::{Level, Metadata, Record};
use log::{LevelFilter, SetLoggerError};
use crate::uart::write_uart;
fn print(content: String) {
write_uart(content);
}
struct Logger;
impl log::Log for Logger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Info
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
if let Some((file, line)) = record.file().zip(record.line()) {
println!(
"[{}] at {}:{} - {}",
record.level(),
file,
line,
record.args()
);
} else {
println!("[{}] - {}", record.level(), record.args());
}
}
}
fn flush(&self) {}
}
static LOGGER: Logger = Logger;
pub fn init_log() -> Result<(), SetLoggerError> {
log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Info))
}
#[macro_export]
macro_rules! print {
($($args:expr),*) => {
$crate::io::print(format!($($args),*))
};
}
#[macro_export]
macro_rules! println {
() => {
$crate::print!("\n\r");
};
($($args:expr),*) => {
$crate::print!($($args),*);
$crate::println!();
};
}

View File

@@ -1,69 +0,0 @@
#![no_std]
#![no_main]
#![allow(static_mut_refs)]
#![feature(
riscv_ext_intrinsics,
const_trait_impl,
iter_map_windows,
str_from_raw_parts,
macro_metavar_expr,
macro_metavar_expr_concat
)]
use embedded_alloc::LlffHeap as Heap;
use log::info;
use crate::{
io::init_log,
process::create_process,
riscv::enable_supervisor_interrupt,
scheduler::{idle, scheduler_init},
tests_fat::MemoryDisk,
user::{proc2, test},
vga::{Color, Vga},
};
extern crate alloc;
mod boot;
mod critical_section;
mod interrupt;
mod io;
mod panic_handler;
mod process;
mod riscv;
mod scheduler;
mod syscall;
mod tests_fat;
mod time;
mod uart;
mod user;
mod vga;
pub const HEAP_SIZE: usize = 4096;
#[global_allocator]
static HEAP: Heap = Heap::empty();
// Usize is assumed to be an u64 in the whole kernel
const _: () = assert!(size_of::<usize>() == size_of::<u64>());
#[unsafe(no_mangle)]
pub extern "C" fn supervisor_mode_entry() {
unsafe {
embedded_alloc::init!(HEAP, HEAP_SIZE);
init_log().unwrap();
Vga::init();
scheduler_init();
// enable_supervisor_interrupt();
}
info!("Hello World !");
unsafe { Vga::draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
create_process(test, "proc1");
create_process(proc2, "proc2");
MemoryDisk::new();
enable_supervisor_interrupt();
idle();
}

View File

@@ -1,32 +0,0 @@
use core::arch::riscv64::wfi;
use alloc::{format, string::ToString};
use log::error;
use crate::vga::{Color, Vga, FONT_HEIGHT};
#[panic_handler]
fn panic(panic_info: &core::panic::PanicInfo) -> ! {
error!("PANIC !");
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, Color::WHITE) };
unsafe {
Vga::draw_string(
0,
FONT_HEIGHT as u16,
panic_message,
Color::BLACK,
Color::WHITE,
)
};
loop {
unsafe { wfi() }
}
}

View File

@@ -1,107 +0,0 @@
use core::{arch::riscv64::wfi, time::Duration};
use alloc::{format, string::String};
use crate::{
scheduler::{ACTIVE_PID, PROCESS_COUNT, PROCESS_TABLE},
time::elapsed_time_since_startup,
};
const STACK_SIZE: usize = 4096;
#[derive(Debug, PartialEq, Eq)]
pub enum ProcessState {
Active,
Activable,
Dead,
Asleep,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ExecutionContext {
pub ra: *const u64,
pub sp: *const u64,
pub gp: u64,
pub tp: u64,
pub a: [u64; 8],
pub t: [u64; 7],
pub s: [u64; 12],
pub mepc: *const u64,
pub mstatus: u64,
}
pub struct Process {
pub pid: i64,
pub name: String,
pub state: ProcessState,
pub entry: Option<fn()>,
pub wake_time: Duration,
pub ctx: ExecutionContext,
pub stack: [u64; STACK_SIZE],
}
impl core::fmt::Debug for Process {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Process")
.field("pid", &self.pid)
.field("name", &self.name)
.field("state", &self.state)
.field("wake_time", &self.wake_time)
.field("ctx", &self.ctx)
.field("stack", &format!("[_; {}]", STACK_SIZE))
.finish()
}
}
pub fn create_process<T: Into<String>>(code: fn(), name: T) -> i64 {
let mut next_pid = 0;
while next_pid < PROCESS_COUNT && unsafe { PROCESS_TABLE[next_pid].state != ProcessState::Dead }
{
next_pid += 1;
}
if next_pid >= PROCESS_COUNT {
return -1;
}
unsafe {
PROCESS_TABLE[next_pid].pid = next_pid as i64;
PROCESS_TABLE[next_pid].name = name.into();
PROCESS_TABLE[next_pid].state = ProcessState::Activable;
PROCESS_TABLE[next_pid].entry = Some(code);
PROCESS_TABLE[next_pid].ctx.a[0] =
PROCESS_TABLE[next_pid].entry.as_ref().unwrap_unchecked() as *const fn() as u64;
PROCESS_TABLE[next_pid].ctx.mepc = process_launcher as *const _;
PROCESS_TABLE[next_pid].ctx.mstatus = 1 << 1 | 1 << 5;
PROCESS_TABLE[next_pid].ctx.sp = &raw const PROCESS_TABLE[next_pid].stack[STACK_SIZE - 1];
}
next_pid as i64
}
extern "C" fn process_launcher(code: *const fn()) {
unsafe { (*code)() };
terminate_process();
}
fn terminate_process() {
unsafe {
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Dead;
}
unsafe {
wfi();
}
// scheduler();
}
pub fn sleep(duration: Duration) {
unsafe {
PROCESS_TABLE[ACTIVE_PID].wake_time = elapsed_time_since_startup() + duration;
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Asleep;
}
unsafe {
wfi();
}
// scheduler();
}

View File

@@ -1,101 +0,0 @@
#![allow(unused)]
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 {
(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() {
set_csr!(mstatus, MStatus::MIE);
}
pub fn enable_supervisor_interrupt() {
set_csr!(sstatus, SStatus::SIE);
}
pub fn disable_interrupt() {
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 {
enable_interrupt();
} else {
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
"
)
}

View File

@@ -1,85 +0,0 @@
use core::{arch::riscv64::wfi, array, cell::LazyCell, time::Duration};
use alloc::string::String;
use log::info;
use crate::{
process::{create_process, ExecutionContext, Process, ProcessState},
time,
};
pub const PROCESS_COUNT: usize = 16;
pub static mut ACTIVE_PID: usize = 0;
pub static mut PROCESS_TABLE: LazyCell<[Process; PROCESS_COUNT]> = LazyCell::new(|| {
array::from_fn(|_| Process {
pid: -1,
name: String::new(),
state: ProcessState::Dead,
wake_time: Duration::new(0, 0),
ctx: ExecutionContext {
ra: core::ptr::null(),
sp: core::ptr::null(),
gp: 0,
tp: 0,
a: [0; _],
t: [0; _],
s: [0; _],
mepc: core::ptr::null(),
mstatus: 0,
},
stack: [0; _],
entry: None,
})
});
pub fn idle() {
loop {
// write_string_temp("idle");
// info!("idle");
unsafe {
wfi();
}
}
}
pub fn scheduler_init() {
info!("scheduler init");
for pid in 0..PROCESS_COUNT {
unsafe {
PROCESS_TABLE[pid].state = ProcessState::Dead;
}
}
create_process(idle, "idle");
unsafe {
PROCESS_TABLE[0].state = ProcessState::Active;
}
}
pub fn scheduler(interrupt_state: ExecutionContext) -> *const ExecutionContext {
// info!("scheduler");
unsafe {
let prev_pid = ACTIVE_PID;
PROCESS_TABLE[prev_pid].ctx = interrupt_state;
if PROCESS_TABLE[prev_pid].state == ProcessState::Active {
PROCESS_TABLE[prev_pid].state = ProcessState::Activable;
}
loop {
if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Asleep
&& time::elapsed_time_since_startup() > PROCESS_TABLE[ACTIVE_PID].wake_time
{
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Activable;
}
ACTIVE_PID = (ACTIVE_PID + 1) % PROCESS_COUNT;
if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Activable {
break;
}
}
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Active;
&raw const PROCESS_TABLE[ACTIVE_PID].ctx
}
}

View File

@@ -1,91 +0,0 @@
use core::time::Duration;
#[repr(u64)]
pub enum SysCall {
NanoSleep = 101,
WriteTemp = 999,
Unimplemented = 1 << 31,
}
impl From<u64> for SysCall {
fn from(value: u64) -> Self {
match value {
101 => SysCall::NanoSleep,
999 => SysCall::WriteTemp,
_ => SysCall::Unimplemented,
}
}
}
#[allow(clippy::too_many_arguments)]
unsafe fn _syscall(
syscall: SysCall,
mut a1: u64,
mut a2: u64,
mut a3: u64,
mut a4: u64,
mut a5: u64,
mut a6: u64,
mut a7: u64,
) -> (u64, u64, u64, u64, u64, u64, u64, u64) {
let mut a0 = syscall as u64;
unsafe {
core::arch::asm!(
"ecall",
inlateout("a0") a0,
inlateout("a1") a1,
inlateout("a2") a2,
inlateout("a3") a3,
inlateout("a4") a4,
inlateout("a5") a5,
inlateout("a6") a6,
inlateout("a7") a7,
clobber_abi("system")
);
}
(a0, a1, a2, a3, a4, a5, a6, a7)
}
macro_rules! syscall {
($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr, $a7:expr) => {
_syscall($syscall, $a1, $a2, $a3, $a4, $a5, $a6, $a7)
};
($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
syscall!($syscall, $a1, $a2, $a3, $a4, $a5, $a6, 0)
};
($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
syscall!($syscall, $a1, $a2, $a3, $a4, $a5, 0)
};
($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
syscall!($syscall, $a1, $a2, $a3, $a4, 0)
};
($syscall:expr, $a1:expr, $a2:expr, $a3:expr) => {
syscall!($syscall, $a1, $a2, $a3, 0)
};
($syscall:expr, $a1:expr, $a2:expr) => {
syscall!($syscall, $a1, $a2, 0)
};
($syscall:expr, $a1:expr) => {
syscall!($syscall, $a1, 0)
};
($syscall:expr) => {
syscall!($syscall, 0)
};
}
pub fn sleep(duration: Duration) {
unsafe {
let (duration_secs, duration_nanos) = (duration.as_secs(), duration.subsec_nanos() as u64);
syscall!(SysCall::NanoSleep, duration_secs, duration_nanos);
}
}
pub fn write_string_temp(content: &'static str) {
unsafe {
syscall!(
SysCall::WriteTemp,
content.as_ptr() as u64,
content.len() as u64
);
}
}

View File

@@ -1,14 +0,0 @@
use core::str;
use log::info;
const DISK_ADDR: usize = 0x9000_0000;
const DISK_SIZE: usize = 16 * 1024 * 1024; // 16MB
pub struct MemoryDisk {}
impl MemoryDisk {
pub fn new() {
let test = unsafe { str::from_raw_parts(DISK_ADDR as *const u8, 13) };
info!("{}", test);
}
}

View File

@@ -1,66 +0,0 @@
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

@@ -1,15 +0,0 @@
const UART_BASE: *mut u8 = 0x10000000 as *mut _;
pub fn write_char_uart(c: char) {
while unsafe { core::ptr::read_volatile(UART_BASE.byte_add(0x5)) } >> 5 & 1 == 0 {}
unsafe { core::ptr::write_volatile(UART_BASE, c as u8) };
}
pub fn write_uart<T: AsRef<str>>(print: T) {
print.as_ref().chars().for_each(|a| {
// Add \r if needed
write_char_uart(a);
if a == '\n' {
write_char_uart('\r');
}
});
}

View File

@@ -1,17 +0,0 @@
use core::time::Duration;
use crate::syscall::{sleep, write_string_temp};
pub fn test() {
loop {
write_string_temp("test");
sleep(Duration::new(2, 0));
}
}
pub fn proc2() {
loop {
write_string_temp("proc2");
sleep(Duration::new(3, 0));
}
}

View File

@@ -1,149 +0,0 @@
use kernel_macros::include_font_plate;
use log::info;
const PCI_ECAM_BASE_ADDRESS: *mut u32 = 0x30000000 as *mut _;
const BOCHS_DISPLAY_BASE_ADDRESS: *mut u32 = 0x50000000 as *mut _;
const BOCHS_CONFIG_BASE_ADDRESS: *mut u16 = 0x40000000 as *mut _;
pub const VGA_ADDRESS: *mut Color = BOCHS_DISPLAY_BASE_ADDRESS as *mut Color;
pub const WIDTH: usize = 1600;
pub const HEIGHT: usize = 900;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Color(u32);
#[allow(unused)]
impl Color {
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self((r as u32) << 16 | (g as u32) << 8 | b as u32)
}
pub const WHITE: Color = Color::from_rgb(255, 255, 255);
pub const GRAY: Color = Color::from_rgb(128, 128, 128);
pub const LIGHT_GRAY: Color = Color::from_rgb(128, 64, 64);
pub const BLACK: Color = Color::from_rgb(0, 0, 0);
pub const RED: Color = Color::from_rgb(255, 0, 0);
pub const GREEN: Color = Color::from_rgb(0, 255, 0);
pub const BLUE: Color = Color::from_rgb(0, 0, 255);
}
pub struct Vga {}
impl Vga {
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) };
if header >> 16 == 0x1111 && header & 0xFFFF == 0x1234 {
info!("VGA Bochs PCI found");
unsafe {
let mut command = core::ptr::read_volatile(addr.byte_add(0x04));
command |= 0b111;
core::ptr::write_volatile(addr.byte_add(0x04), command);
core::ptr::write_volatile(
addr.byte_add(0x10),
BOCHS_DISPLAY_BASE_ADDRESS as u32,
);
core::ptr::write_volatile(addr.byte_add(0x18), BOCHS_CONFIG_BASE_ADDRESS as u32)
};
break;
}
}
unsafe {
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x508), 0x0);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x502), WIDTH as u16);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x504), HEIGHT as u16);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x506), 32);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x50a), 0x0);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x510), 0x0);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x512), 0x0);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x508), 0x41)
};
Vga::clear_screen(Color::BLACK);
}
/// # Safety
/// `x` must be less than `WIDTH` and `y` must be less than `HEIGHT`
pub unsafe fn write_pixel_unsafe(x: u16, y: u16, color: Color) {
let pixel_index = x as usize + y as usize * WIDTH;
unsafe { *VGA_ADDRESS.add(pixel_index) = 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' ') {
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) {
if unsafe { Self::font_plate_index(char_x as u16 + i, char_y as u16 + j) } {
unsafe { Self::write_pixel_unsafe(xx, yy, color) }
} else {
unsafe { Self::write_pixel_unsafe(xx, yy, bg_color) }
}
}
}
}
}
/// # 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,
bg_color: Color,
) {
let mut current_x = x;
str.as_ref().chars().for_each(|c| unsafe {
match c {
'\n' => {
current_x = x;
y += FONT_HEIGHT as u16;
}
'\r' => {
current_x = x;
}
c => {
Self::draw_char_bg(current_x, y, c, color, bg_color);
current_x += FONT_WIDTH as u16;
}
}
});
}
pub fn clear_screen(color: Color) {
for i in 0..WIDTH * HEIGHT {
unsafe { *VGA_ADDRESS.add(i) = color }
}
}
unsafe fn font_plate_index(x: u16, y: u16) -> bool {
let pixel_index = (y as usize) * FONTPLATE_WIDTH + (x as usize);
let byte_index = pixel_index / 8;
let bit_index = pixel_index % 8;
(FONTPLATE[byte_index] >> bit_index) & 0b1 == 0b1
}
}
pub const FONT_WIDTH: usize = 6;
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 static FONTPLATE: [u8; FONTPLATE_SIZE] = include_font_plate! {"assets/fontplate.png"};