Improve floating point handling.

This commit is contained in:
4lDO2
2022-07-11 18:51:05 +02:00
parent 94578efd1e
commit 351d77ad9b
7 changed files with 54 additions and 58 deletions

View File

@@ -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<FloatRegisters> {
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),
);

View File

@@ -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<Box<[u8]>>,
pub kfx: Option<AlignedBox<[u8; {arch::KFX_SIZE}], {arch::KFX_ALIGN}>>,
/// Kernel stack
pub kstack: Option<Box<[u8]>>,
/// Kernel signal backup: Registers, Kernel FX, Kernel Stack, Signal number
pub ksig: Option<(arch::Context, Option<Box<[u8]>>, Option<Box<[u8]>>, u8)>,
pub ksig: Option<(arch::Context, Option<AlignedBox<[u8; arch::KFX_SIZE], {arch::KFX_ALIGN}>>, Option<Box<[u8]>>, 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<T, const ALIGN: usize> AlignedBox<T, ALIGN> {
}
};
#[inline(always)]
pub fn try_zeroed() -> Result<Self>
pub fn try_zeroed() -> Result<Self, Enomem>
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<T, const ALIGN: usize> Drop for AlignedBox<T, ALIGN> {
}
}
}
impl<T, const ALIGN: usize> core::ops::Deref for AlignedBox<T, ALIGN> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.inner.as_ptr() }
}
}
impl<T, const ALIGN: usize> core::ops::DerefMut for AlignedBox<T, ALIGN> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.inner.as_ptr() }
}
}
impl<T: Clone + ValidForZero, const ALIGN: usize> Clone for AlignedBox<T, ALIGN> {
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<Context> {
@@ -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(())
}
}

View File

@@ -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::<usize>();
@@ -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)

View File

@@ -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());

View File

@@ -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");
}

View File

@@ -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<Enomem> for Error {
fn from(_: Enomem) -> Self {
Self::new(ENOMEM)
}
}

View File

@@ -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::<FloatRegisters>()))
})?,
RegsKind::Int => try_stop_context(info.pid, |context| match unsafe { ptrace::regs_for(&context) } {