1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! Structures and functions for user space.
use memory_addr::VirtAddr;
use crate::TrapFrame;
/// Context to enter user space.
pub struct UspaceContext(TrapFrame);
impl UspaceContext {
/// Creates an empty context with all registers set to zero.
pub const fn empty() -> Self {
unsafe { core::mem::MaybeUninit::zeroed().assume_init() }
}
/// Creates a new context with the given entry point, user stack pointer,
/// and the argument.
pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self {
use crate::GdtStruct;
use x86_64::registers::rflags::RFlags;
Self(TrapFrame {
rdi: arg0 as _,
rip: entry as _,
cs: GdtStruct::UCODE64_SELECTOR.0 as _,
rflags: RFlags::INTERRUPT_FLAG.bits(), // IOPL = 0, IF = 1
rsp: ustack_top.as_usize() as _,
ss: GdtStruct::UDATA_SELECTOR.0 as _,
..Default::default()
})
}
/// Creates a new context from the given [`TrapFrame`].
///
/// It copies almost all registers except `CS` and `SS` which need to be
/// set to the user segment selectors.
pub const fn from(tf: &TrapFrame) -> Self {
use crate::GdtStruct;
let mut tf = *tf;
tf.cs = GdtStruct::UCODE64_SELECTOR.0 as _;
tf.ss = GdtStruct::UDATA_SELECTOR.0 as _;
Self(tf)
}
/// Gets the instruction pointer.
pub const fn get_ip(&self) -> usize {
self.0.rip as _
}
/// Gets the stack pointer.
pub const fn get_sp(&self) -> usize {
self.0.rsp as _
}
/// Sets the instruction pointer.
pub const fn set_ip(&mut self, rip: usize) {
self.0.rip = rip as _;
}
/// Sets the stack pointer.
pub const fn set_sp(&mut self, rsp: usize) {
self.0.rsp = rsp as _;
}
/// Sets the return value register.
pub const fn set_retval(&mut self, rax: usize) {
self.0.rax = rax as _;
}
/// Enters user space.
///
/// It restores the user registers and jumps to the user entry point
/// (saved in `rip`).
/// When an exception or syscall occurs, the kernel stack pointer is
/// switched to `kstack_top`.
///
/// # Safety
///
/// This function is unsafe because it changes processor mode and the stack.
pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! {
crate::asm::disable_irqs();
assert_eq!(super::gdt::read_tss_rsp0(), kstack_top);
unsafe {
core::arch::asm!("
mov rsp, {tf}
pop rax
pop rcx
pop rdx
pop rbx
pop rbp
pop rsi
pop rdi
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15
add rsp, 16 // skip vector, error_code
swapgs
iretq",
tf = in(reg) &self.0,
options(noreturn),
)
}
}
}