Skip to main content

ax_cpu/aarch64/
uspace.rs

1//! Structures and functions for user space.
2
3use core::ops::{Deref, DerefMut};
4
5use aarch64_cpu::registers::{ESR_EL1, FAR_EL1, Readable};
6use ax_memory_addr::VirtAddr;
7use tock_registers::LocalRegisterCopy;
8
9use super::trap::{TrapKind, is_valid_page_fault};
10pub use crate::uspace_common::{ExceptionKind, ReturnReason};
11use crate::{TrapFrame, trap::PageFaultFlags};
12
13/// Context to enter user space.
14#[repr(C, align(16))]
15#[derive(Debug, Clone, Copy)]
16pub struct UserContext {
17    tf: TrapFrame,
18    /// Stack Pointer (SP_EL0).
19    pub sp: u64,
20    /// Software Thread ID Register (TPIDR_EL0).
21    pub tpidr: u64,
22}
23
24impl UserContext {
25    const PAD_MAGIC: u64 = 0x1234_5678_9abc_def0;
26    /// Creates a new context with the given entry point, user stack pointer,
27    /// and the argument.
28    pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self {
29        use aarch64_cpu::registers::SPSR_EL1;
30        let mut regs = [0; 31];
31        regs[0] = arg0 as _;
32        Self {
33            tf: TrapFrame {
34                x: regs,
35                elr: entry as _,
36                spsr: (SPSR_EL1::M::EL0t
37                    + SPSR_EL1::D::Masked
38                    + SPSR_EL1::A::Masked
39                    + SPSR_EL1::I::Unmasked
40                    + SPSR_EL1::F::Masked)
41                    .value,
42                __pad: Self::PAD_MAGIC,
43            },
44            sp: ustack_top.as_usize() as _,
45            tpidr: 0,
46        }
47    }
48
49    /// Normalizes a cloned user context so it can safely return to EL0.
50    pub fn prepare_clone_child_return_state(&mut self) {
51        use aarch64_cpu::registers::SPSR_EL1;
52
53        self.tf.spsr = (self.tf.spsr
54            & !(SPSR_EL1::M.mask
55                | SPSR_EL1::D.mask
56                | SPSR_EL1::A.mask
57                | SPSR_EL1::I.mask
58                | SPSR_EL1::F.mask))
59            | (SPSR_EL1::M::EL0t
60                + SPSR_EL1::D::Masked
61                + SPSR_EL1::A::Masked
62                + SPSR_EL1::I::Unmasked
63                + SPSR_EL1::F::Masked)
64                .value;
65    }
66
67    /// Gets the stack pointer.
68    pub const fn sp(&self) -> usize {
69        self.sp as _
70    }
71
72    /// Sets the stack pointer.
73    pub const fn set_sp(&mut self, sp: usize) {
74        self.sp = sp as _;
75    }
76
77    /// Gets the TLS area.
78    pub const fn tls(&self) -> usize {
79        self.tpidr as _
80    }
81
82    /// Sets the TLS area.
83    pub const fn set_tls(&mut self, tls: usize) {
84        self.tpidr = tls as _;
85    }
86
87    /// Enters user space.
88    ///
89    /// It restores the user registers and jumps to the user entry point
90    /// (saved in `elr`).
91    ///
92    /// This function returns when an exception or syscall occurs.
93    pub fn run(&mut self) -> ReturnReason {
94        unsafe extern "C" {
95            fn enter_user(uctx: &mut UserContext) -> TrapKind;
96        }
97
98        crate::asm::disable_irqs();
99        let kind = unsafe { enter_user(self) };
100
101        let ret = match kind {
102            TrapKind::Irq => {
103                crate::trap::irq_handler(0);
104                ReturnReason::Interrupt
105            }
106            TrapKind::Fiq | TrapKind::SError => ReturnReason::Unknown,
107            TrapKind::Synchronous => {
108                let esr = ESR_EL1.extract();
109                let far = FAR_EL1.get() as usize;
110
111                let iss = esr.read(ESR_EL1::ISS);
112
113                match esr.read_as_enum(ESR_EL1::EC) {
114                    Some(ESR_EL1::EC::Value::SVC64) => ReturnReason::Syscall,
115                    Some(ESR_EL1::EC::Value::InstrAbortLowerEL) if is_valid_page_fault(iss) => {
116                        ReturnReason::PageFault(
117                            va!(far),
118                            PageFaultFlags::EXECUTE | PageFaultFlags::USER,
119                        )
120                    }
121                    Some(ESR_EL1::EC::Value::DataAbortLowerEL) if is_valid_page_fault(iss) => {
122                        let wnr = (iss & (1 << 6)) != 0; // WnR: Write not Read
123                        let cm = (iss & (1 << 8)) != 0; // CM: Cache maintenance
124                        ReturnReason::PageFault(
125                            va!(far),
126                            if wnr & !cm {
127                                PageFaultFlags::WRITE
128                            } else {
129                                PageFaultFlags::READ
130                            } | PageFaultFlags::USER,
131                        )
132                    }
133                    _ => ReturnReason::Exception(ExceptionInfo { esr, far }),
134                }
135            }
136        };
137
138        crate::asm::enable_irqs();
139        ret
140    }
141}
142
143impl Deref for UserContext {
144    type Target = TrapFrame;
145
146    fn deref(&self) -> &Self::Target {
147        &self.tf
148    }
149}
150
151impl DerefMut for UserContext {
152    fn deref_mut(&mut self) -> &mut Self::Target {
153        &mut self.tf
154    }
155}
156
157/// Information about an exception that occurred in user space.
158#[derive(Debug, Clone, Copy)]
159pub struct ExceptionInfo {
160    /// Exception Syndrome Register
161    pub esr: LocalRegisterCopy<u64, ESR_EL1::Register>,
162    /// Fault Address Register
163    pub far: usize,
164}
165
166impl ExceptionInfo {
167    /// Returns the raw Exception Syndrome Register value.
168    pub fn esr_value(&self) -> u64 {
169        self.esr.get()
170    }
171
172    /// Returns the raw exception class bits.
173    pub fn ec_value(&self) -> u64 {
174        self.esr.read(ESR_EL1::EC)
175    }
176
177    /// Returns the instruction specific syndrome bits.
178    pub fn iss_value(&self) -> u64 {
179        self.esr.read(ESR_EL1::ISS)
180    }
181
182    /// Returns a generalized kind of this exception.
183    pub fn kind(&self) -> ExceptionKind {
184        match self.esr.read_as_enum(ESR_EL1::EC) {
185            Some(ESR_EL1::EC::Value::Brk64) | Some(ESR_EL1::EC::Value::Bkpt32) => {
186                ExceptionKind::Breakpoint
187            }
188            Some(ESR_EL1::EC::Value::IllegalExecutionState) | Some(ESR_EL1::EC::Value::Unknown) => {
189                ExceptionKind::IllegalInstruction
190            }
191            Some(ESR_EL1::EC::Value::PCAlignmentFault)
192            | Some(ESR_EL1::EC::Value::SPAlignmentFault) => ExceptionKind::Misaligned,
193            _ => ExceptionKind::Other,
194        }
195    }
196}