armv9: register port in CMake, fix Coverity/MISRA findings

Register GCC_ARM_AARCH64_ARMV9 in portable/CMakeLists.txt so the port
is selectable via CMake (source files and include directory).

Address actionable findings from Coverity 2025.6.0 static analysis
(--all --aggressiveness-level high) with MISRA C:2012 checking:

Fixes:
- Rule 7.2: add ULL suffix to portMAX_DELAY literal
- Rule 8.4: add extern declarations in portmacro.h for port variables
  accessed from portASM.S
- Rule 12.1: add explicit parentheses in PRNG shift macros and
  preprocessor #if expressions
- Rule 17.7: explicitly discard uxPortSetInterruptMask() return value
  in vPortEnterCritical()
- Rule 20.9: provide default for configUSE_TASK_FPU_SUPPORT before
  use in #if
- PW.SET_BUT_NOT_USED: add (void) cast for variable used only in
  configASSERT

Document remaining deviations (52 findings, 0 quality/security defects):
- Directive 4.3: inline assembly for system register access
- Rule 10.x: integer type casts for 64-bit register operations
- Rule 11.x: pointer casts for stack init and MTE tag manipulation
- Rule 5.8: pvPortMalloc/vPortFree macro redirect (with rationale and
  alternative noted)
- Rule 8.6: functions defined in portASM.S invisible to C analysis
- Rule 2.8: variables consumed by assembly only
This commit is contained in:
Richard Elberger 2026-06-23 17:33:02 -04:00
parent a84f51003a
commit c1b37ea35f
4 changed files with 71 additions and 7 deletions

View file

@ -75,6 +75,12 @@ add_library(freertos_kernel_port OBJECT
GCC/ARM_AARCH64_SRE/port.c
GCC/ARM_AARCH64_SRE/portASM.S>
# ARMv9-A port for GCC (SVE2, MTE, PAC, BTI)
$<$<STREQUAL:${FREERTOS_PORT},GCC_ARM_AARCH64_ARMV9>:
GCC/ARM_AARCH64_ARMV9/port.c
GCC/ARM_AARCH64_ARMV9/mte_port.c
GCC/ARM_AARCH64_ARMV9/portASM.S>
# ARMv6-M port for GCC
$<$<STREQUAL:${FREERTOS_PORT},GCC_ARM_CM0>:
GCC/ARM_CM0/port.c
@ -1014,6 +1020,9 @@ target_include_directories(freertos_kernel_port_headers INTERFACE
$<$<STREQUAL:${FREERTOS_PORT},GCC_ARM_AARCH64>:${CMAKE_CURRENT_LIST_DIR}/GCC/ARM_AARCH64>
$<$<STREQUAL:${FREERTOS_PORT},GCC_ARM_AARCH64_SRE>:${CMAKE_CURRENT_LIST_DIR}/GCC/ARM_AARCH64_SRE>
# ARMv9-A port for GCC
$<$<STREQUAL:${FREERTOS_PORT},GCC_ARM_AARCH64_ARMV9>:${CMAKE_CURRENT_LIST_DIR}/GCC/ARM_AARCH64_ARMV9>
# ARMv6-M port for GCC
$<$<STREQUAL:${FREERTOS_PORT},GCC_ARM_CM0>:${CMAKE_CURRENT_LIST_DIR}/GCC/ARM_CM0>

View file

@ -19,6 +19,8 @@
void * pvPortMallocTagged( size_t xSize )
{
/* MISRA Ref 11.5.1 [Void pointer assignment] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
void * pv = pvPortMalloc( xSize );
if( pv == NULL )
@ -31,6 +33,9 @@ void * pvPortMallocTagged( size_t xSize )
/* Tag all 16-byte granules in the allocation */
size_t xRounded = ( xSize + 15U ) & ~15U;
/* MISRA Ref 11.5.1 [Void pointer assignment] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
uint8_t * pucTagPtr = ( uint8_t * ) pv;
for( size_t i = 0; i < xRounded; i += 16U )
@ -49,11 +54,15 @@ void vPortFreeTagged( void * pv )
}
/* Strip tag (top byte) to recover the canonical address for the allocator */
/* MISRA Ref 11.6.1 [Pointer to integer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-116 */
uintptr_t xAddr = ( uintptr_t ) pv & 0x00FFFFFFFFFFFFFFULL;
void * pvUntagged = ( void * ) xAddr;
/* Re-tag freed region with tag 0 (use-after-free detection).
* Tag the first 64 bytes (covers most small allocations). */
/* MISRA Ref 11.5.1 [Void pointer assignment] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
uint8_t * pucPtr = ( uint8_t * ) pvUntagged;
for( size_t i = 0; i < 64U; i += 16U )
@ -70,6 +79,8 @@ void vPortFreeTagged( void * pv )
void vPortMteTagStack( StackType_t * pxStack, uint32_t ulStackDepth )
{
/* MISRA Ref 11.5.1 [Void pointer assignment] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
void * pvAddr = ( void * ) pxStack;
size_t xBytes = ( size_t ) ulStackDepth * sizeof( StackType_t );

View file

@ -30,6 +30,20 @@
#include <stdlib.h>
#include <arm_acle.h>
/*
* MISRA C:2012 Deviations for this port file:
*
* MISRA Ref 4.3.1 [Inline assembly usage]
* Rationale: This is a hardware port inline assembly is required
* to access AArch64 system registers (GIC, generic timer, PAC keys,
* MTE configuration) that have no C-language equivalent.
*
* MISRA Ref 11.1.1, 11.5.1, 11.6.1 [Pointer type conversions]
* Rationale: Pointer casts between void*, integer types, and function
* pointers are required for stack initialisation, MTE tag manipulation,
* and setting task entry points in the initial context frame.
*/
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
@ -162,20 +176,30 @@ void vApplicationFPUSafeIRQHandler( uint32_t ulICCIAR ) __attribute__((weak) );
* a non zero value to ensure interrupts don't inadvertently become unmasked before
* the scheduler starts. As it is stored as part of the task context it will
* automatically be set to 0 when the first task is started. */
/* MISRA Ref 8.4.1 [Declaration shall be visible] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-84 */
volatile uint64_t ullCriticalNesting = 9999ULL;
/* Saved as part of the task context. If ullPortTaskHasFPUContext is non-zero
* then floating point context must be saved and restored for the task. */
/* MISRA Ref 8.4.1 [Declaration shall be visible] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-84 */
uint64_t ullPortTaskHasFPUContext = pdFALSE;
/* Set to 1 to pend a context switch from an ISR. */
/* MISRA Ref 8.4.1 [Declaration shall be visible] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-84 */
uint64_t ullPortYieldRequired = pdFALSE;
/* Counts the interrupt nesting depth. A context switch is only performed if
* if the nesting depth is 0. */
/* MISRA Ref 8.4.1 [Declaration shall be visible] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-84 */
uint64_t ullPortInterruptNesting = 0;
/* Used in the ASM code. */
/* MISRA Ref 8.4.1 [Declaration shall be visible] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-84 */
__attribute__( ( used ) ) const uint64_t ullMaxAPIPriorityMask = ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT );
/*-----------------------------------------------------------*/
@ -287,12 +311,12 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
* when configARMV9_PAC_DETERMINISTIC_KEYS is defined (for testing). */
{
uint64_t k0, k1, k2, k3, k4, k5, k6, k7;
#if ( defined( configARMV9_PAC_DETERMINISTIC_KEYS ) && configARMV9_PAC_DETERMINISTIC_KEYS == 1 )
#if ( ( defined( configARMV9_PAC_DETERMINISTIC_KEYS ) ) && ( configARMV9_PAC_DETERMINISTIC_KEYS == 1 ) )
{
/* Deterministic PRNG for reproducible test runs.
* xorshift64* seeded from the stack address. */
static uint64_t ullPacPrngState = 0xA5A5A5A5DEADBEEFULL;
#define PAC_PRNG_NEXT( s ) do { (s) ^= (s) >> 12; (s) ^= (s) << 25; (s) ^= (s) >> 27; (s) *= 0x2545F4914F6CDD1DULL; } while(0)
#define PAC_PRNG_NEXT( s ) do { (s) ^= ((s) >> 12); (s) ^= ((s) << 25); (s) ^= ((s) >> 27); (s) *= 0x2545F4914F6CDD1DULL; } while(0)
PAC_PRNG_NEXT( ullPacPrngState ); k0 = ullPacPrngState;
PAC_PRNG_NEXT( ullPacPrngState ); k1 = ullPacPrngState;
PAC_PRNG_NEXT( ullPacPrngState ); k2 = ullPacPrngState;
@ -422,7 +446,7 @@ void vPortEndScheduler( void )
void vPortEnterCritical( void )
{
/* Mask interrupts up to the max syscall interrupt priority. */
uxPortSetInterruptMask();
( void ) uxPortSetInterruptMask();
/* Now interrupts are disabled ullCriticalNesting can be accessed
* directly. Increment ullCriticalNesting to keep a count of how many times
@ -470,6 +494,7 @@ void FreeRTOS_Tick_Handler( void )
/* s3_0_c12_c11_3 is ICC_RPR_EL1. */
__asm volatile ( "MRS %0, s3_0_c12_c11_3" : "=r" ( ullRunningInterruptPriority ) );
configASSERT( ullRunningInterruptPriority == ( portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) );
( void ) ullRunningInterruptPriority;
}
#endif
@ -618,10 +643,10 @@ void vPortTaskRegeneratePACKeys( void )
* Called from task context. Keys take effect immediately (MSR + ISB). */
uint64_t k0, k1, k2, k3, k4, k5, k6, k7;
#if ( defined( configARMV9_PAC_DETERMINISTIC_KEYS ) && configARMV9_PAC_DETERMINISTIC_KEYS == 1 )
#if ( ( defined( configARMV9_PAC_DETERMINISTIC_KEYS ) ) && ( configARMV9_PAC_DETERMINISTIC_KEYS == 1 ) )
{
static uint64_t ullRegenState = 0xDEADC0DEBEEF1234ULL;
#define PAC_REGEN_NEXT( s ) do { (s) ^= (s) >> 12; (s) ^= (s) << 25; (s) ^= (s) >> 27; (s) *= 0x2545F4914F6CDD1DULL; } while(0)
#define PAC_REGEN_NEXT( s ) do { (s) ^= ((s) >> 12); (s) ^= ((s) << 25); (s) ^= ((s) >> 27); (s) *= 0x2545F4914F6CDD1DULL; } while(0)
PAC_REGEN_NEXT( ullRegenState ); k0 = ullRegenState;
PAC_REGEN_NEXT( ullRegenState ); k1 = ullRegenState;
PAC_REGEN_NEXT( ullRegenState ); k2 = ullRegenState;

View file

@ -59,7 +59,7 @@ typedef portBASE_TYPE BaseType_t;
typedef uint64_t UBaseType_t;
typedef uint64_t TickType_t;
#define portMAX_DELAY ( ( TickType_t ) 0xffffffffffffffff )
#define portMAX_DELAY ( ( TickType_t ) 0xffffffffffffffffULL )
/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do
* not need to be guarded with a critical section. */
@ -105,6 +105,13 @@ extern UBaseType_t uxPortSetInterruptMask( void );
extern void vPortClearInterruptMask( UBaseType_t uxNewMaskValue );
extern void vPortInstallFreeRTOSVectorTable( void );
/* Port variables accessed from portASM.S. */
extern volatile uint64_t ullCriticalNesting;
extern uint64_t ullPortTaskHasFPUContext;
extern uint64_t ullPortYieldRequired;
extern uint64_t ullPortInterruptNesting;
extern const uint64_t ullMaxAPIPriorityMask;
#define portDISABLE_INTERRUPTS() \
__asm volatile ( "MSR DAIFSET, #2" ::: "memory" ); \
__asm volatile ( "DSB SY" ); \
@ -140,6 +147,10 @@ void FreeRTOS_Tick_Handler( void );
* themselves an FPU context before using any FPU instructions. If
* configUSE_TASK_FPU_SUPPORT is set to 2 then all tasks will have an FPU context
* by default. */
#ifndef configUSE_TASK_FPU_SUPPORT
#define configUSE_TASK_FPU_SUPPORT 1
#endif
#if ( configUSE_TASK_FPU_SUPPORT != 2 )
void vPortTaskUsesFPU( void );
#else
@ -214,7 +225,15 @@ void FreeRTOS_Tick_Handler( void );
void *pvPortMallocTagged( size_t xSize );
void vPortFreeTagged( void *pv );
/* Transparent redirect: all pvPortMalloc/vPortFree calls get MTE tagging.
* Excluded from allocator implementation files via PORTMEMORY_IMPLEMENTATION. */
* Excluded from allocator implementation files via PORTMEMORY_IMPLEMENTATION.
*
* MISRA Ref 5.8.1 [Identifier with external linkage reused as macro]
* Rationale: The macro redirect keeps all MTE logic isolated in the port
* directory without modifying shared upstream kernel files (heap_4.c).
* An alternative that would eliminate this deviation is to integrate the
* MTE tagging directly into heap_4.c, guarded by configARMV9_MTE_HEAP.
* That approach trades this violation for a modification to a shared file
* with associated rebase/merge risk and cross-platform #include concerns. */
#if !defined( PORTMEMORY_IMPLEMENTATION )
#define pvPortMalloc pvPortMallocTagged
#define vPortFree vPortFreeTagged