Made the alarmX take exclusive ownership of Timer on functions that could cause UB when run in parallel

This commit is contained in:
Victor Koenders 2021-11-20 08:46:47 +01:00
parent 97a5bd355c
commit 1fd04d3384
No known key found for this signature in database
GPG key ID: 2E441540865B8A1C

View file

@ -3,7 +3,7 @@
use embedded_time::duration::Microseconds; use embedded_time::duration::Microseconds;
use crate::pac::{timer, RESETS, TIMER}; use crate::pac::{RESETS, TIMER};
use crate::resets::SubsystemReset; use crate::resets::SubsystemReset;
use core::marker::PhantomData; use core::marker::PhantomData;
@ -190,24 +190,17 @@ macro_rules! impl_alarm {
pub struct $name(PhantomData<()>); pub struct $name(PhantomData<()>);
impl $name { impl $name {
#[inline]
fn timer() -> &'static timer::RegisterBlock {
// # Safety
//
// This alarm should only have 1 instance available. This alarm
// only changes fields that are relevant to this alarm, and does
// not modify any other fields. Therefor it's safe to get a reference
// to this pointer.
unsafe { &*TIMER::ptr() }
}
/// Clear the interrupt flag. This should be called after interrupt ` /// Clear the interrupt flag. This should be called after interrupt `
#[doc = $int_name] #[doc = $int_name]
/// ` is called. /// ` is called.
/// ///
/// The interrupt is unable to trigger a 2nd time until this interrupt is cleared. /// The interrupt is unable to trigger a 2nd time until this interrupt is cleared.
pub fn clear_interrupt(&mut self) { pub fn clear_interrupt(&mut self, timer: &mut Timer) {
Self::timer().intr.modify(|_, w| w.$int_alarm().set_bit()); // safety: Because we have a mutable reference on `timer`, we have exclusive access to `TIMER::ptr()`
let _ = timer;
let timer = unsafe { &*TIMER::ptr() };
timer.intr.modify(|_, w| w.$int_alarm().set_bit());
} }
/// Enable this alarm to trigger an interrupt. This alarm will trigger ` /// Enable this alarm to trigger an interrupt. This alarm will trigger `
@ -217,13 +210,21 @@ macro_rules! impl_alarm {
/// After this interrupt is triggered, make sure to clear the interrupt with [clear_interrupt]. /// After this interrupt is triggered, make sure to clear the interrupt with [clear_interrupt].
/// ///
/// [clear_interrupt]: #method.clear_interrupt /// [clear_interrupt]: #method.clear_interrupt
pub fn enable_interrupt(&mut self) { pub fn enable_interrupt(&mut self, timer: &mut Timer) {
Self::timer().inte.modify(|_, w| w.$int_alarm().set_bit()); // safety: Because we have a mutable reference on `timer`, we have exclusive access to `TIMER::ptr()`
let _ = timer;
let timer = unsafe { &*TIMER::ptr() };
timer.inte.modify(|_, w| w.$int_alarm().set_bit());
} }
/// Disable this alarm, preventing it from triggering an interrupt. /// Disable this alarm, preventing it from triggering an interrupt.
pub fn disable_interrupt(&mut self) { pub fn disable_interrupt(&mut self, timer: &mut Timer) {
Self::timer().inte.modify(|_, w| w.$int_alarm().clear_bit()); // safety: Because we have a mutable reference on `timer`, we have exclusive access to `TIMER::ptr()`
let _ = timer;
let timer = unsafe { &*TIMER::ptr() };
timer.inte.modify(|_, w| w.$int_alarm().clear_bit());
} }
/// Schedule the alarm to be finished after `countdown`. If [enable_interrupt] is called, this will trigger interrupt ` /// Schedule the alarm to be finished after `countdown`. If [enable_interrupt] is called, this will trigger interrupt `
@ -243,8 +244,15 @@ macro_rules! impl_alarm {
if duration < MIN_MICROSECONDS { if duration < MIN_MICROSECONDS {
return Err(ScheduleAlarmError::AlarmTooSoon); return Err(ScheduleAlarmError::AlarmTooSoon);
} else { } else {
let target_time = Self::timer().timelr.read().bits().wrapping_add(duration); // safety: This is a read action and should not have any UB
Self::timer() let target_time = unsafe { &*TIMER::ptr() }
.timelr
.read()
.bits()
.wrapping_add(duration);
// safety: This is the only code in the codebase that accesses memory address $timer_alarm
unsafe { &*TIMER::ptr() }
.$timer_alarm .$timer_alarm
.write(|w| unsafe { w.bits(target_time) }); .write(|w| unsafe { w.bits(target_time) });
Ok(()) Ok(())
@ -253,8 +261,9 @@ macro_rules! impl_alarm {
/// Return true if this alarm is finished. /// Return true if this alarm is finished.
pub fn finished(&self) -> bool { pub fn finished(&self) -> bool {
let bits: u32 = Self::timer().armed.read().bits(); // safety: This is a read action and should not have any UB
(bits & $armed_bit_mask) > 0 let bits: u32 = unsafe { &*TIMER::ptr() }.armed.read().bits();
(bits & $armed_bit_mask) == 0
} }
} }
}; };