esp_sync/
raw.rs

1//! TODO
2
3use core::sync::atomic::{Ordering, compiler_fence};
4
5use crate::RestoreState;
6
7/// Trait for single-core locks.
8pub trait RawLock {
9    /// Acquires the raw lock
10    ///
11    /// # Safety
12    ///
13    /// The returned tokens must be released in reverse order, on the same thread that they were
14    /// created on.
15    unsafe fn enter(&self) -> RestoreState;
16
17    /// Releases the raw lock
18    ///
19    /// # Safety
20    ///
21    /// - The `token` must be created by `self.enter()`
22    /// - Tokens must be released in reverse order to their creation, on the same thread that they
23    ///   were created on.
24    unsafe fn exit(&self, token: RestoreState);
25}
26
27/// A lock that disables interrupts.
28pub struct SingleCoreInterruptLock;
29
30// Reserved bits in the PS register, these must be written as 0.
31#[cfg(all(xtensa, debug_assertions))]
32const RESERVED_MASK: u32 = 0b1111_1111_1111_1000_1111_0000_0000_0000;
33
34impl RawLock for SingleCoreInterruptLock {
35    #[inline]
36    unsafe fn enter(&self) -> RestoreState {
37        cfg_if::cfg_if! {
38            if #[cfg(riscv)] {
39                let mut mstatus = 0u32;
40                unsafe { core::arch::asm!("csrrci {0}, mstatus, 8", inout(reg) mstatus); }
41                let token = mstatus & 0b1000;
42            } else if #[cfg(xtensa)] {
43                let token: u32;
44                unsafe { core::arch::asm!("rsil {0}, 5", out(reg) token); }
45                #[cfg(debug_assertions)]
46                let token = token & !RESERVED_MASK;
47            } else {
48                compile_error!("Unsupported architecture")
49            }
50        };
51
52        // Ensure no subsequent memory accesses are reordered to before interrupts are
53        // disabled.
54        compiler_fence(Ordering::SeqCst);
55
56        unsafe { RestoreState::new(token) }
57    }
58
59    #[inline]
60    unsafe fn exit(&self, token: RestoreState) {
61        // Ensure no preceeding memory accesses are reordered to after interrupts are
62        // enabled.
63        compiler_fence(Ordering::SeqCst);
64
65        let token = token.inner();
66
67        cfg_if::cfg_if! {
68            if #[cfg(riscv)] {
69                if token != 0 {
70                    unsafe {
71                        riscv::interrupt::enable();
72                    }
73                }
74            } else if #[cfg(xtensa)] {
75                #[cfg(debug_assertions)]
76                if token & RESERVED_MASK != 0 {
77                    // We could do this transformation in fmt.rs automatically, but experiments
78                    // show this is only worth it in terms of binary size for code inlined into many places.
79                    #[cold]
80                    #[inline(never)]
81                    fn __assert_failed() {
82                        panic!("Reserved bits in PS register must be written as 0");
83                    }
84
85                    __assert_failed();
86                }
87
88                unsafe {
89                    core::arch::asm!(
90                        "wsr.ps {0}",
91                        "rsync", in(reg) token)
92                }
93            } else {
94                compile_error!("Unsupported architecture")
95            }
96        }
97    }
98}