Implement setting FS/GS offset on x86
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user