#![no_std]
#![deny(missing_docs)]
#[cfg(feature = "foreign")]
pub mod foreign;
#[derive(Clone)]
#[repr(C)]
pub struct LocalContext {
sctx: usize,
x: [usize; 31],
sepc: usize,
pub supervisor: bool,
pub interrupt: bool,
}
impl LocalContext {
#[inline]
pub const fn empty() -> Self {
Self {
sctx: 0,
x: [0; 31],
supervisor: false,
interrupt: false,
sepc: 0,
}
}
#[inline]
pub const fn user(pc: usize) -> Self {
Self {
sctx: 0,
x: [0; 31],
supervisor: false,
interrupt: true,
sepc: pc,
}
}
#[inline]
pub const fn thread(pc: usize, interrupt: bool) -> Self {
Self {
sctx: 0,
x: [0; 31],
supervisor: true,
interrupt,
sepc: pc,
}
}
#[inline]
pub fn x(&self, n: usize) -> usize {
self.x[n - 1]
}
#[inline]
pub fn x_mut(&mut self, n: usize) -> &mut usize {
&mut self.x[n - 1]
}
#[inline]
pub fn a(&self, n: usize) -> usize {
self.x(n + 10)
}
#[inline]
pub fn a_mut(&mut self, n: usize) -> &mut usize {
self.x_mut(n + 10)
}
#[inline]
pub fn ra(&self) -> usize {
self.x(1)
}
#[inline]
pub fn sp(&self) -> usize {
self.x(2)
}
#[inline]
pub fn sp_mut(&mut self) -> &mut usize {
self.x_mut(2)
}
#[inline]
pub fn pc(&self) -> usize {
self.sepc
}
#[inline]
pub fn pc_mut(&mut self) -> &mut usize {
&mut self.sepc
}
#[inline]
pub fn move_next(&mut self) {
self.sepc = self.sepc.wrapping_add(4);
}
#[inline(never)]
pub unsafe fn execute(&mut self) -> usize {
#[cfg(target_arch = "riscv64")]
{
let mut sstatus = build_sstatus(self.supervisor, self.interrupt);
let ctx_ptr = self as *mut Self;
let mut sepc = self.sepc;
let old_sscratch: usize;
core::arch::asm!(
" csrrw {old_ss}, sscratch, {ctx}
csrw sepc , {sepc}
csrw sstatus , {sstatus}
addi sp, sp, -8
sd ra, (sp)
call {execute_naked}
ld ra, (sp)
addi sp, sp, 8
csrw sscratch, {old_ss}
csrr {sepc} , sepc
csrr {sstatus}, sstatus
",
ctx = in (reg) ctx_ptr,
old_ss = out (reg) old_sscratch,
sepc = inlateout(reg) sepc,
sstatus = inlateout(reg) sstatus,
execute_naked = sym execute_naked,
);
let _ = old_sscratch; (*ctx_ptr).sepc = sepc;
sstatus
}
#[cfg(not(target_arch = "riscv64"))]
unimplemented!("LocalContext::execute() is only supported on riscv64")
}
}
#[cfg(target_arch = "riscv64")]
#[inline]
fn build_sstatus(supervisor: bool, interrupt: bool) -> usize {
let mut sstatus: usize;
unsafe { core::arch::asm!("csrr {}, sstatus", out(reg) sstatus) };
const PREVILEGE_BIT: usize = 1 << 8;
const INTERRUPT_BIT: usize = 1 << 5;
match supervisor {
false => sstatus &= !PREVILEGE_BIT,
true => sstatus |= PREVILEGE_BIT,
}
match interrupt {
false => sstatus &= !INTERRUPT_BIT,
true => sstatus |= INTERRUPT_BIT,
}
sstatus
}
#[cfg(not(target_arch = "riscv64"))]
#[allow(dead_code)]
#[inline]
fn build_sstatus(_supervisor: bool, _interrupt: bool) -> usize {
unimplemented!("build_sstatus() is only supported on riscv64")
}
#[cfg(target_arch = "riscv64")]
#[unsafe(naked)]
unsafe extern "C" fn execute_naked() {
core::arch::naked_asm!(
r" .altmacro
.macro SAVE n
sd x\n, \n*8(sp)
.endm
.macro SAVE_ALL
sd x1, 1*8(sp)
.set n, 3
.rept 29
SAVE %n
.set n, n+1
.endr
.endm
.macro LOAD n
ld x\n, \n*8(sp)
.endm
.macro LOAD_ALL
ld x1, 1*8(sp)
.set n, 3
.rept 29
LOAD %n
.set n, n+1
.endr
.endm
",
" .option push
.option nopic
",
" addi sp, sp, -32*8
SAVE_ALL
",
" la t0, 1f
csrw stvec, t0
",
" csrr t0, sscratch
sd sp, (t0)
mv sp, t0
",
" LOAD_ALL
ld sp, 2*8(sp)
",
" sret",
" .align 2",
"1: csrrw sp, sscratch, sp",
" SAVE_ALL
csrrw t0, sscratch, sp
sd t0, 2*8(sp)
",
" ld sp, (sp)",
" LOAD_ALL
addi sp, sp, 32*8
",
" ret",
" .option pop",
)
}
#[cfg(not(target_arch = "riscv64"))]
#[allow(dead_code)]
unsafe extern "C" fn execute_naked() {
unimplemented!("execute_naked() is only supported on riscv64")
}