From 351d77ad9b469faf236c373ca0c88782602bf69c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 11 Jul 2022 18:51:05 +0200 Subject: [PATCH] Improve floating point handling. --- src/context/arch/x86_64.rs | 43 ++++++++------------------------------ src/context/context.rs | 35 +++++++++++++++++++++++++++---- src/context/list.rs | 10 ++------- src/context/mod.rs | 9 +------- src/context/switch.rs | 4 ++-- src/memory/mod.rs | 9 +++++++- src/scheme/proc.rs | 2 +- 7 files changed, 54 insertions(+), 58 deletions(-) diff --git a/src/context/arch/x86_64.rs b/src/context/arch/x86_64.rs index 09d0a79..3066e50 100644 --- a/src/context/arch/x86_64.rs +++ b/src/context/arch/x86_64.rs @@ -13,6 +13,9 @@ pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false); const ST_RESERVED: u128 = 0xFFFF_FFFF_FFFF_0000_0000_0000_0000_0000; +pub const KFX_SIZE: usize = 512; +pub const KFX_ALIGN: usize = 16; + #[derive(Clone, Debug)] #[repr(C)] pub struct Context { @@ -46,21 +49,11 @@ pub struct Context { /// running. With fsgsbase, this is neither saved nor restored upon every syscall (there is no /// need to!), and thus it must be re-read from the register before copying this struct. pub(crate) gsbase: usize, - /// FX valid? - loadable: AbiCompatBool, -} - -#[repr(u8)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum AbiCompatBool { - False, - True, } impl Context { pub fn new() -> Context { Context { - loadable: AbiCompatBool::False, fx: 0, cr3: 0, rflags: 0, @@ -80,10 +73,7 @@ impl Context { self.cr3 } - pub fn get_fx_regs(&self) -> Option { - if self.loadable == AbiCompatBool::False { - return None; - } + pub fn get_fx_regs(&self) -> FloatRegisters { let mut regs = unsafe { *(self.fx as *const FloatRegisters) }; regs._reserved = 0; let mut new_st = regs.st_space; @@ -92,14 +82,10 @@ impl Context { *st &= !ST_RESERVED; } regs.st_space = new_st; - Some(regs) + regs } - pub fn set_fx_regs(&mut self, mut new: FloatRegisters) -> bool { - if self.loadable == AbiCompatBool::False { - return false; - } - + pub fn set_fx_regs(&mut self, mut new: FloatRegisters) { { let old = unsafe { &*(self.fx as *const FloatRegisters) }; new._reserved = old._reserved; @@ -117,7 +103,6 @@ impl Context { unsafe { *(self.fx as *mut FloatRegisters) = new; } - true } pub fn set_fx(&mut self, address: usize) { @@ -220,20 +205,12 @@ pub unsafe extern "C" fn switch_to(_prev: &mut Context, _next: &mut Context) { // save processor SSE/FPU/AVX state in `prev.fx` pointee fxsave64 [rax] - // set `prev.loadable` to true - mov BYTE PTR [rdi + {off_loadable}], {true} - // compare `next.loadable` with true - cmp BYTE PTR [rsi + {off_loadable}], {true} - je 3f - - fninit - jmp 3f - -2: + // load `next.fx` mov rax, [rsi + {off_fx}] + + // load processor SSE/FPU/AVX state from `next.fx` pointee fxrstor64 [rax] -3: // Save the current CR3, and load the next CR3 if not identical mov rcx, cr3 mov [rdi + {off_cr3}], rcx @@ -292,7 +269,6 @@ pub unsafe extern "C" fn switch_to(_prev: &mut Context, _next: &mut Context) { off_fx = const(offset_of!(Cx, fx)), off_cr3 = const(offset_of!(Cx, cr3)), off_rflags = const(offset_of!(Cx, rflags)), - off_loadable = const(offset_of!(Cx, loadable)), off_rbx = const(offset_of!(Cx, rbx)), off_r12 = const(offset_of!(Cx, r12)), @@ -308,7 +284,6 @@ pub unsafe extern "C" fn switch_to(_prev: &mut Context, _next: &mut Context) { MSR_FSBASE = const(x86::msr::IA32_FS_BASE), MSR_KERNELGSBASE = const(x86::msr::IA32_KERNEL_GSBASE), - true = const(AbiCompatBool::True as u8), switch_hook = sym crate::context::switch_finish_hook, options(noreturn), ); diff --git a/src/context/context.rs b/src/context/context.rs index 082ef9e..2c8b2ef 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -18,6 +18,7 @@ use crate::context::arch; use crate::context::file::{FileDescriptor, FileDescription}; use crate::context::memory::{AddrSpace, new_addrspace, UserGrants}; use crate::ipi::{ipi, IpiKind, IpiTarget}; +use crate::memory::Enomem; use crate::scheme::{SchemeNamespace, FileHandle}; use crate::sync::WaitMap; @@ -219,11 +220,11 @@ pub struct Context { /// The architecture specific context pub arch: arch::Context, /// Kernel FX - used to store SIMD and FPU registers on context switch - pub kfx: Option>, + pub kfx: Option>, /// Kernel stack pub kstack: Option>, /// Kernel signal backup: Registers, Kernel FX, Kernel Stack, Signal number - pub ksig: Option<(arch::Context, Option>, Option>, u8)>, + pub ksig: Option<(arch::Context, Option>, Option>, u8)>, /// Restore ksig context on next switch pub ksig_restore: bool, /// Address space containing a page table lock, and grants. Normally this will have a value, @@ -278,14 +279,14 @@ impl AlignedBox { } }; #[inline(always)] - pub fn try_zeroed() -> Result + pub fn try_zeroed() -> Result where T: ValidForZero, { Ok(unsafe { let ptr = crate::ALLOCATOR.alloc_zeroed(Self::LAYOUT); if ptr.is_null() { - return Err(Error::new(ENOMEM))?; + return Err(Enomem)?; } Self { inner: Unique::new_unchecked(ptr.cast()), @@ -307,6 +308,25 @@ impl Drop for AlignedBox { } } } +impl core::ops::Deref for AlignedBox { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.inner.as_ptr() } + } +} +impl core::ops::DerefMut for AlignedBox { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.inner.as_ptr() } + } +} +impl Clone for AlignedBox { + fn clone(&self) -> Self { + let mut new = Self::try_zeroed().unwrap_or_else(|_| alloc::alloc::handle_alloc_error(Self::LAYOUT)); + T::clone_from(&mut new, self); + new + } +} impl Context { pub fn new(id: ContextId) -> Result { @@ -545,4 +565,11 @@ impl Context { self.arch.set_page_utable(physaddr.data()); self.addr_space.replace(addr_space) } + + pub fn init_fx(&mut self) -> Result<(), Enomem> { + let mut fx = AlignedBox::<[u8; arch::KFX_SIZE], {arch::KFX_ALIGN}>::try_zeroed()?; + self.arch.set_fx(fx.as_mut_ptr() as usize); + self.kfx = Some(fx); + Ok(()) + } } diff --git a/src/context/list.rs b/src/context/list.rs index a745955..7019a14 100644 --- a/src/context/list.rs +++ b/src/context/list.rs @@ -79,12 +79,8 @@ impl ContextList { let context_lock = self.new_context()?; { let mut context = context_lock.write(); - let mut fx = unsafe { - // TODO: Alignment must match, the following can be UB. Use AlignedBox. - let ptr = crate::ALLOCATOR.alloc_zeroed(Layout::from_size_align_unchecked(1024, 16)) as *mut [u8; 1024]; - if ptr.is_null() { return Err(Error::new(ENOMEM)); } - Box::from_raw(ptr) - }; + context.init_fx()?; + let mut stack = vec![0; 65_536].into_boxed_slice(); let offset = stack.len() - mem::size_of::(); @@ -102,9 +98,7 @@ impl ContextList { context.arch.set_context_handle(); } - context.arch.set_fx(fx.as_ptr() as usize); context.arch.set_stack(stack.as_ptr() as usize + offset); - context.kfx = Some(fx); context.kstack = Some(stack); } Ok(context_lock) diff --git a/src/context/mod.rs b/src/context/mod.rs index 6b80e0f..a45efdc 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -57,15 +57,8 @@ pub fn init() { let mut contexts = contexts_mut(); let context_lock = contexts.new_context().expect("could not initialize first context"); let mut context = context_lock.write(); - let fx = unsafe { - // TODO: Alignment must match, the following can be UB. Use AlignedBox. - let ptr = crate::ALLOCATOR.alloc_zeroed(Layout::from_size_align_unchecked(1024, 16)) as *mut [u8; 1024]; - assert!(!ptr.is_null(), "failed to allocate FX to kmain!"); - Box::from_raw(ptr) - }; + context.init_fx().expect("failed to allocate FX for first context"); - context.arch.set_fx(fx.as_ptr() as usize); - context.kfx = Some(fx); context.status = Status::Runnable; context.running = true; context.cpu_id = Some(crate::cpu_id()); diff --git a/src/context/switch.rs b/src/context/switch.rs index 9d7c38c..e641142 100644 --- a/src/context/switch.rs +++ b/src/context/switch.rs @@ -30,13 +30,13 @@ unsafe fn update(context: &mut Context, cpu_id: usize) { context.arch = ksig.0; if let Some(ref mut kfx) = context.kfx { - kfx.clone_from_slice(&ksig.1.expect("context::switch: ksig kfx not set with ksig_restore")); + kfx.copy_from_slice(&*ksig.1.expect("context::switch: ksig kfx not set with ksig_restore")); } else { panic!("context::switch: kfx not set with ksig_restore"); } if let Some(ref mut kstack) = context.kstack { - kstack.clone_from_slice(&ksig.2.expect("context::switch: ksig kstack not set with ksig_restore")); + kstack.copy_from_slice(&ksig.2.expect("context::switch: ksig kstack not set with ksig_restore")); } else { panic!("context::switch: kstack not set with ksig_restore"); } diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 42dc53b..e72f5af 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -10,7 +10,8 @@ use rmm::{ FrameAllocator, FrameCount, }; -use syscall::{PartialAllocStrategy, PhysallocFlags}; +use crate::syscall::flag::{PartialAllocStrategy, PhysallocFlags}; +use crate::syscall::error::{ENOMEM, Error}; /// A memory map area #[derive(Copy, Clone, Debug, Default)] @@ -125,3 +126,9 @@ impl Iterator for FrameIter { #[derive(Debug)] pub struct Enomem; + +impl From for Error { + fn from(_: Enomem) -> Self { + Self::new(ENOMEM) + } +} diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index fc2802a..5318b99 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -575,7 +575,7 @@ impl Scheme for ProcScheme { // In the rare case of not having floating // point registers uninitiated, return // empty everything. - let fx = context.arch.get_fx_regs().unwrap_or_default(); + let fx = context.kfx.as_ref().map(|_| context.arch.get_fx_regs()).unwrap_or_default(); Ok((Output { float: fx }, mem::size_of::())) })?, RegsKind::Int => try_stop_context(info.pid, |context| match unsafe { ptrace::regs_for(&context) } {