Skip to main content

syscall/
sigabi.rs

1use core::sync::atomic::{AtomicUsize, Ordering};
2
3/// Signal runtime struct for the entire process
4#[derive(Debug)]
5#[repr(C, align(4096))]
6pub struct SigProcControl {
7    pub pending: AtomicU64,
8    pub actions: [RawAction; 64],
9    pub sender_infos: [AtomicU64; 32],
10    //pub queue: [RealtimeSig; 32], TODO
11    // qhead, qtail TODO
12}
13/*#[derive(Debug)]
14#[repr(transparent)]
15pub struct RealtimeSig {
16    pub arg: NonatomicUsize,
17}*/
18#[derive(Debug, Default)]
19#[repr(C, align(16))]
20pub struct RawAction {
21    /// Only two MSBs are interesting for the kernel. If bit 63 is set, signal is ignored. If bit
22    /// 62 is set and the signal is SIGTSTP/SIGTTIN/SIGTTOU, it's equivalent to the action of
23    /// Stop.
24    pub first: AtomicU64,
25    /// Completely ignored by the kernel, but exists so userspace can (when 16-byte atomics exist)
26    /// atomically set both the handler, sigaction flags, and sigaction mask.
27    pub user_data: AtomicU64,
28}
29
30/// Signal runtime struct for a thread
31#[derive(Debug, Default)]
32#[repr(C)]
33pub struct Sigcontrol {
34    // composed of [lo "pending" | lo "unmasked", hi "pending" | hi "unmasked"]
35    pub word: [AtomicU64; 2],
36
37    // lo = sender pid, hi = sender ruid
38    pub sender_infos: [AtomicU64; 32],
39
40    pub control_flags: SigatomicUsize,
41
42    pub saved_ip: NonatomicUsize,          // rip/eip/pc
43    pub saved_archdep_reg: NonatomicUsize, // rflags(x64)/eflags(x86)/x0(aarch64)/t0(riscv64)
44    pub saved_excp_code: NonatomicUsize,   // code(x64/x86)/esr(aarch64)/scause(riscv64)
45    pub saved_excp_addr: NonatomicUsize,   // cr2(x64/x86)/far(aarch64)/stval(riscv64)
46}
47#[derive(Clone, Copy, Debug)]
48pub struct SenderInfo {
49    pub pid: u32,
50    pub ruid: u32,
51}
52impl SenderInfo {
53    #[inline]
54    pub fn raw(self) -> u64 {
55        u64::from(self.pid) | (u64::from(self.ruid) << 32)
56    }
57    #[inline]
58    pub const fn from_raw(raw: u64) -> Self {
59        Self {
60            pid: raw as u32,
61            ruid: (raw >> 32) as u32,
62        }
63    }
64}
65
66impl Sigcontrol {
67    pub fn currently_pending_unblocked(&self, proc: &SigProcControl) -> u64 {
68        let proc_pending = proc.pending.load(Ordering::Relaxed);
69        let [w0, w1] = core::array::from_fn(|i| {
70            let w = self.word[i].load(Ordering::Relaxed);
71            ((w | (proc_pending >> (i * 32))) & 0xffff_ffff) & (w >> 32)
72        });
73        //core::sync::atomic::fence(Ordering::Acquire);
74        w0 | (w1 << 32)
75    }
76    pub fn set_allowset(&self, new_allowset: u64) -> u64 {
77        //core::sync::atomic::fence(Ordering::Release);
78        let [w0, w1] = self.word.each_ref().map(|w| w.load(Ordering::Relaxed));
79        let old_a0 = w0 & 0xffff_ffff_0000_0000;
80        let old_a1 = w1 & 0xffff_ffff_0000_0000;
81        let new_a0 = (new_allowset & 0xffff_ffff) << 32;
82        let new_a1 = new_allowset & 0xffff_ffff_0000_0000;
83
84        let prev_w0 = self.word[0].fetch_add(new_a0.wrapping_sub(old_a0), Ordering::Relaxed);
85        let prev_w1 = self.word[1].fetch_add(new_a1.wrapping_sub(old_a1), Ordering::Relaxed);
86        //core::sync::atomic::fence(Ordering::Acquire);
87        let up0 = prev_w0 & (prev_w0 >> 32);
88        let up1 = prev_w1 & (prev_w1 >> 32);
89
90        up0 | (up1 << 32)
91    }
92}
93
94#[derive(Debug, Default)]
95#[repr(transparent)]
96pub struct SigatomicUsize(AtomicUsize);
97
98impl SigatomicUsize {
99    #[inline]
100    pub fn load(&self, ordering: Ordering) -> usize {
101        let value = self.0.load(Ordering::Relaxed);
102        if ordering != Ordering::Relaxed {
103            core::sync::atomic::compiler_fence(ordering);
104        }
105        value
106    }
107    #[inline]
108    pub fn store(&self, value: usize, ordering: Ordering) {
109        if ordering != Ordering::Relaxed {
110            core::sync::atomic::compiler_fence(ordering);
111        }
112        self.0.store(value, Ordering::Relaxed);
113    }
114}
115#[derive(Debug, Default)]
116#[repr(transparent)]
117pub struct NonatomicUsize(AtomicUsize);
118
119impl NonatomicUsize {
120    #[inline]
121    pub const fn new(a: usize) -> Self {
122        Self(AtomicUsize::new(a))
123    }
124
125    #[inline]
126    pub fn get(&self) -> usize {
127        self.0.load(Ordering::Relaxed)
128    }
129    #[inline]
130    pub fn set(&self, value: usize) {
131        self.0.store(value, Ordering::Relaxed);
132    }
133}
134
135pub fn sig_bit(sig: usize) -> u64 {
136    1 << (sig - 1)
137}
138
139// TODO: Move to redox_rt?
140impl SigProcControl {
141    /// Checks if `sig` should be ignored based on the current action flags.
142    ///
143    /// * `sig` - The signal to check (e.g. `SIGCHLD`).
144    ///
145    /// * `stop_or_continue` - Whether the signal is generated because a child
146    ///   process stopped (`SIGSTOP`, `SIGTSTP`) or continued (`SIGCONT`). If
147    ///   `true` and `sig` is `SIGCHLD`, the signal shall not be delivered if the
148    ///   `SA_NOCLDSTOP` flag is set for `SIGCHLD`.
149    pub fn signal_will_ign(&self, sig: usize, stop_or_continue: bool) -> bool {
150        let flags = self.actions[sig - 1].first.load(Ordering::Relaxed);
151        let will_ign = flags & (1 << 63) != 0;
152        let sig_specific = flags & (1 << 62) != 0; // SA_NOCLDSTOP if sig == SIGCHLD
153
154        will_ign || (sig == SIGCHLD && stop_or_continue && sig_specific)
155    }
156    // TODO: Move to redox_rt?
157    pub fn signal_will_stop(&self, sig: usize) -> bool {
158        use crate::flag::*;
159        matches!(sig, SIGTSTP | SIGTTIN | SIGTTOU)
160            && self.actions[sig - 1].first.load(Ordering::Relaxed) & (1 << 62) != 0
161    }
162}
163
164#[cfg(not(target_arch = "x86"))]
165pub use core::sync::atomic::AtomicU64;
166
167use crate::SIGCHLD;
168
169#[cfg(target_arch = "x86")]
170pub use self::atomic::AtomicU64;
171
172#[cfg(target_arch = "x86")]
173mod atomic {
174    use core::{cell::UnsafeCell, sync::atomic::Ordering};
175
176    #[derive(Debug, Default)]
177    pub struct AtomicU64(UnsafeCell<u64>);
178
179    unsafe impl Send for AtomicU64 {}
180    unsafe impl Sync for AtomicU64 {}
181
182    impl AtomicU64 {
183        pub const fn new(inner: u64) -> Self {
184            Self(UnsafeCell::new(inner))
185        }
186        pub fn compare_exchange(
187            &self,
188            old: u64,
189            new: u64,
190            _success: Ordering,
191            _failure: Ordering,
192        ) -> Result<u64, u64> {
193            let old_hi = (old >> 32) as u32;
194            let old_lo = old as u32;
195            let new_hi = (new >> 32) as u32;
196            let new_lo = new as u32;
197            let mut out_hi;
198            let mut out_lo;
199
200            unsafe {
201                core::arch::asm!("lock cmpxchg8b [{}]", in(reg) self.0.get(), inout("edx") old_hi => out_hi, inout("eax") old_lo => out_lo, in("ecx") new_hi, in("ebx") new_lo);
202            }
203
204            if old_hi == out_hi && old_lo == out_lo {
205                Ok(old)
206            } else {
207                Err(u64::from(out_lo) | (u64::from(out_hi) << 32))
208            }
209        }
210        pub fn load(&self, ordering: Ordering) -> u64 {
211            match self.compare_exchange(0, 0, ordering, ordering) {
212                Ok(new) => new,
213                Err(new) => new,
214            }
215        }
216        pub fn store(&self, new: u64, ordering: Ordering) {
217            let mut old = 0;
218
219            loop {
220                match self.compare_exchange(old, new, ordering, Ordering::Relaxed) {
221                    Ok(_) => break,
222                    Err(new) => {
223                        old = new;
224                        core::hint::spin_loop();
225                    }
226                }
227            }
228        }
229        pub fn fetch_update(
230            &self,
231            set_order: Ordering,
232            fetch_order: Ordering,
233            mut f: impl FnMut(u64) -> Option<u64>,
234        ) -> Result<u64, u64> {
235            let mut old = self.load(fetch_order);
236
237            loop {
238                let new = f(old).ok_or(old)?;
239                match self.compare_exchange(old, new, set_order, Ordering::Relaxed) {
240                    Ok(_) => return Ok(new),
241                    Err(changed) => {
242                        old = changed;
243                        core::hint::spin_loop();
244                    }
245                }
246            }
247        }
248        pub fn fetch_or(&self, bits: u64, order: Ordering) -> u64 {
249            self.fetch_update(order, Ordering::Relaxed, |b| Some(b | bits))
250                .unwrap()
251        }
252        pub fn fetch_and(&self, bits: u64, order: Ordering) -> u64 {
253            self.fetch_update(order, Ordering::Relaxed, |b| Some(b & bits))
254                .unwrap()
255        }
256        pub fn fetch_add(&self, term: u64, order: Ordering) -> u64 {
257            self.fetch_update(order, Ordering::Relaxed, |b| Some(b.wrapping_add(term)))
258                .unwrap()
259        }
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use std::sync::{
266        atomic::{AtomicU64, Ordering},
267        Arc,
268    };
269
270    #[cfg(not(loom))]
271    use std::{sync::Mutex, thread};
272    #[cfg(not(loom))]
273    fn model(f: impl FnOnce()) {
274        f()
275    }
276
277    #[cfg(loom)]
278    use loom::{model, sync::Mutex, thread};
279
280    use crate::{RawAction, SigProcControl, Sigcontrol};
281
282    struct FakeThread {
283        ctl: Sigcontrol,
284        pctl: SigProcControl,
285        ctxt: Mutex<()>,
286    }
287    impl Default for FakeThread {
288        fn default() -> Self {
289            Self {
290                ctl: Sigcontrol::default(),
291                pctl: SigProcControl {
292                    pending: AtomicU64::new(0),
293                    actions: core::array::from_fn(|_| RawAction::default()),
294                    sender_infos: Default::default(),
295                },
296                ctxt: Default::default(),
297            }
298        }
299    }
300
301    #[test]
302    fn singlethread_mask() {
303        model(|| {
304            let fake_thread = Arc::new(FakeThread::default());
305
306            let thread = {
307                let fake_thread = Arc::clone(&fake_thread);
308
309                thread::spawn(move || {
310                    fake_thread.ctl.set_allowset(!0);
311                    {
312                        let _g = fake_thread.ctxt.lock();
313                        if fake_thread
314                            .ctl
315                            .currently_pending_unblocked(&fake_thread.pctl)
316                            == 0
317                        {
318                            drop(_g);
319                            thread::park();
320                        }
321                    }
322                })
323            };
324
325            for sig in 1..=64 {
326                let _g = fake_thread.ctxt.lock();
327
328                let idx = sig - 1;
329                let bit = 1 << (idx % 32);
330
331                fake_thread.ctl.word[idx / 32].fetch_or(bit, Ordering::Relaxed);
332                let w = fake_thread.ctl.word[idx / 32].load(Ordering::Relaxed);
333
334                if w & (w >> 32) != 0 {
335                    thread.thread().unpark();
336                }
337            }
338
339            thread.join().unwrap();
340        });
341    }
342}