Skip to main content

ax_cpu/loongarch64/
uspace.rs

1//! Structures and functions for user space.
2
3use core::ops::{Deref, DerefMut};
4
5use ax_memory_addr::VirtAddr;
6use loongArch64::register::{
7    badi, badv,
8    estat::{self, Exception, Trap},
9};
10
11pub use crate::uspace_common::{ExceptionKind, ReturnReason};
12use crate::{TrapFrame, trap::PageFaultFlags};
13
14/// Context to enter user space.
15#[derive(Debug, Clone, Copy)]
16#[repr(C)]
17pub struct UserContext(TrapFrame);
18
19impl UserContext {
20    /// Creates a new context with the given entry point, user stack pointer,
21    /// and the argument.
22    pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self {
23        let mut trap_frame = TrapFrame::default();
24        const PPLV_UMODE: usize = 0b11;
25        const PIE: usize = 1 << 2;
26        trap_frame.regs.sp = ustack_top.as_usize();
27        trap_frame.era = entry;
28        trap_frame.prmd = PPLV_UMODE | PIE;
29        trap_frame.regs.a0 = arg0;
30        Self(trap_frame)
31    }
32
33    /// Enter user space.
34    ///
35    /// It restores the user registers and jumps to the user entry point
36    /// (saved in `sepc`).
37    ///
38    /// This function returns when an exception or syscall occurs.
39    pub fn run(&mut self) -> ReturnReason {
40        extern "C" {
41            fn enter_user(uctx: &mut UserContext);
42        }
43
44        crate::asm::disable_irqs();
45        unsafe { enter_user(self) };
46
47        let estat = estat::read();
48        let badv = badv::read().vaddr();
49        let badi = badi::read().inst();
50
51        let ret = match estat.cause() {
52            Trap::Interrupt(_) => {
53                let irq_num: usize = estat.is().trailing_zeros() as usize;
54                crate::trap::irq_handler(irq_num);
55                ReturnReason::Interrupt
56            }
57            Trap::Exception(Exception::Syscall) => {
58                self.era += 4;
59                ReturnReason::Syscall
60            }
61            Trap::Exception(Exception::LoadPageFault)
62            | Trap::Exception(Exception::PageNonReadableFault) => {
63                ReturnReason::PageFault(va!(badv), PageFaultFlags::READ | PageFaultFlags::USER)
64            }
65            Trap::Exception(Exception::StorePageFault)
66            | Trap::Exception(Exception::PageModifyFault) => {
67                ReturnReason::PageFault(va!(badv), PageFaultFlags::WRITE | PageFaultFlags::USER)
68            }
69            Trap::Exception(Exception::FetchPageFault)
70            | Trap::Exception(Exception::PageNonExecutableFault) => {
71                ReturnReason::PageFault(va!(badv), PageFaultFlags::EXECUTE | PageFaultFlags::USER)
72            }
73            Trap::Exception(e) => ReturnReason::Exception(ExceptionInfo { e, badv, badi }),
74            _ => ReturnReason::Unknown,
75        };
76
77        crate::asm::enable_irqs();
78        ret
79    }
80}
81
82impl Deref for UserContext {
83    type Target = TrapFrame;
84
85    fn deref(&self) -> &Self::Target {
86        &self.0
87    }
88}
89
90impl DerefMut for UserContext {
91    fn deref_mut(&mut self) -> &mut Self::Target {
92        &mut self.0
93    }
94}
95
96/// Information about an exception that occurred in user space.
97#[derive(Debug, Clone, Copy)]
98pub struct ExceptionInfo {
99    /// The raw exception.
100    pub e: Exception,
101    /// The faulting address (from `badv`).
102    pub badv: usize,
103    /// The instruction causing the fault (from `badi`).
104    pub badi: u32,
105}
106
107impl ExceptionInfo {
108    /// Returns a generalized kind of this exception.
109    pub fn kind(&self) -> ExceptionKind {
110        match self.e {
111            Exception::Breakpoint => ExceptionKind::Breakpoint,
112            Exception::InstructionNotExist | Exception::InstructionPrivilegeIllegal => {
113                ExceptionKind::IllegalInstruction
114            }
115            Exception::AddressNotAligned => ExceptionKind::Misaligned,
116            _ => ExceptionKind::Other,
117        }
118    }
119}