Implement setting FS/GS offset on x86

This commit is contained in:
Jeremy Soller
2022-08-20 21:21:32 -06:00
parent 01e4bc899e
commit c750ee26a8
4 changed files with 70 additions and 14 deletions

View File

@@ -16,11 +16,12 @@ pub const GDT_NULL: usize = 0;
pub const GDT_KERNEL_CODE: usize = 1;
pub const GDT_KERNEL_DATA: usize = 2;
pub const GDT_KERNEL_KPCR: usize = 3;
pub const GDT_USER_CODE32_UNUSED: usize = 4;
pub const GDT_USER_CODE: usize = 4;
pub const GDT_USER_DATA: usize = 5;
pub const GDT_USER_CODE: usize = 6;
pub const GDT_TSS: usize = 7;
pub const GDT_CPU_ID_CONTAINER: usize = 8;
pub const GDT_USER_FS: usize = 6;
pub const GDT_USER_GS: usize = 7;
pub const GDT_TSS: usize = 8;
pub const GDT_CPU_ID_CONTAINER: usize = 9;
pub const GDT_A_PRESENT: u8 = 1 << 7;
pub const GDT_A_RING_0: u8 = 0 << 5;
@@ -52,7 +53,7 @@ static mut INIT_GDT: [GdtEntry; 4] = [
];
#[thread_local]
pub static mut GDT: [GdtEntry; 9] = [
pub static mut GDT: [GdtEntry; 10] = [
// Null
GdtEntry::new(0, 0, 0, 0),
// Kernel code
@@ -61,12 +62,14 @@ pub static mut GDT: [GdtEntry; 9] = [
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_PROTECTED_MODE),
// Kernel TLS
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_PROTECTED_MODE),
// Dummy 32-bit user code - apparently necessary for SYSEXIT. We restrict it to ring 0 anyway.
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_PROTECTED_MODE),
// User (32-bit) code
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_PROTECTED_MODE),
// User data
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_PROTECTED_MODE),
// User (64-bit) code
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_PROTECTED_MODE),
// User FS (for TLS)
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_PROTECTED_MODE),
// User GS (for TLS)
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_PROTECTED_MODE),
// TSS
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_TSS_AVAIL, 0),
// Unused entry which stores the CPU ID. This is necessary for paranoid interrupts as they have

View File

@@ -322,7 +322,9 @@ pub unsafe extern "C" fn usermode(_ip: usize, _sp: usize, _arg: usize, _is_singl
mov eax, {user_data_seg_selector}
mov ds, eax
mov es, eax
mov eax, {user_fs_seg_selector}
mov fs, eax
mov eax, {user_gs_seg_selector}
mov gs, eax
// Set up iret stack
@@ -353,6 +355,8 @@ pub unsafe extern "C" fn usermode(_ip: usize, _sp: usize, _arg: usize, _is_singl
shift_singlestep = const(SHIFT_SINGLESTEP),
user_data_seg_selector = const(gdt::GDT_USER_DATA << 3 | 3),
user_code_seg_selector = const(gdt::GDT_USER_CODE << 3 | 3),
user_fs_seg_selector = const(gdt::GDT_USER_FS << 3 | 3),
user_gs_seg_selector = const(gdt::GDT_USER_GS << 3 | 3),
options(noreturn),
);

View File

@@ -3,7 +3,7 @@ use core::sync::atomic::AtomicBool;
use alloc::sync::Arc;
use crate::gdt::{GDT, GDT_TSS};
use crate::gdt::{GDT, GDT_USER_FS, GDT_USER_GS};
use crate::paging::{RmmA, RmmArch, TableKind};
use crate::syscall::FloatRegisters;
@@ -135,8 +135,10 @@ pub unsafe fn switch_to(prev: &mut super::Context, next: &mut super::Context) {
);
{
prev.arch.gsbase = GDT[GDT_TSS].offset() as usize;
GDT[GDT_TSS].set_offset(next.arch.gsbase as u32);
prev.arch.fsbase = GDT[GDT_USER_FS].offset() as usize;
GDT[GDT_USER_FS].set_offset(next.arch.fsbase as u32);
prev.arch.gsbase = GDT[GDT_USER_GS].offset() as usize;
GDT[GDT_USER_GS].set_offset(next.arch.gsbase as u32);
}
match next.addr_space {

View File

@@ -404,7 +404,24 @@ impl ProcScheme {
Err(Error::new(EINVAL))
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[cfg(target_arch = "x86")]
fn read_env_regs(&self, info: &Info) -> Result<EnvRegisters> {
let (fsbase, gsbase) = if info.pid == context::context_id() {
unsafe {
(
crate::gdt::GDT[crate::gdt::GDT_USER_FS].offset() as u64,
crate::gdt::GDT[crate::gdt::GDT_USER_GS].offset() as u64
)
}
} else {
try_stop_context(info.pid, |context| {
Ok((context.arch.fsbase as u64, context.arch.gsbase as u64))
})?
};
Ok(EnvRegisters { fsbase: fsbase as _, gsbase: gsbase as _ })
}
#[cfg(target_arch = "x86_64")]
fn read_env_regs(&self, info: &Info) -> Result<EnvRegisters> {
let (fsbase, gsbase) = if info.pid == context::context_id() {
#[cfg(not(feature = "x86_fsgsbase"))]
@@ -442,7 +459,37 @@ impl ProcScheme {
Err(Error::new(EINVAL))
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[cfg(target_arch = "x86")]
fn write_env_regs(&self, info: &Info, regs: EnvRegisters) -> Result<()> {
println!("{:?}", regs);
if !(RmmA::virt_is_valid(VirtualAddress::new(regs.fsbase as usize)) && RmmA::virt_is_valid(VirtualAddress::new(regs.gsbase as usize))) {
return Err(Error::new(EINVAL));
}
if info.pid == context::context_id() {
unsafe {
crate::gdt::GDT[crate::gdt::GDT_USER_FS].set_offset(regs.fsbase);
crate::gdt::GDT[crate::gdt::GDT_USER_GS].set_offset(regs.gsbase);
match context::contexts().current().ok_or(Error::new(ESRCH))?.write().arch {
ref mut arch => {
arch.fsbase = regs.fsbase as usize;
arch.gsbase = regs.gsbase as usize;
}
}
}
} else {
try_stop_context(info.pid, |context| {
context.arch.fsbase = regs.fsbase as usize;
context.arch.gsbase = regs.gsbase as usize;
Ok(())
})?;
}
Ok(())
}
#[cfg(target_arch = "x86_64")]
fn write_env_regs(&self, info: &Info, regs: EnvRegisters) -> Result<()> {
if !(RmmA::virt_is_valid(VirtualAddress::new(regs.fsbase as usize)) && RmmA::virt_is_valid(VirtualAddress::new(regs.gsbase as usize))) {
return Err(Error::new(EINVAL));