kernel/abi/linux/riscv64/
signal.rs

1//! Linux RISC-V 64 signal syscalls and signal handling
2//!
3//! Implements POSIX signals with Linux-compatible semantics, integrated with Scarlet's
4//! event system for cross-ABI signal delivery.
5
6use crate::abi::linux::riscv64::LinuxRiscv64Abi;
7use crate::arch::Trapframe;
8use crate::ipc::event::{Event, EventContent, ProcessControlType};
9use crate::task::mytask;
10use alloc::collections::BTreeMap;
11
12/// Linux signal numbers (POSIX standard)
13#[repr(u32)]
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
15pub enum LinuxSignal {
16    SIGHUP = 1,
17    SIGINT = 2,
18    SIGQUIT = 3,
19    SIGILL = 4,
20    SIGTRAP = 5,
21    SIGABRT = 6,
22    SIGBUS = 7,
23    SIGFPE = 8,
24    SIGKILL = 9,
25    SIGUSR1 = 10,
26    SIGSEGV = 11,
27    SIGUSR2 = 12,
28    SIGPIPE = 13,
29    SIGALRM = 14,
30    SIGTERM = 15,
31    SIGSTKFLT = 16,
32    SIGCHLD = 17,
33    SIGCONT = 18,
34    SIGSTOP = 19,
35    SIGTSTP = 20,
36    SIGTTIN = 21,
37    SIGTTOU = 22,
38    SIGURG = 23,
39    SIGXCPU = 24,
40    SIGXFSZ = 25,
41    SIGVTALRM = 26,
42    SIGPROF = 27,
43    SIGWINCH = 28,
44    SIGIO = 29,
45    SIGPWR = 30,
46    SIGSYS = 31,
47}
48
49impl LinuxSignal {
50    /// Convert from u32 to LinuxSignal
51    pub fn from_u32(signal: u32) -> Option<Self> {
52        match signal {
53            1 => Some(Self::SIGHUP),
54            2 => Some(Self::SIGINT),
55            3 => Some(Self::SIGQUIT),
56            4 => Some(Self::SIGILL),
57            5 => Some(Self::SIGTRAP),
58            6 => Some(Self::SIGABRT),
59            7 => Some(Self::SIGBUS),
60            8 => Some(Self::SIGFPE),
61            9 => Some(Self::SIGKILL),
62            10 => Some(Self::SIGUSR1),
63            11 => Some(Self::SIGSEGV),
64            12 => Some(Self::SIGUSR2),
65            13 => Some(Self::SIGPIPE),
66            14 => Some(Self::SIGALRM),
67            15 => Some(Self::SIGTERM),
68            16 => Some(Self::SIGSTKFLT),
69            17 => Some(Self::SIGCHLD),
70            18 => Some(Self::SIGCONT),
71            19 => Some(Self::SIGSTOP),
72            20 => Some(Self::SIGTSTP),
73            21 => Some(Self::SIGTTIN),
74            22 => Some(Self::SIGTTOU),
75            23 => Some(Self::SIGURG),
76            24 => Some(Self::SIGXCPU),
77            25 => Some(Self::SIGXFSZ),
78            26 => Some(Self::SIGVTALRM),
79            27 => Some(Self::SIGPROF),
80            28 => Some(Self::SIGWINCH),
81            29 => Some(Self::SIGIO),
82            30 => Some(Self::SIGPWR),
83            31 => Some(Self::SIGSYS),
84            _ => None,
85        }
86    }
87
88    /// Get default action for this signal
89    pub fn default_action(&self) -> SignalAction {
90        match self {
91            Self::SIGKILL | Self::SIGSTOP => SignalAction::ForceTerminate,
92            Self::SIGCHLD | Self::SIGURG | Self::SIGWINCH => SignalAction::Ignore,
93            Self::SIGCONT => SignalAction::Continue,
94            Self::SIGTSTP | Self::SIGTTIN | Self::SIGTTOU => SignalAction::Stop,
95            _ => SignalAction::Terminate,
96        }
97    }
98}
99
100/// Signal action types
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub enum SignalAction {
103    /// Default action: terminate process
104    Terminate,
105    /// Force terminate (cannot be caught/ignored)
106    ForceTerminate,
107    /// Ignore signal
108    Ignore,
109    /// Stop process
110    Stop,
111    /// Continue process
112    Continue,
113    /// Custom handler
114    Custom(usize), // Handler function address
115}
116
117/// Signal mask for blocking signals
118#[derive(Debug, Clone, Copy, Default)]
119pub struct SignalMask {
120    mask: u64, // Bit mask for signals 1-64
121}
122
123impl SignalMask {
124    pub fn new() -> Self {
125        Self { mask: 0 }
126    }
127
128    pub fn block_signal(&mut self, signal: LinuxSignal) {
129        self.mask |= 1u64 << (signal as u32 - 1);
130    }
131
132    pub fn unblock_signal(&mut self, signal: LinuxSignal) {
133        self.mask &= !(1u64 << (signal as u32 - 1));
134    }
135
136    pub fn is_blocked(&self, signal: LinuxSignal) -> bool {
137        (self.mask & (1u64 << (signal as u32 - 1))) != 0
138    }
139
140    pub fn raw(&self) -> u64 {
141        self.mask
142    }
143
144    pub fn set_raw(&mut self, mask: u64) {
145        self.mask = mask;
146    }
147}
148
149/// Signal handler state for a task
150#[derive(Debug, Clone)]
151pub struct SignalState {
152    /// Signal handlers (signal number -> handler action)
153    pub handlers: BTreeMap<LinuxSignal, SignalAction>,
154    /// Blocked signals mask
155    pub blocked: SignalMask,
156    /// Pending signals that are blocked
157    pub pending: SignalMask,
158}
159
160impl Default for SignalState {
161    fn default() -> Self {
162        let mut handlers = BTreeMap::new();
163        // Set default actions for all signals
164        for signal_num in 1..=31 {
165            if let Some(signal) = LinuxSignal::from_u32(signal_num) {
166                handlers.insert(signal, signal.default_action());
167            }
168        }
169
170        Self {
171            handlers,
172            blocked: SignalMask::new(),
173            pending: SignalMask::new(),
174        }
175    }
176}
177
178impl SignalState {
179    pub fn new() -> Self {
180        Self::default()
181    }
182
183    /// Set signal handler
184    pub fn set_handler(&mut self, signal: LinuxSignal, action: SignalAction) {
185        // SIGKILL and SIGSTOP cannot be caught or ignored
186        if signal != LinuxSignal::SIGKILL && signal != LinuxSignal::SIGSTOP {
187            self.handlers.insert(signal, action);
188        }
189    }
190
191    /// Get signal handler
192    pub fn get_handler(&self, signal: LinuxSignal) -> SignalAction {
193        self.handlers
194            .get(&signal)
195            .copied()
196            .unwrap_or(signal.default_action())
197    }
198
199    /// Add pending signal
200    pub fn add_pending(&mut self, signal: LinuxSignal) {
201        self.pending.block_signal(signal);
202    }
203
204    /// Remove pending signal
205    pub fn remove_pending(&mut self, signal: LinuxSignal) {
206        self.pending.unblock_signal(signal);
207    }
208
209    /// Check if signal is pending
210    pub fn is_pending(&self, signal: LinuxSignal) -> bool {
211        self.pending.is_blocked(signal)
212    }
213
214    /// Get next deliverable signal (not blocked and pending)
215    pub fn next_deliverable_signal(&self) -> Option<LinuxSignal> {
216        for signal_num in 1..=31 {
217            if let Some(signal) = LinuxSignal::from_u32(signal_num) {
218                if self.is_pending(signal) && !self.blocked.is_blocked(signal) {
219                    return Some(signal);
220                }
221            }
222        }
223        None
224    }
225}
226
227/// Linux rt_sigaction system call implementation
228///
229/// int rt_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact, size_t sigsetsize);
230pub fn sys_rt_sigaction(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
231    let task = mytask().unwrap();
232
233    let signum = trapframe.get_arg(0) as u32;
234    let act_ptr = trapframe.get_arg(1);
235    let oldact_ptr = trapframe.get_arg(2);
236    let _sigsetsize = trapframe.get_arg(3);
237
238    // Convert signal number to LinuxSignal
239    let signal = match LinuxSignal::from_u32(signum) {
240        Some(sig) => sig,
241        None => {
242            trapframe.set_return_value(!0usize); // -1 (EINVAL)
243            trapframe.increment_pc_next(&task);
244            return !0usize;
245        }
246    };
247
248    let mut signal_state = abi.signal_state.lock();
249
250    // Get old action if requested
251    if oldact_ptr != 0 {
252        let old_action = signal_state.get_handler(signal);
253        // TODO: Copy old action to user space (requires memory copying functionality)
254        // For now, just acknowledge the request
255        let _ = old_action;
256    }
257
258    // Set new action if provided
259    if act_ptr != 0 {
260        // TODO: Read sigaction structure from user space
261        // For now, we'll implement a simplified version that just sets ignore/default
262        // In a real implementation, this would read the sigaction struct from user memory
263
264        // For demonstration, assume user wants to ignore the signal
265        signal_state.set_handler(signal, SignalAction::Ignore);
266    }
267
268    trapframe.set_return_value(0);
269    trapframe.increment_pc_next(&task);
270    0
271}
272
273/// Linux rt_sigprocmask system call implementation
274///
275/// int rt_sigprocmask(int how, const sigset_t *set, sigset_t *oldset, size_t sigsetsize);
276pub fn sys_rt_sigprocmask(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
277    let task = mytask().unwrap();
278
279    let how = trapframe.get_arg(0);
280    let set_ptr = trapframe.get_arg(1);
281    let oldset_ptr = trapframe.get_arg(2);
282    let _sigsetsize = trapframe.get_arg(3);
283
284    let mut signal_state = abi.signal_state.lock();
285
286    // Save old mask if requested
287    if oldset_ptr != 0 {
288        let old_mask = signal_state.blocked.raw();
289        // TODO: Copy old mask to user space
290        // For now, just acknowledge the request
291        let _ = old_mask;
292    }
293
294    // Modify mask if new set is provided
295    if set_ptr != 0 {
296        // TODO: Read sigset_t from user space
297        // For now, implement a simplified version
298
299        // SIG_BLOCK = 0, SIG_UNBLOCK = 1, SIG_SETMASK = 2
300        match how {
301            0 => { // SIG_BLOCK
302                // TODO: Read mask from user space and add to blocked signals
303            }
304            1 => { // SIG_UNBLOCK
305                // TODO: Read mask from user space and remove from blocked signals
306            }
307            2 => { // SIG_SETMASK
308                // TODO: Read mask from user space and set as blocked signals
309            }
310            _ => {
311                trapframe.set_return_value(!0usize); // -1 (EINVAL)
312                trapframe.increment_pc_next(&task);
313                return !0usize;
314            }
315        }
316    }
317
318    trapframe.set_return_value(0);
319    trapframe.increment_pc_next(&task);
320    0
321}
322
323/// Convert Scarlet ProcessControlType to Linux signal
324pub fn process_control_to_signal(control_type: ProcessControlType) -> Option<LinuxSignal> {
325    match control_type {
326        ProcessControlType::Terminate => Some(LinuxSignal::SIGTERM),
327        ProcessControlType::Kill => Some(LinuxSignal::SIGKILL),
328        ProcessControlType::Stop => Some(LinuxSignal::SIGSTOP),
329        ProcessControlType::Continue => Some(LinuxSignal::SIGCONT),
330        ProcessControlType::Interrupt => Some(LinuxSignal::SIGINT),
331        ProcessControlType::Quit => Some(LinuxSignal::SIGQUIT),
332        ProcessControlType::Hangup => Some(LinuxSignal::SIGHUP),
333        ProcessControlType::ChildExit => Some(LinuxSignal::SIGCHLD),
334        ProcessControlType::PipeBroken => Some(LinuxSignal::SIGPIPE),
335        ProcessControlType::Alarm => Some(LinuxSignal::SIGALRM),
336        ProcessControlType::IoReady => Some(LinuxSignal::SIGIO),
337        ProcessControlType::User(sig) => LinuxSignal::from_u32(sig + 32), // Map to RT signals
338    }
339}
340
341/// Handle incoming event and convert to signal if needed
342pub fn handle_event_to_signal(event: &Event) -> Option<LinuxSignal> {
343    match &event.content {
344        EventContent::ProcessControl(control_type) => process_control_to_signal(*control_type),
345        _ => None, // Non-signal events are ignored in Linux ABI
346    }
347}
348
349/// Deliver a signal to a task's signal state
350pub fn deliver_signal_to_task(abi: &LinuxRiscv64Abi, signal: LinuxSignal) {
351    // Add signal to pending if it's not already pending
352    let mut signal_state = abi.signal_state.lock();
353    if !signal_state.is_pending(signal) {
354        signal_state.add_pending(signal);
355    }
356}
357
358/// Check if task has pending signals and return the next one to handle
359pub fn get_next_pending_signal(abi: &LinuxRiscv64Abi) -> Option<LinuxSignal> {
360    let signal_state = abi.signal_state.lock();
361    signal_state.next_deliverable_signal()
362}
363
364/// Process pending signals for a task with explicit signal state
365/// Returns true if a signal was handled and execution should be interrupted
366pub fn process_pending_signals_with_state(
367    signal_state: &mut SignalState,
368    trapframe: &mut Trapframe,
369) -> bool {
370    if let Some(signal) = signal_state.next_deliverable_signal() {
371        let action = signal_state.get_handler(signal);
372
373        // Remove signal from pending
374        signal_state.remove_pending(signal);
375
376        match action {
377            SignalAction::Terminate | SignalAction::ForceTerminate => {
378                // TODO: Implement actual task termination
379                // This should call task.set_state(TaskState::Terminated)
380                // and set exit code based on signal
381                crate::early_println!("Signal {}: Terminating task", signal as u32);
382                true
383            }
384            SignalAction::Ignore => {
385                // Signal ignored, continue execution
386                false
387            }
388            SignalAction::Stop => {
389                // TODO: Implement actual task stopping
390                // This should call task.set_state(TaskState::Stopped)
391                crate::early_println!("Signal {}: Stopping task", signal as u32);
392                true
393            }
394            SignalAction::Continue => {
395                // TODO: Implement actual task continuation
396                // This should call task.set_state(TaskState::Ready) if stopped
397                crate::early_println!("Signal {}: Continuing task", signal as u32);
398                false
399            }
400            SignalAction::Custom(handler_addr) => {
401                // Set up user-space signal handler execution
402                crate::early_println!(
403                    "Signal {}: Calling custom handler at {:#x}",
404                    signal as u32,
405                    handler_addr
406                );
407                setup_signal_handler(trapframe, handler_addr, signal);
408                true
409            }
410        }
411    } else {
412        false
413    }
414}
415
416/// Enhanced signal handler setup with proper context saving
417fn setup_signal_handler(trapframe: &mut Trapframe, handler_addr: usize, signal: LinuxSignal) {
418    // TODO: Complete signal handler setup
419    // 1. Save current trapframe on user stack
420    // 2. Set up signal stack frame
421    // 3. Set up signal handler arguments
422    // 4. Set up return address to signal return trampoline
423
424    // For now, basic setup:
425    // Set up arguments for signal handler: handler(signal_number)
426    trapframe.set_arg(0, signal as usize);
427
428    // Jump to signal handler
429    trapframe.epc = handler_addr as u64;
430
431    // TODO: Implement signal return mechanism
432    // - Set up return address to rt_sigreturn trampoline
433    // - Save original context for restoration
434}
435
436/// Handle fatal signals that should terminate immediately
437/// This is a simplified implementation for basic signal handling
438pub fn handle_fatal_signal_immediately(signal: LinuxSignal) -> Result<(), &'static str> {
439    if let Some(task) = crate::task::mytask() {
440        let exit_code = match signal {
441            LinuxSignal::SIGKILL => 128 + 9,  // Standard SIGKILL exit code
442            LinuxSignal::SIGTERM => 128 + 15, // Standard SIGTERM exit code
443            LinuxSignal::SIGINT => 128 + 2,   // Standard SIGINT exit code
444            _ => return Err("Not a fatal signal"),
445        };
446
447        crate::early_println!(
448            "Signal {}: Immediately terminating task {} with exit code {}",
449            signal as u32,
450            task.get_id(),
451            exit_code
452        );
453
454        // Set task state to terminated and exit
455        task.exit(exit_code);
456        Ok(())
457    } else {
458        Err("No current task to terminate")
459    }
460}
461
462/// Check if a signal should be handled immediately (cannot be blocked/ignored)
463pub fn is_fatal_signal(signal: LinuxSignal) -> bool {
464    matches!(
465        signal,
466        LinuxSignal::SIGKILL | LinuxSignal::SIGTERM | LinuxSignal::SIGINT
467    )
468}
469
470/// Linux sys_tkill - Send a signal to a specific thread
471///
472/// tkill() sends a signal to a specific thread within the same thread group.
473/// This is a simplified implementation that mainly prevents crashes.
474///
475/// Arguments:
476/// - abi: LinuxRiscv64Abi context
477/// - trapframe: Trapframe containing syscall arguments
478///   - arg0: tid (thread ID)
479///   - arg1: sig (signal number)
480///
481/// Returns:
482/// - 0 on success
483/// - usize::MAX (Linux -1) on error
484pub fn sys_tkill(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
485    let task = match mytask() {
486        Some(t) => t,
487        None => return usize::MAX,
488    };
489
490    let tid = trapframe.get_arg(0) as i32;
491    let sig = trapframe.get_arg(1) as i32;
492
493    // Increment PC to avoid infinite loop
494    trapframe.increment_pc_next(task);
495
496    // For now, just log and return success
497    // A proper implementation would:
498    // 1. Find the target task by TID
499    // 2. Add the signal to its pending signal queue
500    // 3. Wake the task if it's sleeping
501    crate::early_println!(
502        "[sys_tkill] tid={} sig={} - NOOP (signal delivery not implemented)",
503        tid,
504        sig
505    );
506
507    // Return success to avoid crashing applications
508    // Many applications use tkill for thread management
509    0
510}