diff options
author | Patryk Duda <pdk@semihalf.com> | 2021-06-09 15:35:29 +0200 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-09-06 09:43:45 +0000 |
commit | 51625cbd2ccc7c7f976d1f2e6b64e2f1f75bc1ff (patch) | |
tree | 2ddeeaa6b7581581afcacefd962df94b54dbf690 /core | |
parent | b7b266b58035b4a24d6e5ec93ee775e6229e7929 (diff) | |
download | chrome-ec-51625cbd2ccc7c7f976d1f2e6b64e2f1f75bc1ff.tar.gz |
cortex-m/task: Check if interrupts are enabled before switching task
Switching task with disabled interrupts leads to Forced Hard Fault on
Cortex-M3/M4/M7 because:
- SVCall exception have configurable priority (full list can be found
at 2.4.2 Exception types PM0253 Rev 5 p.40)
- We are using 'cpsid i' to disable interrupts. This instruction sets
PRIMASK bit (3.12.2 CPS PM0253 Rev 5 p.176)
- When PRIMASK bit is set, all exceptions with configurable priority
are disabled (PM0253 Rev 5 p.25), so SVCall is masked too
- SVCall is escalated to Forced Hard Fault because "A fault occurs
and the handler for that fault is not enabled" (PM0253 Rev 5 p.48)
If Hard Fault is inevitable, it will be a good idea to catch this
earlier. It will save time spent debugging why Forced Hard Fault
happens. In functions responsible for enabling, disabling or making
task ready we postpone task switch when interrupts are disabled
BUG=b:190597666
BRANCH=none
TEST=Compile and flash EC on boards with Cortex-M3/M4/M7 and make
sure that it works properly.
Signed-off-by: Patryk Duda <pdk@semihalf.com>
Change-Id: I50976154b0cf0307c5334f6f03e4b3bc137a4ffc
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2953228
Commit-Queue: Marcin Wojtas <mwojtas@google.com>
Tested-by: Patryk Duda <patrykd@google.com>
Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
Diffstat (limited to 'core')
-rw-r--r-- | core/cortex-m/task.c | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/core/cortex-m/task.c b/core/cortex-m/task.c index e64063dc15..bf0eb5b397 100644 --- a/core/cortex-m/task.c +++ b/core/cortex-m/task.c @@ -415,6 +415,15 @@ static uint32_t __wait_evt(int timeout_us, task_id_t resched) uint32_t evt; int ret __attribute__((unused)); + /* + * Scheduling task when interrupts are disabled will result in Forced + * Hard Fault because: + * - Disabling interrupt using 'cpsid i' also disables SVCall handler + * (because it has configurable priority) + * - Escalation to Hard Fault (also known as 'priority escalation') + * occurs when handler for that fault is not enabled + */ + ASSERT(is_interrupt_enabled()); ASSERT(!in_interrupt_context()); if (timeout_us > 0) { @@ -445,7 +454,7 @@ uint32_t task_set_event(task_id_t tskid, uint32_t event) atomic_or(&receiver->events, event); /* Re-schedule if priorities have changed */ - if (in_interrupt_context()) { + if (in_interrupt_context() || !is_interrupt_enabled()) { /* The receiver might run again */ atomic_or(&tasks_ready, 1 << tskid); #ifndef CONFIG_TASK_PROFILING @@ -497,7 +506,8 @@ void task_enable_all_tasks(void) /* Mark all tasks as ready and able to run. */ tasks_ready = tasks_enabled = BIT(TASK_ID_COUNT) - 1; /* Reschedule the highest priority task. */ - __schedule(0, 0); + if (is_interrupt_enabled()) + __schedule(0, 0); } void task_enable_task(task_id_t tskid) @@ -509,7 +519,8 @@ void task_disable_task(task_id_t tskid) { atomic_clear_bits(&tasks_enabled, BIT(tskid)); - if (!in_interrupt_context() && tskid == task_get_current()) + if (!in_interrupt_context() && is_interrupt_enabled() && + tskid == task_get_current()) __schedule(0, 0); } |