Save FPU context on signal stack
This commit is contained in:
parent
6cd53fbb8a
commit
f1299d4b8d
|
|
@ -4,6 +4,8 @@ use alloc::{format, string::String};
|
|||
|
||||
use ostd::{
|
||||
cpu::context::{CpuExceptionInfo, GeneralRegs, UserContext},
|
||||
mm::Vaddr,
|
||||
user::UserContextApi,
|
||||
Pod,
|
||||
};
|
||||
|
||||
|
|
@ -46,47 +48,8 @@ impl LinuxAbi for UserContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// General-purpose registers.
|
||||
#[derive(Debug, Clone, Copy, Pod, Default)]
|
||||
#[repr(C)]
|
||||
pub struct GpRegs {
|
||||
pub zero: usize,
|
||||
pub ra: usize,
|
||||
pub sp: usize,
|
||||
pub gp: usize,
|
||||
pub tp: usize,
|
||||
pub t0: usize,
|
||||
pub t1: usize,
|
||||
pub t2: usize,
|
||||
pub s0: usize,
|
||||
pub s1: usize,
|
||||
pub a0: usize,
|
||||
pub a1: usize,
|
||||
pub a2: usize,
|
||||
pub a3: usize,
|
||||
pub a4: usize,
|
||||
pub a5: usize,
|
||||
pub a6: usize,
|
||||
pub a7: usize,
|
||||
pub s2: usize,
|
||||
pub s3: usize,
|
||||
pub s4: usize,
|
||||
pub s5: usize,
|
||||
pub s6: usize,
|
||||
pub s7: usize,
|
||||
pub s8: usize,
|
||||
pub s9: usize,
|
||||
pub s10: usize,
|
||||
pub s11: usize,
|
||||
pub t3: usize,
|
||||
pub t4: usize,
|
||||
pub t5: usize,
|
||||
pub t6: usize,
|
||||
}
|
||||
|
||||
macro_rules! copy_gp_regs {
|
||||
($src: ident, $dst: ident) => {
|
||||
$dst.zero = $src.zero;
|
||||
$dst.ra = $src.ra;
|
||||
$dst.sp = $src.sp;
|
||||
$dst.gp = $src.gp;
|
||||
|
|
@ -121,13 +84,60 @@ macro_rules! copy_gp_regs {
|
|||
};
|
||||
}
|
||||
|
||||
impl GpRegs {
|
||||
pub fn copy_to_raw(&self, dst: &mut GeneralRegs) {
|
||||
copy_gp_regs!(self, dst);
|
||||
/// Represents the context of a signal handler.
|
||||
///
|
||||
/// This contains the context saved before a signal handler is invoked and restored by `sys_rt_sigreturn`.
|
||||
#[repr(C)]
|
||||
#[repr(align(16))]
|
||||
#[derive(Clone, Copy, Debug, Default, Pod)]
|
||||
pub struct SigContext {
|
||||
pc: usize,
|
||||
ra: usize,
|
||||
sp: usize,
|
||||
gp: usize,
|
||||
tp: usize,
|
||||
t0: usize,
|
||||
t1: usize,
|
||||
t2: usize,
|
||||
s0: usize,
|
||||
s1: usize,
|
||||
a0: usize,
|
||||
a1: usize,
|
||||
a2: usize,
|
||||
a3: usize,
|
||||
a4: usize,
|
||||
a5: usize,
|
||||
a6: usize,
|
||||
a7: usize,
|
||||
s2: usize,
|
||||
s3: usize,
|
||||
s4: usize,
|
||||
s5: usize,
|
||||
s6: usize,
|
||||
s7: usize,
|
||||
s8: usize,
|
||||
s9: usize,
|
||||
s10: usize,
|
||||
s11: usize,
|
||||
t3: usize,
|
||||
t4: usize,
|
||||
t5: usize,
|
||||
t6: usize,
|
||||
// In RISC-V, the signal stack layout places the FPU context directly
|
||||
// after the general-purpose registers.
|
||||
}
|
||||
|
||||
impl SigContext {
|
||||
pub fn copy_user_regs_to(&self, dst: &mut UserContext) {
|
||||
let gp_regs = dst.general_regs_mut();
|
||||
copy_gp_regs!(self, gp_regs);
|
||||
dst.set_instruction_pointer(self.pc);
|
||||
}
|
||||
|
||||
pub fn copy_from_raw(&mut self, src: &GeneralRegs) {
|
||||
copy_gp_regs!(src, self);
|
||||
pub fn copy_user_regs_from(&mut self, src: &UserContext) {
|
||||
let gp_regs = src.general_regs();
|
||||
copy_gp_regs!(gp_regs, self);
|
||||
self.pc = src.instruction_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ use alloc::{
|
|||
};
|
||||
|
||||
use ostd::{
|
||||
cpu::context::{
|
||||
cpuid, CpuException, GeneralRegs, PageFaultErrorCode, RawPageFaultInfo, UserContext,
|
||||
},
|
||||
cpu::context::{cpuid, CpuException, PageFaultErrorCode, RawPageFaultInfo, UserContext},
|
||||
mm::Vaddr,
|
||||
Pod,
|
||||
};
|
||||
|
||||
|
|
@ -52,30 +51,41 @@ impl LinuxAbi for UserContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// General-purpose registers.
|
||||
#[derive(Debug, Clone, Copy, Pod, Default)]
|
||||
/// Represents the context of a signal handler.
|
||||
///
|
||||
/// This contains the context saved before a signal handler is invoked and restored by `sys_rt_sigreturn`.
|
||||
#[derive(Clone, Copy, Debug, Default, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct GpRegs {
|
||||
pub rax: usize,
|
||||
pub rbx: usize,
|
||||
pub rcx: usize,
|
||||
pub rdx: usize,
|
||||
pub rsi: usize,
|
||||
pub rdi: usize,
|
||||
pub rbp: usize,
|
||||
pub rsp: usize,
|
||||
pub r8: usize,
|
||||
pub r9: usize,
|
||||
pub r10: usize,
|
||||
pub r11: usize,
|
||||
pub r12: usize,
|
||||
pub r13: usize,
|
||||
pub r14: usize,
|
||||
pub r15: usize,
|
||||
pub rip: usize,
|
||||
pub rflags: usize,
|
||||
pub fsbase: usize,
|
||||
pub gsbase: usize,
|
||||
pub struct SigContext {
|
||||
r8: usize,
|
||||
r9: usize,
|
||||
r10: usize,
|
||||
r11: usize,
|
||||
r12: usize,
|
||||
r13: usize,
|
||||
r14: usize,
|
||||
r15: usize,
|
||||
rdi: usize,
|
||||
rsi: usize,
|
||||
rbp: usize,
|
||||
rbx: usize,
|
||||
rdx: usize,
|
||||
rax: usize,
|
||||
rcx: usize,
|
||||
rsp: usize,
|
||||
rip: usize,
|
||||
rflags: usize,
|
||||
cs: u16,
|
||||
gs: u16,
|
||||
fs: u16,
|
||||
ss: u16,
|
||||
error_code: usize,
|
||||
trap_num: usize,
|
||||
old_mask: u64,
|
||||
page_fault_addr: usize,
|
||||
// A stack pointer to FPU context.
|
||||
fpu_context_addr: Vaddr,
|
||||
reserved: [u64; 8],
|
||||
}
|
||||
|
||||
macro_rules! copy_gp_regs {
|
||||
|
|
@ -98,18 +108,28 @@ macro_rules! copy_gp_regs {
|
|||
$dst.r15 = $src.r15;
|
||||
$dst.rip = $src.rip;
|
||||
$dst.rflags = $src.rflags;
|
||||
$dst.fsbase = $src.fsbase;
|
||||
$dst.gsbase = $src.gsbase;
|
||||
};
|
||||
}
|
||||
|
||||
impl GpRegs {
|
||||
pub fn copy_to_raw(&self, dst: &mut GeneralRegs) {
|
||||
copy_gp_regs!(self, dst);
|
||||
impl SigContext {
|
||||
pub fn copy_user_regs_to(&self, dst: &mut UserContext) {
|
||||
let gp_regs = dst.general_regs_mut();
|
||||
copy_gp_regs!(self, gp_regs);
|
||||
}
|
||||
|
||||
pub fn copy_from_raw(&mut self, src: &GeneralRegs) {
|
||||
copy_gp_regs!(src, self);
|
||||
pub fn copy_user_regs_from(&mut self, src: &UserContext) {
|
||||
let gp_regs = src.general_regs();
|
||||
copy_gp_regs!(gp_regs, self);
|
||||
|
||||
// TODO: Fill exception information in `SigContext`.
|
||||
}
|
||||
|
||||
pub fn fpu_context_addr(&self) -> Vaddr {
|
||||
self.fpu_context_addr
|
||||
}
|
||||
|
||||
pub fn set_fpu_context_addr(&mut self, addr: Vaddr) {
|
||||
self.fpu_context_addr = addr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@
|
|||
use core::mem::{self, size_of};
|
||||
|
||||
use aster_util::read_union_field;
|
||||
use inherit_methods_macro::inherit_methods;
|
||||
use ostd::cpu::context::UserContext;
|
||||
|
||||
use super::sig_num::SigNum;
|
||||
use crate::{
|
||||
arch::cpu::GpRegs,
|
||||
arch::cpu::SigContext,
|
||||
prelude::*,
|
||||
process::{Pid, Uid},
|
||||
};
|
||||
|
|
@ -157,7 +159,8 @@ union siginfo_addr_bnd_t {
|
|||
upper: Vaddr, // *const c_void,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Pod)]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[derive(Clone, Copy, Debug, Default, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct ucontext_t {
|
||||
pub uc_flags: u64,
|
||||
|
|
@ -165,18 +168,26 @@ pub struct ucontext_t {
|
|||
pub uc_stack: stack_t,
|
||||
pub uc_mcontext: mcontext_t,
|
||||
pub uc_sigmask: sigset_t,
|
||||
pub fpregs: [u8; 64 * 8], //fxsave structure
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
#[derive(Clone, Copy, Debug, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct ucontext_t {
|
||||
pub uc_flags: u64,
|
||||
pub uc_link: Vaddr, // *mut ucontext_t
|
||||
pub uc_stack: stack_t,
|
||||
pub uc_sigmask: sigset_t,
|
||||
pub __unused: [u8; 120],
|
||||
pub uc_mcontext: mcontext_t,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
impl Default for ucontext_t {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
uc_flags: Default::default(),
|
||||
uc_link: Default::default(),
|
||||
uc_stack: Default::default(),
|
||||
uc_mcontext: Default::default(),
|
||||
uc_sigmask: Default::default(),
|
||||
fpregs: [0u8; 64 * 8],
|
||||
__unused: [0; 120],
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -194,20 +205,17 @@ pub struct sigaltstack_t {
|
|||
#[derive(Debug, Clone, Copy, Pod, Default)]
|
||||
#[repr(C)]
|
||||
pub struct mcontext_t {
|
||||
pub inner: SignalCpuContext,
|
||||
// TODO: the fields should be csgsfs, err, trapno, oldmask, and cr2
|
||||
_unused0: [u64; 5],
|
||||
// TODO: this field should be `fpregs: fpregset_t,`
|
||||
_unused1: usize,
|
||||
_reserved: [u64; 8],
|
||||
inner: SigContext,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod, Default)]
|
||||
#[repr(C)]
|
||||
pub struct SignalCpuContext {
|
||||
pub gp_regs: GpRegs,
|
||||
pub fpregs_on_heap: u64,
|
||||
pub fpregs: Vaddr, // *mut FpRegs,
|
||||
#[inherit_methods(from = "self.inner")]
|
||||
impl mcontext_t {
|
||||
pub fn copy_user_regs_to(&self, context: &mut UserContext);
|
||||
pub fn copy_user_regs_from(&mut self, context: &UserContext);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn fpu_context_addr(&self) -> Vaddr;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn set_fpu_context_addr(&mut self, addr: Vaddr);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Pod)]
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ use align_ext::AlignExt;
|
|||
use c_types::{siginfo_t, ucontext_t};
|
||||
use constants::SIGSEGV;
|
||||
pub use events::{SigEvents, SigEventsFilter};
|
||||
use ostd::{cpu::context::UserContext, user::UserContextApi};
|
||||
use ostd::{
|
||||
cpu::context::{FpuContext, UserContext},
|
||||
user::UserContextApi,
|
||||
};
|
||||
pub use pause::{with_sigmask_changed, Pause};
|
||||
pub use poll::{PollAdaptor, PollHandle, Pollable, Pollee, Poller};
|
||||
use sig_action::{SigAction, SigActionFlags, SigDefaultAction};
|
||||
|
|
@ -197,31 +200,65 @@ pub fn handle_user_signal(
|
|||
let siginfo_addr = stack_pointer;
|
||||
|
||||
// 2. Write ucontext_t.
|
||||
stack_pointer = alloc_aligned_in_user_stack(stack_pointer, mem::size_of::<ucontext_t>(), 16)?;
|
||||
|
||||
let mut ucontext = ucontext_t {
|
||||
uc_sigmask: mask.into(),
|
||||
..Default::default()
|
||||
};
|
||||
ucontext
|
||||
.uc_mcontext
|
||||
.inner
|
||||
.gp_regs
|
||||
.copy_from_raw(user_ctx.general_regs());
|
||||
ucontext.uc_mcontext.copy_user_regs_from(user_ctx);
|
||||
let sig_context = ctx.thread_local.sig_context().get();
|
||||
if let Some(sig_context_addr) = sig_context {
|
||||
ucontext.uc_link = sig_context_addr;
|
||||
} else {
|
||||
ucontext.uc_link = 0;
|
||||
}
|
||||
// TODO: store fp regs in ucontext
|
||||
user_space.write_val(stack_pointer as _, &ucontext)?;
|
||||
let ucontext_addr = stack_pointer;
|
||||
|
||||
// Clone and reset the FPU context.
|
||||
let fpu_context = ctx.thread_local.fpu().clone_context();
|
||||
let fpu_context_bytes = fpu_context.as_bytes();
|
||||
ctx.thread_local.fpu().set_context(FpuContext::new());
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "x86_64")] {
|
||||
// Align the FPU context address to the 64-byte boundary so that the
|
||||
// user program can use the XSAVE/XRSTOR instructions at that address,
|
||||
// if necessary.
|
||||
let fpu_context_addr =
|
||||
alloc_aligned_in_user_stack(stack_pointer, fpu_context_bytes.len(), 64)?;
|
||||
let ucontext_addr = alloc_aligned_in_user_stack(
|
||||
fpu_context_addr,
|
||||
size_of::<ucontext_t>(),
|
||||
align_of::<ucontext_t>(),
|
||||
)?;
|
||||
ucontext
|
||||
.uc_mcontext
|
||||
.set_fpu_context_addr(fpu_context_addr as _);
|
||||
|
||||
const UC_FP_XSTATE: u64 = 1 << 0;
|
||||
ucontext.uc_flags = UC_FP_XSTATE;
|
||||
} else if #[cfg(target_arch = "riscv64")] {
|
||||
let ucontext_addr = alloc_aligned_in_user_stack(
|
||||
stack_pointer,
|
||||
size_of::<ucontext_t>() + fpu_context_bytes.len(),
|
||||
align_of::<ucontext_t>(),
|
||||
)?;
|
||||
let fpu_context_addr = (ucontext_addr as usize) + size_of::<ucontext_t>();
|
||||
} else {
|
||||
compile_error!("unsupported target");
|
||||
}
|
||||
}
|
||||
|
||||
let mut fpu_context_reader = VmReader::from(fpu_context.as_bytes());
|
||||
user_space.write_bytes(fpu_context_addr as _, &mut fpu_context_reader)?;
|
||||
|
||||
user_space.write_val(ucontext_addr as _, &ucontext)?;
|
||||
// Store the ucontext addr in sig context of current thread.
|
||||
ctx.thread_local
|
||||
.sig_context()
|
||||
.set(Some(ucontext_addr as Vaddr));
|
||||
|
||||
// 3. Write the address of the restorer code.
|
||||
stack_pointer = ucontext_addr;
|
||||
if flags.contains(SigActionFlags::SA_RESTORER) {
|
||||
// If the SA_RESTORER flag is present, the restorer code address is provided by the user.
|
||||
stack_pointer = write_u64_to_user_stack(stack_pointer, restorer_addr as u64)?;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use ostd::{cpu::context::UserContext, user::UserContextApi};
|
||||
use ostd::{
|
||||
cpu::context::{FpuContext, UserContext},
|
||||
user::UserContextApi,
|
||||
};
|
||||
|
||||
use super::SyscallReturn;
|
||||
use crate::{prelude::*, process::signal::c_types::ucontext_t};
|
||||
|
|
@ -41,11 +44,24 @@ pub fn sys_rt_sigreturn(ctx: &Context, user_ctx: &mut UserContext) -> Result<Sys
|
|||
} else {
|
||||
thread_local.sig_context().set(Some(ucontext.uc_link));
|
||||
};
|
||||
ucontext
|
||||
.uc_mcontext
|
||||
.inner
|
||||
.gp_regs
|
||||
.copy_to_raw(user_ctx.general_regs_mut());
|
||||
ucontext.uc_mcontext.copy_user_regs_to(user_ctx);
|
||||
|
||||
// Restore FPU context on stack
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "x86_64")] {
|
||||
let fpu_context_addr = ucontext.uc_mcontext.fpu_context_addr();
|
||||
} else if #[cfg(target_arch = "riscv64")] {
|
||||
// In RISC-V, FPU context is placed directly after `ucontext_t` on signal stack.
|
||||
let fpu_context_addr = sig_context_addr + size_of::<ucontext_t>();
|
||||
} else {
|
||||
compile_error!("unsupported target");
|
||||
}
|
||||
}
|
||||
let mut fpu_context = FpuContext::new();
|
||||
let mut fpu_context_writer = VmWriter::from(fpu_context.as_bytes_mut());
|
||||
ctx.user_space()
|
||||
.read_bytes(fpu_context_addr, &mut fpu_context_writer)?;
|
||||
ctx.thread_local.fpu().set_context(fpu_context);
|
||||
|
||||
// unblock sig mask
|
||||
let sig_mask = ucontext.uc_sigmask;
|
||||
|
|
|
|||
|
|
@ -196,10 +196,9 @@ int test_handle_sigfpe()
|
|||
(void)c;
|
||||
fxsave(y);
|
||||
|
||||
// Asterinas does not save and restore fpregs now, so we emit this check.
|
||||
// if (memcmp(x, y, 512) != 0) {
|
||||
// THROW_ERROR("floating point registers are modified");
|
||||
// }
|
||||
if (memcmp(x, y, 512) != 0) {
|
||||
THROW_ERROR("floating point registers are modified");
|
||||
}
|
||||
|
||||
printf("Signal handler successfully jumped over the divide-by-zero instruction\n");
|
||||
fflush(stdout);
|
||||
|
|
@ -318,7 +317,7 @@ static void handle_sigpipe(int num, siginfo_t *info, void *context)
|
|||
recursion_level--;
|
||||
}
|
||||
|
||||
#define SIGSTACKSIZE (4 * 4096)
|
||||
#define SIGSTACKSIZE (8 * 4096)
|
||||
|
||||
int test_sigaltstack()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue