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
use super::UserContext;
use core::arch::global_asm;
use x86_64::registers::control::{Cr4, Cr4Flags};
use x86_64::registers::model_specific::{Efer, EferFlags, LStar, SFMask};
use x86_64::registers::rflags::RFlags;
use x86_64::VirtAddr;
global_asm!(include_str!("syscall.S"));
pub fn init() {
let cpuid = raw_cpuid::CpuId::new();
unsafe {
// enable `syscall` instruction
assert!(cpuid
.get_extended_processor_and_feature_identifiers()
.unwrap()
.has_syscall_sysret());
Efer::update(|efer| {
efer.insert(EferFlags::SYSTEM_CALL_EXTENSIONS);
});
// enable `FSGSBASE` instructions
assert!(cpuid.get_extended_feature_info().unwrap().has_fsgsbase());
Cr4::update(|cr4| {
cr4.insert(Cr4Flags::FSGSBASE);
});
// flags to clear on syscall
// copy from Linux 5.0
// TF|DF|IF|IOPL|AC|NT
const RFLAGS_MASK: u64 = 0x47700;
LStar::write(VirtAddr::new(syscall_entry as usize as u64));
SFMask::write(RFlags::from_bits(RFLAGS_MASK).unwrap());
}
}
extern "sysv64" {
fn syscall_entry();
fn syscall_return(regs: &mut UserContext);
}
impl UserContext {
/// Go to user space with the context, and come back when a trap occurs.
///
/// On return, the context will be reset to the status before the trap.
/// Trap reason and error code will be placed at `trap_num` and `error_code`.
///
/// If the trap was triggered by `syscall` instruction, the `trap_num` will be set to `0x100`.
///
/// If `trap_num` is `0x100`, it will go user by `sysret` (`rcx` and `r11` are dropped),
/// otherwise it will use `iret`.
///
/// # Example
/// ```no_run
/// use trapframe::{UserContext, GeneralRegs};
///
/// // init user space context
/// let mut context = UserContext {
/// general: GeneralRegs {
/// rip: 0x1000,
/// rsp: 0x10000,
/// ..Default::default()
/// },
/// ..Default::default()
/// };
/// // go to user
/// context.run();
/// // back from user
/// println!("back from user: {:#x?}", context);
/// ```
pub fn run(&mut self) {
unsafe {
syscall_return(self);
}
}
}