Skip to content

Commit 97a6260

Browse files
x86_64: make use of FSGSBASE instructions if available
Signed-off-by: Anhad Singh <[email protected]>
1 parent 1977333 commit 97a6260

File tree

3 files changed

+130
-31
lines changed

3 files changed

+130
-31
lines changed

src/aero_kernel/src/arch/x86_64/io.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
//! Wrapper functions for the hardware IO using respective assembly instructions.
1919
20+
use crate::mem::paging::VirtAddr;
21+
2022
pub const IA32_EFER: u32 = 0xc0000080;
2123

2224
/// Map of BASE Address of FS (R/W) See Table 35-2.
@@ -226,3 +228,91 @@ impl BasedPort {
226228
unsafe { V::port_out(self.base + offset, value) }
227229
}
228230
}
231+
232+
#[inline]
233+
pub unsafe fn wrfsbase(base: VirtAddr) {
234+
asm!("wrfsbase {}", in(reg) base.as_u64(), options(nostack, preserves_flags));
235+
}
236+
237+
#[inline]
238+
pub fn rdfsbase() -> VirtAddr {
239+
unsafe {
240+
let val: u64;
241+
asm!("rdfsbase {}", out(reg) val, options(nomem, nostack, preserves_flags));
242+
243+
VirtAddr::new(val)
244+
}
245+
}
246+
247+
#[inline]
248+
pub unsafe fn wrgsbase(base: VirtAddr) {
249+
asm!("wrgsbase {}", in(reg) base.as_u64(), options(nostack, preserves_flags));
250+
}
251+
252+
#[inline]
253+
pub fn rdgsbase() -> VirtAddr {
254+
unsafe {
255+
let val: u64;
256+
asm!("rdgsbase {}", out(reg) val, options(nomem, nostack, preserves_flags));
257+
258+
VirtAddr::new(val)
259+
}
260+
}
261+
262+
/// # Safety
263+
/// The caller must ensure that the swap operation does not cause any undefined behavior.
264+
#[inline]
265+
pub unsafe fn swapgs() {
266+
asm!("swapgs", options(nostack, preserves_flags));
267+
}
268+
269+
// XXX: There is no variant of the {rd,wr}gsbase instructions for accessing `KERNEL_GS_BASE`, so we
270+
// wrap those in two `swapgs` instructions. This approach is still faster than reading/writing from
271+
// the `KERNEL_GS_BASE` MSR.
272+
273+
/// Sets the inactive `GS` segment's base address.
274+
#[inline]
275+
pub unsafe fn set_inactive_gsbase(base: VirtAddr) {
276+
if super::has_fsgsbase() {
277+
swapgs();
278+
wrgsbase(base);
279+
swapgs();
280+
} else {
281+
wrmsr(IA32_KERNEL_GSBASE, base.as_u64());
282+
}
283+
}
284+
285+
/// Returns the inactive `GS` segment's base address.
286+
#[inline]
287+
pub fn get_inactive_gsbase() -> VirtAddr {
288+
if super::has_fsgsbase() {
289+
// SAFETY: The GS base is swapped back to the original value after the read.
290+
unsafe { swapgs() };
291+
let base = rdgsbase();
292+
unsafe { swapgs() };
293+
294+
base
295+
} else {
296+
VirtAddr::new(unsafe { rdmsr(IA32_KERNEL_GSBASE) })
297+
}
298+
}
299+
300+
/// Returns the `FS` segment's base address.
301+
#[inline]
302+
pub fn get_fsbase() -> VirtAddr {
303+
if super::has_fsgsbase() {
304+
rdfsbase()
305+
} else {
306+
VirtAddr::new(unsafe { rdmsr(IA32_FS_BASE) })
307+
}
308+
}
309+
310+
/// Sets the `FS` segment's base address.
311+
#[inline]
312+
pub unsafe fn set_fsbase(base: VirtAddr) {
313+
if super::has_fsgsbase() {
314+
wrfsbase(base);
315+
} else {
316+
wrmsr(IA32_FS_BASE, base.as_u64());
317+
}
318+
}

src/aero_kernel/src/arch/x86_64/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use crate::{drivers, logger, rendy};
4141
use raw_cpuid::CpuId;
4242

4343
use limine::*;
44+
use spin::Once;
4445

4546
use self::interrupts::INTERRUPT_CONTROLLER;
4647

@@ -244,6 +245,17 @@ fn enable_xsave() {
244245
unsafe { controlregs::write_xcr0(xcr0) }
245246
}
246247

248+
pub fn has_fsgsbase() -> bool {
249+
static HAS_FSGSBASE: Once<bool> = Once::new();
250+
251+
*HAS_FSGSBASE.call_once(|| {
252+
CpuId::new()
253+
.get_extended_feature_info()
254+
.unwrap()
255+
.has_fsgsbase()
256+
})
257+
}
258+
247259
pub fn init_cpu() {
248260
unsafe {
249261
// Enable the no-execute page protection feature.
@@ -268,6 +280,10 @@ pub fn init_cpu() {
268280
cr4.insert(controlregs::Cr4Flags::OSFXSR);
269281
cr4.insert(controlregs::Cr4Flags::OSXMMEXCPT_ENABLE);
270282

283+
if has_fsgsbase() {
284+
cr4.insert(controlregs::Cr4Flags::FSGSBASE);
285+
}
286+
271287
controlregs::write_cr4(cr4);
272288
}
273289

src/aero_kernel/src/arch/x86_64/task.rs

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ struct Context {
7272
#[repr(u64)]
7373
#[derive(Debug, Copy, Clone)]
7474
pub enum AuxvType {
75-
AtNull = 0,
76-
AtPhdr = 3,
77-
AtPhEnt = 4,
78-
AtPhNum = 5,
79-
AtEntry = 9,
75+
Null = 0,
76+
Phdr = 3,
77+
PhEnt = 4,
78+
PhNum = 5,
79+
Entry = 9,
8080
}
8181

8282
/// Returns the first address outside the user range.
@@ -96,14 +96,8 @@ pub fn userland_last_address() -> VirtAddr {
9696
static CACHED: spin::Once<VirtAddr> = spin::Once::new();
9797

9898
*CACHED.call_once(|| {
99-
let virtual_mask_shift: u64;
10099
let la57 = crate::mem::paging::level_5_paging_enabled();
101-
102-
if la57 {
103-
virtual_mask_shift = 56;
104-
} else {
105-
virtual_mask_shift = 47;
106-
}
100+
let virtual_mask_shift: u64 = if la57 { 56 } else { 47 };
107101

108102
VirtAddr::new((1u64 << virtual_mask_shift) - Size4KiB::SIZE)
109103
})
@@ -386,13 +380,13 @@ impl ArchTask {
386380
let mut envp = Vec::new();
387381
let mut argp = Vec::new();
388382

389-
loaded_binary
390-
.envv
391-
.map(|envv| envp = envv.push_into_stack(&mut stack));
383+
if let Some(envv) = loaded_binary.envv {
384+
envp = envv.push_into_stack(&mut stack);
385+
}
392386

393-
loaded_binary
394-
.argv
395-
.map(|argv| argp = argv.push_into_stack(&mut stack));
387+
if let Some(argv) = loaded_binary.argv {
388+
argp = argv.push_into_stack(&mut stack);
389+
}
396390

397391
stack.align_down();
398392

@@ -409,16 +403,16 @@ impl ArchTask {
409403
unsafe {
410404
let hdr: [(AuxvType, usize); 4] = [
411405
(
412-
AuxvType::AtPhdr,
406+
AuxvType::Phdr,
413407
(p2_header.ph_offset() + loaded_binary.base_addr.as_u64()) as usize,
414408
),
415-
(AuxvType::AtPhEnt, p2_header.ph_entry_size() as usize),
416-
(AuxvType::AtPhNum, p2_header.ph_count() as usize),
417-
(AuxvType::AtEntry, p2_header.entry_point() as usize),
409+
(AuxvType::PhEnt, p2_header.ph_entry_size() as usize),
410+
(AuxvType::PhNum, p2_header.ph_count() as usize),
411+
(AuxvType::Entry, p2_header.entry_point() as usize),
418412
];
419413

420414
stack.write(0usize); // Make it 16 bytes aligned
421-
stack.write(AuxvType::AtNull);
415+
stack.write(AuxvType::Null);
422416
stack.write(hdr);
423417
}
424418

@@ -504,8 +498,7 @@ impl ArchTask {
504498
/// belongs to. This is required since we also update the GS base register with the
505499
/// `base` immediately (not waiting for a switch).
506500
pub unsafe fn set_gs_base(&mut self, base: VirtAddr) {
507-
io::wrmsr(io::IA32_KERNEL_GSBASE, base.as_u64());
508-
self.gs_base = base;
501+
io::set_inactive_gsbase(base);
509502
}
510503

511504
/// Returns the saved FS base for this task.
@@ -520,8 +513,7 @@ impl ArchTask {
520513
/// belongs to. This is required since we also update the FS base register with the
521514
/// `base` immediately (not waiting for a switch).
522515
pub unsafe fn set_fs_base(&mut self, base: VirtAddr) {
523-
io::wrmsr(io::IA32_FS_BASE, base.as_u64());
524-
self.fs_base = base;
516+
io::set_fsbase(base);
525517
}
526518
}
527519

@@ -559,11 +551,12 @@ pub fn arch_task_spinup(from: &mut ArchTask, to: &ArchTask) {
559551
super::gdt::get_task_state_segment().rsp[0] = kstackp;
560552
io::wrmsr(io::IA32_SYSENTER_ESP, kstackp);
561553

562-
// switch to the new FS base.
563-
io::wrmsr(io::IA32_FS_BASE, to.fs_base.as_u64());
554+
// Preserve and restore the %fs, %gs bases.
555+
from.fs_base = io::get_fsbase();
556+
from.gs_base = io::get_inactive_gsbase();
564557

565-
// update the swap GS target to point to the new GS base.
566-
io::wrmsr(io::IA32_KERNEL_GSBASE, to.gs_base.as_u64());
558+
io::set_fsbase(to.fs_base);
559+
io::set_inactive_gsbase(to.gs_base);
567560

568561
if let Some(fpu) = from.fpu_storage.as_mut() {
569562
xsave(fpu);

0 commit comments

Comments
 (0)