Improve floating point handling.
This commit is contained in:
@@ -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),
|
||||
);
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) } {
|
||||
|
||||
Reference in New Issue
Block a user