aboutsummaryrefslogtreecommitdiff
path: root/embassy-extras/src/peripheral_shared.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2021-07-29 13:08:30 +0200
committerGitHub <[email protected]>2021-07-29 13:08:30 +0200
commitc8a48d726a7cc92ef989a519fdf55ec1f9fffbcd (patch)
treec4d55f123620b68ed41cd517b437f61210fe6cfb /embassy-extras/src/peripheral_shared.rs
parent61340d8c654abfb1e809042f9deceeaec0424d05 (diff)
parentcd1a3fcff34943117f446e1afeb9e6d531ee577b (diff)
Merge pull request #277 from Liamolucko/fix-peripheral-ub
extras: Fix UB in `Peripheral`
Diffstat (limited to 'embassy-extras/src/peripheral_shared.rs')
-rw-r--r--embassy-extras/src/peripheral_shared.rs75
1 files changed, 67 insertions, 8 deletions
diff --git a/embassy-extras/src/peripheral_shared.rs b/embassy-extras/src/peripheral_shared.rs
index c62113396..71d746341 100644
--- a/embassy-extras/src/peripheral_shared.rs
+++ b/embassy-extras/src/peripheral_shared.rs
@@ -1,16 +1,24 @@
1use core::cell::UnsafeCell;
2use core::marker::{PhantomData, PhantomPinned}; 1use core::marker::{PhantomData, PhantomPinned};
3use core::pin::Pin; 2use core::pin::Pin;
4 3
5use embassy::interrupt::{Interrupt, InterruptExt}; 4use embassy::interrupt::{Interrupt, InterruptExt};
6 5
7pub trait PeripheralState { 6use crate::peripheral::can_be_preempted;
7
8/// A type which can be used as state with `Peripheral`.
9///
10/// It needs to be `Sync` because references are shared between the 'thread' which owns the `Peripheral` and the interrupt.
11///
12/// It also requires `'static` to be used safely with `Peripheral::register_interrupt`,
13/// because although `Pin` guarantees that the memory of the state won't be invalidated,
14/// it doesn't guarantee that the lifetime will last.
15pub trait PeripheralState: Sync {
8 type Interrupt: Interrupt; 16 type Interrupt: Interrupt;
9 fn on_interrupt(&self); 17 fn on_interrupt(&self);
10} 18}
11 19
12pub struct Peripheral<S: PeripheralState> { 20pub struct Peripheral<S: PeripheralState> {
13 state: UnsafeCell<S>, 21 state: S,
14 22
15 irq_setup_done: bool, 23 irq_setup_done: bool,
16 irq: S::Interrupt, 24 irq: S::Interrupt,
@@ -19,26 +27,58 @@ pub struct Peripheral<S: PeripheralState> {
19 _pinned: PhantomPinned, 27 _pinned: PhantomPinned,
20} 28}
21 29
30impl<S: PeripheralState + 'static> Peripheral<S> {
31 /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it.
32 ///
33 /// This requires this `Peripheral`'s `PeripheralState` to live for `'static`,
34 /// because `Pin` only guarantees that it's memory won't be repurposed,
35 /// not that it's lifetime will last.
36 ///
37 /// To use non-`'static` `PeripheralState`, use the unsafe `register_interrupt_unchecked`.
38 ///
39 /// Note: `'static` doesn't mean it _has_ to live for the entire program, like an `&'static T`;
40 /// it just means it _can_ live for the entire program - for example, `u8` lives for `'static`.
41 pub fn register_interrupt(self: Pin<&mut Self>) {
42 // SAFETY: `S: 'static`, so there's no way it's lifetime can expire.
43 unsafe { self.register_interrupt_unchecked() }
44 }
45}
46
22impl<S: PeripheralState> Peripheral<S> { 47impl<S: PeripheralState> Peripheral<S> {
23 pub fn new(irq: S::Interrupt, state: S) -> Self { 48 pub fn new(irq: S::Interrupt, state: S) -> Self {
49 if can_be_preempted(&irq) {
50 panic!("`Peripheral` cannot be created in an interrupt with higher priority than the interrupt it wraps");
51 }
52
24 Self { 53 Self {
25 irq, 54 irq,
26 irq_setup_done: false, 55 irq_setup_done: false,
27 56
28 state: UnsafeCell::new(state), 57 state,
29 _not_send: PhantomData, 58 _not_send: PhantomData,
30 _pinned: PhantomPinned, 59 _pinned: PhantomPinned,
31 } 60 }
32 } 61 }
33 62
34 pub fn register_interrupt(self: Pin<&mut Self>) { 63 /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it.
35 let this = unsafe { self.get_unchecked_mut() }; 64 ///
65 /// # Safety
66 /// The lifetime of any data in `PeripheralState` that is accessed by the interrupt handler
67 /// must not end without `Drop` being called on this `Peripheral`.
68 ///
69 /// This can be accomplished by either not accessing any data with a lifetime in `on_interrupt`,
70 /// or making sure that nothing like `mem::forget` is used on the `Peripheral`.
71 pub unsafe fn register_interrupt_unchecked(self: Pin<&mut Self>) {
72 let this = self.get_unchecked_mut();
36 if this.irq_setup_done { 73 if this.irq_setup_done {
37 return; 74 return;
38 } 75 }
39 76
40 this.irq.disable(); 77 this.irq.disable();
41 this.irq.set_handler(|p| { 78 this.irq.set_handler(|p| {
79 // The state can't have been dropped, otherwise the interrupt would have been disabled.
80 // We checked in `new` that the thread owning the `Peripheral` can't preempt the interrupt,
81 // so someone can't have preempted us before this point and dropped the `Peripheral`.
42 let state = unsafe { &*(p as *const S) }; 82 let state = unsafe { &*(p as *const S) };
43 state.on_interrupt(); 83 state.on_interrupt();
44 }); 84 });
@@ -50,8 +90,27 @@ impl<S: PeripheralState> Peripheral<S> {
50 } 90 }
51 91
52 pub fn state(self: Pin<&mut Self>) -> &S { 92 pub fn state(self: Pin<&mut Self>) -> &S {
53 let this = unsafe { self.get_unchecked_mut() }; 93 &self.into_ref().get_ref().state
54 unsafe { &*this.state.get() } 94 }
95
96 /// Returns whether the wrapped interrupt is currently in a pending state.
97 pub fn is_pending(&self) -> bool {
98 self.irq.is_pending()
99 }
100
101 /// Forces the wrapped interrupt into a pending state.
102 pub fn pend(&self) {
103 self.irq.pend()
104 }
105
106 /// Forces the wrapped interrupt out of a pending state.
107 pub fn unpend(&self) {
108 self.irq.unpend()
109 }
110
111 /// Gets the priority of the wrapped interrupt.
112 pub fn priority(&self) -> <S::Interrupt as Interrupt>::Priority {
113 self.irq.get_priority()
55 } 114 }
56} 115}
57 116