1use core::{
4 mem::{align_of, offset_of, size_of},
5 ops::{Deref, DerefMut},
6};
7
8use ax_memory_addr::VirtAddr;
9use x86_64::{
10 registers::{
11 control::{Cr2, Cr4, Cr4Flags},
12 model_specific::{Efer, EferFlags, LStar, SFMask, Star},
13 rflags::RFlags,
14 },
15 structures::idt::ExceptionVector,
16};
17
18use super::{
19 TrapFrame, gdt,
20 trap::{IRQ_VECTOR_END, IRQ_VECTOR_START, LEGACY_SYSCALL_VECTOR, err_code_to_flags},
21};
22pub use crate::uspace_common::{ExceptionKind, ExceptionSyndrome, ReturnReason};
23
24#[derive(Debug, Clone, Copy)]
26#[repr(C, align(16))]
27pub struct UserContext {
28 tf: TrapFrame,
29 pub fs_base: u64,
34 pub gs_base: u64,
36 kernel_stack_pointer: u64,
38 _reserved: u64,
40}
41
42unsafe impl bytemuck::NoUninit for UserContext {}
46
47const _: () = {
48 assert!(align_of::<UserContext>() >= 16);
53 assert!(size_of::<TrapFrame>().is_multiple_of(16));
54 assert!(offset_of!(UserContext, tf) == 0);
55 assert!(offset_of!(UserContext, fs_base) == size_of::<TrapFrame>());
56 assert!(offset_of!(UserContext, gs_base) == size_of::<TrapFrame>() + size_of::<u64>());
57 assert!(
58 offset_of!(UserContext, kernel_stack_pointer)
59 == size_of::<TrapFrame>() + 2 * size_of::<u64>()
60 );
61 assert!(offset_of!(UserContext, _reserved) == size_of::<TrapFrame>() + 3 * size_of::<u64>());
62 assert!(size_of::<UserContext>() == size_of::<TrapFrame>() + 4 * size_of::<u64>());
63};
64
65impl UserContext {
66 pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self {
69 use x86_64::registers::rflags::RFlags;
70 Self {
71 tf: TrapFrame {
72 rdi: arg0 as _,
73 rip: entry as _,
74 cs: gdt::UCODE64.0 as _,
75 rflags: RFlags::INTERRUPT_FLAG.bits(), rsp: ustack_top.as_usize() as _,
77 ss: gdt::UDATA.0 as _,
78 ..Default::default()
79 },
80 fs_base: 0,
81 gs_base: 0,
82 kernel_stack_pointer: 0,
83 _reserved: 0,
84 }
85 }
86
87 pub fn prepare_clone_child_return_state(&mut self) {
89 let mut flags = RFlags::from_bits_truncate(self.tf.rflags);
90 flags.insert(RFlags::INTERRUPT_FLAG);
91 flags.remove(RFlags::TRAP_FLAG | RFlags::NESTED_TASK | RFlags::RESUME_FLAG);
92 self.tf.rflags = flags.bits();
93 }
94
95 pub fn clear_single_step_after_debug(&mut self) -> bool {
99 let mut flags = RFlags::from_bits_truncate(self.tf.rflags);
100 let was_set = flags.contains(RFlags::TRAP_FLAG);
101 flags.remove(RFlags::TRAP_FLAG);
102 self.tf.rflags = flags.bits();
103 was_set
104 }
105
106 pub const fn syscall_insn_len(&self) -> usize {
108 2
109 }
110
111 pub const fn tls(&self) -> usize {
113 self.fs_base as _
114 }
115
116 pub const fn set_tls(&mut self, tls_area: usize) {
118 self.fs_base = tls_area as _;
119 }
120
121 pub fn has_interruptible_user_return_mode(&self) -> bool {
124 let forbidden =
125 RFlags::IOPL_LOW | RFlags::IOPL_HIGH | RFlags::NESTED_TASK | RFlags::VIRTUAL_8086_MODE;
126 let flags = RFlags::from_bits_retain(self.tf.rflags);
127 self.tf.cs == gdt::UCODE64.0 as u64
128 && self.tf.ss == gdt::UDATA.0 as u64
129 && flags.contains(RFlags::INTERRUPT_FLAG)
130 && !flags.intersects(forbidden)
131 }
132
133 pub unsafe fn run_unchecked(&mut self) -> ReturnReason {
159 unsafe extern "C" {
160 fn enter_user(uctx: &mut UserContext);
161 }
162
163 assert!(
164 self.has_interruptible_user_return_mode(),
165 "raw user entry requires an interruptible ring-3 register image"
166 );
167
168 assert!(
169 !crate::asm::irqs_enabled(),
170 "raw user entry requires the prepared IRQ-off boundary"
171 );
172 super::local_state::install_current_user_tls(self.fs_base as _, self.gs_base as _);
173
174 unsafe { enter_user(self) };
175
176 let vector = self.vector as u8;
177
178 const PAGE_FAULT_VECTOR: u8 = ExceptionVector::Page as u8;
179
180 let ret = match (vector, err_code_to_flags(self.error_code)) {
181 (PAGE_FAULT_VECTOR, Ok(flags)) => {
182 ReturnReason::PageFault(va!(Cr2::read_raw() as usize), flags)
183 }
184 (LEGACY_SYSCALL_VECTOR, _) => ReturnReason::Syscall,
185 (IRQ_VECTOR_START..=IRQ_VECTOR_END, _) => {
186 crate::trap::dispatch_irq(vector as _, crate::trap::TrapOrigin::User);
187 ReturnReason::Interrupt
188 }
189 _ => ReturnReason::Exception(ExceptionInfo {
190 vector,
191 error_code: self.error_code,
192 cr2: Cr2::read_raw() as usize,
193 }),
194 };
195
196 crate::asm::enable_irqs();
197 ret
198 }
199}
200
201const _: unsafe fn(&mut UserContext) -> ReturnReason = UserContext::run_unchecked;
202
203impl Deref for UserContext {
204 type Target = TrapFrame;
205
206 fn deref(&self) -> &Self::Target {
207 &self.tf
208 }
209}
210
211impl DerefMut for UserContext {
212 fn deref_mut(&mut self) -> &mut Self::Target {
213 &mut self.tf
214 }
215}
216
217#[derive(Debug, Clone, Copy)]
219pub struct ExceptionInfo {
220 pub vector: u8,
222 pub error_code: u64,
224 pub cr2: usize,
226}
227
228impl ExceptionInfo {
229 pub const fn fault_addr(&self) -> Option<usize> {
231 Some(self.cr2)
232 }
233
234 pub const fn syndrome(&self) -> ExceptionSyndrome {
236 ExceptionSyndrome {
237 raw: self.error_code,
238 class: self.vector as u64,
239 iss: 0,
240 }
241 }
242
243 pub fn kind(&self) -> ExceptionKind {
245 match ExceptionVector::try_from(self.vector) {
246 Ok(ExceptionVector::Debug) => ExceptionKind::Debug,
247 Ok(ExceptionVector::Breakpoint) => ExceptionKind::Breakpoint,
248 Ok(ExceptionVector::InvalidOpcode) => ExceptionKind::IllegalInstruction,
249 Ok(ExceptionVector::Division) => ExceptionKind::ArithmeticError,
253 _ => ExceptionKind::Other,
254 }
255 }
256}
257
258pub(super) fn init_syscall() {
260 unsafe extern "C" {
261 fn syscall_entry();
262 }
263
264 assert!(
265 !Cr4::read().contains(Cr4Flags::FSGSBASE),
266 "LinuxCurrent user TLS requires trapping all FS/GS base changes"
267 );
268 super::local_state::initialize_cpu_user_tls();
269 LStar::write(x86_64::VirtAddr::new_truncate(
270 syscall_entry as *const () as usize as _,
271 ));
272 Star::write(gdt::UCODE64, gdt::UDATA, gdt::KCODE64, gdt::KDATA).unwrap();
273 SFMask::write(
274 RFlags::TRAP_FLAG
275 | RFlags::INTERRUPT_FLAG
276 | RFlags::DIRECTION_FLAG
277 | RFlags::IOPL_LOW
278 | RFlags::IOPL_HIGH
279 | RFlags::NESTED_TASK
280 | RFlags::ALIGNMENT_CHECK,
281 ); unsafe {
283 Efer::update(|efer| *efer |= EferFlags::SYSTEM_CALL_EXTENSIONS);
284 }
285}