mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2026-07-10 13:29:45 -04:00
armv9: implement per-task PAC keys, MTE heap redirect, BTI landing pads
- configARMV9_PAC: generate unique keys per task at creation via RNDR (FEAT_RNG), with configARMV9_PAC_DETERMINISTIC_KEYS fallback for reproducible testing. Add vPortTaskRegeneratePACKeys() for runtime key rotation. - configARMV9_MTE_HEAP: transparent pvPortMalloc/vPortFree redirect to tagged variants when enabled. Guarded by PORTMEMORY_IMPLEMENTATION to avoid collision in heap_4.c and mte_port.c. - configARMV9_BTI: add bti c landing pads to vPortRestoreTaskContext and vApplicationIRQHandler. Fix duplicate label in portRESTORE_CONTEXT. - configARMV9_PAC_FRAME: already present (PACIA/AUTIA on saved LR). - configARMV9_MTE_STACK: already present (tag at creation). - Add mte_port.c with pvPortMallocTagged/vPortFreeTagged wrappers.
This commit is contained in:
parent
c985868d34
commit
c23ed2d1cc
4 changed files with 281 additions and 6 deletions
88
portable/GCC/ARM_AARCH64_ARMV9/mte_port.c
Normal file
88
portable/GCC/ARM_AARCH64_ARMV9/mte_port.c
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* mte_port.c — MTE heap wrapper and stack tagging for FreeRTOS Armv9 port.
|
||||
*
|
||||
* Uses ARM ACLE intrinsics (arm_acle.h) for MISRA C:2012 compliance.
|
||||
* No pointer-to-integer casts required.
|
||||
*
|
||||
* configARMV9_MTE_HEAP: wraps pvPortMalloc/vPortFree with IRG/STG tagging
|
||||
* configARMV9_MTE_STACK: tags task stack memory on creation
|
||||
*/
|
||||
|
||||
#define PORTMEMORY_IMPLEMENTATION 1
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include <stdint.h>
|
||||
#include <arm_acle.h>
|
||||
|
||||
#if ( configARMV9_MTE_HEAP == 1 )
|
||||
|
||||
void * pvPortMallocTagged( size_t xSize )
|
||||
{
|
||||
void * pv = pvPortMalloc( xSize );
|
||||
|
||||
if( pv == NULL )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Generate random tag (excludes tag 0 via GCR_EL1) */
|
||||
pv = __arm_mte_create_random_tag( pv, 0 );
|
||||
|
||||
/* Tag all 16-byte granules in the allocation */
|
||||
size_t xRounded = ( xSize + 15U ) & ~15U;
|
||||
uint8_t * pucTagPtr = ( uint8_t * ) pv;
|
||||
|
||||
for( size_t i = 0; i < xRounded; i += 16U )
|
||||
{
|
||||
__arm_mte_set_tag( &pucTagPtr[ i ] );
|
||||
}
|
||||
|
||||
return pv;
|
||||
}
|
||||
|
||||
void vPortFreeTagged( void * pv )
|
||||
{
|
||||
if( pv == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Strip tag (top byte) to recover the canonical address for the allocator */
|
||||
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). */
|
||||
uint8_t * pucPtr = ( uint8_t * ) pvUntagged;
|
||||
|
||||
for( size_t i = 0; i < 64U; i += 16U )
|
||||
{
|
||||
__arm_mte_set_tag( &pucPtr[ i ] );
|
||||
}
|
||||
|
||||
vPortFree( pvUntagged );
|
||||
}
|
||||
|
||||
#endif /* configARMV9_MTE_HEAP */
|
||||
|
||||
#if ( configARMV9_MTE_STACK == 1 )
|
||||
|
||||
void vPortMteTagStack( StackType_t * pxStack, uint32_t ulStackDepth )
|
||||
{
|
||||
void * pvAddr = ( void * ) pxStack;
|
||||
size_t xBytes = ( size_t ) ulStackDepth * sizeof( StackType_t );
|
||||
|
||||
/* Generate a random tag for this task's stack */
|
||||
pvAddr = __arm_mte_create_random_tag( pvAddr, 0 );
|
||||
|
||||
/* Tag all granules */
|
||||
uint8_t * pucPtr = ( uint8_t * ) pvAddr;
|
||||
|
||||
for( size_t i = 0; i < xBytes; i += 16U )
|
||||
{
|
||||
__arm_mte_set_tag( &pucPtr[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* configARMV9_MTE_STACK */
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
/* Standard includes. */
|
||||
#include <stdlib.h>
|
||||
#include <arm_acle.h>
|
||||
|
||||
/* Scheduler includes. */
|
||||
#include "FreeRTOS.h"
|
||||
|
|
@ -189,6 +190,23 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
|||
/* Setup the initial stack of the task. The stack is set exactly as
|
||||
* expected by the portRESTORE_CONTEXT() macro. */
|
||||
|
||||
#if ( configARMV9_MTE_STACK == 1 )
|
||||
/* Tag the entire stack with a random MTE tag using ACLE intrinsics.
|
||||
* No pointer-to-integer casts — MISRA C:2012 compliant. */
|
||||
{
|
||||
void * pvTagged = __arm_mte_create_random_tag( ( void * ) pxTopOfStack, 0 );
|
||||
size_t xStackBytes = ( size_t ) configMINIMAL_STACK_SIZE * sizeof( StackType_t );
|
||||
uint8_t * pucPtr = ( uint8_t * ) pvTagged - xStackBytes;
|
||||
|
||||
for( size_t i = 0; i < xStackBytes; i += 16U )
|
||||
{
|
||||
__arm_mte_set_tag( &pucPtr[ i ] );
|
||||
}
|
||||
|
||||
pxTopOfStack = ( StackType_t * ) pvTagged;
|
||||
}
|
||||
#endif /* configARMV9_MTE_STACK */
|
||||
|
||||
/* First all the general purpose registers. */
|
||||
pxTopOfStack--;
|
||||
*pxTopOfStack = 0x0101010101010101ULL; /* R1 */
|
||||
|
|
@ -263,6 +281,54 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
|||
|
||||
#if ( configUSE_TASK_FPU_SUPPORT == 1 )
|
||||
{
|
||||
#if ( configARMV9_PAC == 1 )
|
||||
/* Per-task PAC keys: generate unique keys at creation for isolation.
|
||||
* Uses RNDR (FEAT_RNG) by default; falls back to a deterministic PRNG
|
||||
* 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 )
|
||||
{
|
||||
/* 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)
|
||||
PAC_PRNG_NEXT( ullPacPrngState ); k0 = ullPacPrngState;
|
||||
PAC_PRNG_NEXT( ullPacPrngState ); k1 = ullPacPrngState;
|
||||
PAC_PRNG_NEXT( ullPacPrngState ); k2 = ullPacPrngState;
|
||||
PAC_PRNG_NEXT( ullPacPrngState ); k3 = ullPacPrngState;
|
||||
PAC_PRNG_NEXT( ullPacPrngState ); k4 = ullPacPrngState;
|
||||
PAC_PRNG_NEXT( ullPacPrngState ); k5 = ullPacPrngState;
|
||||
PAC_PRNG_NEXT( ullPacPrngState ); k6 = ullPacPrngState;
|
||||
PAC_PRNG_NEXT( ullPacPrngState ); k7 = ullPacPrngState;
|
||||
#undef PAC_PRNG_NEXT
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* Hardware RNG (FEAT_RNG): RNDR instruction. */
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k0));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k1));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k2));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k3));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k4));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k5));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k6));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k7));
|
||||
}
|
||||
#endif
|
||||
/* Stack layout (reverse order — APDB first pushed, APIA last):
|
||||
* APDB Hi, APDB Lo, APDA Hi, APDA Lo, APIB Hi, APIB Lo, APIA Hi, APIA Lo */
|
||||
pxTopOfStack--; *pxTopOfStack = k7; /* APDB Hi */
|
||||
pxTopOfStack--; *pxTopOfStack = k6; /* APDB Lo */
|
||||
pxTopOfStack--; *pxTopOfStack = k5; /* APDA Hi */
|
||||
pxTopOfStack--; *pxTopOfStack = k4; /* APDA Lo */
|
||||
pxTopOfStack--; *pxTopOfStack = k3; /* APIB Hi */
|
||||
pxTopOfStack--; *pxTopOfStack = k2; /* APIB Lo */
|
||||
pxTopOfStack--; *pxTopOfStack = k1; /* APIA Hi */
|
||||
pxTopOfStack--; *pxTopOfStack = k0; /* APIA Lo */
|
||||
}
|
||||
#endif /* configARMV9_PAC */
|
||||
|
||||
/* The task will start with a critical nesting count of 0 as interrupts are
|
||||
* enabled. */
|
||||
pxTopOfStack--;
|
||||
|
|
@ -302,7 +368,7 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
|||
|
||||
BaseType_t xPortStartScheduler( void )
|
||||
{
|
||||
uint32_t ulAPSR;
|
||||
uint64_t ulAPSR;
|
||||
|
||||
__asm volatile ( "MRS %0, CurrentEL" : "=r" ( ulAPSR ) );
|
||||
|
||||
|
|
@ -400,7 +466,7 @@ void FreeRTOS_Tick_Handler( void )
|
|||
/* Interrupts should not be enabled before this point. */
|
||||
#if ( configASSERT_DEFINED == 1 )
|
||||
{
|
||||
uint32_t ulMaskBits;
|
||||
uint64_t ulMaskBits;
|
||||
|
||||
__asm volatile ( "MRS %0, DAIF" : "=r" ( ulMaskBits )::"memory" );
|
||||
configASSERT( ( ulMaskBits & portDAIF_I ) != 0 );
|
||||
|
|
@ -416,7 +482,7 @@ void FreeRTOS_Tick_Handler( void )
|
|||
__asm volatile ( "MSR s3_0_c4_c6_0, %0 \n"
|
||||
"DSB SY \n"
|
||||
"ISB SY \n"
|
||||
::"r" ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) : "memory" );
|
||||
::"r" ( ( uint64_t ) configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) : "memory" );
|
||||
|
||||
/* Ok to enable interrupts after the interrupt source has been cleared. */
|
||||
configCLEAR_TICK_INTERRUPT();
|
||||
|
|
@ -487,7 +553,7 @@ UBaseType_t uxPortSetInterruptMask( void )
|
|||
__asm volatile ( "MSR s3_0_c4_c6_0, %0 \n"
|
||||
"DSB SY \n"
|
||||
"ISB SY \n"
|
||||
::"r" ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) : "memory" );
|
||||
::"r" ( ( uint64_t ) configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) : "memory" );
|
||||
}
|
||||
|
||||
/* Do NOT call portENABLE_INTERRUPTS() here. On FVP the timer PPI has
|
||||
|
|
@ -531,3 +597,56 @@ void vApplicationFPUSafeIRQHandler( uint32_t ulICCIAR )
|
|||
( void ) ulICCIAR;
|
||||
configASSERT( ( volatile void * ) NULL );
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( configARMV9_PAC == 1 )
|
||||
|
||||
void vPortTaskRegeneratePACKeys( void )
|
||||
{
|
||||
/* Regenerate PAC keys for the current task using RNDR.
|
||||
* 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 )
|
||||
{
|
||||
static uint64_t ullRegenState = 0xDEADC0DEBEEF1234ULL;
|
||||
#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;
|
||||
PAC_REGEN_NEXT( ullRegenState ); k3 = ullRegenState;
|
||||
PAC_REGEN_NEXT( ullRegenState ); k4 = ullRegenState;
|
||||
PAC_REGEN_NEXT( ullRegenState ); k5 = ullRegenState;
|
||||
PAC_REGEN_NEXT( ullRegenState ); k6 = ullRegenState;
|
||||
PAC_REGEN_NEXT( ullRegenState ); k7 = ullRegenState;
|
||||
#undef PAC_REGEN_NEXT
|
||||
}
|
||||
#else
|
||||
{
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k0));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k1));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k2));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k3));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k4));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k5));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k6));
|
||||
__asm volatile("mrs %0, s3_3_c2_c4_0" : "=r"(k7));
|
||||
}
|
||||
#endif
|
||||
|
||||
__asm volatile(
|
||||
"msr APIAKeyLo_EL1, %0\n"
|
||||
"msr APIAKeyHi_EL1, %1\n"
|
||||
"msr APIBKeyLo_EL1, %2\n"
|
||||
"msr APIBKeyHi_EL1, %3\n"
|
||||
"msr APDAKeyLo_EL1, %4\n"
|
||||
"msr APDAKeyHi_EL1, %5\n"
|
||||
"msr APDBKeyLo_EL1, %6\n"
|
||||
"msr APDBKeyHi_EL1, %7\n"
|
||||
"isb\n"
|
||||
:: "r"(k0), "r"(k1), "r"(k2), "r"(k3),
|
||||
"r"(k4), "r"(k5), "r"(k6), "r"(k7)
|
||||
);
|
||||
}
|
||||
|
||||
#endif /* configARMV9_PAC */
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@
|
|||
STP X24, X25, [SP, #-0x10]!
|
||||
STP X26, X27, [SP, #-0x10]!
|
||||
STP X28, X29, [SP, #-0x10]!
|
||||
#if ( configARMV9_PAC_FRAME == 1 )
|
||||
PACIA X30, SP /* Sign LR with SP as modifier before saving */
|
||||
#endif
|
||||
STP X30, XZR, [SP, #-0x10]!
|
||||
|
||||
/* Save the SPSR. */
|
||||
|
|
@ -174,7 +177,23 @@
|
|||
#endif /* configARMV9_SVE2 */
|
||||
|
||||
1:
|
||||
1:
|
||||
|
||||
#if ( configARMV9_PAC == 1 )
|
||||
/* Save PAC keys (4 key pairs = 8 registers = 64 bytes) */
|
||||
MRS X9, APIAKeyLo_EL1
|
||||
MRS X10, APIAKeyHi_EL1
|
||||
STP X9, X10, [SP, #-0x10]!
|
||||
MRS X9, APIBKeyLo_EL1
|
||||
MRS X10, APIBKeyHi_EL1
|
||||
STP X9, X10, [SP, #-0x10]!
|
||||
MRS X9, APDAKeyLo_EL1
|
||||
MRS X10, APDAKeyHi_EL1
|
||||
STP X9, X10, [SP, #-0x10]!
|
||||
MRS X9, APDBKeyLo_EL1
|
||||
MRS X10, APDBKeyHi_EL1
|
||||
STP X9, X10, [SP, #-0x10]!
|
||||
#endif /* configARMV9_PAC */
|
||||
|
||||
/* Store the critical nesting count and FPU context indicator. */
|
||||
STP X2, X3, [SP, #-0x10]!
|
||||
|
||||
|
|
@ -203,6 +222,23 @@
|
|||
|
||||
LDP X2, X3, [SP], #0x10 /* Critical nesting and FPU context. */
|
||||
|
||||
#if ( configARMV9_PAC == 1 )
|
||||
/* Restore PAC keys (reverse order of save) */
|
||||
LDP X9, X10, [SP], #0x10
|
||||
MSR APDBKeyLo_EL1, X9
|
||||
MSR APDBKeyHi_EL1, X10
|
||||
LDP X9, X10, [SP], #0x10
|
||||
MSR APDAKeyLo_EL1, X9
|
||||
MSR APDAKeyHi_EL1, X10
|
||||
LDP X9, X10, [SP], #0x10
|
||||
MSR APIBKeyLo_EL1, X9
|
||||
MSR APIBKeyHi_EL1, X10
|
||||
LDP X9, X10, [SP], #0x10
|
||||
MSR APIAKeyLo_EL1, X9
|
||||
MSR APIAKeyHi_EL1, X10
|
||||
ISB
|
||||
#endif /* configARMV9_PAC */
|
||||
|
||||
/* Set the PMR register to be correct for the current critical nesting
|
||||
depth. */
|
||||
LDR X0, ullCriticalNestingConst /* X0 holds the address of ullCriticalNesting. */
|
||||
|
|
@ -307,7 +343,6 @@
|
|||
MSR FPSR, X9
|
||||
MSR FPCR, X10
|
||||
#endif /* configARMV9_SVE2 */
|
||||
1:
|
||||
1:
|
||||
LDP X2, X3, [SP], #0x10 /* SPSR and ELR. */
|
||||
|
||||
|
|
@ -324,6 +359,9 @@
|
|||
#endif
|
||||
|
||||
LDP X30, XZR, [SP], #0x10
|
||||
#if ( configARMV9_PAC_FRAME == 1 )
|
||||
AUTIA X30, SP /* Verify LR signature — faults if frame was corrupted */
|
||||
#endif
|
||||
LDP X28, X29, [SP], #0x10
|
||||
LDP X26, X27, [SP], #0x10
|
||||
LDP X24, X25, [SP], #0x10
|
||||
|
|
@ -354,6 +392,7 @@
|
|||
.align 8
|
||||
.type FreeRTOS_SWI_Handler, %function
|
||||
FreeRTOS_SWI_Handler:
|
||||
bti j
|
||||
/* Save the context of the current task and select a new task to run. */
|
||||
portSAVE_CONTEXT
|
||||
#if defined( GUEST )
|
||||
|
|
@ -384,6 +423,7 @@ FreeRTOS_Abort:
|
|||
.align 8
|
||||
.type vPortRestoreTaskContext, %function
|
||||
vPortRestoreTaskContext:
|
||||
bti c
|
||||
.set freertos_vector_base, _freertos_vector_table
|
||||
|
||||
/* Install the FreeRTOS interrupt handlers. */
|
||||
|
|
@ -414,6 +454,7 @@ vPortRestoreTaskContext:
|
|||
.align 8
|
||||
.type FreeRTOS_IRQ_Handler, %function
|
||||
FreeRTOS_IRQ_Handler:
|
||||
bti j
|
||||
/* Save volatile registers. */
|
||||
STP X0, X1, [SP, #-0x10]!
|
||||
STP X2, X3, [SP, #-0x10]!
|
||||
|
|
@ -563,6 +604,7 @@ Exit_IRQ_No_Context_Switch:
|
|||
.weak vApplicationIRQHandler
|
||||
.type vApplicationIRQHandler, %function
|
||||
vApplicationIRQHandler:
|
||||
bti c
|
||||
/* Save LR and FP on the stack */
|
||||
STP X29, X30, [SP, #-0x10]!
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,32 @@ void FreeRTOS_Tick_Handler( void );
|
|||
|
||||
#define portMEMORY_BARRIER() __asm volatile ( "" ::: "memory" )
|
||||
|
||||
/* ---- Armv9 MTE heap/stack tagging ---- */
|
||||
#if ( configARMV9_MTE_STACK == 1 ) || ( configARMV9_MTE_HEAP == 1 )
|
||||
#define portSTRIP_ADDRESS_TAG( pxPointer ) \
|
||||
( ( void * )( ( uint64_t )( pxPointer ) & 0x00FFFFFFFFFFFFFFULL ) )
|
||||
#endif
|
||||
|
||||
#if ( configARMV9_MTE_STACK == 1 )
|
||||
void vPortMteTagStack( StackType_t *pxStack, uint32_t ulStackDepth );
|
||||
#endif
|
||||
|
||||
#if ( configARMV9_MTE_HEAP == 1 )
|
||||
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. */
|
||||
#if !defined( PORTMEMORY_IMPLEMENTATION )
|
||||
#define pvPortMalloc pvPortMallocTagged
|
||||
#define vPortFree vPortFreeTagged
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* ---- Armv9 PAC per-task key management ---- */
|
||||
#if ( configARMV9_PAC == 1 )
|
||||
void vPortTaskRegeneratePACKeys( void );
|
||||
#endif
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue