obkrnl/context/
x86_64.rs

1use super::Base;
2use crate::arch::{ArchConfig, wrmsr};
3use core::arch::asm;
4use core::marker::PhantomPinned;
5use core::mem::offset_of;
6use core::pin::Pin;
7
8pub const fn current_trap_rsp_offset() -> usize {
9    offset_of!(Context, trap_rsp)
10}
11
12pub const fn current_user_rsp_offset() -> usize {
13    offset_of!(Context, user_rsp)
14}
15
16/// Extended [Base] for x86-64.
17#[repr(C)]
18pub(super) struct Context {
19    pub base: Base,        // Must be first field.
20    pub trap_rsp: *mut u8, // pc_rsp0
21    pub user_rsp: usize,   // pc_scratch_rsp
22    phantom: PhantomPinned,
23}
24
25impl Context {
26    pub fn new(base: Base, arch: &ArchConfig) -> Self {
27        Self {
28            base,
29            trap_rsp: arch.trap_rsp as *mut u8,
30            user_rsp: 0,
31            phantom: PhantomPinned,
32        }
33    }
34
35    /// Set kernel `GS` segment register to `self`.
36    ///
37    /// At a glance this may looks incorrect due to `0xc0000102` is `KERNEL_GS_BAS` according to the
38    /// docs. The problem is the CPU always use the value from `0xc0000101` regardless the current
39    /// privilege level. That means `KERNEL_GS_BAS` is the name when the CPU currently on the user
40    /// space.
41    ///
42    /// This also set user-mode `FS` and `GS` to null.
43    pub unsafe fn activate(self: Pin<&mut Self>) {
44        // Set GS for kernel mode.
45        unsafe { wrmsr(0xc0000101, self.get_unchecked_mut() as *mut Self as usize) };
46
47        // Clear FS and GS for user mode.
48        unsafe { wrmsr(0xc0000100, 0) };
49        unsafe { wrmsr(0xc0000102, 0) };
50    }
51
52    pub unsafe fn load_static_ptr<const O: usize, T>() -> *const T {
53        let mut v;
54
55        unsafe {
56            asm!(
57                "mov {out}, gs:[{off}]",
58                off = const O,
59                out = out(reg) v,
60                options(pure, nomem, preserves_flags, nostack)
61            )
62        };
63
64        v
65    }
66
67    pub unsafe fn load_ptr<const O: usize, T>() -> *const T {
68        let mut v;
69
70        unsafe {
71            asm!(
72                "mov {out}, gs:[{off}]",
73                off = const O,
74                out = out(reg) v,
75                options(pure, readonly, preserves_flags, nostack)
76            )
77        };
78
79        v
80    }
81
82    pub unsafe fn load_volatile_usize<const O: usize>() -> usize {
83        let mut v;
84
85        unsafe {
86            asm!(
87                "mov {out}, gs:[{off}]",
88                off = const O,
89                out = out(reg) v,
90                options(preserves_flags, nostack)
91            )
92        };
93
94        v
95    }
96}