Refine context switch for exception/signal handling
This commit is contained in:
parent
3c481d1297
commit
587f4debb3
@ -103,8 +103,9 @@ pub fn do_handle_exception(
|
||||
let user_context = unsafe { &mut *user_context };
|
||||
*user_context = CpuContext::from_sgx(&info.cpu_context);
|
||||
let xsave_area = info.xsave_area.as_mut_ptr();
|
||||
user_context.extra_context = ExtraContext::Xsave;
|
||||
user_context.extra_context = ExtraContext::XsaveOnStack;
|
||||
user_context.extra_context_ptr = xsave_area;
|
||||
user_context.extra_context_size = info.xsave_size;
|
||||
|
||||
// Try to do instruction emulation first
|
||||
if info.exception_vector == sgx_exception_vector_t::SGX_EXCEPTION_VECTOR_UD {
|
||||
|
@ -29,8 +29,9 @@ pub fn do_handle_interrupt(
|
||||
// The cpu context is overriden so that it is as if the syscall is called from where the
|
||||
// interrupt happened
|
||||
*context = CpuContext::from_sgx(&info.cpu_context);
|
||||
context.extra_context = ExtraContext::Xsave;
|
||||
context.extra_context = ExtraContext::XsaveOnStack;
|
||||
context.extra_context_ptr = info.xsave_area.as_mut_ptr();
|
||||
context.extra_context_size = info.xsave_size;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
|
@ -225,8 +225,8 @@ pub type stack_t = sigaltstack_t;
|
||||
pub struct mcontext_t {
|
||||
pub inner: CpuContext,
|
||||
// TODO: the fields should be csgsfs, err, trapno, oldmask, and cr2
|
||||
// The number should be 5 but we use extra 2 spaces to store something else in the CpuContext. Thus make it 3.
|
||||
_unused0: [u64; 3],
|
||||
// The number should be 5 but we use extra 3 spaces to store something else in the CpuContext. Thus make it 2.
|
||||
_unused0: [u64; 2],
|
||||
// TODO: this field should be `fpregs: fpregset_t,`
|
||||
_unused1: usize,
|
||||
_reserved: [u64; 8],
|
||||
|
@ -5,18 +5,19 @@ use super::{SigAction, SigActionFlags, SigDefaultAction, SigSet, Signal};
|
||||
use crate::lazy_static::__Deref;
|
||||
use crate::prelude::*;
|
||||
use crate::process::{ProcessRef, TermStatus, ThreadRef};
|
||||
use crate::syscall::{CpuContext, ExtraContext, FpRegs, XsaveArea};
|
||||
use crate::syscall::{BoxXsaveArea, CpuContext, ExtraContext, FpRegs};
|
||||
use aligned::{Aligned, A16};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::{ptr, slice};
|
||||
|
||||
pub fn do_rt_sigreturn(curr_user_ctxt: &mut CpuContext) -> Result<()> {
|
||||
debug!("do_rt_sigreturn");
|
||||
let last_ucontext = {
|
||||
let last_ucontext = PRE_UCONTEXTS.with(|ref_cell| {
|
||||
let (last_ucontext_ptr, last_extra_context) = {
|
||||
let last_context = PRE_UCONTEXTS.with(|ref_cell| {
|
||||
let mut stack = ref_cell.borrow_mut();
|
||||
stack.pop()
|
||||
});
|
||||
if last_ucontext.is_none() {
|
||||
if last_context.is_none() {
|
||||
let term_status = TermStatus::Killed(SIGKILL);
|
||||
current!().process().force_exit(term_status);
|
||||
return_errno!(
|
||||
@ -24,34 +25,54 @@ pub fn do_rt_sigreturn(curr_user_ctxt: &mut CpuContext) -> Result<()> {
|
||||
"sigreturn should not have been called; kill this process"
|
||||
);
|
||||
}
|
||||
unsafe { &*last_ucontext.unwrap() }
|
||||
last_context.unwrap().consume()
|
||||
};
|
||||
|
||||
let last_ucontext = unsafe { &*last_ucontext_ptr };
|
||||
let (extra_context, extra_context_ptr, extra_context_size) = last_extra_context.consume();
|
||||
|
||||
// Restore sigmask
|
||||
*current!().sig_mask().write().unwrap() = SigSet::from_c(last_ucontext.uc_sigmask);
|
||||
// Restore user context
|
||||
*curr_user_ctxt = last_ucontext.uc_mcontext.inner;
|
||||
// Restore extra context
|
||||
curr_user_ctxt.extra_context = extra_context;
|
||||
curr_user_ctxt.extra_context_ptr = extra_context_ptr;
|
||||
curr_user_ctxt.extra_context_size = extra_context_size;
|
||||
debug_assert!(
|
||||
curr_user_ctxt.extra_context_ptr != ptr::null_mut()
|
||||
&& curr_user_ctxt.extra_context_size != 0
|
||||
);
|
||||
|
||||
// Restore the floating point registers to a temp area
|
||||
// The floating point registers would be recoved just before return to user's code
|
||||
// Udpate the fpregs field based on the current's user context
|
||||
match curr_user_ctxt.extra_context {
|
||||
ExtraContext::Fpregs => {
|
||||
// Signal raised by direct syscall
|
||||
// fpregs should be stored on the heap. Because the ucontext_t will be freed when this function returns. And curr_user_ctxt only stores the pointer
|
||||
let mut fpregs = Box::new(unsafe { FpRegs::from_slice(&last_ucontext.fpregs) });
|
||||
curr_user_ctxt.extra_context_ptr = Box::into_raw(fpregs) as *mut u8;
|
||||
let mut old_fpregs_slice = unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
curr_user_ctxt.extra_context_ptr,
|
||||
curr_user_ctxt.extra_context_size as usize,
|
||||
)
|
||||
};
|
||||
old_fpregs_slice.copy_from_slice(&last_ucontext.fpregs);
|
||||
}
|
||||
ExtraContext::Xsave => {
|
||||
ExtraContext::XsaveOnHeap => {
|
||||
// Signal raised by exception
|
||||
// The xsave_area is stored at a special area reserved on kernel's stack. We can just overwrite this area with the latest user context
|
||||
// Note: Currently, we only restore the fpregs instead of restoring the whole xsave area for sigreturn. Because during the
|
||||
// handle path, we don't touch other advanced registers. However, in the future, if we have to touch those registers,
|
||||
// we should restore the whole xsave area when sigreturn.
|
||||
// The xsave_area is stored on kernel heap
|
||||
let latest_fpregs = unsafe { FpRegs::from_slice(&last_ucontext.fpregs) };
|
||||
let xsave_area =
|
||||
unsafe { (&mut *(curr_user_ctxt.extra_context_ptr as *mut XsaveArea)) };
|
||||
let mut xsave_area = ManuallyDrop::new(unsafe {
|
||||
BoxXsaveArea::from_raw(
|
||||
curr_user_ctxt.extra_context_ptr,
|
||||
curr_user_ctxt.extra_context_size as usize,
|
||||
)
|
||||
});
|
||||
xsave_area.set_fpregs_area(latest_fpregs);
|
||||
}
|
||||
ExtraContext::XsaveOnStack => {
|
||||
// As long as we switch the control to the userspace, we must save the context on heap
|
||||
// to prevent overwritten when recursive exception/interrupt happens
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -263,7 +284,8 @@ fn handle_signals_by_user(
|
||||
*info = signal.to_info();
|
||||
info as *mut siginfo_t
|
||||
};
|
||||
// 2. Allocate and init ucontext_t on the user stack.
|
||||
// 2. Allocate and init ucontext_t on the user stack. And save the context's address for restoring after the handler
|
||||
let mut restore_context = RestoreContext::new_uninit();
|
||||
let ucontext = {
|
||||
// The x86 calling convention requires rsp to be 16-byte aligned.
|
||||
// The following allocation on stack is right before we "call" the
|
||||
@ -277,32 +299,109 @@ fn handle_signals_by_user(
|
||||
ucontext.uc_sigmask = old_sigmask.to_c();
|
||||
// Save the user context
|
||||
ucontext.uc_mcontext.inner = curr_user_ctxt.clone();
|
||||
// Reset the extra context to prevent leaking the info to userspace
|
||||
ucontext.uc_mcontext.inner.extra_context_ptr = ptr::null_mut();
|
||||
ucontext.uc_mcontext.inner.extra_context_size = 0;
|
||||
|
||||
// Save the floating point registers
|
||||
if curr_user_ctxt.extra_context_ptr != ptr::null_mut() {
|
||||
// Signal from exception handling
|
||||
debug_assert!(matches!(curr_user_ctxt.extra_context, ExtraContext::Xsave));
|
||||
let fpregs_area =
|
||||
unsafe { (&*(curr_user_ctxt.extra_context_ptr as *mut XsaveArea)) }.get_fpregs();
|
||||
ucontext.fpregs.copy_from_slice(fpregs_area.as_slice());
|
||||
// Clear the floating point registers, since we do not need to recover this when this syscall return
|
||||
curr_user_ctxt.extra_context_ptr = ptr::null_mut();
|
||||
} else {
|
||||
// Raise the signal with direct syscall
|
||||
debug_assert!(
|
||||
matches!(curr_user_ctxt.extra_context, ExtraContext::Fpregs)
|
||||
&& curr_user_ctxt.extra_context_ptr == ptr::null_mut()
|
||||
);
|
||||
|
||||
// We need a correct fxsave structure in the buffer,
|
||||
// because the app may modify part of it to update the
|
||||
// floating point after the signal handler finished.
|
||||
let fpregs = FpRegs::save();
|
||||
if curr_user_ctxt.extra_context_ptr == ptr::null_mut() {
|
||||
match curr_user_ctxt.extra_context {
|
||||
ExtraContext::Fpregs => {
|
||||
// If curr_user_ctxt.extra_context_ptr is null, signal is handled in a normal syscall
|
||||
// We need a correct fxsave structure in the buffer, because the app may modify part of
|
||||
// it to update the floating point after the signal handler finished.
|
||||
// fpregs context is saved at two places:
|
||||
// (1) Copy to ucontext which can be used by user's handler
|
||||
// (2) Save at the heap and store the pointer to Occlum's defined RestoreContext
|
||||
let fpregs = Box::new(FpRegs::save());
|
||||
ucontext.fpregs.copy_from_slice(fpregs.as_slice());
|
||||
let fpregs_raw_ptr = Box::into_raw(fpregs) as *mut u8;
|
||||
let extra_context = ExtraCtx::new(
|
||||
ExtraContext::Fpregs,
|
||||
fpregs_raw_ptr,
|
||||
std::mem::size_of::<FpRegs>() as u64,
|
||||
);
|
||||
restore_context.set_extra_context(extra_context);
|
||||
}
|
||||
ExtraContext::XsaveOnStack | ExtraContext::XsaveOnHeap => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
// curr_user_ctxt.extra_context_ptr != ptr::null_mut()
|
||||
match curr_user_ctxt.extra_context {
|
||||
ExtraContext::Fpregs => {
|
||||
// Handle another signal found after previous sigreturn and before sysret, reuse the fpregs context
|
||||
let fpregs =
|
||||
unsafe { Box::from_raw(curr_user_ctxt.extra_context_ptr as *mut FpRegs) };
|
||||
ucontext.fpregs.copy_from_slice(fpregs.as_slice());
|
||||
let fpregs_raw_ptr = Box::into_raw(fpregs) as *mut u8;
|
||||
|
||||
// ucontext.uc_mcontext.inner.extra_context = ExtraContext::Fpregs;
|
||||
// ucontext.uc_mcontext.inner.extra_context_size =
|
||||
// std::mem::size_of::<FpRegs>() as u64;
|
||||
let extra_context = ExtraCtx::new(
|
||||
ExtraContext::Fpregs,
|
||||
fpregs_raw_ptr,
|
||||
std::mem::size_of::<FpRegs>() as u64,
|
||||
);
|
||||
restore_context.set_extra_context(extra_context);
|
||||
}
|
||||
// force_signal from exception or deliver_signal when interrupt returns
|
||||
ExtraContext::XsaveOnStack => {
|
||||
// Save Xsave area at the heap and store the pointer to Occlum's defined CpuContext.
|
||||
// So that recursive exception will not overwrite the xsave area.
|
||||
let xsave_size = curr_user_ctxt.extra_context_size;
|
||||
debug_assert!(
|
||||
xsave_size != 0 && curr_user_ctxt.extra_context_ptr != ptr::null_mut()
|
||||
);
|
||||
let xsave_area = ManuallyDrop::new(BoxXsaveArea::new_with_slice(unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
curr_user_ctxt.extra_context_ptr,
|
||||
xsave_size as usize,
|
||||
)
|
||||
}));
|
||||
|
||||
let extra_context =
|
||||
ExtraCtx::new(ExtraContext::XsaveOnHeap, xsave_area.raw_ptr(), xsave_size);
|
||||
restore_context.set_extra_context(extra_context);
|
||||
|
||||
// Copy fpregs to ucontext for user's handler
|
||||
let fpregs_area = xsave_area.get_fpregs();
|
||||
ucontext.fpregs.copy_from_slice(fpregs_area.as_slice());
|
||||
}
|
||||
// Handle another signal before sysret during exception/interrupt
|
||||
ExtraContext::XsaveOnHeap => {
|
||||
debug_assert!(
|
||||
curr_user_ctxt.extra_context_size != 0
|
||||
&& curr_user_ctxt.extra_context_ptr != ptr::null_mut()
|
||||
);
|
||||
// xsave area is already saved on the heap. Just reuse it.
|
||||
let xsave_area = ManuallyDrop::new(unsafe {
|
||||
BoxXsaveArea::from_raw(
|
||||
curr_user_ctxt.extra_context_ptr,
|
||||
curr_user_ctxt.extra_context_size as usize,
|
||||
)
|
||||
});
|
||||
let extra_context = ExtraCtx::new(
|
||||
ExtraContext::XsaveOnHeap,
|
||||
xsave_area.raw_ptr(),
|
||||
curr_user_ctxt.extra_context_size,
|
||||
);
|
||||
restore_context.set_extra_context(extra_context);
|
||||
|
||||
// Copy fpregs to ucontext for user's handler
|
||||
let fpregs_area = xsave_area.get_fpregs();
|
||||
ucontext.fpregs.copy_from_slice(fpregs_area.as_slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the context pointer, since we do not need to recover this when this syscall return
|
||||
curr_user_ctxt.extra_context_ptr = ptr::null_mut();
|
||||
curr_user_ctxt.extra_context_size = 0;
|
||||
|
||||
ucontext as *mut ucontext_t
|
||||
};
|
||||
restore_context.set_ucontext_ptr(ucontext);
|
||||
|
||||
// 3. Set up the call return address on the stack before we "call" the signal handler
|
||||
let handler_stack_top = {
|
||||
let handler_stack_top = user_stack.alloc::<usize>()?;
|
||||
@ -322,7 +421,7 @@ fn handle_signals_by_user(
|
||||
|
||||
PRE_UCONTEXTS.with(|ref_cell| {
|
||||
let mut stack = ref_cell.borrow_mut();
|
||||
stack.push(ucontext).unwrap();
|
||||
stack.push(restore_context).unwrap();
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@ -402,10 +501,73 @@ thread_local! {
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct CpuContextStack {
|
||||
stack: [Option<*mut ucontext_t>; 32],
|
||||
stack: [Option<RestoreContext>; 32],
|
||||
count: usize,
|
||||
}
|
||||
|
||||
// Save the context ptr to restore after the signal handler returns
|
||||
#[derive(Debug)]
|
||||
struct RestoreContext {
|
||||
ucontext: *mut ucontext_t,
|
||||
extra_context: ExtraCtx,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ExtraCtx {
|
||||
type_: ExtraContext,
|
||||
ptr: *mut u8,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
impl Default for ExtraCtx {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
type_: ExtraContext::Fpregs,
|
||||
ptr: ptr::null_mut(),
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtraCtx {
|
||||
fn new(type_: ExtraContext, ptr: *mut u8, size: u64) -> Self {
|
||||
Self { type_, ptr, size }
|
||||
}
|
||||
|
||||
pub fn consume(self) -> (ExtraContext, *mut u8, u64) {
|
||||
(self.type_, self.ptr, self.size)
|
||||
}
|
||||
}
|
||||
|
||||
impl RestoreContext {
|
||||
pub fn new_uninit() -> Self {
|
||||
Self {
|
||||
ucontext: ptr::null_mut() as *mut ucontext_t,
|
||||
extra_context: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_ucontext_ptr(&mut self, ucontext: *mut ucontext_t) {
|
||||
self.ucontext = ucontext;
|
||||
}
|
||||
|
||||
pub fn set_extra_context(&mut self, extra_context: ExtraCtx) {
|
||||
self.extra_context = extra_context;
|
||||
}
|
||||
|
||||
pub fn ucontext(&self) -> *mut ucontext_t {
|
||||
self.ucontext
|
||||
}
|
||||
|
||||
pub fn extra_context(&self) -> &ExtraCtx {
|
||||
&self.extra_context
|
||||
}
|
||||
|
||||
pub fn consume(self) -> (*mut ucontext_t, ExtraCtx) {
|
||||
(self.ucontext, self.extra_context)
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuContextStack {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
@ -419,7 +581,7 @@ impl CpuContextStack {
|
||||
self.count == 0
|
||||
}
|
||||
|
||||
pub fn push(&mut self, cpu_context: *mut ucontext_t) -> Result<()> {
|
||||
pub fn push(&mut self, cpu_context: RestoreContext) -> Result<()> {
|
||||
if self.full() {
|
||||
return_errno!(ENOMEM, "cpu context stack is full");
|
||||
}
|
||||
@ -428,7 +590,7 @@ impl CpuContextStack {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<*mut ucontext_t> {
|
||||
pub fn pop(&mut self) -> Option<RestoreContext> {
|
||||
if self.empty() {
|
||||
return None;
|
||||
}
|
||||
|
@ -9,13 +9,15 @@
|
||||
|
||||
use aligned::{Aligned, A16, A64};
|
||||
use core::arch::x86_64::{_fxrstor, _fxsave};
|
||||
use std::alloc::Layout;
|
||||
use std::any::Any;
|
||||
use std::convert::TryFrom;
|
||||
use std::default::Default;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::mem::{ManuallyDrop, MaybeUninit};
|
||||
use std::ptr;
|
||||
use std::ptr::NonNull;
|
||||
use time::{clockid_t, itimerspec_t, timespec_t, timeval_t};
|
||||
use util::log::{self, LevelFilter};
|
||||
use util::mem_util::from_user::*;
|
||||
@ -752,24 +754,55 @@ fn do_sysret(user_context: &mut CpuContext) -> ! {
|
||||
if user_context.extra_context_ptr != ptr::null_mut() {
|
||||
match user_context.extra_context {
|
||||
ExtraContext::Fpregs => {
|
||||
let fpregs = user_context.extra_context_ptr as *mut FpRegs;
|
||||
unsafe { fpregs.as_ref().unwrap().restore() };
|
||||
// The fpregs must be allocated on heap
|
||||
drop(unsafe { Box::from_raw(user_context.extra_context_ptr as *mut FpRegs) });
|
||||
let fpregs =
|
||||
unsafe { Box::from_raw(user_context.extra_context_ptr as *mut FpRegs) };
|
||||
unsafe { fpregs.restore() };
|
||||
// Drop automatically
|
||||
// Note: Manually-drop could modify some of the context registers.
|
||||
// For example, we observe using $XMM0 register when drop manually with debug build.
|
||||
// Same for the xsave area
|
||||
}
|
||||
ExtraContext::Xsave => {
|
||||
ExtraContext::XsaveOnStack => {
|
||||
let xsave_area = user_context.extra_context_ptr;
|
||||
unsafe { (&*(xsave_area as *mut XsaveArea)).restore() };
|
||||
unsafe {
|
||||
restore_xregs(xsave_area as *const u8);
|
||||
}
|
||||
}
|
||||
// return from rt_sigreturn
|
||||
ExtraContext::XsaveOnHeap => {
|
||||
let xsave_area = unsafe {
|
||||
BoxXsaveArea::from_raw(
|
||||
user_context.extra_context_ptr,
|
||||
user_context.extra_context_size as usize,
|
||||
)
|
||||
};
|
||||
xsave_area.restore();
|
||||
// This heap memory will finally be dropped and freed.
|
||||
}
|
||||
}
|
||||
user_context.extra_context_ptr = ptr::null_mut();
|
||||
}
|
||||
unsafe { __occlum_sysret(user_context) } // jump to user space
|
||||
} else {
|
||||
if user_context.extra_context_ptr != ptr::null_mut()
|
||||
&& matches!(user_context.extra_context, ExtraContext::Fpregs)
|
||||
{
|
||||
drop(unsafe { Box::from_raw(user_context.extra_context_ptr as *mut FpRegs) });
|
||||
if user_context.extra_context_ptr != ptr::null_mut() {
|
||||
match user_context.extra_context {
|
||||
ExtraContext::Fpregs => {
|
||||
let fpregs =
|
||||
unsafe { Box::from_raw(user_context.extra_context_ptr as *mut FpRegs) };
|
||||
// Drop automatically
|
||||
}
|
||||
ExtraContext::XsaveOnStack => {}
|
||||
ExtraContext::XsaveOnHeap => {
|
||||
let xsave_area = unsafe {
|
||||
BoxXsaveArea::from_raw(
|
||||
user_context.extra_context_ptr,
|
||||
user_context.extra_context_size as usize,
|
||||
)
|
||||
};
|
||||
// This heap memory will finally be dropped and freed.
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe { do_exit_task() } // exit enclave
|
||||
}
|
||||
@ -1026,38 +1059,75 @@ impl FpRegs {
|
||||
}
|
||||
}
|
||||
|
||||
// This is used to represent the xsave area on heap.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct XsaveArea {
|
||||
inner: Aligned<A64, [u8; 4096]>,
|
||||
pub struct BoxXsaveArea {
|
||||
ptr: NonNull<u8>,
|
||||
layout: Layout,
|
||||
}
|
||||
|
||||
impl XsaveArea {
|
||||
impl BoxXsaveArea {
|
||||
// The first 512 bytes of xsave area is used for FP registers
|
||||
const FXSAVE_AREA_LEN: usize = 512;
|
||||
const ALIGNMENT: usize = 64;
|
||||
|
||||
/// Save the current CPU floating pointer states to an instance of FpRegs
|
||||
pub fn save() -> Self {
|
||||
let mut xsave_area = MaybeUninit::<Self>::uninit();
|
||||
unsafe {
|
||||
save_xregs(xsave_area.as_mut_ptr() as *mut u8);
|
||||
xsave_area.assume_init()
|
||||
pub fn new_with_slice(slice: &[u8]) -> Self {
|
||||
let size = slice.len();
|
||||
let layout = Layout::from_size_align(size, Self::ALIGNMENT).expect("Invalid layout");
|
||||
let ptr = unsafe {
|
||||
let raw_ptr = std::alloc::alloc(layout);
|
||||
if raw_ptr.is_null() {
|
||||
panic!("Heap memory allocation failure");
|
||||
}
|
||||
std::slice::from_raw_parts_mut(raw_ptr, size).copy_from_slice(slice);
|
||||
NonNull::<u8>::new(raw_ptr).unwrap()
|
||||
};
|
||||
|
||||
Self { ptr, layout }
|
||||
}
|
||||
|
||||
// Reconstruct from raw pointer
|
||||
// Safety: Users must ensure the heap memory area is valid.
|
||||
pub unsafe fn from_raw(raw_ptr: *mut u8, size: usize) -> Self {
|
||||
let ptr = NonNull::<u8>::new(raw_ptr).expect("raw ptr shouldn't be null");
|
||||
let layout = Layout::from_size_align(size, Self::ALIGNMENT).unwrap();
|
||||
Self { ptr, layout }
|
||||
}
|
||||
|
||||
pub fn raw_ptr(&self) -> *mut u8 {
|
||||
self.ptr.as_ptr()
|
||||
}
|
||||
|
||||
/// Restore the current CPU floating pointer states from this FpRegs instance
|
||||
pub fn restore(&self) {
|
||||
unsafe {
|
||||
restore_xregs(self.inner.as_ptr());
|
||||
restore_xregs(self.raw_ptr() as *const u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { std::slice::from_raw_parts(self.raw_ptr() as *const u8, self.layout.size()) }
|
||||
}
|
||||
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
unsafe { std::slice::from_raw_parts_mut(self.raw_ptr(), self.layout.size()) }
|
||||
}
|
||||
|
||||
pub fn get_fpregs(&self) -> FpRegs {
|
||||
unsafe { FpRegs::from_slice(&self.inner[..Self::FXSAVE_AREA_LEN]) }
|
||||
let xsave_slice = self.as_slice();
|
||||
unsafe { FpRegs::from_slice(&xsave_slice[..Self::FXSAVE_AREA_LEN]) }
|
||||
}
|
||||
|
||||
pub fn set_fpregs_area(&mut self, fpregs: FpRegs) {
|
||||
self.inner[..Self::FXSAVE_AREA_LEN].copy_from_slice(fpregs.as_slice())
|
||||
self.as_mut_slice()[..Self::FXSAVE_AREA_LEN].copy_from_slice(fpregs.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BoxXsaveArea {
|
||||
fn drop(&mut self) {
|
||||
// Modify this function with cautions because this is usally called after restore the xsave area.
|
||||
// Extra operations could mess up the context.
|
||||
unsafe { std::alloc::dealloc(self.raw_ptr(), self.layout) };
|
||||
}
|
||||
}
|
||||
|
||||
@ -1088,13 +1158,34 @@ pub struct CpuContext {
|
||||
pub rflags: u64,
|
||||
pub extra_context: ExtraContext,
|
||||
pub extra_context_ptr: *mut u8,
|
||||
pub extra_context_size: u64,
|
||||
}
|
||||
|
||||
// Define 3 kinds of extra context types:
|
||||
//
|
||||
// 1. Fpregs: store fpregs on kernel heap -> restore fpregs when sysret
|
||||
// example: syscall + find signal when sysret + user signal handler + sigreturn + sysret
|
||||
//
|
||||
// 2. XsaveOnStack: xsave on kernel stack -> restore xsave when sysret
|
||||
// example: exception + kernel can handle + sysret
|
||||
//
|
||||
// 3. XsaveOnHeap: xsave initiailly on kernel stack -> copy xsave area to kernel heap -> restore xsave area (on kernel heap) when sysret
|
||||
// example:
|
||||
// a. exception + kernel can handle + find signal when sysret + user signal handler + sigreturn + sysret
|
||||
// b. exception + kernel can't handle + signal + user signal handler + sigreturn + sysret
|
||||
// c. Interrupt + signal + user signal handler + sigreturn + sysret
|
||||
#[repr(u64)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ExtraContext {
|
||||
// To handle signals before syscall returns
|
||||
Fpregs = 0,
|
||||
Xsave = 1,
|
||||
// To handle SGX SDK defined "non-standard exception", some kinds of exceptions can solely be handled by the kernel,
|
||||
// e.g. #PF, and the xsave area will not be overwritten, thus, save on the stack is enough.
|
||||
XsaveOnStack = 1,
|
||||
// Some kinds of exceptions can't be handled by the kernel, e.g. divided-by-zero exception, and will switch to user's
|
||||
// signal handler and could trigger recursive exception and the xsave area will be overwritten, thus, saving on the
|
||||
// heap is required.
|
||||
XsaveOnHeap = 2,
|
||||
}
|
||||
|
||||
impl Default for ExtraContext {
|
||||
@ -1126,6 +1217,7 @@ impl CpuContext {
|
||||
rflags: src.rflags,
|
||||
extra_context: Default::default(),
|
||||
extra_context_ptr: ptr::null_mut(),
|
||||
extra_context_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ __occlum_syscall_linux_abi:
|
||||
// Save the target CPU state when `call __occlum_syscall` is returned in
|
||||
// a CpuContext struct. The registers are saved in the reverse order of
|
||||
// the fields in CpuContext.
|
||||
pushq $0 // default extra_context_size is 0
|
||||
pushq $0 // default extra_context_ptr is NULL
|
||||
pushq $0 // default extra_context is floating point registers
|
||||
pushfq
|
||||
|
Loading…
Reference in New Issue
Block a user