diff --git a/kernel/src/arch/riscv/cpu.rs b/kernel/src/arch/riscv/cpu.rs index aca3096b5..214e71f7f 100644 --- a/kernel/src/arch/riscv/cpu.rs +++ b/kernel/src/arch/riscv/cpu.rs @@ -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(); } } diff --git a/kernel/src/arch/x86/cpu.rs b/kernel/src/arch/x86/cpu.rs index 4f657c9e1..a684efd36 100644 --- a/kernel/src/arch/x86/cpu.rs +++ b/kernel/src/arch/x86/cpu.rs @@ -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; } } diff --git a/kernel/src/process/signal/c_types.rs b/kernel/src/process/signal/c_types.rs index d7b94a525..e94586a4e 100644 --- a/kernel/src/process/signal/c_types.rs +++ b/kernel/src/process/signal/c_types.rs @@ -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)] diff --git a/kernel/src/process/signal/mod.rs b/kernel/src/process/signal/mod.rs index 9ec4b5d93..e110dc9ab 100644 --- a/kernel/src/process/signal/mod.rs +++ b/kernel/src/process/signal/mod.rs @@ -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::(), 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::(), + align_of::(), + )?; + 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::() + fpu_context_bytes.len(), + align_of::(), + )?; + let fpu_context_addr = (ucontext_addr as usize) + size_of::(); + } 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)?; diff --git a/kernel/src/syscall/rt_sigreturn.rs b/kernel/src/syscall/rt_sigreturn.rs index f9c2ec75c..1b85f78de 100644 --- a/kernel/src/syscall/rt_sigreturn.rs +++ b/kernel/src/syscall/rt_sigreturn.rs @@ -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(); + } 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; diff --git a/test/src/apps/signal_c/signal_test.c b/test/src/apps/signal_c/signal_test.c index a5fe491cd..4cd6f1755 100644 --- a/test/src/apps/signal_c/signal_test.c +++ b/test/src/apps/signal_c/signal_test.c @@ -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() {