use core::ops::{Deref, DerefMut};
use ax_memory_addr::VirtAddr;
#[cfg(feature = "fp-simd")]
use riscv::register::sstatus::FS;
use riscv::{
interrupt::{
Trap,
supervisor::{Exception as E, Interrupt as I},
},
register::{scause, sstatus::Sstatus, stval},
};
pub use crate::uspace_common::{ExceptionKind, ReturnReason};
use crate::{GeneralRegisters, TrapFrame, trap::PageFaultFlags};
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct UserContext(TrapFrame);
impl UserContext {
pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self {
let mut sstatus = Sstatus::from_bits(0);
sstatus.set_spie(true); sstatus.set_sum(true); #[cfg(feature = "fp-simd")]
sstatus.set_fs(FS::Initial);
#[cfg(feature = "xuantie-c9xx")]
{
const SSTATUS_VS_INITIAL: usize = 0x1 << 9;
const XTHEAD_LEGACY_VS_MASK: usize = 0x3 << 23;
Self::set_sstatus(
&mut sstatus,
SSTATUS_VS_INITIAL | XTHEAD_LEGACY_VS_MASK,
false,
);
}
Self(TrapFrame {
regs: GeneralRegisters {
a0: arg0,
sp: ustack_top.as_usize(),
..Default::default()
},
sepc: entry,
sstatus,
})
}
pub fn prepare_clone_child_return_state(&mut self) {
self.0.sstatus.set_spie(true);
self.0.sstatus.set_sum(true);
#[cfg(feature = "fp-simd")]
if matches!(self.0.sstatus.fs(), FS::Off) {
self.0.sstatus.set_fs(FS::Initial);
}
}
pub fn run(&mut self) -> ReturnReason {
unsafe extern "C" {
fn enter_user(uctx: &mut UserContext);
}
riscv::asm::fence_i();
crate::asm::disable_irqs();
unsafe { enter_user(self) };
let scause = scause::read();
let ret = if let Ok(cause) = scause.cause().try_into::<I, E>() {
let stval = stval::read();
match cause {
Trap::Interrupt(_) => {
crate::trap::irq_handler(scause.bits());
ReturnReason::Interrupt
}
Trap::Exception(E::UserEnvCall) => {
self.sepc += 4;
ReturnReason::Syscall
}
Trap::Exception(E::LoadPageFault) => {
ReturnReason::PageFault(va!(stval), PageFaultFlags::READ | PageFaultFlags::USER)
}
Trap::Exception(E::StorePageFault) => ReturnReason::PageFault(
va!(stval),
PageFaultFlags::WRITE | PageFaultFlags::USER,
),
Trap::Exception(E::InstructionPageFault) => ReturnReason::PageFault(
va!(stval),
PageFaultFlags::EXECUTE | PageFaultFlags::USER,
),
Trap::Exception(e) => ReturnReason::Exception(ExceptionInfo { e, stval }),
}
} else {
ReturnReason::Unknown
};
crate::asm::enable_irqs();
ret
}
pub fn set_sstatus(sstatus: &mut Sstatus, bits: usize, is_clear: bool) {
if bits == 0 {
log::error!("Invalid parameter: {:x}", bits);
return;
}
unsafe {
let sstatus_ptr = sstatus as *mut Sstatus as *mut usize;
if is_clear {
*sstatus_ptr &= !bits;
} else {
*sstatus_ptr |= bits;
}
}
}
}
impl Deref for UserContext {
type Target = TrapFrame;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for UserContext {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Debug, Clone, Copy)]
pub struct ExceptionInfo {
pub e: E,
pub stval: usize,
}
impl ExceptionInfo {
pub fn kind(&self) -> ExceptionKind {
match self.e {
E::Breakpoint => ExceptionKind::Breakpoint,
E::IllegalInstruction => ExceptionKind::IllegalInstruction,
E::InstructionMisaligned | E::LoadMisaligned | E::StoreMisaligned => {
ExceptionKind::Misaligned
}
_ => ExceptionKind::Other,
}
}
}