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    /// Gets the TLS area.
55    pub const fn tls(&self) -> usize {
56        self.fs_base as _
57    }
58
59    /// Sets the TLS area.
60    pub const fn set_tls(&mut self, tls_area: usize) {
61        self.fs_base = tls_area as _;
62    }
63
64    /// Enters user space.
65    ///
66    /// It restores the user registers and jumps to the user entry point
67    /// (saved in `rip`).
68    ///
69    /// This function returns when an exception or syscall occurs.
70    pub fn run(&mut self) -> ReturnReason {
71        extern "C" {
72            fn enter_user(uctx: &mut UserContext);
73        }
74
75        assert_eq!(self.cs, gdt::UCODE64.0 as _);
76        assert_eq!(self.ss, gdt::UDATA.0 as _);
77
78        crate::asm::disable_irqs();
79
80        let kernel_fs_base = read_thread_pointer();
81        unsafe { write_thread_pointer(self.fs_base as _) };
82        KernelGsBase::write(x86_64::VirtAddr::new_truncate(self.gs_base));
83
84        unsafe { enter_user(self) };
85
86        self.gs_base = KernelGsBase::read().as_u64();
87        self.fs_base = read_thread_pointer() as _;
88        unsafe { write_thread_pointer(kernel_fs_base) };
89
90        let cr2 = Cr2::read().unwrap().as_u64() as usize;
91        let vector = self.vector as u8;
92
93        const PAGE_FAULT_VECTOR: u8 = ExceptionVector::Page as u8;
94
95        let ret = match (vector, err_code_to_flags(self.error_code)) {
96            (PAGE_FAULT_VECTOR, Ok(flags)) => ReturnReason::PageFault(va!(cr2), flags),
97            (LEGACY_SYSCALL_VECTOR, _) => ReturnReason::Syscall,
98            (IRQ_VECTOR_START..=IRQ_VECTOR_END, _) => {
99                crate::trap::irq_handler(vector as _);
100                ReturnReason::Interrupt
101            }
102            _ => ReturnReason::Exception(ExceptionInfo {
103                vector,
104                error_code: self.error_code,
105                cr2,
106            }),
107        };
108
109        crate::asm::enable_irqs();
110        ret
111    }
112}
113
114impl Deref for UserContext {
115    type Target = TrapFrame;
116
117    fn deref(&self) -> &Self::Target {
118        &self.tf
119    }
120}
121
122impl DerefMut for UserContext {
123    fn deref_mut(&mut self) -> &mut Self::Target {
124        &mut self.tf
125    }
126}
127
128/// Information about an exception that occurred in user space.
129#[derive(Debug, Clone, Copy)]
130pub struct ExceptionInfo {
131    /// The exception vector.
132    pub vector: u8,
133    /// The error code.
134    pub error_code: u64,
135    /// The faulting virtual address (if applicable).
136    pub cr2: usize,
137}
138
139impl ExceptionInfo {
140    /// Returns a generalized kind of this exception.
141    pub fn kind(&self) -> ExceptionKind {
142        match ExceptionVector::try_from(self.vector) {
143            Ok(ExceptionVector::Breakpoint) => ExceptionKind::Breakpoint,
144            Ok(ExceptionVector::InvalidOpcode) => ExceptionKind::IllegalInstruction,
145            _ => ExceptionKind::Other,
146        }
147    }
148}
149
150/// Initializes syscall support and setups the syscall handler.
151pub(super) fn init_syscall() {
152    extern "C" {
153        fn syscall_entry();
154    }
155
156    LStar::write(x86_64::VirtAddr::new_truncate(
157        syscall_entry as *const () as usize as _,
158    ));
159    Star::write(gdt::UCODE64, gdt::UDATA, gdt::KCODE64, gdt::KDATA).unwrap();
160    SFMask::write(
161        RFlags::TRAP_FLAG
162            | RFlags::INTERRUPT_FLAG
163            | RFlags::DIRECTION_FLAG
164            | RFlags::IOPL_LOW
165            | RFlags::IOPL_HIGH
166            | RFlags::NESTED_TASK
167            | RFlags::ALIGNMENT_CHECK,
168    ); // TF | IF | DF | IOPL | AC | NT (0x47700)
169    unsafe {
170        Efer::update(|efer| *efer |= EferFlags::SYSTEM_CALL_EXTENSIONS);
171    }
172}