aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiam Murphy <[email protected]>2021-07-27 17:28:52 +1000
committerLiam Murphy <[email protected]>2021-07-27 17:28:52 +1000
commit079526559f44a0822574205e8798136f11f207d3 (patch)
tree139c2f70d7227a128cae234869f846e86b6dd6e7
parent1b7ad7080e6a8c96f2622d75b99a4d3bdbc7c394 (diff)
Remove critical sections from `PeripheralMutex` interrupt handler by checking the interrupt's priority on startup.
Since `PeripheralMutex` is the only way to safely maintain state across interrupts, and it no longer allows setting the interrupt's priority, the priority changing isn't a concern. This also prevents other causes of UB due to the interrupt being exposed during `with`, and allowing enabling the interrupt and setting its context to a bogus pointer.
-rw-r--r--embassy-extras/src/peripheral.rs140
-rw-r--r--embassy-extras/src/peripheral_shared.rs61
-rw-r--r--embassy-extras/src/usb/usb_serial.rs6
-rw-r--r--embassy-nrf/src/buffered_uarte.rs20
-rw-r--r--embassy-stm32/src/eth/v2/mod.rs8
5 files changed, 187 insertions, 48 deletions
diff --git a/embassy-extras/src/peripheral.rs b/embassy-extras/src/peripheral.rs
index 2358ab96f..2402ba9eb 100644
--- a/embassy-extras/src/peripheral.rs
+++ b/embassy-extras/src/peripheral.rs
@@ -1,8 +1,9 @@
1use core::cell::UnsafeCell; 1use core::cell::UnsafeCell;
2use core::marker::{PhantomData, PhantomPinned}; 2use core::marker::{PhantomData, PhantomPinned};
3use core::pin::Pin; 3use core::pin::Pin;
4use core::ptr;
5 4
5use cortex_m::peripheral::scb::{Exception, SystemHandler, VectActive};
6use cortex_m::peripheral::{NVIC, SCB};
6use embassy::interrupt::{Interrupt, InterruptExt}; 7use embassy::interrupt::{Interrupt, InterruptExt};
7 8
8/// A version of `PeripheralState` without the `'static` bound, 9/// A version of `PeripheralState` without the `'static` bound,
@@ -50,8 +51,79 @@ pub struct PeripheralMutex<S: PeripheralStateUnchecked> {
50 _pinned: PhantomPinned, 51 _pinned: PhantomPinned,
51} 52}
52 53
54fn exception_to_system_handler(exception: Exception) -> Option<SystemHandler> {
55 match exception {
56 Exception::NonMaskableInt | Exception::HardFault => None,
57 #[cfg(not(armv6m))]
58 Exception::MemoryManagement => Some(SystemHandler::MemoryManagement),
59 #[cfg(not(armv6m))]
60 Exception::BusFault => Some(SystemHandler::BusFault),
61 #[cfg(not(armv6m))]
62 Exception::UsageFault => Some(SystemHandler::UsageFault),
63 #[cfg(any(armv8m, target_arch = "x86_64"))]
64 Exception::SecureFault => Some(SystemHandler::SecureFault),
65 Exception::SVCall => Some(SystemHandler::SVCall),
66 #[cfg(not(armv6m))]
67 Exception::DebugMonitor => Some(SystemHandler::DebugMonitor),
68 Exception::PendSV => Some(SystemHandler::PendSV),
69 Exception::SysTick => Some(SystemHandler::SysTick),
70 }
71}
72
73/// Whether `irq` can be preempted by the current interrupt.
74pub(crate) fn can_be_preempted(irq: &impl Interrupt) -> bool {
75 match SCB::vect_active() {
76 // Thread mode can't preempt each other
77 VectActive::ThreadMode => false,
78 VectActive::Exception(exception) => {
79 // `SystemHandler` is a subset of `Exception` for those with configurable priority.
80 // There's no built in way to convert between them, so `exception_to_system_handler` was necessary.
81 if let Some(system_handler) = exception_to_system_handler(exception) {
82 let current_prio = SCB::get_priority(system_handler);
83 let irq_prio = irq.get_priority().into();
84 if current_prio < irq_prio {
85 true
86 } else if current_prio == irq_prio {
87 // When multiple interrupts have the same priority number,
88 // the pending interrupt with the lowest interrupt number takes precedence.
89 (exception.irqn() as i16) < irq.number() as i16
90 } else {
91 false
92 }
93 } else {
94 // There's no safe way I know of to maintain `!Send` state across invocations of HardFault or NMI, so that should be fine.
95 false
96 }
97 }
98 VectActive::Interrupt { irqn } => {
99 #[derive(Clone, Copy)]
100 struct NrWrap(u16);
101 unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap {
102 fn number(self) -> u16 {
103 self.0
104 }
105 }
106 let current_prio = NVIC::get_priority(NrWrap(irqn.into()));
107 let irq_prio = irq.get_priority().into();
108 if current_prio < irq_prio {
109 true
110 } else if current_prio == irq_prio {
111 // When multiple interrupts have the same priority number,
112 // the pending interrupt with the lowest interrupt number takes precedence.
113 (irqn as u16) < irq.number()
114 } else {
115 false
116 }
117 }
118 }
119}
120
53impl<S: PeripheralStateUnchecked> PeripheralMutex<S> { 121impl<S: PeripheralStateUnchecked> PeripheralMutex<S> {
54 pub fn new(state: S, irq: S::Interrupt) -> Self { 122 pub fn new(state: S, irq: S::Interrupt) -> Self {
123 if can_be_preempted(&irq) {
124 panic!("`PeripheralMutex` cannot be created in an interrupt with higher priority than the interrupt it wraps");
125 }
126
55 Self { 127 Self {
56 irq, 128 irq,
57 irq_setup_done: false, 129 irq_setup_done: false,
@@ -70,17 +142,13 @@ impl<S: PeripheralStateUnchecked> PeripheralMutex<S> {
70 142
71 this.irq.disable(); 143 this.irq.disable();
72 this.irq.set_handler(|p| { 144 this.irq.set_handler(|p| {
73 critical_section::with(|_| { 145 // Safety: it's OK to get a &mut to the state, since
74 if p.is_null() { 146 // - We checked that the thread owning the `PeripheralMutex` can't preempt us in `new`.
75 // The state was dropped, so we can't operate on it. 147 // Interrupts' priorities can only be changed with raw embassy `Interrupts`,
76 return; 148 // which can't safely store a `PeripheralMutex` across invocations.
77 } 149 // - We can't have preempted a with() call because the irq is disabled during it.
78 // Safety: it's OK to get a &mut to the state, since 150 let state = unsafe { &mut *(p as *mut S) };
79 // - We're in a critical section, no one can preempt us (and call with()) 151 state.on_interrupt();
80 // - We can't have preempted a with() call because the irq is disabled during it.
81 let state = unsafe { &mut *(p as *mut S) };
82 state.on_interrupt();
83 })
84 }); 152 });
85 this.irq 153 this.irq
86 .set_handler_context((&mut this.state) as *mut _ as *mut ()); 154 .set_handler_context((&mut this.state) as *mut _ as *mut ());
@@ -89,27 +157,63 @@ impl<S: PeripheralStateUnchecked> PeripheralMutex<S> {
89 this.irq_setup_done = true; 157 this.irq_setup_done = true;
90 } 158 }
91 159
92 pub fn with<R>(self: Pin<&mut Self>, f: impl FnOnce(&mut S, &mut S::Interrupt) -> R) -> R { 160 pub fn with<R>(self: Pin<&mut Self>, f: impl FnOnce(&mut S) -> R) -> R {
93 let this = unsafe { self.get_unchecked_mut() }; 161 let this = unsafe { self.get_unchecked_mut() };
94 162
163 let was_enabled = this.irq.is_enabled();
95 this.irq.disable(); 164 this.irq.disable();
96 165
97 // Safety: it's OK to get a &mut to the state, since the irq is disabled. 166 // Safety: it's OK to get a &mut to the state, since the irq is disabled.
98 let state = unsafe { &mut *this.state.get() }; 167 let state = unsafe { &mut *this.state.get() };
99 let r = f(state, &mut this.irq); 168 let r = f(state);
100 169
101 this.irq.enable(); 170 if was_enabled {
171 this.irq.enable();
172 }
102 173
103 r 174 r
104 } 175 }
176
177 /// Enables the wrapped interrupt.
178 pub fn enable(&self) {
179 // This is fine to do before initialization, because we haven't set the handler yet.
180 self.irq.enable()
181 }
182
183 /// Disables the wrapped interrupt.
184 pub fn disable(&self) {
185 self.irq.disable()
186 }
187
188 /// Returns whether the wrapped interrupt is enabled.
189 pub fn is_enabled(&self) -> bool {
190 self.irq.is_enabled()
191 }
192
193 /// Returns whether the wrapped interrupt is currently in a pending state.
194 pub fn is_pending(&self) -> bool {
195 self.irq.is_pending()
196 }
197
198 /// Forces the wrapped interrupt into a pending state.
199 pub fn pend(&self) {
200 self.irq.pend()
201 }
202
203 /// Forces the wrapped interrupt out of a pending state.
204 pub fn unpend(&self) {
205 self.irq.unpend()
206 }
207
208 /// Gets the priority of the wrapped interrupt.
209 pub fn priority(&self) -> <S::Interrupt as Interrupt>::Priority {
210 self.irq.get_priority()
211 }
105} 212}
106 213
107impl<S: PeripheralStateUnchecked> Drop for PeripheralMutex<S> { 214impl<S: PeripheralStateUnchecked> Drop for PeripheralMutex<S> {
108 fn drop(&mut self) { 215 fn drop(&mut self) {
109 self.irq.disable(); 216 self.irq.disable();
110 self.irq.remove_handler(); 217 self.irq.remove_handler();
111 // Set the context to null so that the interrupt will know we're dropped
112 // if we pre-empted it before it entered a critical section.
113 self.irq.set_handler_context(ptr::null_mut());
114 } 218 }
115} 219}
diff --git a/embassy-extras/src/peripheral_shared.rs b/embassy-extras/src/peripheral_shared.rs
index 5961441e0..ae9ae6935 100644
--- a/embassy-extras/src/peripheral_shared.rs
+++ b/embassy-extras/src/peripheral_shared.rs
@@ -1,9 +1,10 @@
1use core::marker::{PhantomData, PhantomPinned}; 1use core::marker::{PhantomData, PhantomPinned};
2use core::pin::Pin; 2use core::pin::Pin;
3use core::ptr;
4 3
5use embassy::interrupt::{Interrupt, InterruptExt}; 4use embassy::interrupt::{Interrupt, InterruptExt};
6 5
6use crate::peripheral::can_be_preempted;
7
7/// A version of `PeripheralState` without the `'static` bound, 8/// A version of `PeripheralState` without the `'static` bound,
8/// for cases where the compiler can't statically make sure 9/// for cases where the compiler can't statically make sure
9/// that `on_interrupt` doesn't reference anything which might be invalidated. 10/// that `on_interrupt` doesn't reference anything which might be invalidated.
@@ -39,6 +40,10 @@ pub struct Peripheral<S: PeripheralStateUnchecked> {
39 40
40impl<S: PeripheralStateUnchecked> Peripheral<S> { 41impl<S: PeripheralStateUnchecked> Peripheral<S> {
41 pub fn new(irq: S::Interrupt, state: S) -> Self { 42 pub fn new(irq: S::Interrupt, state: S) -> Self {
43 if can_be_preempted(&irq) {
44 panic!("`Peripheral` cannot be created in an interrupt with higher priority than the interrupt it wraps");
45 }
46
42 Self { 47 Self {
43 irq, 48 irq,
44 irq_setup_done: false, 49 irq_setup_done: false,
@@ -57,16 +62,11 @@ impl<S: PeripheralStateUnchecked> Peripheral<S> {
57 62
58 this.irq.disable(); 63 this.irq.disable();
59 this.irq.set_handler(|p| { 64 this.irq.set_handler(|p| {
60 // We need to be in a critical section so that no one can preempt us 65 // The state can't have been dropped, otherwise the interrupt would have been disabled.
61 // and drop the state after we check whether `p.is_null()`. 66 // We checked in `new` that the thread owning the `Peripheral` can't preempt the interrupt,
62 critical_section::with(|_| { 67 // so someone can't have preempted us before this point and dropped the `Peripheral`.
63 if p.is_null() { 68 let state = unsafe { &*(p as *const S) };
64 // The state was dropped, so we can't operate on it. 69 state.on_interrupt();
65 return;
66 }
67 let state = unsafe { &*(p as *const S) };
68 state.on_interrupt();
69 });
70 }); 70 });
71 this.irq 71 this.irq
72 .set_handler_context((&this.state) as *const _ as *mut ()); 72 .set_handler_context((&this.state) as *const _ as *mut ());
@@ -78,14 +78,47 @@ impl<S: PeripheralStateUnchecked> Peripheral<S> {
78 pub fn state(self: Pin<&mut Self>) -> &S { 78 pub fn state(self: Pin<&mut Self>) -> &S {
79 &self.into_ref().get_ref().state 79 &self.into_ref().get_ref().state
80 } 80 }
81
82 /// Enables the wrapped interrupt.
83 pub fn enable(&self) {
84 // This is fine to do before initialization, because we haven't set the handler yet.
85 self.irq.enable()
86 }
87
88 /// Disables the wrapped interrupt.
89 pub fn disable(&self) {
90 self.irq.disable()
91 }
92
93 /// Returns whether the wrapped interrupt is enabled.
94 pub fn is_enabled(&self) -> bool {
95 self.irq.is_enabled()
96 }
97
98 /// Returns whether the wrapped interrupt is currently in a pending state.
99 pub fn is_pending(&self) -> bool {
100 self.irq.is_pending()
101 }
102
103 /// Forces the wrapped interrupt into a pending state.
104 pub fn pend(&self) {
105 self.irq.pend()
106 }
107
108 /// Forces the wrapped interrupt out of a pending state.
109 pub fn unpend(&self) {
110 self.irq.unpend()
111 }
112
113 /// Gets the priority of the wrapped interrupt.
114 pub fn priority(&self) -> <S::Interrupt as Interrupt>::Priority {
115 self.irq.get_priority()
116 }
81} 117}
82 118
83impl<S: PeripheralStateUnchecked> Drop for Peripheral<S> { 119impl<S: PeripheralStateUnchecked> Drop for Peripheral<S> {
84 fn drop(&mut self) { 120 fn drop(&mut self) {
85 self.irq.disable(); 121 self.irq.disable();
86 self.irq.remove_handler(); 122 self.irq.remove_handler();
87 // Set the context to null so that the interrupt will know we're dropped
88 // if we pre-empted it before it entered a critical section.
89 self.irq.set_handler_context(ptr::null_mut());
90 } 123 }
91} 124}
diff --git a/embassy-extras/src/usb/usb_serial.rs b/embassy-extras/src/usb/usb_serial.rs
index 9cbfb2da4..a229b2000 100644
--- a/embassy-extras/src/usb/usb_serial.rs
+++ b/embassy-extras/src/usb/usb_serial.rs
@@ -55,7 +55,7 @@ where
55 let this = self.get_mut(); 55 let this = self.get_mut();
56 let mut mutex = this.inner.borrow_mut(); 56 let mut mutex = this.inner.borrow_mut();
57 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; 57 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) };
58 mutex.with(|state, _irq| { 58 mutex.with(|state| {
59 let serial = state.classes.get_serial(); 59 let serial = state.classes.get_serial();
60 let serial = Pin::new(serial); 60 let serial = Pin::new(serial);
61 61
@@ -77,7 +77,7 @@ where
77 let this = self.get_mut(); 77 let this = self.get_mut();
78 let mut mutex = this.inner.borrow_mut(); 78 let mut mutex = this.inner.borrow_mut();
79 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; 79 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) };
80 mutex.with(|state, _irq| { 80 mutex.with(|state| {
81 let serial = state.classes.get_serial(); 81 let serial = state.classes.get_serial();
82 let serial = Pin::new(serial); 82 let serial = Pin::new(serial);
83 83
@@ -101,7 +101,7 @@ where
101 let this = self.get_mut(); 101 let this = self.get_mut();
102 let mut mutex = this.inner.borrow_mut(); 102 let mut mutex = this.inner.borrow_mut();
103 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; 103 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) };
104 mutex.with(|state, _irq| { 104 mutex.with(|state| {
105 let serial = state.classes.get_serial(); 105 let serial = state.classes.get_serial();
106 let serial = Pin::new(serial); 106 let serial = Pin::new(serial);
107 107
diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs
index 1fa98a6b2..2cce122bc 100644
--- a/embassy-nrf/src/buffered_uarte.rs
+++ b/embassy-nrf/src/buffered_uarte.rs
@@ -176,7 +176,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
176 pub fn set_baudrate(self: Pin<&mut Self>, baudrate: Baudrate) { 176 pub fn set_baudrate(self: Pin<&mut Self>, baudrate: Baudrate) {
177 let mut inner = self.inner(); 177 let mut inner = self.inner();
178 inner.as_mut().register_interrupt(); 178 inner.as_mut().register_interrupt();
179 inner.with(|state, _irq| { 179 inner.with(|state| {
180 let r = U::regs(); 180 let r = U::regs();
181 181
182 let timeout = 0x8000_0000 / (baudrate as u32 / 40); 182 let timeout = 0x8000_0000 / (baudrate as u32 / 40);
@@ -196,7 +196,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> AsyncBufRead for BufferedUarte<'d,
196 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> { 196 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
197 let mut inner = self.inner(); 197 let mut inner = self.inner();
198 inner.as_mut().register_interrupt(); 198 inner.as_mut().register_interrupt();
199 inner.with(|state, _irq| { 199 inner.with(|state| {
200 // Conservative compiler fence to prevent optimizations that do not 200 // Conservative compiler fence to prevent optimizations that do not
201 // take in to account actions by DMA. The fence has been placed here, 201 // take in to account actions by DMA. The fence has been placed here,
202 // before any DMA action has started 202 // before any DMA action has started
@@ -221,11 +221,11 @@ impl<'d, U: UarteInstance, T: TimerInstance> AsyncBufRead for BufferedUarte<'d,
221 fn consume(self: Pin<&mut Self>, amt: usize) { 221 fn consume(self: Pin<&mut Self>, amt: usize) {
222 let mut inner = self.inner(); 222 let mut inner = self.inner();
223 inner.as_mut().register_interrupt(); 223 inner.as_mut().register_interrupt();
224 inner.with(|state, irq| { 224 inner.as_mut().with(|state| {
225 trace!("consume {:?}", amt); 225 trace!("consume {:?}", amt);
226 state.rx.pop(amt); 226 state.rx.pop(amt);
227 irq.pend(); 227 });
228 }) 228 inner.pend();
229 } 229 }
230} 230}
231 231
@@ -233,7 +233,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> AsyncWrite for BufferedUarte<'d, U,
233 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> { 233 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
234 let mut inner = self.inner(); 234 let mut inner = self.inner();
235 inner.as_mut().register_interrupt(); 235 inner.as_mut().register_interrupt();
236 inner.with(|state, irq| { 236 let poll = inner.as_mut().with(|state| {
237 trace!("poll_write: {:?}", buf.len()); 237 trace!("poll_write: {:?}", buf.len());
238 238
239 let tx_buf = state.tx.push_buf(); 239 let tx_buf = state.tx.push_buf();
@@ -254,10 +254,12 @@ impl<'d, U: UarteInstance, T: TimerInstance> AsyncWrite for BufferedUarte<'d, U,
254 // before any DMA action has started 254 // before any DMA action has started
255 compiler_fence(Ordering::SeqCst); 255 compiler_fence(Ordering::SeqCst);
256 256
257 irq.pend();
258
259 Poll::Ready(Ok(n)) 257 Poll::Ready(Ok(n))
260 }) 258 });
259
260 inner.pend();
261
262 poll
261 } 263 }
262} 264}
263 265
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs
index 567088e8d..3ac09f944 100644
--- a/embassy-stm32/src/eth/v2/mod.rs
+++ b/embassy-stm32/src/eth/v2/mod.rs
@@ -161,7 +161,7 @@ impl<'d, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, P, TX, RX> {
161 let mut mutex = unsafe { Pin::new_unchecked(&mut this.state) }; 161 let mut mutex = unsafe { Pin::new_unchecked(&mut this.state) };
162 mutex.as_mut().register_interrupt(); 162 mutex.as_mut().register_interrupt();
163 163
164 mutex.with(|s, _| { 164 mutex.with(|s| {
165 s.desc_ring.init(); 165 s.desc_ring.init();
166 166
167 fence(Ordering::SeqCst); 167 fence(Ordering::SeqCst);
@@ -237,7 +237,7 @@ impl<'d, P: PHY, const TX: usize, const RX: usize> Device for Pin<&mut Ethernet<
237 let this = unsafe { self.as_mut().get_unchecked_mut() }; 237 let this = unsafe { self.as_mut().get_unchecked_mut() };
238 let mutex = unsafe { Pin::new_unchecked(&mut this.state) }; 238 let mutex = unsafe { Pin::new_unchecked(&mut this.state) };
239 239
240 mutex.with(|s, _| s.desc_ring.tx.available()) 240 mutex.with(|s| s.desc_ring.tx.available())
241 } 241 }
242 242
243 fn transmit(&mut self, pkt: PacketBuf) { 243 fn transmit(&mut self, pkt: PacketBuf) {
@@ -245,7 +245,7 @@ impl<'d, P: PHY, const TX: usize, const RX: usize> Device for Pin<&mut Ethernet<
245 let this = unsafe { self.as_mut().get_unchecked_mut() }; 245 let this = unsafe { self.as_mut().get_unchecked_mut() };
246 let mutex = unsafe { Pin::new_unchecked(&mut this.state) }; 246 let mutex = unsafe { Pin::new_unchecked(&mut this.state) };
247 247
248 mutex.with(|s, _| unwrap!(s.desc_ring.tx.transmit(pkt))); 248 mutex.with(|s| unwrap!(s.desc_ring.tx.transmit(pkt)));
249 } 249 }
250 250
251 fn receive(&mut self) -> Option<PacketBuf> { 251 fn receive(&mut self) -> Option<PacketBuf> {
@@ -253,7 +253,7 @@ impl<'d, P: PHY, const TX: usize, const RX: usize> Device for Pin<&mut Ethernet<
253 let this = unsafe { self.as_mut().get_unchecked_mut() }; 253 let this = unsafe { self.as_mut().get_unchecked_mut() };
254 let mutex = unsafe { Pin::new_unchecked(&mut this.state) }; 254 let mutex = unsafe { Pin::new_unchecked(&mut this.state) };
255 255
256 mutex.with(|s, _| s.desc_ring.rx.pop_packet()) 256 mutex.with(|s| s.desc_ring.rx.pop_packet())
257 } 257 }
258 258
259 fn register_waker(&mut self, waker: &Waker) { 259 fn register_waker(&mut self, waker: &Waker) {