diff --git a/src/arch/x86_64/gdt.rs b/src/arch/x86_64/gdt.rs index 9f62738..d7276c2 100644 --- a/src/arch/x86_64/gdt.rs +++ b/src/arch/x86_64/gdt.rs @@ -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; } } @@ -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 diff --git a/src/arch/x86_64/syscall.S b/src/arch/x86_64/syscall.S index dc96e5b..bf924c1 100644 --- a/src/arch/x86_64/syscall.S +++ b/src/arch/x86_64/syscall.S @@ -6,11 +6,6 @@ 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 @@ -18,6 +13,10 @@ syscall_return: 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 @@ -25,14 +24,10 @@ syscall_return: # 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 @@ -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: @@ -137,20 +120,23 @@ 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 @@ -158,11 +144,5 @@ trap_syscall_entry: pop r14 pop r15 - pop rax - mov ecx, 0xC0000100 - mov rdx, rax - shr rdx, 32 - wrmsr # pop fsbase - # go back to Rust ret diff --git a/src/arch/x86_64/syscall.rs b/src/arch/x86_64/syscall.rs index b2fc6a6..51d5044 100644 --- a/src/arch/x86_64/syscall.rs +++ b/src/arch/x86_64/syscall.rs @@ -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; @@ -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