Skip to main content

ax_cpu/x86_64/
uspace.rs

1//! Structures and functions for user space.
2
3use core::ops::{Deref, DerefMut};
4
5use ax_memory_addr::VirtAddr;
6use x86_64::{
7    registers::{
8        control::Cr2,
9        model_specific::{Efer, EferFlags, KernelGsBase, LStar, SFMask, Star},
10        rflags::RFlags,
11    },
12    structures::idt::ExceptionVector,
13};
14
15use super::{
16    TrapFrame,
17    asm::{read_thread_pointer, write_thread_pointer},
18    gdt,
19    trap::{IRQ_VECTOR_END, IRQ_VECTOR_START, LEGACY_SYSCALL_VECTOR, err_code_to_flags},
20};
21pub use crate::uspace_common::{ExceptionKind, ReturnReason};
22
23/// Context to enter user space.
24#[derive(Debug, Clone, Copy)]
25#[repr(C)]
26pub struct UserContext {
27    tf: TrapFrame,
28    /// FS Segment Base
29    pub fs_base: u64,
30    /// GS Segment Base
31    pub gs_base: u64,
32}
33
34impl UserContext {
35    /// Creates a new context with the given entry point, user stack pointer,
36    /// and the argument.
37    pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self {
38        use x86_64::registers::rflags::RFlags;
39        Self {
40            tf: TrapFrame {
41                rdi: arg0 as _,
42                rip: entry as _,
43                cs: gdt::UCODE64.0 as _,
44                rflags: RFlags::INTERRUPT_FLAG.bits(), // IOPL = 0, IF = 1
45                rsp: ustack_top.as_usize() as _,
46                ss: gdt::UDATA.0 as _,
47                ..Default::default()
48            },
49            fs_base: 0,
50            gs_base: 0,
51        }
52    }
53
54    /// Normalizes a cloned user context so it can safely return to ring 3.
55    pub fn prepare_clone_child_return_state(&mut self) {
56        let mut flags = RFlags::from_bits_truncate(self.tf.rflags);
57        flags.insert(RFlags::INTERRUPT_FLAG);
58        flags.remove(RFlags::TRAP_FLAG | RFlags::NESTED_TASK | RFlags::RESUME_FLAG);
59        self.tf.rflags = flags.bits();
60    }
61
62    /// Gets the TLS area.
63    pub const fn tls(&self) -> usize {
64        self.fs_base as _
65    }
66
67    /// Sets the TLS area.
68    pub const fn set_tls(&mut self, tls_area: usize) {
69        self.fs_base = tls_area as _;
70    }
71
72    /// Enters user space.
73    ///
74    /// It restores the user registers and jumps to the user entry point
75    /// (saved in `rip`).
76    ///
77    /// This function returns when an exception or syscall occurs.
78    pub fn run(&mut self) -> ReturnReason {
79        unsafe extern "C" {
80            fn enter_user(uctx: &mut UserContext);
81        }
82
83        assert_eq!(self.cs, gdt::UCODE64.0 as _);
84        assert_eq!(self.ss, gdt::UDATA.0 as _);
85
86        crate::asm::disable_irqs();
87
88        let kernel_fs_base = read_thread_pointer();
89        unsafe { write_thread_pointer(self.fs_base as _) };
90        KernelGsBase::write(x86_64::VirtAddr::new_truncate(self.gs_base));
91
92        unsafe { enter_user(self) };
93
94        self.gs_base = KernelGsBase::read().as_u64();
95        self.fs_base = read_thread_pointer() as _;
96        unsafe { write_thread_pointer(kernel_fs_base) };
97
98        let cr2 = Cr2::read().unwrap().as_u64() as usize;
99        let vector = self.vector as u8;
100
101        const PAGE_FAULT_VECTOR: u8 = ExceptionVector::Page as u8;
102
103        let ret = match (vector, err_code_to_flags(self.error_code)) {
104            (PAGE_FAULT_VECTOR, Ok(flags)) => ReturnReason::PageFault(va!(cr2), flags),
105            (LEGACY_SYSCALL_VECTOR, _) => ReturnReason::Syscall,
106            (IRQ_VECTOR_START..=IRQ_VECTOR_END, _) => {
107                crate::trap::irq_handler(vector as _);
108                ReturnReason::Interrupt
109            }
110            _ => ReturnReason::Exception(ExceptionInfo {
111                vector,
112                error_code: self.error_code,
113                cr2,
114            }),
115        };
116
117        crate::asm::enable_irqs();
118        ret
119    }
120}
121
122impl Deref for UserContext {
123    type Target = TrapFrame;
124
125    fn deref(&self) -> &Self::Target {
126        &self.tf
127    }
128}
129
130impl DerefMut for UserContext {
131    fn deref_mut(&mut self) -> &mut Self::Target {
132        &mut self.tf
133    }
134}
135
136/// Information about an exception that occurred in user space.
137#[derive(Debug, Clone, Copy)]
138pub struct ExceptionInfo {
139    /// The exception vector.
140    pub vector: u8,
141    /// The error code.
142    pub error_code: u64,
143    /// The faulting virtual address (if applicable).
144    pub cr2: usize,
145}
146
147impl ExceptionInfo {
148    /// Returns a generalized kind of this exception.
149    pub fn kind(&self) -> ExceptionKind {
150        match ExceptionVector::try_from(self.vector) {
151            Ok(ExceptionVector::Debug) => ExceptionKind::Debug,
152            Ok(ExceptionVector::Breakpoint) => ExceptionKind::Breakpoint,
153            Ok(ExceptionVector::InvalidOpcode) => ExceptionKind::IllegalInstruction,
154            _ => ExceptionKind::Other,
155        }
156    }
157}
158
159/// Initializes syscall support and setups the syscall handler.
160pub(super) fn init_syscall() {
161    unsafe extern "C" {
162        fn syscall_entry();
163    }
164
165    LStar::write(x86_64::VirtAddr::new_truncate(
166        syscall_entry as *const () as usize as _,
167    ));
168    Star::write(gdt::UCODE64, gdt::UDATA, gdt::KCODE64, gdt::KDATA).unwrap();
169    SFMask::write(
170        RFlags::TRAP_FLAG
171            | RFlags::INTERRUPT_FLAG
172            | RFlags::DIRECTION_FLAG
173            | RFlags::IOPL_LOW
174            | RFlags::IOPL_HIGH
175            | RFlags::NESTED_TASK
176            | RFlags::ALIGNMENT_CHECK,
177    ); // TF | IF | DF | IOPL | AC | NT (0x47700)
178    unsafe {
179        Efer::update(|efer| *efer |= EferFlags::SYSTEM_CALL_EXTENSIONS);
180    }
181}