Unverified Commit 41eff498 authored by Stefan Lankes's avatar Stefan Lankes
Browse files

introduce feature flag to enable/disable FSGSBASE support

parent 168e7a15
......@@ -38,11 +38,12 @@ name = "measure_startup_time"
harness = false
[features]
default = ["pci", "acpi"]
default = ["pci", "acpi", "fsgsbase"]
vga = []
newlib = []
pci = []
acpi = []
fsgsbase = []
[dev-dependencies]
x86 = { version = "0.34.*", default-features = false }
......
......@@ -50,8 +50,10 @@ pub mod virtio_net;
#[cfg(not(test))]
global_asm!(include_str!("start.s"));
#[cfg(not(test))]
#[cfg(all(not(test), not(fsgsbase)))]
global_asm!(include_str!("switch.s"));
#[cfg(all(not(test), fsgsbase))]
global_asm!(include_str!("switch_fsgsbase.s"));
const SERIAL_PORT_BAUDRATE: u32 = 115_200;
......
......@@ -19,7 +19,7 @@ use core::arch::x86_64::__rdtscp as rdtscp;
use core::arch::x86_64::_rdtsc as rdtsc;
use core::convert::TryInto;
use core::sync::atomic::spin_loop_hint;
use core::{fmt, ptr, u32};
use core::{fmt, u32};
const IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP: u64 = 1 << 16;
const IA32_MISC_ENABLE_SPEEDSTEP_LOCK: u64 = 1 << 20;
......@@ -50,13 +50,6 @@ static mut SUPPORTS_FSGS: bool = false;
static mut RUN_ON_HYPERVISOR: bool = false;
static mut TIMESTAMP_FUNCTION: unsafe fn() -> u64 = get_timestamp_rdtsc;
extern "C" {
static Lpatch0: u64;
static Lpatch1: u64;
static Lpatch2: u64;
static Lpatch3: u64;
}
#[repr(C, align(16))]
pub struct XSaveLegacyRegion {
pub fpu_control_word: u16,
......@@ -804,27 +797,13 @@ pub fn configure() {
if supports_fsgs() {
cr4.insert(Cr4::CR4_ENABLE_FSGSBASE);
// enable the usage of fsgsbase during a context switch
// => replace short jump with nops
// => see switch.s
unsafe {
#[cfg(not(feature = "newlib"))]
let base: u64 = environment::get_base_address().as_u64();
// newlib based application doesn't support relocatable binaries
// => we don't have to recalulate the address
#[cfg(feature = "newlib")]
let base: u64 = 0;
let addr = &Lpatch0 as *const _ as u64;
ptr::write_bytes((addr + base) as *mut u8, 0x90, 2);
let addr = &Lpatch1 as *const _ as u64;
ptr::write_bytes((addr + base) as *mut u8, 0x90, 2);
let addr = &Lpatch2 as *const _ as u64;
ptr::write_bytes((addr + base) as *mut u8, 0x90, 2);
let addr = &Lpatch3 as *const _ as u64;
ptr::write_bytes((addr + base) as *mut u8, 0x90, 2);
}
#[cfg(feature = "fsgsbase")]
info!("Enable FSGSBASE support");
}
#[cfg(feature = "fsgsbase")]
if !supports_fsgs() {
error!("FSGSBASE support is enabled, but the processor doesn't support it!");
loop {}
}
debug!("Set CR4 to 0x{:x}", cr4);
......@@ -1025,63 +1004,90 @@ pub fn get_frequency() -> u16 {
}
#[inline]
#[cfg(feature = "fsgsbase")]
pub fn readfs() -> usize {
let val: u64;
unsafe {
if supports_fsgs() {
llvm_asm!("rdfsbase $0" : "=r"(val) ::: "volatile");
} else {
let rdx: u64;
let rax: u64;
llvm_asm!("rdfsbase $0" : "=r"(val) ::: "volatile");
}
llvm_asm!("rdmsr" : "=%rdx"(rdx), "=%rax"(rax) : "%rcx"(0xc0000100u64) :: "volatile");
val as usize
}
val = (rdx << 32) | rax;
}
#[inline]
#[cfg(not(feature = "fsgsbase"))]
pub fn readfs() -> usize {
let rdx: u64;
let rax: u64;
unsafe {
llvm_asm!("rdmsr" : "=%rdx"(rdx), "=%rax"(rax) : "%rcx"(0xc0000100u64) :: "volatile");
}
val as usize
((rdx << 32) | rax) as usize
}
#[inline]
#[cfg(feature = "fsgsbase")]
pub fn readgs() -> usize {
let val: u64;
unsafe {
if supports_fsgs() {
llvm_asm!("rdgsbase $0" : "=r"(val) ::: "volatile");
} else {
let rdx: u64;
let rax: u64;
llvm_asm!("rdgsbase $0" : "=r"(val) ::: "volatile");
}
llvm_asm!("rdmsr" : "=%rdx"(rdx), "=%rax"(rax) : "%rcx"(0xc0000101u64) :: "volatile");
val as usize
}
val = (rdx << 32) | rax;
}
#[inline]
#[cfg(not(feature = "fsgsbase"))]
pub fn readgs() -> usize {
let rdx: u64;
let rax: u64;
unsafe {
llvm_asm!("rdmsr" : "=%rdx"(rdx), "=%rax"(rax) : "%rcx"(0xc0000101u64) :: "volatile");
}
val as usize
((rdx << 32) | rax) as usize
}
#[inline]
#[cfg(feature = "fsgsbase")]
pub fn writefs(fs: usize) {
unsafe {
if supports_fsgs() {
llvm_asm!("wrfsbase $0" :: "r"(fs as u64) :: "volatile");
} else {
let edx = fs >> 32;
let eax = fs as u64 & (u32::MAX - 1) as u64;
llvm_asm!("wrfsbase $0" :: "r"(fs) :: "volatile");
}
}
llvm_asm!("wrmsr" :: "%rcx"(0xc0000100u64), "%rdx"(edx), "%rax"(eax) :: "volatile");
}
#[inline]
#[cfg(not(feature = "fsgsbase"))]
pub fn writefs(fs: usize) {
let rdx = fs >> 32;
let rax = fs & (u32::MAX - 1) as usize;
unsafe {
llvm_asm!("wrmsr" :: "%rcx"(0xc0000100u64), "%rdx"(rdx), "%rax"(rax) :: "volatile");
}
}
#[inline]
#[cfg(feature = "fsgsbase")]
pub fn writegs(gs: usize) {
unsafe {
llvm_asm!("wrgsbase $0" :: "r"(gs) :: "volatile");
}
}
#[inline]
#[cfg(not(feature = "fsgsbase"))]
pub fn writegs(gs: usize) {
let rdx = gs >> 32;
let rax = gs & (u32::MAX - 1) as usize;
unsafe {
llvm_asm!("wrgsbase $0" :: "r"(gs as u64) :: "volatile");
llvm_asm!("wrmsr" :: "%rcx"(0xc0000101u64), "%rdx"(rdx), "%rax"(rax) :: "volatile");
}
}
......
......@@ -8,10 +8,6 @@
.section .text
.global switch_to_task
.global switch_to_fpu_owner
.global Lpatch0
.global Lpatch1
.global Lpatch2
.global Lpatch3
.extern set_current_kernel_stack
.align 16
......@@ -36,18 +32,11 @@ switch_to_task:
push %r14
push %r15
// push fs registers
Lpatch0:
jmp Lrdgs0 // we patch later this jump to enable rdfsbase/rdgsbase
rdfsbaseq %rax
push %rax
jmp Lgo0
Lrdgs0:
mov $0xc0000100, %ecx
rdmsr
sub $8, %rsp
mov %edx, 4(%rsp)
mov %eax, (%rsp)
Lgo0:
// store the old stack pointer in the dereferenced first parameter\n\t\
// and load the new stack pointer in the second parameter.\n\t\
mov %rsp, (%rdi)
......@@ -59,18 +48,11 @@ Lgo0:
// set stack pointer in TSS
call set_current_kernel_stack
// restore context
Lpatch1:
jmp Lwrfsgs1 // we patch later this jump to enable wrfsbase/wrgsbase
pop %rax
wrfsbaseq %rax
jmp Lgo1
Lwrfsgs1:
mov $0xc0000100, %ecx
mov 4(%rsp), %edx
mov (%rsp), %eax
add $8, %rsp
wrmsr
Lgo1:
pop %r15
pop %r14
pop %r13
......@@ -115,18 +97,11 @@ switch_to_fpu_owner:
push %r14
push %r15
// push fs registers
Lpatch2:
jmp Lrdgs2 // we patch later this jump to enable rdfsbase/rdgsbase
rdfsbaseq %rax
push %rax
jmp Lgo2
Lrdgs2:
mov $0xc0000100, %ecx
rdmsr
sub $8, %rsp
mov %edx, 4(%rsp)
mov %eax, (%rsp)
Lgo2:
// store the old stack pointer in the dereferenced first parameter\n\t\
// and load the new stack pointer in the second parameter.\n\t\
mov %rsp, (%rdi)
......@@ -134,18 +109,11 @@ Lgo2:
// set stack pointer in TSS
call set_current_kernel_stack
// restore context
Lpatch3:
jmp Lwrfsgs3 // we patch later this jump to enable wrfsbase/wrgsbase
pop %rax
wrfsbaseq %rax
jmp Lgo3
Lwrfsgs3:
mov $0xc0000100, %ecx
mov 4(%rsp), %edx
mov (%rsp), %eax
add $8, %rsp
wrmsr
Lgo3:
pop %r15
pop %r14
pop %r13
......
// Copyright (c) 2020 Stefan Lankes, RWTH Aachen University
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
.section .text
.global switch_to_task
.global switch_to_fpu_owner
.extern set_current_kernel_stack
.align 16
switch_to_task:
// rdi = old_stack => the address to store the old rsp
// rsi = new_stack => stack pointer of the new task
pushfq
push %rax
push %rcx
push %rdx
push %rbx
push %rbp
push %rsi
push %rdi
push %r8
push %r9
push %r10
push %r11
push %r12
push %r13
push %r14
push %r15
// push fs registers
rdfsbaseq %rax
push %rax
// store the old stack pointer in the dereferenced first parameter\n\t\
// and load the new stack pointer in the second parameter.\n\t\
mov %rsp, (%rdi)
mov %rsi, %rsp
// Set task switched flag
mov %cr0, %rax
or $8, %rax
mov %rax, %cr0
// set stack pointer in TSS
call set_current_kernel_stack
// restore context
pop %rax
wrfsbaseq %rax
pop %r15
pop %r14
pop %r13
pop %r12
pop %r11
pop %r10
pop %r9
pop %r8
pop %rdi
pop %rsi
pop %rbp
pop %rbx
pop %rdx
pop %rcx
pop %rax
popfq
ret
/// The function triggers a context switch to an idle task or
/// a task, which is alread owner of the FPU.
/// Consequently the kernel don't set the task switched flag.
.align 16
switch_to_fpu_owner:
// rdi = old_stack => the address to store the old rsp
// rsi = new_stack => stack pointer of the new task
// store context
pushfq
push %rax
push %rcx
push %rdx
push %rbx
push %rbp
push %rsi
push %rdi
push %r8
push %r9
push %r10
push %r11
push %r12
push %r13
push %r14
push %r15
// push fs registers
rdfsbaseq %rax
push %rax
// store the old stack pointer in the dereferenced first parameter\n\t\
// and load the new stack pointer in the second parameter.\n\t\
mov %rsp, (%rdi)
mov %rsi, %rsp
// set stack pointer in TSS
call set_current_kernel_stack
// restore context
pop %rax
wrfsbaseq %rax
pop %r15
pop %r14
pop %r13
pop %r12
pop %r11
pop %r10
pop %r9
pop %r8
pop %rdi
pop %rsi
pop %rbp
pop %rbx
pop %rdx
pop %rcx
pop %rax
popfq
ret
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment