Skip to main content

ax_cpu/arch/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    context::TrapFrame,
20    gdt,
21    trap::{IRQ_VECTOR_END, IRQ_VECTOR_START, LEGACY_SYSCALL_VECTOR, err_code_to_flags},
22};
23pub use crate::uspace_common::{ExceptionKind, ExceptionSyndrome, ReturnReason};
24
25/// Context to enter user space.
26#[derive(Debug, Clone, Copy)]
27#[repr(C, align(16))]
28pub struct UserContext {
29    tf: TrapFrame,
30    /// User-owned FS segment base.
31    ///
32    /// `CR4.FSGSBASE` stays disabled, so userspace cannot modify this value
33    /// without an `arch_prctl`-style kernel operation updating this image.
34    pub fs_base: u64,
35    /// User-owned GS segment base.
36    pub gs_base: u64,
37    /// Kernel continuation stack saved while this context executes in ring 3.
38    kernel_stack_pointer: u64,
39    /// Explicitly initializes the tail bytes required by the 16-byte ABI alignment.
40    _reserved: u64,
41}
42
43// SAFETY: `TrapFrame` and every following field are integer-only, the explicit
44// tail word consumes the alignment padding, and the offset assertions below
45// pin that layout.
46unsafe impl bytemuck::NoUninit for UserContext {}
47
48const _: () = {
49    // A privilege transition may align TSS.RSP0 down to 16 bytes before
50    // constructing the hardware frame. `enter_user` uses the end of `tf` as
51    // both RSP0 and the boundary above which it saves the kernel continuation,
52    // so both the object and that boundary must already be aligned.
53    assert!(align_of::<UserContext>() >= 16);
54    assert!(size_of::<TrapFrame>().is_multiple_of(16));
55    assert!(offset_of!(UserContext, tf) == 0);
56    assert!(offset_of!(UserContext, fs_base) == size_of::<TrapFrame>());
57    assert!(offset_of!(UserContext, gs_base) == size_of::<TrapFrame>() + size_of::<u64>());
58    assert!(
59        offset_of!(UserContext, kernel_stack_pointer)
60            == size_of::<TrapFrame>() + 2 * size_of::<u64>()
61    );
62    assert!(offset_of!(UserContext, _reserved) == size_of::<TrapFrame>() + 3 * size_of::<u64>());
63    assert!(size_of::<UserContext>() == size_of::<TrapFrame>() + 4 * size_of::<u64>());
64};
65
66impl UserContext {
67    /// Creates a new context with the given entry point, user stack pointer,
68    /// and the argument.
69    pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self {
70        use x86_64::registers::rflags::RFlags;
71        Self {
72            tf: TrapFrame {
73                regs: super::registers::GeneralRegisters {
74                    rdi: arg0 as _,
75                    ..Default::default()
76                },
77                rip: entry as _,
78                cs: gdt::UCODE64.0 as _,
79                rflags: RFlags::INTERRUPT_FLAG.bits(), // IOPL = 0, IF = 1
80                rsp: ustack_top.as_usize() as _,
81                ss: gdt::UDATA.0 as _,
82                ..Default::default()
83            },
84            fs_base: 0,
85            gs_base: 0,
86            kernel_stack_pointer: 0,
87            _reserved: 0,
88        }
89    }
90
91    /// Normalizes a cloned user context so it can safely return to ring 3.
92    pub fn prepare_clone_child_return_state(&mut self) {
93        let mut flags = RFlags::from_bits_truncate(self.tf.rflags);
94        flags.insert(RFlags::INTERRUPT_FLAG);
95        flags.remove(RFlags::TRAP_FLAG | RFlags::NESTED_TASK | RFlags::RESUME_FLAG);
96        self.tf.rflags = flags.bits();
97    }
98
99    /// Clears the single-step trap flag after a debug exception.
100    ///
101    /// Returns whether the flag had been set in the saved user context.
102    pub fn clear_single_step_after_debug(&mut self) -> bool {
103        let mut flags = RFlags::from_bits_truncate(self.tf.rflags);
104        let was_set = flags.contains(RFlags::TRAP_FLAG);
105        flags.remove(RFlags::TRAP_FLAG);
106        self.tf.rflags = flags.bits();
107        was_set
108    }
109
110    /// Returns the syscall instruction length in bytes.
111    pub const fn syscall_insn_len(&self) -> usize {
112        2
113    }
114
115    /// Gets the TLS area.
116    pub const fn tls(&self) -> usize {
117        self.fs_base as _
118    }
119
120    /// Sets the TLS area.
121    pub const fn set_tls(&mut self, tls_area: usize) {
122        self.fs_base = tls_area as _;
123    }
124
125    /// Returns whether this register image can be restored as an interruptible
126    /// ring-3 context.
127    pub fn has_interruptible_user_return_mode(&self) -> bool {
128        let forbidden =
129            RFlags::IOPL_LOW | RFlags::IOPL_HIGH | RFlags::NESTED_TASK | RFlags::VIRTUAL_8086_MODE;
130        let flags = RFlags::from_bits_retain(self.tf.rflags);
131        self.tf.cs == gdt::UCODE64.0 as u64
132            && self.tf.ss == gdt::UDATA.0 as u64
133            && flags.contains(RFlags::INTERRUPT_FLAG)
134            && !flags.intersects(forbidden)
135    }
136
137    /// Enters user space without validating the runtime transition.
138    ///
139    /// It restores the user registers and jumps to the user entry point
140    /// (saved in `rip`).
141    ///
142    /// This function returns when an exception or syscall occurs.
143    ///
144    /// # Safety
145    ///
146    /// The caller must be the runtime's prepared user-entry boundary for the
147    /// current scheduler task. Its context-switch tail must be complete, no
148    /// IRQ/preemption guard or hard interrupt may be active, and local IRQs
149    /// must remain disabled after the final scheduler-work check. The active
150    /// logical address space, hardware root and CPU footprint must match this
151    /// task and keep every user address referenced by `self` valid. The saved
152    /// selectors and RFLAGS must describe an interruptible ring-3 return. No
153    /// code may run between those validations and this call.
154    pub unsafe fn run_unchecked(&mut self) -> ReturnReason {
155        unsafe extern "C" {
156            fn enter_user(uctx: &mut UserContext);
157        }
158
159        assert!(
160            self.has_interruptible_user_return_mode(),
161            "raw user entry requires an interruptible ring-3 register image"
162        );
163
164        assert!(
165            !crate::asm::irqs_enabled(),
166            "raw user entry requires the prepared IRQ-off boundary"
167        );
168        super::local_state::install_current_user_tls(self.fs_base as _, self.gs_base as _);
169
170        unsafe { enter_user(self) };
171
172        let vector = self.vector as u8;
173
174        const PAGE_FAULT_VECTOR: u8 = ExceptionVector::Page as u8;
175
176        let ret = match (vector, err_code_to_flags(self.error_code)) {
177            (PAGE_FAULT_VECTOR, Ok(flags)) => {
178                ReturnReason::PageFault(va!(Cr2::read_raw() as usize), flags)
179            }
180            (LEGACY_SYSCALL_VECTOR, _) => ReturnReason::Syscall,
181            (IRQ_VECTOR_START..=IRQ_VECTOR_END, _) => {
182                crate::trap::dispatch_irq(
183                    vector as _,
184                    crate::trap::TrapOrigin::User,
185                    Some(self.tf.interrupted_context()),
186                );
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}