Skip to main content

ax_cpu/x86_64/
uspace.rs

1//! Structures and functions for user space.
2
3use 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/// Context to enter user space.
25#[derive(Debug, Clone, Copy)]
26#[repr(C, align(16))]
27pub struct UserContext {
28    tf: TrapFrame,
29    /// User-owned FS segment base.
30    ///
31    /// `CR4.FSGSBASE` stays disabled, so userspace cannot modify this value
32    /// without an `arch_prctl`-style kernel operation updating this image.
33    pub fs_base: u64,
34    /// User-owned GS segment base.
35    pub gs_base: u64,
36    /// Kernel continuation stack saved while this context executes in ring 3.
37    kernel_stack_pointer: u64,
38    /// Explicitly initializes the tail bytes required by the 16-byte ABI alignment.
39    _reserved: u64,
40}
41
42// SAFETY: `TrapFrame` and every following field are integer-only, the explicit
43// tail word consumes the alignment padding, and the offset assertions below
44// pin that layout.
45unsafe impl bytemuck::NoUninit for UserContext {}
46
47const _: () = {
48    // A privilege transition may align TSS.RSP0 down to 16 bytes before
49    // constructing the hardware frame. `enter_user` uses the end of `tf` as
50    // both RSP0 and the boundary above which it saves the kernel continuation,
51    // so both the object and that boundary must already be aligned.
52    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    /// Creates a new context with the given entry point, user stack pointer,
67    /// and the argument.
68    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(), // IOPL = 0, IF = 1
76                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    /// Normalizes a cloned user context so it can safely return to ring 3.
88    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    /// Clears the single-step trap flag after a debug exception.
96    ///
97    /// Returns whether the flag had been set in the saved user context.
98    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    /// Returns the syscall instruction length in bytes.
107    pub const fn syscall_insn_len(&self) -> usize {
108        2
109    }
110
111    /// Gets the TLS area.
112    pub const fn tls(&self) -> usize {
113        self.fs_base as _
114    }
115
116    /// Sets the TLS area.
117    pub const fn set_tls(&mut self, tls_area: usize) {
118        self.fs_base = tls_area as _;
119    }
120
121    /// Returns whether this register image can be restored as an interruptible
122    /// ring-3 context.
123    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    /// Enters user space without validating the runtime transition.
134    ///
135    /// It restores the user registers and jumps to the user entry point
136    /// (saved in `rip`).
137    ///
138    /// This function returns when an exception or syscall occurs.
139    ///
140    /// # Safety
141    ///
142    /// The caller must be the runtime's prepared user-entry boundary for the
143    /// current scheduler task. Its context-switch tail must be complete, no
144    /// IRQ/preemption guard or hard interrupt may be active, and local IRQs
145    /// must remain disabled after the final scheduler-work check. The active
146    /// logical address space, hardware root and CPU footprint must match this
147    /// task and keep every user address referenced by `self` valid. The saved
148    /// selectors and RFLAGS must describe an interruptible ring-3 return. No
149    /// code may run between those validations and this call.
150    ///
151    /// Safe code cannot invoke this raw boundary:
152    ///
153    /// ```compile_fail
154    /// fn bypass_runtime(context: &mut ax_cpu::uspace::UserContext) {
155    ///     context.run_unchecked();
156    /// }
157    /// ```
158    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/// Information about an exception that occurred in user space.
218#[derive(Debug, Clone, Copy)]
219pub struct ExceptionInfo {
220    /// The exception vector.
221    pub vector: u8,
222    /// The error code.
223    pub error_code: u64,
224    /// The faulting virtual address (if applicable).
225    pub cr2: usize,
226}
227
228impl ExceptionInfo {
229    /// Returns the faulting virtual address when the CPU records one.
230    pub const fn fault_addr(&self) -> Option<usize> {
231        Some(self.cr2)
232    }
233
234    /// Returns architecture-neutral syndrome information for this exception.
235    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    /// Returns a generalized kind of this exception.
244    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            // `#DE`: integer divide-by-zero / `INT_MIN / -1`. Linux delivers this
250            // as SIGFPE/FPE_INTDIV; the HotSpot JVM's x86 interpreter and JIT
251            // rely on the trap to raise Java `ArithmeticException`.
252            Ok(ExceptionVector::Division) => ExceptionKind::ArithmeticError,
253            _ => ExceptionKind::Other,
254        }
255    }
256}
257
258/// Initializes syscall support and setups the syscall handler.
259pub(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    ); // TF | IF | DF | IOPL | AC | NT (0x47700)
282    unsafe {
283        Efer::update(|efer| *efer |= EferFlags::SYSTEM_CALL_EXTENSIONS);
284    }
285}