Skip to content

Commit 11f90b9

Browse files
authored
Protect SYSTIMER/TIMG shared registers (#2051)
* Protect SYSTIMER/TIMG shared registers * some review comments * more review comments * more review comments * bring back portable_atomic --------- Co-authored-by: Dominic Fischer <[email protected]>
1 parent 6b4079b commit 11f90b9

File tree

3 files changed

+141
-28
lines changed

3 files changed

+141
-28
lines changed

esp-hal/src/lib.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,94 @@ mod critical_section_impl {
572572
}
573573
}
574574

575+
// The state of a re-entrant lock
576+
pub(crate) struct LockState {
577+
#[cfg(multi_core)]
578+
core: portable_atomic::AtomicUsize,
579+
}
580+
581+
impl LockState {
582+
#[cfg(multi_core)]
583+
const UNLOCKED: usize = usize::MAX;
584+
585+
pub const fn new() -> Self {
586+
Self {
587+
#[cfg(multi_core)]
588+
core: portable_atomic::AtomicUsize::new(Self::UNLOCKED),
589+
}
590+
}
591+
}
592+
593+
// This is preferred over critical-section as this allows you to have multiple
594+
// locks active at the same time rather than using the global mutex that is
595+
// critical-section.
596+
#[allow(unused_variables)]
597+
pub(crate) fn lock<T>(state: &LockState, f: impl FnOnce() -> T) -> T {
598+
// In regards to disabling interrupts, we only need to disable
599+
// the interrupts that may be calling this function.
600+
601+
#[cfg(not(multi_core))]
602+
{
603+
// Disabling interrupts is enough on single core chips to ensure mutual
604+
// exclusion.
605+
606+
#[cfg(riscv)]
607+
return riscv::interrupt::free(f);
608+
#[cfg(xtensa)]
609+
return xtensa_lx::interrupt::free(|_| f());
610+
}
611+
612+
#[cfg(multi_core)]
613+
{
614+
use portable_atomic::Ordering;
615+
616+
let current_core = get_core() as usize;
617+
618+
let mut f = f;
619+
620+
loop {
621+
let func = || {
622+
// Use Acquire ordering in success to ensure `f()` "happens after" the lock is
623+
// taken. Use Relaxed ordering in failure as there's no
624+
// synchronisation happening.
625+
if let Err(locked_core) = state.core.compare_exchange(
626+
LockState::UNLOCKED,
627+
current_core,
628+
Ordering::Acquire,
629+
Ordering::Relaxed,
630+
) {
631+
assert_ne!(
632+
locked_core, current_core,
633+
"esp_hal::lock is not re-entrant!"
634+
);
635+
636+
Err(f)
637+
} else {
638+
let result = f();
639+
640+
// Use Release ordering here to ensure `f()` "happens before" this lock is
641+
// released.
642+
state.core.store(LockState::UNLOCKED, Ordering::Release);
643+
644+
Ok(result)
645+
}
646+
};
647+
648+
#[cfg(riscv)]
649+
let result = riscv::interrupt::free(func);
650+
#[cfg(xtensa)]
651+
let result = xtensa_lx::interrupt::free(|_| func());
652+
653+
match result {
654+
Ok(result) => break result,
655+
Err(the_function) => f = the_function,
656+
}
657+
658+
// Consider using core::hint::spin_loop(); Might need SW_INT.
659+
}
660+
}
661+
}
662+
575663
/// Default (unhandled) interrupt handler
576664
pub const DEFAULT_INTERRUPT_HANDLER: interrupt::InterruptHandler = interrupt::InterruptHandler::new(
577665
unsafe { core::mem::transmute::<*const (), extern "C" fn()>(EspDefaultHandler as *const ()) },

esp-hal/src/timer/systimer.rs

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@ use fugit::{Instant, MicrosDurationU32, MicrosDurationU64};
8080
use super::{Error, Timer as _};
8181
use crate::{
8282
interrupt::{self, InterruptHandler},
83+
lock,
8384
peripheral::Peripheral,
8485
peripherals::{Interrupt, SYSTIMER},
8586
system::{Peripheral as PeripheralEnable, PeripheralClockControl},
8687
Async,
8788
Blocking,
8889
Cpu,
8990
InterruptConfigurable,
91+
LockState,
9092
Mode,
9193
};
9294

@@ -240,7 +242,7 @@ pub trait Unit {
240242
let systimer = unsafe { &*SYSTIMER::ptr() };
241243
let conf = systimer.conf();
242244

243-
critical_section::with(|_| {
245+
lock(&CONF_LOCK, || {
244246
conf.modify(|_, w| match config {
245247
UnitConfig::Disabled => match self.channel() {
246248
0 => w.timer_unit0_work_en().clear_bit(),
@@ -418,20 +420,22 @@ pub trait Comparator {
418420
fn set_enable(&self, enable: bool) {
419421
let systimer = unsafe { &*SYSTIMER::ptr() };
420422

421-
critical_section::with(|_| {
423+
lock(&CONF_LOCK, || {
422424
#[cfg(not(esp32s2))]
423425
systimer.conf().modify(|_, w| match self.channel() {
424426
0 => w.target0_work_en().bit(enable),
425427
1 => w.target1_work_en().bit(enable),
426428
2 => w.target2_work_en().bit(enable),
427429
_ => unreachable!(),
428430
});
429-
430-
#[cfg(esp32s2)]
431-
systimer
432-
.target_conf(self.channel() as usize)
433-
.modify(|_r, w| w.work_en().bit(enable));
434431
});
432+
433+
// Note: The ESP32-S2 doesn't require a lock because each
434+
// comparator's enable bit in a different register.
435+
#[cfg(esp32s2)]
436+
systimer
437+
.target_conf(self.channel() as usize)
438+
.modify(|_r, w| w.work_en().bit(enable));
435439
}
436440

437441
/// Returns true if the comparator has been enabled. This means
@@ -964,9 +968,11 @@ where
964968
}
965969

966970
fn enable_interrupt(&self, state: bool) {
967-
unsafe { &*SYSTIMER::PTR }
968-
.int_ena()
969-
.modify(|_, w| w.target(self.comparator.channel()).bit(state));
971+
lock(&INT_ENA_LOCK, || {
972+
unsafe { &*SYSTIMER::PTR }
973+
.int_ena()
974+
.modify(|_, w| w.target(self.comparator.channel()).bit(state));
975+
});
970976
}
971977

972978
fn clear_interrupt(&self) {
@@ -1004,6 +1010,9 @@ where
10041010
}
10051011
}
10061012

1013+
static CONF_LOCK: LockState = LockState::new();
1014+
static INT_ENA_LOCK: LockState = LockState::new();
1015+
10071016
// Async functionality of the system timer.
10081017
#[cfg(feature = "async")]
10091018
mod asynch {
@@ -1090,27 +1099,33 @@ mod asynch {
10901099

10911100
#[handler]
10921101
fn target0_handler() {
1093-
unsafe { &*crate::peripherals::SYSTIMER::PTR }
1094-
.int_ena()
1095-
.modify(|_, w| w.target0().clear_bit());
1102+
lock(&INT_ENA_LOCK, || {
1103+
unsafe { &*crate::peripherals::SYSTIMER::PTR }
1104+
.int_ena()
1105+
.modify(|_, w| w.target0().clear_bit());
1106+
});
10961107

10971108
WAKERS[0].wake();
10981109
}
10991110

11001111
#[handler]
11011112
fn target1_handler() {
1102-
unsafe { &*crate::peripherals::SYSTIMER::PTR }
1103-
.int_ena()
1104-
.modify(|_, w| w.target1().clear_bit());
1113+
lock(&INT_ENA_LOCK, || {
1114+
unsafe { &*crate::peripherals::SYSTIMER::PTR }
1115+
.int_ena()
1116+
.modify(|_, w| w.target1().clear_bit());
1117+
});
11051118

11061119
WAKERS[1].wake();
11071120
}
11081121

11091122
#[handler]
11101123
fn target2_handler() {
1111-
unsafe { &*crate::peripherals::SYSTIMER::PTR }
1112-
.int_ena()
1113-
.modify(|_, w| w.target2().clear_bit());
1124+
lock(&INT_ENA_LOCK, || {
1125+
unsafe { &*crate::peripherals::SYSTIMER::PTR }
1126+
.int_ena()
1127+
.modify(|_, w| w.target2().clear_bit());
1128+
});
11141129

11151130
WAKERS[2].wake();
11161131
}

esp-hal/src/timer/timg.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,20 @@ use crate::soc::constants::TIMG_DEFAULT_CLK_SRC;
7777
use crate::{
7878
clock::Clocks,
7979
interrupt::{self, InterruptHandler},
80+
lock,
8081
peripheral::{Peripheral, PeripheralRef},
8182
peripherals::{timg0::RegisterBlock, Interrupt, TIMG0},
8283
private::Sealed,
8384
system::{Peripheral as PeripheralEnable, PeripheralClockControl},
8485
Async,
8586
Blocking,
8687
InterruptConfigurable,
88+
LockState,
8789
Mode,
8890
};
8991

92+
static INT_ENA_LOCK: LockState = LockState::new();
93+
9094
/// A timer group consisting of up to 2 timers (chip dependent) and a watchdog
9195
/// timer.
9296
pub struct TimerGroup<'d, T, DM>
@@ -481,9 +485,11 @@ where
481485
.config()
482486
.modify(|_, w| w.level_int_en().set_bit());
483487

484-
self.register_block()
485-
.int_ena()
486-
.modify(|_, w| w.t(self.timer_number()).bit(state));
488+
lock(&INT_ENA_LOCK, || {
489+
self.register_block()
490+
.int_ena()
491+
.modify(|_, w| w.t(self.timer_number()).bit(state));
492+
});
487493
}
488494

489495
fn clear_interrupt(&self) {
@@ -706,15 +712,19 @@ where
706712
.config()
707713
.modify(|_, w| w.level_int_en().set_bit());
708714

709-
self.register_block()
710-
.int_ena()
711-
.modify(|_, w| w.t(T).set_bit());
715+
lock(&INT_ENA_LOCK, || {
716+
self.register_block()
717+
.int_ena()
718+
.modify(|_, w| w.t(T).set_bit());
719+
});
712720
}
713721

714722
fn unlisten(&self) {
715-
self.register_block()
716-
.int_ena()
717-
.modify(|_, w| w.t(T).clear_bit());
723+
lock(&INT_ENA_LOCK, || {
724+
self.register_block()
725+
.int_ena()
726+
.modify(|_, w| w.t(T).clear_bit());
727+
});
718728
}
719729

720730
fn clear_interrupt(&self) {

0 commit comments

Comments
 (0)