ax_cpu/arch/aarch64/
uspace.rs1use core::{
4 mem::{offset_of, size_of},
5 ops::{Deref, DerefMut},
6};
7
8use aarch64_cpu::registers::FAR_EL1;
9pub use aarch64_cpu::registers::{ESR_EL1, Readable};
10use ax_memory_addr::VirtAddr;
11pub use tock_registers::{
12 LocalRegisterCopy, RegisterLongName, UIntLike,
13 debug::{RegisterDebugInfo, RegisterDebugValue},
14 fields::{Field, FieldValue, TryFromValue},
15};
16
17use super::trap::{TrapKind, is_valid_page_fault};
18pub use crate::uspace_common::{ExceptionKind, ExceptionSyndrome, ReturnReason};
19use crate::{arch::current::context::TrapFrame, trap::PageFaultFlags};
20
21#[repr(C, align(16))]
23#[derive(Debug, Clone, Copy)]
24pub struct UserContext {
25 tf: TrapFrame,
26 pub sp: u64,
28 pub tpidr: u64,
30}
31
32unsafe impl bytemuck::NoUninit for UserContext {}
35
36const _: () = {
37 assert!(size_of::<TrapFrame>() == 34 * size_of::<u64>());
38 assert!(offset_of!(UserContext, tf) == 0);
39 assert!(offset_of!(UserContext, sp) == size_of::<TrapFrame>());
40 assert!(offset_of!(UserContext, tpidr) == size_of::<TrapFrame>() + size_of::<u64>());
41 assert!(size_of::<UserContext>() == size_of::<TrapFrame>() + 2 * size_of::<u64>());
42};
43
44impl UserContext {
45 pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self {
47 use aarch64_cpu::registers::SPSR_EL1;
48 let mut regs = [0; 31];
49 regs[0] = arg0 as _;
50 Self {
51 tf: TrapFrame {
52 x: regs,
53 elr: entry as _,
54 spsr: (SPSR_EL1::M::EL0t
55 + SPSR_EL1::D::Masked
56 + SPSR_EL1::A::Masked
57 + SPSR_EL1::I::Unmasked
58 + SPSR_EL1::F::Masked)
59 .value,
60 sp: 0,
61 },
62 sp: ustack_top.as_usize() as _,
63 tpidr: 0,
64 }
65 }
66
67 pub fn prepare_clone_child_return_state(&mut self) {
69 use aarch64_cpu::registers::SPSR_EL1;
70
71 self.tf.spsr = (self.tf.spsr
72 & !(SPSR_EL1::M.mask
73 | SPSR_EL1::D.mask
74 | SPSR_EL1::A.mask
75 | SPSR_EL1::I.mask
76 | SPSR_EL1::F.mask))
77 | (SPSR_EL1::M::EL0t
78 + SPSR_EL1::D::Masked
79 + SPSR_EL1::A::Masked
80 + SPSR_EL1::I::Unmasked
81 + SPSR_EL1::F::Masked)
82 .value;
83 }
84
85 pub const fn clear_single_step_after_debug(&mut self) -> bool {
90 false
91 }
92
93 pub const fn syscall_insn_len(&self) -> usize {
95 4
96 }
97
98 pub const fn sp(&self) -> usize {
100 self.sp as _
101 }
102
103 pub const fn set_sp(&mut self, sp: usize) {
105 self.sp = sp as _;
106 }
107
108 pub const fn tls(&self) -> usize {
110 self.tpidr as _
111 }
112
113 pub const fn set_tls(&mut self, tls: usize) {
115 self.tpidr = tls as _;
116 }
117
118 pub fn has_interruptible_user_return_mode(&self) -> bool {
121 use aarch64_cpu::registers::SPSR_EL1;
122
123 let runtime_daif =
124 SPSR_EL1::D::Masked + SPSR_EL1::A::Masked + SPSR_EL1::I::Unmasked + SPSR_EL1::F::Masked;
125 self.tf.spsr & SPSR_EL1::M::EL0t.mask() == SPSR_EL1::M::EL0t.value
126 && self.tf.spsr & runtime_daif.mask() == runtime_daif.value
127 }
128
129 pub unsafe fn run_unchecked(&mut self) -> ReturnReason {
147 unsafe extern "C" {
148 fn enter_user(uctx: &mut UserContext) -> TrapKind;
149 }
150
151 assert!(
152 !crate::asm::irqs_enabled(),
153 "raw user entry requires the prepared IRQ-off boundary"
154 );
155 assert!(
156 self.has_interruptible_user_return_mode(),
157 "raw user entry requires an interruptible EL0 register image"
158 );
159 let kind = unsafe { enter_user(self) };
160
161 let ret = match kind {
162 TrapKind::Irq => {
163 crate::trap::dispatch_irq(
164 0,
165 crate::trap::TrapOrigin::User,
166 Some(crate::trap::InterruptedContext {
167 pc: self.tf.ip(),
168 sp: self.sp as usize,
169 fp: self.tf.x[29] as usize,
170 privilege: crate::trap::InterruptedPrivilege::User,
171 }),
172 );
173 ReturnReason::Interrupt
174 }
175 TrapKind::Fiq | TrapKind::SError => ReturnReason::Unknown,
176 TrapKind::Synchronous => {
177 let esr = ESR_EL1.extract();
178 let far = FAR_EL1.get() as usize;
179
180 let iss = esr.read(ESR_EL1::ISS);
181
182 match esr.read_as_enum(ESR_EL1::EC) {
183 Some(ESR_EL1::EC::Value::SVC64) => ReturnReason::Syscall,
184 Some(ESR_EL1::EC::Value::InstrAbortLowerEL) if is_valid_page_fault(iss) => {
185 ReturnReason::PageFault(
186 va!(far),
187 PageFaultFlags::EXECUTE | PageFaultFlags::USER,
188 )
189 }
190 Some(ESR_EL1::EC::Value::DataAbortLowerEL) if is_valid_page_fault(iss) => {
191 let wnr = (iss & (1 << 6)) != 0; let cm = (iss & (1 << 8)) != 0; ReturnReason::PageFault(
194 va!(far),
195 if wnr & !cm {
196 PageFaultFlags::WRITE
197 } else {
198 PageFaultFlags::READ
199 } | PageFaultFlags::USER,
200 )
201 }
202 _ => ReturnReason::Exception(ExceptionInfo { esr, far }),
203 }
204 }
205 };
206
207 crate::asm::enable_irqs();
208 ret
209 }
210}
211
212const _: unsafe fn(&mut UserContext) -> ReturnReason = UserContext::run_unchecked;
213
214impl Deref for UserContext {
215 type Target = TrapFrame;
216
217 fn deref(&self) -> &Self::Target {
218 &self.tf
219 }
220}
221
222impl DerefMut for UserContext {
223 fn deref_mut(&mut self) -> &mut Self::Target {
224 &mut self.tf
225 }
226}
227
228#[derive(Debug, Clone, Copy)]
230pub struct ExceptionInfo {
231 pub esr: LocalRegisterCopy<u64, ESR_EL1::Register>,
233 pub far: usize,
235}
236
237impl ExceptionInfo {
238 pub const fn fault_addr(&self) -> Option<usize> {
240 Some(self.far)
241 }
242
243 pub fn syndrome(&self) -> ExceptionSyndrome {
245 ExceptionSyndrome {
246 raw: self.esr_value(),
247 class: self.ec_value(),
248 iss: self.iss_value(),
249 }
250 }
251
252 pub fn esr_value(&self) -> u64 {
254 self.esr.get()
255 }
256
257 pub fn ec_value(&self) -> u64 {
259 self.esr.read(ESR_EL1::EC)
260 }
261
262 pub fn iss_value(&self) -> u64 {
264 self.esr.read(ESR_EL1::ISS)
265 }
266
267 pub fn kind(&self) -> ExceptionKind {
269 match self.esr.read_as_enum(ESR_EL1::EC) {
270 Some(ESR_EL1::EC::Value::Brk64) | Some(ESR_EL1::EC::Value::Bkpt32) => {
271 ExceptionKind::Breakpoint
272 }
273 Some(ESR_EL1::EC::Value::IllegalExecutionState) | Some(ESR_EL1::EC::Value::Unknown) => {
274 ExceptionKind::IllegalInstruction
275 }
276 Some(ESR_EL1::EC::Value::PCAlignmentFault)
277 | Some(ESR_EL1::EC::Value::SPAlignmentFault) => ExceptionKind::Misaligned,
278 _ => ExceptionKind::Other,
279 }
280 }
281}