SuperTinyKernel™ RTOS 1.08.x
Lightweight, high-performance, deterministic, bare-metal C++ RTOS for resource-constrained embedded systems. MIT Open Source License.
Loading...
Searching...
No Matches
stk_arch_arm-cortex-m.cpp
Go to the documentation of this file.
1/*
2 * SuperTinyKernel(TM) RTOS: Lightweight High-Performance Deterministic C++ RTOS for Embedded Systems.
3 *
4 * Source: https://github.com/SuperTinyKernel-RTOS
5 *
6 * Copyright (c) 2022-2026 Neutron Code Limited <stk@neutroncode.com>. All Rights Reserved.
7 * License: MIT License, see LICENSE for a full text.
8 */
9
10// note: If missing, this header must be customized (get it in the root of the source folder) and
11// copied to the /include folder manually.
12#include "stk_config.h"
13
14#ifdef _STK_ARCH_ARM_CORTEX_M
15
16#ifdef _STK_CORTEX_M_TRUSTZONE
17#include <arm_cmse.h>
18#endif
19
20#include "stk.h"
21#include "stk_arch.h"
24
25using namespace stk;
26
27// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
28#if !defined(_STK_CORTEX_M_TRUSTZONE_NON_SECURE)
29// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
30
32#ifndef __CORTEX_M
33#error Expecting __CORTEX_M with value corresponding to Cortex-M model (0, 3, 4, 7, 33, ...)!
34#endif
35
37#if !defined(SysTick)
38 #error "SysTick peripheral definition is missing!"
39#endif
40
42#if (__CORTEX_M > 1U) && STK_TICKLESS_USE_ARM_DWT && !defined(DWT)
43 #error "DWT peripheral definition is missing!"
44#endif
45
47#define STK_CORTEX_M_FPU ((__FPU_PRESENT == 1U) && (__FPU_USED == 1U))
48
50#ifdef __MPU_PRESENT
51 #define STK_CORTEX_M_MPU (__MPU_PRESENT == 1U)
52#else
53 #define STK_CORTEX_M_MPU (0)
54#endif
55#if !STK_CORTEX_M_MPU
56 #if STK_MPU
57 #error "MPU is not present on this platform, STK_MPU feature is not supported!"
58 #endif
59 #if STK_MPU_STACK_GUARD
60 #error "MPU is not present on this platform, STK_MPU_STACK_GUARD feature is not supported!"
61 #endif
62#endif
63#if STK_MPU_STACK_GUARD && !STK_MPU
64 #error "Enable MPU support (STK_MPU=1) to use per-task MPU feature (STK_MPU_STACK_GUARD=1)!"
65#endif
66
79#ifndef STK_CORTEX_M_MPU_TASK_REGION_IDX
80 #define STK_CORTEX_M_MPU_TASK_REGION_IDX (STK_CORTEX_M_MPU_REGIONS_MAX - STK_MPU_TASK_REGIONS)
81#endif
82
95#ifndef STK_CORTEX_M_MPU_TASK_REGION_IDX_NS
96 #define STK_CORTEX_M_MPU_TASK_REGION_IDX_NS (STK_CORTEX_M_MPU_REGIONS_MAX - STK_MPU_TASK_REGIONS_NS)
97#endif
98
100#define STK_CORTEX_M_MANAGE_LR (__CORTEX_M >= 3U)
101
103#define STK_CORTEX_M_PAC (__ARM_FEATURE_PA_BITS && (__CORTEX_M >= 85U))
104
106#define STK_CORTEX_M_EXC_RETURN_THREAD_PSP (0xFFFFFFFDU) // Thread mode, PSP, basic
107
108// ISR priorities:
109#define STK_CORTEX_M_ISR_PRIORITY_HIGHEST (0U)
110#define STK_CORTEX_M_ISR_PRIORITY_LOWEST (0xFFU)
111
113#if STK_CORTEX_M_MANAGE_LR
114 #define STK_CORTEX_M_REGISTER_COUNT (17U)
115#else
116 #define STK_CORTEX_M_REGISTER_COUNT (16U)
117#endif
118
128#ifdef _STK_CORTEX_M_TRUSTZONE
129 #define STK_CORTEX_M_TZ_REGISTER_COUNT (4U)
130#else
131 #define STK_CORTEX_M_TZ_REGISTER_COUNT (0U)
132#endif
133
142#ifdef CONTROL_nPRIV_Msk
143 #define STK_CORTEX_M_PRIVILEGE_FRAME (1)
144#else
145 #define STK_CORTEX_M_PRIVILEGE_FRAME (0)
146#endif
147
149#if STK_CORTEX_M_PRIVILEGE_FRAME
150 #define STK_CORTEX_M_PRIV_REGISTER_COUNT (1U)
151#else
152 #define STK_CORTEX_M_PRIV_REGISTER_COUNT (0U)
153#endif
154
156#define STK_CORTEX_M_TOTAL_REGISTER_COUNT \
157 (STK_CORTEX_M_REGISTER_COUNT + STK_CORTEX_M_TZ_REGISTER_COUNT + STK_CORTEX_M_PRIV_REGISTER_COUNT)
158
160#ifndef STK_SYSTICK_HANDLER
161 #define STK_SYSTICK_HANDLER SysTick_Handler
162#endif
163
165#ifndef STK_PENDSV_HANDLER
166 #define STK_PENDSV_HANDLER PendSV_Handler
167#endif
168
170#ifndef STK_SVC_HANDLER
171 #define STK_SVC_HANDLER SVC_Handler
172#endif
173
175#ifndef STK_USE_MEMMANAGE_HANDLER
176#define STK_USE_MEMMANAGE_HANDLER (0)
177#endif
178#if STK_USE_MEMMANAGE_HANDLER
179 #ifndef STK_MEMMANAGE_HANDLER
180 #define STK_MEMMANAGE_HANDLER MemManage_Handler
181 #endif
182#endif
183
185#ifndef STK_USE_HARDFAULT_HANDLER
186#define STK_USE_HARDFAULT_HANDLER (0)
187#endif
188#if STK_USE_HARDFAULT_HANDLER
189 #ifndef STK_HARDFAULT_HANDLER
190 #define STK_HARDFAULT_HANDLER HardFault_Handler
191 #endif
192#endif
193
201#ifndef STK_SPINLOCK_TIMEOUT_US
202 #define STK_SPINLOCK_TIMEOUT_US (5LL * 1000LL * 1000LL) // 5 sec
203#endif
204
214#ifndef STK_SPINLOCK_MIN_CYCLES_PER_ITER
215 #define STK_SPINLOCK_MIN_CYCLES_PER_ITER (4U)
216#endif
217
223#ifndef STK_CORTEX_M_FORCE_SWITCH
224 #define STK_CORTEX_M_FORCE_SWITCH (1)
225#endif
226
233enum ESvcCommandId : uint8_t
234{
235 SVC_START_SCHEDULING = 0U,
236 SVC_ENTER_CRITICAL,
237 SVC_EXIT_CRITICAL
238#if STK_MPU
239 , SVC_BOOST_PRIV
240#endif
241#if STK_CORTEX_M_FORCE_SWITCH
242 , SVC_FORCE_SWITCH
243#endif
244};
245
246// ----------------------------------------------------------------------------
248namespace stk {
249namespace hw {
250namespace reg {
251// ----------------------------------------------------------------------------
252
254namespace PC {
255
257enum ERegMask : Word
258{
259 MASK_THUMB_BIT = (1U << 0U)
260};
261
263static inline Word ClearThumbBit(Word REG_PC) noexcept
264{
265 return (REG_PC & ~static_cast<Word>(ERegMask::MASK_THUMB_BIT));
266}
267
268} // namespace PC
269
271namespace XPSR {
272
274constexpr Word DEFAULT_INIT = 0U;
275
277enum ERegMask : Word
278{
279 MASK_T_BIT = (1U << 24U)
280};
281
283static inline Word SetThumbExecution(Word REG_XPSR) noexcept
284{
285 return (REG_XPSR | static_cast<Word>(ERegMask::MASK_T_BIT));
286}
287
288} // namespace XPSR
289
291namespace CONTROL {
292
294constexpr Word DEFAULT_INIT = 0U;
295
297enum ERegMask : Word
298{
299 MASK_nPRIV = (1U << 0),
300 MASK_SPSEL = (1U << 1),
301 MASK_FPCA = (1U << 2),
302 MASK_SFPA = (1U << 3)
303};
304
306__stk_attr_unused static inline Word SetUnprivileged(Word REG_CONTROL) noexcept
307{
308 return (REG_CONTROL | static_cast<Word>(ERegMask::MASK_nPRIV));
309}
310
312__stk_attr_unused static inline Word SetPrivileged(Word REG_CONTROL) noexcept
313{
314 return (REG_CONTROL & ~static_cast<Word>(ERegMask::MASK_nPRIV));
315}
316
318__stk_attr_unused static inline Word SetSPSelectionToPSP(Word REG_CONTROL) noexcept
319{
320 return (REG_CONTROL | static_cast<Word>(ERegMask::MASK_SPSEL));
321}
322
323} // namespace CONTROL
324
325// ----------------------------------------------------------------------------
326} // namespace reg
327} // namespace hw
328} // namespace stk
329// ----------------------------------------------------------------------------
330
331// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
332#if STK_MPU
333// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
334
335// ----------------------------------------------------------------------------
336namespace stk {
337namespace hw {
338namespace mpu {
339// ----------------------------------------------------------------------------
340
341// Helpers to extract effective physical bounds [start, end_inclusive]
342struct RegionBounds
343{
344 Word start;
345 Word end;
346 bool valid;
347};
348
349static inline RegionBounds GetRegionBounds(const MpuRegionConfig &cfg)
350{
351 RegionBounds result = { 0U, 0U, false };
352
353 if ((cfg.size != 0U) && (cfg.access_perm != EMpuAccess::ACCESS_NONE))
354 {
355 #if STK_ARCH_ARMV8_M
356 // PMSAv8: 32-byte aligned base and size
357 result.start = cfg.addr & ~31U;
358 result.end = result.start + cfg.size - 1U;
359 #else
360 // PMSAv7: naturally aligned power-of-two size
361 result.start = cfg.addr & ~(cfg.size - 1U);
362 result.end = result.start + cfg.size - 1U;
363 #endif
364
365 result.valid = (result.start < result.end);
366 }
367
368 return result;
369}
370
371static inline bool IsOverlapping(const RegionBounds &a, const RegionBounds &b)
372{
373#if STK_ARCH_ARMV8_M
374 // standard closed-interval collision test [start, end]
375 return ((!a.valid || !b.valid) ? false : (a.start <= b.end) && (b.start <= a.end));
376#else
377 // ARMv7-M (PMSAv7) supports regions overlapping, so we always allow overlapping
378 STK_UNUSED(a);
379 STK_UNUSED(b);
380 return false;
381#endif
382}
383
384static void ValidateNoOverlaps(const MpuRegionConfig dynamic_cfg_list[], size_t dynamic_cfg_count,
385 const MpuRegionConfig static_cfg_list[], size_t static_cfg_count)
386{
387 // 1. Validate internal overlaps within dynamic list
388 for (size_t i = 0U; i < dynamic_cfg_count; ++i)
389 {
390 const RegionBounds bounds_i = GetRegionBounds(dynamic_cfg_list[i]);
391 if (!bounds_i.valid)
392 {
393 continue;
394 }
395
396 for (size_t j = i + 1U; j < dynamic_cfg_count; ++j)
397 {
398 const RegionBounds bounds_j = GetRegionBounds(dynamic_cfg_list[j]);
399 if (!bounds_j.valid)
400 {
401 continue;
402 }
403
404 // Assert failure means two active dynamic configurations cross boundaries
405 if (IsOverlapping(bounds_i, bounds_j))
406 {
407 STK_KERNEL_PANIC(KERNEL_PANIC_BAD_MEMORY_REGION);
408 }
409 }
410
411 // 2. Validate dynamic region 'i' against static regions from overrider
412 if (static_cfg_list != nullptr)
413 {
414 for (size_t k = 0U; k < static_cfg_count; ++k)
415 {
416 const RegionBounds static_bounds = GetRegionBounds(static_cfg_list[k]);
417 if (!static_bounds.valid)
418 {
419 continue;
420 }
421
422 // Assert failure means a dynamic region collides with a static region
423 if (IsOverlapping(bounds_i, static_bounds))
424 {
425 STK_KERNEL_PANIC(KERNEL_PANIC_BAD_MEMORY_REGION);
426 }
427 }
428 }
429 }
430}
431
432// ----------------------------------------------------------------------------
433} // namespace mpu
434} // namespace hw
435} // namespace stk
436// ----------------------------------------------------------------------------
437
438// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
439#endif
440// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
441
442#if defined(_STK_CORTEX_M_TRUSTZONE) && (STK_CORTEX_M_TZ_REGISTER_COUNT != 0U)
464struct TrustZoneFrame
465{
466 Word PSP_NS;
467 Word CONTROL_NS;
468 Word PSPLIM;
469 Word PSPLIM_NS;
470};
471#define STK_CORTEX_M_TRUSTZONE_FRAME (1)
472#else
473#define STK_CORTEX_M_TRUSTZONE_FRAME (0)
474#endif
475
476#if STK_CORTEX_M_PRIVILEGE_FRAME
484struct PrivilegeFrame
485{
486 Word CONTROL;
487};
488#endif // STK_CORTEX_M_PRIVILEGE_FRAME
489
501struct TaskFrame
502{
503#if STK_CORTEX_M_MANAGE_LR
504 Word EXC_RETURN;
505#endif
506 hw::ExceptionFrame exc;
507};
508
509// Shortcuts:
510#define STK_ASM_EXIT_FROM_HANDLER "BX LR" // use in naked exception/ISR handlers
511#define STK_ASM_DISABLE_INTERRUPTS "CPSID i"
512#define STK_ASM_ENABLE_INTERRUPTS "CPSIE i"
513
514// Local static functions:
515namespace stk {
516static void OnTaskRun(ITask *runnable);
517static void OnTaskExit();
518static void OnSchedulerSleep();
519static void OnSchedulerSleepOverride();
520static void OnSchedulerExit();
521#if STK_SEGGER_SYSVIEW
522static void SendSysDesc()
523{
524 SEGGER_SYSVIEW_SendSysDesc("SuperTinyKernel RTOS (STK)");
525}
526#endif
527} // namespace stk
528
529// Wrapper to survive LTO optimization.
530#if STK_SEGGER_SYSVIEW
531extern "C" __stk_attr_used void StkSystemView_RecordEnterISR()
532{
533 SEGGER_SYSVIEW_RecordEnterISR();
534}
535#endif
536
537// Local static variables:
539#if STK_CORE_FREQ_UNIFIED
540static uint32_t s_StkSystemCoreClock[1U];
541#else
542static uint32_t s_StkSystemCoreClock[STK_ARCH_CPU_COUNT];
543#endif
544
554static uint32_t s_StkSpinlockTimeoutIters = 0xFFFFFFFU;
555
558static volatile bool s_StkCortexmCsuLock = false;
559
562static volatile EKernelPanicId g_StkLastPanicId = KERNEL_PANIC_NONE;
563
567static __stk_forceinline void HW_StartScheduler()
568{
569 __asm volatile("SVC %0"
570 : /* output: none */
571 : "I"(SVC_START_SCHEDULING)
572 : "memory" /* protect against compiler reordering */
573 );
574}
575
578#if STK_CORTEX_M_FORCE_SWITCH
579static __stk_forceinline void HW_ForceContextSwitch(TId id)
580{
581 __asm volatile(
582 "MOV r0, %0 \n" /* move id parameter into r0 */
583 "SVC %1 \n" /* trigger SVC interrupt */
584 : /* output: none */
585 : "r" (id),
586 "I" (SVC_FORCE_SWITCH) /* 'r' uses any general register, mapped to r0 in assembly */
587 : "r0", "memory" /* r0 is clobbered with id, protect against compiler reordering */
588 );
589}
590#endif
591
601static __stk_forceinline void HW_UnprivEnterCriticalSection()
602{
603 __asm volatile("SVC %0"
604 : /* output: none */
605 : "I"(SVC_ENTER_CRITICAL)
606 : "memory" /* protect against compiler reordering */
607 );
608}
609
619static __stk_forceinline void HW_UnprivExitCriticalSection()
620{
621 __asm volatile("SVC %0"
622 : /* output: none */
623 : "I"(SVC_EXIT_CRITICAL)
624 : "memory" /* protect against compiler reordering */
625 );
626}
627
630static __stk_forceinline void HW_DisableInterrupts()
631{
632#if (defined(__clang__) && defined(__ARMCOMPILER_VERSION)) || defined(__ICCARM__)
633 __asm volatile(STK_ASM_DISABLE_INTERRUPTS ::: "memory");
634#else
635 __disable_irq();
636#endif
637}
638
641static __stk_forceinline void HW_EnableInterrupts()
642{
643#if (defined(__clang__) && defined(__ARMCOMPILER_VERSION)) || defined(__ICCARM__)
644 __asm volatile(STK_ASM_ENABLE_INTERRUPTS ::: "memory");
645#else
646 __enable_irq();
647#endif
648}
649
654static __stk_forceinline bool HW_InterruptsDisabled()
655{
656#if (defined(__clang__) && defined(__ARMCOMPILER_VERSION)) || defined(__ICCARM__)
657 Word primask;
658 __asm volatile("MRS %0, primask" : "=r"(primask));
659 return ((primask & 1U) != 0U);
660#else
661 return ((__get_PRIMASK() & 1U) != 0U);
662#endif
663}
664
668static __stk_forceinline uint32_t HW_CriticalSectionStart()
669{
670 const uint32_t ses = __get_PRIMASK();
671 HW_DisableInterrupts();
672
673 // ensure the disable is recognized before subsequent code
674 __DSB();
675 __ISB();
676
677 return ses;
678}
679
683static __stk_forceinline void HW_CriticalSectionEnd(uint32_t ses)
684{
685 // ensure all memory work is finished before re-enabling
686 __DSB();
687 __set_PRIMASK(ses);
688 // synchronization point: any pending interrupt can be serviced immediately at this boundary
689 __ISB();
690}
691
694static __stk_forceinline void HW_EnterSleepMode()
695{
696 SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; // disable deep-sleep, go into a WAIT mode (sleep)
697 __DSB(); // ensure store takes effect (see ARM info)
698 __WFI();
699}
700
701// IAR wrappers for: __atomic_test_and_set, __atomic_clear.
702#ifdef __ICCARM__
703 #include <xatomic.h>
704
705 #define __ATOMIC_RELAXED __MEMORY_ORDER_RELAXED__
706 #define __ATOMIC_CONSUME __MEMORY_ORDER_CONSUME__
707 #define __ATOMIC_ACQUIRE __MEMORY_ORDER_ACQUIRE__
708 #define __ATOMIC_RELEASE __MEMORY_ORDER_RELEASE__
709 #define __ATOMIC_ACQ_REL __MEMORY_ORDER_ACQ_REL__
710 #define __ATOMIC_SEQ_CST __MEMORY_ORDER_SEQ_CST__
711
712 static __stk_forceinline bool __atomic_test_and_set(volatile bool *ptr, int memorder)
713 {
714 #ifdef __ICCARM__
715 STK_STATIC_ASSERT(sizeof(std::__iar_atomic_flag) == sizeof(bool));
716 #endif
717
718 return std::__iar_atomic_flag_test_and_set(ptr, memorder);
719 }
720
721 static __stk_forceinline void __atomic_clear(volatile bool *ptr, int memorder)
722 {
723 #ifdef __ICCARM__
724 STK_STATIC_ASSERT(sizeof(std::__iar_atomic_flag) == sizeof(bool));
725 #endif
726
727 std::__iar_atomic_flag_clear(ptr, memorder);
728 }
729#endif
730
731#ifdef CONTROL_nPRIV_Msk
744static __stk_forceinline bool HW_SpinLockTryLock(volatile bool &slock)
745{
746 return !__atomic_test_and_set(&slock, __ATOMIC_ACQUIRE);
747}
748
761static __stk_forceinline void HW_SpinLockUnlock(volatile bool &slock)
762{
763 if (slock == false)
764 {
765 STK_KERNEL_PANIC(KERNEL_PANIC_SPINLOCK_DEADLOCK); // release attempt of unowned lock
766 }
767
768 // ensure all data writes (like scheduling metadata) are flushed before the lock is released:
769 // __atomic_clear with __ATOMIC_RELEASE provides the required store-release barrier,
770 // the explicit dmb ishst is retained for toolchains that do not lower __ATOMIC_RELEASE
771 // to a full DMB on ARMv7-M (e.g. older GCC versions with -mcpu=cortex-m4)
772#ifndef __ICCARM__
773 __asm volatile("dmb ishst" ::: "memory");
774#endif
775
776 __atomic_clear(&slock, __ATOMIC_RELEASE);
777}
778#elif defined(RP2040_H) || defined(RP2350_H)
779// Raspberry RP2040 dual-core M0+ implementation, using Hardware Spinlock 0 (SIO base 0xd0000000 + offset)
780#define STK_SIO_SPINLOCK SIO->SPINLOCK31
781
797static __stk_forceinline bool HW_SpinLockTryLock(volatile bool &slock)
798{
799 bool success = (STK_SIO_SPINLOCK == 0 ? false : ((slock) = true, true));
800 __stk_dmb();
801
802 return success;
803}
804
821static __stk_forceinline void HW_SpinLockUnlock(volatile bool &slock)
822{
823 if (!slock)
824 STK_KERNEL_PANIC(KERNEL_PANIC_SPINLOCK_DEADLOCK); // release attempt of unowned lock
825
826 __stk_dmb();
827 (slock) = false;
828 STK_SIO_SPINLOCK = 1; // writing any value releases the hardware lock
829}
830
831#undef STK_SIO_SPINLOCK
832#else // !RP2040_H
833// Standard single-core Cortex-M0 implementation:
834
848static __stk_forceinline bool HW_SpinLockTryLock(volatile bool &slock)
849{
850 const uint32_t ses = HW_CriticalSectionStart();
851
852 if (slock)
853 {
854 HW_CriticalSectionEnd(ses);
855 return false;
856 }
857
858 slock = true;
859 __stk_dmb();
860
861 HW_CriticalSectionEnd(ses);
862 return true;
863}
864
876static __stk_forceinline void HW_SpinLockUnlock(volatile bool &slock)
877{
878 if (!slock)
879 STK_KERNEL_PANIC(KERNEL_PANIC_SPINLOCK_DEADLOCK); // release attempt of unowned lock
880
881 __stk_dmb();
882 slock = false;
883}
884#endif // CONTROL_nPRIV_Msk
885
905static __stk_forceinline void HW_SpinLockLock(volatile bool &slock)
906{
907 uint32_t timeout_itrs = s_StkSpinlockTimeoutIters;
908 while (!HW_SpinLockTryLock(slock))
909 {
910 if (--timeout_itrs == 0U)
911 {
912 // invariant violated: the lock owner exited without releasing
914 }
915 __stk_relax_cpu();
916 }
917}
918
919// -----------------------------------------------------------------------------
920namespace stk {
921// -----------------------------------------------------------------------------
922
947
957struct JmpFrame
958{
959 Word R4, R5, R6, R7, R8, R9, R10, R11;
960 Word SP;
961 Word LR;
962#if STK_CORTEX_M_FPU
963 Word FPSCR;
964#endif
965#if STK_CORTEX_M_PAC
966 Word PAC_R12;
967#endif
968};
969
985int32_t SaveJmp(JmpFrame &/*f*/)
986{
987 __asm volatile(
989
990#if (__CORTEX_M >= 3U)
991 // Cortex-M3/M4/M7: STMIA stores r4-r11 at r0+0 .. r0+28
992 "STMIA r0, {r4-r11} \n" // store r4-r11 at offsets 0-28, no writeback
993 "STR SP, [r0, #32] \n" // SP at offset 32
994 "STR LR, [r0, #36] \n" // LR at offset 36
995#else
996 // Cortex-M0/M0+/M1: Thumb-1 only
997 "STR r4, [r0, #0] \n"
998 "STR r5, [r0, #4] \n"
999 "STR r6, [r0, #8] \n"
1000 "STR r7, [r0, #12] \n"
1001 "MOV r1, r8 \n"
1002 "STR r1, [r0, #16] \n"
1003 "MOV r1, r9 \n"
1004 "STR r1, [r0, #20] \n"
1005 "MOV r1, r10 \n"
1006 "STR r1, [r0, #24] \n"
1007 "MOV r1, r11 \n"
1008 "STR r1, [r0, #28] \n"
1009 "MOV r1, SP \n"
1010 "STR r1, [r0, #32] \n"
1011 "MOV r1, LR \n"
1012 "STR r1, [r0, #36] \n"
1013#endif // (__CORTEX_M >= 3U)
1014
1015#if STK_CORTEX_M_FPU
1016 "VMRS r1, FPSCR \n"
1017 "STR r1, [r0, #40] \n"
1018#endif
1019
1020#if STK_CORTEX_M_PAC
1021 "PAC r12, LR, SP \n" // sign LR using SP as modifier -> R12
1022 #if STK_CORTEX_M_FPU
1023 "STR r12, [r0, #44] \n" // offset 44 if FPU is present
1024 #else
1025 "STR r12, [r0, #40] \n" // offset 40 if FPU is absent
1026 #endif
1027#endif
1028
1029 "MOVS r0, #0 \n"
1030 "BX LR \n");
1031
1032#ifdef __ICCARM__
1033#pragma diag_suppress=Pe940
1034 // return value is passed in r0 by the SVC handler per AAPCS;
1035 // IAR cannot see this through the naked asm, suppress the warning.
1036 return 0;
1037#pragma diag_default=Pe940
1038#endif
1039}
1040
1059void RestoreJmp(JmpFrame &/*f*/, int32_t /*val*/)
1060{
1061 __asm volatile(
1063
1064#if (__CORTEX_M >= 3U)
1065 // Cortex-M3/M4/M7: LDMIA loads r4-r11 from offsets 0-28
1066 "LDR sp, [r0, #32] \n" // restore SP
1067
1068#if STK_CORTEX_M_FPU
1069 "LDR r2, [r0, #40] \n" // load saved FPSCR
1070 "VMSR FPSCR, r2 \n" // restore rounding mode + flags
1071#endif
1072
1073#if STK_CORTEX_M_PAC
1074 #if STK_CORTEX_M_FPU
1075 "LDR r12, [r0, #44] \n" // load saved PAC signature into R12
1076 #else
1077 "LDR r12, [r0, #40] \n"
1078 #endif
1079#endif
1080
1081 "LDR r2, [r0, #36] \n" // load saved LR into r2
1082
1083 #if STK_CORTEX_M_PAC
1084 "MOV LR, r2 \n" // move to LR for authentication
1085 "AUT r12, LR, SP \n" // authenticate LR using current SP and R12
1086 "MOV r2, LR \n" // move validated address back to r2
1087 #endif
1088
1089 "LDMIA r0, {r4-r11} \n" // restore r4-r11, no writeback
1090 "MOV r0, r1 \n" // return val
1091 "BX r2 \n"
1092#else
1093 // Cortex-M0/M0+/M1: Thumb-1 only
1094 "LDR r2, [r0, #36] \n"
1095 "MOV LR, r2 \n"
1096 "LDR r2, [r0, #32] \n"
1097 "MOV SP, r2 \n"
1098 "LDR r2, [r0, #28] \n"
1099 "MOV r11, r2 \n"
1100 "LDR r2, [r0, #24] \n"
1101 "MOV r10, r2 \n"
1102 "LDR r2, [r0, #20] \n"
1103 "MOV r9, r2 \n"
1104 "LDR r2, [r0, #16] \n"
1105 "MOV r8, r2 \n"
1106 "LDR r4, [r0, #0] \n"
1107 "LDR r5, [r0, #4] \n"
1108 "LDR r6, [r0, #8] \n"
1109 "LDR r7, [r0, #12] \n"
1110 "MOV r0, r1 \n" // return val
1111 "BX LR \n"
1112#endif // (__CORTEX_M >= 3U)
1113 );
1114}
1115
1116// -----------------------------------------------------------------------------
1117} // namespace stk
1118// -----------------------------------------------------------------------------
1119
1124static __stk_forceinline Word HW_GetCurrentException()
1125{
1126 return __get_IPSR();
1127}
1128
1131static __stk_forceinline bool HW_IsHandlerMode()
1132{
1133 return (HW_GetCurrentException() != 0U);
1134}
1135
1139static __stk_forceinline bool HW_IsPrivilegedContext()
1140{
1141#ifdef CONTROL_nPRIV_Msk
1142 return ((__get_CONTROL() & CONTROL_nPRIV_Msk) == 0U);
1143#else
1144 return true;
1145#endif
1146}
1147
1151static __stk_forceinline Word HW_GetCallerSP()
1152{
1153 // use SP (R13) directly: __get_PSP() returns 0 in unprivileged thread mode
1154 Word sp;
1155 __asm volatile("MOV %0, SP" : "=r" (sp));
1156 return sp;
1157}
1158
1161static __stk_forceinline void HW_ScheduleContextSwitch()
1162{
1163 SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
1164}
1165
1168static __stk_forceinline void HW_ClearFpuState()
1169{
1170#if STK_CORTEX_M_FPU
1171 __set_CONTROL(__get_CONTROL() & ~CONTROL_FPCA_Msk);
1172#endif
1173}
1174
1182static __stk_forceinline void HW_EnableFullFpuAccess(void)
1183{
1184#if defined(STK_CORTEX_M_FPU) && (STK_CORTEX_M_FPU != 0)
1185
1186 // Compile-time constant bitmasks
1187 constexpr uint32_t SCB_CPACR_CP10_FULL_ACCESS = (3UL << 20U); // enable CP10
1188 constexpr uint32_t SCB_CPACR_CP11_FULL_ACCESS = (3UL << 22U); // enable CP11
1189
1190 /* -------------------------------------------------------------------------- */
1191 /* 1. ARMv7-M & ARMv8-M Base: Grant Privileged & Unprivileged Access */
1192 /* -------------------------------------------------------------------------- */
1193 uint32_t cpacr_val = SCB->CPACR;
1194 cpacr_val |= (SCB_CPACR_CP10_FULL_ACCESS | SCB_CPACR_CP11_FULL_ACCESS);
1195 SCB->CPACR = cpacr_val;
1196
1197#if defined(_STK_CORTEX_M_TRUSTZONE)
1198 constexpr uint32_t SCB_NSACR_CP10_ACCESS = (1UL << 10U); // allow Non-Secure access to CP10
1199 constexpr uint32_t SCB_NSACR_CP11_ACCESS = (1UL << 11U); // allow Non-Secure access to CP11
1200
1201 /* -------------------------------------------------------------------------- */
1202 /* 2. ARMv8-M TrustZone (Secure State Configuration) */
1203 /* -------------------------------------------------------------------------- */
1204
1205 // grant Non-Secure access to CP10 and CP11
1206 uint32_t nsacr_val = SCB->NSACR;
1207 nsacr_val |= (SCB_NSACR_CP10_ACCESS | SCB_NSACR_CP11_ACCESS);
1208 SCB->NSACR = nsacr_val;
1209
1210 // grant Full Access in Non-Secure State
1211 uint32_t ns_cpacr_val = SCB_NS->CPACR;
1212 ns_cpacr_val |= (SCB_CPACR_CP10_FULL_ACCESS | SCB_CPACR_CP11_FULL_ACCESS);
1213 SCB_NS->CPACR = ns_cpacr_val;
1214
1215 // configure Lazy Stacking for Secure & Non-Secure states
1216 uint32_t fpccr_val = FPU->FPCCR;
1217 fpccr_val |= (static_cast<uint32_t>(FPU_FPCCR_ASPEN_Msk) | static_cast<uint32_t>(FPU_FPCCR_LSPEN_Msk));
1218 FPU->FPCCR = fpccr_val;
1219
1220 uint32_t ns_fpccr_val = FPU_NS->FPCCR;
1221 ns_fpccr_val |= (static_cast<uint32_t>(FPU_FPCCR_ASPEN_Msk) | static_cast<uint32_t>(FPU_FPCCR_LSPEN_Msk));
1222 FPU_NS->FPCCR = ns_fpccr_val;
1223
1224#else
1225 /* -------------------------------------------------------------------------- */
1226 /* 3. Non-TrustZone / ARMv7-M FPU Control */
1227 /* -------------------------------------------------------------------------- */
1228 uint32_t fpccr_val = FPU->FPCCR;
1229 fpccr_val |= (static_cast<uint32_t>(FPU_FPCCR_ASPEN_Msk) | static_cast<uint32_t>(FPU_FPCCR_LSPEN_Msk));
1230 FPU->FPCCR = fpccr_val;
1231#endif
1232
1233 // ensure barriers complete FPU configuration before execution proceeds
1234 __DSB();
1235 __ISB();
1236
1237#endif /* defined(STK_CORTEX_M_FPU) && (STK_CORTEX_M_FPU != 0) */
1238}
1239
1243static __stk_forceinline uint32_t HW_CoreClockFrequency()
1244{
1245#if STK_CORE_FREQ_UNIFIED
1246 return s_StkSystemCoreClock[0];
1247#else
1248 return s_StkSystemCoreClock[STK_ARCH_GET_CPU_ID()];
1249#endif
1250}
1251
1254static __stk_forceinline void HW_ClearPendingSwitch()
1255{
1256 SCB->ICSR = SCB_ICSR_PENDSVCLR_Msk;
1257}
1258
1261static __stk_forceinline void HW_SysTickStart(uint32_t period_ticks)
1262{
1263 const Ticks period_ticks_64 = static_cast<Ticks>(period_ticks);
1264
1265 const uint32_t result = SysTick_Config(static_cast<uint32_t>(ConvertTimeUsToClockCycles(HW_CoreClockFrequency(), period_ticks_64)));
1266 STK_ASSERT(result == 0U);
1267 STK_UNUSED(result);
1268
1269 // QEMU workaround (Launchpad Bug #1872237):
1270 // SysTick_Config() writes VAL=0 before setting ENABLE=1. On QEMU,
1271 // systick_reload() silently discards VAL writes while ENABLE=0, leaving
1272 // the internal tick accumulator stale from the previous period;
1273 // writing VAL=0 here, after ENABLE=1 is already set, forces a correct reload.
1274 // This is a no-op on real Cortex-M hardware (spec: ARMv7-M ARM B3.3.1)
1275 SysTick->VAL = 0U;
1276}
1277
1280static __stk_forceinline void HW_SysTickStop()
1281{
1282 SysTick->CTRL = 0U;
1283 SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk;
1284}
1285
1288__stk_attr_unused /* can be unused due to configuration */
1289static __stk_forceinline void HW_SysTickDisable()
1290{
1291 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
1292}
1293
1297static __stk_forceinline uint32_t HW_SysTickValue()
1298{
1299 return SysTick->VAL;
1300}
1301
1305__stk_attr_unused /* can be unused due to configuration */
1306static __stk_forceinline uint32_t HW_SysTickValueAfterDisable()
1307{
1308 // is required for QEMU which then resets SysTick->VAL and thus we can't
1309 // calculate elapsed time correctly
1310 __DSB();
1311
1312 // check for a QEMU case and discard elapsed result
1313 uint32_t ticks = HW_SysTickValue();
1314 if (ticks == 0U)
1315 {
1316 ticks = SysTick->LOAD;
1317 }
1318
1319 return ticks;
1320}
1321
1325__stk_attr_unused /* can be unused due to configuration */
1326static __stk_forceinline uint32_t HW_SysTickElapsed(uint32_t ticks)
1327{
1328 return SysTick->LOAD - ticks;
1329}
1330
1334__stk_attr_unused /* can be unused due to configuration */
1335static __stk_forceinline void HW_SysTickRearm(uint32_t ticks)
1336{
1337 SysTick->LOAD = ticks - 1U;
1338 SysTick->VAL = 0U;
1339 SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
1340}
1341
1344static __stk_forceinline void HW_DWTEnableCounter()
1345{
1346 // enable Trace and Debug blocks (DWT, ITM, ETM, TPIU)
1347#if defined(CoreDebug)
1348 CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
1349#elif defined(DCB)
1350 DCB->DEMCR |= DCB_DEMCR_TRCENA_Msk;
1351#endif
1352 __DSB();
1353
1354 // LAR (Lock Access Register) is mandatory for Cortex-M7 to allow register writes,
1355 // it is typically not implemented or deprecated on M0, M3, M4, and M33
1356#if (__CORTEX_M == 7U)
1357 DWT->LAR = 0xC5ACCE55; // unlock DWT unit using standard CoreSight magic key
1358 __DSB();
1359#endif
1360
1361#if defined(DWT)
1362 // do not interfere with already enabled and running counter
1363 if ((DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk) != DWT_CTRL_CYCCNTENA_Msk)
1364 {
1365 DWT->CYCCNT = 0U; // reset counter value to zero
1366 DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // start counter
1367 }
1368#endif
1369}
1370
1375static __stk_forceinline uint32_t HW_DWTGetCounter()
1376{
1377#if defined(DWT)
1378 return DWT->CYCCNT;
1379#else
1380 return 0U;
1381#endif
1382}
1383
1396static void HW_InitSpinlockTimeout()
1397{
1398 const Cycles cycles_budget = ConvertTimeUsToClockCycles(HW_CoreClockFrequency(), STK_SPINLOCK_TIMEOUT_US);
1399 const Cycles iters = cycles_budget / STK_SPINLOCK_MIN_CYCLES_PER_ITER;
1400
1401 // floor guards against a zero/not-yet-configured clock; ceiling guards against overflow
1402 // when narrowing to uint32_t
1403 s_StkSpinlockTimeoutIters = static_cast<uint32_t>(
1404 stk::Min(stk::Max(iters, static_cast<Cycles>(0x1000U)), static_cast<Cycles>(0xFFFFFFFFU)));
1405}
1406
1407// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1408#if STK_MPU
1409// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1410
1429struct ScopedPrivilegeBoost
1430{
1436 explicit ScopedPrivilegeBoost();
1437
1444 ~ScopedPrivilegeBoost()
1445 {
1446 // drop privileges and flush pipeline (__set_CONTROL also issues __ISB)
1447 __set_CONTROL(__get_CONTROL() | CONTROL_nPRIV_Msk);
1448 }
1449};
1450
1467ScopedPrivilegeBoost::ScopedPrivilegeBoost()
1468{
1469 // boost privilege via SVC SVC_BOOST_PRIV handler
1470 __asm volatile(
1471 "SVC %0 \n"
1472 : /* no outputs */
1473 : "I"(SVC_BOOST_PRIV)
1474 : "memory"
1475 );
1476}
1477
1478// ----------------------------------------------------------------------------
1479namespace stk {
1480namespace hw {
1481namespace mpu {
1482// ----------------------------------------------------------------------------
1483
1504void ConfigureRegion(MpuRegion &reg, const struct MpuRegionConfig &cfg, uint32_t region_idx);
1505
1517void ConfigureStatic(const MpuRegionConfig cfg_list[], size_t cfg_count, uint32_t control_flags,
1518 bool non_secure);
1519
1532void ConfigureDynamic(TaskMpu &task_mpu, const struct MpuRegionConfig cfg_list[], const size_t cfg_count,
1533 IPlatform::IEventOverrider *overrider, bool non_secure);
1534
1546void ApplyRegion(const MpuRegion &reg, uint32_t index, bool non_secure);
1547
1555void DisableRegion(uint32_t index, bool non_secure);
1556
1567void Enable(bool enable, uint32_t control_flags, bool non_secure);
1568
1569#if STK_ARCH_ARMV8_M
1577static constexpr uint32_t MAIR0_PMSAV8_INIT = 0xFF440400U;
1578
1583static constexpr uint32_t MAIR1_PMSAV8_INIT = 0x00000000U;
1584
1589static constexpr Word RLAR_ENABLE_FLAG = (1U << 0U);
1590
1599static __stk_forceinline Word RBAR_DISABLED_REGION(uint32_t region_idx)
1600{
1601 STK_UNUSED(region_idx);
1602 return SCB->VTOR;
1603}
1604
1610static constexpr Word RLAR_DISABLED_REGION = 0U;
1611#else
1616static constexpr Word RASR_ENABLE_FLAG = (1U << 0U);
1617
1626static constexpr Word RBAR_DISABLED_REGION(uint32_t region_idx) { return (1U << 4U) | (region_idx & 0xFU); }
1627
1633static constexpr Word RASR_DISABLED_REGION = 0U;
1634#endif
1635
1636// ----------------------------------------------------------------------------
1637} // namespace mpu
1638} // namespace hw
1639} // namespace stk
1640// ----------------------------------------------------------------------------
1641
1642// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1643#endif // STK_MPU
1644// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1645
1660#if STK_MPU
1661 extern char __stk_mpu_shared_code_start[];
1662 extern char __stk_mpu_shared_code_end[];
1663 #if defined(DEBUG) || defined(_DEBUG)
1664 extern char __stk_mpu_shared_data_start[];
1665 extern char __stk_mpu_shared_data_end[];
1666 #endif
1667#endif
1668
1669#if STK_MPU
1675static constexpr MpuRegionConfig s_StkDisabledMpuRegion =
1676{
1677 .addr = 0U,
1678 .size = 0U,
1679 .access_perm = hw::mpu::ACCESS_NONE,
1680 .mem_type = hw::mpu::TYPE_STRONGLY_ORDERED,
1681 .share = hw::mpu::SHARE_NON,
1682 .exec = hw::mpu::EXEC_ALLOWED
1683};
1684#endif
1685
1687namespace stk {
1689static struct Context final : public PlatformContext
1690{
1691 typedef IPlatform::IEventOverrider eovrd_t;
1692
1693 Context() : PlatformContext(), m_exit_buf(), m_overrider(nullptr),
1694 #if STK_CORTEX_M_TRUSTZONE_FRAME
1695 m_overrider_ns(nullptr),
1696 #endif
1697 #if STK_TICKLESS_IDLE
1698 m_sleep_ticks(0), m_sleep_error(0U),
1699 #endif
1700 m_csu(0U), m_csu_nesting(0U), m_started(false), m_exiting(false)
1701 {}
1702
1703 void Initialize(IPlatform::IEventHandler *handler, IKernelService *service, Stack *exit_trap,
1704 uint32_t resolution_us)
1705 {
1706 InitializeBase(handler, service, exit_trap, resolution_us);
1707
1708 STK_STATIC_ASSERT_DESC_N(SP, offsetof(Stack, SP) == 0U,
1709 "expect Stack::mode member at offset of 0 (first member)");
1710 #if STK_MPU
1712 (STK_MPU_TASK_REGIONS <= 4U)
1713 ? ((STK_CORTEX_M_MPU_TASK_REGION_IDX % 4U) <= (4U - STK_MPU_TASK_REGIONS))
1714 : ((STK_CORTEX_M_MPU_TASK_REGION_IDX % 4U) == 0U),
1715 "STK_CORTEX_M_MPU_TASK_REGION_IDX is not aligned correctly for the RNR-relative "
1716 "alias burst write at the configured STK_MPU_TASK_REGIONS width");
1717 STK_STATIC_ASSERT_DESC_N(mpu_task_regions,
1718 (STK_MPU_TASK_REGIONS == 2U) || (STK_MPU_TASK_REGIONS == 4U) || (STK_MPU_TASK_REGIONS == 6U) ||
1719 (STK_MPU_TASK_REGIONS == 8U) || (STK_MPU_TASK_REGIONS == 12U) || (STK_MPU_TASK_REGIONS == 16U),
1720 "STK_MPU_TASK_REGIONS must be defined as 2, 4, 6, 8, 12 or 16");
1721 #ifdef _STK_CORTEX_M_TRUSTZONE
1724 ? ((STK_CORTEX_M_MPU_TASK_REGION_IDX_NS % 4U) <= (4U - STK_MPU_TASK_REGIONS_NS))
1725 : ((STK_CORTEX_M_MPU_TASK_REGION_IDX_NS % 4U) == 0U),
1726 "STK_CORTEX_M_MPU_TASK_REGION_IDX_NS is not aligned correctly for the RNR-relative "
1727 "alias burst write at the configured STK_MPU_TASK_REGIONS_NS width");
1728 STK_STATIC_ASSERT_DESC_N(mpu_task_regions_ns,
1731 "STK_MPU_TASK_REGIONS_NS must be defined as 2, 4, 6, 8, 12 or 16");
1732 #endif
1733 // make sure linker declares __stk_mpu_shared_xxx regions correctly, i.e. code and data
1734 // sections must contain data
1735 STK_ASSERT(hw::PtrToWord(__stk_mpu_shared_code_end) - hw::PtrToWord(__stk_mpu_shared_code_start) > 0U);
1736 STK_ASSERT(hw::PtrToWord(__stk_mpu_shared_data_end) - hw::PtrToWord(__stk_mpu_shared_data_start) > 0U);
1737 #endif
1738 STK_ASSERT(s_StkCortexmCsuLock == false);
1739 STK_ASSERT(g_StkLastPanicId == KERNEL_PANIC_NONE);
1740
1741 m_csu = 0U;
1742 m_csu_nesting = 0U;
1743 m_started = false;
1744 m_exiting = false;
1745 #if STK_TICKLESS_IDLE
1746 m_sleep_ticks = 0;
1747 m_sleep_error = 0U;
1748 #endif
1749
1750 // initialize system clock if user-side code has not yet done it
1751 if (s_StkSystemCoreClock[0] == 0U)
1752 {
1753 for (uint8_t i = 0U; i < STK_STATIC_ARRAY_SIZE(s_StkSystemCoreClock); ++i)
1754 {
1755 s_StkSystemCoreClock[i] = SystemCoreClock;
1756 }
1757 }
1758
1759 HW_InitSpinlockTimeout();
1760
1761 #if (__CORTEX_M > 1) && STK_TICKLESS_USE_ARM_DWT
1762 HW_DWTEnableCounter();
1763 #endif
1764
1765 #if STK_SEGGER_SYSVIEW
1766 SEGGER_SYSVIEW_Init(
1767 HW_CoreClockFrequency(),
1768 HW_CoreClockFrequency(),
1769 nullptr,
1770 stk::SendSysDesc);
1771 #endif
1772 }
1773
1774#if STK_TICKLESS_IDLE
1775 __stk_forceinline void OnTick(Timeout &ticks)
1776#else
1777 __stk_forceinline void OnTick()
1778#endif
1779 {
1780 if (m_handler->OnTick(m_stack_idle, m_stack_active
1781 #if STK_TICKLESS_IDLE
1782 , ticks
1783 #endif
1784 ))
1785 {
1786 HW_ScheduleContextSwitch();
1787 }
1788 }
1789
1790 __stk_forceinline void ProcessTick()
1791 {
1792 HW_DisableInterrupts();
1793
1794 #if STK_TICKLESS_IDLE
1795 Timeout ticks = m_sleep_ticks;
1796
1797 OnTick(ticks);
1798
1799 // re-arm SysTick only if tick period changed
1800 if (ticks != m_sleep_ticks)
1801 {
1802 m_sleep_ticks = ReloadTickPeriod(ticks);
1803 }
1804 #else
1805 OnTick();
1806 #endif // STK_TICKLESS_IDLE
1807
1808 HW_EnableInterrupts();
1809 }
1810
1811 __stk_forceinline void OnForceContextSwitch(TId id)
1812 {
1813 if (m_handler->OnForceContextSwitch(id, m_stack_idle, m_stack_active))
1814 {
1815 HW_ScheduleContextSwitch();
1816 }
1817 }
1818
1819 __stk_forceinline void OnEnterCriticalSection(uint32_t current_ses)
1820 {
1821 if (m_csu_nesting == 0U)
1822 {
1823 // ONLY attempt the global spinlock if we aren't already nested
1824 HW_SpinLockLock(s_StkCortexmCsuLock);
1825
1826 // store the hardware interrupt state to restore later
1827 m_csu = current_ses;
1828 }
1829
1830 // increase nesting count within a limit
1831 if (++m_csu_nesting > STK_CS_NESTINGS_MAX)
1832 {
1833 // invariant violated: exceeded max allowed number of recursions
1834 STK_KERNEL_PANIC(KERNEL_PANIC_CS_NESTING_OVERFLOW);
1835 }
1836 }
1837
1838 __stk_forceinline bool OnExitCriticalSection(uint32_t &restore_ses)
1839 {
1840 bool released = false;
1841
1842 STK_ASSERT(m_csu_nesting != 0U);
1843 --m_csu_nesting;
1844
1845 if (m_csu_nesting == 0U)
1846 {
1847 // capture the state before releasing lock
1848 restore_ses = m_csu;
1849
1850 // release global lock
1851 HW_SpinLockUnlock(s_StkCortexmCsuLock);
1852
1853 released = true;
1854 }
1855
1856 return released;
1857 }
1858
1859 __stk_forceinline void EnterCriticalSection()
1860 {
1861 const uint32_t current_ses = HW_CriticalSectionStart();
1862
1863 OnEnterCriticalSection(current_ses);
1864 }
1865
1866 __stk_forceinline void ExitCriticalSection()
1867 {
1868 uint32_t restore_ses;
1869 if (OnExitCriticalSection(restore_ses))
1870 {
1871 HW_CriticalSectionEnd(restore_ses);
1872 }
1873 }
1874
1875 void StartTickTimer(Timeout elapsed_ticks)
1876 {
1877 #if STK_TICKLESS_IDLE
1878 // reset sleep ticks if kernel was restarted
1879 m_sleep_ticks = elapsed_ticks;
1880 #else
1881 STK_UNUSED(elapsed_ticks);
1882 #endif
1883
1884 // start SysTick timer (it is yet can't fire an interrupt due to HW_DisableInterrupts)
1885 HW_SysTickStart(m_tick_resolution);
1886
1887 // note: Always after SysTick_Config because it may change SysTick priority
1888 NVIC_SetPriority(SysTick_IRQn, STK_CORTEX_M_ISR_PRIORITY_LOWEST);
1889 }
1890
1891#if STK_TLS && !STK_INLINE_TLS
1892 Word GetTls()
1893 {
1894 hw::CriticalSection::ScopedLock cs_;
1895
1896 STK_ASSERT(m_stack_active != nullptr);
1897
1898 return m_stack_active->tls;
1899 }
1900
1901 void SetTls(Word tp)
1902 {
1903 hw::CriticalSection::ScopedLock cs_;
1904
1905 STK_ASSERT(m_stack_active != nullptr);
1906
1907 m_stack_active->tls = tp;
1908 }
1909#endif // STK_TLS && !STK_INLINE_TLS
1910
1911 void OnSleepOverride()
1912 {
1913 #if STK_TICKLESS_IDLE
1914 const Timeout sleep_ticks = m_sleep_ticks;
1915 #else
1916 const Timeout sleep_ticks = 1;
1917 #endif
1918
1919 if (!m_overrider->OnSleep(sleep_ticks))
1920 {
1921 HW_EnterSleepMode();
1922 }
1923 }
1924
1925 uint32_t GetTickResolutionInClockCycles()
1926 {
1927 const Ticks resolution_ticks = static_cast<Ticks>(m_tick_resolution);
1928 return static_cast<uint32_t>(ConvertTimeUsToClockCycles(HW_CoreClockFrequency(), resolution_ticks));
1929 }
1930
1931#if STK_MPU
1932 void ConfigureMpuInstance(eovrd_t *overrider, bool non_secure)
1933 {
1934 // configure Secure binary side if TrustZone enabled, or just standard
1935 if (overrider != nullptr)
1936 {
1937 const MpuConfig *const cfg = overrider->OnConfigureMpu();
1938
1939 // OnConfigureMpu() may legitimately return nullptr (no custom global config supplied),
1940 // treat that the same as MPU_CFG_NONE below.
1941 if (cfg != nullptr)
1942 {
1943 #if STK_CORTEX_M_TRUSTZONE_FRAME
1944 // MPU_CFG_NONSECURE_MPU selects which hardware MPU instance a config targets
1945 // (absent = Secure, set = Non-Secure). It must agree with the instance this call
1946 // is actually configuring (see ConfigureMpu() below), catching a config wired to
1947 // the wrong overrider (e.g. Secure flags returned from the Non-Secure overrider).
1948 STK_ASSERT(non_secure == ((cfg->mode & hw::mpu::MPU_CFG_NONSECURE_MPU) != 0U));
1949 #endif
1950
1951 if ((cfg->mode & hw::mpu::MPU_CFG_CLEAR_ON_INIT) != 0U)
1952 {
1953 // wipe every hardware region slot (static block + per-task block) so no stale
1954 // configuration left behind by a previous run/bootloader survives the static
1955 // table applied by ConfigureStatic() below.
1956 for (uint32_t index = 0U; index < STK_CORTEX_M_MPU_REGIONS_MAX; ++index)
1957 {
1958 hw::mpu::DisableRegion(index, non_secure);
1959 }
1960 }
1961
1962 uint32_t control_flags = 0U;
1963
1964 if ((cfg->mode & hw::mpu::MPU_CFG_PRIVILEGED_BG_MEM) != 0U)
1965 {
1966 control_flags |= MPU_CTRL_PRIVDEFENA_Msk;
1967 }
1968
1969 if ((cfg->mode & hw::mpu::MPU_CFG_IN_FAULTS) != 0U)
1970 {
1971 control_flags |= MPU_CTRL_HFNMIENA_Msk;
1972 }
1973
1974 hw::mpu::ConfigureStatic(cfg->regions.GetPtr(), cfg->regions.GetSize(), control_flags, non_secure);
1975 }
1976 else
1977 {
1978 hw::mpu::Enable(false, 0, non_secure);
1979 }
1980 }
1981 }
1982#endif // STK_MPU
1983
1984#if STK_MPU
1985 void ConfigureMpu()
1986 {
1987 // configure Secure binary side if TrustZone enabled, or just standard
1988 ConfigureMpuInstance(m_overrider, false);
1989
1990 #if STK_CORTEX_M_TRUSTZONE_FRAME
1991 // configure MPU_NS for Non-Secure binary side
1992 ConfigureMpuInstance(m_overrider_ns, true);
1993 #endif
1994 }
1995#endif // STK_MPU
1996
1997 TId GetCurrentTId() const
1998 {
1999 #if STK_STACK_NEEDS_TASK_ID
2000 const TId tid = (m_stack_active != nullptr ? m_stack_active->tid : TID_NONE);
2001 #else
2002 const TId tid = TID_NONE;
2003 #endif
2004
2005 return tid;
2006 }
2007
2008 void Start();
2009 void OnStart();
2010 void OnStop();
2011#if STK_TICKLESS_IDLE
2012 Timeout ReloadTickPeriod(Timeout ticks_requested);
2013 Timeout Suspend();
2014 void Resume(Timeout elapsed_ticks);
2015#endif
2016
2017 JmpFrame m_exit_buf;
2018 eovrd_t *m_overrider;
2019#if STK_CORTEX_M_TRUSTZONE_FRAME
2020 eovrd_t *m_overrider_ns;
2021#endif
2022#if STK_TICKLESS_IDLE
2023 Timeout m_sleep_ticks;
2024 uint32_t m_sleep_error;
2025#endif
2026 uint32_t m_csu;
2027 uint8_t m_csu_nesting;
2028 volatile bool m_started;
2029 bool m_exiting;
2030}
2031s_StkPlatformContext[STK_ARCH_CPU_COUNT];
2032} // namespace stk
2033
2035class HiResClockDWT
2036{
2037 Cycles m_acc;
2038 uint32_t m_prev;
2039
2040public:
2041 HiResClockDWT() : m_acc(0U), m_prev(0U)
2042 {
2043 HW_DWTEnableCounter();
2044 m_prev = HW_DWTGetCounter();
2045 }
2046
2047 static HiResClockDWT *GetInstance();
2048
2049 void Update()
2050 {
2051 const uint32_t current = HW_DWTGetCounter();
2052
2053 // unsigned subtraction handles the wrap-around perfectly
2054 const uint32_t delta = current - m_prev;
2055 m_acc += delta;
2056
2057 m_prev = current;
2058 }
2059
2060 Cycles GetCycles()
2061 {
2062 Update();
2063 return m_acc;
2064 }
2065
2066 uint32_t GetFrequency()
2067 {
2068 return HW_CoreClockFrequency();
2069 }
2070};
2071
2073HiResClockDWT *HiResClockDWT::GetInstance()
2074{
2075 // keep declaration function-local to allow compiler stripping it from the binary if
2076 // it is unused by the user code
2077 STK_MPU_KERNEL_DATA_SECTION static HiResClockDWT clock;
2078 return &clock;
2079}
2080
2082class HiResClockM0
2083{
2084public:
2085 static HiResClockM0 *GetInstance();
2086
2087 Cycles GetCycles()
2088 {
2089 const Ticks resolution_ticks = static_cast<Ticks>(GetContext().m_tick_resolution);
2090
2091 // On M0, combine the coarse OS ticks with the fine-grained SysTick counter
2092 const Cycles cycles = ConvertTimeUsToClockCycles(HW_CoreClockFrequency(), (stk::GetTicks() * resolution_ticks));
2093
2094 const uint32_t val = HW_SysTickValue(); // down-counter (cycles remaining in current tick)
2095 const uint32_t load = SysTick->LOAD; // current reload value
2096 const uint32_t delta = load - val; // delta
2097
2098 const Cycles delta_cycles = static_cast<Cycles>(delta);
2099
2100 // total elapsed cycles
2101 return cycles + delta_cycles;
2102 }
2103
2104 uint32_t GetFrequency()
2105 {
2106 return HW_CoreClockFrequency();
2107 }
2108};
2109
2111HiResClockM0 *HiResClockM0::GetInstance()
2112{
2113 // keep declaration function-local to allow compiler stripping it from the binary if
2114 // it is unused by the user code
2115 STK_MPU_KERNEL_DATA_SECTION static HiResClockM0 clock;
2116 return &clock;
2117}
2118
2119#if (__CORTEX_M >= 3U)
2120 typedef HiResClockDWT HiResClockImpl;
2121#else
2122 typedef HiResClockM0 HiResClockImpl;
2123#endif
2124
2125__stk_attr_noinline // keep out of inlining to preserve stack frame
2126__stk_attr_noreturn // never returns - a trap
2128{
2129 g_StkLastPanicId = id;
2130
2131 // disable all maskable interrupts: this prevents scheduler from running again and corrupting state further
2132 HW_DisableInterrupts();
2133
2134 // spin forever: with a watchdog active this produces a clean reset, without a watchdog,
2135 // a debugger can attach and inspect 'id'
2136 for (;;)
2137 {
2138 __stk_relax_cpu();
2139 }
2140}
2141
2143{
2144 GetContext().ProcessTick();
2145}
2146
2147#if STK_TICKLESS_IDLE
2148Timeout Context::ReloadTickPeriod(Timeout ticks_requested)
2149{
2150 Timeout clamped_ticks;
2151
2152 const uint32_t SYSTICK_MAX_LOAD = 0x00FFFFFFU; // SysTick LOAD register is 24-bit
2153 const uint32_t tick_resolution = GetTickResolutionInClockCycles();
2154 if (tick_resolution != 0U)
2155 {
2156 // guard against uint32_t overflow in the reload calculation
2157 STK_ASSERT((static_cast<uint64_t>(ticks_requested) * tick_resolution) <= UINT32_MAX);
2158
2159 // clamp ticks_requested so that cpu_ticks_requested fits into 24-bit SysTick LOAD register
2160 // without clamping large sleep tick counts silently truncate LOAD, causing the timer to fire far too early
2161 // breaking the timing
2162 const uint32_t max_ticks = SYSTICK_MAX_LOAD / tick_resolution;
2163 if (ticks_requested > static_cast<Timeout>(max_ticks))
2164 {
2165 clamped_ticks = static_cast<Timeout>(max_ticks);
2166 }
2167 else
2168 {
2169 clamped_ticks = ticks_requested;
2170 }
2171
2172 // start counting how many CPU cycles further instructions take until SysTick timer is enabled again;
2173 // without DWT we will have tick error of around 80 cycles depending on CPU model and compiler optimization
2174 #if (__CORTEX_M > 1) && STK_TICKLESS_USE_ARM_DWT
2175 const uint32_t error = HW_DWTGetCounter();
2176 __stk_compiler_barrier(); // prevent reordering, we measure all cycles of instructions below this point
2177 #endif
2178
2179 // pause SysTick
2180 HW_SysTickDisable();
2181
2182 // get already elapsed CPU cycles since SysTick ISR invocation up to SysTick timer stop (see above)
2183 // to account for them for a new period value
2184 const uint32_t elapsed_till_stop = HW_SysTickElapsed(HW_SysTickValueAfterDisable());
2185
2186 // OnTick() should not consume more than next period
2187 STK_ASSERT(static_cast<Timeout>(elapsed_till_stop / tick_resolution) <= clamped_ticks);
2188
2189 const uint32_t cpu_ticks_requested = static_cast<uint32_t>(clamped_ticks) * tick_resolution;
2190
2191 // substract number of cycles elapsed till SysTick stop + error from previous round
2192 uint32_t new_load = cpu_ticks_requested - elapsed_till_stop - m_sleep_error;
2193
2194 // clamp: rearm overhead must never push new_load into underflow
2195 if (new_load > cpu_ticks_requested)
2196 {
2197 new_load = cpu_ticks_requested;
2198 }
2199
2200 // reload with elapsed ticks accounted
2201 HW_SysTickRearm(new_load);
2202
2203 #if (__CORTEX_M > 1) && STK_TICKLESS_USE_ARM_DWT
2204 // calculate error: subtract cycles consumed by the rearm sequence itself in the next round
2205 m_sleep_error = HW_DWTGetCounter() - error;
2206 #endif
2207 }
2208 else
2209 {
2210 STK_ASSERT(false);
2211 clamped_ticks = NO_WAIT;
2212 }
2213
2214 // return actual clamped ticks armed
2215 return clamped_ticks;
2216}
2217#endif // STK_TICKLESS_IDLE
2218
2219extern "C" void STK_SYSTICK_HANDLER()
2220{
2221 // start tracing
2222#if STK_SEGGER_SYSVIEW
2223 SEGGER_SYSVIEW_RecordEnterISR();
2224#endif
2225
2226 Context &ctx = GetContext();
2227
2228#ifdef HAL_MODULE_ENABLED // STM32 HAL
2229 // make sure STM32 HAL gets timing information as it depends on SysTick in delaying procedures
2230#if STK_TICKLESS_IDLE
2231 const uint32_t sleep_ticks = static_cast<uint32_t>(ctx.m_sleep_ticks);
2232 uwTick += (sleep_ticks * ctx.m_tick_resolution);
2233#else
2234 HAL_IncTick();
2235#endif
2236
2237 // STM32 HAL starts SysTick on its initialization that will cause a crash on NULL,
2238 // therefore use additional check if HAL_MODULE_ENABLED is defined
2239 if (ctx.m_started)
2240 {
2241#else
2242 {
2243 // make sure SysTick is enabled by the Kernel::Start(), disable its start anywhere else
2244 STK_ASSERT(ctx.m_started);
2245 STK_ASSERT(ctx.m_handler != nullptr);
2246#endif
2247 ctx.ProcessTick();
2248 }
2249
2250 // stop tracing & send packets to host
2251#if STK_SEGGER_SYSVIEW
2252 SEGGER_SYSVIEW_RecordExitISR();
2253 SEGGER_SYSVIEW_IsStarted();
2254#endif
2255}
2256
2257#if STK_SEGGER_SYSVIEW
2258 #define STK_SYSVIEW_CALL(func)\
2259 "PUSH {r0-r3, r7, r11, r12, LR} \n"\
2260 "BL " #func " \n"\
2261 "POP {r0-r3, r7, r11, r12, LR} \n"
2262#endif
2263
2264#if STK_SEGGER_SYSVIEW
2265extern "C" __stk_attr_used void StkSystemView_OnContextSwitch(void)
2266{
2267 Context &ctx = GetContext();
2268
2269 // exit from PendSV
2270 SEGGER_SYSVIEW_RecordExitISR();
2271
2272 if (ctx.m_stack_idle->tid != SYS_TASK_ID_SLEEP)
2273 {
2274 // previous task went idle
2275 SEGGER_SYSVIEW_OnTaskStopReady(ctx.m_stack_idle->tid, TRACE_EVENT_SWITCH);
2276 }
2277
2278 if (ctx.m_stack_active->tid != SYS_TASK_ID_SLEEP)
2279 {
2280 // current task resumed execution
2281 SEGGER_SYSVIEW_OnTaskStartExec(ctx.m_stack_active->tid);
2282 }
2283}
2284#endif
2285
2313#if (STK_MPU_TASK_REGIONS == 2U)
2314#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2315 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2316 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2317 "LDMIA r12, {r4-r7} \n" /* burst load 4 words */\
2318 "STMIA r0, {r4-r7} \n" /* write MPU RBAR..RLAR_A1 (r0 remains anchored) */
2319#elif (STK_MPU_TASK_REGIONS == 4U)
2320#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2321 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2322 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2323 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2324 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 (r0 remains anchored) */
2325#elif (STK_MPU_TASK_REGIONS == 6U)
2326#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2327 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2328 \
2329 /* Block 1 (Regions 0-3 / 8 words) */\
2330 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2331 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2332 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2333 \
2334 /* Block 2 (Regions 4-5 / 4 words) */\
2335 "LDR r12, =%[mpu_rnr_idx4] \n" /* r12 = IDX + 4 */\
2336 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 4 (RNR is RBAR - 4) */\
2337 "ADD r12, r1, %[mpu_reg4_of] \n" /* point to mpu.region[4] */\
2338 "LDMIA r12, {r4-r7} \n" /* burst load 4 words */\
2339 "STMIA r0, {r4-r7} \n" /* write MPU RBAR..RLAR_A1 */\
2340 \
2341 /* Restore RNR */\
2342 "LDR r12, =%[mpu_rnr_idx] \n" /* r12 = IDX */\
2343 "STR r12, [r0, #-4] \n" /* restore MPU->RNR */
2344#elif (STK_MPU_TASK_REGIONS == 8U)
2345#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2346 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2347 \
2348 /* Block 1 (Regions 0-3 / 8 words) */\
2349 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2350 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2351 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2352 \
2353 /* Block 2 (Regions 4-7 / 8 words) */\
2354 "LDR r12, =%[mpu_rnr_idx4] \n" /* r12 = IDX + 4 */\
2355 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 4 (RNR is RBAR - 4) */\
2356 "ADD r12, r1, %[mpu_reg4_of] \n" /* point to mpu.region[4] */\
2357 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2358 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2359 \
2360 /* Restore RNR */\
2361 "LDR r12, =%[mpu_rnr_idx] \n" /* r12 = IDX */\
2362 "STR r12, [r0, #-4] \n" /* restore MPU->RNR */
2363#elif (STK_MPU_TASK_REGIONS == 12U)
2364#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2365 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2366 \
2367 /* Block 1 (Regions 0-3 / 8 words) */\
2368 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2369 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2370 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2371 \
2372 /* Block 2 (Regions 4-7 / 8 words) */\
2373 "LDR r12, =%[mpu_rnr_idx4] \n" /* r12 = IDX + 4 */\
2374 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 4 (RNR is RBAR - 4) */\
2375 "ADD r12, r1, %[mpu_reg4_of] \n" /* point to mpu.region[4] */\
2376 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2377 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2378 \
2379 /* Block 3 (Regions 8-11 / 8 words) */\
2380 "LDR r12, =%[mpu_rnr_idx8] \n" /* r12 = IDX + 8 */\
2381 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 8 */\
2382 "ADD r12, r1, %[mpu_reg8_of] \n" /* point to mpu.region[8] */\
2383 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2384 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2385 \
2386 /* Restore RNR */\
2387 "LDR r12, =%[mpu_rnr_idx] \n" /* r12 = IDX */\
2388 "STR r12, [r0, #-4] \n" /* restore MPU->RNR */
2389#elif (STK_MPU_TASK_REGIONS == 16U)
2390#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2391 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2392 \
2393 /* Block 1 (Regions 0-3 / 8 words) */\
2394 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2395 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2396 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2397 \
2398 /* Block 2 (Regions 4-7 / 8 words) */\
2399 "LDR r12, =%[mpu_rnr_idx4] \n" /* r12 = IDX + 4 */\
2400 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 4 (RNR is RBAR - 4) */\
2401 "ADD r12, r1, %[mpu_reg4_of] \n" /* point to mpu.region[4] */\
2402 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2403 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2404 \
2405 /* Block 3 (Regions 8-11 / 8 words) */\
2406 "LDR r12, =%[mpu_rnr_idx8] \n" /* r12 = IDX + 8 */\
2407 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 8 */\
2408 "ADD r12, r1, %[mpu_reg8_of] \n" /* point to mpu.region[8] */\
2409 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2410 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2411 \
2412 /* Block 4 (Regions 12-15 / 8 words) */\
2413 "LDR r12, =%[mpu_rnr_idx12] \n" /* r12 = IDX + 12 */\
2414 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 12 */\
2415 "ADD r12, r1, %[mpu_reg12_of] \n" /* point to mpu.region[12] */\
2416 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2417 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2418 \
2419 /* Restore RNR */\
2420 "LDR r12, =%[mpu_rnr_idx] \n" /* r12 = IDX */\
2421 "STR r12, [r0, #-4] \n" /* restore MPU->RNR */
2422#else
2423 #error "Unsupported region count, allowed - 2, 4, 6, 8, 12, 16!"
2424#endif
2425
2426#if STK_CORTEX_M_TRUSTZONE_FRAME
2427 #if (STK_MPU_TASK_REGIONS_NS == 2U)
2428 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2429 \
2430 /* Non-Secure Dual MPU (2 regions / 4 words) */\
2431 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2432 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2433 "LDMIA r12, {r4-r7} \n" /* burst load 4 words */\
2434 "STMIA r0, {r4-r7} \n" /* write MPU_NS RBAR..RLAR_A1 */
2435 #elif (STK_MPU_TASK_REGIONS_NS == 4U)
2436 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2437 \
2438 /* Non-Secure Dual MPU (4 regions / 8 words) */\
2439 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2440 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2441 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2442 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */
2443 #elif (STK_MPU_TASK_REGIONS_NS == 6U)
2444 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2445 \
2446 /* Non-Secure Dual MPU (6 regions) */\
2447 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2448 \
2449 /* NS Block 1 (Regions 0-3 / 8 words) */\
2450 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2451 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2452 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2453 \
2454 /* NS Block 2 (Regions 4-5 / 4 words) */\
2455 "LDR r12, =%[mpu_ns_rnr_idx4] \n" /* r12 = IDX + 4 */\
2456 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 4 */\
2457 "ADD r12, r1, %[mpu_ns_reg4_of]\n" /* point to mpu_ns.region[4] */\
2458 "LDMIA r12, {r4-r7} \n" /* burst load 4 words */\
2459 "STMIA r0, {r4-r7} \n" /* write MPU_NS RBAR..RLAR_A1 */\
2460 \
2461 /* Restore NS RNR */\
2462 "LDR r12, =%[mpu_ns_rnr_idx] \n" /* r12 = IDX */\
2463 "STR r12, [r0, #-4] \n" /* restore MPU_NS->RNR */
2464 #elif (STK_MPU_TASK_REGIONS_NS == 8U)
2465 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2466 \
2467 /* Non-Secure Dual MPU (8 regions) */\
2468 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2469 \
2470 /* NS Block 1 (Regions 0-3 / 8 words) */\
2471 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2472 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2473 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2474 \
2475 /* NS Block 2 (Regions 4-7 / 8 words) */\
2476 "LDR r12, =%[mpu_ns_rnr_idx4] \n" /* r12 = IDX + 4 */\
2477 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 4 */\
2478 "ADD r12, r1, %[mpu_ns_reg4_of]\n" /* point to mpu_ns.region[4] */\
2479 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2480 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2481 \
2482 /* Restore NS RNR */\
2483 "LDR r12, =%[mpu_ns_rnr_idx] \n" /* r12 = IDX */\
2484 "STR r12, [r0, #-4] \n" /* restore MPU_NS->RNR */
2485 #elif (STK_MPU_TASK_REGIONS_NS == 12U)
2486 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2487 \
2488 /* Non-Secure Dual MPU (12 regions / 3 blocks) */\
2489 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2490 \
2491 /* NS Block 1 (Regions 0-3 / 8 words) */\
2492 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2493 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2494 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2495 \
2496 /* NS Block 2 (Regions 4-7 / 8 words) */\
2497 "LDR r12, =%[mpu_ns_rnr_idx4] \n" /* r12 = IDX + 4 */\
2498 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 4 */\
2499 "ADD r12, r1, %[mpu_ns_reg4_of]\n" /* point to mpu_ns.region[4] */\
2500 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2501 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2502 \
2503 /* NS Block 3 (Regions 8-11 / 8 words) */\
2504 "LDR r12, =%[mpu_ns_rnr_idx8] \n" /* r12 = IDX + 8 */\
2505 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 8 */\
2506 "ADD r12, r1, %[mpu_ns_reg8_of]\n" /* point to mpu_ns.region[8] */\
2507 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2508 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2509 \
2510 /* Restore NS RNR */\
2511 "LDR r12, =%[mpu_ns_rnr_idx] \n" /* r12 = IDX */\
2512 "STR r12, [r0, #-4] \n" /* restore MPU_NS->RNR */
2513 #elif (STK_MPU_TASK_REGIONS_NS == 16U)
2514 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2515 \
2516 /* Non-Secure Dual MPU (16 regions / 4 blocks) */\
2517 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2518 \
2519 /* NS Block 1 (Regions 0-3 / 8 words) */\
2520 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2521 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2522 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2523 \
2524 /* NS Block 2 (Regions 4-7 / 8 words) */\
2525 "LDR r12, =%[mpu_ns_rnr_idx4] \n" /* r12 = IDX + 4 */\
2526 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 4 */\
2527 "ADD r12, r1, %[mpu_ns_reg4_of]\n" /* point to mpu_ns.region[4] */\
2528 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2529 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2530 \
2531 /* NS Block 3 (Regions 8-11 / 8 words) */\
2532 "LDR r12, =%[mpu_ns_rnr_idx8] \n" /* r12 = IDX + 8 */\
2533 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 8 */\
2534 "ADD r12, r1, %[mpu_ns_reg8_of]\n" /* point to mpu_ns.region[8] */\
2535 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2536 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2537 \
2538 /* NS Block 4 (Regions 12-15 / 8 words) */\
2539 "LDR r12, =%[mpu_ns_rnr_idx12] \n" /* r12 = IDX + 12 */\
2540 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 12 */\
2541 "ADD r12, r1, %[mpu_ns_reg12_of]\n" /* point to mpu_ns.region[12] */\
2542 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2543 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2544 \
2545 /* Restore NS RNR */\
2546 "LDR r12, =%[mpu_ns_rnr_idx] \n" /* r12 = IDX */\
2547 "STR r12, [r0, #-4] \n" /* restore MPU_NS->RNR */
2548 #else
2549 #error "Unsupported region count, allowed - 2, 4, 6, 8, 12, 16!"
2550 #endif
2551#endif
2552
2553#if STK_CORTEX_M_TRUSTZONE_FRAME
2554 #define STK_ASM_BLOCK_MPU_STACK_GUARD\
2555 "MOV r1, %[st_active] \n" /* r1 = Stack* */\
2556 STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2557 STK_ASM_BLOCK_MPU_NS_STACK_GUARD
2558#else
2559 #define STK_ASM_BLOCK_MPU_STACK_GUARD\
2560 "MOV r1, %[st_active] \n" /* r1 = Stack* */\
2561 STK_ASM_BLOCK_MPU_S_STACK_GUARD
2562#endif
2563
2564extern "C" __stk_attr_naked void STK_PENDSV_HANDLER()
2565{
2566#if (STK_ARCH_CPU_COUNT > 1U)
2567 // optimize register utilization and prevent compiler from using r4-r11
2568 // registers before they are saved. Use r12 IPC scratch register for that
2569 // and calculate offset for p_ctx only once; instruct assembler to use
2570 // r3, r2 for holding pointers to Idle and Active stacks
2571 #ifdef __ICCARM__
2572 register Context *p_ctx = &GetContext();
2573 register Stack *p_st_idle = p_ctx->m_stack_idle;
2574 register Stack *p_st_active = p_ctx->m_stack_active;
2575 #else
2576 register Context *p_ctx __asm("r12") = &GetContext();
2577 register Stack *p_st_idle __asm("r3") = p_ctx->m_stack_idle;
2578 register Stack *p_st_active __asm("r2") = p_ctx->m_stack_active;
2579 #endif
2580#endif
2581
2582 __asm volatile(
2584
2585 STK_ASM_DISABLE_INTERRUPTS " \n"
2586
2587 // start tracing
2588#if STK_SEGGER_SYSVIEW
2589 STK_SYSVIEW_CALL(StkSystemView_RecordEnterISR)
2590#endif
2591
2592 // save the Secure PSP unconditionally; it is the spine of the entire context
2593 // frame regardless of whether the task was interrupted inside Secure or
2594 // Non-Secure code; PSP_NS, CONTROL_NS, and both PSPLIMs are embedded inside
2595 // the frame (in TrustZoneFrame) so the restore path never needs to branch on
2596 // EXC_RETURN; the CPU's own EXC_RETURN S-bit in the restored LR selects the
2597 // correct return world on "BX LR"
2598 "MRS r0, PSP \n"
2599
2600#if STK_CORTEX_M_FPU
2601 // Save FP registers.
2602 "TST LR, #16 \n" /* test LR for 0xffffffe_, e.g. Thread mode with FP data */
2603
2604 "IT EQ \n" /* if result is positive */
2605 "VMOVEQ.F32 s0, s0 \n" /* force hardware lazy state preservation */
2606
2607 "IT EQ \n" /* if result is positive */
2608 "VSTMDBEQ r0!, {s16-s31} \n" /* store 16 SP registers */
2609#endif
2610
2611 // save registers of inactive task's CPU context:
2612
2613#if STK_CORTEX_M_MANAGE_LR
2614 // save r4-r11 and LR
2615 // note: for Cortex-M3 and higher save LR to keep correct Thread state of the
2616 // task when it is restored
2617 "STMDB r0!, {r4-r11, LR}\n"
2618#else
2619 // note: STMIA is limited to r0-r7 range, therefore save via stack memory
2620 "SUBS r0, r0, #16 \n" /* decrement for r4-r7 */
2621 "STMIA r0!, {r4-r7} \n"
2622 "MOV r4, r8 \n"
2623 "MOV r5, r9 \n"
2624 "MOV r6, r10 \n"
2625 "MOV r7, r11 \n"
2626 "SUBS r0, r0, #32 \n" /* decrement for r8-r11 and pointer reset */
2627 "STMIA r0!, {r4-r7} \n"
2628 "SUBS r0, r0, #16 \n" /* final pointer adjustment */
2629#endif
2630
2631 // ARMv8-M TrustZone: save PSPLIM (Secure), PSPLIM_NS, PSP_NS, and CONTROL_NS
2632 // into the Secure stack frame below the callee-saved registers, matching
2633 // TrustZoneFrame layout; PSP_NS captures the task's Non-Secure stack mid-execution
2634 // (may be inside an NS call that was interrupted); CONTROL_NS preserves NS
2635 // privilege / stack-select
2636#if STK_CORTEX_M_TRUSTZONE_FRAME
2637 // push PSPLIM, PSPLIM_NS (equivalent to STMDB r0!, {PSPLIM, PSPLIM_NS})
2638 "MRS r12, PSPLIM_NS \n"
2639 "STR r12, [r0, #-4]! \n"
2640 "MRS r12, PSPLIM \n"
2641 "STR r12, [r0, #-4]! \n"
2642
2643 // push PSP_NS, CONTROL_NS (equivalent to STMDB r0!, {PSP_NS, CONTROL_NS})
2644 "MRS r12, CONTROL_NS \n"
2645 "STR r12, [r0, #-4]! \n"
2646 "MRS r12, PSP_NS \n"
2647 "STR r12, [r0, #-4]! \n"
2648#endif
2649
2650 // save the outgoing task's live CONTROL register (nPRIV, SPSEL, FPCA, SFPA)
2651 // so that any privilege boost still in effect (SVC_BOOST_PRIV) is preserved verbatim
2652#if STK_CORTEX_M_PRIVILEGE_FRAME
2653 "MRS r12, CONTROL \n"
2654 "STR r12, [r0, #-4]! \n"
2655#endif
2656
2657 // store in GetContext().m_stack_idle
2658 "STR r0, [%[st_idle]] \n" /* store the first member (Stack::SP) from r0 */
2659
2660 // Set per-task MPU guard.
2661#if STK_MPU_STACK_GUARD
2662 STK_ASM_BLOCK_MPU_STACK_GUARD
2663#endif
2664
2665 // load stack of the active task from GetContext().m_stack_active
2666 // note: keep in sync with OnTaskStart
2667 "LDR r0, [%[st_active]]\n" /* load the first member of Stack (Stack::SP) into r0 */
2668
2669 // restore the incoming task's live CONTROL register exactly as it was
2670 // captured when that task was last switched out (or as initialized by
2671 // InitStack for a brand-new task); this is the lowest-addressed word of the
2672 // saved frame (pushed last), so it is popped first, before TrustZoneFrame
2673#if STK_CORTEX_M_PRIVILEGE_FRAME
2674 "LDR r12, [r0], %[access_mode_of] \n"
2675 "MSR CONTROL, r12 \n" /* note: on EXC_RETURN platform flushes instruction cache, therefore we omit explicit ISB */
2676#endif
2677
2678 // ARMv8-M TrustZone: restore TrustZoneFrame fields in reverse push order.
2679 // Pop order: PSP_NS + CONTROL_NS first (pushed last), then PSPLIM + PSPLIM_NS.
2680 // After this, r0 points to the callee-saved register region.
2681 // PSP_S is restored unconditionally after general-register pop.
2682 // CONTROL_NS restores NS privilege/stack-select; PSP_NS restores the task's
2683 // Non-Secure stack to exactly where it was when the task was interrupted.
2684 // EXC_RETURN in LR (restored by LDMIA below) carries the S-bit that tells
2685 // the CPU which world to return to on "BX LR" - no explicit branch needed.
2686#if STK_CORTEX_M_TRUSTZONE_FRAME
2687 "LDR r12, [r0], #4 \n" /* PSP_NS */
2688 "MSR PSP_NS, r12 \n"
2689 "LDR r12, [r0], #4 \n" /* CONTROL_NS */
2690 "MSR CONTROL_NS, r12 \n"
2691
2692 "LDR r12, [r0], #4 \n" /* PSPLIM */
2693 "MSR PSPLIM, r12 \n"
2694 "LDR r12, [r0], #4 \n" /* PSPLIM_NS */
2695 "MSR PSPLIM_NS, r12 \n"
2696#endif
2697
2698 // restore registers of active task's CPU context:
2699
2700#if STK_CORTEX_M_MANAGE_LR
2701 // restore r4-r11 and LR
2702 "LDMIA r0!, {r4-r11, LR}\n"
2703#else
2704 // note: LDMIA is limited to r0-r7 range, therefore load via stack memory
2705 "LDMIA r0!, {r4-r7} \n"
2706 "MOV r8, r4 \n"
2707 "MOV r9, r5 \n"
2708 "MOV r10, r6 \n"
2709 "MOV r11, r7 \n"
2710 "LDMIA r0!, {r4-r7} \n"
2711#endif
2712
2713#if STK_CORTEX_M_FPU
2714 // restore FP registers
2715 "TST LR, #16 \n" /* test LR for 0xffffffe_, e.g. Thread mode with FP data */
2716 "IT EQ \n" /* if result is positive */
2717 "VLDMIAEQ r0!, {s16-s31} \n" /* restore FP registers */
2718#endif
2719
2720 // Restore PSP.
2721 "MSR PSP, r0 \n"
2722
2723 // stop tracing
2724#if STK_SEGGER_SYSVIEW
2725 STK_SYSVIEW_CALL(StkSystemView_OnContextSwitch)
2726#endif
2727
2728 STK_ASM_ENABLE_INTERRUPTS " \n"
2729
2730 STK_ASM_EXIT_FROM_HANDLER " \n"
2731
2732 : /* output: none */
2733 : [access_mode_of] "i" (offsetof(Stack, access_mode))
2734#if (STK_ARCH_CPU_COUNT > 1U)
2735 #ifdef __ICCARM__
2736 , [st_idle] "r3" (p_st_idle)
2737 , [st_active] "r2" (p_st_active)
2738 #else
2739 , [st_idle] "r" (p_st_idle)
2740 , [st_active] "r" (p_st_active)
2741 #endif
2742#else
2743 , [st_idle] "r" (GetContext().m_stack_idle)
2744 , [st_active] "r" (GetContext().m_stack_active)
2745#endif
2746#if STK_MPU_STACK_GUARD
2747 , [mpu_start_of] "i" (&MPU->RBAR)
2748 , [mpu_reg_of] "i" (offsetof(Stack, mpu.region[0].addr))
2749 #if (STK_MPU_TASK_REGIONS > 4U)
2750 , [mpu_rnr_idx] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX)
2751 , [mpu_reg4_of] "i" (offsetof(Stack, mpu.region[4].addr))
2752 , [mpu_rnr_idx4] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 4U)
2753 #endif
2754 #if (STK_MPU_TASK_REGIONS > 8U)
2755 , [mpu_reg8_of] "i" (offsetof(Stack, mpu.region[8].addr))
2756 , [mpu_rnr_idx8] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 8U)
2757 #endif
2758 #if (STK_MPU_TASK_REGIONS > 12U)
2759 , [mpu_reg12_of] "i" (offsetof(Stack, mpu.region[12].addr))
2760 , [mpu_rnr_idx12] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 12U)
2761 #endif
2762 #if STK_CORTEX_M_TRUSTZONE_FRAME
2763 , [mpu_ns_start_of] "i" (&MPU_NS->RBAR)
2764 , [mpu_ns_reg_of] "i" (offsetof(Stack, mpu_ns.region[0].addr))
2765 #if (STK_MPU_TASK_REGIONS_NS > 4U)
2766 , [mpu_ns_rnr_idx] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS)
2767 , [mpu_ns_reg4_of] "i" (offsetof(Stack, mpu_ns.region[4].addr))
2768 , [mpu_ns_rnr_idx4] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 4U)
2769 #endif
2770 #if (STK_MPU_TASK_REGIONS_NS > 8U)
2771 , [mpu_ns_reg8_of] "i" (offsetof(Stack, mpu_ns.region[8].addr))
2772 , [mpu_ns_rnr_idx8] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 8U)
2773 #endif
2774 #if (STK_MPU_TASK_REGIONS_NS > 12U)
2775 , [mpu_ns_reg12_of] "i" (offsetof(Stack, mpu_ns.region[12].addr))
2776 , [mpu_ns_rnr_idx12] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 12U)
2777 #endif
2778 #endif
2779#endif
2780 : "r0" /* used as a scratchpad throughout */
2781#if STK_MPU_STACK_GUARD
2782 , "r1"
2783#endif
2784 , "memory"
2785#if (STK_CORTEX_M_TRUSTZONE_FRAME || STK_MPU_STACK_GUARD || STK_CORTEX_M_PRIVILEGE_FRAME)
2786 , "r12"
2787#endif
2788#if STK_CORTEX_M_FPU
2789 , "cc"
2790#endif
2791 );
2792}
2793
2794namespace stk {
2795__stk_attr_naked void OnTaskStart()
2796{
2797 // note: HW_DisableInterrupts() must be called prior calling this function
2798
2799 __asm volatile(
2801
2802 // set per-task MPU guard
2803#if STK_MPU_STACK_GUARD
2804 STK_ASM_BLOCK_MPU_STACK_GUARD
2805#endif
2806
2807 // load stack of the active task from GetContext().m_stack_active
2808 // note: keep in sync with OnTaskStart
2809 "LDR r0, [%[st_active]]\n" /* load the first member of Stack (Stack::SP) into r0, %[st_active] is a pointer value register */
2810
2811 // restore this task's CONTROL register as initialized by InitStack (see
2812 // PrivilegeFrame); lowest-addressed word of the saved frame, popped first
2813#if STK_CORTEX_M_PRIVILEGE_FRAME
2814 "LDR r12, [r0], %[access_mode_of] \n"
2815 "MSR CONTROL, r12 \n" /* note: on EXC_RETURN platform flushes instruction cache, therefore we omit explicit ISB */
2816#endif
2817
2818 // ARMv8-M TrustZone: restore TrustZoneFrame fields in reverse push order (see STK_PENDSV_HANDLER);
2819 // pop order: PSP_NS + CONTROL_NS first, then PSPLIM + PSPLIM_NS
2820#if STK_CORTEX_M_TRUSTZONE_FRAME
2821 "LDR r12, [r0], #4 \n" /* PSP_NS */
2822 "MSR PSP_NS, r12 \n"
2823 "LDR r12, [r0], #4 \n" /* CONTROL_NS */
2824 "MSR CONTROL_NS, r12 \n"
2825
2826 "LDR r12, [r0], #4 \n" /* PSPLIM */
2827 "MSR PSPLIM, r12 \n"
2828 "LDR r12, [r0], #4 \n" /* PSPLIM_NS */
2829 "MSR PSPLIM_NS, r12 \n"
2830#endif
2831
2832 // restore registers of active task's CPU context:
2833
2834#if STK_CORTEX_M_MANAGE_LR
2835 // restore r4-r11 and LR
2836 "LDMIA r0!, {r4-r11, LR}\n"
2837#else
2838 // note: LDMIA is limited to r0-r7 range, therefore load via stack memory
2839 "LDMIA r0!, {r4-r7} \n"
2840 "MOV r8, r4 \n"
2841 "MOV r9, r5 \n"
2842 "MOV r10, r6 \n"
2843 "MOV r11, r7 \n"
2844 "LDMIA r0!, {r4-r7} \n"
2845#endif
2846
2847#if STK_CORTEX_M_FPU
2848 // restore FP registers
2849 "TST LR, #16 \n" /* test LR for 0xffffffe_, e.g. Thread mode with FP data */
2850 "IT EQ \n" /* if result is positive */
2851 "VLDMIAEQ r0!, {s16-s31} \n" /* restore FP registers */
2852#endif
2853
2854 // restore PSP
2855 "MSR PSP, r0 \n"
2856
2857#if !STK_CORTEX_M_MANAGE_LR
2858 // M0: set LR to Thread mode, use PSP state and stack
2859 "LDR r0, =%[exc_ret] \n"
2860 "MOV LR, r0 \n"
2861#endif
2862
2863 STK_ASM_ENABLE_INTERRUPTS " \n"
2864
2865 STK_ASM_EXIT_FROM_HANDLER " \n"
2866
2867 : /* output: none */
2868 : [access_mode_of] "i" (offsetof(Stack, access_mode))
2869 , [st_active] "r" (GetContext().m_stack_active)
2870#if !STK_CORTEX_M_MANAGE_LR
2871 , [exc_ret] "i" (STK_CORTEX_M_EXC_RETURN_THREAD_PSP)
2872#endif
2873#if STK_MPU_STACK_GUARD
2874 , [mpu_start_of] "i" (&MPU->RBAR)
2875 , [mpu_reg_of] "i" (offsetof(Stack, mpu.region[0].addr))
2876 #if (STK_MPU_TASK_REGIONS > 4U)
2877 , [mpu_rnr_idx] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX)
2878 , [mpu_reg4_of] "i" (offsetof(Stack, mpu.region[4].addr))
2879 , [mpu_rnr_idx4] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 4U)
2880 #endif
2881 #if (STK_MPU_TASK_REGIONS > 8U)
2882 , [mpu_reg8_of] "i" (offsetof(Stack, mpu.region[8].addr))
2883 , [mpu_rnr_idx8] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 8U)
2884 #endif
2885 #if (STK_MPU_TASK_REGIONS > 12U)
2886 , [mpu_reg12_of] "i" (offsetof(Stack, mpu.region[12].addr))
2887 , [mpu_rnr_idx12] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 12U)
2888 #endif
2889 #if STK_CORTEX_M_TRUSTZONE_FRAME
2890 , [mpu_ns_start_of] "i" (&MPU_NS->RBAR)
2891 , [mpu_ns_reg_of] "i" (offsetof(Stack, mpu_ns.region[0].addr))
2892 #if (STK_MPU_TASK_REGIONS_NS > 4U)
2893 , [mpu_ns_rnr_idx] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS)
2894 , [mpu_ns_reg4_of] "i" (offsetof(Stack, mpu_ns.region[4].addr))
2895 , [mpu_ns_rnr_idx4] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 4U)
2896 #endif
2897 #if (STK_MPU_TASK_REGIONS_NS > 8U)
2898 , [mpu_ns_reg8_of] "i" (offsetof(Stack, mpu_ns.region[8].addr))
2899 , [mpu_ns_rnr_idx8] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 8U)
2900 #endif
2901 #if (STK_MPU_TASK_REGIONS_NS > 12U)
2902 , [mpu_ns_reg12_of] "i" (offsetof(Stack, mpu_ns.region[12].addr))
2903 , [mpu_ns_rnr_idx12] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 12U)
2904 #endif
2905 #endif
2906#endif
2907 : "r0" /* used as a scratchpad throughout */
2908#if STK_MPU_STACK_GUARD
2909 , "r1"
2910#endif
2911 , "memory"
2912#if (STK_CORTEX_M_TRUSTZONE_FRAME || STK_MPU_STACK_GUARD || STK_CORTEX_M_PRIVILEGE_FRAME)
2913 , "r12"
2914#endif
2915#if STK_CORTEX_M_FPU
2916 , "cc"
2917#endif
2918 );
2919}
2920} // namespace stk
2921
2922void Context::Start()
2923{
2924 m_exiting = false;
2925
2926 // enable FPU before SaveJmp as it references FPU with VMRS
2927 HW_EnableFullFpuAccess();
2928
2929 // save jump location of the Exit trap
2930 STK_UNUSED(SaveJmp(m_exit_buf));
2931 if (m_exiting)
2932 {
2933 // notify kernel about a full stop
2934 m_handler->OnStop();
2935 }
2936 else
2937 {
2938 HW_StartScheduler();
2939 }
2940}
2941
2942void Context::OnStart()
2943{
2944 // interrupts must be disabled at this point
2945 STK_ASSERT(HW_InterruptsDisabled());
2946
2947 // clear FPU usage status if FPU was used before kernel start
2948 HW_ClearFpuState();
2949
2950 // get the first active stack from the kernel
2951 m_handler->OnStart(m_stack_active);
2952
2953 // configure MPU
2954#if STK_MPU
2955 ConfigureMpu();
2956#endif
2957
2958 // set lowest priority for PendSV (SysTick priority is set in StartTickTimer)
2959 NVIC_SetPriority(PendSV_IRQn, STK_CORTEX_M_ISR_PRIORITY_LOWEST);
2960 // set highest priority for SVC interrupts to support critical section for unprivileged tasks
2961#ifdef CONTROL_nPRIV_Msk
2962 NVIC_SetPriority(SVCall_IRQn, STK_CORTEX_M_SVCALL_ISR_PRIORITY);
2963#endif
2964
2965 m_started = true;
2966
2967 // start recording task execution start
2968#if STK_SEGGER_SYSVIEW
2969 SEGGER_SYSVIEW_OnTaskStartExec(m_stack_active->tid);
2970#endif
2971
2972 // start with initially 1 elapsed tick (after timer expires), should be the last
2973 // in OnStart to provide full time window to the first starting task
2974 StartTickTimer(1);
2975}
2976
2977#if STK_TICKLESS_IDLE
2978Timeout Context::Suspend()
2979{
2980 Timeout sleep_ticks;
2981
2982 const uint32_t tick_resolution = GetTickResolutionInClockCycles();
2983 if (tick_resolution != 0U)
2984 {
2985 HW_DisableInterrupts();
2986
2987 // pause SysTick in order to read elapsed value
2988 HW_SysTickDisable();
2989
2990 // get already elapsed CPU cycles since SysTick ISR invocation up to SysTick timer stop (see above)
2991 // to account for them for a new period value
2992 const uint32_t elapsed = HW_SysTickElapsed(HW_SysTickValueAfterDisable());
2993
2994 // stop SysTick timer
2995 HW_SysTickStop();
2996
2997 // clear pending PendSV exception
2998 HW_ClearPendingSwitch();
2999
3000 // notify core about suspension (it will also yield currently active task forcibly)
3001 m_handler->OnSuspend(true);
3002
3003 // update tasks and out currently active task (if any) into a sleep, it will cause a switch
3004 // to a sleep trap after HW_EnableInterrupts, otherwise not
3005 Timeout no_sleep = 0;
3006 OnTick(no_sleep);
3007
3008 // get already elapsed ticks since the OnTick and a call to Suspend(), we shall account for this
3009 // period and return only the remainder
3010 const uint32_t elapsed_ticks = elapsed / tick_resolution;
3011 sleep_ticks = Max(m_sleep_ticks - static_cast<Timeout>(elapsed_ticks), static_cast<Timeout>(0));
3012
3013 HW_EnableInterrupts();
3014 }
3015 else
3016 {
3017 STK_ASSERT(false);
3018 sleep_ticks = NO_WAIT;
3019 }
3020
3021 return sleep_ticks;
3022}
3023#endif // STK_TICKLESS_IDLE
3024
3025#if STK_TICKLESS_IDLE
3026void Context::Resume(Timeout elapsed_ticks)
3027{
3028 HW_DisableInterrupts();
3029
3030 // notify core
3031 m_handler->OnSuspend(false);
3032
3033 // start with initially elapsed ticks (OnTick will fire with elapsed_ticks + 1)
3034 StartTickTimer(elapsed_ticks + 1);
3035
3036 HW_EnableInterrupts();
3037}
3038#endif // STK_TICKLESS_IDLE
3039
3040// __stk_attr_used required for Link-Time Optimization (-flto)
3041extern "C" __stk_attr_used void StkSVCHandlerMain(Word *svc_args)
3042{
3043 // start tracing
3044#if STK_SEGGER_SYSVIEW
3045 SEGGER_SYSVIEW_RecordEnterISR();
3046#endif
3047
3048 // Word is typedef uintptr_t (stk_common.h) - the only integer type the Standard
3049 // blesses for lossless pointer round-trips (MISRA C++ 5-2-8, CERT INT36-C)
3050 STK_STATIC_ASSERT_DESC_N(PTR, sizeof(Word) == sizeof(void *),
3051 "Word must be uintptr_t width for safe pointer round-trip via exc_frame->PC");
3052
3053 // priority 0 (NMI, HardFault) unaffected: SVC (priority 0 per OnStart()) remains
3054 // reachable so SVC_EXIT_CRITICAL can always unwind
3055 STK_STATIC_ASSERT_DESC_N(NVIC, __NVIC_PRIO_BITS < 32U,
3056 "NVIC priority bit width exceeds safe shift range");
3057
3058 // 'volatile': R0 is written back to stacked memory, compiler must not eliminate the store
3059 volatile hw::ExceptionFrame *const exc_frame = reinterpret_cast<volatile hw::ExceptionFrame *>(svc_args);
3060
3061 // details: https://developer.arm.com/documentation/ka004005/latest
3062 // Thumb SVC encoding: [15:8] = 0xDF, [7:0] = imm8
3063 // opcode lives two bytes (one Thumb halfword) before the stacked PC:
3064 const uint8_t *const insn_ptr = hw::WordToPtr<const uint8_t>(exc_frame->PC - 2U);
3065 const ESvcCommandId command = static_cast<ESvcCommandId>(*insn_ptr);
3066
3067 switch (command)
3068 {
3069 case SVC_START_SCHEDULING: {
3070 Context &ctx = GetContext();
3071
3072 // disallow duplicate attempt
3073 STK_ASSERT(!ctx.m_started);
3074 if (!ctx.m_started)
3075 {
3076 // make sure interrupts do not interfere, OnStart expects interrupts disabled
3077 HW_DisableInterrupts();
3078
3079 ctx.OnStart();
3080
3081 // start first task
3082 OnTaskStart();
3083 }
3084 break; }
3085
3086#if STK_CORTEX_M_FORCE_SWITCH
3087 case SVC_FORCE_SWITCH: {
3088 Context &ctx = GetContext();
3089
3090 STK_ASSERT(ctx.m_started);
3091
3092 ctx.OnForceContextSwitch(exc_frame->R0);
3093 break; }
3094#endif
3095
3096#if STK_MPU
3097 case SVC_BOOST_PRIV: {
3098 // limit access to Privilege escalation to STK_MPU_SHARED_CODE_SECTION functions only
3099 // which are read-only and immutable
3100 if ((exc_frame->PC >= hw::PtrToWord(__stk_mpu_shared_code_start)) &&
3101 (exc_frame->PC <= hw::PtrToWord(__stk_mpu_shared_code_end)))
3102 {
3103 __set_CONTROL(__get_CONTROL() & ~CONTROL_nPRIV_Msk);
3104 }
3105 else
3106 {
3108 }
3109 break; }
3110#endif // STK_MPU
3111
3112#ifdef CONTROL_nPRIV_Msk
3113 case SVC_ENTER_CRITICAL: {
3114 const uint32_t saved_basepri = __get_BASEPRI();
3115 __set_BASEPRI(static_cast<uint32_t>(1U) << __NVIC_PRIO_BITS); // mask all configurable-priority interrupts
3116 __DSB(); // BASEPRI write visible to bus before SVC return
3117 __ISB(); // pipeline flush: mask in effect at first caller instruction
3118
3119 GetContext().OnEnterCriticalSection(saved_basepri);
3120 break; }
3121
3122 case SVC_EXIT_CRITICAL: {
3123 uint32_t saved_basepri;
3124 if (GetContext().OnExitCriticalSection(saved_basepri))
3125 {
3126 __DSB(); // drain pending stores before widening interrupt window
3127 __set_BASEPRI(saved_basepri); // restore saved BASEPRI
3128 __ISB(); // pending interrupts at restored priority may fire now
3129 }
3130 break; }
3131#endif // CONTROL_nPRIV_Msk
3132
3133 default: {
3134 // any SVC number not in ESvcCommandId is a defect, panic unconditionally
3136 break; }
3137 }
3138
3139 // stop tracing
3140#if STK_SEGGER_SYSVIEW
3141 SEGGER_SYSVIEW_RecordExitISR();
3142#endif
3143}
3144
3145// details: "How to Write an SVC Function", https://developer.arm.com/documentation/ka004005/latest
3146extern "C" __stk_attr_naked void STK_SVC_HANDLER()
3147{
3148 __asm volatile(
3150 STK_ASM_GLOBAL_SYMBOL(StkSVCHandlerMain)
3151 STK_ASM_ALIGN_2 // ensure the entry point is aligned
3152
3154
3155 // even on Cortex-M3+, a long jump is safer when using LTO, we load address
3156 // into register to allow far jump (>2KB)
3157 "LDR r1, =StkSVCHandlerMain \n"
3158 "BX r1 \n"
3159
3160 STK_ASM_ALIGN_2 // ensure literal pool is aligned
3161 STK_ASM_POOL // ensure literal pool is reachable
3162 );
3163}
3164
3165#if (STK_USE_MEMMANAGE_HANDLER && defined(STK_MEMMANAGE_HANDLER)) ||\
3166 (STK_USE_HARDFAULT_HANDLER && defined(STK_HARDFAULT_HANDLER))
3167extern "C" __stk_attr_used
3168void StkExceptionHandlerMain(const Word *stacked_regs, Word exc_id)
3169{
3170 Word exc_return;
3171 __asm volatile ("MOV %0, LR" : "=r" (exc_return) :: "memory"); // EXC_RETURN, if not already clobbered
3172
3173 bool handled = false;
3174 Context &ctx = GetContext();
3175
3176 if (ctx.m_overrider != nullptr)
3177 {
3178 static FaultContext fault_ctx;
3179 fault_ctx.Fill(stacked_regs, exc_return);
3180
3181 handled = ctx.m_overrider->OnException(static_cast<EHwException>(exc_id), ctx.GetCurrentTId(),
3182 &fault_ctx);
3183 }
3184
3185 if (!handled)
3186 {
3187 // default handler of memory management exception:
3188
3189 // 1. break in Debugger
3191
3192 // 2. restart device
3193 NVIC_SystemReset();
3194 }
3195}
3196#endif // STK_USE_MEMMANAGE_HANDLER || STK_USE_HARDFAULT_HANDLER
3197
3198#if STK_USE_MEMMANAGE_HANDLER && defined(STK_MEMMANAGE_HANDLER)
3199extern "C" __stk_attr_naked void STK_MEMMANAGE_HANDLER()
3200{
3201 __asm volatile(
3203 STK_ASM_GLOBAL_SYMBOL(StkExceptionHandlerMain)
3205
3207
3208 "MOVS r1, %0 \n"
3209 "LDR r2, =StkExceptionHandlerMain \n"
3210 "BX r2 \n"
3211
3214 : /* no output */
3215 : "i" (HW_EXCEPT_MEMACCESS)
3216 : /* no clobber */
3217 );
3218}
3219#endif // STK_USE_MEMMANAGE_HANDLER
3220
3221#if STK_USE_HARDFAULT_HANDLER && defined(STK_HARDFAULT_HANDLER)
3222extern "C" __stk_attr_naked void STK_HARDFAULT_HANDLER()
3223{
3224 __asm volatile(
3226 STK_ASM_GLOBAL_SYMBOL(StkExceptionHandlerMain)
3228
3230
3231 "MOVS r1, %0 \n"
3232 "LDR r2, =StkExceptionHandlerMain \n"
3233 "BX r2 \n"
3234
3237 : /* no output */
3238 : "i" (HW_EXCEPT_FATAL)
3239 : /* no clobber */
3240 );
3241}
3242#endif // STK_USE_HARDFAULT_HANDLER
3243
3245void stk::OnTaskRun(ITask *runnable)
3246{
3247 STK_ASSERT(runnable != nullptr);
3248 runnable->Run();
3249}
3250
3252void stk::OnTaskExit()
3253{
3254 const Context &ctx = GetContext();
3255
3256 const uint32_t cs = HW_CriticalSectionStart();
3257
3258 ctx.m_handler->OnTaskExit(ctx.m_stack_active);
3259
3260 HW_CriticalSectionEnd(cs);
3261
3262 if (HW_IsPrivilegedContext())
3263 {
3264 for (;;)
3265 {
3266 // enter standby mode until time slot expires
3267 HW_EnterSleepMode();
3268 }
3269 }
3270 else
3271 {
3272 for (;;)
3273 {
3274 // can only busy-wait when non-Privileged
3275 __stk_relax_cpu();
3276 }
3277 }
3278}
3279
3281void stk::OnSchedulerSleep()
3282{
3283 // if hit here, increase the size of STK_SLEEP_TRAP_STACK_SIZE
3285
3286 for (;;)
3287 {
3288 // record Idle state
3289 #if STK_SEGGER_SYSVIEW
3290 SEGGER_SYSVIEW_OnIdle();
3291 #endif
3292
3293 HW_EnterSleepMode();
3294 }
3295}
3296
3298void stk::OnSchedulerSleepOverride()
3299{
3300 // if hit here, increase the size of STK_SLEEP_TRAP_STACK_SIZE
3302
3303 for (;;)
3304 {
3305 // record Idle state
3306 #if STK_SEGGER_SYSVIEW
3307 SEGGER_SYSVIEW_OnIdle();
3308 #endif
3309
3310 GetContext().OnSleepOverride();
3311 }
3312}
3313
3315void stk::OnSchedulerExit()
3316{
3317 __set_CONTROL(0U); // switch to MSP
3318 __set_PSP(0U); // clear PSP (for a clean register state)
3319
3320 // jump back to SaveJmp's return site with m_exiting already set to true
3321 RestoreJmp(GetContext().m_exit_buf, 0);
3322}
3323
3324void PlatformArmCortexM::Initialize(IEventHandler *event_handler, IKernelService *service, uint32_t resolution_us,
3325 Stack *exit_trap)
3326{
3327 GetContext().Initialize(event_handler, service, exit_trap, resolution_us);
3328}
3329
3331{
3332 GetContext().Start();
3333}
3334
3335#if STK_MPU_STACK_GUARD
3336static void ConfigureTaskMpu(IPlatform::IEventOverrider *overrider, Stack *stack, IStackMemory *stack_memory,
3337 ITask *user_task, bool non_secure)
3338{
3339#if !STK_CORTEX_M_TRUSTZONE_FRAME
3340 STK_UNUSED(non_secure);
3341#endif
3342
3343 MpuRegionConfig task_mpu_cfg[TaskMpu::NUM_REGIONS];
3344 size_t cfg_count = 0U;
3345
3346 // merge in up to (TaskMpu::NUM_REGIONS - 1) application-defined regions
3347 // (task-relative slots [+0..+NUM_REGIONS-2)]; user_task is null for the internal
3348 // sleep/exit trap stacks, which never carry application-defined regions
3349 if (user_task != nullptr)
3350 {
3351 // note: if task does not provide MPU regions the stack guard is not applied either
3352 // this approach avoids stack guard for Privileged tasks with MPU=MPU_CFG_PRIVILEGED_BG_MEM
3353 const MpuRegionList *const regions = user_task->GetMpuRegions();
3354 if (regions != nullptr)
3355 {
3356 // Prepare the stack guard bounds for the overlap check
3357 const MpuRegionConfig stack_guard_cfg =
3358 {
3359 .addr = hw::PtrToWord(stack_memory->GetStack()),
3360 .size = stack_memory->GetStackSize() * sizeof(Word),
3361 .access_perm = hw::mpu::EMpuAccess::ACCESS_FULL,
3362 .mem_type = hw::mpu::EMpuType::TYPE_NORMAL_CACHEABLE,
3363 .share = hw::mpu::EMpuShare::SHARE_NON,
3364 .exec = hw::mpu::EMpuExec::EXEC_NEVER
3365 };
3366
3367 const hw::mpu::RegionBounds bounds_s = hw::mpu::GetRegionBounds(stack_guard_cfg);
3368
3369 size_t regions_count = regions->GetSize();
3370 bool stack_guard_canceled = false;
3371
3372 for (size_t i = 0U; i < regions_count; ++i)
3373 {
3374 const hw::mpu::RegionBounds bounds_i = hw::mpu::GetRegionBounds((*regions)[i]);
3375
3376 // cancel stack region in favour of memory coverage by task's instance
3377 if (hw::mpu::IsOverlapping(bounds_s, bounds_i))
3378 {
3379 stack_guard_canceled = true;
3380 break;
3381 }
3382 }
3383
3384 // Reserve 1 slot for the stack guard at the end unless canceled
3385 const size_t max_user_regions = stack_guard_canceled ? TaskMpu::NUM_REGIONS : (TaskMpu::NUM_REGIONS - 1U);
3386
3387 STK_ASSERT(regions_count <= max_user_regions);
3388 if (regions_count > max_user_regions)
3389 {
3390 regions_count = max_user_regions; // defensively clamp in release builds
3391 }
3392
3393 // Populate initial slots with user-defined regions
3394 for (size_t i = 0U; i < regions_count; ++i)
3395 {
3396 task_mpu_cfg[cfg_count++] = (*regions)[i];
3397 }
3398
3399 // Append stack guard as the final slot if not canceled by an overlap
3400 if (!stack_guard_canceled)
3401 {
3402 task_mpu_cfg[cfg_count++] = stack_guard_cfg;
3403 }
3404 }
3405 }
3406
3407#if STK_CORTEX_M_TRUSTZONE_FRAME
3408 TaskMpu &task_mpu = (non_secure ? stack->mpu_ns : stack->mpu);
3409#else
3410 TaskMpu &task_mpu = stack->mpu;
3411#endif
3412
3413 // any slots beyond cfg_count are left disabled by ConfigureDynamic() itself
3414 hw::mpu::ConfigureDynamic(task_mpu, task_mpu_cfg, cfg_count, overrider, non_secure);
3415}
3416#endif // STK_MPU_STACK_GUARD
3417
3418#if STK_CORTEX_M_TRUSTZONE_FRAME
3419static void ConfigureTaskTrustZone(Stack *stack, IStackMemory *stack_memory, ITask *user_task, bool non_secure)
3420{
3421 // write TrustZoneFrame immediately above stack->SP (below the r4-r11 region);
3422 // frame layout in memory (low -> high address, matching STMDB push order in PendSV):
3423 // [SP+0] PSP_NS (pushed last by 2nd STMDB, popped first by 1st LDMIA)
3424 // [SP+4] CONTROL_NS
3425 // [SP+8] PSPLIM (pushed first by 1st STMDB, popped last by 2nd LDMIA)
3426 // [SP+12] PSPLIM_NS
3427 // TrustZoneFrame sits one word above Stack::SP when PrivilegeFrame is present,
3428 // since PrivilegeFrame::CONTROL occupies the lowest word of the saved region
3429 TrustZoneFrame *const tz_frame = hw::WordToPtr<TrustZoneFrame>(stack->SP + (STK_CORTEX_M_PRIV_REGISTER_COUNT * sizeof(Word)));
3430 tz_frame->PSP_NS = 0U;
3431 tz_frame->CONTROL_NS = hw::reg::CONTROL::DEFAULT_INIT;
3432 tz_frame->PSPLIM = 0U; // unlimited for Secure task
3433 tz_frame->PSPLIM_NS = 0U; // unlimited for Secure task
3434
3435 if (non_secure)
3436 {
3437 // instance points to non-secure stack memory, secure memory is obtained by GetSecureStackMemory
3438 IStackMemory *ns_stack_memory = user_task;
3439
3440 // initialize stack memory
3441 const Word ns_stack_top = Context::InitStackMemory(ns_stack_memory);
3442
3443 // NS thread Privilege status
3444 tz_frame->CONTROL_NS = (((stack->access_mode & ACCESS_PRIVILEGED) != 0U) ?
3445 hw::reg::CONTROL::SetPrivileged(tz_frame->CONTROL_NS) :
3446 hw::reg::CONTROL::SetUnprivileged(tz_frame->CONTROL_NS));
3447
3448 // NS thread is using PSP_NS
3449 tz_frame->CONTROL_NS = hw::reg::CONTROL::SetSPSelectionToPSP(tz_frame->CONTROL_NS);
3450
3451 // for a fresh NS task, PSP_NS starts at the top of its NS stack
3452 // (the hardware exception frame was placed there by the caller)
3453 tz_frame->PSP_NS = ns_stack_top;
3454
3455 // bottom of NS stack
3456 tz_frame->PSPLIM_NS = hw::PtrToWord(ns_stack_memory->GetStack());
3457
3458 // bottom of S stack
3459 tz_frame->PSPLIM = hw::PtrToWord(stack_memory->GetStack());
3460 }
3461 else
3462 {
3463 stack->access_mode |= ACCESS_SECURE;
3464
3465 // bottom of S stack
3466 tz_frame->PSPLIM = hw::PtrToWord(stack_memory->GetStack());
3467 }
3468}
3469#endif // STK_CORTEX_M_TRUSTZONE_FRAME
3470
3471void PlatformArmCortexM::InitStack(EStackType stack_type, Stack *stack, IStackMemory *stack_memory, ITask *user_task)
3472{
3474 "ExceptionFrame layout must match the ARMv7-M hardware exception frame exactly");
3475 STK_ASSERT(stack_memory->GetStackSize() > STK_CORTEX_M_TOTAL_REGISTER_COUNT);
3476
3477 const Context &ctx = GetContext();
3478
3479#ifdef _STK_CORTEX_M_TRUSTZONE
3480 bool is_non_secure_task = false;
3481
3482 // Replace stack_memory of Non-Secure task with a Secure memory which is required for launching the task
3483 // and Secure operations via NSC veneer.
3484 if ((user_task != nullptr) && (user_task->GetSecureStackMemory() != nullptr))
3485 {
3486 stack_memory = user_task->GetSecureStackMemory();
3487 is_non_secure_task = true;
3488
3489 STK_ASSERT(stack_memory->GetStackSize() > STK_CORTEX_M_TOTAL_REGISTER_COUNT);
3490 }
3491#endif
3492
3493 // initialize stack memory
3494 const Word stack_top = Context::InitStackMemory(stack_memory);
3495
3496 // initialize Stack Pointer (SP)
3497 stack->SP = stack_top - (STK_CORTEX_M_TOTAL_REGISTER_COUNT * sizeof(Word));
3498
3499 // place the initial task frame flush against the top of the stack:
3500 // TaskFrame::exc (ExceptionFrame) occupies the top 8 words, TaskFrame::EXC_RETURN
3501 // (when present) sits immediately below it, and TrustZoneFrame (when present) sits
3502 // below that
3503 TaskFrame *const task_frame = hw::WordToPtr<TaskFrame>(stack_top - sizeof(TaskFrame));
3504
3505 // initialize registers for the user task's first start
3506 switch (stack_type)
3507 {
3508 case STACK_USER_TASK: {
3509 task_frame->exc.PC = hw::PtrToWord(&OnTaskRun);
3510 task_frame->exc.LR = hw::PtrToWord(&OnTaskExit);
3511 task_frame->exc.R0 = hw::PtrToWord(user_task);
3512 break; }
3513
3514 case STACK_SLEEP_TRAP: {
3515 task_frame->exc.PC = hw::PtrToWord(ctx.m_overrider != nullptr ? &OnSchedulerSleepOverride : &OnSchedulerSleep);
3516 task_frame->exc.LR = STK_STACK_MEMORY_FILLER; // should not attempt to exit
3517 task_frame->exc.R0 = 0U;
3518 break; }
3519
3520 case STACK_EXIT_TRAP: {
3521 task_frame->exc.PC = hw::PtrToWord(&OnSchedulerExit);
3522 task_frame->exc.LR = STK_STACK_MEMORY_FILLER; // should not attempt to exit
3523 task_frame->exc.R0 = 0U;
3524 break; }
3525
3526 default: {
3528 break; }
3529 }
3530
3531 // ensure the Program Counter is properly aligned to halfword boundaries
3532 // by clearing the Thumb state tracking bit from the physical target address
3533 task_frame->exc.PC = hw::reg::PC::ClearThumbBit(task_frame->exc.PC);
3534
3535 // initialize the Execution Program Status Register (EPSR) with the T-bit enabled,
3536 // which is required for all ARM Cortex-M processors to execute instructions
3537 task_frame->exc.xPSR = hw::reg::XPSR::SetThumbExecution(hw::reg::XPSR::DEFAULT_INIT);
3538
3539#if STK_CORTEX_M_MANAGE_LR
3540 // set the EXC_RETURN value to target Thread Mode using the Process Stack Pointer (PSP)
3541 // Note for TrustZone configurations: Execution will return to the Non-Secure state
3542 // via a Secure-side trampoline, which invokes the task's entry/ITask::Run function
3543 // using a dedicated Non-Secure function call (__BXNS / Non-Secure callable boundary)
3544 task_frame->EXC_RETURN = STK_CORTEX_M_EXC_RETURN_THREAD_PSP;
3545#endif // STK_CORTEX_M_MANAGE_LR
3546
3547#if STK_CORTEX_M_PRIVILEGE_FRAME
3548 // initial CONTROL for this task's first start: nPRIV reflects the task's
3549 // static access_mode. SPSEL is fixed up by hardware from EXC_RETURN on the
3550 // first exception return; FPCA/SFPA start clear. This word sits at the very
3551 // bottom of the saved frame (== stack->SP) and is popped first by
3552 // OnTaskStart/STK_PENDSV_HANDLER (see PrivilegeFrame)
3553 {
3554 PrivilegeFrame *const priv_frame = hw::WordToPtr<PrivilegeFrame>(stack->SP);
3555
3556 priv_frame->CONTROL = (((stack->access_mode & ACCESS_PRIVILEGED) != 0U) ?
3557 hw::reg::CONTROL::SetPrivileged(hw::reg::CONTROL::DEFAULT_INIT) :
3558 hw::reg::CONTROL::SetUnprivileged(hw::reg::CONTROL::DEFAULT_INIT));
3559 }
3560#endif
3561
3562 // configure per-task TrustZone hardware frames and boundaries
3563#if STK_CORTEX_M_TRUSTZONE_FRAME
3564 ConfigureTaskTrustZone(stack, stack_memory, user_task, is_non_secure_task);
3565#endif
3566
3567 // configure per-task MPU
3568#if STK_MPU_STACK_GUARD
3569 ConfigureTaskMpu(ctx.m_overrider, stack, stack_memory, user_task, false);
3570 #if STK_CORTEX_M_TRUSTZONE_FRAME
3571 if (is_non_secure_task)
3572 {
3573 ConfigureTaskMpu(ctx.m_overrider_ns, stack, user_task, user_task, true);
3574 }
3575 #endif
3576#endif
3577}
3578
3579// ---------------------------------------------------------------------------
3580// ARMv8-M TrustZone Non-Secure callable (NSC) gateway veneers.
3581// ---------------------------------------------------------------------------
3582
3583// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3584#ifdef _STK_CORTEX_M_TRUSTZONE
3585// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3586
3590hw::CriticalSection::Session NSC_stk_hw_CriticalSection_Enter(const hw::CriticalSection::Session ses)
3591{
3592 return hw::CriticalSection::Enter(ses);
3593}
3594
3598void NSC_stk_hw_CriticalSection_Exit(const hw::CriticalSection::Session ses)
3599{
3601}
3602
3606void NSC_stk_hw_SpinLock_Lock(hw::SpinLock *sl)
3607{
3608 if ((sl != nullptr) && (cmse_check_pointed_object(sl, CMSE_NONSECURE) != nullptr))
3609 {
3610 sl->Lock();
3611 }
3612}
3613
3617void NSC_stk_hw_SpinLock_Unlock(hw::SpinLock *sl)
3618{
3619 if ((sl != nullptr) && (cmse_check_pointed_object(sl, CMSE_NONSECURE) != nullptr))
3620 {
3621 sl->Unlock();
3622 }
3623}
3624
3628bool NSC_stk_hw_SpinLock_TryLock(hw::SpinLock *sl)
3629{
3630 bool locked;
3631
3632 if ((sl != nullptr) && (cmse_check_pointed_object(sl, CMSE_NONSECURE) != nullptr))
3633 {
3634 locked = sl->TryLock();
3635 }
3636 else
3637 {
3638 locked = false;
3639 }
3640
3641 return locked;
3642}
3643
3647uint32_t NSC_stk_hw_HiResClock_GetFrequency()
3648{
3650}
3651
3655stk::Cycles NSC_stk_hw_HiResClock_GetCycles()
3656{
3658}
3659
3663bool NSC_stk_hw_IsInsideISR()
3664{
3665 return hw::IsInsideISR();
3666}
3667
3671TId NSC_stk_debug_GetCurrentTId()
3672{
3673 return GetContext().GetCurrentTId();
3674}
3675
3676// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3677#endif // _STK_CORTEX_M_TRUSTZONE
3678// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3679
3680void Context::OnStop()
3681{
3682#if STK_SEGGER_SYSVIEW
3683 SEGGER_SYSVIEW_Stop();
3684#endif
3685
3686 // stop SysTick timer
3687 HW_SysTickStop();
3688
3689 // clear pending PendSV exception
3690 HW_ClearPendingSwitch();
3691
3692 m_started = false;
3693 m_exiting = true;
3694
3695 // make sure all assignments are set and executed
3696 __DSB();
3697 __ISB();
3698}
3699
3701{
3702 GetContext().OnStop();
3703
3704 // load context of the Exit trap
3705 HW_DisableInterrupts();
3706 OnTaskStart();
3707}
3708
3710{
3711 return GetContext().m_tick_resolution;
3712}
3713
3715{
3716 return static_cast<Cycles>(HW_SysTickValue());
3717}
3718
3720{
3721 return HW_CoreClockFrequency();
3722}
3723
3725{
3726 GetContext().m_handler->OnTaskSwitch(HW_GetCallerSP());
3727}
3728
3730{
3731 HW_ForceContextSwitch(id);
3732}
3733
3735{
3736 GetContext().m_handler->OnTaskSleep(HW_GetCallerSP(), ticks);
3737}
3738
3740{
3741 return GetContext().m_handler->OnTaskSleepUntil(HW_GetCallerSP(), timestamp);
3742}
3743
3745{
3746 return GetContext().m_handler->OnTaskWait(HW_GetCallerSP(), sync_obj, mutex, timeout);
3747}
3748
3750{
3751 TId result;
3752 const Word isr = HW_GetCurrentException();
3753
3754 // return special TId which denotes ISR
3755 if (isr != 0U)
3756 {
3757 const TId isr_tid = (TID_ISR_N | isr);
3758 STK_ASSERT(IsIsrTid(isr_tid));
3759 result = isr_tid;
3760 }
3761 else
3762 {
3763 const Context &ctx = GetContext();
3764
3765 if (ctx.m_started)
3766 {
3767 result = ctx.m_handler->OnGetTid(HW_GetCallerSP());
3768 }
3769 else
3770 {
3771 result = TID_NONE;
3772 }
3773 }
3774
3775 return result;
3776}
3777
3779{
3780 bool is_handled = false;
3781 const Context &ctx = GetContext();
3782
3783 if (ctx.m_overrider != nullptr)
3784 {
3785 is_handled = ctx.m_overrider->OnHardFault();
3786 }
3787
3788 if (!is_handled)
3789 {
3791 }
3792}
3793
3794void PlatformArmCortexM::SetEventOverrider(IEventOverrider *overrider, bool non_secure)
3795{
3796 Context &ctx = GetContext();
3797
3798 STK_ASSERT(!ctx.m_started);
3799
3800#if STK_CORTEX_M_TRUSTZONE_FRAME
3801 if (non_secure)
3802 {
3803 ctx.m_overrider_ns = overrider;
3804 }
3805 else
3806#else
3807 STK_UNUSED(non_secure);
3808#endif
3809 {
3810 ctx.m_overrider = overrider;
3811 }
3812}
3813
3815{
3816 return HW_GetCallerSP();
3817}
3818
3820{
3821#if STK_TICKLESS_IDLE
3822 return GetContext().Suspend();
3823#else
3824 return 0;
3825#endif
3826}
3827
3828void PlatformArmCortexM::Resume(Timeout elapsed_ticks)
3829{
3830#if STK_TICKLESS_IDLE
3831 GetContext().Resume(elapsed_ticks);
3832#else
3833 STK_UNUSED(elapsed_ticks);
3834#endif
3835}
3836
3837void PlatformArmCortexM::SetCpuFrequency(uint8_t core_id, uint32_t frequency)
3838{
3840
3841 if (core_id == 0xFFU)
3842 {
3843 for (uint8_t i = 0U; i < STK_STATIC_ARRAY_SIZE(s_StkSystemCoreClock); ++i)
3844 {
3845 s_StkSystemCoreClock[i] = frequency;
3846 }
3847 }
3848 else
3849 {
3850 STK_ASSERT(core_id < STK_STATIC_ARRAY_SIZE(s_StkSystemCoreClock));
3851
3852 if (core_id < STK_STATIC_ARRAY_SIZE(s_StkSystemCoreClock))
3853 {
3854 s_StkSystemCoreClock[core_id] = frequency;
3855 }
3856 }
3857}
3858
3859#if STK_MPU
3873static class KernelServiceSvcProxy final : public stk::IKernelService
3874{
3875public:
3878 explicit KernelServiceSvcProxy() : IKernelService()
3879 {}
3880
3884 STK_VIRT_DTOR ~KernelServiceSvcProxy() = default;
3885
3886 stk::TId GetTid() const override;
3887 stk::Ticks GetTicks() const override;
3888 uint32_t GetTickResolution() const override;
3889 stk::Cycles GetSysTimerCount() const override;
3890 uint32_t GetSysTimerFrequency() const override;
3891 void Delay(stk::Timeout ticks) override;
3892 void Sleep(stk::Timeout ticks) override;
3893 bool SleepUntil(stk::Ticks timestamp) override;
3894 void SleepCancel(stk::TId task_id) override;
3895 void SwitchToNext() override;
3896 stk::EWaitResult Wait(stk::ISyncObject *sobj, stk::IMutex *mutex, stk::Timeout timeout) override;
3897 void Wake(stk::ISyncObject *sobj, bool all);
3898 stk::Timeout Suspend() override;
3899 void Resume(stk::Timeout elapsed_ticks) override;
3900 void InheritWeight(stk::TId tid, stk::Weight weight) override;
3901 void RestoreWeight(stk::TId tid, stk::ISyncObject *sobj) override;
3902}
3906s_StkKernelServiceUnprivProxy;
3907
3909stk::TId KernelServiceSvcProxy::GetTid() const
3910{
3911 const ScopedPrivilegeBoost pb;
3912 return GetContext().m_service->GetTid();
3913}
3914
3916stk::Ticks KernelServiceSvcProxy::GetTicks() const
3917{
3918 const ScopedPrivilegeBoost pb;
3919 return GetContext().m_service->GetTicks();
3920}
3921
3923uint32_t KernelServiceSvcProxy::GetTickResolution() const
3924{
3925 const ScopedPrivilegeBoost pb;
3926 return GetContext().m_tick_resolution;
3927}
3928
3930stk::Cycles KernelServiceSvcProxy::GetSysTimerCount() const
3931{
3932 const ScopedPrivilegeBoost pb;
3933 return GetContext().m_service->GetSysTimerCount();
3934}
3935
3937uint32_t KernelServiceSvcProxy::GetSysTimerFrequency() const
3938{
3939 const ScopedPrivilegeBoost pb;
3940 return GetContext().m_service->GetSysTimerFrequency();
3941}
3942
3944void KernelServiceSvcProxy::Delay(stk::Timeout ticks)
3945{
3946 const ScopedPrivilegeBoost pb;
3947 GetContext().m_service->Delay(ticks);
3948}
3949
3951void KernelServiceSvcProxy::Sleep(stk::Timeout ticks)
3952{
3953 const ScopedPrivilegeBoost pb;
3954 GetContext().m_service->Sleep(ticks);
3955}
3956
3958bool KernelServiceSvcProxy::SleepUntil(stk::Ticks timestamp)
3959{
3960 const ScopedPrivilegeBoost pb;
3961 return GetContext().m_service->SleepUntil(timestamp);
3962}
3963
3965void KernelServiceSvcProxy::SleepCancel(stk::TId task_id)
3966{
3967 const ScopedPrivilegeBoost pb;
3968 GetContext().m_service->SleepCancel(task_id);
3969}
3970
3972void KernelServiceSvcProxy::SwitchToNext()
3973{
3974 const ScopedPrivilegeBoost pb;
3975 GetContext().m_service->SwitchToNext();
3976}
3977
3979stk::EWaitResult KernelServiceSvcProxy::Wait(stk::ISyncObject *sobj, stk::IMutex *mutex, stk::Timeout timeout)
3980{
3981 const ScopedPrivilegeBoost pb;
3982 return GetContext().m_service->Wait(sobj, mutex, timeout);
3983}
3984
3986void KernelServiceSvcProxy::Wake(stk::ISyncObject *sobj, bool all)
3987{
3988 const ScopedPrivilegeBoost pb;
3989 GetContext().m_service->Wake(sobj, all);
3990}
3991
3993stk::Timeout KernelServiceSvcProxy::Suspend()
3994{
3995 return 0; // not allowed for non-privileged process
3996}
3997
3999void KernelServiceSvcProxy::Resume(stk::Timeout elapsed_ticks)
4000{
4001 // not allowed for non-privileged process
4002 STK_UNUSED(elapsed_ticks);
4003}
4004
4006void KernelServiceSvcProxy::InheritWeight(stk::TId tid, stk::Weight weight)
4007{
4008 const ScopedPrivilegeBoost pb;
4009 GetContext().m_service->InheritWeight(tid, weight);
4010}
4011
4013void KernelServiceSvcProxy::RestoreWeight(stk::TId tid, stk::ISyncObject *sobj)
4014{
4015 const ScopedPrivilegeBoost pb;
4016 GetContext().m_service->RestoreWeight(tid, sobj);
4017}
4018#endif // STK_MPU
4019
4021{
4022 IKernelService *service;
4023
4024#if STK_MPU
4025 if (HW_IsPrivilegedContext() || HW_IsHandlerMode())
4026 {
4027 service = GetContext().m_service;
4028 }
4029 else
4030 {
4031 service = &s_StkKernelServiceUnprivProxy;
4032 }
4033#else
4034 service = GetContext().m_service;
4035#endif
4036
4037 return service;
4038}
4039
4042{
4044
4045 const bool is_priv_ses = ((ses & SESSION_FLAG_NPRIV) == 0U);
4046 const bool is_priv_ctx = HW_IsPrivilegedContext();
4047 const bool is_handler = HW_IsHandlerMode();
4048
4049 if (is_priv_ses && (is_priv_ctx || is_handler))
4050 {
4051 GetContext().EnterCriticalSection();
4052 ret = SESSION_FLAG_NONE;
4053 }
4054 else
4055 {
4056 HW_UnprivEnterCriticalSection();
4057 ret = SESSION_FLAG_NPRIV;
4058 }
4059
4060 return ret;
4061}
4062
4065{
4066 if ((ses & SESSION_FLAG_NPRIV) == 0U)
4067 {
4068 GetContext().ExitCriticalSection();
4069 }
4070 else
4071 {
4072 HW_UnprivExitCriticalSection();
4073 }
4074}
4075
4078{
4079 HW_SpinLockLock(m_lock);
4080}
4081
4084{
4085 HW_SpinLockUnlock(m_lock);
4086}
4087
4090{
4091 return HW_SpinLockTryLock(m_lock);
4092}
4093
4096{
4097 return HW_IsHandlerMode();
4098}
4099
4102{
4103 return HW_IsPrivilegedContext();
4104}
4105
4108{
4109 return HiResClockImpl::GetInstance()->GetCycles();
4110}
4111
4114{
4115 const uint32_t freq = HiResClockImpl::GetInstance()->GetFrequency();
4116 STK_ASSERT(freq != 0U);
4117 return freq;
4118}
4119
4120#if STK_TLS && !STK_INLINE_TLS
4121Word stk::hw::GetTls()
4122{
4123 return GetContext().GetTls();
4124}
4125
4126void stk::hw::SetTls(Word tp)
4127{
4128 GetContext().SetTls(tp);
4129}
4130#endif // STK_TLS && !STK_INLINE_TLS
4131
4132// ----------------------------------------------------------------------------
4133// MPU management API
4134// ----------------------------------------------------------------------------
4135
4136// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4137#if STK_MPU
4138// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4139
4140void hw::mpu::ConfigureRegion(MpuRegion &reg, const struct MpuRegionConfig &cfg, uint32_t region_idx)
4141{
4142#if STK_ARCH_ARMV8_M
4143 if ((cfg.size != 0U) && (cfg.access_perm != EMpuAccess::ACCESS_NONE))
4144 {
4145 // hardware rules: both Base and Limit addresses must be 32-byte aligned.
4146 STK_ASSERT((cfg.addr & 31U) == 0U);
4147 STK_ASSERT((cfg.size & 31U) == 0U);
4148
4149 // calculate RBAR: Base address | Shareability [4:3] | AP permissions [2:1] | Execution state [0]
4150 reg.addr = (cfg.addr & ~31U) |
4151 static_cast<uint32_t>(cfg.share) |
4152 static_cast<uint32_t>(cfg.access_perm) |
4153 static_cast<uint32_t>(cfg.exec);
4154
4155 // calculate RLAR: Final 32-byte chunk base address | MAIR allocation index [3:1] | Enable Flag [0]
4156 // find the absolute last byte address within the protected region
4157 const uint32_t end_byte_addr = cfg.addr + cfg.size - 1U;
4158 // clear the bottom 5 bits to isolate the base of the final 32-byte window
4159 const uint32_t limit_addr = end_byte_addr & ~31U;
4160 reg.attr = limit_addr | (static_cast<uint32_t>(cfg.mem_type) << 1U) | hw::mpu::RLAR_ENABLE_FLAG;
4161 }
4162 else
4163 {
4164 // disabled region
4165 reg.addr = hw::mpu::RBAR_DISABLED_REGION(region_idx);
4166 reg.attr = hw::mpu::RLAR_DISABLED_REGION;
4167 }
4168#else
4169 // legacy ARMv7-M Attribute layout
4170 if ((cfg.size != 0U) && (cfg.access_perm != EMpuAccess::ACCESS_NONE))
4171 {
4172 // hardware rules: must be a strict power-of-two size, naturally aligned
4173 STK_ASSERT((cfg.size & (cfg.size - 1U)) == 0U);
4174 STK_ASSERT((cfg.addr & (cfg.size - 1U)) == 0U);
4175
4176 // active ARMv7-M Base layout: masked completely by its structural size constraint
4177 reg.addr = (cfg.addr & ~(cfg.size - 1U)) | (1U << 4U) | (region_idx & 0xFU);
4178
4179 // active Region
4180 const uint32_t size_field = static_cast<uint32_t>(31U - CountLeadingZeros(cfg.size)) - 1U;
4181
4182 // defensive masking: Protect bits [26:24] against sentinel values or dirty enum extensions
4183 const uint32_t ap_bits = static_cast<uint32_t>(cfg.access_perm) & 0x07000000U;
4184
4185 const uint32_t attr = (ap_bits |
4186 static_cast<uint32_t>(cfg.share) |
4187 static_cast<uint32_t>(cfg.mem_type) |
4188 static_cast<uint32_t>(cfg.exec));
4189
4190 reg.attr = attr | (size_field << 1U) | hw::mpu::RASR_ENABLE_FLAG;
4191 }
4192 else
4193 {
4194 // disabled region: clear out base address entirely to prevent false matches,
4195 // point to the target hardware index slot and strip the RASR enable flag
4196 reg.addr = hw::mpu::RBAR_DISABLED_REGION(region_idx);
4197 reg.attr = hw::mpu::RASR_DISABLED_REGION;
4198 }
4199#endif
4200}
4201
4202void hw::mpu::ApplyRegion(const MpuRegion &reg, uint32_t index, bool non_secure)
4203{
4204#if STK_ARCH_ARMV8_M && !STK_TZ_NON_SECURE
4205 MPU_Type *const MPU_ptr = (non_secure ? MPU_NS : MPU);
4206#else
4207 MPU_Type *const MPU_ptr = MPU;
4208 STK_UNUSED(non_secure);
4209#endif
4210
4211 MPU_ptr->RNR = index;
4212 __DSB();
4213 __ISB();
4214
4215 MPU_ptr->RBAR = reg.addr;
4216#if STK_ARCH_ARMV8_M
4217 MPU_ptr->RLAR = reg.attr;
4218#else
4219 MPU_ptr->RASR = reg.attr;
4220#endif
4221}
4222
4223void hw::mpu::Enable(bool enable, uint32_t control_flags, bool non_secure)
4224{
4225 __stk_dmb();
4226
4227#if STK_ARCH_ARMV8_M && !STK_TZ_NON_SECURE
4228 MPU_Type *const MPU_ptr = (non_secure ? MPU_NS : MPU);
4229 SCB_Type *const SCB_ptr = (non_secure ? SCB_NS : SCB);
4230#else
4231 MPU_Type *const MPU_ptr = MPU;
4232 SCB_Type *const SCB_ptr = SCB;
4233 STK_UNUSED(non_secure);
4234#endif
4235
4236 if (enable)
4237 {
4238 #ifdef SCB_SHCSR_MEMFAULTENA_Msk
4239 SCB_ptr->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
4240 #endif
4241 MPU_ptr->CTRL = control_flags | MPU_CTRL_ENABLE_Msk;
4242 }
4243 else
4244 {
4245 #ifdef SCB_SHCSR_MEMFAULTENA_Msk
4246 SCB_ptr->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;
4247 #endif
4248 MPU_ptr->CTRL &= ~MPU_CTRL_ENABLE_Msk;
4249 }
4250
4251 __DSB();
4252 __ISB();
4253}
4254
4255void hw::mpu::DisableRegion(uint32_t index, bool non_secure)
4256{
4257#if STK_ARCH_ARMV8_M && !STK_TZ_NON_SECURE
4258 MPU_Type *const MPU_ptr = (non_secure ? MPU_NS : MPU);
4259#else
4260 MPU_Type *const MPU_ptr = MPU;
4261 STK_UNUSED(non_secure); // no Secure/Non-Secure MPU alias on ARMv7-M
4262#endif
4263
4264 MPU_ptr->RNR = index;
4265
4266#if STK_ARCH_ARMV8_M
4267 MPU_ptr->RLAR = hw::mpu::RLAR_DISABLED_REGION;
4268#else
4269 MPU_ptr->RASR = hw::mpu::RASR_DISABLED_REGION;
4270#endif
4271}
4272
4273void hw::mpu::ConfigureStatic(const MpuRegionConfig cfg_list[], size_t cfg_count, uint32_t control_flags,
4274 bool non_secure)
4275{
4276 STK_ASSERT((cfg_list != nullptr) || (cfg_count == 0U));
4277
4278 const size_t region_idx = (non_secure ? STK_CORTEX_M_MPU_TASK_REGION_IDX_NS : STK_CORTEX_M_MPU_TASK_REGION_IDX);
4279 STK_ASSERT((cfg_count <= region_idx) || (region_idx == 0U));
4280
4281 // validate static table configuration against internal overlaps before writing hardware
4282 ValidateNoOverlaps(nullptr, 0U, cfg_list, cfg_count);
4283
4284 Enable(false, 0U, non_secure);
4285
4286 // cfg_list[i] always targets hardware region i: static regions occupy a fixed,
4287 // contiguous block starting at region 0, so the index is derived from array
4288 // position rather than being supplied by the caller
4289 size_t index = 0U;
4290 for (; index < cfg_count; ++index)
4291 {
4292 const MpuRegionConfig &cfg = cfg_list[index];
4293
4294 MpuRegion reg;
4295 ConfigureRegion(reg, cfg, static_cast<uint32_t>(index));
4296 ApplyRegion(reg, static_cast<uint32_t>(index), non_secure);
4297 }
4298
4299 // disable every remaining static-region slot so a shorter table than a previous
4300 // call can never leave stale configuration active in hardware; callers no longer
4301 // need to supply placeholder/empty entries just to occupy unused regions
4302 for (; index < region_idx; ++index)
4303 {
4304 MpuRegion reg;
4305 ConfigureRegion(reg, s_StkDisabledMpuRegion, static_cast<uint32_t>(index));
4306 ApplyRegion(reg, static_cast<uint32_t>(index), non_secure);
4307 }
4308
4309 // configure memory attributes and point to the start of the per-task region beginning
4310 // note: ARMv7-M does not require RNR to be set as region index is embedded into RBAR,
4311 // though we do it for a consistency with ARMv8-M here
4312 {
4313 #if STK_ARCH_ARMV8_M && STK_TZ_SECURE
4314 MPU_Type *const MPU_ptr = (non_secure ? MPU_NS : MPU);
4315 #else
4316 MPU_Type *const MPU_ptr = MPU;
4317 STK_UNUSED(non_secure); // no Secure/Non-Secure MPU alias on ARMv7-M
4318 #endif
4319
4320 #if STK_ARCH_ARMV8_M
4321 // configure memory attributes
4322 MPU_ptr->MAIR0 = MAIR0_PMSAV8_INIT;
4323 MPU_ptr->MAIR1 = MAIR1_PMSAV8_INIT;
4324 #endif
4325
4326 // point to the start of the per-task config area
4327 MPU_ptr->RNR = region_idx;
4328 }
4329
4330 Enable(true, control_flags, non_secure);
4331}
4332
4333void hw::mpu::ConfigureDynamic(TaskMpu &task_mpu, const struct MpuRegionConfig cfg_list[],
4334 const size_t cfg_count, IPlatform::IEventOverrider *overrider, bool non_secure)
4335{
4336 STK_ASSERT((cfg_list != nullptr) || (cfg_count == 0U));
4337
4338 const size_t region_idx = (non_secure ? STK_CORTEX_M_MPU_TASK_REGION_IDX_NS : STK_CORTEX_M_MPU_TASK_REGION_IDX);
4339 const size_t task_mpu_regions = (non_secure ? TaskMpuNs::NUM_REGIONS : TaskMpu::NUM_REGIONS);
4340 STK_ASSERT(cfg_count <= task_mpu_regions);
4341
4342 const size_t clamped_count = Min(cfg_count, task_mpu_regions);
4343
4344 // retrieve static regions directly via overrider getter
4345 MpuRegionList static_regions(nullptr, 0);
4346 if (overrider != nullptr)
4347 {
4348 const MpuConfig *const cfg = overrider->OnConfigureMpu();
4349 if (cfg != nullptr)
4350 {
4351 static_regions = cfg->regions;
4352 }
4353 }
4354
4355 // validate task region list internal consistency
4356 ValidateNoOverlaps(static_regions.GetPtr(), static_regions.GetSize(), cfg_list, clamped_count);
4357
4358 // cfg_list[i] always targets task-relative slot i (hardware region region_idx + i): the index
4359 // is derived from array position rather than being supplied by the caller
4360 size_t task_idx = 0U;
4361 for (; task_idx < clamped_count; ++task_idx)
4362 {
4363 ConfigureRegion(task_mpu.region[task_idx], cfg_list[task_idx],
4364 static_cast<uint32_t>(region_idx + task_idx));
4365 }
4366
4367 // fill any remaining task slots with a disabled-region descriptor so callers no
4368 // longer need to supply placeholder/empty entries just to occupy unused slots;
4369 // this only updates the in-memory shadow table, it takes effect in hardware on
4370 // the next context switch into this task, via STK_ASM_BLOCK_MPU_STACK_GUARD
4371 for (; task_idx < task_mpu_regions; ++task_idx)
4372 {
4373 ConfigureRegion(task_mpu.region[task_idx], s_StkDisabledMpuRegion,
4374 static_cast<uint32_t>(region_idx + task_idx));
4375 }
4376}
4377
4378// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4379#endif // STK_MPU
4380// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4381
4382// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4383#endif // !defined(_STK_CORTEX_M_TRUSTZONE_NON_SECURE)
4384// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4385
4386#if STK_MPU
4387static void FaultContext_FillMpu(FaultContext::Mpu &mpu_ctx, MPU_Type *mpu_inst)
4388{
4389 mpu_ctx.CTRL = mpu_inst->CTRL;
4390#if STK_ARCH_ARMV8_M
4391 mpu_ctx.MAIR0 = mpu_inst->MAIR0;
4392 mpu_ctx.MAIR1 = mpu_inst->MAIR1;
4393#endif
4394
4395 const Word saved_rnr = mpu_inst->RNR;
4396 for (size_t i = 0U; i < STK_STATIC_ARRAY_SIZE(mpu_ctx.regions); ++i)
4397 {
4398 mpu_inst->RNR = static_cast<Word>(i);
4399 __DSB();
4400 __ISB();
4401 mpu_ctx.regions[i].RNR = static_cast<Word>(i);
4402 mpu_ctx.regions[i].RBAR = mpu_inst->RBAR;
4403 #if STK_ARCH_ARMV8_M
4404 mpu_ctx.regions[i].ATTR = mpu_inst->RLAR;
4405 #else
4406 mpu_ctx.regions[i].ATTR = mpu_inst->RASR;
4407 #endif
4408 }
4409 mpu_inst->RNR = saved_rnr;
4410}
4411#endif // STK_MPU
4412
4413void FaultContext::Fill(const Word *stacked_regs, Word exc_return)
4414{
4415 this->frame.R0 = stacked_regs[0];
4416 this->frame.R1 = stacked_regs[1];
4417 this->frame.R2 = stacked_regs[2];
4418 this->frame.R3 = stacked_regs[3];
4419 this->frame.R12 = stacked_regs[4];
4420 this->frame.LR = stacked_regs[5];
4421 this->frame.PC = stacked_regs[6];
4422 this->frame.xPSR = stacked_regs[7];
4423
4424 this->EXC_RETURN = exc_return;
4425 this->CONTROL = __get_CONTROL();
4426
4427#if STK_ARCH_ARMV6_M
4428 // ARMv6-M (Cortex-M0/M0+) does not have CFSR, HFSR, AFSR, MMFAR, or BFAR.
4429 this->CFSR = 0U;
4430 this->HFSR = 0U;
4431 this->AFSR = 0U;
4432 this->mmfar_valid = false;
4433 this->bfar_valid = false;
4434 this->MMFAR = 0U;
4435 this->BFAR = 0U;
4436#else
4437 // ARMv7-M / ARMv8-M
4438 this->CFSR = SCB->CFSR;
4439 this->HFSR = SCB->HFSR;
4440 this->AFSR = SCB->AFSR;
4441 this->mmfar_valid = ((this->CFSR & SCB_CFSR_MMARVALID_Msk) != 0U);
4442 this->bfar_valid = ((this->CFSR & SCB_CFSR_BFARVALID_Msk) != 0U);
4443 this->MMFAR = (this->mmfar_valid ? SCB->MMFAR : 0U);
4444 this->BFAR = (this->bfar_valid ? SCB->BFAR : 0U);
4445#endif // STK_ARCH_ARMV6_M
4446
4447#if STK_MPU
4448 FaultContext_FillMpu(this->mpu, MPU);
4449 #if STK_ARCH_ARMV8_M && STK_TZ_SECURE
4450 FaultContext_FillMpu(this->mpu_ns, MPU_NS);
4451 #endif
4452#endif // STK_MPU
4453}
4454
4455#endif // _STK_ARCH_ARM_CORTEX_M
#define STK_ASM_POOL
#define STK_ASM_SYNTAX_UNIFIED
#define STK_ASM_ALIGN_2
#define STK_ASM_EXTRACT_STACK_POINTER_TO_R0
#define STK_ASM_GLOBAL_SYMBOL(name)
Generates a toolchain-agnostic assembly directive to declare a global symbol.
static void __stk_dmb()
Hardware memory barrier: ensures visibility across cores and bus masters.
#define STK_TZ_NSC_GATEWAY
#define STK_CORTEX_M_MPU_REGIONS_MAX
Number of MPU regions supported by MPU peripheral.
#define STK_CORTEX_M_SVCALL_ISR_PRIORITY
Max SVCall ISR priority.
Contains common inventory for platform implementation.
#define STK_ARCH_GET_CPU_ID()
Get CPU core id of the caller, e.g. if called while running on core 0 then returned value must be 0.
#define GetContext()
Get platform's context.
Top-level STK include. Provides the Kernel class template and all built-in task-switching strategies.
Hardware Abstraction Layer (HAL) declarations for the stk::hw namespace.
void STK_PANIC_HANDLER_DEFAULT(stk::EKernelPanicId id)
Default panic handler: disable interrupts, record the id, and spin in a tight loop - a defined,...
#define STK_MPU_SHARED_CODE_SECTION
Definition stk_arch.h:158
#define STK_MPU_KERNEL_DATA_SECTION
Definition stk_arch.h:160
#define STK_MPU_KERNEL_BSS_SECTION
Definition stk_arch.h:162
#define STK_MPU_KERNEL_CODE_SECTION
Definition stk_arch.h:161
#define STK_MPU_SHARED_DATA_SECTION
Definition stk_arch.h:157
#define STK_UNUSED(X)
Explicitly marks a variable as unused to suppress compiler warnings.
Definition stk_defs.h:715
#define __stk_attr_used
Marks a symbol as used, preventing the linker from discarding it even if no references are visible (d...
Definition stk_defs.h:346
#define __stk_forceinline
Forces compiler to always inline the decorated function, regardless of optimisation level.
Definition stk_defs.h:277
#define STK_SLEEP_TRAP_STACK_SIZE
Stack size for the sleep trap in elements of Word (default: STK_STACK_SIZE_MIN).
Definition stk_defs.h:653
#define STK_CS_NESTINGS_MAX
Maximum allowable recursion depth for critical section entry (default: 16).
Definition stk_defs.h:590
#define STK_ASSERT(e)
Runtime assertion. Halts execution if the expression e evaluates to false.
Definition stk_defs.h:516
#define STK_STACK_SIZE_MIN
Minimum stack size in elements of Word, shared by all stack allocation lower-bound checks.
Definition stk_defs.h:640
#define STK_MPU_TASK_REGIONS
Number of hardware MPU region slots reserved per task (stk::TaskMpu::NUM_REGIONS).
Definition stk_defs.h:163
#define __stk_attr_noinline
Prevents compiler from inlining the decorated function (function prefix).
Definition stk_defs.h:357
#define STK_STATIC_ASSERT_DESC_N(NAME, X, DESC)
Compile-time assertion with a user-defined name suffix and a custom error description.
Definition stk_defs.h:528
#define STK_ARCH_CPU_COUNT
Number of physical CPU cores available to the scheduler (default: 1).
Definition stk_defs.h:600
#define __stk_attr_naked
Suppresses compiler-generated function prologue and epilogue (function prefix).
Definition stk_defs.h:313
#define STK_MPU_TASK_REGIONS_NS
Definition stk_defs.h:166
#define STK_STACK_MEMORY_FILLER
Sentinel value written to the entire stack region at initialization (stack watermark pattern).
Definition stk_defs.h:563
#define __stk_attr_unused
Suppresses compiler warnings about an unused type, variable, or function (declaration prefix).
Definition stk_defs.h:335
#define __stk_attr_noreturn
Declares that function never returns to its caller (function prefix).
Definition stk_defs.h:324
static void __stk_debug_break()
Definition stk_defs.h:479
#define STK_STATIC_ARRAY_SIZE(ARRAY)
Get size of the static array.
Definition stk_defs.h:742
#define STK_STATIC_ASSERT(X)
Compile-time assertion. Produces a compilation error if X is false.
Definition stk_defs.h:553
#define STK_VIRT_DTOR
Makes destructors virtual and compliant to strict rules if STK_STRICT_COMPLIANCY=0.
Definition stk_defs.h:261
Namespace of STK package.
uintptr_t Word
Native processor word type.
Definition stk_common.h:144
static uint32_t CountLeadingZeros(const uint32_t value) noexcept
Count leading zeros.
Definition stk_defs.h:783
static constexpr TId TID_ISR_N
Bitmask sentinel for ISR-context task identifiers.
Definition stk_common.h:201
@ ACCESS_PRIVILEGED
Privileged access mode (access to hardware is fully unrestricted).
Definition stk_common.h:38
@ ACCESS_SECURE
Secure access mode (ARM TrustZone, Secure binary).
Definition stk_common.h:39
static constexpr Timeout NO_WAIT
Timeout value: return immediately if the synchronization object is not yet signaled (non-blocking pol...
Definition stk_common.h:218
EWaitResult
Wait result (see IKernelService::Wait).
Definition stk_common.h:121
static __stk_forceinline Cycles ConvertTimeUsToClockCycles(uint32_t clock_freq, Ticks time_us)
Convert time (microseconds) to core clock cycles.
int64_t Ticks
Ticks value.
Definition stk_common.h:159
EKernelPanicId
Identifies the source of a kernel panic.
Definition stk_common.h:58
@ KERNEL_PANIC_UNKNOWN_SVC
Unknown service command received by SVC handler.
Definition stk_common.h:66
@ KERNEL_PANIC_BAD_STACK_TYPE
Stack type is unknown.
Definition stk_common.h:69
@ KERNEL_PANIC_NS_ACCESS
Non-secure access to protected resource.
Definition stk_common.h:70
@ KERNEL_PANIC_HRT_HARD_FAULT
Kernel running in KERNEL_HRT mode reported deadline failure of the task.
Definition stk_common.h:63
@ KERNEL_PANIC_NONE
Panic is absent (no fault).
Definition stk_common.h:59
@ KERNEL_PANIC_SPINLOCK_DEADLOCK
Spin-lock timeout expired: lock owner never released.
Definition stk_common.h:60
int32_t Timeout
Timeout time (ticks).
Definition stk_common.h:154
static bool IsIsrTid(TId id)
Test whether a task identifier represents an ISR context.
Definition stk_common.h:241
static void STK_KERNEL_PANIC(stk::EKernelPanicId id)
Called when the kernel detects an unrecoverable internal fault.
Definition stk_arch.h:183
@ SYS_TASK_ID_SLEEP
Sleep trap.
Definition stk_common.h:100
static constexpr T Max(T a, T b) noexcept
Compile-time maximum of two values.
Definition stk_defs.h:759
static Ticks GetTicks()
Get number of ticks elapsed since kernel start.
Definition stk_helper.h:434
static constexpr TId TID_NONE
Reserved task/thread id representing zero/none thread id.
Definition stk_common.h:206
static constexpr T Min(T a, T b) noexcept
Compile-time minimum of two values.
Definition stk_defs.h:753
@ TRACE_EVENT_SWITCH
Task blocked by the context switch.
Definition stk_common.h:111
EStackType
Stack type.
Definition stk_common.h:79
@ STACK_SLEEP_TRAP
Stack of the Sleep trap.
Definition stk_common.h:81
@ STACK_USER_TASK
Stack of the user task.
Definition stk_common.h:80
@ STACK_EXIT_TRAP
Stack of the Exit trap.
Definition stk_common.h:82
uint64_t Cycles
Cycles value.
Definition stk_common.h:169
EHwException
Hardware exception id (see IPlatform::IEventOverrider::OnException).
Definition stk_common.h:132
@ HW_EXCEPT_MEMACCESS
MemManage on ARM / Page Fault or PMP violation on RISC-V.
Definition stk_common.h:134
@ HW_EXCEPT_FATAL
HardFault on ARM / Unhandled Fatal Trap on RISC-V.
Definition stk_common.h:133
Word TId
Task (thread) id.
Definition stk_common.h:149
int32_t Weight
Weight value (aka priority).
Definition stk_common.h:174
Hardware Abstraction Layer (HAL) for architecture-specific operations.
bool IsPrivilegedContext()
Check if caller context is Privileged.
static constexpr T * WordToPtr(Word value) noexcept
Cast a CPU register-width integer back to a pointer.
Definition stk_arch.h:231
static constexpr Word PtrToWord(T *const ptr) noexcept
Cast a pointer to a CPU register-width integer.
Definition stk_arch.h:214
bool IsInsideISR()
Check whether the CPU is currently executing inside a hardware interrupt service routine (ISR).
void SwitchToNext() override
Switch to a next task.
void Sleep(Timeout ticks) override
Put calling process into a sleep state.
void ProcessTick() override
Process one tick.
Word GetCallerSP() const override
Get caller's Stack Pointer (SP).
void ProcessHardFault() override
Cause a hard fault of the system.
void ForceContextSwitch(TId id) override
Force context switch.
uint32_t GetTickResolution() const override
Get resolution of the system tick timer in microseconds. Resolution means a number of microseconds be...
Timeout Suspend() override
Suspend scheduling.
void Start() override
Start scheduling.
void SetCpuFrequency(uint8_t core_id, uint32_t frequency) override
Notify the scheduler of a CPU core's operating frequency.
void Stop() override
Stop scheduling.
TId GetTid() const override
Get thread Id.
void SetEventOverrider(IEventOverrider *overrider, bool non_secure) override
Set platform event overrider.
uint32_t GetSysTimerFrequency() const override
Get system timer frequency.
Cycles GetSysTimerCount() const override
Get system timer count value.
void InitStack(EStackType stack_type, Stack *stack, IStackMemory *stack_memory, ITask *user_task) override
Initialize stack memory of the user task.
EWaitResult Wait(ISyncObject *sync_obj, IMutex *mutex, Timeout timeout) override
Put calling process into a waiting state until synchronization object is signaled or timeout occurs.
void Resume(Timeout elapsed_ticks) override
Resume scheduling after a prior Suspend() call.
void Initialize(IEventHandler *event_handler, IKernelService *service, uint32_t resolution_us, Stack *exit_trap) override
Initialize scheduler's context.
bool SleepUntil(Ticks timestamp) override
Put calling process into a sleep state until the specified timestamp.
ARMv7-M/ARMv8-M hardware exception frame (8 words, highest address on the stack).
ARMv7-M/ARMv8-M system fault exception context state capture.
void Fill(const Word *stacked_regs, Word exc_return)
Populate fault context registers and hardware MPU state from an exception stack frame.
Region regions[(8U)]
Active hardware MPU region register state table.
Word CTRL
MPU Control Register (MPU_CTRL).
Word RNR
Region Number Register (RNR).
Word ATTR
Region Attribute and Size Register (RASR / RLAR / MPU_RLAR).
Word RBAR
Region Base Address Register (RBAR).
Base platform context for all platform implementations.
static Session Enter(const Session ses=DEFAULT_SESSION)
Enter a critical section.
uint8_t Session
Opaque session token returned by Enter() and consumed by Exit().
Definition stk_arch.h:471
static void Exit(const Session ses=DEFAULT_SESSION)
Exit a critical section.
@ SESSION_FLAG_NPRIV
Calling context is non-Privileged.
Definition stk_arch.h:464
RAII instance that enters the critical section on construction and exits it on destruction.
Definition stk_arch.h:494
Atomic busy-wait lock used as the global cross-core synchronisation primitive inside CriticalSection.
Definition stk_arch.h:570
bool TryLock()
Attempt to acquire SpinLock in a single non-blocking attempt.
void Lock()
Acquire SpinLock, blocking until it is available.
void Unlock()
Release SpinLock, allowing another thread or core to acquire it.
static uint32_t GetFrequency()
Get clock frequency.
static Cycles GetCycles()
Get number of clock cycles elapsed.
Stack descriptor.
Definition stk_common.h:393
uint32_t access_mode
Bitfield with hardware access mode of the task (see EAccessMode).
Definition stk_common.h:395
Word SP
Offset 0: Stack Pointer (SP) register (note: must always be at offset 0).
Definition stk_common.h:394
Interface for a stack memory region.
Definition stk_common.h:414
virtual size_t GetStackSize() const =0
Get number of elements of the stack memory array.
virtual const Word * GetStack() const =0
Get pointer to the stack memory.
Synchronization object interface.
Definition stk_common.h:578
Interface for mutex synchronization primitive.
Definition stk_common.h:714
Interface for a user task.
Definition stk_common.h:772
virtual void Run()=0
Entry point of the user task.
Interface for a platform event overrider.
Interface for the kernel services exposed to the user processes during run-time when Kernel started s...
virtual TId GetTid() const =0
Get thread Id of the currently running task.
virtual void Wake(ISyncObject *sobj, bool all)=0
Wake one or all tasks currently waiting on a synchronization object.
virtual void SleepCancel(TId task_id)=0
Cancel sleep of the task.
virtual void InheritWeight(TId tid, Weight weight)=0
Inherit weight for the task.
static IKernelService * GetInstance()
Get CPU-local instance of the kernel service.
virtual uint32_t GetTickResolution() const =0
Get number of microseconds in one tick.
virtual bool SleepUntil(Ticks timestamp)=0
Put calling process into a sleep state until the specified timestamp.
virtual Ticks GetTicks() const =0
Get number of ticks elapsed since kernel start.
virtual void SwitchToNext()=0
Notify scheduler to switch to the next task (yield).
virtual void Resume(Timeout elapsed_ticks)=0
Resume scheduling after a prior Suspend() call.
virtual void Sleep(Timeout ticks)=0
Put calling process into a sleep state.
virtual EWaitResult Wait(ISyncObject *sobj, IMutex *mutex, Timeout timeout)=0
Put calling process into a waiting state until synchronization object is signaled or timeout occurs.
virtual Cycles GetSysTimerCount() const =0
Get system timer count value.
virtual uint32_t GetSysTimerFrequency() const =0
Get system timer frequency.
virtual Timeout Suspend()=0
Suspend scheduling.
virtual void RestoreWeight(TId tid, ISyncObject *sobj=nullptr)=0
Restore weight of the task to the original value.
virtual void Delay(Timeout ticks)=0
Delay calling process.
RISC-V specific event handler.
MPU descriptor of the task.