1use 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#[repr(C, align(16))]
15#[derive(Debug, Clone, Copy)]
16pub struct UserContext {
17 tf: TrapFrame,
18 pub sp: u64,
20 pub tpidr: u64,
22}
23
24impl UserContext {
25 const PAD_MAGIC: u64 = 0x1234_5678_9abc_def0;
26 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 pub const fn sp(&self) -> usize {
51 self.sp as _
52 }
53
54 pub const fn set_sp(&mut self, sp: usize) {
56 self.sp = sp as _;
57 }
58
59 pub const fn tls(&self) -> usize {
61 self.tpidr as _
62 }
63
64 pub const fn set_tls(&mut self, tls: usize) {
66 self.tpidr = tls as _;
67 }
68
69 pub fn run(&mut self) -> ReturnReason {
76 extern "C" {
77 fn enter_user(uctx: &mut UserContext) -> TrapKind;
78 }
79
80 crate::asm::disable_irqs();
81 let kind = unsafe { enter_user(self) };
82
83 let ret = match kind {
84 TrapKind::Irq => {
85 crate::trap::irq_handler(0);
86 ReturnReason::Interrupt
87 }
88 TrapKind::Fiq | TrapKind::SError => ReturnReason::Unknown,
89 TrapKind::Synchronous => {
90 let esr = ESR_EL1.extract();
91 let far = FAR_EL1.get() as usize;
92
93 let iss = esr.read(ESR_EL1::ISS);
94
95 match esr.read_as_enum(ESR_EL1::EC) {
96 Some(ESR_EL1::EC::Value::SVC64) => ReturnReason::Syscall,
97 Some(ESR_EL1::EC::Value::InstrAbortLowerEL) if is_valid_page_fault(iss) => {
98 ReturnReason::PageFault(
99 va!(far),
100 PageFaultFlags::EXECUTE | PageFaultFlags::USER,
101 )
102 }
103 Some(ESR_EL1::EC::Value::DataAbortLowerEL) if is_valid_page_fault(iss) => {
104 let wnr = (iss & (1 << 6)) != 0; let cm = (iss & (1 << 8)) != 0; ReturnReason::PageFault(
107 va!(far),
108 if wnr & !cm {
109 PageFaultFlags::WRITE
110 } else {
111 PageFaultFlags::READ
112 } | PageFaultFlags::USER,
113 )
114 }
115 _ => ReturnReason::Exception(ExceptionInfo { esr, far }),
116 }
117 }
118 };
119
120 crate::asm::enable_irqs();
121 ret
122 }
123}
124
125impl Deref for UserContext {
126 type Target = TrapFrame;
127
128 fn deref(&self) -> &Self::Target {
129 &self.tf
130 }
131}
132
133impl DerefMut for UserContext {
134 fn deref_mut(&mut self) -> &mut Self::Target {
135 &mut self.tf
136 }
137}
138
139#[derive(Debug, Clone, Copy)]
141pub struct ExceptionInfo {
142 pub esr: LocalRegisterCopy<u64, ESR_EL1::Register>,
144 pub far: usize,
146}
147
148impl ExceptionInfo {
149 pub fn kind(&self) -> ExceptionKind {
151 match self.esr.read_as_enum(ESR_EL1::EC) {
152 Some(ESR_EL1::EC::Value::Brk64) | Some(ESR_EL1::EC::Value::Bkpt32) => {
153 ExceptionKind::Breakpoint
154 }
155 Some(ESR_EL1::EC::Value::IllegalExecutionState) => ExceptionKind::IllegalInstruction,
156 Some(ESR_EL1::EC::Value::PCAlignmentFault)
157 | Some(ESR_EL1::EC::Value::SPAlignmentFault) => ExceptionKind::Misaligned,
158 _ => ExceptionKind::Other,
159 }
160 }
161}