Skip to content

Commit

Permalink
Merge pull request #14 from cqs21/opt-msr
Browse files Browse the repository at this point in the history
x86_64: avoid accessing MSR registers when do trap/syscall
  • Loading branch information
equation314 authored Jul 8, 2024
2 parents 0f27d58 + 4651f01 commit 72a1a72
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 46 deletions.
15 changes: 11 additions & 4 deletions src/arch/x86_64/gdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ pub fn init() {
#[allow(const_item_mutation)]
GsBase::MSR.write(tss as *const _ as u64);

Star::write_raw(
SegmentSelector::new(entry_count as u16 + 4, PrivilegeLevel::Ring3).0,
SegmentSelector::new(entry_count as u16 + 2, PrivilegeLevel::Ring0).0,
);
let sysret = SegmentSelector::new(entry_count as u16 + 4, PrivilegeLevel::Ring3).0;
let syscall = SegmentSelector::new(entry_count as u16 + 2, PrivilegeLevel::Ring0).0;
Star::write_raw(sysret, syscall);

USER_SS = sysret + 8;
USER_CS = sysret + 16;
}
}

Expand All @@ -81,6 +83,11 @@ unsafe fn sgdt() -> DescriptorTablePointer {
gdt
}

#[no_mangle]
static mut USER_SS: u16 = 0;
#[no_mangle]
static mut USER_CS: u16 = 0;

const KCODE64: u64 = 0x00209800_00000000; // EXECUTABLE | USER_SEGMENT | PRESENT | LONG_MODE
const UCODE64: u64 = 0x0020F800_00000000; // EXECUTABLE | USER_SEGMENT | USER_MODE | PRESENT | LONG_MODE
const KDATA64: u64 = 0x00009200_00000000; // DATA_WRITABLE | USER_SEGMENT | PRESENT
Expand Down
64 changes: 22 additions & 42 deletions src/arch/x86_64/syscall.S
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,28 @@ syscall_return:
cli

# save callee-saved registers
mov ecx, 0xC0000100
rdmsr
shl rdx, 32
or rax, rdx
push rax # push fsbase
push r15
push r14
push r13
push r12
push rbp
push rbx

# save fsbase, kernel may use
rdfsbase rbx
push rbx

push rdi
push rdi # keep rsp 16 bytes align
mov gs:4, rsp # store kernel rsp -> TSS.sp0
mov rsp, rdi # set rsp = bottom of trap frame

# pop fsbase gsbase
swapgs # store kernel gsbase
mov ecx, 0xC0000100
mov edx, [rsp + 18*8+4]
mov eax, [rsp + 18*8]
wrmsr # pop fsbase
mov ecx, 0xC0000101
mov edx, [rsp + 19*8+4]
mov eax, [rsp + 19*8]
wrmsr # pop gsbase
mov rax, [rsp + 18*8]
wrfsbase rax
mov rax, [rsp + 19*8]
wrgsbase rax

pop rax
pop rbx
Expand Down Expand Up @@ -61,25 +56,13 @@ syscall_return:
cmp dword ptr [rsp + 4*8], 0x100 # syscall?
je sysret
iret:
# get user cs from STAR MSR
mov ecx, 0xC0000081
rdmsr # msr[ecx] => edx:eax
shr edx, 16 # dx = user_cs32
lea ax, [edx + 8] # ax = user_ss
add dx, 16 # dx = user_cs64

# construct trap frame
push rax # push ss
push [USER_SS] # push ss
push [rsp - 8*8] # push rsp
push [rsp + 3*8] # push rflags
push rdx # push cs
push [USER_CS] # push cs
push [rsp + 4*8] # push rip

# recover rcx, rdx, rax
mov rax, [rsp - 11*8]
mov rcx, [rsp - 9*8]
mov rdx, [rsp - 8*8]

iretq

sysret:
Expand Down Expand Up @@ -137,32 +120,29 @@ trap_syscall_entry:
push rbx
push rax

# push fsbase gsbase
mov ecx, 0xC0000100
rdmsr
mov [rsp + 18*8+4], edx
mov [rsp + 18*8], eax
mov ecx, 0xC0000102 # kernelgs
rdmsr
mov [rsp + 19*8+4], edx
mov [rsp + 19*8], eax
# save user fsbase/gsbase
rdfsbase rbx
mov [rsp + 18*8], rbx
swapgs
rdgsbase rbx
mov [rsp + 19*8], rbx
swapgs

# restore callee-saved registers
mov rsp, gs:4 # load kernel rsp <- TSS.sp0
pop rbx
pop rbx

# restore fsbase of kernel
pop rbx
wrfsbase rbx

pop rbx
pop rbp
pop r12
pop r13
pop r14
pop r15

pop rax
mov ecx, 0xC0000100
mov rdx, rax
shr rdx, 32
wrmsr # pop fsbase

# go back to Rust
ret
7 changes: 7 additions & 0 deletions src/arch/x86_64/syscall.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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;
Expand All @@ -18,6 +19,12 @@ pub fn init() {
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
Expand Down

0 comments on commit 72a1a72

Please sign in to comment.