@@ -13,10 +13,11 @@ use crate::{
1313 posix:: errno:: EResult ,
1414 process:: {
1515 Process , State ,
16- signal:: { Signal , SignalSet } ,
16+ signal:: { Signal , SignalDelivery , SignalSet } ,
1717 task:: Task ,
1818 } ,
1919 sched:: Scheduler ,
20+ uapi:: signal:: { SA_ONSTACK , siginfo_t} ,
2021} ;
2122use alloc:: boxed:: Box ;
2223use core:: {
@@ -75,6 +76,10 @@ impl Context {
7576 self . rax as usize
7677 }
7778
79+ pub fn syscall_error ( & self ) -> usize {
80+ self . rdx as usize
81+ }
82+
7883 pub fn arg0 ( & self ) -> usize {
7984 self . rdi as usize
8085 }
@@ -103,6 +108,36 @@ impl Context {
103108 self . rax = val as _ ;
104109 self . rdx = err as _ ;
105110 }
111+
112+ pub fn sp ( & self ) -> usize {
113+ self . rsp as usize
114+ }
115+
116+ pub fn ip ( & self ) -> usize {
117+ self . rip as usize
118+ }
119+
120+ pub fn snapshot_syscall ( & self ) -> SyscallRestart {
121+ SyscallRestart {
122+ nr : self . rax as usize ,
123+ arg2 : self . rdx as usize ,
124+ }
125+ }
126+
127+ pub fn restart_syscall ( & mut self , restart : & SyscallRestart ) {
128+ self . rip -= 2 ; // Length of the `syscall` instruction.
129+ self . rax = restart. nr as u64 ;
130+ self . rdx = restart. arg2 as u64 ;
131+ }
132+ }
133+
134+ /// Registers captured before a syscall is dispatched so that an interrupted
135+ /// syscall can be transparently restarted afterwards.
136+ /// The fields are whichever registers [`Context::set_return`] would clobber.
137+ #[ derive( Clone , Copy , Debug ) ]
138+ pub struct SyscallRestart {
139+ nr : usize ,
140+ arg2 : usize ,
106141}
107142
108143impl core:: fmt:: Debug for Context {
@@ -379,57 +414,132 @@ pub(in crate::arch) unsafe fn jump_to_context(context: *mut Context) -> ! {
379414 }
380415}
381416
382- /// Signal frame placed on the user stack when delivering a signal.
383- /// The restorer function pops this frame via sigreturn.
417+ /// Saved state for `sigreturn`, written to the user stack below the rest of the signal frame.
384418#[ repr( C ) ]
385419#[ derive( Clone , Copy ) ]
386420struct SignalFrame {
387- /// The saved signal mask to restore after handler returns.
388421 saved_mask : u64 ,
389- /// The saved user context to restore after handler returns.
390422 saved_context : Context ,
391- /// The signal number.
392- signal_number : u32 ,
423+ }
424+
425+ #[ repr( C ) ]
426+ #[ derive( Clone , Copy , Default ) ]
427+ struct UserStack {
428+ ss_sp : u64 ,
429+ ss_size : u64 ,
430+ ss_flags : i32 ,
393431 _pad : u32 ,
394- /// Return address: points to the restorer trampoline.
395- restorer_ret : u64 ,
396432}
397433
398- pub ( in crate :: arch) fn setup_signal_frame (
399- context : & mut Context ,
400- handler : usize ,
401- signal : u32 ,
402- mask : SignalSet ,
403- restorer : usize ,
404- ) {
405- let frame_sp = ( context. rsp as usize - size_of :: < SignalFrame > ( ) ) & !0xF ;
434+ #[ repr( C ) ]
435+ #[ derive( Clone , Copy , Default ) ]
436+ struct Mcontext {
437+ oldmask : u64 ,
438+ gregs : [ u64 ; 16 ] ,
439+ pc : u64 ,
440+ pr : u64 ,
441+ sr : u64 ,
442+ gbr : u64 ,
443+ mach : u64 ,
444+ macl : u64 ,
445+ fpregs : [ u64 ; 16 ] ,
446+ xfpregs : [ u64 ; 16 ] ,
447+ fpscr : u32 ,
448+ fpul : u32 ,
449+ ownedfp : u32 ,
450+ }
451+
452+ #[ repr( C ) ]
453+ #[ derive( Clone , Copy ) ]
454+ struct Ucontext {
455+ uc_link : u64 ,
456+ uc_stack : UserStack ,
457+ uc_mcontext : Mcontext ,
458+ uc_sigmask : u64 ,
459+ }
460+
461+ pub ( in crate :: arch) fn setup_signal_frame ( context : & mut Context , delivery : & SignalDelivery ) {
462+ let altstack = delivery. altstack ;
463+ let on_altstack = altstack. contains ( context. rsp as usize ) ;
464+ let use_altstack = delivery. flags & SA_ONSTACK != 0 && altstack. is_enabled ( ) && !on_altstack;
465+
466+ // Top of the alternate stack if requested, otherwise below the 128-byte red zone
467+ // of the interrupted stack (System V AMD64 ABI).
468+ let base = if use_altstack {
469+ altstack. sp + altstack. size
470+ } else {
471+ context. rsp as usize - 128
472+ } ;
473+
474+ let align = |x : usize | x & !0xF ;
475+
476+ let info_addr = align ( base - size_of :: < siginfo_t > ( ) ) ;
477+ let uc_addr = align ( info_addr - size_of :: < Ucontext > ( ) ) ;
478+ let sf_addr = align ( uc_addr - size_of :: < SignalFrame > ( ) ) ;
479+ let ret_addr = sf_addr - 8 ;
480+
481+ let mut gregs = [ 0u64 ; 16 ] ;
482+ gregs[ 0 ] = context. rax ;
483+ gregs[ 1 ] = context. rbx ;
484+ gregs[ 2 ] = context. rcx ;
485+ gregs[ 3 ] = context. rdx ;
486+ gregs[ 4 ] = context. rsi ;
487+ gregs[ 5 ] = context. rdi ;
488+ gregs[ 6 ] = context. rbp ;
489+ gregs[ 7 ] = context. rsp ;
490+ gregs[ 8 ] = context. r8 ;
491+ gregs[ 9 ] = context. r9 ;
492+ gregs[ 10 ] = context. r10 ;
493+ gregs[ 11 ] = context. r11 ;
494+ gregs[ 12 ] = context. r12 ;
495+ gregs[ 13 ] = context. r13 ;
496+ gregs[ 14 ] = context. r14 ;
497+ gregs[ 15 ] = context. r15 ;
498+
499+ let ucontext = Ucontext {
500+ uc_link : 0 ,
501+ uc_stack : UserStack {
502+ ss_sp : altstack. sp as u64 ,
503+ ss_size : altstack. size as u64 ,
504+ ss_flags : altstack. flags ,
505+ _pad : 0 ,
506+ } ,
507+ uc_mcontext : Mcontext {
508+ oldmask : delivery. old_mask . as_raw ( ) ,
509+ gregs,
510+ pc : context. rip ,
511+ ..Default :: default ( )
512+ } ,
513+ uc_sigmask : delivery. old_mask . as_raw ( ) ,
514+ } ;
406515
407516 let frame = SignalFrame {
408- saved_mask : mask . as_raw ( ) ,
517+ saved_mask : delivery . old_mask . as_raw ( ) ,
409518 saved_context : * context,
410- signal_number : signal,
411- _pad : 0 ,
412- restorer_ret : restorer as u64 ,
413519 } ;
414520
415- let mut ptr = UserPtr :: < SignalFrame > :: new ( VirtAddr :: new ( frame_sp) ) ;
416- if ptr. write ( frame) . is_none ( ) {
417- Process :: exit ( State :: Signaled ( Signal :: SigSegv ) ) ;
418- }
419-
420- // Place the restorer return address BELOW the signal frame so the
421- // handler's stack grows downward away from the frame, not into it.
422- // Also ensures RSP % 16 == 8 at handler entry (x86_64 ABI).
423- let ret_sp = frame_sp - 8 ;
424- let mut ret_ptr = UserPtr :: < u64 > :: new ( VirtAddr :: new ( ret_sp) ) ;
425- if ret_ptr. write ( restorer as u64 ) . is_none ( ) {
521+ let ok = UserPtr :: < siginfo_t > :: new ( VirtAddr :: new ( info_addr) )
522+ . write ( delivery. info )
523+ . is_some ( )
524+ && UserPtr :: < Ucontext > :: new ( VirtAddr :: new ( uc_addr) )
525+ . write ( ucontext)
526+ . is_some ( )
527+ && UserPtr :: < SignalFrame > :: new ( VirtAddr :: new ( sf_addr) )
528+ . write ( frame)
529+ . is_some ( )
530+ && UserPtr :: < u64 > :: new ( VirtAddr :: new ( ret_addr) )
531+ . write ( delivery. restorer as u64 )
532+ . is_some ( ) ;
533+ if !ok {
426534 Process :: exit ( State :: Signaled ( Signal :: SigSegv ) ) ;
427535 }
428536
429- // Modify context to jump to the handler.
430- context. rip = handler as u64 ;
431- context. rdi = signal as u64 ; // First argument: signal number.
432- context. rsp = ret_sp as u64 ;
537+ // Hand control to the handler.
538+ context. rip = delivery. handler as u64 ;
539+ context. rdi = delivery. signal as u64 ;
540+ context. rsi = info_addr as u64 ;
541+ context. rdx = uc_addr as u64 ;
542+ context. rsp = ret_addr as u64 ;
433543 context. rflags &= !consts:: RFLAGS_DF ;
434544}
435545
0 commit comments