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, ...)!
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 (5U * 1000U * 1000U) // 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 &lock)
745{
746 return !__atomic_test_and_set(&lock, __ATOMIC_ACQUIRE);
747}
748
761static __stk_forceinline void HW_SpinLockUnlock(volatile bool &lock)
762{
763 if (lock == 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(&lock, __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 &lock)
798{
799 bool success = (STK_SIO_SPINLOCK == 0 ? false : ((lock) = true, true));
800 __stk_dmb();
801
802 return success;
803}
804
821static __stk_forceinline void HW_SpinLockUnlock(volatile bool &lock)
822{
823 if (!lock)
824 STK_KERNEL_PANIC(KERNEL_PANIC_SPINLOCK_DEADLOCK); // release attempt of unowned lock
825
826 __stk_dmb();
827 (lock) = 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 &lock)
849{
850 const uint32_t ses = HW_CriticalSectionStart();
851
852 if (lock)
853 {
854 HW_CriticalSectionEnd(ses);
855 return false;
856 }
857
858 lock = true;
859 __stk_dmb();
860
861 HW_CriticalSectionEnd(ses);
862 return true;
863}
864
876static __stk_forceinline void HW_SpinLockUnlock(volatile bool &lock)
877{
878 if (!lock)
879 STK_KERNEL_PANIC(KERNEL_PANIC_SPINLOCK_DEADLOCK); // release attempt of unowned lock
880
881 __stk_dmb();
882 lock = false;
883}
884#endif // CONTROL_nPRIV_Msk
885
905static __stk_forceinline void HW_SpinLockLock(volatile bool &lock)
906{
907 uint32_t timeout = s_StkSpinlockTimeoutIters;
908 while (!HW_SpinLockTryLock(lock))
909 {
910 if (--timeout == 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 uint32_t result = SysTick_Config(static_cast<uint32_t>(ConvertTimeUsToClockCycles(HW_CoreClockFrequency(), period_ticks)));
1264 STK_ASSERT(result == 0U);
1265 STK_UNUSED(result);
1266
1267 // QEMU workaround (Launchpad Bug #1872237):
1268 // SysTick_Config() writes VAL=0 before setting ENABLE=1. On QEMU,
1269 // systick_reload() silently discards VAL writes while ENABLE=0, leaving
1270 // the internal tick accumulator stale from the previous period;
1271 // writing VAL=0 here, after ENABLE=1 is already set, forces a correct reload.
1272 // This is a no-op on real Cortex-M hardware (spec: ARMv7-M ARM B3.3.1)
1273 SysTick->VAL = 0U;
1274}
1275
1278static __stk_forceinline void HW_SysTickStop()
1279{
1280 SysTick->CTRL = 0U;
1281 SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk;
1282}
1283
1286__stk_attr_unused /* can be unused due to configuration */
1287static __stk_forceinline void HW_SysTickDisable()
1288{
1289 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
1290}
1291
1295static __stk_forceinline uint32_t HW_SysTickValue()
1296{
1297 return SysTick->VAL;
1298}
1299
1303__stk_attr_unused /* can be unused due to configuration */
1304static __stk_forceinline uint32_t HW_SysTickValueAfterDisable()
1305{
1306 // is required for QEMU which then resets SysTick->VAL and thus we can't
1307 // calculate elapsed time correctly
1308 __DSB();
1309
1310 // check for a QEMU case and discard elapsed result
1311 uint32_t val = HW_SysTickValue();
1312 if (val == 0U)
1313 {
1314 val = SysTick->LOAD;
1315 }
1316
1317 return val;
1318}
1319
1323__stk_attr_unused /* can be unused due to configuration */
1324static __stk_forceinline uint32_t HW_SysTickElapsed(uint32_t val)
1325{
1326 return SysTick->LOAD - val;
1327}
1328
1332__stk_attr_unused /* can be unused due to configuration */
1333static __stk_forceinline void HW_SysTickRearm(uint32_t ticks)
1334{
1335 SysTick->LOAD = ticks - 1U;
1336 SysTick->VAL = 0U;
1337 SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
1338}
1339
1342static __stk_forceinline void HW_DWTEnableCounter()
1343{
1344 // enable Trace and Debug blocks (DWT, ITM, ETM, TPIU)
1345#if defined(CoreDebug)
1346 CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
1347#elif defined(DCB)
1348 DCB->DEMCR |= DCB_DEMCR_TRCENA_Msk;
1349#endif
1350 __DSB();
1351
1352 // LAR (Lock Access Register) is mandatory for Cortex-M7 to allow register writes,
1353 // it is typically not implemented or deprecated on M0, M3, M4, and M33
1354#if (__CORTEX_M == 7U)
1355 DWT->LAR = 0xC5ACCE55; // unlock DWT unit using standard CoreSight magic key
1356 __DSB();
1357#endif
1358
1359#if defined(DWT)
1360 // do not interfere with already enabled and running counter
1361 if ((DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk) != DWT_CTRL_CYCCNTENA_Msk)
1362 {
1363 DWT->CYCCNT = 0U; // reset counter value to zero
1364 DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // start counter
1365 }
1366#endif
1367}
1368
1373static __stk_forceinline uint32_t HW_DWTGetCounter()
1374{
1375#if defined(DWT)
1376 return DWT->CYCCNT;
1377#else
1378 return 0U;
1379#endif
1380}
1381
1394static void HW_InitSpinlockTimeout()
1395{
1396 const Cycles cycles_budget = ConvertTimeUsToClockCycles(HW_CoreClockFrequency(), STK_SPINLOCK_TIMEOUT_US);
1397 const Cycles iters = cycles_budget / STK_SPINLOCK_MIN_CYCLES_PER_ITER;
1398
1399 // floor guards against a zero/not-yet-configured clock; ceiling guards against overflow
1400 // when narrowing to uint32_t
1401 s_StkSpinlockTimeoutIters = static_cast<uint32_t>(
1402 stk::Min(stk::Max(iters, static_cast<Cycles>(0x1000U)), static_cast<Cycles>(0xFFFFFFFFU)));
1403}
1404
1405// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1406#if STK_MPU
1407// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1408
1427struct ScopedPrivilegeBoost
1428{
1434 explicit ScopedPrivilegeBoost();
1435
1442 ~ScopedPrivilegeBoost()
1443 {
1444 // drop privileges and flush pipeline (__set_CONTROL also issues __ISB)
1445 __set_CONTROL(__get_CONTROL() | CONTROL_nPRIV_Msk);
1446 }
1447};
1448
1465ScopedPrivilegeBoost::ScopedPrivilegeBoost()
1466{
1467 // boost privilege via SVC SVC_BOOST_PRIV handler
1468 __asm volatile(
1469 "SVC %0 \n"
1470 : /* no outputs */
1471 : "I"(SVC_BOOST_PRIV)
1472 : "memory"
1473 );
1474}
1475
1476// ----------------------------------------------------------------------------
1477namespace stk {
1478namespace hw {
1479namespace mpu {
1480// ----------------------------------------------------------------------------
1481
1502void ConfigureRegion(MpuRegion &reg, const struct MpuRegionConfig &cfg, uint32_t region_idx);
1503
1515void ConfigureStatic(const MpuRegionConfig cfg_list[], size_t cfg_count, uint32_t control_flags,
1516 bool non_secure);
1517
1530void ConfigureDynamic(TaskMpu &task_mpu, const struct MpuRegionConfig cfg_list[], const size_t cfg_count,
1531 IPlatform::IEventOverrider *overrider, bool non_secure);
1532
1544void ApplyRegion(const MpuRegion &reg, uint32_t index, bool non_secure);
1545
1553void DisableRegion(uint32_t index, bool non_secure);
1554
1565void Enable(bool enable, uint32_t control_flags, bool non_secure);
1566
1567#if STK_ARCH_ARMV8_M
1575static constexpr uint32_t MAIR0_PMSAV8_INIT = 0xFF440400U;
1576
1581static constexpr uint32_t MAIR1_PMSAV8_INIT = 0x00000000U;
1582
1587static constexpr Word RLAR_ENABLE_FLAG = (1U << 0U);
1588
1597static __stk_forceinline Word RBAR_DISABLED_REGION(uint32_t region_idx)
1598{
1599 STK_UNUSED(region_idx);
1600 return SCB->VTOR;
1601}
1602
1608static constexpr Word RLAR_DISABLED_REGION = 0U;
1609#else
1614static constexpr Word RASR_ENABLE_FLAG = (1U << 0U);
1615
1624static constexpr Word RBAR_DISABLED_REGION(uint32_t region_idx) { return (1U << 4U) | (region_idx & 0xFU); }
1625
1631static constexpr Word RASR_DISABLED_REGION = 0U;
1632#endif
1633
1634// ----------------------------------------------------------------------------
1635} // namespace mpu
1636} // namespace hw
1637} // namespace stk
1638// ----------------------------------------------------------------------------
1639
1640// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1641#endif // STK_MPU
1642// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1643
1658#if STK_MPU
1659 extern char __stk_mpu_shared_code_start[];
1660 extern char __stk_mpu_shared_code_end[];
1661 #if defined(DEBUG) || defined(_DEBUG)
1662 extern char __stk_mpu_shared_data_start[];
1663 extern char __stk_mpu_shared_data_end[];
1664 #endif
1665#endif
1666
1667#if STK_MPU
1673static constexpr MpuRegionConfig s_StkDisabledMpuRegion =
1674{
1675 .addr = 0U,
1676 .size = 0U,
1677 .access_perm = hw::mpu::ACCESS_NONE,
1678 .mem_type = hw::mpu::TYPE_STRONGLY_ORDERED,
1679 .share = hw::mpu::SHARE_NON,
1680 .exec = hw::mpu::EXEC_ALLOWED
1681};
1682#endif
1683
1685namespace stk {
1687static struct Context final : public PlatformContext
1688{
1689 typedef IPlatform::IEventOverrider eovrd_t;
1690
1691 explicit Context() : PlatformContext(), m_exit_buf(), m_overrider(nullptr),
1692 #if STK_CORTEX_M_TRUSTZONE_FRAME
1693 m_overrider_ns(nullptr),
1694 #endif
1695 #if STK_TICKLESS_IDLE
1696 m_sleep_ticks(0), m_sleep_error(0U),
1697 #endif
1698 m_csu(0U), m_csu_nesting(0U), m_started(false), m_exiting(false)
1699 {}
1700
1701 void Initialize(IPlatform::IEventHandler *handler, IKernelService *service, Stack *exit_trap,
1702 uint32_t resolution_us)
1703 {
1704 InitializeBase(handler, service, exit_trap, resolution_us);
1705
1706 STK_STATIC_ASSERT_DESC_N(SP, offsetof(Stack, SP) == 0U,
1707 "expect Stack::mode member at offset of 0 (first member)");
1708 #if STK_MPU
1710 (STK_MPU_TASK_REGIONS <= 4U)
1711 ? ((STK_CORTEX_M_MPU_TASK_REGION_IDX % 4U) <= (4U - STK_MPU_TASK_REGIONS))
1712 : ((STK_CORTEX_M_MPU_TASK_REGION_IDX % 4U) == 0U),
1713 "STK_CORTEX_M_MPU_TASK_REGION_IDX is not aligned correctly for the RNR-relative "
1714 "alias burst write at the configured STK_MPU_TASK_REGIONS width");
1715 STK_STATIC_ASSERT_DESC_N(mpu_task_regions,
1716 (STK_MPU_TASK_REGIONS == 2U) || (STK_MPU_TASK_REGIONS == 4U) || (STK_MPU_TASK_REGIONS == 6U) ||
1717 (STK_MPU_TASK_REGIONS == 8U) || (STK_MPU_TASK_REGIONS == 12U) || (STK_MPU_TASK_REGIONS == 16U),
1718 "STK_MPU_TASK_REGIONS must be defined as 2, 4, 6, 8, 12 or 16");
1719 #ifdef _STK_CORTEX_M_TRUSTZONE
1722 ? ((STK_CORTEX_M_MPU_TASK_REGION_IDX_NS % 4U) <= (4U - STK_MPU_TASK_REGIONS_NS))
1723 : ((STK_CORTEX_M_MPU_TASK_REGION_IDX_NS % 4U) == 0U),
1724 "STK_CORTEX_M_MPU_TASK_REGION_IDX_NS is not aligned correctly for the RNR-relative "
1725 "alias burst write at the configured STK_MPU_TASK_REGIONS_NS width");
1726 STK_STATIC_ASSERT_DESC_N(mpu_task_regions_ns,
1729 "STK_MPU_TASK_REGIONS_NS must be defined as 2, 4, 6, 8, 12 or 16");
1730 #endif
1731 // make sure linker declares __stk_mpu_shared_xxx regions correctly, i.e. code and data
1732 // sections must contain data
1733 STK_ASSERT(hw::PtrToWord(__stk_mpu_shared_code_end) - hw::PtrToWord(__stk_mpu_shared_code_start) > 0U);
1734 STK_ASSERT(hw::PtrToWord(__stk_mpu_shared_data_end) - hw::PtrToWord(__stk_mpu_shared_data_start) > 0U);
1735 #endif
1736 STK_ASSERT(s_StkCortexmCsuLock == false);
1737 STK_ASSERT(g_StkLastPanicId == KERNEL_PANIC_NONE);
1738
1739 m_csu = 0U;
1740 m_csu_nesting = 0U;
1741 m_started = false;
1742 m_exiting = false;
1743 #if STK_TICKLESS_IDLE
1744 m_sleep_ticks = 0;
1745 m_sleep_error = 0U;
1746 #endif
1747
1748 // initialize system clock if user-side code has not yet done it
1749 if (s_StkSystemCoreClock[0] == 0U)
1750 {
1751 for (uint8_t i = 0U; i < STK_STATIC_ARRAY_SIZE(s_StkSystemCoreClock); ++i)
1752 {
1753 s_StkSystemCoreClock[i] = SystemCoreClock;
1754 }
1755 }
1756
1757 HW_InitSpinlockTimeout();
1758
1759 #if (__CORTEX_M > 1) && STK_TICKLESS_USE_ARM_DWT
1760 HW_DWTEnableCounter();
1761 #endif
1762
1763 #if STK_SEGGER_SYSVIEW
1764 SEGGER_SYSVIEW_Init(
1765 HW_CoreClockFrequency(),
1766 HW_CoreClockFrequency(),
1767 nullptr,
1768 stk::SendSysDesc);
1769 #endif
1770 }
1771
1772#if STK_TICKLESS_IDLE
1773 __stk_forceinline void OnTick(Timeout &ticks)
1774#else
1775 __stk_forceinline void OnTick()
1776#endif
1777 {
1778 if (m_handler->OnTick(m_stack_idle, m_stack_active
1779 #if STK_TICKLESS_IDLE
1780 , ticks
1781 #endif
1782 ))
1783 {
1784 HW_ScheduleContextSwitch();
1785 }
1786 }
1787
1788 __stk_forceinline void ProcessTick()
1789 {
1790 HW_DisableInterrupts();
1791
1792 #if STK_TICKLESS_IDLE
1793 Timeout ticks = m_sleep_ticks;
1794
1795 OnTick(ticks);
1796
1797 // re-arm SysTick only if tick period changed
1798 if (ticks != m_sleep_ticks)
1799 {
1800 m_sleep_ticks = ReloadTickPeriod(ticks);
1801 }
1802 #else
1803 OnTick();
1804 #endif // STK_TICKLESS_IDLE
1805
1806 HW_EnableInterrupts();
1807 }
1808
1809 __stk_forceinline void OnForceContextSwitch(TId id)
1810 {
1811 if (m_handler->OnForceContextSwitch(id, m_stack_idle, m_stack_active))
1812 {
1813 HW_ScheduleContextSwitch();
1814 }
1815 }
1816
1817 __stk_forceinline void OnEnterCriticalSection(uint32_t current_ses)
1818 {
1819 if (m_csu_nesting == 0U)
1820 {
1821 // ONLY attempt the global spinlock if we aren't already nested
1822 HW_SpinLockLock(s_StkCortexmCsuLock);
1823
1824 // store the hardware interrupt state to restore later
1825 m_csu = current_ses;
1826 }
1827
1828 // increase nesting count within a limit
1829 if (++m_csu_nesting > STK_CS_NESTINGS_MAX)
1830 {
1831 // invariant violated: exceeded max allowed number of recursions
1832 STK_KERNEL_PANIC(KERNEL_PANIC_CS_NESTING_OVERFLOW);
1833 }
1834 }
1835
1836 __stk_forceinline bool OnExitCriticalSection(uint32_t &restore_ses)
1837 {
1838 bool released = false;
1839
1840 STK_ASSERT(m_csu_nesting != 0U);
1841 --m_csu_nesting;
1842
1843 if (m_csu_nesting == 0U)
1844 {
1845 // capture the state before releasing lock
1846 restore_ses = m_csu;
1847
1848 // release global lock
1849 HW_SpinLockUnlock(s_StkCortexmCsuLock);
1850
1851 released = true;
1852 }
1853
1854 return released;
1855 }
1856
1857 __stk_forceinline void EnterCriticalSection()
1858 {
1859 const uint32_t current_ses = HW_CriticalSectionStart();
1860
1861 OnEnterCriticalSection(current_ses);
1862 }
1863
1864 __stk_forceinline void ExitCriticalSection()
1865 {
1866 uint32_t restore_ses;
1867 if (OnExitCriticalSection(restore_ses))
1868 {
1869 HW_CriticalSectionEnd(restore_ses);
1870 }
1871 }
1872
1873 void StartTickTimer(Timeout elapsed_ticks)
1874 {
1875 #if STK_TICKLESS_IDLE
1876 // reset sleep ticks if kernel was restarted
1877 m_sleep_ticks = elapsed_ticks;
1878 #else
1879 STK_UNUSED(elapsed_ticks);
1880 #endif
1881
1882 // start SysTick timer (it is yet can't fire an interrupt due to HW_DisableInterrupts)
1883 HW_SysTickStart(m_tick_resolution);
1884
1885 // note: Always after SysTick_Config because it may change SysTick priority
1886 NVIC_SetPriority(SysTick_IRQn, STK_CORTEX_M_ISR_PRIORITY_LOWEST);
1887 }
1888
1889#if STK_TLS && !STK_INLINE_TLS
1890 Word GetTls()
1891 {
1892 hw::CriticalSection::ScopedLock cs_;
1893
1894 STK_ASSERT(m_stack_active != nullptr);
1895
1896 return m_stack_active->tls;
1897 }
1898
1899 void SetTls(Word tp)
1900 {
1901 hw::CriticalSection::ScopedLock cs_;
1902
1903 STK_ASSERT(m_stack_active != nullptr);
1904
1905 m_stack_active->tls = tp;
1906 }
1907#endif // STK_TLS && !STK_INLINE_TLS
1908
1909 void OnSleepOverride()
1910 {
1911 #if STK_TICKLESS_IDLE
1912 const Timeout sleep_ticks = m_sleep_ticks;
1913 #else
1914 const Timeout sleep_ticks = 1;
1915 #endif
1916
1917 if (!m_overrider->OnSleep(sleep_ticks))
1918 {
1919 HW_EnterSleepMode();
1920 }
1921 }
1922
1923 uint32_t GetTickResolutionInClockCycles()
1924 {
1925 return static_cast<uint32_t>(ConvertTimeUsToClockCycles(HW_CoreClockFrequency(), static_cast<Ticks>(m_tick_resolution)));
1926 }
1927
1928#if STK_MPU
1929 void ConfigureMpuInstance(eovrd_t *overrider, bool non_secure)
1930 {
1931 // configure Secure binary side if TrustZone enabled, or just standard
1932 if (overrider != nullptr)
1933 {
1934 const MpuConfig *const cfg = overrider->OnConfigureMpu();
1935
1936 // OnConfigureMpu() may legitimately return nullptr (no custom global config supplied),
1937 // treat that the same as MPU_CFG_NONE below.
1938 if (cfg != nullptr)
1939 {
1940 #if STK_CORTEX_M_TRUSTZONE_FRAME
1941 // MPU_CFG_NONSECURE_MPU selects which hardware MPU instance a config targets
1942 // (absent = Secure, set = Non-Secure). It must agree with the instance this call
1943 // is actually configuring (see ConfigureMpu() below), catching a config wired to
1944 // the wrong overrider (e.g. Secure flags returned from the Non-Secure overrider).
1945 STK_ASSERT(non_secure == ((cfg->mode & hw::mpu::MPU_CFG_NONSECURE_MPU) != 0U));
1946 #endif
1947
1948 if ((cfg->mode & hw::mpu::MPU_CFG_CLEAR_ON_INIT) != 0U)
1949 {
1950 // wipe every hardware region slot (static block + per-task block) so no stale
1951 // configuration left behind by a previous run/bootloader survives the static
1952 // table applied by ConfigureStatic() below.
1953 for (uint32_t index = 0U; index < STK_CORTEX_M_MPU_REGIONS_MAX; ++index)
1954 {
1955 hw::mpu::DisableRegion(index, non_secure);
1956 }
1957 }
1958
1959 uint32_t control_flags = 0U;
1960
1961 if ((cfg->mode & hw::mpu::MPU_CFG_PRIVILEGED_BG_MEM) != 0U)
1962 {
1963 control_flags |= MPU_CTRL_PRIVDEFENA_Msk;
1964 }
1965
1966 if ((cfg->mode & hw::mpu::MPU_CFG_IN_FAULTS) != 0U)
1967 {
1968 control_flags |= MPU_CTRL_HFNMIENA_Msk;
1969 }
1970
1971 hw::mpu::ConfigureStatic(cfg->regions.GetPtr(), cfg->regions.GetSize(), control_flags, non_secure);
1972 }
1973 else
1974 {
1975 hw::mpu::Enable(false, 0, non_secure);
1976 }
1977 }
1978 }
1979#endif // STK_MPU
1980
1981#if STK_MPU
1982 void ConfigureMpu()
1983 {
1984 // configure Secure binary side if TrustZone enabled, or just standard
1985 ConfigureMpuInstance(m_overrider, false);
1986
1987 #if STK_CORTEX_M_TRUSTZONE_FRAME
1988 // configure MPU_NS for Non-Secure binary side
1989 ConfigureMpuInstance(m_overrider_ns, true);
1990 #endif
1991 }
1992#endif // STK_MPU
1993
1994 TId GetCurrentTId() const
1995 {
1996 #if STK_STACK_NEEDS_TASK_ID
1997 const TId tid = (m_stack_active != nullptr ? m_stack_active->tid : TID_NONE);
1998 #else
1999 const TId tid = TID_NONE;
2000 #endif
2001
2002 return tid;
2003 }
2004
2005 void Start();
2006 void OnStart();
2007 void OnStop();
2008#if STK_TICKLESS_IDLE
2009 Timeout ReloadTickPeriod(Timeout ticks_requested);
2010 Timeout Suspend();
2011 void Resume(Timeout elapsed_ticks);
2012#endif
2013
2014 JmpFrame m_exit_buf;
2015 eovrd_t *m_overrider;
2016#if STK_CORTEX_M_TRUSTZONE_FRAME
2017 eovrd_t *m_overrider_ns;
2018#endif
2019#if STK_TICKLESS_IDLE
2020 Timeout m_sleep_ticks;
2021 uint32_t m_sleep_error;
2022#endif
2023 uint32_t m_csu;
2024 uint8_t m_csu_nesting;
2025 volatile bool m_started;
2026 bool m_exiting;
2027}
2028s_StkPlatformContext[STK_ARCH_CPU_COUNT];
2029} // namespace stk
2030
2032class HiResClockDWT
2033{
2034 Cycles m_acc;
2035 uint32_t m_prev;
2036
2037public:
2038 HiResClockDWT() : m_acc(0U), m_prev(0U)
2039 {
2040 HW_DWTEnableCounter();
2041
2042 m_prev = HW_DWTGetCounter();
2043 m_acc = 0U;
2044 }
2045
2046 static HiResClockDWT *GetInstance();
2047
2048 void Update()
2049 {
2050 const uint32_t current = HW_DWTGetCounter();
2051
2052 // unsigned subtraction handles the wrap-around perfectly
2053 const uint32_t delta = current - m_prev;
2054 m_acc += delta;
2055
2056 m_prev = current;
2057 }
2058
2059 Cycles GetCycles()
2060 {
2061 Update();
2062 return m_acc;
2063 }
2064
2065 uint32_t GetFrequency()
2066 {
2067 return HW_CoreClockFrequency();
2068 }
2069};
2070
2072HiResClockDWT *HiResClockDWT::GetInstance()
2073{
2074 // keep declaration function-local to allow compiler stripping it from the binary if
2075 // it is unused by the user code
2076 STK_MPU_KERNEL_DATA_SECTION static HiResClockDWT clock;
2077 return &clock;
2078}
2079
2081class HiResClockM0
2082{
2083public:
2084 static HiResClockM0 *GetInstance();
2085
2086 Cycles GetCycles()
2087 {
2088 // On M0, combine the coarse OS ticks with the fine-grained SysTick counter
2089 const Cycles cycles = ConvertTimeUsToClockCycles(HW_CoreClockFrequency(),
2090 static_cast<Ticks>(stk::GetTicks() * GetContext().m_tick_resolution));
2091
2092 const uint32_t val = HW_SysTickValue(); // down-counter (cycles remaining in current tick)
2093 const uint32_t load = SysTick->LOAD; // current reload value
2094
2095 // total elapsed cycles
2096 return cycles + static_cast<Cycles>(load - val);
2097 }
2098
2099 uint32_t GetFrequency()
2100 {
2101 return HW_CoreClockFrequency();
2102 }
2103};
2104
2106HiResClockM0 *HiResClockM0::GetInstance()
2107{
2108 // keep declaration function-local to allow compiler stripping it from the binary if
2109 // it is unused by the user code
2110 STK_MPU_KERNEL_DATA_SECTION static HiResClockM0 clock;
2111 return &clock;
2112}
2113
2114#if (__CORTEX_M >= 3U)
2115 typedef HiResClockDWT HiResClockImpl;
2116#else
2117 typedef HiResClockM0 HiResClockImpl;
2118#endif
2119
2120__stk_attr_noinline // keep out of inlining to preserve stack frame
2121__stk_attr_noreturn // never returns - a trap
2123{
2124 g_StkLastPanicId = id;
2125
2126 // disable all maskable interrupts: this prevents scheduler from running again and corrupting state further
2127 HW_DisableInterrupts();
2128
2129 // spin forever: with a watchdog active this produces a clean reset, without a watchdog,
2130 // a debugger can attach and inspect 'id'
2131 for (;;)
2132 {
2133 __stk_relax_cpu();
2134 }
2135}
2136
2138{
2139 GetContext().ProcessTick();
2140}
2141
2142#if STK_TICKLESS_IDLE
2143Timeout Context::ReloadTickPeriod(Timeout ticks_requested)
2144{
2145 const uint32_t SYSTICK_MAX_LOAD = 0x00FFFFFFU; // SysTick LOAD register is 24-bit
2146 const uint32_t tick_resolution = GetTickResolutionInClockCycles();
2147 if (tick_resolution == 0U)
2148 {
2149 STK_ASSERT(false);
2150 return NO_WAIT;
2151 }
2152
2153 // guard against uint32_t overflow in the reload calculation
2154 STK_ASSERT(static_cast<uint64_t>(ticks_requested) * tick_resolution <= UINT32_MAX);
2155
2156 // clamp ticks_requested so that cpu_ticks_requested fits into 24-bit SysTick LOAD register
2157 // without clamping large sleep tick counts silently truncate LOAD, causing the timer to fire far too early
2158 // breaking the timing
2159 const Timeout max_ticks = static_cast<Timeout>(SYSTICK_MAX_LOAD / tick_resolution);
2160 if (ticks_requested > max_ticks)
2161 {
2162 ticks_requested = max_ticks;
2163 }
2164
2165 // start counting how many CPU cycles further instructions take until SysTick timer is enabled again;
2166 // without DWT we will have tick error of around 80 cycles depending on CPU model and compiler optimization
2167#if (__CORTEX_M > 1) && STK_TICKLESS_USE_ARM_DWT
2168 const uint32_t error = HW_DWTGetCounter();
2169 __stk_compiler_barrier(); // prevent reordering, we measure all cycles of instructions below this point
2170#endif
2171
2172 // pause SysTick
2173 HW_SysTickDisable();
2174
2175 // get already elapsed CPU cycles since SysTick ISR invocation up to SysTick timer stop (see above)
2176 // to account for them for a new period value
2177 const uint32_t elapsed_till_stop = HW_SysTickElapsed(HW_SysTickValueAfterDisable());
2178
2179 // OnTick() should not consume more than next period
2180 STK_ASSERT(static_cast<Timeout>(elapsed_till_stop / tick_resolution) <= static_cast<Timeout>(ticks_requested));
2181
2182 const uint32_t cpu_ticks_requested = static_cast<uint32_t>(ticks_requested) * tick_resolution;
2183
2184 // substract number of cycles elapsed till SysTick stop + error from previous round
2185 uint32_t new_load = cpu_ticks_requested - elapsed_till_stop - m_sleep_error;
2186
2187 // clamp: rearm overhead must never push new_load into underflow
2188 if (new_load > cpu_ticks_requested)
2189 {
2190 new_load = cpu_ticks_requested;
2191 }
2192
2193 // reload with elapsed ticks accounted
2194 HW_SysTickRearm(new_load);
2195
2196#if (__CORTEX_M > 1) && STK_TICKLESS_USE_ARM_DWT
2197 // calculate error: subtract cycles consumed by the rearm sequence itself in the next round
2198 m_sleep_error = HW_DWTGetCounter() - error;
2199#endif
2200
2201 // return actual clamped ticks armed
2202 return ticks_requested;
2203}
2204#endif // STK_TICKLESS_IDLE
2205
2206extern "C" void STK_SYSTICK_HANDLER()
2207{
2208 // start tracing
2209#if STK_SEGGER_SYSVIEW
2210 SEGGER_SYSVIEW_RecordEnterISR();
2211#endif
2212
2213 Context &ctx = GetContext();
2214
2215#ifdef HAL_MODULE_ENABLED // STM32 HAL
2216 // make sure STM32 HAL gets timing information as it depends on SysTick in delaying procedures
2217#if STK_TICKLESS_IDLE
2218 uwTick += static_cast<uint32_t>(ctx.m_sleep_ticks * ctx.m_tick_resolution);
2219#else
2220 HAL_IncTick();
2221#endif
2222
2223 // STM32 HAL starts SysTick on its initialization that will cause a crash on NULL,
2224 // therefore use additional check if HAL_MODULE_ENABLED is defined
2225 if (ctx.m_started)
2226 {
2227#else
2228 {
2229 // make sure SysTick is enabled by the Kernel::Start(), disable its start anywhere else
2230 STK_ASSERT(ctx.m_started);
2231 STK_ASSERT(ctx.m_handler != nullptr);
2232#endif
2233 ctx.ProcessTick();
2234 }
2235
2236 // stop tracing & send packets to host
2237#if STK_SEGGER_SYSVIEW
2238 SEGGER_SYSVIEW_RecordExitISR();
2239 SEGGER_SYSVIEW_IsStarted();
2240#endif
2241}
2242
2243#if STK_SEGGER_SYSVIEW
2244 #define STK_SYSVIEW_CALL(func)\
2245 "PUSH {r0-r3, r7, r11, r12, LR} \n"\
2246 "BL " #func " \n"\
2247 "POP {r0-r3, r7, r11, r12, LR} \n"
2248#endif
2249
2250#if STK_SEGGER_SYSVIEW
2251extern "C" __stk_attr_used void StkSystemView_OnContextSwitch(void)
2252{
2253 Context &ctx = GetContext();
2254
2255 // exit from PendSV
2256 SEGGER_SYSVIEW_RecordExitISR();
2257
2258 if (ctx.m_stack_idle->tid != SYS_TASK_ID_SLEEP)
2259 {
2260 // previous task went idle
2261 SEGGER_SYSVIEW_OnTaskStopReady(ctx.m_stack_idle->tid, TRACE_EVENT_SWITCH);
2262 }
2263
2264 if (ctx.m_stack_active->tid != SYS_TASK_ID_SLEEP)
2265 {
2266 // current task resumed execution
2267 SEGGER_SYSVIEW_OnTaskStartExec(ctx.m_stack_active->tid);
2268 }
2269}
2270#endif
2271
2299#if (STK_MPU_TASK_REGIONS == 2U)
2300#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2301 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2302 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2303 "LDMIA r12, {r4-r7} \n" /* burst load 4 words */\
2304 "STMIA r0, {r4-r7} \n" /* write MPU RBAR..RLAR_A1 (r0 remains anchored) */
2305#elif (STK_MPU_TASK_REGIONS == 4U)
2306#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2307 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2308 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2309 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2310 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 (r0 remains anchored) */
2311#elif (STK_MPU_TASK_REGIONS == 6U)
2312#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2313 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2314 \
2315 /* Block 1 (Regions 0-3 / 8 words) */\
2316 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2317 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2318 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2319 \
2320 /* Block 2 (Regions 4-5 / 4 words) */\
2321 "LDR r12, =%[mpu_rnr_idx4] \n" /* r12 = IDX + 4 */\
2322 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 4 (RNR is RBAR - 4) */\
2323 "ADD r12, r1, %[mpu_reg4_of] \n" /* point to mpu.region[4] */\
2324 "LDMIA r12, {r4-r7} \n" /* burst load 4 words */\
2325 "STMIA r0, {r4-r7} \n" /* write MPU RBAR..RLAR_A1 */\
2326 \
2327 /* Restore RNR */\
2328 "LDR r12, =%[mpu_rnr_idx] \n" /* r12 = IDX */\
2329 "STR r12, [r0, #-4] \n" /* restore MPU->RNR */
2330#elif (STK_MPU_TASK_REGIONS == 8U)
2331#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2332 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2333 \
2334 /* Block 1 (Regions 0-3 / 8 words) */\
2335 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2336 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2337 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2338 \
2339 /* Block 2 (Regions 4-7 / 8 words) */\
2340 "LDR r12, =%[mpu_rnr_idx4] \n" /* r12 = IDX + 4 */\
2341 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 4 (RNR is RBAR - 4) */\
2342 "ADD r12, r1, %[mpu_reg4_of] \n" /* point to mpu.region[4] */\
2343 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2344 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2345 \
2346 /* Restore RNR */\
2347 "LDR r12, =%[mpu_rnr_idx] \n" /* r12 = IDX */\
2348 "STR r12, [r0, #-4] \n" /* restore MPU->RNR */
2349#elif (STK_MPU_TASK_REGIONS == 12U)
2350#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2351 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2352 \
2353 /* Block 1 (Regions 0-3 / 8 words) */\
2354 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2355 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2356 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2357 \
2358 /* Block 2 (Regions 4-7 / 8 words) */\
2359 "LDR r12, =%[mpu_rnr_idx4] \n" /* r12 = IDX + 4 */\
2360 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 4 (RNR is RBAR - 4) */\
2361 "ADD r12, r1, %[mpu_reg4_of] \n" /* point to mpu.region[4] */\
2362 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2363 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2364 \
2365 /* Block 3 (Regions 8-11 / 8 words) */\
2366 "LDR r12, =%[mpu_rnr_idx8] \n" /* r12 = IDX + 8 */\
2367 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 8 */\
2368 "ADD r12, r1, %[mpu_reg8_of] \n" /* point to mpu.region[8] */\
2369 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2370 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2371 \
2372 /* Restore RNR */\
2373 "LDR r12, =%[mpu_rnr_idx] \n" /* r12 = IDX */\
2374 "STR r12, [r0, #-4] \n" /* restore MPU->RNR */
2375#elif (STK_MPU_TASK_REGIONS == 16U)
2376#define STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2377 "LDR r0, =%[mpu_start_of] \n" /* r0 = &MPU->RBAR */\
2378 \
2379 /* Block 1 (Regions 0-3 / 8 words) */\
2380 "ADD r12, r1, %[mpu_reg_of] \n" /* point to mpu.region[0] */\
2381 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2382 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2383 \
2384 /* Block 2 (Regions 4-7 / 8 words) */\
2385 "LDR r12, =%[mpu_rnr_idx4] \n" /* r12 = IDX + 4 */\
2386 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 4 (RNR is RBAR - 4) */\
2387 "ADD r12, r1, %[mpu_reg4_of] \n" /* point to mpu.region[4] */\
2388 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2389 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2390 \
2391 /* Block 3 (Regions 8-11 / 8 words) */\
2392 "LDR r12, =%[mpu_rnr_idx8] \n" /* r12 = IDX + 8 */\
2393 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 8 */\
2394 "ADD r12, r1, %[mpu_reg8_of] \n" /* point to mpu.region[8] */\
2395 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2396 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2397 \
2398 /* Block 4 (Regions 12-15 / 8 words) */\
2399 "LDR r12, =%[mpu_rnr_idx12] \n" /* r12 = IDX + 12 */\
2400 "STR r12, [r0, #-4] \n" /* MPU->RNR = IDX + 12 */\
2401 "ADD r12, r1, %[mpu_reg12_of] \n" /* point to mpu.region[12] */\
2402 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2403 "STMIA r0, {r4-r11} \n" /* write MPU RBAR..RLAR_A3 */\
2404 \
2405 /* Restore RNR */\
2406 "LDR r12, =%[mpu_rnr_idx] \n" /* r12 = IDX */\
2407 "STR r12, [r0, #-4] \n" /* restore MPU->RNR */
2408#else
2409 #error "Unsupported region count, allowed - 2, 4, 6, 8, 12, 16!"
2410#endif
2411
2412#if STK_CORTEX_M_TRUSTZONE_FRAME
2413 #if (STK_MPU_TASK_REGIONS_NS == 2U)
2414 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2415 \
2416 /* Non-Secure Dual MPU (2 regions / 4 words) */\
2417 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2418 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2419 "LDMIA r12, {r4-r7} \n" /* burst load 4 words */\
2420 "STMIA r0, {r4-r7} \n" /* write MPU_NS RBAR..RLAR_A1 */
2421 #elif (STK_MPU_TASK_REGIONS_NS == 4U)
2422 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2423 \
2424 /* Non-Secure Dual MPU (4 regions / 8 words) */\
2425 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2426 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2427 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2428 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */
2429 #elif (STK_MPU_TASK_REGIONS_NS == 6U)
2430 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2431 \
2432 /* Non-Secure Dual MPU (6 regions) */\
2433 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2434 \
2435 /* NS Block 1 (Regions 0-3 / 8 words) */\
2436 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2437 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2438 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2439 \
2440 /* NS Block 2 (Regions 4-5 / 4 words) */\
2441 "LDR r12, =%[mpu_ns_rnr_idx4] \n" /* r12 = IDX + 4 */\
2442 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 4 */\
2443 "ADD r12, r1, %[mpu_ns_reg4_of]\n" /* point to mpu_ns.region[4] */\
2444 "LDMIA r12, {r4-r7} \n" /* burst load 4 words */\
2445 "STMIA r0, {r4-r7} \n" /* write MPU_NS RBAR..RLAR_A1 */\
2446 \
2447 /* Restore NS RNR */\
2448 "LDR r12, =%[mpu_ns_rnr_idx] \n" /* r12 = IDX */\
2449 "STR r12, [r0, #-4] \n" /* restore MPU_NS->RNR */
2450 #elif (STK_MPU_TASK_REGIONS_NS == 8U)
2451 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2452 \
2453 /* Non-Secure Dual MPU (8 regions) */\
2454 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2455 \
2456 /* NS Block 1 (Regions 0-3 / 8 words) */\
2457 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2458 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2459 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2460 \
2461 /* NS Block 2 (Regions 4-7 / 8 words) */\
2462 "LDR r12, =%[mpu_ns_rnr_idx4] \n" /* r12 = IDX + 4 */\
2463 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 4 */\
2464 "ADD r12, r1, %[mpu_ns_reg4_of]\n" /* point to mpu_ns.region[4] */\
2465 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2466 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2467 \
2468 /* Restore NS RNR */\
2469 "LDR r12, =%[mpu_ns_rnr_idx] \n" /* r12 = IDX */\
2470 "STR r12, [r0, #-4] \n" /* restore MPU_NS->RNR */
2471 #elif (STK_MPU_TASK_REGIONS_NS == 12U)
2472 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2473 \
2474 /* Non-Secure Dual MPU (12 regions / 3 blocks) */\
2475 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2476 \
2477 /* NS Block 1 (Regions 0-3 / 8 words) */\
2478 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2479 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2480 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2481 \
2482 /* NS Block 2 (Regions 4-7 / 8 words) */\
2483 "LDR r12, =%[mpu_ns_rnr_idx4] \n" /* r12 = IDX + 4 */\
2484 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 4 */\
2485 "ADD r12, r1, %[mpu_ns_reg4_of]\n" /* point to mpu_ns.region[4] */\
2486 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2487 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2488 \
2489 /* NS Block 3 (Regions 8-11 / 8 words) */\
2490 "LDR r12, =%[mpu_ns_rnr_idx8] \n" /* r12 = IDX + 8 */\
2491 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 8 */\
2492 "ADD r12, r1, %[mpu_ns_reg8_of]\n" /* point to mpu_ns.region[8] */\
2493 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2494 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2495 \
2496 /* Restore NS RNR */\
2497 "LDR r12, =%[mpu_ns_rnr_idx] \n" /* r12 = IDX */\
2498 "STR r12, [r0, #-4] \n" /* restore MPU_NS->RNR */
2499 #elif (STK_MPU_TASK_REGIONS_NS == 16U)
2500 #define STK_ASM_BLOCK_MPU_NS_STACK_GUARD\
2501 \
2502 /* Non-Secure Dual MPU (16 regions / 4 blocks) */\
2503 "LDR r0, =%[mpu_ns_start_of] \n" /* r0 = &MPU_NS->RBAR */\
2504 \
2505 /* NS Block 1 (Regions 0-3 / 8 words) */\
2506 "ADD r12, r1, %[mpu_ns_reg_of] \n" /* point to mpu_ns.region[0] */\
2507 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2508 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2509 \
2510 /* NS Block 2 (Regions 4-7 / 8 words) */\
2511 "LDR r12, =%[mpu_ns_rnr_idx4] \n" /* r12 = IDX + 4 */\
2512 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 4 */\
2513 "ADD r12, r1, %[mpu_ns_reg4_of]\n" /* point to mpu_ns.region[4] */\
2514 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2515 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2516 \
2517 /* NS Block 3 (Regions 8-11 / 8 words) */\
2518 "LDR r12, =%[mpu_ns_rnr_idx8] \n" /* r12 = IDX + 8 */\
2519 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 8 */\
2520 "ADD r12, r1, %[mpu_ns_reg8_of]\n" /* point to mpu_ns.region[8] */\
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 4 (Regions 12-15 / 8 words) */\
2525 "LDR r12, =%[mpu_ns_rnr_idx12] \n" /* r12 = IDX + 12 */\
2526 "STR r12, [r0, #-4] \n" /* MPU_NS->RNR = IDX + 12 */\
2527 "ADD r12, r1, %[mpu_ns_reg12_of]\n" /* point to mpu_ns.region[12] */\
2528 "LDMIA r12, {r4-r11} \n" /* burst load 8 words */\
2529 "STMIA r0, {r4-r11} \n" /* write MPU_NS RBAR..RLAR_A3 */\
2530 \
2531 /* Restore NS RNR */\
2532 "LDR r12, =%[mpu_ns_rnr_idx] \n" /* r12 = IDX */\
2533 "STR r12, [r0, #-4] \n" /* restore MPU_NS->RNR */
2534 #else
2535 #error "Unsupported region count, allowed - 2, 4, 6, 8, 12, 16!"
2536 #endif
2537#endif
2538
2539#if STK_CORTEX_M_TRUSTZONE_FRAME
2540 #define STK_ASM_BLOCK_MPU_STACK_GUARD\
2541 "MOV r1, %[st_active] \n" /* r1 = Stack* */\
2542 STK_ASM_BLOCK_MPU_S_STACK_GUARD\
2543 STK_ASM_BLOCK_MPU_NS_STACK_GUARD
2544#else
2545 #define STK_ASM_BLOCK_MPU_STACK_GUARD\
2546 "MOV r1, %[st_active] \n" /* r1 = Stack* */\
2547 STK_ASM_BLOCK_MPU_S_STACK_GUARD
2548#endif
2549
2550extern "C" __stk_attr_naked void STK_PENDSV_HANDLER()
2551{
2552#if (STK_ARCH_CPU_COUNT > 1U)
2553 // optimize register utilization and prevent compiler from using r4-r11
2554 // registers before they are saved. Use r12 IPC scratch register for that
2555 // and calculate offset for p_ctx only once; instruct assembler to use
2556 // r3, r2 for holding pointers to Idle and Active stacks
2557 #ifdef __ICCARM__
2558 register Context *p_ctx = &GetContext();
2559 register Stack *p_st_idle = p_ctx->m_stack_idle;
2560 register Stack *p_st_active = p_ctx->m_stack_active;
2561 #else
2562 register Context *p_ctx __asm("r12") = &GetContext();
2563 register Stack *p_st_idle __asm("r3") = p_ctx->m_stack_idle;
2564 register Stack *p_st_active __asm("r2") = p_ctx->m_stack_active;
2565 #endif
2566#endif
2567
2568 __asm volatile(
2570
2571 STK_ASM_DISABLE_INTERRUPTS " \n"
2572
2573 // start tracing
2574#if STK_SEGGER_SYSVIEW
2575 STK_SYSVIEW_CALL(StkSystemView_RecordEnterISR)
2576#endif
2577
2578 // save the Secure PSP unconditionally; it is the spine of the entire context
2579 // frame regardless of whether the task was interrupted inside Secure or
2580 // Non-Secure code; PSP_NS, CONTROL_NS, and both PSPLIMs are embedded inside
2581 // the frame (in TrustZoneFrame) so the restore path never needs to branch on
2582 // EXC_RETURN; the CPU's own EXC_RETURN S-bit in the restored LR selects the
2583 // correct return world on "BX LR"
2584 "MRS r0, PSP \n"
2585
2586#if STK_CORTEX_M_FPU
2587 // Save FP registers.
2588 "TST LR, #16 \n" /* test LR for 0xffffffe_, e.g. Thread mode with FP data */
2589
2590 "IT EQ \n" /* if result is positive */
2591 "VMOVEQ.F32 s0, s0 \n" /* force hardware lazy state preservation */
2592
2593 "IT EQ \n" /* if result is positive */
2594 "VSTMDBEQ r0!, {s16-s31} \n" /* store 16 SP registers */
2595#endif
2596
2597 // save registers of inactive task's CPU context:
2598
2599#if STK_CORTEX_M_MANAGE_LR
2600 // save r4-r11 and LR
2601 // note: for Cortex-M3 and higher save LR to keep correct Thread state of the
2602 // task when it is restored
2603 "STMDB r0!, {r4-r11, LR}\n"
2604#else
2605 // note: STMIA is limited to r0-r7 range, therefore save via stack memory
2606 "SUBS r0, r0, #16 \n" /* decrement for r4-r7 */
2607 "STMIA r0!, {r4-r7} \n"
2608 "MOV r4, r8 \n"
2609 "MOV r5, r9 \n"
2610 "MOV r6, r10 \n"
2611 "MOV r7, r11 \n"
2612 "SUBS r0, r0, #32 \n" /* decrement for r8-r11 and pointer reset */
2613 "STMIA r0!, {r4-r7} \n"
2614 "SUBS r0, r0, #16 \n" /* final pointer adjustment */
2615#endif
2616
2617 // ARMv8-M TrustZone: save PSPLIM (Secure), PSPLIM_NS, PSP_NS, and CONTROL_NS
2618 // into the Secure stack frame below the callee-saved registers, matching
2619 // TrustZoneFrame layout; PSP_NS captures the task's Non-Secure stack mid-execution
2620 // (may be inside an NS call that was interrupted); CONTROL_NS preserves NS
2621 // privilege / stack-select
2622#if STK_CORTEX_M_TRUSTZONE_FRAME
2623 // push PSPLIM, PSPLIM_NS (equivalent to STMDB r0!, {PSPLIM, PSPLIM_NS})
2624 "MRS r12, PSPLIM_NS \n"
2625 "STR r12, [r0, #-4]! \n"
2626 "MRS r12, PSPLIM \n"
2627 "STR r12, [r0, #-4]! \n"
2628
2629 // push PSP_NS, CONTROL_NS (equivalent to STMDB r0!, {PSP_NS, CONTROL_NS})
2630 "MRS r12, CONTROL_NS \n"
2631 "STR r12, [r0, #-4]! \n"
2632 "MRS r12, PSP_NS \n"
2633 "STR r12, [r0, #-4]! \n"
2634#endif
2635
2636 // save the outgoing task's live CONTROL register (nPRIV, SPSEL, FPCA, SFPA)
2637 // so that any privilege boost still in effect (SVC_BOOST_PRIV) is preserved verbatim
2638#if STK_CORTEX_M_PRIVILEGE_FRAME
2639 "MRS r12, CONTROL \n"
2640 "STR r12, [r0, #-4]! \n"
2641#endif
2642
2643 // store in GetContext().m_stack_idle
2644 "STR r0, [%[st_idle]] \n" /* store the first member (Stack::SP) from r0 */
2645
2646 // Set per-task MPU guard.
2647#if STK_MPU_STACK_GUARD
2648 STK_ASM_BLOCK_MPU_STACK_GUARD
2649#endif
2650
2651 // load stack of the active task from GetContext().m_stack_active
2652 // note: keep in sync with OnTaskStart
2653 "LDR r0, [%[st_active]]\n" /* load the first member of Stack (Stack::SP) into r0 */
2654
2655 // restore the incoming task's live CONTROL register exactly as it was
2656 // captured when that task was last switched out (or as initialized by
2657 // InitStack for a brand-new task); this is the lowest-addressed word of the
2658 // saved frame (pushed last), so it is popped first, before TrustZoneFrame
2659#if STK_CORTEX_M_PRIVILEGE_FRAME
2660 "LDR r12, [r0], %[access_mode_of] \n"
2661 "MSR CONTROL, r12 \n" /* note: on EXC_RETURN platform flushes instruction cache, therefore we omit explicit ISB */
2662#endif
2663
2664 // ARMv8-M TrustZone: restore TrustZoneFrame fields in reverse push order.
2665 // Pop order: PSP_NS + CONTROL_NS first (pushed last), then PSPLIM + PSPLIM_NS.
2666 // After this, r0 points to the callee-saved register region.
2667 // PSP_S is restored unconditionally after general-register pop.
2668 // CONTROL_NS restores NS privilege/stack-select; PSP_NS restores the task's
2669 // Non-Secure stack to exactly where it was when the task was interrupted.
2670 // EXC_RETURN in LR (restored by LDMIA below) carries the S-bit that tells
2671 // the CPU which world to return to on "BX LR" - no explicit branch needed.
2672#if STK_CORTEX_M_TRUSTZONE_FRAME
2673 "LDR r12, [r0], #4 \n" /* PSP_NS */
2674 "MSR PSP_NS, r12 \n"
2675 "LDR r12, [r0], #4 \n" /* CONTROL_NS */
2676 "MSR CONTROL_NS, r12 \n"
2677
2678 "LDR r12, [r0], #4 \n" /* PSPLIM */
2679 "MSR PSPLIM, r12 \n"
2680 "LDR r12, [r0], #4 \n" /* PSPLIM_NS */
2681 "MSR PSPLIM_NS, r12 \n"
2682#endif
2683
2684 // restore registers of active task's CPU context:
2685
2686#if STK_CORTEX_M_MANAGE_LR
2687 // restore r4-r11 and LR
2688 "LDMIA r0!, {r4-r11, LR}\n"
2689#else
2690 // note: LDMIA is limited to r0-r7 range, therefore load via stack memory
2691 "LDMIA r0!, {r4-r7} \n"
2692 "MOV r8, r4 \n"
2693 "MOV r9, r5 \n"
2694 "MOV r10, r6 \n"
2695 "MOV r11, r7 \n"
2696 "LDMIA r0!, {r4-r7} \n"
2697#endif
2698
2699#if STK_CORTEX_M_FPU
2700 // restore FP registers
2701 "TST LR, #16 \n" /* test LR for 0xffffffe_, e.g. Thread mode with FP data */
2702 "IT EQ \n" /* if result is positive */
2703 "VLDMIAEQ r0!, {s16-s31} \n" /* restore FP registers */
2704#endif
2705
2706 // Restore PSP.
2707 "MSR PSP, r0 \n"
2708
2709 // stop tracing
2710#if STK_SEGGER_SYSVIEW
2711 STK_SYSVIEW_CALL(StkSystemView_OnContextSwitch)
2712#endif
2713
2714 STK_ASM_ENABLE_INTERRUPTS " \n"
2715
2716 STK_ASM_EXIT_FROM_HANDLER " \n"
2717
2718 : /* output: none */
2719 : [access_mode_of] "i" (offsetof(Stack, access_mode))
2720#if (STK_ARCH_CPU_COUNT > 1U)
2721 #ifdef __ICCARM__
2722 , [st_idle] "r3" (p_st_idle)
2723 , [st_active] "r2" (p_st_active)
2724 #else
2725 , [st_idle] "r" (p_st_idle)
2726 , [st_active] "r" (p_st_active)
2727 #endif
2728#else
2729 , [st_idle] "r" (GetContext().m_stack_idle)
2730 , [st_active] "r" (GetContext().m_stack_active)
2731#endif
2732#if STK_MPU_STACK_GUARD
2733 , [mpu_start_of] "i" (&MPU->RBAR)
2734 , [mpu_reg_of] "i" (offsetof(Stack, mpu.region[0].addr))
2735 #if (STK_MPU_TASK_REGIONS > 4U)
2736 , [mpu_rnr_idx] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX)
2737 , [mpu_reg4_of] "i" (offsetof(Stack, mpu.region[4].addr))
2738 , [mpu_rnr_idx4] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 4U)
2739 #endif
2740 #if (STK_MPU_TASK_REGIONS > 8U)
2741 , [mpu_reg8_of] "i" (offsetof(Stack, mpu.region[8].addr))
2742 , [mpu_rnr_idx8] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 8U)
2743 #endif
2744 #if (STK_MPU_TASK_REGIONS > 12U)
2745 , [mpu_reg12_of] "i" (offsetof(Stack, mpu.region[12].addr))
2746 , [mpu_rnr_idx12] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 12U)
2747 #endif
2748 #if STK_CORTEX_M_TRUSTZONE_FRAME
2749 , [mpu_ns_start_of] "i" (&MPU_NS->RBAR)
2750 , [mpu_ns_reg_of] "i" (offsetof(Stack, mpu_ns.region[0].addr))
2751 #if (STK_MPU_TASK_REGIONS_NS > 4U)
2752 , [mpu_ns_rnr_idx] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS)
2753 , [mpu_ns_reg4_of] "i" (offsetof(Stack, mpu_ns.region[4].addr))
2754 , [mpu_ns_rnr_idx4] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 4U)
2755 #endif
2756 #if (STK_MPU_TASK_REGIONS_NS > 8U)
2757 , [mpu_ns_reg8_of] "i" (offsetof(Stack, mpu_ns.region[8].addr))
2758 , [mpu_ns_rnr_idx8] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 8U)
2759 #endif
2760 #if (STK_MPU_TASK_REGIONS_NS > 12U)
2761 , [mpu_ns_reg12_of] "i" (offsetof(Stack, mpu_ns.region[12].addr))
2762 , [mpu_ns_rnr_idx12] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 12U)
2763 #endif
2764 #endif
2765#endif
2766 : "r0" /* used as a scratchpad throughout */
2767#if STK_MPU_STACK_GUARD
2768 , "r1"
2769#endif
2770 , "memory"
2771#if (STK_CORTEX_M_TRUSTZONE_FRAME || STK_MPU_STACK_GUARD || STK_CORTEX_M_PRIVILEGE_FRAME)
2772 , "r12"
2773#endif
2774#if STK_CORTEX_M_FPU
2775 , "cc"
2776#endif
2777 );
2778}
2779
2780namespace stk {
2781__stk_attr_naked void OnTaskStart()
2782{
2783 // note: HW_DisableInterrupts() must be called prior calling this function
2784
2785 __asm volatile(
2787
2788 // set per-task MPU guard
2789#if STK_MPU_STACK_GUARD
2790 STK_ASM_BLOCK_MPU_STACK_GUARD
2791#endif
2792
2793 // load stack of the active task from GetContext().m_stack_active
2794 // note: keep in sync with OnTaskStart
2795 "LDR r0, [%[st_active]]\n" /* load the first member of Stack (Stack::SP) into r0, %[st_active] is a pointer value register */
2796
2797 // restore this task's CONTROL register as initialized by InitStack (see
2798 // PrivilegeFrame); lowest-addressed word of the saved frame, popped first
2799#if STK_CORTEX_M_PRIVILEGE_FRAME
2800 "LDR r12, [r0], %[access_mode_of] \n"
2801 "MSR CONTROL, r12 \n" /* note: on EXC_RETURN platform flushes instruction cache, therefore we omit explicit ISB */
2802#endif
2803
2804 // ARMv8-M TrustZone: restore TrustZoneFrame fields in reverse push order (see STK_PENDSV_HANDLER);
2805 // pop order: PSP_NS + CONTROL_NS first, then PSPLIM + PSPLIM_NS
2806#if STK_CORTEX_M_TRUSTZONE_FRAME
2807 "LDR r12, [r0], #4 \n" /* PSP_NS */
2808 "MSR PSP_NS, r12 \n"
2809 "LDR r12, [r0], #4 \n" /* CONTROL_NS */
2810 "MSR CONTROL_NS, r12 \n"
2811
2812 "LDR r12, [r0], #4 \n" /* PSPLIM */
2813 "MSR PSPLIM, r12 \n"
2814 "LDR r12, [r0], #4 \n" /* PSPLIM_NS */
2815 "MSR PSPLIM_NS, r12 \n"
2816#endif
2817
2818 // restore registers of active task's CPU context:
2819
2820#if STK_CORTEX_M_MANAGE_LR
2821 // restore r4-r11 and LR
2822 "LDMIA r0!, {r4-r11, LR}\n"
2823#else
2824 // note: LDMIA is limited to r0-r7 range, therefore load via stack memory
2825 "LDMIA r0!, {r4-r7} \n"
2826 "MOV r8, r4 \n"
2827 "MOV r9, r5 \n"
2828 "MOV r10, r6 \n"
2829 "MOV r11, r7 \n"
2830 "LDMIA r0!, {r4-r7} \n"
2831#endif
2832
2833#if STK_CORTEX_M_FPU
2834 // restore FP registers
2835 "TST LR, #16 \n" /* test LR for 0xffffffe_, e.g. Thread mode with FP data */
2836 "IT EQ \n" /* if result is positive */
2837 "VLDMIAEQ r0!, {s16-s31} \n" /* restore FP registers */
2838#endif
2839
2840 // restore PSP
2841 "MSR PSP, r0 \n"
2842
2843#if !STK_CORTEX_M_MANAGE_LR
2844 // M0: set LR to Thread mode, use PSP state and stack
2845 "LDR r0, =%[exc_ret] \n"
2846 "MOV LR, r0 \n"
2847#endif
2848
2849 STK_ASM_ENABLE_INTERRUPTS " \n"
2850
2851 STK_ASM_EXIT_FROM_HANDLER " \n"
2852
2853 : /* output: none */
2854 : [access_mode_of] "i" (offsetof(Stack, access_mode))
2855 , [st_active] "r" (GetContext().m_stack_active)
2856#if !STK_CORTEX_M_MANAGE_LR
2857 , [exc_ret] "i" (STK_CORTEX_M_EXC_RETURN_THREAD_PSP)
2858#endif
2859#if STK_MPU_STACK_GUARD
2860 , [mpu_start_of] "i" (&MPU->RBAR)
2861 , [mpu_reg_of] "i" (offsetof(Stack, mpu.region[0].addr))
2862 #if (STK_MPU_TASK_REGIONS > 4U)
2863 , [mpu_rnr_idx] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX)
2864 , [mpu_reg4_of] "i" (offsetof(Stack, mpu.region[4].addr))
2865 , [mpu_rnr_idx4] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 4U)
2866 #endif
2867 #if (STK_MPU_TASK_REGIONS > 8U)
2868 , [mpu_reg8_of] "i" (offsetof(Stack, mpu.region[8].addr))
2869 , [mpu_rnr_idx8] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 8U)
2870 #endif
2871 #if (STK_MPU_TASK_REGIONS > 12U)
2872 , [mpu_reg12_of] "i" (offsetof(Stack, mpu.region[12].addr))
2873 , [mpu_rnr_idx12] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX + 12U)
2874 #endif
2875 #if STK_CORTEX_M_TRUSTZONE_FRAME
2876 , [mpu_ns_start_of] "i" (&MPU_NS->RBAR)
2877 , [mpu_ns_reg_of] "i" (offsetof(Stack, mpu_ns.region[0].addr))
2878 #if (STK_MPU_TASK_REGIONS_NS > 4U)
2879 , [mpu_ns_rnr_idx] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS)
2880 , [mpu_ns_reg4_of] "i" (offsetof(Stack, mpu_ns.region[4].addr))
2881 , [mpu_ns_rnr_idx4] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 4U)
2882 #endif
2883 #if (STK_MPU_TASK_REGIONS_NS > 8U)
2884 , [mpu_ns_reg8_of] "i" (offsetof(Stack, mpu_ns.region[8].addr))
2885 , [mpu_ns_rnr_idx8] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 8U)
2886 #endif
2887 #if (STK_MPU_TASK_REGIONS_NS > 12U)
2888 , [mpu_ns_reg12_of] "i" (offsetof(Stack, mpu_ns.region[12].addr))
2889 , [mpu_ns_rnr_idx12] "i" (STK_CORTEX_M_MPU_TASK_REGION_IDX_NS + 12U)
2890 #endif
2891 #endif
2892#endif
2893 : "r0" /* used as a scratchpad throughout */
2894#if STK_MPU_STACK_GUARD
2895 , "r1"
2896#endif
2897 , "memory"
2898#if (STK_CORTEX_M_TRUSTZONE_FRAME || STK_MPU_STACK_GUARD || STK_CORTEX_M_PRIVILEGE_FRAME)
2899 , "r12"
2900#endif
2901#if STK_CORTEX_M_FPU
2902 , "cc"
2903#endif
2904 );
2905}
2906} // namespace stk
2907
2908void Context::Start()
2909{
2910 m_exiting = false;
2911
2912 // enable FPU before SaveJmp as it references FPU with VMRS
2913 HW_EnableFullFpuAccess();
2914
2915 // save jump location of the Exit trap
2916 STK_UNUSED(SaveJmp(m_exit_buf));
2917 if (m_exiting)
2918 {
2919 // notify kernel about a full stop
2920 m_handler->OnStop();
2921 }
2922 else
2923 {
2924 HW_StartScheduler();
2925 }
2926}
2927
2928void Context::OnStart()
2929{
2930 // interrupts must be disabled at this point
2931 STK_ASSERT(HW_InterruptsDisabled());
2932
2933 // clear FPU usage status if FPU was used before kernel start
2934 HW_ClearFpuState();
2935
2936 // get the first active stack from the kernel
2937 m_handler->OnStart(m_stack_active);
2938
2939 // configure MPU
2940#if STK_MPU
2941 ConfigureMpu();
2942#endif
2943
2944 // set lowest priority for PendSV (SysTick priority is set in StartTickTimer)
2945 NVIC_SetPriority(PendSV_IRQn, STK_CORTEX_M_ISR_PRIORITY_LOWEST);
2946 // set highest priority for SVC interrupts to support critical section for unprivileged tasks
2947#ifdef CONTROL_nPRIV_Msk
2948 NVIC_SetPriority(SVCall_IRQn, STK_CORTEX_M_SVCALL_ISR_PRIORITY);
2949#endif
2950
2951 m_started = true;
2952
2953 // start recording task execution start
2954#if STK_SEGGER_SYSVIEW
2955 SEGGER_SYSVIEW_OnTaskStartExec(m_stack_active->tid);
2956#endif
2957
2958 // start with initially 1 elapsed tick (after timer expires), should be the last
2959 // in OnStart to provide full time window to the first starting task
2960 StartTickTimer(1);
2961}
2962
2963#if STK_TICKLESS_IDLE
2964Timeout Context::Suspend()
2965{
2966 const uint32_t tick_resolution = GetTickResolutionInClockCycles();
2967 if (tick_resolution == 0U)
2968 {
2969 STK_ASSERT(false);
2970 return NO_WAIT;
2971 }
2972
2973 HW_DisableInterrupts();
2974
2975 // pause SysTick in order to read elapsed value
2976 HW_SysTickDisable();
2977
2978 // get already elapsed CPU cycles since SysTick ISR invocation up to SysTick timer stop (see above)
2979 // to account for them for a new period value
2980 const uint32_t elapsed = HW_SysTickElapsed(HW_SysTickValueAfterDisable());
2981
2982 // stop SysTick timer
2983 HW_SysTickStop();
2984
2985 // clear pending PendSV exception
2986 HW_ClearPendingSwitch();
2987
2988 // notify core about suspension (it will also yield currently active task forcibly)
2989 m_handler->OnSuspend(true);
2990
2991 // update tasks and out currently active task (if any) into a sleep, it will cause a switch
2992 // to a sleep trap after HW_EnableInterrupts, otherwise not
2993 Timeout no_sleep = 0;
2994 OnTick(no_sleep);
2995
2996 // get already elapsed ticks since the OnTick and a call to Suspend(), we shall account for this
2997 // period and return only the remainder
2998 const Timeout elapsed_ticks = static_cast<Timeout>(elapsed / tick_resolution);
2999 const Timeout sleep_ticks = Max(m_sleep_ticks - elapsed_ticks, static_cast<Timeout>(0));
3000
3001 HW_EnableInterrupts();
3002
3003 return sleep_ticks;
3004}
3005#endif // STK_TICKLESS_IDLE
3006
3007#if STK_TICKLESS_IDLE
3008void Context::Resume(Timeout elapsed_ticks)
3009{
3010 HW_DisableInterrupts();
3011
3012 // notify core
3013 m_handler->OnSuspend(false);
3014
3015 // start with initially elapsed ticks (OnTick will fire with elapsed_ticks + 1)
3016 StartTickTimer(elapsed_ticks + 1);
3017
3018 HW_EnableInterrupts();
3019}
3020#endif // STK_TICKLESS_IDLE
3021
3022// __stk_attr_used required for Link-Time Optimization (-flto)
3023extern "C" __stk_attr_used void StkSVCHandlerMain(Word *svc_args)
3024{
3025 // start tracing
3026#if STK_SEGGER_SYSVIEW
3027 SEGGER_SYSVIEW_RecordEnterISR();
3028#endif
3029
3030 // Word is typedef uintptr_t (stk_common.h) - the only integer type the Standard
3031 // blesses for lossless pointer round-trips (MISRA C++ 5-2-8, CERT INT36-C)
3032 STK_STATIC_ASSERT_DESC_N(PTR, sizeof(Word) == sizeof(void *),
3033 "Word must be uintptr_t width for safe pointer round-trip via frame->PC");
3034
3035 // priority 0 (NMI, HardFault) unaffected: SVC (priority 0 per OnStart()) remains
3036 // reachable so SVC_EXIT_CRITICAL can always unwind
3037 STK_STATIC_ASSERT_DESC_N(NVIC, __NVIC_PRIO_BITS < 32U,
3038 "NVIC priority bit width exceeds safe shift range");
3039
3040 // 'volatile': R0 is written back to stacked memory, compiler must not eliminate the store
3041 volatile hw::ExceptionFrame *const frame = reinterpret_cast<volatile hw::ExceptionFrame *>(svc_args);
3042
3043 // details: https://developer.arm.com/documentation/ka004005/latest
3044 // Thumb SVC encoding: [15:8] = 0xDF, [7:0] = imm8
3045 // opcode lives two bytes (one Thumb halfword) before the stacked PC:
3046 const uint8_t *const insn_ptr = hw::WordToPtr<const uint8_t>(frame->PC - 2U);
3047 const ESvcCommandId command = static_cast<ESvcCommandId>(*insn_ptr);
3048
3049 switch (command)
3050 {
3051 case SVC_START_SCHEDULING: {
3052 Context &ctx = GetContext();
3053
3054 // disallow duplicate attempt
3055 STK_ASSERT(!ctx.m_started);
3056 if (!ctx.m_started)
3057 {
3058 // make sure interrupts do not interfere, OnStart expects interrupts disabled
3059 HW_DisableInterrupts();
3060
3061 ctx.OnStart();
3062
3063 // start first task
3064 OnTaskStart();
3065 }
3066 break; }
3067
3068#if STK_CORTEX_M_FORCE_SWITCH
3069 case SVC_FORCE_SWITCH: {
3070 Context &ctx = GetContext();
3071
3072 STK_ASSERT(ctx.m_started);
3073
3074 ctx.OnForceContextSwitch(frame->R0);
3075 break; }
3076#endif
3077
3078#if STK_MPU
3079 case SVC_BOOST_PRIV: {
3080 // limit access to Privilege escalation to STK_MPU_SHARED_CODE_SECTION functions only
3081 // which are read-only and immutable
3082 if ((frame->PC >= hw::PtrToWord(__stk_mpu_shared_code_start)) &&
3083 (frame->PC <= hw::PtrToWord(__stk_mpu_shared_code_end)))
3084 {
3085 __set_CONTROL(__get_CONTROL() & ~CONTROL_nPRIV_Msk);
3086 }
3087 else
3088 {
3090 }
3091 break; }
3092#endif // STK_MPU
3093
3094#ifdef CONTROL_nPRIV_Msk
3095 case SVC_ENTER_CRITICAL: {
3096 const uint32_t saved_basepri = __get_BASEPRI();
3097 __set_BASEPRI(static_cast<uint32_t>(1U) << __NVIC_PRIO_BITS); // mask all configurable-priority interrupts
3098 __DSB(); // BASEPRI write visible to bus before SVC return
3099 __ISB(); // pipeline flush: mask in effect at first caller instruction
3100
3101 GetContext().OnEnterCriticalSection(saved_basepri);
3102 break; }
3103
3104 case SVC_EXIT_CRITICAL: {
3105 uint32_t saved_basepri;
3106 if (GetContext().OnExitCriticalSection(saved_basepri))
3107 {
3108 __DSB(); // drain pending stores before widening interrupt window
3109 __set_BASEPRI(saved_basepri); // restore saved BASEPRI
3110 __ISB(); // pending interrupts at restored priority may fire now
3111 }
3112 break; }
3113#endif // CONTROL_nPRIV_Msk
3114
3115 default: {
3116 // any SVC number not in ESvcCommandId is a defect, panic unconditionally
3118 break; }
3119 }
3120
3121 // stop tracing
3122#if STK_SEGGER_SYSVIEW
3123 SEGGER_SYSVIEW_RecordExitISR();
3124#endif
3125}
3126
3127// details: "How to Write an SVC Function", https://developer.arm.com/documentation/ka004005/latest
3128extern "C" __stk_attr_naked void STK_SVC_HANDLER()
3129{
3130 __asm volatile(
3132 STK_ASM_GLOBAL_SYMBOL(StkSVCHandlerMain)
3133 STK_ASM_ALIGN_2 // ensure the entry point is aligned
3134
3136
3137 // even on Cortex-M3+, a long jump is safer when using LTO, we load address
3138 // into register to allow far jump (>2KB)
3139 "LDR r1, =StkSVCHandlerMain \n"
3140 "BX r1 \n"
3141
3142 STK_ASM_ALIGN_2 // ensure literal pool is aligned
3143 STK_ASM_POOL // ensure literal pool is reachable
3144 );
3145}
3146
3147#if (STK_USE_MEMMANAGE_HANDLER && defined(STK_MEMMANAGE_HANDLER)) ||\
3148 (STK_USE_HARDFAULT_HANDLER && defined(STK_HARDFAULT_HANDLER))
3149extern "C" __stk_attr_used
3150void StkExceptionHandlerMain(const Word *stacked_regs, Word exc_id)
3151{
3152 Word exc_return;
3153 __asm volatile ("MOV %0, LR" : "=r" (exc_return) :: "memory"); // EXC_RETURN, if not already clobbered
3154
3155 bool handled = false;
3156 Context &ctx = GetContext();
3157
3158 if (ctx.m_overrider != nullptr)
3159 {
3160 static FaultContext fault_ctx;
3161 fault_ctx.Fill(stacked_regs, exc_return);
3162
3163 handled = ctx.m_overrider->OnException(static_cast<EHwException>(exc_id), ctx.GetCurrentTId(),
3164 &fault_ctx);
3165 }
3166
3167 if (!handled)
3168 {
3169 // default handler of memory management exception:
3170
3171 // 1. break in Debugger
3173
3174 // 2. restart device
3175 NVIC_SystemReset();
3176 }
3177}
3178#endif // STK_USE_MEMMANAGE_HANDLER || STK_USE_HARDFAULT_HANDLER
3179
3180#if STK_USE_MEMMANAGE_HANDLER && defined(STK_MEMMANAGE_HANDLER)
3181extern "C" __stk_attr_naked void STK_MEMMANAGE_HANDLER()
3182{
3183 __asm volatile(
3185 STK_ASM_GLOBAL_SYMBOL(StkExceptionHandlerMain)
3187
3189
3190 "MOVS r1, %0 \n"
3191 "LDR r2, =StkExceptionHandlerMain \n"
3192 "BX r2 \n"
3193
3196 : /* no output */
3197 : "i" (HW_EXCEPT_MEMACCESS)
3198 : /* no clobber */
3199 );
3200}
3201#endif // STK_USE_MEMMANAGE_HANDLER
3202
3203#if STK_USE_HARDFAULT_HANDLER && defined(STK_HARDFAULT_HANDLER)
3204extern "C" __stk_attr_naked void STK_HARDFAULT_HANDLER()
3205{
3206 __asm volatile(
3208 STK_ASM_GLOBAL_SYMBOL(StkExceptionHandlerMain)
3210
3212
3213 "MOVS r1, %0 \n"
3214 "LDR r2, =StkExceptionHandlerMain \n"
3215 "BX r2 \n"
3216
3219 : /* no output */
3220 : "i" (HW_EXCEPT_FATAL)
3221 : /* no clobber */
3222 );
3223}
3224#endif // STK_USE_HARDFAULT_HANDLER
3225
3227void stk::OnTaskRun(ITask *runnable)
3228{
3229 STK_ASSERT(runnable != nullptr);
3230 runnable->Run();
3231}
3232
3234void stk::OnTaskExit()
3235{
3236 Context &ctx = GetContext();
3237
3238 const uint32_t cs = HW_CriticalSectionStart();
3239
3240 ctx.m_handler->OnTaskExit(ctx.m_stack_active);
3241
3242 HW_CriticalSectionEnd(cs);
3243
3244 if (HW_IsPrivilegedContext())
3245 {
3246 for (;;)
3247 {
3248 // enter standby mode until time slot expires
3249 HW_EnterSleepMode();
3250 }
3251 }
3252 else
3253 {
3254 for (;;)
3255 {
3256 // can only busy-wait when non-Privileged
3257 __stk_relax_cpu();
3258 }
3259 }
3260}
3261
3263void stk::OnSchedulerSleep()
3264{
3265 // if hit here, increase the size of STK_SLEEP_TRAP_STACK_SIZE
3267
3268 for (;;)
3269 {
3270 // record Idle state
3271 #if STK_SEGGER_SYSVIEW
3272 SEGGER_SYSVIEW_OnIdle();
3273 #endif
3274
3275 HW_EnterSleepMode();
3276 }
3277}
3278
3280void stk::OnSchedulerSleepOverride()
3281{
3282 // if hit here, increase the size of STK_SLEEP_TRAP_STACK_SIZE
3284
3285 for (;;)
3286 {
3287 // record Idle state
3288 #if STK_SEGGER_SYSVIEW
3289 SEGGER_SYSVIEW_OnIdle();
3290 #endif
3291
3292 GetContext().OnSleepOverride();
3293 }
3294}
3295
3297void stk::OnSchedulerExit()
3298{
3299 __set_CONTROL(0U); // switch to MSP
3300 __set_PSP(0U); // clear PSP (for a clean register state)
3301
3302 // jump back to SaveJmp's return site with m_exiting already set to true
3303 RestoreJmp(GetContext().m_exit_buf, 0);
3304}
3305
3306void PlatformArmCortexM::Initialize(IEventHandler *event_handler, IKernelService *service, uint32_t resolution_us,
3307 Stack *exit_trap)
3308{
3309 GetContext().Initialize(event_handler, service, exit_trap, resolution_us);
3310}
3311
3313{
3314 GetContext().Start();
3315}
3316
3317#if STK_MPU_STACK_GUARD
3318static void ConfigureTaskMpu(IPlatform::IEventOverrider *overrider, Stack *stack, IStackMemory *stack_memory,
3319 ITask *user_task, bool non_secure)
3320{
3321#if !STK_CORTEX_M_TRUSTZONE_FRAME
3322 STK_UNUSED(non_secure);
3323#endif
3324
3325 MpuRegionConfig task_mpu_cfg[TaskMpu::NUM_REGIONS];
3326 size_t cfg_count = 0U;
3327
3328 // merge in up to (TaskMpu::NUM_REGIONS - 1) application-defined regions
3329 // (task-relative slots [+0..+NUM_REGIONS-2)]; user_task is null for the internal
3330 // sleep/exit trap stacks, which never carry application-defined regions
3331 if (user_task != nullptr)
3332 {
3333 // note: if task does not provide MPU regions the stack guard is not applied either
3334 // this approach avoids stack guard for Privileged tasks with MPU=MPU_CFG_PRIVILEGED_BG_MEM
3335 const MpuRegionList *const regions = user_task->GetMpuRegions();
3336 if (regions != nullptr)
3337 {
3338 // Prepare the stack guard bounds for the overlap check
3339 const MpuRegionConfig stack_guard_cfg =
3340 {
3341 .addr = hw::PtrToWord(stack_memory->GetStack()),
3342 .size = stack_memory->GetStackSize() * sizeof(Word),
3343 .access_perm = hw::mpu::EMpuAccess::ACCESS_FULL,
3344 .mem_type = hw::mpu::EMpuType::TYPE_NORMAL_CACHEABLE,
3345 .share = hw::mpu::EMpuShare::SHARE_NON,
3346 .exec = hw::mpu::EMpuExec::EXEC_NEVER
3347 };
3348
3349 const hw::mpu::RegionBounds bounds_s = hw::mpu::GetRegionBounds(stack_guard_cfg);
3350
3351 size_t regions_count = regions->GetSize();
3352 bool stack_guard_canceled = false;
3353
3354 for (size_t i = 0U; i < regions_count; ++i)
3355 {
3356 const hw::mpu::RegionBounds bounds_i = hw::mpu::GetRegionBounds((*regions)[i]);
3357
3358 // cancel stack region in favour of memory coverage by task's instance
3359 if (hw::mpu::IsOverlapping(bounds_s, bounds_i))
3360 {
3361 stack_guard_canceled = true;
3362 break;
3363 }
3364 }
3365
3366 // Reserve 1 slot for the stack guard at the end unless canceled
3367 const size_t max_user_regions = stack_guard_canceled ? TaskMpu::NUM_REGIONS : (TaskMpu::NUM_REGIONS - 1U);
3368
3369 STK_ASSERT(regions_count <= max_user_regions);
3370 if (regions_count > max_user_regions)
3371 {
3372 regions_count = max_user_regions; // defensively clamp in release builds
3373 }
3374
3375 // Populate initial slots with user-defined regions
3376 for (size_t i = 0U; i < regions_count; ++i)
3377 {
3378 task_mpu_cfg[cfg_count++] = (*regions)[i];
3379 }
3380
3381 // Append stack guard as the final slot if not canceled by an overlap
3382 if (!stack_guard_canceled)
3383 {
3384 task_mpu_cfg[cfg_count++] = stack_guard_cfg;
3385 }
3386 }
3387 }
3388
3389#if STK_CORTEX_M_TRUSTZONE_FRAME
3390 TaskMpu &task_mpu = (non_secure ? stack->mpu_ns : stack->mpu);
3391#else
3392 TaskMpu &task_mpu = stack->mpu;
3393#endif
3394
3395 // any slots beyond cfg_count are left disabled by ConfigureDynamic() itself
3396 hw::mpu::ConfigureDynamic(task_mpu, task_mpu_cfg, cfg_count, overrider, non_secure);
3397}
3398#endif // STK_MPU_STACK_GUARD
3399
3400#if STK_CORTEX_M_TRUSTZONE_FRAME
3401static void ConfigureTaskTrustZone(Stack *stack, IStackMemory *stack_memory, ITask *user_task, bool non_secure)
3402{
3403 // write TrustZoneFrame immediately above stack->SP (below the r4-r11 region);
3404 // frame layout in memory (low -> high address, matching STMDB push order in PendSV):
3405 // [SP+0] PSP_NS (pushed last by 2nd STMDB, popped first by 1st LDMIA)
3406 // [SP+4] CONTROL_NS
3407 // [SP+8] PSPLIM (pushed first by 1st STMDB, popped last by 2nd LDMIA)
3408 // [SP+12] PSPLIM_NS
3409 // TrustZoneFrame sits one word above Stack::SP when PrivilegeFrame is present,
3410 // since PrivilegeFrame::CONTROL occupies the lowest word of the saved region
3411 TrustZoneFrame *const tz_frame = hw::WordToPtr<TrustZoneFrame>(stack->SP + (STK_CORTEX_M_PRIV_REGISTER_COUNT * sizeof(Word)));
3412 tz_frame->PSP_NS = 0U;
3413 tz_frame->CONTROL_NS = hw::reg::CONTROL::DEFAULT_INIT;
3414 tz_frame->PSPLIM = 0U; // unlimited for Secure task
3415 tz_frame->PSPLIM_NS = 0U; // unlimited for Secure task
3416
3417 if (non_secure)
3418 {
3419 // instance points to non-secure stack memory, secure memory is obtained by GetSecureStackMemory
3420 IStackMemory *ns_stack_memory = user_task;
3421
3422 // initialize stack memory
3423 const Word ns_stack_top = Context::InitStackMemory(ns_stack_memory);
3424
3425 // NS thread Privilege status
3426 tz_frame->CONTROL_NS = (((stack->access_mode & ACCESS_PRIVILEGED) != 0U) ?
3427 hw::reg::CONTROL::SetPrivileged(tz_frame->CONTROL_NS) :
3428 hw::reg::CONTROL::SetUnprivileged(tz_frame->CONTROL_NS));
3429
3430 // NS thread is using PSP_NS
3431 tz_frame->CONTROL_NS = hw::reg::CONTROL::SetSPSelectionToPSP(tz_frame->CONTROL_NS);
3432
3433 // for a fresh NS task, PSP_NS starts at the top of its NS stack
3434 // (the hardware exception frame was placed there by the caller)
3435 tz_frame->PSP_NS = ns_stack_top;
3436
3437 // bottom of NS stack
3438 tz_frame->PSPLIM_NS = hw::PtrToWord(ns_stack_memory->GetStack());
3439
3440 // bottom of S stack
3441 tz_frame->PSPLIM = hw::PtrToWord(stack_memory->GetStack());
3442 }
3443 else
3444 {
3445 stack->access_mode |= ACCESS_SECURE;
3446
3447 // bottom of S stack
3448 tz_frame->PSPLIM = hw::PtrToWord(stack_memory->GetStack());
3449 }
3450}
3451#endif // STK_CORTEX_M_TRUSTZONE_FRAME
3452
3453void PlatformArmCortexM::InitStack(EStackType stack_type, Stack *stack, IStackMemory *stack_memory, ITask *user_task)
3454{
3456 "ExceptionFrame layout must match the ARMv7-M hardware exception frame exactly");
3457 STK_ASSERT(stack_memory->GetStackSize() > STK_CORTEX_M_TOTAL_REGISTER_COUNT);
3458
3459 Context &ctx = GetContext();
3460
3461#ifdef _STK_CORTEX_M_TRUSTZONE
3462 bool is_non_secure_task = false;
3463
3464 // Replace stack_memory of Non-Secure task with a Secure memory which is required for launching the task
3465 // and Secure operations via NSC veneer.
3466 if ((user_task != nullptr) && (user_task->GetSecureStackMemory() != nullptr))
3467 {
3468 stack_memory = user_task->GetSecureStackMemory();
3469 is_non_secure_task = true;
3470
3471 STK_ASSERT(stack_memory->GetStackSize() > STK_CORTEX_M_TOTAL_REGISTER_COUNT);
3472 }
3473#endif
3474
3475 // initialize stack memory
3476 const Word stack_top = Context::InitStackMemory(stack_memory);
3477
3478 // initialize Stack Pointer (SP)
3479 stack->SP = stack_top - (STK_CORTEX_M_TOTAL_REGISTER_COUNT * sizeof(Word));
3480
3481 // place the initial task frame flush against the top of the stack:
3482 // TaskFrame::exc (ExceptionFrame) occupies the top 8 words, TaskFrame::EXC_RETURN
3483 // (when present) sits immediately below it, and TrustZoneFrame (when present) sits
3484 // below that
3485 TaskFrame *const task_frame = hw::WordToPtr<TaskFrame>(stack_top - sizeof(TaskFrame));
3486
3487 // initialize registers for the user task's first start
3488 switch (stack_type)
3489 {
3490 case STACK_USER_TASK: {
3491 task_frame->exc.PC = hw::PtrToWord(&OnTaskRun);
3492 task_frame->exc.LR = hw::PtrToWord(&OnTaskExit);
3493 task_frame->exc.R0 = hw::PtrToWord(user_task);
3494 break; }
3495
3496 case STACK_SLEEP_TRAP: {
3497 task_frame->exc.PC = hw::PtrToWord(ctx.m_overrider != nullptr ? &OnSchedulerSleepOverride : &OnSchedulerSleep);
3498 task_frame->exc.LR = STK_STACK_MEMORY_FILLER; // should not attempt to exit
3499 task_frame->exc.R0 = 0U;
3500 break; }
3501
3502 case STACK_EXIT_TRAP: {
3503 task_frame->exc.PC = hw::PtrToWord(&OnSchedulerExit);
3504 task_frame->exc.LR = STK_STACK_MEMORY_FILLER; // should not attempt to exit
3505 task_frame->exc.R0 = 0U;
3506 break; }
3507
3508 default: {
3510 break; }
3511 }
3512
3513 // ensure the Program Counter is properly aligned to halfword boundaries
3514 // by clearing the Thumb state tracking bit from the physical target address
3515 task_frame->exc.PC = hw::reg::PC::ClearThumbBit(task_frame->exc.PC);
3516
3517 // initialize the Execution Program Status Register (EPSR) with the T-bit enabled,
3518 // which is required for all ARM Cortex-M processors to execute instructions
3519 task_frame->exc.xPSR = hw::reg::XPSR::SetThumbExecution(hw::reg::XPSR::DEFAULT_INIT);
3520
3521#if STK_CORTEX_M_MANAGE_LR
3522 // set the EXC_RETURN value to target Thread Mode using the Process Stack Pointer (PSP)
3523 // Note for TrustZone configurations: Execution will return to the Non-Secure state
3524 // via a Secure-side trampoline, which invokes the task's entry/ITask::Run function
3525 // using a dedicated Non-Secure function call (__BXNS / Non-Secure callable boundary)
3526 task_frame->EXC_RETURN = STK_CORTEX_M_EXC_RETURN_THREAD_PSP;
3527#endif // STK_CORTEX_M_MANAGE_LR
3528
3529#if STK_CORTEX_M_PRIVILEGE_FRAME
3530 // initial CONTROL for this task's first start: nPRIV reflects the task's
3531 // static access_mode. SPSEL is fixed up by hardware from EXC_RETURN on the
3532 // first exception return; FPCA/SFPA start clear. This word sits at the very
3533 // bottom of the saved frame (== stack->SP) and is popped first by
3534 // OnTaskStart/STK_PENDSV_HANDLER (see PrivilegeFrame)
3535 {
3536 PrivilegeFrame *const priv_frame = hw::WordToPtr<PrivilegeFrame>(stack->SP);
3537
3538 priv_frame->CONTROL = (((stack->access_mode & ACCESS_PRIVILEGED) != 0U) ?
3539 hw::reg::CONTROL::SetPrivileged(hw::reg::CONTROL::DEFAULT_INIT) :
3540 hw::reg::CONTROL::SetUnprivileged(hw::reg::CONTROL::DEFAULT_INIT));
3541 }
3542#endif
3543
3544 // configure per-task TrustZone hardware frames and boundaries
3545#if STK_CORTEX_M_TRUSTZONE_FRAME
3546 ConfigureTaskTrustZone(stack, stack_memory, user_task, is_non_secure_task);
3547#endif
3548
3549 // configure per-task MPU
3550#if STK_MPU_STACK_GUARD
3551 ConfigureTaskMpu(ctx.m_overrider, stack, stack_memory, user_task, false);
3552 #if STK_CORTEX_M_TRUSTZONE_FRAME
3553 if (is_non_secure_task)
3554 {
3555 ConfigureTaskMpu(ctx.m_overrider_ns, stack, user_task, user_task, true);
3556 }
3557 #endif
3558#endif
3559}
3560
3561// ---------------------------------------------------------------------------
3562// ARMv8-M TrustZone Non-Secure callable (NSC) gateway veneers.
3563// ---------------------------------------------------------------------------
3564
3565// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3566#ifdef _STK_CORTEX_M_TRUSTZONE
3567// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3568
3572hw::CriticalSection::Session NSC_stk_hw_CriticalSection_Enter(const hw::CriticalSection::Session ses)
3573{
3574 return hw::CriticalSection::Enter(ses);
3575}
3576
3580void NSC_stk_hw_CriticalSection_Exit(const hw::CriticalSection::Session ses)
3581{
3583}
3584
3588void NSC_stk_hw_SpinLock_Lock(hw::SpinLock *sl)
3589{
3590 if ((sl != nullptr) && (cmse_check_pointed_object(sl, CMSE_NONSECURE) != nullptr))
3591 {
3592 sl->Lock();
3593 }
3594}
3595
3599void NSC_stk_hw_SpinLock_Unlock(hw::SpinLock *sl)
3600{
3601 if ((sl != nullptr) && (cmse_check_pointed_object(sl, CMSE_NONSECURE) != nullptr))
3602 {
3603 sl->Unlock();
3604 }
3605}
3606
3610bool NSC_stk_hw_SpinLock_TryLock(hw::SpinLock *sl)
3611{
3612 bool locked;
3613
3614 if ((sl != nullptr) && (cmse_check_pointed_object(sl, CMSE_NONSECURE) != nullptr))
3615 {
3616 locked = sl->TryLock();
3617 }
3618 else
3619 {
3620 locked = false;
3621 }
3622
3623 return locked;
3624}
3625
3629uint32_t NSC_stk_hw_HiResClock_GetFrequency()
3630{
3632}
3633
3637stk::Cycles NSC_stk_hw_HiResClock_GetCycles()
3638{
3640}
3641
3645bool NSC_stk_hw_IsInsideISR()
3646{
3647 return hw::IsInsideISR();
3648}
3649
3653TId NSC_stk_debug_GetCurrentTId()
3654{
3655 return GetContext().GetCurrentTId();
3656}
3657
3658// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3659#endif // _STK_CORTEX_M_TRUSTZONE
3660// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3661
3662void Context::OnStop()
3663{
3664#if STK_SEGGER_SYSVIEW
3665 SEGGER_SYSVIEW_Stop();
3666#endif
3667
3668 // stop SysTick timer
3669 HW_SysTickStop();
3670
3671 // clear pending PendSV exception
3672 HW_ClearPendingSwitch();
3673
3674 m_started = false;
3675 m_exiting = true;
3676
3677 // make sure all assignments are set and executed
3678 __DSB();
3679 __ISB();
3680}
3681
3683{
3684 GetContext().OnStop();
3685
3686 // load context of the Exit trap
3687 HW_DisableInterrupts();
3688 OnTaskStart();
3689}
3690
3692{
3693 return GetContext().m_tick_resolution;
3694}
3695
3697{
3698 return static_cast<Cycles>(HW_SysTickValue());
3699}
3700
3702{
3703 return HW_CoreClockFrequency();
3704}
3705
3707{
3708 GetContext().m_handler->OnTaskSwitch(HW_GetCallerSP());
3709}
3710
3712{
3713 HW_ForceContextSwitch(id);
3714}
3715
3717{
3718 GetContext().m_handler->OnTaskSleep(HW_GetCallerSP(), ticks);
3719}
3720
3722{
3723 return GetContext().m_handler->OnTaskSleepUntil(HW_GetCallerSP(), timestamp);
3724}
3725
3727{
3728 return GetContext().m_handler->OnTaskWait(HW_GetCallerSP(), sync_obj, mutex, timeout);
3729}
3730
3732{
3733 TId result;
3734 const Word isr = HW_GetCurrentException();
3735
3736 // return special TId which denotes ISR
3737 if (isr != 0U)
3738 {
3739 const TId isr_tid = (TID_ISR_N | isr);
3740 STK_ASSERT(IsIsrTid(isr_tid));
3741 result = isr_tid;
3742 }
3743 else
3744 {
3745 Context &ctx = GetContext();
3746
3747 if (ctx.m_started)
3748 {
3749 result = ctx.m_handler->OnGetTid(HW_GetCallerSP());
3750 }
3751 else
3752 {
3753 result = TID_NONE;
3754 }
3755 }
3756
3757 return result;
3758}
3759
3761{
3762 bool is_handled = false;
3763 Context &ctx = GetContext();
3764
3765 if (ctx.m_overrider != nullptr)
3766 {
3767 is_handled = ctx.m_overrider->OnHardFault();
3768 }
3769
3770 if (!is_handled)
3771 {
3773 }
3774}
3775
3776void PlatformArmCortexM::SetEventOverrider(IEventOverrider *overrider, bool non_secure)
3777{
3778 Context &ctx = GetContext();
3779
3780 STK_ASSERT(!ctx.m_started);
3781
3782#if STK_CORTEX_M_TRUSTZONE_FRAME
3783 if (non_secure)
3784 {
3785 ctx.m_overrider_ns = overrider;
3786 }
3787 else
3788#else
3789 STK_UNUSED(non_secure);
3790#endif
3791 {
3792 ctx.m_overrider = overrider;
3793 }
3794}
3795
3797{
3798 return HW_GetCallerSP();
3799}
3800
3802{
3803#if STK_TICKLESS_IDLE
3804 return GetContext().Suspend();
3805#else
3806 return 0;
3807#endif
3808}
3809
3810void PlatformArmCortexM::Resume(Timeout elapsed_ticks)
3811{
3812#if STK_TICKLESS_IDLE
3813 GetContext().Resume(elapsed_ticks);
3814#else
3815 STK_UNUSED(elapsed_ticks);
3816#endif
3817}
3818
3819void PlatformArmCortexM::SetCpuFrequency(uint8_t core_id, uint32_t frequency)
3820{
3822
3823 if (core_id == 0xFFU)
3824 {
3825 for (uint8_t i = 0U; i < STK_STATIC_ARRAY_SIZE(s_StkSystemCoreClock); ++i)
3826 {
3827 s_StkSystemCoreClock[i] = frequency;
3828 }
3829 }
3830 else
3831 {
3832 STK_ASSERT(core_id < STK_STATIC_ARRAY_SIZE(s_StkSystemCoreClock));
3833
3834 if (core_id < STK_STATIC_ARRAY_SIZE(s_StkSystemCoreClock))
3835 {
3836 s_StkSystemCoreClock[core_id] = frequency;
3837 }
3838 }
3839}
3840
3841#if STK_MPU
3855static class KernelServiceSvcProxy final : public stk::IKernelService
3856{
3857public:
3860 explicit KernelServiceSvcProxy() : IKernelService()
3861 {}
3862
3866 STK_VIRT_DTOR ~KernelServiceSvcProxy() = default;
3867
3868 stk::TId GetTid() const override;
3869 stk::Ticks GetTicks() const override;
3870 uint32_t GetTickResolution() const override;
3871 stk::Cycles GetSysTimerCount() const override;
3872 uint32_t GetSysTimerFrequency() const override;
3873 void Delay(stk::Timeout ticks) override;
3874 void Sleep(stk::Timeout ticks) override;
3875 bool SleepUntil(stk::Ticks timestamp) override;
3876 void SleepCancel(stk::TId task_id) override;
3877 void SwitchToNext() override;
3878 stk::EWaitResult Wait(stk::ISyncObject *sobj, stk::IMutex *mutex, stk::Timeout timeout) override;
3879 void Wake(stk::ISyncObject *sobj, bool all);
3880 stk::Timeout Suspend() override;
3881 void Resume(stk::Timeout elapsed_ticks) override;
3882 void InheritWeight(stk::TId tid, stk::Weight weight) override;
3883 void RestoreWeight(stk::TId tid, stk::ISyncObject *sobj) override;
3884}
3888s_StkKernelServiceUnprivProxy;
3889
3891stk::TId KernelServiceSvcProxy::GetTid() const
3892{
3893 const ScopedPrivilegeBoost pb;
3894 return GetContext().m_service->GetTid();
3895}
3896
3898stk::Ticks KernelServiceSvcProxy::GetTicks() const
3899{
3900 const ScopedPrivilegeBoost pb;
3901 return GetContext().m_service->GetTicks();
3902}
3903
3905uint32_t KernelServiceSvcProxy::GetTickResolution() const
3906{
3907 const ScopedPrivilegeBoost pb;
3908 return GetContext().m_tick_resolution;
3909}
3910
3912stk::Cycles KernelServiceSvcProxy::GetSysTimerCount() const
3913{
3914 const ScopedPrivilegeBoost pb;
3915 return GetContext().m_service->GetSysTimerCount();
3916}
3917
3919uint32_t KernelServiceSvcProxy::GetSysTimerFrequency() const
3920{
3921 const ScopedPrivilegeBoost pb;
3922 return GetContext().m_service->GetSysTimerFrequency();
3923}
3924
3926void KernelServiceSvcProxy::Delay(stk::Timeout ticks)
3927{
3928 const ScopedPrivilegeBoost pb;
3929 GetContext().m_service->Delay(ticks);
3930}
3931
3933void KernelServiceSvcProxy::Sleep(stk::Timeout ticks)
3934{
3935 const ScopedPrivilegeBoost pb;
3936 GetContext().m_service->Sleep(ticks);
3937}
3938
3940bool KernelServiceSvcProxy::SleepUntil(stk::Ticks timestamp)
3941{
3942 const ScopedPrivilegeBoost pb;
3943 return GetContext().m_service->SleepUntil(timestamp);
3944}
3945
3947void KernelServiceSvcProxy::SleepCancel(stk::TId task_id)
3948{
3949 const ScopedPrivilegeBoost pb;
3950 GetContext().m_service->SleepCancel(task_id);
3951}
3952
3954void KernelServiceSvcProxy::SwitchToNext()
3955{
3956 const ScopedPrivilegeBoost pb;
3957 GetContext().m_service->SwitchToNext();
3958}
3959
3961stk::EWaitResult KernelServiceSvcProxy::Wait(stk::ISyncObject *sobj, stk::IMutex *mutex, stk::Timeout timeout)
3962{
3963 const ScopedPrivilegeBoost pb;
3964 return GetContext().m_service->Wait(sobj, mutex, timeout);
3965}
3966
3968void KernelServiceSvcProxy::Wake(stk::ISyncObject *sobj, bool all)
3969{
3970 const ScopedPrivilegeBoost pb;
3971 GetContext().m_service->Wake(sobj, all);
3972}
3973
3975stk::Timeout KernelServiceSvcProxy::Suspend()
3976{
3977 return 0; // not allowed for non-privileged process
3978}
3979
3981void KernelServiceSvcProxy::Resume(stk::Timeout elapsed_ticks)
3982{
3983 // not allowed for non-privileged process
3984 STK_UNUSED(elapsed_ticks);
3985}
3986
3988void KernelServiceSvcProxy::InheritWeight(stk::TId tid, stk::Weight weight)
3989{
3990 const ScopedPrivilegeBoost pb;
3991 GetContext().m_service->InheritWeight(tid, weight);
3992}
3993
3995void KernelServiceSvcProxy::RestoreWeight(stk::TId tid, stk::ISyncObject *sobj)
3996{
3997 const ScopedPrivilegeBoost pb;
3998 GetContext().m_service->RestoreWeight(tid, sobj);
3999}
4000#endif // STK_MPU
4001
4003{
4004 IKernelService *service;
4005
4006#if STK_MPU
4007 if (HW_IsPrivilegedContext() || HW_IsHandlerMode())
4008 {
4009 service = GetContext().m_service;
4010 }
4011 else
4012 {
4013 service = &s_StkKernelServiceUnprivProxy;
4014 }
4015#else
4016 service = GetContext().m_service;
4017#endif
4018
4019 return service;
4020}
4021
4024{
4026
4027 const bool is_priv = ((ses & SESSION_FLAG_NPRIV) == 0U) && (HW_IsPrivilegedContext() || HW_IsHandlerMode());
4028
4029 if (is_priv)
4030 {
4031 GetContext().EnterCriticalSection();
4032 ret = SESSION_FLAG_NONE;
4033 }
4034 else
4035 {
4036 HW_UnprivEnterCriticalSection();
4037 ret = SESSION_FLAG_NPRIV;
4038 }
4039
4040 return ret;
4041}
4042
4045{
4046 if ((ses & SESSION_FLAG_NPRIV) == 0U)
4047 {
4048 GetContext().ExitCriticalSection();
4049 }
4050 else
4051 {
4052 HW_UnprivExitCriticalSection();
4053 }
4054}
4055
4058{
4059 HW_SpinLockLock(m_lock);
4060}
4061
4064{
4065 HW_SpinLockUnlock(m_lock);
4066}
4067
4070{
4071 return HW_SpinLockTryLock(m_lock);
4072}
4073
4076{
4077 return HW_IsHandlerMode();
4078}
4079
4082{
4083 return HW_IsPrivilegedContext();
4084}
4085
4088{
4089 return HiResClockImpl::GetInstance()->GetCycles();
4090}
4091
4094{
4095 const uint32_t freq = HiResClockImpl::GetInstance()->GetFrequency();
4096 STK_ASSERT(freq != 0U);
4097 return freq;
4098}
4099
4100#if STK_TLS && !STK_INLINE_TLS
4101Word stk::hw::GetTls()
4102{
4103 return GetContext().GetTls();
4104}
4105
4106void stk::hw::SetTls(Word tp)
4107{
4108 GetContext().SetTls(tp);
4109}
4110#endif // STK_TLS && !STK_INLINE_TLS
4111
4112// ----------------------------------------------------------------------------
4113// MPU management API
4114// ----------------------------------------------------------------------------
4115
4116// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4117#if STK_MPU
4118// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4119
4120void hw::mpu::ConfigureRegion(MpuRegion &reg, const struct MpuRegionConfig &cfg, uint32_t region_idx)
4121{
4122#if STK_ARCH_ARMV8_M
4123 if ((cfg.size != 0U) && (cfg.access_perm != EMpuAccess::ACCESS_NONE))
4124 {
4125 // hardware rules: both Base and Limit addresses must be 32-byte aligned.
4126 STK_ASSERT((cfg.addr & 31U) == 0U);
4127 STK_ASSERT((cfg.size & 31U) == 0U);
4128
4129 // calculate RBAR: Base address | Shareability [4:3] | AP permissions [2:1] | Execution state [0]
4130 reg.addr = (cfg.addr & ~31U) |
4131 static_cast<uint32_t>(cfg.share) |
4132 static_cast<uint32_t>(cfg.access_perm) |
4133 static_cast<uint32_t>(cfg.exec);
4134
4135 // calculate RLAR: Final 32-byte chunk base address | MAIR allocation index [3:1] | Enable Flag [0]
4136 // find the absolute last byte address within the protected region
4137 const uint32_t end_byte_addr = cfg.addr + cfg.size - 1U;
4138 // clear the bottom 5 bits to isolate the base of the final 32-byte window
4139 const uint32_t limit_addr = end_byte_addr & ~31U;
4140 reg.attr = limit_addr | (static_cast<uint32_t>(cfg.mem_type) << 1U) | hw::mpu::RLAR_ENABLE_FLAG;
4141 }
4142 else
4143 {
4144 // disabled region
4145 reg.addr = hw::mpu::RBAR_DISABLED_REGION(region_idx);
4146 reg.attr = hw::mpu::RLAR_DISABLED_REGION;
4147 }
4148#else
4149 // legacy ARMv7-M Attribute layout
4150 if ((cfg.size != 0U) && (cfg.access_perm != EMpuAccess::ACCESS_NONE))
4151 {
4152 // hardware rules: must be a strict power-of-two size, naturally aligned
4153 STK_ASSERT((cfg.size & (cfg.size - 1U)) == 0U);
4154 STK_ASSERT((cfg.addr & (cfg.size - 1U)) == 0U);
4155
4156 // active ARMv7-M Base layout: masked completely by its structural size constraint
4157 reg.addr = (cfg.addr & ~(cfg.size - 1U)) | (1U << 4U) | (region_idx & 0xFU);
4158
4159 // active Region
4160 const uint32_t size_field = static_cast<uint32_t>(31U - CountLeadingZeros(cfg.size)) - 1U;
4161
4162 // defensive masking: Protect bits [26:24] against sentinel values or dirty enum extensions
4163 const uint32_t ap_bits = static_cast<uint32_t>(cfg.access_perm) & 0x07000000U;
4164
4165 const uint32_t attr = (ap_bits |
4166 static_cast<uint32_t>(cfg.share) |
4167 static_cast<uint32_t>(cfg.mem_type) |
4168 static_cast<uint32_t>(cfg.exec));
4169
4170 reg.attr = attr | (size_field << 1U) | hw::mpu::RASR_ENABLE_FLAG;
4171 }
4172 else
4173 {
4174 // disabled region: clear out base address entirely to prevent false matches,
4175 // point to the target hardware index slot and strip the RASR enable flag
4176 reg.addr = hw::mpu::RBAR_DISABLED_REGION(region_idx);
4177 reg.attr = hw::mpu::RASR_DISABLED_REGION;
4178 }
4179#endif
4180}
4181
4182void hw::mpu::ApplyRegion(const MpuRegion &reg, uint32_t index, bool non_secure)
4183{
4184#if STK_ARCH_ARMV8_M && !STK_TZ_NON_SECURE
4185 MPU_Type *const MPU_ptr = (non_secure ? MPU_NS : MPU);
4186#else
4187 MPU_Type *const MPU_ptr = MPU;
4188 STK_UNUSED(non_secure);
4189#endif
4190
4191 MPU_ptr->RNR = index;
4192 __DSB();
4193 __ISB();
4194
4195 MPU_ptr->RBAR = reg.addr;
4196#if STK_ARCH_ARMV8_M
4197 MPU_ptr->RLAR = reg.attr;
4198#else
4199 MPU_ptr->RASR = reg.attr;
4200#endif
4201}
4202
4203void hw::mpu::Enable(bool enable, uint32_t control_flags, bool non_secure)
4204{
4205 __stk_dmb();
4206
4207#if STK_ARCH_ARMV8_M && !STK_TZ_NON_SECURE
4208 MPU_Type *const MPU_ptr = (non_secure ? MPU_NS : MPU);
4209 SCB_Type *const SCB_ptr = (non_secure ? SCB_NS : SCB);
4210#else
4211 MPU_Type *const MPU_ptr = MPU;
4212 SCB_Type *const SCB_ptr = SCB;
4213 STK_UNUSED(non_secure);
4214#endif
4215
4216 if (enable)
4217 {
4218 #ifdef SCB_SHCSR_MEMFAULTENA_Msk
4219 SCB_ptr->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
4220 #endif
4221 MPU_ptr->CTRL = control_flags | MPU_CTRL_ENABLE_Msk;
4222 }
4223 else
4224 {
4225 #ifdef SCB_SHCSR_MEMFAULTENA_Msk
4226 SCB_ptr->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;
4227 #endif
4228 MPU_ptr->CTRL &= ~MPU_CTRL_ENABLE_Msk;
4229 }
4230
4231 __DSB();
4232 __ISB();
4233}
4234
4235void hw::mpu::DisableRegion(uint32_t index, bool non_secure)
4236{
4237#if STK_ARCH_ARMV8_M && !STK_TZ_NON_SECURE
4238 MPU_Type *const MPU_ptr = (non_secure ? MPU_NS : MPU);
4239#else
4240 MPU_Type *const MPU_ptr = MPU;
4241 STK_UNUSED(non_secure); // no Secure/Non-Secure MPU alias on ARMv7-M
4242#endif
4243
4244 MPU_ptr->RNR = index;
4245
4246#if STK_ARCH_ARMV8_M
4247 MPU_ptr->RLAR = hw::mpu::RLAR_DISABLED_REGION;
4248#else
4249 MPU_ptr->RASR = hw::mpu::RASR_DISABLED_REGION;
4250#endif
4251}
4252
4253void hw::mpu::ConfigureStatic(const MpuRegionConfig cfg_list[], size_t cfg_count, uint32_t control_flags,
4254 bool non_secure)
4255{
4256 STK_ASSERT((cfg_list != nullptr) || (cfg_count == 0U));
4257
4258 const size_t region_idx = (non_secure ? STK_CORTEX_M_MPU_TASK_REGION_IDX_NS : STK_CORTEX_M_MPU_TASK_REGION_IDX);
4259 STK_ASSERT((cfg_count <= region_idx) || (region_idx == 0U));
4260
4261 // validate static table configuration against internal overlaps before writing hardware
4262 ValidateNoOverlaps(nullptr, 0U, cfg_list, cfg_count);
4263
4264 Enable(false, 0U, non_secure);
4265
4266 // cfg_list[i] always targets hardware region i: static regions occupy a fixed,
4267 // contiguous block starting at region 0, so the index is derived from array
4268 // position rather than being supplied by the caller
4269 size_t index = 0U;
4270 for (; index < cfg_count; ++index)
4271 {
4272 const MpuRegionConfig &cfg = cfg_list[index];
4273
4274 MpuRegion reg;
4275 ConfigureRegion(reg, cfg, static_cast<uint32_t>(index));
4276 ApplyRegion(reg, static_cast<uint32_t>(index), non_secure);
4277 }
4278
4279 // disable every remaining static-region slot so a shorter table than a previous
4280 // call can never leave stale configuration active in hardware; callers no longer
4281 // need to supply placeholder/empty entries just to occupy unused regions
4282 for (; index < region_idx; ++index)
4283 {
4284 MpuRegion reg;
4285 ConfigureRegion(reg, s_StkDisabledMpuRegion, static_cast<uint32_t>(index));
4286 ApplyRegion(reg, static_cast<uint32_t>(index), non_secure);
4287 }
4288
4289 // configure memory attributes and point to the start of the per-task region beginning
4290 // note: ARMv7-M does not require RNR to be set as region index is embedded into RBAR,
4291 // though we do it for a consistency with ARMv8-M here
4292 {
4293 #if STK_ARCH_ARMV8_M && STK_TZ_SECURE
4294 MPU_Type *const MPU_ptr = (non_secure ? MPU_NS : MPU);
4295 #else
4296 MPU_Type *const MPU_ptr = MPU;
4297 STK_UNUSED(non_secure); // no Secure/Non-Secure MPU alias on ARMv7-M
4298 #endif
4299
4300 #if STK_ARCH_ARMV8_M
4301 // configure memory attributes
4302 MPU_ptr->MAIR0 = MAIR0_PMSAV8_INIT;
4303 MPU_ptr->MAIR1 = MAIR1_PMSAV8_INIT;
4304 #endif
4305
4306 // point to the start of the per-task config area
4307 MPU_ptr->RNR = region_idx;
4308 }
4309
4310 Enable(true, control_flags, non_secure);
4311}
4312
4313void hw::mpu::ConfigureDynamic(TaskMpu &task_mpu, const struct MpuRegionConfig cfg_list[],
4314 const size_t cfg_count, IPlatform::IEventOverrider *overrider, bool non_secure)
4315{
4316 STK_ASSERT((cfg_list != nullptr) || (cfg_count == 0U));
4317
4318 const size_t region_idx = (non_secure ? STK_CORTEX_M_MPU_TASK_REGION_IDX_NS : STK_CORTEX_M_MPU_TASK_REGION_IDX);
4319 const size_t task_mpu_regions = (non_secure ? TaskMpuNs::NUM_REGIONS : TaskMpu::NUM_REGIONS);
4320 STK_ASSERT(cfg_count <= task_mpu_regions);
4321
4322 const size_t clamped_count = Min(cfg_count, task_mpu_regions);
4323
4324 // retrieve static regions directly via overrider getter
4325 MpuRegionList static_regions(nullptr, 0);
4326 if (overrider != nullptr)
4327 {
4328 const MpuConfig *const cfg = overrider->OnConfigureMpu();
4329 if (cfg != nullptr)
4330 {
4331 static_regions = cfg->regions;
4332 }
4333 }
4334
4335 // validate task region list internal consistency
4336 ValidateNoOverlaps(static_regions.GetPtr(), static_regions.GetSize(), cfg_list, clamped_count);
4337
4338 // cfg_list[i] always targets task-relative slot i (hardware region region_idx + i): the index
4339 // is derived from array position rather than being supplied by the caller
4340 size_t task_idx = 0U;
4341 for (; task_idx < clamped_count; ++task_idx)
4342 {
4343 ConfigureRegion(task_mpu.region[task_idx], cfg_list[task_idx],
4344 static_cast<uint32_t>(region_idx + task_idx));
4345 }
4346
4347 // fill any remaining task slots with a disabled-region descriptor so callers no
4348 // longer need to supply placeholder/empty entries just to occupy unused slots;
4349 // this only updates the in-memory shadow table, it takes effect in hardware on
4350 // the next context switch into this task, via STK_ASM_BLOCK_MPU_STACK_GUARD
4351 for (; task_idx < task_mpu_regions; ++task_idx)
4352 {
4353 ConfigureRegion(task_mpu.region[task_idx], s_StkDisabledMpuRegion,
4354 static_cast<uint32_t>(region_idx + task_idx));
4355 }
4356}
4357
4358// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4359#endif // STK_MPU
4360// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4361
4362// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4363#endif // !defined(_STK_CORTEX_M_TRUSTZONE_NON_SECURE)
4364// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4365
4366#if STK_MPU
4367static void FaultContext_FillMpu(FaultContext::Mpu &mpu_ctx, MPU_Type *mpu_inst)
4368{
4369 mpu_ctx.CTRL = mpu_inst->CTRL;
4370#if STK_ARCH_ARMV8_M
4371 mpu_ctx.MAIR0 = mpu_inst->MAIR0;
4372 mpu_ctx.MAIR1 = mpu_inst->MAIR1;
4373#endif
4374
4375 const Word saved_rnr = mpu_inst->RNR;
4376 for (size_t i = 0U; i < STK_STATIC_ARRAY_SIZE(mpu_ctx.regions); ++i)
4377 {
4378 mpu_inst->RNR = static_cast<Word>(i);
4379 __DSB();
4380 __ISB();
4381 mpu_ctx.regions[i].RNR = static_cast<Word>(i);
4382 mpu_ctx.regions[i].RBAR = mpu_inst->RBAR;
4383 #if STK_ARCH_ARMV8_M
4384 mpu_ctx.regions[i].ATTR = mpu_inst->RLAR;
4385 #else
4386 mpu_ctx.regions[i].ATTR = mpu_inst->RASR;
4387 #endif
4388 }
4389 mpu_inst->RNR = saved_rnr;
4390}
4391#endif // STK_MPU
4392
4393void FaultContext::Fill(const Word *stacked_regs, Word exc_return)
4394{
4395 this->frame.R0 = stacked_regs[0];
4396 this->frame.R1 = stacked_regs[1];
4397 this->frame.R2 = stacked_regs[2];
4398 this->frame.R3 = stacked_regs[3];
4399 this->frame.R12 = stacked_regs[4];
4400 this->frame.LR = stacked_regs[5];
4401 this->frame.PC = stacked_regs[6];
4402 this->frame.xPSR = stacked_regs[7];
4403
4404 this->EXC_RETURN = exc_return;
4405 this->CONTROL = __get_CONTROL();
4406
4407#if STK_ARCH_ARMV6_M
4408 // ARMv6-M (Cortex-M0/M0+) does not have CFSR, HFSR, AFSR, MMFAR, or BFAR.
4409 this->CFSR = 0U;
4410 this->HFSR = 0U;
4411 this->AFSR = 0U;
4412 this->mmfar_valid = false;
4413 this->bfar_valid = false;
4414 this->MMFAR = 0U;
4415 this->BFAR = 0U;
4416#else
4417 // ARMv7-M / ARMv8-M
4418 this->CFSR = SCB->CFSR;
4419 this->HFSR = SCB->HFSR;
4420 this->AFSR = SCB->AFSR;
4421 this->mmfar_valid = ((this->CFSR & SCB_CFSR_MMARVALID_Msk) != 0U);
4422 this->bfar_valid = ((this->CFSR & SCB_CFSR_BFARVALID_Msk) != 0U);
4423 this->MMFAR = (this->mmfar_valid ? SCB->MMFAR : 0U);
4424 this->BFAR = (this->bfar_valid ? SCB->BFAR : 0U);
4425#endif // STK_ARCH_ARMV6_M
4426
4427#if STK_MPU
4428 FaultContext_FillMpu(this->mpu, MPU);
4429 #if STK_ARCH_ARMV8_M && STK_TZ_SECURE
4430 FaultContext_FillMpu(this->mpu_ns, MPU_NS);
4431 #endif
4432#endif // STK_MPU
4433}
4434
4435#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:143
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:200
@ 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:217
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:158
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:153
static bool IsIsrTid(TId id)
Test whether a task identifier represents an ISR context.
Definition stk_common.h:240
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:205
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:168
EHwException
Hardware exception id (see IPlatform::IEventOverrider::OnException).
Definition stk_common.h:131
@ HW_EXCEPT_MEMACCESS
MemManage on ARM / Page Fault or PMP violation on RISC-V.
Definition stk_common.h:133
@ HW_EXCEPT_FATAL
HardFault on ARM / Unhandled Fatal Trap on RISC-V.
Definition stk_common.h:132
Word TId
Task (thread) id.
Definition stk_common.h:148
int32_t Weight
Weight value (aka priority).
Definition stk_common.h:173
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:392
uint32_t access_mode
Bitfield with hardware access mode of the task (see EAccessMode).
Definition stk_common.h:394
Word SP
Offset 0: Stack Pointer (SP) register (note: must always be at offset 0).
Definition stk_common.h:393
Interface for a stack memory region.
Definition stk_common.h:413
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:564
Interface for mutex synchronization primitive.
Definition stk_common.h:697
Interface for a user task.
Definition stk_common.h:755
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.