From 42f333261d790fabdd0851d4f7a619824443aff9 Mon Sep 17 00:00:00 2001 From: Ahmed Ismail Date: Fri, 3 Oct 2025 17:24:10 +0100 Subject: [PATCH 1/3] cortex-r82-smp-example: Add minor improvements This commit adds multiple improvements to the example and adapt to the latest port code changes. Signed-off-by: Ahmed Ismail --- .../BSP/CMakeLists.txt | 2 +- .../BSP/Include/gic.h | 49 ++++++++++++------- .../BSP/Source/boot.S | 20 +++++--- .../BSP/Source/gic.c | 24 +++++---- .../BSP/Source/{xil-crt0.S => startup.S} | 11 +++-- CORTEX_R82_SMP_FVP_GCC_ARMCLANG/README.md | 2 +- .../armclang_toolchain.cmake | 4 +- .../gnu_toolchain.cmake | 4 +- CORTEX_R82_SMP_FVP_GCC_ARMCLANG/main.c | 20 +++++--- 9 files changed, 81 insertions(+), 55 deletions(-) rename CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/{xil-crt0.S => startup.S} (92%) diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt index be24c21..84a0d52 100644 --- a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt @@ -10,7 +10,7 @@ target_sources(bsp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/Source/port_asm_vectors.S ${CMAKE_CURRENT_SOURCE_DIR}/Source/boot.S - ${CMAKE_CURRENT_SOURCE_DIR}/Source/xil-crt0.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/startup.S ${CMAKE_CURRENT_SOURCE_DIR}/Source/gic.c ) diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Include/gic.h b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Include/gic.h index 9da5b78..2da5c38 100644 --- a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Include/gic.h +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Include/gic.h @@ -2,29 +2,38 @@ * SPDX-License-Identifier: MIT */ -#define GICD_BASE ( 0xAF000000UL ) /* Base of GIC Distributor on BaseR FVP */ -#define GICR_BASE_PER_CORE( core ) ( 0xAF100000 + (0x20000 * ( core ) ) ) /* Base of GIC Redistributor per core on BaseR FVP */ -#define SGI_BASE ( 0x10000 ) /* SGI Base */ -#define GICD_CTLR ( 0x000 ) /* Distributor Control Register */ -#define GICR_WAKER ( 0x14 ) /* ReDistributor Wake Register */ -#define GICR_PWRR ( 0x24 ) /* ReDistributor Power Register */ -#define GICR_IGROUPR0 ( SGI_BASE + 0x80 ) /* Interrupt Group Registers */ -#define GICR_ISENABLER0 ( SGI_BASE + 0x100 ) /* Interrupt Set-Enable Registers */ -#define GICR_IPRIORITYR( n ) ( SGI_BASE + ( 0x400 + ( 4 * n ) ) ) /* Interrupt Priority Registers */ -#define GICR_IGRPMODR0 ( SGI_BASE + 0xD00 ) /* Distributor Interrupt group modifier Register */ +#ifndef GIC_H +#define GIC_H -#define GICD_CTLR_ENABLEGRP1NS_BIT ( 1U ) /* GICD_CTRL.EnableGrp1NS bit */ -#define GICD_CTLR_ENABLEGRP1S_BIT ( 2U ) /* GICD_CTRL.EnableGrp1S bit */ -#define GICD_CTLR_ARES_BIT ( 4U ) /* GICD_CTRL.ARE_S bit */ -#define GICD_CTLR_DS_BIT ( 6U ) /* GICD_CTRL.DS bit */ +#include "FreeRTOSConfig.h" -#define GICR_PWRR_RDPD_BIT ( 0U ) /* GICR_PWRR.RDPD bit */ +#if ( configNUMBER_OF_CORES == 1 ) + #define ucPortGetCoreID() ( 0 ) /* Single core system, always core 0 */ +#endif -#define GICR_WAKER_PS_BIT ( 1U ) /* GICR_WAKER.PS bit */ -#define GICR_WAKER_CA_BIT ( 2U ) /* GICR_WAKER.CA bit */ +#define GICD_BASE ( 0xAF000000UL ) /* Base of GIC Distributor on BaseR FVP */ +#define GICR_BASE_PER_CORE( core ) ( 0xAF100000UL + ( 0x20000UL * ( core ) ) ) /* Base of GIC Redistributor per core on BaseR FVP */ +#define SGI_BASE ( 0x10000UL ) /* SGI Base */ +#define GICD_CTLR ( 0x000 ) /* Distributor Control Register */ +#define GICR_WAKER ( 0x14 ) /* ReDistributor Wake Register */ +#define GICR_PWRR ( 0x24 ) /* ReDistributor Power Register */ +#define GICR_IGROUPR0 ( SGI_BASE + 0x80 ) /* Interrupt Group Registers */ +#define GICR_ISENABLER0 ( SGI_BASE + 0x100 ) /* Interrupt Set-Enable Registers */ +#define GICR_IPRIORITYR( n ) ( SGI_BASE + ( 0x400 + ( 4 * n ) ) ) /* Interrupt Priority Registers */ +#define GICR_IGRPMODR0 ( SGI_BASE + 0xD00 ) /* Distributor Interrupt group modifier Register */ -#define GIC_MAX_INTERRUPT_ID ( 31UL ) /* Maximum Interrupt ID for PPIs and SGIs */ -#define GIC_WAIT_TIMEOUT ( 1000000U ) /* Timeout for waiting on GIC operations */ +#define GICD_CTLR_ENABLEGRP1NS_BIT ( 1U ) /* GICD_CTRL.EnableGrp1NS bit */ +#define GICD_CTLR_ENABLEGRP1S_BIT ( 2U ) /* GICD_CTRL.EnableGrp1S bit */ +#define GICD_CTLR_ARES_BIT ( 4U ) /* GICD_CTRL.ARE_S bit */ +#define GICD_CTLR_DS_BIT ( 6U ) /* GICD_CTRL.DS bit */ + +#define GICR_PWRR_RDPD_BIT ( 0U ) /* GICR_PWRR.RDPD bit */ + +#define GICR_WAKER_PS_BIT ( 1U ) /* GICR_WAKER.PS bit */ +#define GICR_WAKER_CA_BIT ( 2U ) /* GICR_WAKER.CA bit */ + +#define GIC_MAX_INTERRUPT_ID ( 31UL ) /* Maximum Interrupt ID for PPIs and SGIs */ +#define GIC_WAIT_TIMEOUT ( 1000000U ) /* Timeout for waiting on GIC operations */ /** * Assigns the specified interrupt to Group 1 and enables it @@ -66,3 +75,5 @@ void vGIC_SetPriority( uint32_t ulInterruptID, uint32_t ulPriority ); * sets SGI0 to be a Group 1 interrupt, and enables delivery of Group-1 IRQs to EL1. */ void vGIC_SetupSgi0( void ); + +#endif /* GIC_H */ diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/boot.S b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/boot.S index 5d87233..347ecdb 100644 --- a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/boot.S +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/boot.S @@ -48,6 +48,10 @@ #error "Unsupported configNUMBER_OF_CORES value — must be a power‑of‑two up to 4" #endif +.set SCTLR_EL1_NTWE, 18 /* SCTLR_EL1.nTWE bit position */ +.set SCTLR_EL1_NTWI, 16 /* SCTLR_EL1.nTWI bit position */ +.set SCTLR_EL1_CACHE, 2 /* SCTLR_EL1.C bit position */ + .section .boot,"ax" _prestart: @@ -87,10 +91,11 @@ start: mov x30, #0 mrs x0, currentEL + /* Check we’ve come from EL1 (currentEL==0x4), otherwise fault */ cmp x0, #0x4 beq InitEL1 - b error /* Check we’ve come from EL1 (currentEL==0x4), otherwise fault */ + b error InitEL1: /* Set vector table base address */ ldr x1, =vector_base @@ -103,8 +108,7 @@ InitEL1: isb /* Clear FP status flags (FPSR) to avoid spurious exceptions on first use */ - mov x0, 0x0 - msr FPSR, x0 + msr FPSR, xzr /* Define stack pointer for current exception level */ #if configNUMBER_OF_CORES > 1 @@ -120,8 +124,8 @@ InitEL1: /* x4 = this CPU’s index (Aff0 field of MPIDR_EL1) */ mrs x4, MPIDR_EL1 and x4, x4, #0xFF /* core_id ∈ {0 … N_CPUS-1} */ - cmp x4, #configNUMBER_OF_CORES - b.hs error + cmp x4, #configNUMBER_OF_CORES + b.hs error /* x0 = slice_size * core_id → how far to step back from the top */ mul x0, x1, x4 /* sp = top_of_pool – offset (so core 0 gets the very top) */ @@ -153,9 +157,9 @@ InitEL1: * - Allow EL0 to execute WFE/WFI (Set nTWE/nTWI so they don't trap) */ mrs x1, SCTLR_EL1 - orr x1, x1, #(1 << 18) /* nTWE = 1 → WFE at EL0 does not trap */ - orr x1, x1, #(1 << 16) /* nTWI = 1 → WFI at EL0 does not trap */ - orr x1, x1, #(1 << 2) /* C = 1 → enable data cache */ + orr x1, x1, #(1 << SCTLR_EL1_NTWE) /* nTWE = 1 → WFE at EL0 does not trap */ + orr x1, x1, #(1 << SCTLR_EL1_NTWI) /* nTWI = 1 → WFI at EL0 does not trap */ + orr x1, x1, #(1 << SCTLR_EL1_CACHE) /* C = 1 → enable data cache */ msr SCTLR_EL1, x1 isb diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/gic.c b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/gic.c index 7171d4f..d2c91d8 100644 --- a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/gic.c +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/gic.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: MIT */ -#include #include #include "FreeRTOS.h" #include "FreeRTOSConfig.h" @@ -22,8 +21,12 @@ void vGIC_EnableCPUInterface( void ) void vGIC_PowerUpRedistributor( void ) { - volatile uint32_t *pulPwrr = ( uint32_t * )( GICR_BASE_PER_CORE( xPortGetCoreID() ) + GICR_PWRR ); - volatile uint32_t *pulWaker = ( uint32_t * )( GICR_BASE_PER_CORE( xPortGetCoreID() ) + GICR_WAKER ); + volatile uint32_t *pulPwrr = ( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + + ( portPOINTER_SIZE_TYPE ) GICR_PWRR ); + + volatile uint32_t *pulWaker = ( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + + ( portPOINTER_SIZE_TYPE ) GICR_WAKER ); + uint32_t ulTimeout = GIC_WAIT_TIMEOUT; /* Clear RDPD (Redistributor Power-Down) to 0 → power on the redistributor */ @@ -59,10 +62,10 @@ void vGIC_InitDist( void ) /* Enable Group-1 Non-Secure (NS) and Secure (S) interrupts, and turn on Affinity-routing (ARE_S) *plus Disable-Security (DS) for full GICv3 operation */ - *( volatile uint32_t * )( GICD_BASE + GICD_CTLR ) = ( 1 << GICD_CTLR_ENABLEGRP1NS_BIT ) | - ( 1 << GICD_CTLR_ENABLEGRP1S_BIT ) | - ( 1 << GICD_CTLR_ARES_BIT ) | - ( 1 << GICD_CTLR_DS_BIT ); + *( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICD_BASE + ( portPOINTER_SIZE_TYPE ) GICD_CTLR ) = ( 1 << GICD_CTLR_ENABLEGRP1NS_BIT ) | + ( 1 << GICD_CTLR_ENABLEGRP1S_BIT ) | + ( 1 << GICD_CTLR_ARES_BIT ) | + ( 1 << GICD_CTLR_DS_BIT ); /* Ensure distributor configuration is visible before continuing */ __asm volatile ( "dsb sy" ::: "memory" ); @@ -78,7 +81,8 @@ void vGIC_SetPriority( uint32_t ulInterruptID, uint32_t ulPriority ) uint32_t ulShift = ( ulInterruptID % 4U ) * 8U; /* Byte lane offset */ uint32_t ulMask = 0xFFUL << ulShift; /* Field mask */ - volatile uint32_t * pulPriorityReg = ( volatile uint32_t * ) ( ( uintptr_t ) GICR_BASE_PER_CORE( xPortGetCoreID() ) + GICR_IPRIORITYR( ulIndex ) ); + volatile uint32_t * pulPriorityReg = ( volatile uint32_t * ) ( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + + ( portPOINTER_SIZE_TYPE ) GICR_IPRIORITYR( ulIndex ) ); uint32_t ulRegValue = *pulPriorityReg; ulRegValue &= ~( ulMask ); ulRegValue |= ( ( uint32_t ) ulPriority << ulShift ); @@ -99,10 +103,10 @@ void vGIC_EnableIRQ( uint32_t ulInterruptID ) uint32_t ulBitMask = 1U << ulInterruptID; /* 1. Assign the interrupt to group 1 */ - *( volatile uint32_t * )( GICR_BASE_PER_CORE( xPortGetCoreID() ) + GICR_IGROUPR0 ) |= ( 1U << ulInterruptID ); + *( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + ( portPOINTER_SIZE_TYPE ) GICR_IGROUPR0 ) |= ( 1U << ulInterruptID ); /* 2. Enable the interrupt in GIC */ - *( volatile uint32_t* )( GICR_BASE_PER_CORE( xPortGetCoreID() ) + GICR_ISENABLER0 ) |= ulBitMask; + *( volatile uint32_t* )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + ( portPOINTER_SIZE_TYPE ) GICR_ISENABLER0 ) |= ulBitMask; /* Ensure interrupt enable is visible */ __asm volatile ( "dsb sy" ::: "memory" ); diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/xil-crt0.S b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/startup.S similarity index 92% rename from CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/xil-crt0.S rename to CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/startup.S index a136aca..2f6eba8 100644 --- a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/xil-crt0.S +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/startup.S @@ -13,7 +13,8 @@ #endif #include "FreeRTOSConfig.h" - .file "xil-crt0.S" +#include "portmacro.h" + .file "startup.S" .align 2 .extern _vector_table @@ -98,6 +99,7 @@ c_init_end: #endif b jump_to_main +#if configNUMBER_OF_CORES > 1 secondary_cores_hold: ldr x0, =ucPrimaryCoreInitDoneFlag ldr w1, [x0] /* Has the primary core set the flag? */ @@ -115,11 +117,12 @@ make_secondary_cores_ready: ldr x0, =ucSecondaryCoresReadyFlags mrs x1, MPIDR_EL1 and x1, x1, #0xFF - sub x1, x1, #1 /* Core 1 is index 0 in the array */ + sub x1, x1, #1 /* Core 1 is index 0 in the array */ add x0, x0, x1 mov w1, #1 - strb w1, [x0] /* Mark this core as ready */ - SVC 106 /* Start the first task on the secondary core */ + strb w1, [x0] /* Mark this core as ready */ + SVC # portSVC_START_FIRST_TASK /* Start the first task on the secondary core */ +#endif jump_to_main: ldr X1, = freertos_vector_base diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/README.md b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/README.md index 87d0498..1568691 100644 --- a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/README.md +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/README.md @@ -59,7 +59,7 @@ This example relies on the `FVP_BaseR_AEMv8R` implementing fully coherent caches | **Shared flag** | `ulSharedFlag` (64-bit) is cache-coherent across cores. | | **Tasks** | `prvTaskCore0` (core 0) prints **Ping**, sets flag to 1; `prvTaskCore1` (core 1) prints **Pong**, sets flag to 0. Each task delays for 1 s (`vTaskDelay( pdMS_TO_TICKS(1000) )`). | | **Core affinity** | After creation, tasks are pinned via `vTaskCoreAffinitySet()` to ensure deterministic execution. | -| **Scheduler bring-up** | Only primary Core (i.e., Core 0) jumps to main, creates the user tasks, and calls `vTaskStartScheduler()`. Each secondary core starts and does all the its core specific initialisation and spin in `wfe` until `ucPrimaryCoreInitDoneFlag` is set to `1`, initialize the GIC redistributor and enable SGIs so interrupts from the primary core are receivable, signal the primary core that this secondary core is online and ready by setting the its flag in the `ucSecondaryCoresReadyFlags` array, finally issues an SVC with immediate value `106` to enter `FreeRTOS_SWI_Handler`, which will call `vPortRestoreContext()` based on the SVC number to start scheduling on the secondary core. | +| **Scheduler bring-up** | Only primary Core (i.e., Core 0) jumps to main, creates the user tasks, and calls `vTaskStartScheduler()`. Each secondary core starts and does all the its core specific initialisation and spin in `wfe` until `ucPrimaryCoreInitDoneFlag` is set to `1`, initialize the GIC redistributor and enable SGIs so interrupts from the primary core are receivable, signal the primary core that this secondary core is online and ready by setting the its flag in the `ucSecondaryCoresReadyFlags` array, finally issues an SVC with immediate value `106` (i.e., `portSVC_START_FIRST_TASK`) to enter `FreeRTOS_SWI_Handler`, which will call `vPortRestoreContext()` based on the SVC number to start scheduling on the secondary core. | | **Tick timer** | `vConfigureTickInterrupt()` programs `CNTP_EL0` for the FreeRTOS tick and routes `IRQ 30` through the GIC to all cores. | | **Cache maintenance** | Each write to `ulSharedFlag` is followed by a `DSB SY` to guarantee visibility before the other core wakes. | diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_toolchain.cmake b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_toolchain.cmake index 1a1513d..961cbec 100644 --- a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_toolchain.cmake +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_toolchain.cmake @@ -15,8 +15,8 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # Common flags for compilation -set(CMAKE_C_FLAGS "--target=aarch64-arm-none-eabi -mcpu=cortex-r82 -mlittle-endian -Werror -g -gdwarf-3 -mstrict-align" ) -set(CMAKE_ASM_FLAGS "--target=aarch64-arm-none-eabi -mcpu=cortex-r82 -Werror -g -gdwarf-3 -mstrict-align" ) +set(CMAKE_C_FLAGS "--target=aarch64-arm-none-eabi -mcpu=cortex-r82 -mlittle-endian -Werror -mstrict-align" ) +set(CMAKE_ASM_FLAGS "--target=aarch64-arm-none-eabi -mcpu=cortex-r82 -Werror -mstrict-align" ) set(CMAKE_EXE_LINKER_FLAGS_INIT "--entry=_boot" ) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_INIT}") diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_toolchain.cmake b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_toolchain.cmake index 159df91..5a04a4f 100644 --- a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_toolchain.cmake +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_toolchain.cmake @@ -11,8 +11,8 @@ set(CMAKE_ASM_COMPILER "aarch64-none-elf-gcc") set(CMAKE_C_STANDARD 11) -set(CMAKE_C_FLAGS "-mcpu=cortex-r82 -mabi=lp64 -mlittle-endian -Wall -Wextra -g -gdwarf-3 -mstrict-align") -set(CMAKE_ASM_FLAGS "-mcpu=cortex-r82 -mabi=lp64 -Wall -g -gdwarf-3") +set(CMAKE_C_FLAGS "-mcpu=cortex-r82 -mabi=lp64 -mlittle-endian -Wall -Wextra -Werror -mstrict-align") +set(CMAKE_ASM_FLAGS "-mcpu=cortex-r82 -mabi=lp64 -Wall -Werror -mstrict-align") set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/main.c b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/main.c index 2a1223a..6bb6748 100644 --- a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/main.c +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/main.c @@ -63,7 +63,7 @@ static void prvTaskCore0( void * arg ) { if( ulSharedFlag == 0U ) { - printf( "Ping from Core %lu\r\n", xPortGetCoreID() ); + printf( "Ping from Core %d\r\n", ucPortGetCoreID() ); ulSharedFlag = 1U; __asm volatile( "dsb sy"); } @@ -83,7 +83,7 @@ static void prvTaskCore1( void * arg ) { if( ulSharedFlag == 1U ) { - printf( "Pong from Core %lu\r\n", xPortGetCoreID() ); + printf( "Pong from Core %d\r\n", ucPortGetCoreID() ); ulSharedFlag = 0U; __asm volatile( "dsb sy"); } @@ -116,8 +116,10 @@ int main() return EXIT_FAILURE; } - vTaskCoreAffinitySet( prvTaskCore0Handle, 1UL << 0 ); /* Pin to Core 0 */ - vTaskCoreAffinitySet( prvTaskCore1Handle, 1UL << 1 ); /* Pin to Core 1 */ + #if ( configNUMBER_OF_CORES > 1 ) + vTaskCoreAffinitySet( prvTaskCore0Handle, 1UL << 0 ); /* Pin to Core 0 */ + vTaskCoreAffinitySet( prvTaskCore1Handle, 1UL << 1 ); /* Pin to Core 1 */ + #endif xSharedFlagMutex = xSemaphoreCreateMutex(); @@ -266,10 +268,12 @@ void vApplicationIRQHandler( uint32_t ulICCIAR ) { FreeRTOS_Tick_Handler(); } - else if( ulInterruptID == SGI0_IRQ ) - { - FreeRTOS_SGI_Handler(); - } + #if ( configNUMBER_OF_CORES > 1 ) + else if( ulInterruptID == SGI0_IRQ ) + { + FreeRTOS_SGI_Handler(); + } + #endif else { /* Handle other interrupts as needed. */ From 7f8326417243dcbbcff8a59ba29f5174851fe4fb Mon Sep 17 00:00:00 2001 From: Ahmed Ismail Date: Mon, 17 Nov 2025 14:16:18 +0000 Subject: [PATCH 2/3] cortex-r82-mpu-example: Add Arm Cortex-R82 SMP MPU example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds Arm Cortex-R82 SMP MPU example which brings up FreeRTOS‑SMP with MPU enabled on Arm Cortex‑R82 `FVP_BaseR_AEMv8R` and demonstrates MPU‑backed isolation for unprivileged application tasks that communicate via queues while a privileged logger task prints activity. Signed-off-by: Ahmed Ismail --- .../BSP/CMakeLists.txt | 25 + .../BSP/Include/gic.h | 79 +++ .../BSP/Source/boot.S | 225 +++++++++ .../BSP/Source/gic.c | 128 +++++ .../BSP/Source/port_asm_vectors.S | 164 +++++++ .../BSP/Source/startup.S | 140 ++++++ .../CMakeLists.txt | 64 +++ CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/README.md | 126 +++++ .../armclang_linker_script.sct | 93 ++++ .../armclang_toolchain.cmake | 23 + .../config/FreeRTOSConfig.h | 231 +++++++++ .../crt_replacements.c | 136 ++++++ .../fvp_config.txt | 15 + .../gnu_linker_script.ld | 110 +++++ .../gnu_toolchain.cmake | 23 + CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/main.c | 449 ++++++++++++++++++ CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/run.sh | 6 + 17 files changed, 2037 insertions(+) create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Include/gic.h create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/boot.S create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/gic.c create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/startup.S create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/CMakeLists.txt create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/README.md create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/armclang_linker_script.sct create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/armclang_toolchain.cmake create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/crt_replacements.c create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/fvp_config.txt create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/gnu_linker_script.ld create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/gnu_toolchain.cmake create mode 100644 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/main.c create mode 100755 CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/run.sh diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt new file mode 100644 index 0000000..84a0d52 --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.15) + +add_library(bsp INTERFACE) + +target_sources(bsp + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/Source/port_asm_vectors.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/boot.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/startup.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/gic.c +) + +target_include_directories(bsp + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/Include +) + +target_link_libraries(bsp + INTERFACE + freertos_kernel +) diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Include/gic.h b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Include/gic.h new file mode 100644 index 0000000..2da5c38 --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Include/gic.h @@ -0,0 +1,79 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#ifndef GIC_H +#define GIC_H + +#include "FreeRTOSConfig.h" + +#if ( configNUMBER_OF_CORES == 1 ) + #define ucPortGetCoreID() ( 0 ) /* Single core system, always core 0 */ +#endif + +#define GICD_BASE ( 0xAF000000UL ) /* Base of GIC Distributor on BaseR FVP */ +#define GICR_BASE_PER_CORE( core ) ( 0xAF100000UL + ( 0x20000UL * ( core ) ) ) /* Base of GIC Redistributor per core on BaseR FVP */ +#define SGI_BASE ( 0x10000UL ) /* SGI Base */ +#define GICD_CTLR ( 0x000 ) /* Distributor Control Register */ +#define GICR_WAKER ( 0x14 ) /* ReDistributor Wake Register */ +#define GICR_PWRR ( 0x24 ) /* ReDistributor Power Register */ +#define GICR_IGROUPR0 ( SGI_BASE + 0x80 ) /* Interrupt Group Registers */ +#define GICR_ISENABLER0 ( SGI_BASE + 0x100 ) /* Interrupt Set-Enable Registers */ +#define GICR_IPRIORITYR( n ) ( SGI_BASE + ( 0x400 + ( 4 * n ) ) ) /* Interrupt Priority Registers */ +#define GICR_IGRPMODR0 ( SGI_BASE + 0xD00 ) /* Distributor Interrupt group modifier Register */ + +#define GICD_CTLR_ENABLEGRP1NS_BIT ( 1U ) /* GICD_CTRL.EnableGrp1NS bit */ +#define GICD_CTLR_ENABLEGRP1S_BIT ( 2U ) /* GICD_CTRL.EnableGrp1S bit */ +#define GICD_CTLR_ARES_BIT ( 4U ) /* GICD_CTRL.ARE_S bit */ +#define GICD_CTLR_DS_BIT ( 6U ) /* GICD_CTRL.DS bit */ + +#define GICR_PWRR_RDPD_BIT ( 0U ) /* GICR_PWRR.RDPD bit */ + +#define GICR_WAKER_PS_BIT ( 1U ) /* GICR_WAKER.PS bit */ +#define GICR_WAKER_CA_BIT ( 2U ) /* GICR_WAKER.CA bit */ + +#define GIC_MAX_INTERRUPT_ID ( 31UL ) /* Maximum Interrupt ID for PPIs and SGIs */ +#define GIC_WAIT_TIMEOUT ( 1000000U ) /* Timeout for waiting on GIC operations */ + +/** + * Assigns the specified interrupt to Group 1 and enables it + * in the Redistributor for the local core. + */ +void vGIC_EnableIRQ( uint32_t ulInterruptID ); + +/** + * Enables signaling of Group-1 interrupts at EL1 via ICC_IGRPEN1_EL1. + */ +void vGIC_EnableCPUInterface( void ); + +/** + * Initializes the GIC Distributor: + * - Enables Group-1 Non-Secure and Group-1 Secure interrupts + * - Enables Affinity Routing (ARE_S) and Disable Security (DS) bits + */ +void vGIC_InitDist( void ); + +/** + * Powers up and wakes the Redistributor for the current core: + * 1. Clears the Redistributor power-down bit and waits for RDPD=0 + * 2. Clears the Processor-Sleep bit and waits for Children-Asleep=0 + */ +void vGIC_PowerUpRedistributor( void ); + +/** + * Sets the priority of the specified SGI/PPI (INTID 0‑31) in the local + * Redistributor bank via GICR_IPRIORITYR. + * For shared peripheral interrupts (SPI, INTID ≥ 32) use the GICD_IPRIORITYR path. + * + * @param ulInterruptID The ID of the interrupt to set the priority for. + * @param ulPriority The priority value to set. + */ +void vGIC_SetPriority( uint32_t ulInterruptID, uint32_t ulPriority ); + +/** + * Powers up the GIC Redistributor, Sets up the priority for SGI0, + * sets SGI0 to be a Group 1 interrupt, and enables delivery of Group-1 IRQs to EL1. + */ +void vGIC_SetupSgi0( void ); + +#endif /* GIC_H */ diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/boot.S b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/boot.S new file mode 100644 index 0000000..162fc31 --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/boot.S @@ -0,0 +1,225 @@ +/****************************************************************************** +* Copyright (c) 2014 - 2020 Xilinx, Inc. All rights reserved. +* Copyright 2025 Arm Limited and/or its affiliates +* SPDX-License-Identifier: MIT +******************************************************************************/ +#if defined(__ARMCC_VERSION) + /* Externs needed by the MPU setup code. These are defined in Scatter-Loading + * description file (armclang.sct). */ + .set __el1_stack, Image$$ARM_LIB_STACK$$Base + .set _el1_stack_end, Image$$ARM_LIB_HEAP$$Base +#endif + +#include "FreeRTOSConfig.h" + +.global _prestart +.global _boot + +.global __el1_stack +.global _vector_table + +.set EL1_stack, __el1_stack + +.set EL1_stack_end, _el1_stack_end + +.set vector_base, _vector_table + +/* + * N_CPUS_SHIFT must equal log2(configNUMBER_OF_CORES). It represents the + * number of bits required to index the core that owns a particular slice + * of the shared EL1 stack pool. + * + * To avoid overlapping stack regions, the code assumes + * configNUMBER_OF_CORES is a power‑of‑two. The static check below forces + * a build‑time error if that assumption is broken. + */ +#if ( (configNUMBER_OF_CORES & (configNUMBER_OF_CORES - 1)) != 0 ) + #error "configNUMBER_OF_CORES must be a power‑of‑two" +#endif + +/* Compute log2(configNUMBER_OF_CORES). */ +#if (configNUMBER_OF_CORES == 1) + .set N_CPUS_SHIFT, 0 +#elif (configNUMBER_OF_CORES == 2) + .set N_CPUS_SHIFT, 1 +#elif (configNUMBER_OF_CORES == 4) + .set N_CPUS_SHIFT, 2 +#else + #error "Unsupported configNUMBER_OF_CORES value — must be a power‑of‑two up to 4" +#endif + +.set SCTLR_EL1_SPAN, 23 /* SCTLR_EL1.SPAN bit position */ +.set SCTLR_EL1_NTWE, 18 /* SCTLR_EL1.nTWE bit position */ +.set SCTLR_EL1_NTWI, 16 /* SCTLR_EL1.nTWI bit position */ +.set SCTLR_EL1_CACHE, 2 /* SCTLR_EL1.C bit position */ + +.section .boot,"ax" + +_prestart: +_boot: +start: +/* Clear all GP registers (x0–x30) for a known initial state */ + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + mov x18, #0 + mov x19, #0 + mov x20, #0 + mov x21, #0 + mov x22, #0 + mov x23, #0 + mov x24, #0 + mov x25, #0 + mov x26, #0 + mov x27, #0 + mov x28, #0 + mov x29, #0 + mov x30, #0 + + mrs x0, currentEL + /* Check we’ve come from EL1 (currentEL==0x4), otherwise fault */ + cmp x0, #0x4 + beq InitEL1 + + b error +InitEL1: + /* Set vector table base address */ + ldr x1, =vector_base + msr VBAR_EL1,x1 + + mrs x0, CPACR_EL1 + /* Allow FP/SIMD at both EL1 and EL0: CPACR_EL1.FPEN[21:20] = 0b11 */ + orr x0, x0, #(0x3 << 20) + msr CPACR_EL1, x0 /* Enable FP/SIMD access at EL1 and EL0 */ + isb + + /* Clear FP status flags (FPSR) to avoid spurious exceptions on first use */ + msr FPSR, xzr + + /* Define stack pointer for current exception level */ +#if configNUMBER_OF_CORES > 1 + /* Divide the EL1 stack region equally among all cores, then set SP based on MPIDR_EL1[7:0] */ + /* x0 = log2(N_CPUS) is assumed to be a build-time constant */ + mov x0, N_CPUS_SHIFT /* log2(#cores) */ + /* load overall stack limits */ + ldr x2, =EL1_stack /* low address of the shared stack pool */ + ldr x3, =EL1_stack_end /* high address (one past the pool) */ + /* x1 = total size of the pool, x1 >> N_CPUS_SHIFT = size per core */ + sub x1, x3, x2 /* total_stack_size */ + lsr x1, x1, x0 /* slice_size = total/#cores */ + /* x4 = this CPU’s index (Aff0 field of MPIDR_EL1) */ + mrs x4, MPIDR_EL1 + and x4, x4, #0xFF /* core_id ∈ {0 … N_CPUS-1} */ + cmp x4, #configNUMBER_OF_CORES + b.hs error + /* x0 = slice_size * core_id → how far to step back from the top */ + mul x0, x1, x4 + /* sp = top_of_pool – offset (so core 0 gets the very top) */ + sub x3, x3, x0 /* x3 = initial SP for this core */ + bic x3, x3, #0xF /* keep the mandated 16-byte alignment */ + mov sp, x3 +#else + ldr x2, =EL1_stack_end + mov sp, x2 +#endif + + /* Enable ICC system-register interface (SRE=1) and disable FIQ/IRQ bypass (DFB/DIB) */ + mov x0, #0x7 + msr ICC_SRE_EL1, x0 + + /* Invalidate I and D caches */ + ic IALLU + bl invalidate_dcaches + dsb sy + isb + + /* Unmask SError interrupts (clear DAIF.A bit) */ + mrs x1,DAIF + bic x1,x1,#(0x1<<8) + msr DAIF,x1 + + /* Configure SCTLR_EL1: + * - Enable data cache (C=1) + * - Allow EL0 to execute WFE/WFI (Set nTWE/nTWI so they don't trap) + * - Set SPAN so PSTATE.PAN is preserved on exception entry + */ + mrs x1, SCTLR_EL1 + orr x1, x1, #(1 << SCTLR_EL1_SPAN) /* SPAN = 1 → The value of PSTATE.PAN is left unchanged on taking an exception to EL1 */ + orr x1, x1, #(1 << SCTLR_EL1_NTWE) /* nTWE = 1 → WFE at EL0 does not trap */ + orr x1, x1, #(1 << SCTLR_EL1_NTWI) /* nTWI = 1 → WFI at EL0 does not trap */ + orr x1, x1, #(1 << SCTLR_EL1_CACHE) /* C = 1 → enable data cache */ + msr SCTLR_EL1, x1 + isb + + /* Branch to C-level startup (zero BSS, init data, etc.) */ + bl _startup + +/* If we ever get here, something went wrong—hang forever */ +error: + b error + +invalidate_dcaches: + + dmb ISH + mrs x0, CLIDR_EL1 /* x0 = CLIDR */ + ubfx w2, w0, #24, #3 /* w2 = CLIDR.LoC */ + cmp w2, #0 /* LoC is 0? */ + b.eq invalidateCaches_end /* No cleaning required */ + mov w1, #0 /* w1 = level iterator */ + +invalidateCaches_flush_level: + add w3, w1, w1, lsl #1 /* w3 = w1 * 3 (right-shift for cache type) */ + lsr w3, w0, w3 /* w3 = w0 >> w3 */ + ubfx w3, w3, #0, #3 /* w3 = cache type of this level */ + cmp w3, #2 /* No cache at this level? */ + b.lt invalidateCaches_next_level + + lsl w4, w1, #1 + msr CSSELR_EL1, x4 /* Select current cache level in CSSELR */ + isb /* ISB required to reflect new CSIDR */ + mrs x4, CCSIDR_EL1 /* w4 = CSIDR */ + + ubfx w3, w4, #0, #3 + add w3, w3, #2 /* w3 = log2(line size) */ + ubfx w5, w4, #13, #15 + ubfx w4, w4, #3, #10 /* w4 = Way number */ + clz w6, w4 /* w6 = 32 - log2(number of ways) */ + +invalidateCaches_flush_set: + mov w8, w4 /* w8 = Way number */ +invalidateCaches_flush_way: + lsl w7, w1, #1 /* Fill level field */ + lsl w9, w5, w3 + orr w7, w7, w9 /* Fill level field */ + lsl w9, w8, w6 + orr w7, w7, w9 /* Fill way field */ + dc CISW, x7 /* Invalidate by set/way to point of coherency */ + subs w8, w8, #1 /* Decrement way */ + b.ge invalidateCaches_flush_way + subs w5, w5, #1 /* Descrement set */ + b.ge invalidateCaches_flush_set + +invalidateCaches_next_level: + add w1, w1, #1 /* Next level */ + cmp w2, w1 + b.gt invalidateCaches_flush_level + +invalidateCaches_end: + ret + +.end diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/gic.c b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/gic.c new file mode 100644 index 0000000..d2c91d8 --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/gic.c @@ -0,0 +1,128 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#include +#include "FreeRTOS.h" +#include "FreeRTOSConfig.h" +#include "gic.h" + +void vGIC_EnableCPUInterface( void ) +{ + /* Enable delivery of Group-1 IRQs to EL1 via ICC_IGRPEN1_EL1 */ + __asm volatile( + "msr ICC_IGRPEN1_EL1, %0\n" + "dsb sy\n" + "isb sy\n" + :: "r"(1UL) + : "memory" + ); +} + +void vGIC_PowerUpRedistributor( void ) +{ + volatile uint32_t *pulPwrr = ( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + + ( portPOINTER_SIZE_TYPE ) GICR_PWRR ); + + volatile uint32_t *pulWaker = ( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + + ( portPOINTER_SIZE_TYPE ) GICR_WAKER ); + + uint32_t ulTimeout = GIC_WAIT_TIMEOUT; + + /* Clear RDPD (Redistributor Power-Down) to 0 → power on the redistributor */ + *pulPwrr &= ~(1U << GICR_PWRR_RDPD_BIT); + /* Wait until RDPD reads 0 (powered up) */ + while( (*pulPwrr & (1U << GICR_PWRR_RDPD_BIT)) != 0U ) + { + if( --ulTimeout == 0U ) + { + configASSERT( "GICR_PWRR_RDPD_BIT did not clear in time" ); + return; + } + } + + /* Clear ProcessorSleep to 0 → wake hardware threads */ + *pulWaker &= ~(1U << GICR_WAKER_PS_BIT); + ulTimeout = GIC_WAIT_TIMEOUT; + /* Wait until ChildrenAsleep reads 0 (all subcomponents awake) */ + while( (*pulWaker & (1U << GICR_WAKER_CA_BIT)) != 0U ) + { + if( --ulTimeout == 0U ) + { + configASSERT( "GICR_WAKER_CA_BIT did not clear in time" ); + return; + } + } + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "isb sy" ::: "memory" ); +} + +void vGIC_InitDist( void ) +{ + /* Enable Group-1 Non-Secure (NS) and Secure (S) interrupts, and turn on Affinity-routing (ARE_S) + *plus Disable-Security (DS) for full GICv3 operation + */ + *( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICD_BASE + ( portPOINTER_SIZE_TYPE ) GICD_CTLR ) = ( 1 << GICD_CTLR_ENABLEGRP1NS_BIT ) | + ( 1 << GICD_CTLR_ENABLEGRP1S_BIT ) | + ( 1 << GICD_CTLR_ARES_BIT ) | + ( 1 << GICD_CTLR_DS_BIT ); + + /* Ensure distributor configuration is visible before continuing */ + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "isb sy" ::: "memory" ); +} + +void vGIC_SetPriority( uint32_t ulInterruptID, uint32_t ulPriority ) +{ + if( ( ulInterruptID <= GIC_MAX_INTERRUPT_ID ) && ( ulPriority <= 0xFF ) ) + { + /* Each GICR_IPRIORITYR holds 4 one‑byte priority fields. */ + uint32_t ulIndex = ulInterruptID / 4U; /* Register number */ + uint32_t ulShift = ( ulInterruptID % 4U ) * 8U; /* Byte lane offset */ + uint32_t ulMask = 0xFFUL << ulShift; /* Field mask */ + + volatile uint32_t * pulPriorityReg = ( volatile uint32_t * ) ( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + + ( portPOINTER_SIZE_TYPE ) GICR_IPRIORITYR( ulIndex ) ); + uint32_t ulRegValue = *pulPriorityReg; + ulRegValue &= ~( ulMask ); + ulRegValue |= ( ( uint32_t ) ulPriority << ulShift ); + *pulPriorityReg = ulRegValue; + /* Ensure priority write is observed */ + __asm volatile ( "dsb ish" ::: "memory" ); + } + else + { + configASSERT( "Invalid ulInterruptID or ulPriority passed to vGIC_SetPriority" ); + } +} + +void vGIC_EnableIRQ( uint32_t ulInterruptID ) +{ + if( ulInterruptID <= GIC_MAX_INTERRUPT_ID ) + { + uint32_t ulBitMask = 1U << ulInterruptID; + + /* 1. Assign the interrupt to group 1 */ + *( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + ( portPOINTER_SIZE_TYPE ) GICR_IGROUPR0 ) |= ( 1U << ulInterruptID ); + + /* 2. Enable the interrupt in GIC */ + *( volatile uint32_t* )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + ( portPOINTER_SIZE_TYPE ) GICR_ISENABLER0 ) |= ulBitMask; + + /* Ensure interrupt enable is visible */ + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "isb sy" ::: "memory" ); + } + else + { + configASSERT( "Invalid ulInterruptID for vGIC_EnableIRQ: must be < 32" ); + } +} + + +void vGIC_SetupSgi0( void ) +{ + vGIC_PowerUpRedistributor(); + vGIC_SetPriority( portYIELD_CORE_INT_ID, ( portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) ); + vGIC_EnableIRQ( portYIELD_CORE_INT_ID ); + vGIC_EnableCPUInterface(); +} diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S new file mode 100644 index 0000000..1380570 --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S @@ -0,0 +1,164 @@ +/****************************************************************************** +* +* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* Copyright (C) 2014 - 2020 Xilinx, Inc. All rights reserved. +* Copyright 2025 Arm Limited and/or its affiliates +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of +* this software and associated documentation files (the "Software"), to deal in +* the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +* the Software, and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* http://www.FreeRTOS.org +* http://aws.amazon.com/freertos +* +* 1 tab == 4 spaces! +* +******************************************************************************/ + +.org 0 +.text + +/* Entry point symbol for startup; referenced by the linker */ +.globl _boot + +/* Base of the EL1 exception vector table (vbases for EL1t/EL1h) */ +.globl _vector_table + +/* Alternate vector table used once the scheduler is running */ +.globl _freertos_vector_table + +.org 0 + +/* Map this section to read-only, executable memory as the EL1 vector base */ +.section .vectors, "ax" + +_vector_table: + +.set VBAR, _vector_table + +.org VBAR + b _boot + +.org (VBAR + 0x80) + b . + +.org (VBAR + 0x100) + b . + +.org (VBAR + 0x180) + b . + + +.org (VBAR + 0x200) + b . + +.org (VBAR + 0x280) + b . + +.org (VBAR + 0x300) + b . + +.org (VBAR + 0x380) + b . + +.org (VBAR + 0x400) + b . + +.org (VBAR + 0x480) + b . + +.org (VBAR + 0x500) + b . + +.org (VBAR + 0x580) + b . + +.org (VBAR + 0x600) + b . + +.org (VBAR + 0x680) + b . + +.org (VBAR + 0x700) + b . + +.org (VBAR + 0x780) + b . + + + +/****************************************************************************** + * Vector table to use when FreeRTOS is running. + *****************************************************************************/ +/* Reserve 0x1000 bytes so we can switch VBAR at runtime without overlap */ +.set FREERTOS_VBAR, (VBAR+0x1000) + +.org(FREERTOS_VBAR) + +/* Map this section to read-only, executable memory as the EL1 vector base */ +.section .vectors, "ax" + +_freertos_vector_table: + b FreeRTOS_SWI_Handler + +.org (FREERTOS_VBAR + 0x80) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x100) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x180) + b . + +.org (FREERTOS_VBAR + 0x200) + b FreeRTOS_SWI_Handler + +.org (FREERTOS_VBAR + 0x280) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x300) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x380) + b . + +.org (FREERTOS_VBAR + 0x400) + b FreeRTOS_SWI_Handler + +.org (FREERTOS_VBAR + 0x480) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x500) + b . + +.org (FREERTOS_VBAR + 0x580) + b . + +.org (FREERTOS_VBAR + 0x600) + b . + +.org (FREERTOS_VBAR + 0x680) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x700) + b . + +.org (FREERTOS_VBAR + 0x780) + b . + +.org (FREERTOS_VBAR + 0x800) + +.end diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/startup.S b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/startup.S new file mode 100644 index 0000000..5a254c4 --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/BSP/Source/startup.S @@ -0,0 +1,140 @@ +/****************************************************************************** +* Copyright (C) 2014 - 2020 Xilinx, Inc. All rights reserved. +* Copyright 2025 Arm Limited and/or its affiliates +* SPDX-License-Identifier: MIT +******************************************************************************/ + +#if defined(__ARMCC_VERSION) + .set __bss_start__, ( Image$$ER_IRAM_BSS_UNPRIVILEGED$$Base ) + .set __bss_end__, ( Image$$ARM_LIB_STACK$$Base - 1 ) + .set __data_start__, ( Image$$ER_IRAM_PRIVILEGED$$Base ) + .set __data_end__, ( Image$$ER_IRAM_BSS_UNPRIVILEGED$$Base - 1 ) + .set _sidata, ( Load$$ER_IRAM_PRIVILEGED$$Base ) +#endif + +#include "FreeRTOSConfig.h" +#include "portmacro.h" + .file "startup.S" + .align 2 + +.extern _vector_table +.extern ucSecondaryCoresReadyFlags +.extern ucPrimaryCoreInitDoneFlag +.extern _freertos_vector_table +.extern vSetupMPU + +.set freertos_vector_base, _freertos_vector_table + + .text + +/* Store the addresses of .bss start/end so they can be cleared if needed */ +.Lbss_start: + .quad __bss_start__ + +.Lbss_end: + .quad __bss_end__ + +/* + * _startup: early runtime init + * - For CORE0: copy .data, clear .bss + * - For all cores: hand off to C code + */ + .globl _startup +_startup: + +/* Only core0 initializes RAM image; others skip to c_init_end */ +#if configNUMBER_OF_CORES > 1 + /* Get CPU Id */ + mrs x0, MPIDR_EL1 + and x0, x0, #0xFF + cmp x0, #0 + bne c_init_end +#endif + + /* Copy .data section from ROM to RAM */ + ldr x0, =_sidata /* Source: ROM load address */ + ldr x1, =__data_start__ /* Destination: RAM */ + ldr x2, =__data_end__ /* End of destination */ + +/* Copy initialised data in 8-byte words for performance/alignment */ +1: cmp x1, x2 + b.ge .Lenclbss /* Done copying */ + ldr x3, [x0], #8 /* Load 64-bit word from ROM */ + str x3, [x1], #8 /* Store to RAM */ + b 1b + +/* Zero `.bss` in 8-byte words; efficient bulk clear of uninitialised data */ +.Lenclbss: + /* clear bss */ + ldr x1,.Lbss_start /* calculate beginning of the BSS */ + ldr x2,.Lbss_end /* calculate end of the BSS */ + +.Lloop_bss: + mov x0, #0 + cmp x1,x2 + bge libc_init /* If no BSS, no clearing required */ + str x0, [x1], #8 + b .Lloop_bss + +libc_init: +#if defined( __GNUC__ ) && !defined( __clang__ ) + /* Creates the _reent structure that holds errno, + * the three static FILE objects (stdin, stdout, stderr). + * Runs any functions placed in .preinit_array and calls _init(). + */ + bl __libc_init_array + /* Executes three semihosting SYS_OPEN SWIs to open the host's + * special file :tt three times, obtaining descriptors 0, 1 and 2 + * and writes those into stdin->_file, stdout->_file, stderr->_file. + * It also sets the __SWR (write-enabled) flag in each stream. + */ + bl initialise_monitor_handles +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ + +c_init_end: +#if configNUMBER_OF_CORES > 1 + /* Get CPU Id */ + mrs x0, MPIDR_EL1 + and x0, x0, #0xFF + cbnz x0, secondary_cores_hold +#endif + b jump_to_main + +#if configNUMBER_OF_CORES > 1 +secondary_cores_hold: + ldr x0, =ucPrimaryCoreInitDoneFlag + ldrb w1, [x0] /* Has the primary core set the flag? */ + cmp w1, #1 + b.eq make_secondary_cores_ready /* One? → Secondary cores released */ + wfe + b secondary_cores_hold /* Re-test the flag */ + +make_secondary_cores_ready: + ldr X1, = freertos_vector_base + msr VBAR_EL1, X1 + DSB SY + ISB SY + bl vGIC_SetupSgi0 + bl vSetupMPU + ldr x0, =ucSecondaryCoresReadyFlags + mrs x1, MPIDR_EL1 + and x1, x1, #0xFF + sub x1, x1, #1 /* Core 1 is index 0 in the array */ + add x0, x0, x1 + mov w1, #1 + strb w1, [x0] /* Mark this core as ready */ + bl vEnableMPU + SVC # portSVC_START_FIRST_TASK /* Start the first task on the secondary core */ +#endif + +jump_to_main: + ldr X1, = freertos_vector_base + msr VBAR_EL1, X1 /* Set VBAR_EL1 to Freertos vector table */ + DSB SY + ISB SY + mov x0, #0 + mov x1, #0 + bl main + +.Lexit: + b .Lexit diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/CMakeLists.txt b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/CMakeLists.txt new file mode 100644 index 0000000..311487e --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/CMakeLists.txt @@ -0,0 +1,64 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.15) + +project( + CORTEX-R82-SMP-MPU + VERSION 0.1 + LANGUAGES C ASM) + +set (CMAKE_BUILD_TYPE Release) + +set(CMAKE_EXECUTABLE_SUFFIX ".axf") + +get_filename_component(FREERTOS_DIR_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../.. REALPATH) +message(DEBUG "FREERTOS_DIR_PATH is ${FREERTOS_DIR_PATH}") + +set(KERNEL_DIR_PATH ${FREERTOS_DIR_PATH}/Source) +message(DEBUG "KERNEL_DIR_PATH is ${KERNEL_DIR_PATH}") + +# Select the native compile PORT +if("${CMAKE_C_COMPILER_ID}" STREQUAL "ARMClang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + set(FREERTOS_PORT "GCC_ARM_CR82" CACHE STRING "" FORCE) +else() + message(FATAL_ERROR "Unsupported compiler: "${CMAKE_C_COMPILER_ID}"") +endif() + +set(FREERTOS_HEAP "4" CACHE STRING "" FORCE) + +add_library(freertos_config INTERFACE) + +target_include_directories(freertos_config SYSTEM + INTERFACE + config +) + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/BSP BSP) + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../../../Source freertos_kernel) + +add_executable(cortex_r82_smp_mpu_fvp_example) + +target_sources(cortex_r82_smp_mpu_fvp_example + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/crt_replacements.c +) + +target_include_directories(cortex_r82_smp_mpu_fvp_example + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_options(cortex_r82_smp_mpu_fvp_example + PRIVATE + $<$:--map $<$:--list=cortex_r82_smp_mpu_fvp_example.map> --scatter=${CMAKE_CURRENT_SOURCE_DIR}/armclang_linker_script.sct> + $<$:-T${CMAKE_CURRENT_SOURCE_DIR}/gnu_linker_script.ld -Wl,--gc-sections,-Map=cortex_r82_smp_mpu_fvp_example.map> +) + +target_link_libraries(cortex_r82_smp_mpu_fvp_example + PRIVATE + bsp +) diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/README.md b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/README.md new file mode 100644 index 0000000..9bc5afc --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/README.md @@ -0,0 +1,126 @@ +# Arm Cortex‑R82 FreeRTOS SMP MPU Demo (FVP_BaseR_AEMv8R) + +**Overview** + +This demo brings up FreeRTOS‑SMP on Arm Cortex‑R82 `FVP_BaseR_AEMv8R` and demonstrates MPU‑backed isolation for unprivileged application tasks that communicate via queues while a privileged logger task prints activity. + +What actually runs in this test: + +- Sender task (unprivileged): periodically enqueues a counter value to a queue which is shared with the receiver task and logs a status message. +- Receiver task (unprivileged): dequeues messages from the same shared queue and logs what it received. +- Logger task (privileged): receives log messages from both unprivileged tasks and prints them to the console. + +The unprivileged tasks are created with `xTaskCreateRestricted()` and granted access only to a specific shared memory window that contains queue handles. The logger task runs privileged (via `portPRIVILEGE_BIT`). The demo validates correct MPU configuration for normal message‑passing between isolated tasks. + +# Prerequisites + +## Downloading and installing AEMv8R Architecture Envelope Model (AEM) Fixed Virtual Platform + +Follow the instructions on the [page](https://developer.arm.com/Tools%20and%20Software/Fixed%20Virtual%20Platforms/Arm%20Architecture%20FVPs) to download `FVP_Base_AEMv8R` based on your operating system. + +Ensure that requirements mentioned in the [page](https://developer.arm.com/documentation/100966/1126/Getting-Started-with-Fixed-Virtual-Platforms/Requirements-for-FVPs?lang=en) are met. + +Then, add the path to `FVP_BaseR_AEMv8R` executable to the environment variable `PATH` (the executable path would be something like `/home//AEMv8R_base_pkg/models/64__GCC-9.3/`). + +Execute the following command to ensure that the FVP was installed successfully +```bash +FVP_BaseR_AEMv8R --version + +Fast Models [11.xx.yy (MMM DD YYYY)] +Copyright 2000-2025 ARM Limited. +All Rights Reserved. +``` + +## Build tools + +* [CMake](https://cmake.org/download/) + * The Arm Cortex-R82 SMP MPU example uses `CMake` as the build system. +* [Arm GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) or [Arm Compiler for Embedded](https://developer.arm.com/Tools%20and%20Software/Arm%20Compiler%20for%20Embedded) + * To use Arm Compiler For Embedded (ArmClang), login is required for the download, and you will need a license in order to run the toolchain once installed. + +## Supported toolchains + +The example is supported and tested on the following toolchains: + + * Arm Compiler for Embedded v6.23 (armclang). + * Arm GNU toolchain v14.2. + +## Supported FVPs + +The example is supported and tested on FVP_BaseR_AEMv8R Fast Models [11.28.23 (Feb 17 2025)] + +## Cache Coherency + +This example relies on the `FVP_BaseR_AEMv8R` implementing fully coherent caches. If run on hardware or models without full cache coherency, additional cache maintenance or non‑cacheable mappings would be required for correctness. + +## What It Demonstrates + +- Privilege separation between kernel privileged code and unprivileged tasks using the Armv8‑R Profile MPU. +- Enforced read‑only flash regions for both privileged and unprivileged code, with a separate region for FreeRTOS system calls. +- Privileged‑only RAM for kernel data (for example, TCBs) versus unprivileged task stacks/data. +- Controlled data sharing via a user‑defined MPU region that exposes only queue handle storage to unprivileged tasks. + +## How It Works + +- MPU setup happens in `Source/portable/GCC/ARM_CR82/port.c` during `vSetupMPU()`, using linker‑defined region boundaries from `gnu_linker_script.ld` (GNU) or `armclang_linker_script.sct` (ArmClang). +- Regions configured include: + - `__privileged_functions_*__`: privileged flash, read‑only to privileged code. + - `__syscalls_flash_*__`: unprivileged‑accessible flash containing FreeRTOS system calls. + - `__unprivileged_flash_*__`: unprivileged flash, read‑only by all. + - `__privileged_sram_*__`: RAM reserved for kernel data, privileged‑only access. +- Unprivileged tasks are given one user‑defined MPU region for a small shared array that holds queue handles (`xQueueHandlesArray`). Stacks and the shared array are 64‑byte aligned. + +Important: Any user‑defined MPU region must be aligned to 64 bytes and shouldn't be less than 64 bytes in size. This demo explicitly aligns unprivileged task stacks and the shared queue handle array to 64 bytes to satisfy the MPU granularity and alignment requirements. + +## Building and Running + +First, run the following command to clone FreeRTOS repository: + +```bash +git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules +``` + +Run the following commands to build the example: + +```bash +cd FreeRTOS/FreeRTOS/Demo/ThirdParty/Partner-Supported-Demos/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG +rm -rf build && cmake -B build --toolchain=_toolchain.cmake . && cmake --build build +``` + +### Running + +Execute the following script to run the example: +```bash +./run.sh +``` + +### Expected Output + +Your console will show FVP startup messages followed by periodic sender/receiver activity logged by the privileged logger task, for example: + +``` +[Core: x] Sender: Sent message 0 +[Core: y] Receiver: Received message 0 +[Core: x] Sender: Sent message 1 +[Core: z] Receiver: Received message 1 +... (continues) ... +``` + +## Configuration — Up To 4 Cores + +FreeRTOS is built for SMP and the FVP model can start 1–4 Cortex‑R82 cores. Keep firmware and model in sync: + +- `config/FreeRTOSConfig.h`: set `#define configNUMBER_OF_CORES ` +- `fvp_config.txt`: set `cluster0.NUM_CORES=` + +Both values must match. Rebuild and run as usual. + +## Notes on MPU Regions + +- Minimum MPU granularity and alignment: 64 bytes. Ensure any user‑defined region base and size are 64‑byte aligned. This demo aligns unprivileged stacks and the shared handle array accordingly. + +- Keep user regions minimal. Only map the specific buffers that must be shared between unprivileged tasks. + +## License + +This example is released under the **MIT License**. diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/armclang_linker_script.sct b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/armclang_linker_script.sct new file mode 100644 index 0000000..a11ba1c --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/armclang_linker_script.sct @@ -0,0 +1,93 @@ +#! armclang --target=aarch64-arm-none-eabi -march=armv8-r -E -x c +/* + * Copyright (c) 2021-2025 Arm Limited. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#define __privileged_functions_start__ ( 0x80000000 ) +#define __SRAM_segment_start__ ( 0x00000000 ) +#define __STACK_SIZE ( 0x00010000 ) +#define __HEAP_SIZE ( 0x00020000 ) + +;=============================================================================== +; LOAD REGION: on-board flash 0x80000000 (4 MB) +;=============================================================================== +LOAD_REGION __privileged_functions_start__ +{ + ;-- Code + RO data --------------------------------------------------------- + ER_ROM_CODE __privileged_functions_start__ ALIGN 64 + { + *.o (.vectors +First) + *(privileged_functions) + } + + ER_IROM_PRIVILEGED_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ER_IROM_FREERTOS_SYSTEM_CALLS +0 ALIGN 64 + { + *(freertos_system_calls) + } + + ER_IROM_FREERTOS_SYSTEM_CALLS_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ER_IROM_UNPRIVILEGED +0 ALIGN 64 + { + *(+RO) + } + + ER_IROM_UNPRIVILEGED_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ER_IRAM_PRIVILEGED __SRAM_segment_start__ ALIGN 64 + { + *(privileged_data) + } + + ER_IRAM_PRIVILEGED_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ER_IRAM_DATA_UNPRIVILEGED +0 ALIGN 64 + { + *(.data*) + *(+RW) + } + + ER_IRAM_DATA_UNPRIVILEGED_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ER_IRAM_BSS_UNPRIVILEGED +0 ALIGN 64 + { + *(+ZI) + } + + ER_IRAM_BSS_UNPRIVILEGED_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ;-- Stack ------------------------------------------------------------------ + ARM_LIB_STACK +0 ALIGN 16 EMPTY (__STACK_SIZE) { + } + + ;-- Heap ------------------------------------------------------------------- + ARM_LIB_HEAP +0 ALIGN 16 EMPTY (__HEAP_SIZE) { + } +} diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/armclang_toolchain.cmake b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/armclang_toolchain.cmake new file mode 100644 index 0000000..7a1959d --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/armclang_toolchain.cmake @@ -0,0 +1,23 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_ARCH armv8-r) + +# Use the ARM Compiler 6 front-end +set(CMAKE_C_COMPILER armclang) +set(CMAKE_CXX_COMPILER armclang++) +set(CMAKE_ASM_COMPILER armclang) + +set(CMAKE_C_STANDARD 11) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# Common flags for compilation +# -Wno-pointer-to-int-cast and -Wno-int-to-pointer-cast are added to suppress warnings in FreeRTOS MPU code (i.e., mpu_wrappers_v2.c) not the Arm Cortex-R82 port code. +set(CMAKE_C_FLAGS "--target=aarch64-arm-none-eabi -mcpu=cortex-r82 -mlittle-endian -mstrict-align -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast" ) +set(CMAKE_ASM_FLAGS "--target=aarch64-arm-none-eabi -mcpu=cortex-r82 -mstrict-align" ) + +set(CMAKE_EXE_LINKER_FLAGS_INIT "--entry=_boot" ) +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_INIT}") diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h new file mode 100644 index 0000000..f5963c8 --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h @@ -0,0 +1,231 @@ +/* + * FreeRTOS V202212.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2024-2025 Arm Limited and/or its affiliates + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/****************************************************************************** +* See http://www.freertos.org/a00110.html for an explanation of the +* definitions contained in this file. +******************************************************************************/ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- +* Application specific definitions. +* +* These definitions should be adjusted for your particular hardware and +* application requirements. +* +* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE +* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. +* https://www.FreeRTOS.org/a00110.html +*----------------------------------------------------------*/ + +/* Ensure definitions are only used by the compiler, and not by the assembler. */ +#if !defined(__ASSEMBLER__) + #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) + extern uint32_t SystemCoreClock; + void vAssertCalled( const char * pcFile, unsigned long ulLine ); + #endif +#endif + +/* See https://freertos.org/a00110.html#configPROTECTED_KERNEL_OBJECT_POOL_SIZE for details. */ +#define configPROTECTED_KERNEL_OBJECT_POOL_SIZE 150 +/* See https://freertos.org/a00110.html#configSYSTEM_CALL_STACK_SIZE for details. */ +#define configSYSTEM_CALL_STACK_SIZE 128 + +/* Cortex M33 port configuration. */ +#define configENABLE_MPU 1 +#define configTOTAL_MPU_REGIONS 16 +#define configENABLE_FPU 1 +#define configUSE_TASK_FPU_SUPPORT 2 +#define configENABLE_TRUSTZONE 0 +#define configENABLE_MVE 0 + +/* Run FreeRTOS on the secure side and never jump to the non-secure side. */ +#define configRUN_FREERTOS_SECURE_ONLY 0 + +/* Constants related to the behaviour or the scheduler. */ +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#define configUSE_PREEMPTION 1 +#define configUSE_TIME_SLICING 1 +#define configMAX_PRIORITIES ( 10 ) +#define configIDLE_SHOULD_YIELD 1 +#define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_64_BITS + +/* Constants that describe the hardware and memory usage. */ +#define configCPU_CLOCK_HZ SystemCoreClock +#define configMINIMAL_STACK_SIZE ( ( uint16_t ) 512 ) +#define configMAX_TASK_NAME_LEN ( 12 ) +#define configTOTAL_HEAP_SIZE ( 0x20000 ) +#define configAPPLICATION_ALLOCATED_HEAP 0 + +/* Constants that build features in or out. */ +#define configUSE_MUTEXES 1 +#define configUSE_TICKLESS_IDLE 0 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_NEWLIB_REENTRANT 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_QUEUE_SETS 0 +#define configUSE_TASK_NOTIFICATIONS 1 +#define configUSE_TRACE_FACILITY 1 +#define configNUM_TX_DESCRIPTORS 15 +#define configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN 2 + +/* Constants that define which hook (callback) functions should be used. */ +#define configUSE_IDLE_HOOK 1 +#define configUSE_TICK_HOOK 1 +#define configUSE_MALLOC_FAILED_HOOK 1 + +/* Constants provided for debugging and optimisation assistance. */ +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ ); +#define configQUEUE_REGISTRY_SIZE 20 + +/* Software timer definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 20 +#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) + +/* Set the following definitions to 1 to include the API function, or zero + * to exclude the API function. NOTE: Setting an INCLUDE_ parameter to 0 is + * only necessary if the linker does not automatically remove functions that are + * not referenced anyway. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 0 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_uxTaskGetStackHighWaterMark2 1 +#define INCLUDE_xTaskGetIdleTaskHandle 0 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xSemaphoreGetMutexHolder 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTimerGetTimerDaemonTaskHandle 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskAbortDelay 1 + +/* This demo makes use of one or more example stats formatting functions. These + * format the raw data provided by the uxTaskGetSystemState() function in to + * human readable ASCII form. See the notes in the implementation of vTaskList() + * within FreeRTOS/Source/tasks.c for limitations. */ +#define configUSE_STATS_FORMATTING_FUNCTIONS 1 + +/* Dimensions a buffer that can be used by the FreeRTOS+CLI command interpreter. + * See the FreeRTOS+CLI documentation for more information: + * https://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/ */ +#define configCOMMAND_INT_MAX_OUTPUT_SIZE 2048 + +/* Interrupt priority configuration follows...................... */ + +/* Interrupt priorities used by the kernel port layer itself. These are generic +* to all Cortex-M ports, and do not rely on any particular library functions. */ +#define configKERNEL_INTERRUPT_PRIORITY ( configUNIQUE_INTERRUPT_PRIORITIES - 1 ) + +/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! + * See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ +#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configMAX_API_CALL_INTERRUPT_PRIORITY ) + +/* Constants related to the generation of run time stats. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() 0 + +/* Adjust configTICK_RATE_HZ and pdMS_TO_TICKS to simulate a tick per ms on a fast model */ +#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) +#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( xTimeInMs * 10 ) ) + +/* Enable dynamic allocation. */ +#define configSUPPORT_DYNAMIC_ALLOCATION 1 + +/* Hardware specific configurations. */ +#define configFPU_D32 0 +#define configUNIQUE_INTERRUPT_PRIORITIES 32 +#define configINTERRUPT_CONTROLLER_BASE_ADDRESS 0xAF000000UL +#define configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET 0x1000 + +#define configNUMBER_OF_CORES 4 + +/* SMP specific configurations. */ +#if ( configNUMBER_OF_CORES > 1 ) + #define configUSE_CORE_AFFINITY 0 + #define configRUN_MULTIPLE_PRIORITIES 1 + #define configUSE_PASSIVE_IDLE_HOOK 0 + #define configTIMER_SERVICE_TASK_CORE_AFFINITY 0 +#endif + +#if !defined(__ASSEMBLER__) + #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) + /* + * The application must provide a function that configures a peripheral to + * create the FreeRTOS tick interrupt, then define configSETUP_TICK_INTERRUPT() + * in FreeRTOSConfig.h to call the function. FreeRTOS_Tick_Handler() must + * be installed as the peripheral's interrupt handler. + */ + void vConfigureTickInterrupt( void ); + void vGIC_SetupSgi0( void ); + #define configSETUP_TICK_INTERRUPT() vConfigureTickInterrupt(); \ + vGIC_SetupSgi0() + #endif +#endif + +/* + * Interrupts that are assigned a priority at or below + * configMAX_API_CALL_INTERRUPT_PRIORITY (which counter-intuitively in the ARM + * generic interrupt controller [GIC] means a priority that has a numerical + * value above configMAX_API_CALL_INTERRUPT_PRIORITY) can call FreeRTOS safe API + * functions and will nest. + * + * Interrupts that are assigned a priority above + * configMAX_API_CALL_INTERRUPT_PRIORITY (which in the GIC means a numerical + * value below configMAX_API_CALL_INTERRUPT_PRIORITY) cannot call any FreeRTOS + * API functions, will nest, and will not be masked by FreeRTOS critical + * sections (although it is necessary for interrupts to be globally disabled + * extremely briefly as the interrupt mask is updated in the GIC). + * + * FreeRTOS functions that can be called from an interrupt are those that end in + * "FromISR". FreeRTOS maintains a separate interrupt safe API to enable + * interrupt entry to be shorter, faster, simpler and smaller. + * + * For the purpose of setting configMAX_API_CALL_INTERRUPT_PRIORITY 255 + * represents the lowest priority. + */ +#define configMAX_API_CALL_INTERRUPT_PRIORITY 18ULL + +#ifndef __ASSEMBLER__ + void vClearTickInterrupt( void ); + #define configCLEAR_TICK_INTERRUPT() vClearTickInterrupt() +#endif /* __ASSEMBLER__ */ + +#endif /* FREERTOS_CONFIG_H */ diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/crt_replacements.c b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/crt_replacements.c new file mode 100644 index 0000000..d43aa92 --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/crt_replacements.c @@ -0,0 +1,136 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#if defined( __GNUC__ ) && !defined( __clang__ ) + #include + #include +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ + +int fputc( int character, FILE *pxFile ) +{ + ( void )pxFile; // Unused parameter as required by the C standard + + register uint64_t ulSysWriteCode __asm__( "x0" ) = 0x03; // SYS_WRITEC: semihosting write character command + register const char *pcCharAddress __asm__( "x1" ) = ( const char * )&character; // Address of character to send + __asm volatile ( + "hlt #0xF000\n" // Issue semihosting call using HLT instruction + : "+r"( ulSysWriteCode ) + : "r"( pcCharAddress ) + : "memory" + ); + return character; +} + +int puts( const char *pcString ) +{ + const char *pcChar = pcString; + while ( *pcChar ) { + fputc( *pcChar++, stdout ); + } + fputc( '\n', stdout ); // Append newline to the output string + return 0; // Standard puts() returns non-negative on success +} + +#if defined( __GNUC__ ) && !defined( __clang__ ) + /* Non-re-entrant version provided by RDIMON. */ + extern int _fstat ( int /*fd*/, struct stat * /*buf*/ ); + + /* ------------------------------------------------------------------------- */ + int _fstat_r ( struct _reent *pxReent, int fd, struct stat *pxStatBuffer ) + { + // Create a 16-byte aligned temporary buffer for the stat structure needed by semihosting. + struct stat xAlignedStat __attribute__( ( aligned ( 16 ) ) ); + + int iFstatResult = _fstat( fd, &xAlignedStat ); // Perform semihosting call to get file status + + if ( iFstatResult == 0 ) { // Success: copy stat data safely + memcpy( pxStatBuffer, &xAlignedStat, sizeof( struct stat ) ); + } + else { // Failure: set error code from errno + pxReent->_errno = errno; + } + return iFstatResult; + } + + /* Helper: Returns true if the provided pointer is naturally 8-byte aligned. */ + static inline int prvIsEightByteAligned( const void *pvAddr ) + { + return ( ( ( uintptr_t )pvAddr ) & 0x7U ) == 0U; // Check alignment to an 8-byte boundary + } + + void *memmove( void *pvDestination, const void *pvSource, size_t xCount ) + { + unsigned char *pucDest = ( unsigned char * )pvDestination; + const unsigned char *pucSrc = ( const unsigned char * )pvSource; + + if ( pucDest == pucSrc || xCount == 0 ) + return pvDestination; + + if ( pucDest < pucSrc ) { // -------- forward copy -------- + if ( prvIsEightByteAligned( pucDest ) && prvIsEightByteAligned( pucSrc ) ) { + while ( xCount >= 8 ) { + *( uint64_t * )pucDest = *( const uint64_t * )pucSrc; + pucDest += 8; + pucSrc += 8; + xCount -= 8; + } + } + while ( xCount-- ) { // Copy remaining bytes forward + *pucDest++ = *pucSrc++; + } + } else { // -------- backward copy -------- + pucDest += xCount; + pucSrc += xCount; + if ( prvIsEightByteAligned( pucDest ) && prvIsEightByteAligned( pucSrc ) ) { + while ( xCount >= 8 ) { + pucDest -= 8; + pucSrc -= 8; + xCount -= 8; + *( uint64_t * )pucDest = *( const uint64_t * )pucSrc; + } + } + while ( xCount-- ) { // Copy remaining bytes in reverse order + *--pucDest = *--pucSrc; + } + } + return pvDestination; + } + + /* memcpy() may assume no overlap, so alias to memmove() */ + void *memcpy( void *pvDestination, const void *pvSource, size_t xCount ) + { + return memmove( pvDestination, pvSource, xCount ); + } + + /* Replacement for memset – linker will rename all calls to this symbol. */ + void *__wrap_memset( void *pvDestination, int value, size_t xCount ) + { + unsigned char *pucDest = ( unsigned char * )pvDestination; + unsigned char ucValue = ( unsigned char )value; + volatile size_t xCountNumber = xCount; + + /* Fast 8-byte stores when destination is naturally aligned. */ + if ( prvIsEightByteAligned( pucDest ) ) + { + uint64_t ullEightBytePattern = 0x0101010101010101ULL * ucValue; // Prepare an 8-byte pattern with all bytes set to ucValue + + while ( xCountNumber >= 8 ) + { + *( uint64_t * )pucDest = ullEightBytePattern; + pucDest += 8; + xCountNumber -= 8; + } + } + + /* Copy any remaining bytes (or if fully unaligned). */ + while ( xCountNumber-- > 0 ) + *pucDest++ = ucValue; + + return pvDestination; + } +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/fvp_config.txt b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/fvp_config.txt new file mode 100644 index 0000000..5e6bcba --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/fvp_config.txt @@ -0,0 +1,15 @@ +cluster0.has_aarch64=1 +cluster0.VMSA_supported=0 +cluster0.NUM_CORES=4 +cluster0.gicv3.SRE-enable-action-on-mmap=2 +cluster0.gicv3.cpuintf-mmap-access-level=2 +cluster0.gicv3.extended-interrupt-range-support=1 +cluster0.has_pl2=0 +cluster0.gicv3.SRE-EL2-enable-RAO=1 +gic_distributor.GICD_CTLR-DS-1-means-secure-only=1 +gic_distributor.has-two-security-states=0 +bp.refcounter.non_arch_start_at_default=1 +bp.vis.disable_visualisation=1 +bp.vis.rate_limit-enable=0 +cache_state_modelled=1 +semihosting-enable=1 diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/gnu_linker_script.ld b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/gnu_linker_script.ld new file mode 100644 index 0000000..9e5a423 --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/gnu_linker_script.ld @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2021-2025 Arm Limited. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Memory regions */ +MEMORY +{ + ROM (rwx) : ORIGIN = 0x80000000, LENGTH = 4M /* System ROM */ + RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 256M /* System RAM */ +} + +/* Sections */ +SECTIONS +{ + . = ORIGIN(ROM); /* Place text at ROM base (0x8000_0000) */ + + /* Code section */ + . = ALIGN(64); + .privileged_functions : ALIGN(64) + { + __privileged_functions_start__ = .; + KEEP(*(.vectors)) /* Vector table */ + KEEP(*(.init)) + KEEP(*(.fini)) + *(privileged_functions) + . = ALIGN(64); + __privileged_functions_end__ = .; + /* Adding 64 bytes pad at the end of the region to avoid end address for . privileged_functions and + * start address of .syscalls_flash sections to have different MPU region attributes with the same address. + */ + . = . + 0x40; + } > ROM + + .syscalls_flash : ALIGN(64) + { + __syscalls_flash_start__ = .; + *(freertos_system_calls) + . = ALIGN(64); + __syscalls_flash_end__ = .; + } > ROM + + .unprivileged_flash : ALIGN(64) + { + __unprivileged_flash_start__ = .; + *(.text*) /* Code */ + *(.rodata*) /* Read-only data */ + . = ALIGN(64); + __unprivileged_flash_end__ = .; + } > ROM + + .privileged_sram : + { + __privileged_sram_start__ = .; + __data_start__ = .; + *(privileged_data) + . = ALIGN(64); + __privileged_sram_end__ = .; + } > RAM AT> ROM /* load in ROM, run in RAM */ + _sidata = LOADADDR(.privileged_sram); + + .unprivileged_sram : + { + __unprivileged_sram_start__ = .; + *(.data*) + *(+RW) + . = ALIGN(64); + __unprivileged_sram_end__ = .; + __data_end__ = .; + } > RAM AT> ROM /* load in ROM, run in RAM */ + + /* Uninitialized data (BSS) */ + .bss : + { + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(64); + __bss_end__ = .; + } > RAM + + PROVIDE(end = __bss_end__); /* Alias for `_end`, commonly used in `_sbrk()` */ + + /* Stack section */ + .stack (NOLOAD) : ALIGN(16) + { + . += 0x10000; /* 64KB stack */ + } > RAM + + /* Heap section (can be used for dynamic memory allocation) */ + .heap (NOLOAD) : + { + . += 0x20000; /* 128KB heap */ + } > RAM +} + +/* Provide symbols for startup code */ +PROVIDE(__el1_stack = ADDR(.stack)); +PROVIDE(_el1_stack_end = ADDR(.stack) + SIZEOF(.stack)); diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/gnu_toolchain.cmake b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/gnu_toolchain.cmake new file mode 100644 index 0000000..fd2831d --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/gnu_toolchain.cmake @@ -0,0 +1,23 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR cortex-r82) + +set(CMAKE_C_COMPILER "aarch64-none-elf-gcc") +set(CMAKE_CXX_COMPILER "aarch64-none-elf-g++") +set(CMAKE_ASM_COMPILER "aarch64-none-elf-gcc") + +set(CMAKE_C_STANDARD 11) + +# -Wno-return-type, -Wno-unused-parameter, -Wno-attributes are added as aarch64-none-elf-gcc does not support __attribute__((naked)) which results in warnings. +# -Wno-pointer-to-int-cast and -Wno-int-to-pointer-cast are added to suppress warnings in FreeRTOS MPU code (i.e., mpu_wrappers_v2.c) not the Arm Cortex-R82 port code. +set(CMAKE_C_FLAGS "-mcpu=cortex-r82 -mabi=lp64 -mlittle-endian -Wall -Wextra -mstrict-align -Wno-return-type -Wno-unused-parameter -Wno-attributes -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") +set(CMAKE_ASM_FLAGS "-mcpu=cortex-r82 -mabi=lp64 -Wall") + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +set(CMAKE_EXE_LINKER_FLAGS_INIT + "-specs=rdimon.specs -Wl,-e,_boot,--wrap=memset,--emit-relocs -lc -lrdimon") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_INIT}") diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/main.c b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/main.c new file mode 100644 index 0000000..fbf08fb --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/main.c @@ -0,0 +1,449 @@ +/* Copyright 2023-2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "portmacro.h" + +/* GIC includes. */ +#include "gic.h" + +#define portINITIAL_PSTATE_EL0 ( 0x0ULL ) /* EL0, SP_EL0 */ +#define GENERIC_TIMER_IRQ ( 30UL ) /* Default IRQ for CNTP_EL0 */ +#define SGI0_IRQ ( 0UL ) /* SGI0 IRQ */ +#define GENERIC_TIMER_IRQ_PRIORITY ( portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) /* priority for CNTP_EL0 */ +#define GENERIC_TIMER_FREQ ( 24000000 ) /* Frequency for Generic Timer */ +#define TIMER_CTRL_ENABLE ( 1UL << 0 ) /* Timer ENABLE bit */ +#define TIMER_CTRL_IMASK ( 1UL << 1 ) /* Timer IMASK bit */ +#define QUEUE_LENGTH ( 100 ) /* Length of the logging queue */ +#define NUMBER_OF_SHARED_ELEMENTS ( 10 ) /* Number of shared elements in the shared array */ +#define TASKS_QUEUE_IDX ( 0 ) /* Index of the tasks queue handle in the shared array */ +#define LOG_QUEUE_IDX ( 1 ) /* Index of the logging queue handle in the shared array */ + + +typedef struct +{ + uint8_t ucText[256]; + uint64_t ullMsgId; + uint8_t ucCore; +} LogMsg_t; + +#if defined(__ARMCC_VERSION) + /* Externs needed by the MPU setup code. These are defined in Scatter-Loading + * description file (armclang.sct). */ + extern uint64_t Image$$ER_ROM_CODE$$Base; + extern uint64_t Image$$ER_IROM_PRIVILEGED_ALIGN$$Limit; + extern uint64_t Image$$ER_IROM_FREERTOS_SYSTEM_CALLS$$Base; + extern uint64_t Image$$ER_IROM_FREERTOS_SYSTEM_CALLS_ALIGN$$Limit; + extern uint64_t Image$$ER_IROM_UNPRIVILEGED$$Base; + extern uint64_t Image$$ER_IROM_UNPRIVILEGED_ALIGN$$Limit; + + extern uint64_t Image$$ER_IRAM_PRIVILEGED$$Base; + extern uint64_t Image$$ER_IRAM_PRIVILEGED_ALIGN$$Limit; + extern uint64_t Image$$ER_IRAM_BSS_UNPRIVILEGED$$Base; + extern uint64_t Image$$ER_IRAM_BSS_UNPRIVILEGED_ALIGN$$Limit; + + /* Privileged flash. */ + const uint64_t * __privileged_functions_start__ = ( uint64_t * ) &( Image$$ER_ROM_CODE$$Base ); + const uint64_t * __privileged_functions_end__ = ( uint64_t * ) ( ( uint64_t ) &( Image$$ER_IROM_PRIVILEGED_ALIGN$$Limit ) - 0x1 ); /* Last address in privileged Flash region. */ + + /* Flash containing system calls. */ + const uint64_t * __syscalls_flash_start__ = ( uint64_t * ) &( Image$$ER_IROM_FREERTOS_SYSTEM_CALLS$$Base ); + const uint64_t * __syscalls_flash_end__ = ( uint64_t * ) ( ( uint64_t ) &( Image$$ER_IROM_FREERTOS_SYSTEM_CALLS_ALIGN$$Limit ) - 0x1 ); /* Last address in Flash region containing system calls. */ + + /* Unprivileged flash. Note that the section containing system calls is + * unprivileged so that unprivileged tasks can make system calls. */ + const uint64_t * __unprivileged_flash_start__ = ( uint64_t * ) &( Image$$ER_IROM_UNPRIVILEGED$$Base ); + const uint64_t * __unprivileged_flash_end__ = ( uint64_t * ) ( ( uint64_t ) &( Image$$ER_IROM_UNPRIVILEGED_ALIGN$$Limit ) - 0x1 ); /* Last address in un-privileged Flash region. */ + + /* RAM with priviledged access only. This contains kernel data. */ + const uint64_t * __privileged_sram_start__ = ( uint64_t * ) &( Image$$ER_IRAM_PRIVILEGED$$Base ); + const uint64_t * __privileged_sram_end__ = ( uint64_t * ) ( ( uint64_t ) &( Image$$ER_IRAM_PRIVILEGED_ALIGN$$Limit ) - 0x1 ); /* Last address in privileged RAM. */ +#else + /* When building with GCC, these symbols come from gnu_linker_script.ld. */ + extern uint64_t __privileged_flash_start__[]; + extern uint64_t __privileged_flash_end__[]; + extern uint64_t __syscalls_flash_start__[]; + extern uint64_t __syscalls_flash_end__[]; + extern uint64_t __unprivileged_flash_start__[]; + extern uint64_t __unprivileged_flash_end__[]; + extern uint64_t __privileged_sram_start__[]; + extern uint64_t __privileged_sram_end__[]; +#endif + +TaskHandle_t xSenderTaskHandle = NULL; +TaskHandle_t xReceiverTaskHandle = NULL; +TaskHandle_t xLoggerTaskHandle = NULL; + +/* User defined task stack. */ +StackType_t xSenderTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 64 ) ) ); +StackType_t xReceiverTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 64 ) ) ); +StackType_t xLoggerTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 64 ) ) ); + +/* User defined shared queue handles array. */ +QueueHandle_t xQueueHandlesArray[ NUMBER_OF_SHARED_ELEMENTS ] __attribute__( ( aligned( 64 ) ) ); + +static uint64_t prvGetTimerClockHz ( void ); +static void prvSetTimerClockHz ( uint64_t ullPhysicalTimerFreq ); +extern void FreeRTOS_Tick_Handler( void ); +extern void FreeRTOS_SGI_Handler( void ); + +void vAssertCalled( const char * pcFile, + unsigned long ulLine ) +{ + printf( "ASSERT failed! file %s:%lu, \r\n", pcFile, ulLine ); + + taskENTER_CRITICAL(); + { + volatile unsigned long looping = 0; + + /* Use the debugger to set ul to a non-zero value in order to step out + * of this function to determine why it was called. */ + while( looping == 0LU ) + { + portNOP(); + } + } + taskEXIT_CRITICAL(); +} + + +static void * prvTinyMemCpy( void * pvDst, const void * pvSrc, size_t xNumberOfElements ) +{ + uint8_t * pucDestination = ( uint8_t * ) pvDst; + const uint8_t * pucSource = ( const uint8_t * ) pvSrc; + while ( xNumberOfElements-- ) { + *pucDestination++ = *pucSource++; + } + return pucDestination; +} + +static size_t prvTinyStrnlen( const char * pcString, size_t xMaxLength ) +{ + size_t xLength = 0; + + if( pcString == NULL ) + { + return 0; + } + + while( ( xLength < xMaxLength ) && ( pcString[ xLength ] != '\0' ) ) + { + xLength++; + } + + return xLength; +} + +static void prvLogMsg( const char * pcMessage, uint64_t ullMsgId ) +{ + LogMsg_t xLogMsg; + + /* Leave room for the terminating NUL character. */ + size_t xMaxCopyLength = sizeof( xLogMsg.ucText ) - 1U; + size_t xMsgLength = prvTinyStrnlen( pcMessage, xMaxCopyLength ); + prvTinyMemCpy( xLogMsg.ucText, pcMessage, xMsgLength ); + xLogMsg.ucText[ xMsgLength ] = '\0'; + xLogMsg.ullMsgId = ullMsgId; + xLogMsg.ucCore = ucPortGetCoreID(); + + (void) xQueueSend( xQueueHandlesArray[ LOG_QUEUE_IDX ], &xLogMsg, portMAX_DELAY ); +} + +/* Privileged logger task: receives log messages and prints them. */ +static void prvLoggerTask( void * arg ) +{ + ( void ) arg; + LogMsg_t xLogMsg; + while( 1) + { + if( xQueueReceive( xQueueHandlesArray[ LOG_QUEUE_IDX ], &xLogMsg, portMAX_DELAY ) == pdPASS ) + { + printf( "[Core: %d] %s %lu\r\n", xLogMsg.ucCore, xLogMsg.ucText, xLogMsg.ullMsgId ); + } + } +} + +static void prvSenderTask( void * arg ) +{ + /* Prevent the compiler warning about the unused parameter. */ + ( void ) arg; + uint64_t ullCounter = 0; + + while( 1 ) + { + /* Block until space is available. */ + if( xQueueSend( xQueueHandlesArray[ TASKS_QUEUE_IDX ], &ullCounter, portMAX_DELAY ) == pdPASS ) + { + prvLogMsg( "Sender: Sent message", ullCounter++); + } + else + { + prvLogMsg( "Sender: Failed to send message", ullCounter); + } + vTaskDelay( 100 ); + } +} + +static void prvRecieverTask( void * arg ) +{ + /* Prevent the compiler warning about the unused parameter. */ + ( void ) arg; + uint64_t ullCounter; + + while( 1 ) + { + /* Block until a message arrives. */ + if( xQueueReceive( xQueueHandlesArray[ TASKS_QUEUE_IDX ], &ullCounter, portMAX_DELAY ) == pdPASS ) + { + /* Process the message */ + prvLogMsg( "Receiver: Received message", ullCounter ); + } + else + { + prvLogMsg( "Receiver: Failed to receive message", ullCounter); + } + } +} + +int main() +{ + TaskParameters_t xSenderTaskParameters = + { + .pvTaskCode = prvSenderTask, + .pcName = NULL, + .usStackDepth = configMINIMAL_STACK_SIZE, + .pvParameters = NULL, + .uxPriority = ( tskIDLE_PRIORITY + 2 ), + .puxStackBuffer = xSenderTaskStack, + .xRegions = + { + /* Base address, size, parameters. */ + { xQueueHandlesArray, NUMBER_OF_SHARED_ELEMENTS * sizeof( QueueHandle_t ), tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER | tskMPU_REGION_INNER_SHAREABLE }, + } + }; + + if(xTaskCreateRestricted( &( xSenderTaskParameters ), &xSenderTaskHandle ) == pdFAIL) + { + return EXIT_FAILURE; + } + + TaskParameters_t xReceiverTaskParameters = + { + .pvTaskCode = prvRecieverTask, + .pcName = NULL, + .usStackDepth = configMINIMAL_STACK_SIZE, + .pvParameters = NULL, + .uxPriority = ( tskIDLE_PRIORITY + 1 ), + .puxStackBuffer = xReceiverTaskStack, + .xRegions = + { + /* Base address, size, parameters. */ + { xQueueHandlesArray, NUMBER_OF_SHARED_ELEMENTS * sizeof( QueueHandle_t ), tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER | tskMPU_REGION_INNER_SHAREABLE }, + } + }; + + if(xTaskCreateRestricted( &( xReceiverTaskParameters ), &xReceiverTaskHandle ) == pdFAIL) + { + return EXIT_FAILURE; + } + + if(xTaskCreate( prvLoggerTask, + NULL, + configMINIMAL_STACK_SIZE, + NULL, + ( ( tskIDLE_PRIORITY + 2 ) | portPRIVILEGE_BIT ), + &xLoggerTaskHandle ) == pdFAIL ) + { + return EXIT_FAILURE; + } + + /* Queue for sending data back and forth between the sender and receiver tasks */ + xQueueHandlesArray[ TASKS_QUEUE_IDX ] = xQueueCreate( QUEUE_LENGTH, sizeof( uint64_t ) ); + + if( xQueueHandlesArray[ TASKS_QUEUE_IDX ] == NULL ) + { + return EXIT_FAILURE; + } + + /* Dedicated logging queue */ + xQueueHandlesArray[ LOG_QUEUE_IDX ] = xQueueCreate( QUEUE_LENGTH, sizeof( LogMsg_t ) ); + + if( xQueueHandlesArray[ LOG_QUEUE_IDX ] == NULL ) + { + return EXIT_FAILURE; + } + + vTaskStartScheduler(); + + /* If all is well, the scheduler will now be running, and the following + * line will never be reached. If the following line does execute, then + * there was insufficient FreeRTOS heap memory available for the idle and/or + * timer tasks to be created. See the memory management section on the + * FreeRTOS web site for more details. NOTE: This demo uses static allocation + * for the idle and timer tasks so this line should never execute. + */ + for( ; ; ) + { + } +} + +/** + * Dummy implementation of the callback function vApplicationStackOverflowHook(). + */ +#if ( configCHECK_FOR_STACK_OVERFLOW > 0 ) + void vApplicationStackOverflowHook( TaskHandle_t xTask, + char * pcTaskName ) + { + ( void ) xTask; + ( void ) pcTaskName; + + /* Assert when stack overflow is enabled but no application defined function exists */ + configASSERT( 0 ); + } +#endif + +/*---------------------------------------------------------------------------*/ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + +/* + * vApplicationGetIdleTaskMemory gets called when configSUPPORT_STATIC_ALLOCATION + * equals to 1 and is required for static memory allocation support. + */ + + __WEAK void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, + StackType_t ** ppxIdleTaskStackBuffer, + StackType_t * pulIdleTaskStackSize ) + { + /* Idle task control block and stack */ + static StaticTask_t Idle_TCB = { 0 }; + static StackType_t Idle_Stack[ configMINIMAL_STACK_SIZE ] = { 0 }; + + *ppxIdleTaskTCBBuffer = &Idle_TCB; + *ppxIdleTaskStackBuffer = &Idle_Stack[ 0 ]; + *pulIdleTaskStackSize = ( uint32_t ) configMINIMAL_STACK_SIZE; + } + +/* + * vApplicationGetTimerTaskMemory gets called when configSUPPORT_STATIC_ALLOCATION + * equals to 1 and is required for static memory allocation support. + */ + __WEAK void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer, + StackType_t ** ppxTimerTaskStackBuffer, + StackType_t * pulTimerTaskStackSize ) + { + /* Timer task control block and stack */ + static StaticTask_t Timer_TCB = { 0 }; + static StackType_t Timer_Stack[ configTIMER_TASK_STACK_DEPTH ] = { 0 }; + + *ppxTimerTaskTCBBuffer = &Timer_TCB; + *ppxTimerTaskStackBuffer = &Timer_Stack[ 0 ]; + *pulTimerTaskStackSize = ( uint32_t ) configTIMER_TASK_STACK_DEPTH; + } +#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + +void vApplicationTickHook( void ) +{ + /* Provide a stub for this function. */ +} + +void vApplicationIdleHook( void ) +{ + /* Exit. Just a stub. */ +} + +void vApplicationMallocFailedHook( void ) +{ + /* Provide a stub for this function. */ +} + +void vConfigureTickInterrupt( void ) +{ + uint64_t ulTickInterval; + uint32_t ulControlRegister = 0U; + + __asm volatile ( "dsb sy" ::: "memory" ); + prvSetTimerClockHz( GENERIC_TIMER_FREQ ); + + /* Calculate tick interval based on configured FreeRTOS tick rate */ + ulTickInterval = ( prvGetTimerClockHz() / configTICK_RATE_HZ ); + + /* Disable CNTP timer before configuring */ + __asm volatile ( "msr cntp_ctl_el0, xzr" ); + + /* Set the timer interval (time until next interrupt) */ + __asm volatile ( "msr cntp_tval_el0, %0" :: "r" ( ulTickInterval ) ); + + /* Enable the timer interrupt in the GIC */ + vGIC_InitDist(); + vGIC_PowerUpRedistributor(); + vGIC_SetPriority( GENERIC_TIMER_IRQ, GENERIC_TIMER_IRQ_PRIORITY ); + vGIC_EnableIRQ( GENERIC_TIMER_IRQ ); + vGIC_EnableCPUInterface(); + + /* Enable the timer without masking interrupts */ + __asm volatile ( "msr cntp_ctl_el0, %0" :: "r" ( ulControlRegister | TIMER_CTRL_ENABLE ) ); + __asm volatile ( "isb sy" ::: "memory" ); +} + +static uint64_t prvGetTimerClockHz ( void ) +{ + uint64_t ullPhysicalTimerFreq; + __asm volatile ( "mrs %0, cntfrq_el0" : "=r" ( ullPhysicalTimerFreq ) ); + return ullPhysicalTimerFreq; +} + +static void prvSetTimerClockHz ( uint64_t ullPhysicalTimerFreq ) +{ + __asm volatile ( "msr cntfrq_el0, %0" :: "r" ( ullPhysicalTimerFreq ) ); +} + +void vApplicationIRQHandler( uint32_t ulICCIAR ) +{ + /* The ID of the interrupt is obtained by bitwise anding the ICCIAR value + with 0x3FF. */ + uint32_t ulInterruptID = ulICCIAR & 0x3FFUL; + if( ulInterruptID == GENERIC_TIMER_IRQ ) + { + FreeRTOS_Tick_Handler(); + } + #if ( configNUMBER_OF_CORES > 1 ) + else if( ulInterruptID == SGI0_IRQ ) + { + FreeRTOS_SGI_Handler(); + } + #endif + else + { + /* Handle other interrupts as needed. */ + printf( "Unhandled interrupt ID: %u\r\n", ulInterruptID ); + } +} + +void vClearTickInterrupt( void ) +{ + /* Disable CNTP timer interrupt before re-configuring */ + uint64_t ulControlRegister = 0U; + ulControlRegister |= TIMER_CTRL_IMASK; + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "msr cntp_ctl_el0, %0" :: "r" ( ulControlRegister ) ); + + /* Calculate tick interval based on configured FreeRTOS tick rate */ + uint64_t ulTickInterval = ( prvGetTimerClockHz() / configTICK_RATE_HZ ); + /* Set the timer interval (time until next interrupt) */ + __asm volatile ( "msr cntp_tval_el0, %0" :: "r" ( ulTickInterval ) ); + + /* Enable the timer without masking interrupts */ + ulControlRegister &= ~( TIMER_CTRL_IMASK ); + ulControlRegister |= TIMER_CTRL_ENABLE; + __asm volatile ( "msr cntp_ctl_el0, %0" :: "r" ( ulControlRegister ) ); + __asm volatile ( "isb sy" ::: "memory" ); +} diff --git a/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/run.sh b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/run.sh new file mode 100755 index 0000000..ea4692b --- /dev/null +++ b/CORTEX_R82_SMP_MPU_FVP_GCC_ARMCLANG/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Copyright 2025 Arm Limited and/or its affiliates +# SPDX-License-Identifier: MIT + +FVP_BaseR_AEMv8R -a build/cortex_r82_smp_mpu_fvp_example.axf --config ./fvp_config.txt From 9115d378dbd44734aedcfbf61cac92a13fa78a11 Mon Sep 17 00:00:00 2001 From: Ahmed Ismail Date: Mon, 17 Nov 2025 14:20:17 +0000 Subject: [PATCH 3/3] cortex-r82-extended-mpu-example: Add MPU violations example This commit adds the Arm Cortex-R82 Extended MPU example which creates three tasks that intentionally exercise MPU protection violations. Signed-off-by: Ahmed Ismail --- .../BSP/CMakeLists.txt | 25 + .../BSP/Include/gic.h | 79 +++ .../BSP/Source/boot.S | 225 +++++++ .../BSP/Source/gic.c | 128 ++++ .../BSP/Source/port_asm_vectors.S | 164 +++++ .../BSP/Source/startup.S | 140 +++++ .../CMakeLists.txt | 64 ++ .../README.md | 132 +++++ .../armclang_linker_script.sct | 92 +++ .../armclang_toolchain.cmake | 23 + .../config/FreeRTOSConfig.h | 229 +++++++ .../crt_replacements.c | 136 +++++ .../fvp_config.txt | 15 + .../gnu_linker_script.ld | 106 ++++ .../gnu_toolchain.cmake | 23 + .../main.c | 559 ++++++++++++++++++ .../run.sh | 6 + 17 files changed, 2146 insertions(+) create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Include/gic.h create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/boot.S create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/gic.c create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/startup.S create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/CMakeLists.txt create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/README.md create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/armclang_linker_script.sct create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/armclang_toolchain.cmake create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/crt_replacements.c create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/fvp_config.txt create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/gnu_linker_script.ld create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/gnu_toolchain.cmake create mode 100644 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/main.c create mode 100755 CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/run.sh diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt new file mode 100644 index 0000000..84a0d52 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.15) + +add_library(bsp INTERFACE) + +target_sources(bsp + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/Source/port_asm_vectors.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/boot.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/startup.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/gic.c +) + +target_include_directories(bsp + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/Include +) + +target_link_libraries(bsp + INTERFACE + freertos_kernel +) diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Include/gic.h b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Include/gic.h new file mode 100644 index 0000000..2da5c38 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Include/gic.h @@ -0,0 +1,79 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#ifndef GIC_H +#define GIC_H + +#include "FreeRTOSConfig.h" + +#if ( configNUMBER_OF_CORES == 1 ) + #define ucPortGetCoreID() ( 0 ) /* Single core system, always core 0 */ +#endif + +#define GICD_BASE ( 0xAF000000UL ) /* Base of GIC Distributor on BaseR FVP */ +#define GICR_BASE_PER_CORE( core ) ( 0xAF100000UL + ( 0x20000UL * ( core ) ) ) /* Base of GIC Redistributor per core on BaseR FVP */ +#define SGI_BASE ( 0x10000UL ) /* SGI Base */ +#define GICD_CTLR ( 0x000 ) /* Distributor Control Register */ +#define GICR_WAKER ( 0x14 ) /* ReDistributor Wake Register */ +#define GICR_PWRR ( 0x24 ) /* ReDistributor Power Register */ +#define GICR_IGROUPR0 ( SGI_BASE + 0x80 ) /* Interrupt Group Registers */ +#define GICR_ISENABLER0 ( SGI_BASE + 0x100 ) /* Interrupt Set-Enable Registers */ +#define GICR_IPRIORITYR( n ) ( SGI_BASE + ( 0x400 + ( 4 * n ) ) ) /* Interrupt Priority Registers */ +#define GICR_IGRPMODR0 ( SGI_BASE + 0xD00 ) /* Distributor Interrupt group modifier Register */ + +#define GICD_CTLR_ENABLEGRP1NS_BIT ( 1U ) /* GICD_CTRL.EnableGrp1NS bit */ +#define GICD_CTLR_ENABLEGRP1S_BIT ( 2U ) /* GICD_CTRL.EnableGrp1S bit */ +#define GICD_CTLR_ARES_BIT ( 4U ) /* GICD_CTRL.ARE_S bit */ +#define GICD_CTLR_DS_BIT ( 6U ) /* GICD_CTRL.DS bit */ + +#define GICR_PWRR_RDPD_BIT ( 0U ) /* GICR_PWRR.RDPD bit */ + +#define GICR_WAKER_PS_BIT ( 1U ) /* GICR_WAKER.PS bit */ +#define GICR_WAKER_CA_BIT ( 2U ) /* GICR_WAKER.CA bit */ + +#define GIC_MAX_INTERRUPT_ID ( 31UL ) /* Maximum Interrupt ID for PPIs and SGIs */ +#define GIC_WAIT_TIMEOUT ( 1000000U ) /* Timeout for waiting on GIC operations */ + +/** + * Assigns the specified interrupt to Group 1 and enables it + * in the Redistributor for the local core. + */ +void vGIC_EnableIRQ( uint32_t ulInterruptID ); + +/** + * Enables signaling of Group-1 interrupts at EL1 via ICC_IGRPEN1_EL1. + */ +void vGIC_EnableCPUInterface( void ); + +/** + * Initializes the GIC Distributor: + * - Enables Group-1 Non-Secure and Group-1 Secure interrupts + * - Enables Affinity Routing (ARE_S) and Disable Security (DS) bits + */ +void vGIC_InitDist( void ); + +/** + * Powers up and wakes the Redistributor for the current core: + * 1. Clears the Redistributor power-down bit and waits for RDPD=0 + * 2. Clears the Processor-Sleep bit and waits for Children-Asleep=0 + */ +void vGIC_PowerUpRedistributor( void ); + +/** + * Sets the priority of the specified SGI/PPI (INTID 0‑31) in the local + * Redistributor bank via GICR_IPRIORITYR. + * For shared peripheral interrupts (SPI, INTID ≥ 32) use the GICD_IPRIORITYR path. + * + * @param ulInterruptID The ID of the interrupt to set the priority for. + * @param ulPriority The priority value to set. + */ +void vGIC_SetPriority( uint32_t ulInterruptID, uint32_t ulPriority ); + +/** + * Powers up the GIC Redistributor, Sets up the priority for SGI0, + * sets SGI0 to be a Group 1 interrupt, and enables delivery of Group-1 IRQs to EL1. + */ +void vGIC_SetupSgi0( void ); + +#endif /* GIC_H */ diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/boot.S b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/boot.S new file mode 100644 index 0000000..162fc31 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/boot.S @@ -0,0 +1,225 @@ +/****************************************************************************** +* Copyright (c) 2014 - 2020 Xilinx, Inc. All rights reserved. +* Copyright 2025 Arm Limited and/or its affiliates +* SPDX-License-Identifier: MIT +******************************************************************************/ +#if defined(__ARMCC_VERSION) + /* Externs needed by the MPU setup code. These are defined in Scatter-Loading + * description file (armclang.sct). */ + .set __el1_stack, Image$$ARM_LIB_STACK$$Base + .set _el1_stack_end, Image$$ARM_LIB_HEAP$$Base +#endif + +#include "FreeRTOSConfig.h" + +.global _prestart +.global _boot + +.global __el1_stack +.global _vector_table + +.set EL1_stack, __el1_stack + +.set EL1_stack_end, _el1_stack_end + +.set vector_base, _vector_table + +/* + * N_CPUS_SHIFT must equal log2(configNUMBER_OF_CORES). It represents the + * number of bits required to index the core that owns a particular slice + * of the shared EL1 stack pool. + * + * To avoid overlapping stack regions, the code assumes + * configNUMBER_OF_CORES is a power‑of‑two. The static check below forces + * a build‑time error if that assumption is broken. + */ +#if ( (configNUMBER_OF_CORES & (configNUMBER_OF_CORES - 1)) != 0 ) + #error "configNUMBER_OF_CORES must be a power‑of‑two" +#endif + +/* Compute log2(configNUMBER_OF_CORES). */ +#if (configNUMBER_OF_CORES == 1) + .set N_CPUS_SHIFT, 0 +#elif (configNUMBER_OF_CORES == 2) + .set N_CPUS_SHIFT, 1 +#elif (configNUMBER_OF_CORES == 4) + .set N_CPUS_SHIFT, 2 +#else + #error "Unsupported configNUMBER_OF_CORES value — must be a power‑of‑two up to 4" +#endif + +.set SCTLR_EL1_SPAN, 23 /* SCTLR_EL1.SPAN bit position */ +.set SCTLR_EL1_NTWE, 18 /* SCTLR_EL1.nTWE bit position */ +.set SCTLR_EL1_NTWI, 16 /* SCTLR_EL1.nTWI bit position */ +.set SCTLR_EL1_CACHE, 2 /* SCTLR_EL1.C bit position */ + +.section .boot,"ax" + +_prestart: +_boot: +start: +/* Clear all GP registers (x0–x30) for a known initial state */ + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + mov x18, #0 + mov x19, #0 + mov x20, #0 + mov x21, #0 + mov x22, #0 + mov x23, #0 + mov x24, #0 + mov x25, #0 + mov x26, #0 + mov x27, #0 + mov x28, #0 + mov x29, #0 + mov x30, #0 + + mrs x0, currentEL + /* Check we’ve come from EL1 (currentEL==0x4), otherwise fault */ + cmp x0, #0x4 + beq InitEL1 + + b error +InitEL1: + /* Set vector table base address */ + ldr x1, =vector_base + msr VBAR_EL1,x1 + + mrs x0, CPACR_EL1 + /* Allow FP/SIMD at both EL1 and EL0: CPACR_EL1.FPEN[21:20] = 0b11 */ + orr x0, x0, #(0x3 << 20) + msr CPACR_EL1, x0 /* Enable FP/SIMD access at EL1 and EL0 */ + isb + + /* Clear FP status flags (FPSR) to avoid spurious exceptions on first use */ + msr FPSR, xzr + + /* Define stack pointer for current exception level */ +#if configNUMBER_OF_CORES > 1 + /* Divide the EL1 stack region equally among all cores, then set SP based on MPIDR_EL1[7:0] */ + /* x0 = log2(N_CPUS) is assumed to be a build-time constant */ + mov x0, N_CPUS_SHIFT /* log2(#cores) */ + /* load overall stack limits */ + ldr x2, =EL1_stack /* low address of the shared stack pool */ + ldr x3, =EL1_stack_end /* high address (one past the pool) */ + /* x1 = total size of the pool, x1 >> N_CPUS_SHIFT = size per core */ + sub x1, x3, x2 /* total_stack_size */ + lsr x1, x1, x0 /* slice_size = total/#cores */ + /* x4 = this CPU’s index (Aff0 field of MPIDR_EL1) */ + mrs x4, MPIDR_EL1 + and x4, x4, #0xFF /* core_id ∈ {0 … N_CPUS-1} */ + cmp x4, #configNUMBER_OF_CORES + b.hs error + /* x0 = slice_size * core_id → how far to step back from the top */ + mul x0, x1, x4 + /* sp = top_of_pool – offset (so core 0 gets the very top) */ + sub x3, x3, x0 /* x3 = initial SP for this core */ + bic x3, x3, #0xF /* keep the mandated 16-byte alignment */ + mov sp, x3 +#else + ldr x2, =EL1_stack_end + mov sp, x2 +#endif + + /* Enable ICC system-register interface (SRE=1) and disable FIQ/IRQ bypass (DFB/DIB) */ + mov x0, #0x7 + msr ICC_SRE_EL1, x0 + + /* Invalidate I and D caches */ + ic IALLU + bl invalidate_dcaches + dsb sy + isb + + /* Unmask SError interrupts (clear DAIF.A bit) */ + mrs x1,DAIF + bic x1,x1,#(0x1<<8) + msr DAIF,x1 + + /* Configure SCTLR_EL1: + * - Enable data cache (C=1) + * - Allow EL0 to execute WFE/WFI (Set nTWE/nTWI so they don't trap) + * - Set SPAN so PSTATE.PAN is preserved on exception entry + */ + mrs x1, SCTLR_EL1 + orr x1, x1, #(1 << SCTLR_EL1_SPAN) /* SPAN = 1 → The value of PSTATE.PAN is left unchanged on taking an exception to EL1 */ + orr x1, x1, #(1 << SCTLR_EL1_NTWE) /* nTWE = 1 → WFE at EL0 does not trap */ + orr x1, x1, #(1 << SCTLR_EL1_NTWI) /* nTWI = 1 → WFI at EL0 does not trap */ + orr x1, x1, #(1 << SCTLR_EL1_CACHE) /* C = 1 → enable data cache */ + msr SCTLR_EL1, x1 + isb + + /* Branch to C-level startup (zero BSS, init data, etc.) */ + bl _startup + +/* If we ever get here, something went wrong—hang forever */ +error: + b error + +invalidate_dcaches: + + dmb ISH + mrs x0, CLIDR_EL1 /* x0 = CLIDR */ + ubfx w2, w0, #24, #3 /* w2 = CLIDR.LoC */ + cmp w2, #0 /* LoC is 0? */ + b.eq invalidateCaches_end /* No cleaning required */ + mov w1, #0 /* w1 = level iterator */ + +invalidateCaches_flush_level: + add w3, w1, w1, lsl #1 /* w3 = w1 * 3 (right-shift for cache type) */ + lsr w3, w0, w3 /* w3 = w0 >> w3 */ + ubfx w3, w3, #0, #3 /* w3 = cache type of this level */ + cmp w3, #2 /* No cache at this level? */ + b.lt invalidateCaches_next_level + + lsl w4, w1, #1 + msr CSSELR_EL1, x4 /* Select current cache level in CSSELR */ + isb /* ISB required to reflect new CSIDR */ + mrs x4, CCSIDR_EL1 /* w4 = CSIDR */ + + ubfx w3, w4, #0, #3 + add w3, w3, #2 /* w3 = log2(line size) */ + ubfx w5, w4, #13, #15 + ubfx w4, w4, #3, #10 /* w4 = Way number */ + clz w6, w4 /* w6 = 32 - log2(number of ways) */ + +invalidateCaches_flush_set: + mov w8, w4 /* w8 = Way number */ +invalidateCaches_flush_way: + lsl w7, w1, #1 /* Fill level field */ + lsl w9, w5, w3 + orr w7, w7, w9 /* Fill level field */ + lsl w9, w8, w6 + orr w7, w7, w9 /* Fill way field */ + dc CISW, x7 /* Invalidate by set/way to point of coherency */ + subs w8, w8, #1 /* Decrement way */ + b.ge invalidateCaches_flush_way + subs w5, w5, #1 /* Descrement set */ + b.ge invalidateCaches_flush_set + +invalidateCaches_next_level: + add w1, w1, #1 /* Next level */ + cmp w2, w1 + b.gt invalidateCaches_flush_level + +invalidateCaches_end: + ret + +.end diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/gic.c b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/gic.c new file mode 100644 index 0000000..d2c91d8 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/gic.c @@ -0,0 +1,128 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#include +#include "FreeRTOS.h" +#include "FreeRTOSConfig.h" +#include "gic.h" + +void vGIC_EnableCPUInterface( void ) +{ + /* Enable delivery of Group-1 IRQs to EL1 via ICC_IGRPEN1_EL1 */ + __asm volatile( + "msr ICC_IGRPEN1_EL1, %0\n" + "dsb sy\n" + "isb sy\n" + :: "r"(1UL) + : "memory" + ); +} + +void vGIC_PowerUpRedistributor( void ) +{ + volatile uint32_t *pulPwrr = ( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + + ( portPOINTER_SIZE_TYPE ) GICR_PWRR ); + + volatile uint32_t *pulWaker = ( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + + ( portPOINTER_SIZE_TYPE ) GICR_WAKER ); + + uint32_t ulTimeout = GIC_WAIT_TIMEOUT; + + /* Clear RDPD (Redistributor Power-Down) to 0 → power on the redistributor */ + *pulPwrr &= ~(1U << GICR_PWRR_RDPD_BIT); + /* Wait until RDPD reads 0 (powered up) */ + while( (*pulPwrr & (1U << GICR_PWRR_RDPD_BIT)) != 0U ) + { + if( --ulTimeout == 0U ) + { + configASSERT( "GICR_PWRR_RDPD_BIT did not clear in time" ); + return; + } + } + + /* Clear ProcessorSleep to 0 → wake hardware threads */ + *pulWaker &= ~(1U << GICR_WAKER_PS_BIT); + ulTimeout = GIC_WAIT_TIMEOUT; + /* Wait until ChildrenAsleep reads 0 (all subcomponents awake) */ + while( (*pulWaker & (1U << GICR_WAKER_CA_BIT)) != 0U ) + { + if( --ulTimeout == 0U ) + { + configASSERT( "GICR_WAKER_CA_BIT did not clear in time" ); + return; + } + } + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "isb sy" ::: "memory" ); +} + +void vGIC_InitDist( void ) +{ + /* Enable Group-1 Non-Secure (NS) and Secure (S) interrupts, and turn on Affinity-routing (ARE_S) + *plus Disable-Security (DS) for full GICv3 operation + */ + *( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICD_BASE + ( portPOINTER_SIZE_TYPE ) GICD_CTLR ) = ( 1 << GICD_CTLR_ENABLEGRP1NS_BIT ) | + ( 1 << GICD_CTLR_ENABLEGRP1S_BIT ) | + ( 1 << GICD_CTLR_ARES_BIT ) | + ( 1 << GICD_CTLR_DS_BIT ); + + /* Ensure distributor configuration is visible before continuing */ + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "isb sy" ::: "memory" ); +} + +void vGIC_SetPriority( uint32_t ulInterruptID, uint32_t ulPriority ) +{ + if( ( ulInterruptID <= GIC_MAX_INTERRUPT_ID ) && ( ulPriority <= 0xFF ) ) + { + /* Each GICR_IPRIORITYR holds 4 one‑byte priority fields. */ + uint32_t ulIndex = ulInterruptID / 4U; /* Register number */ + uint32_t ulShift = ( ulInterruptID % 4U ) * 8U; /* Byte lane offset */ + uint32_t ulMask = 0xFFUL << ulShift; /* Field mask */ + + volatile uint32_t * pulPriorityReg = ( volatile uint32_t * ) ( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + + ( portPOINTER_SIZE_TYPE ) GICR_IPRIORITYR( ulIndex ) ); + uint32_t ulRegValue = *pulPriorityReg; + ulRegValue &= ~( ulMask ); + ulRegValue |= ( ( uint32_t ) ulPriority << ulShift ); + *pulPriorityReg = ulRegValue; + /* Ensure priority write is observed */ + __asm volatile ( "dsb ish" ::: "memory" ); + } + else + { + configASSERT( "Invalid ulInterruptID or ulPriority passed to vGIC_SetPriority" ); + } +} + +void vGIC_EnableIRQ( uint32_t ulInterruptID ) +{ + if( ulInterruptID <= GIC_MAX_INTERRUPT_ID ) + { + uint32_t ulBitMask = 1U << ulInterruptID; + + /* 1. Assign the interrupt to group 1 */ + *( volatile uint32_t * )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + ( portPOINTER_SIZE_TYPE ) GICR_IGROUPR0 ) |= ( 1U << ulInterruptID ); + + /* 2. Enable the interrupt in GIC */ + *( volatile uint32_t* )( ( portPOINTER_SIZE_TYPE ) GICR_BASE_PER_CORE( ucPortGetCoreID() ) + ( portPOINTER_SIZE_TYPE ) GICR_ISENABLER0 ) |= ulBitMask; + + /* Ensure interrupt enable is visible */ + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "isb sy" ::: "memory" ); + } + else + { + configASSERT( "Invalid ulInterruptID for vGIC_EnableIRQ: must be < 32" ); + } +} + + +void vGIC_SetupSgi0( void ) +{ + vGIC_PowerUpRedistributor(); + vGIC_SetPriority( portYIELD_CORE_INT_ID, ( portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) ); + vGIC_EnableIRQ( portYIELD_CORE_INT_ID ); + vGIC_EnableCPUInterface(); +} diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S new file mode 100644 index 0000000..9f1078f --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S @@ -0,0 +1,164 @@ +/****************************************************************************** +* +* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* Copyright (C) 2014 - 2020 Xilinx, Inc. All rights reserved. +* Copyright 2025 Arm Limited and/or its affiliates +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of +* this software and associated documentation files (the "Software"), to deal in +* the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +* the Software, and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* http://www.FreeRTOS.org +* http://aws.amazon.com/freertos +* +* 1 tab == 4 spaces! +* +******************************************************************************/ + +.org 0 +.text + +/* Entry point symbol for startup; referenced by the linker */ +.globl _boot + +/* Base of the EL1 exception vector table (vbases for EL1t/EL1h) */ +.globl _vector_table + +/* Alternate vector table used once the scheduler is running */ +.globl _freertos_vector_table + +.org 0 + +/* Map this section to read-only, executable memory as the EL1 vector base */ +.section .vectors, "ax" + +_vector_table: + +.set VBAR, _vector_table + +.org VBAR + b _boot + +.org (VBAR + 0x80) + b . + +.org (VBAR + 0x100) + b . + +.org (VBAR + 0x180) + b . + + +.org (VBAR + 0x200) + b . + +.org (VBAR + 0x280) + b . + +.org (VBAR + 0x300) + b . + +.org (VBAR + 0x380) + b . + +.org (VBAR + 0x400) + b . + +.org (VBAR + 0x480) + b . + +.org (VBAR + 0x500) + b . + +.org (VBAR + 0x580) + b . + +.org (VBAR + 0x600) + b . + +.org (VBAR + 0x680) + b . + +.org (VBAR + 0x700) + b . + +.org (VBAR + 0x780) + b . + + + +/****************************************************************************** + * Vector table to use when FreeRTOS is running. + *****************************************************************************/ +/* Reserve 0x1000 bytes so we can switch VBAR at runtime without overlap */ +.set FREERTOS_VBAR, (VBAR+0x1000) + +.org(FREERTOS_VBAR) + +/* Map this section to read-only, executable memory as the EL1 vector base */ +.section .vectors, "ax" + +_freertos_vector_table: + b FreeRTOS_SWI_Handler + +.org (FREERTOS_VBAR + 0x80) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x100) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x180) + b . + +.org (FREERTOS_VBAR + 0x200) + b FreeRTOS_SWI_Handler + +.org (FREERTOS_VBAR + 0x280) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x300) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x380) + b . + +.org (FREERTOS_VBAR + 0x400) + b App_Fault_Handler + +.org (FREERTOS_VBAR + 0x480) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x500) + b . + +.org (FREERTOS_VBAR + 0x580) + b . + +.org (FREERTOS_VBAR + 0x600) + b . + +.org (FREERTOS_VBAR + 0x680) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x700) + b . + +.org (FREERTOS_VBAR + 0x780) + b . + +.org (FREERTOS_VBAR + 0x800) + +.end diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/startup.S b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/startup.S new file mode 100644 index 0000000..5a254c4 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/BSP/Source/startup.S @@ -0,0 +1,140 @@ +/****************************************************************************** +* Copyright (C) 2014 - 2020 Xilinx, Inc. All rights reserved. +* Copyright 2025 Arm Limited and/or its affiliates +* SPDX-License-Identifier: MIT +******************************************************************************/ + +#if defined(__ARMCC_VERSION) + .set __bss_start__, ( Image$$ER_IRAM_BSS_UNPRIVILEGED$$Base ) + .set __bss_end__, ( Image$$ARM_LIB_STACK$$Base - 1 ) + .set __data_start__, ( Image$$ER_IRAM_PRIVILEGED$$Base ) + .set __data_end__, ( Image$$ER_IRAM_BSS_UNPRIVILEGED$$Base - 1 ) + .set _sidata, ( Load$$ER_IRAM_PRIVILEGED$$Base ) +#endif + +#include "FreeRTOSConfig.h" +#include "portmacro.h" + .file "startup.S" + .align 2 + +.extern _vector_table +.extern ucSecondaryCoresReadyFlags +.extern ucPrimaryCoreInitDoneFlag +.extern _freertos_vector_table +.extern vSetupMPU + +.set freertos_vector_base, _freertos_vector_table + + .text + +/* Store the addresses of .bss start/end so they can be cleared if needed */ +.Lbss_start: + .quad __bss_start__ + +.Lbss_end: + .quad __bss_end__ + +/* + * _startup: early runtime init + * - For CORE0: copy .data, clear .bss + * - For all cores: hand off to C code + */ + .globl _startup +_startup: + +/* Only core0 initializes RAM image; others skip to c_init_end */ +#if configNUMBER_OF_CORES > 1 + /* Get CPU Id */ + mrs x0, MPIDR_EL1 + and x0, x0, #0xFF + cmp x0, #0 + bne c_init_end +#endif + + /* Copy .data section from ROM to RAM */ + ldr x0, =_sidata /* Source: ROM load address */ + ldr x1, =__data_start__ /* Destination: RAM */ + ldr x2, =__data_end__ /* End of destination */ + +/* Copy initialised data in 8-byte words for performance/alignment */ +1: cmp x1, x2 + b.ge .Lenclbss /* Done copying */ + ldr x3, [x0], #8 /* Load 64-bit word from ROM */ + str x3, [x1], #8 /* Store to RAM */ + b 1b + +/* Zero `.bss` in 8-byte words; efficient bulk clear of uninitialised data */ +.Lenclbss: + /* clear bss */ + ldr x1,.Lbss_start /* calculate beginning of the BSS */ + ldr x2,.Lbss_end /* calculate end of the BSS */ + +.Lloop_bss: + mov x0, #0 + cmp x1,x2 + bge libc_init /* If no BSS, no clearing required */ + str x0, [x1], #8 + b .Lloop_bss + +libc_init: +#if defined( __GNUC__ ) && !defined( __clang__ ) + /* Creates the _reent structure that holds errno, + * the three static FILE objects (stdin, stdout, stderr). + * Runs any functions placed in .preinit_array and calls _init(). + */ + bl __libc_init_array + /* Executes three semihosting SYS_OPEN SWIs to open the host's + * special file :tt three times, obtaining descriptors 0, 1 and 2 + * and writes those into stdin->_file, stdout->_file, stderr->_file. + * It also sets the __SWR (write-enabled) flag in each stream. + */ + bl initialise_monitor_handles +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ + +c_init_end: +#if configNUMBER_OF_CORES > 1 + /* Get CPU Id */ + mrs x0, MPIDR_EL1 + and x0, x0, #0xFF + cbnz x0, secondary_cores_hold +#endif + b jump_to_main + +#if configNUMBER_OF_CORES > 1 +secondary_cores_hold: + ldr x0, =ucPrimaryCoreInitDoneFlag + ldrb w1, [x0] /* Has the primary core set the flag? */ + cmp w1, #1 + b.eq make_secondary_cores_ready /* One? → Secondary cores released */ + wfe + b secondary_cores_hold /* Re-test the flag */ + +make_secondary_cores_ready: + ldr X1, = freertos_vector_base + msr VBAR_EL1, X1 + DSB SY + ISB SY + bl vGIC_SetupSgi0 + bl vSetupMPU + ldr x0, =ucSecondaryCoresReadyFlags + mrs x1, MPIDR_EL1 + and x1, x1, #0xFF + sub x1, x1, #1 /* Core 1 is index 0 in the array */ + add x0, x0, x1 + mov w1, #1 + strb w1, [x0] /* Mark this core as ready */ + bl vEnableMPU + SVC # portSVC_START_FIRST_TASK /* Start the first task on the secondary core */ +#endif + +jump_to_main: + ldr X1, = freertos_vector_base + msr VBAR_EL1, X1 /* Set VBAR_EL1 to Freertos vector table */ + DSB SY + ISB SY + mov x0, #0 + mov x1, #0 + bl main + +.Lexit: + b .Lexit diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/CMakeLists.txt b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/CMakeLists.txt new file mode 100644 index 0000000..c21b66c --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/CMakeLists.txt @@ -0,0 +1,64 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.15) + +project( + CORTEX-R82-SMP-EXTENDED-MPU + VERSION 0.1 + LANGUAGES C ASM) + +set (CMAKE_BUILD_TYPE Release) + +set(CMAKE_EXECUTABLE_SUFFIX ".axf") + +get_filename_component(FREERTOS_DIR_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../.. REALPATH) +message(DEBUG "FREERTOS_DIR_PATH is ${FREERTOS_DIR_PATH}") + +set(KERNEL_DIR_PATH ${FREERTOS_DIR_PATH}/Source) +message(DEBUG "KERNEL_DIR_PATH is ${KERNEL_DIR_PATH}") + +# Select the native compile PORT +if("${CMAKE_C_COMPILER_ID}" STREQUAL "ARMClang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + set(FREERTOS_PORT "GCC_ARM_CR82" CACHE STRING "" FORCE) +else() + message(FATAL_ERROR "Unsupported compiler: "${CMAKE_C_COMPILER_ID}"") +endif() + +set(FREERTOS_HEAP "4" CACHE STRING "" FORCE) + +add_library(freertos_config INTERFACE) + +target_include_directories(freertos_config SYSTEM + INTERFACE + config +) + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/BSP BSP) + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../../../Source freertos_kernel) + +add_executable(cortex_r82_smp_extended_mpu_fvp_example) + +target_sources(cortex_r82_smp_extended_mpu_fvp_example + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/crt_replacements.c +) + +target_include_directories(cortex_r82_smp_extended_mpu_fvp_example + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_options(cortex_r82_smp_extended_mpu_fvp_example + PRIVATE + $<$:--map $<$:--list=cortex_r82_smp_extended_mpu_fvp_example.map> --scatter=${CMAKE_CURRENT_SOURCE_DIR}/armclang_linker_script.sct> + $<$:-T${CMAKE_CURRENT_SOURCE_DIR}/gnu_linker_script.ld -Wl,--gc-sections,-Map=cortex_r82_smp_extended_mpu_fvp_example.map> +) + +target_link_libraries(cortex_r82_smp_extended_mpu_fvp_example + PRIVATE + bsp +) diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/README.md b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/README.md new file mode 100644 index 0000000..ed87615 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/README.md @@ -0,0 +1,132 @@ +# Arm Cortex‑R82 FreeRTOS SMP Extended MPU Demo (FVP_BaseR_AEMv8R) + +**Overview** + +This demo brings up FreeRTOS‑SMP on Arm Cortex‑R82 `FVP_BaseR_AEMv8R` and focuses on Memory Protection Unit (MPU) configuration and privilege separation between kernel/privileged code and unprivileged tasks. + +The example creates three tasks that intentionally exercise MPU protection violations: + +- First Task: Attempts to elevate its privilege by modifying its own TCB (in privileged RAM) and triggers an MPU fault. +- Second Task: Successfully accesses its own unprivileged stack, then attempts to write into another task’s stack and triggers an MPU fault. +- Third Task: Attempts to write to read‑only unprivileged flash and triggers an MPU fault. + +All MPU violations are handled gracefully: the exception handler redirects to a recovery function that prints a message and deletes the offending task, allowing the system to continue running. + +# Prerequisites + +## Downloading and installing AEMv8R Architecture Envelope Model (AEM) Fixed Virtual Platform + +Follow the instructions on the [page](https://developer.arm.com/Tools%20and%20Software/Fixed%20Virtual%20Platforms/Arm%20Architecture%20FVPs) to download `FVP_Base_AEMv8R` based on your operating system. + +Ensure that requirements mentioned in the [page](https://developer.arm.com/documentation/100966/1126/Getting-Started-with-Fixed-Virtual-Platforms/Requirements-for-FVPs?lang=en) are met. + +Then, add the path to `FVP_BaseR_AEMv8R` executable to the environment variable `PATH` (the executable path would be something like `/home//AEMv8R_base_pkg/models/64__GCC-9.3/`). + +Execute the following command to ensure that the FVP was installed successfully +```bash +FVP_BaseR_AEMv8R --version + +Fast Models [11.xx.yy (MMM DD YYYY)] +Copyright 2000-2025 ARM Limited. +All Rights Reserved. +``` + +## Build tools + +* [CMake](https://cmake.org/download/) + * The Arm Cortex-R82 SMP Extended MPU example uses `CMake` as the build system. +* [Arm GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) or [Arm Compiler for Embedded](https://developer.arm.com/Tools%20and%20Software/Arm%20Compiler%20for%20Embedded) + * To use Arm Compiler For Embedded (ArmClang), login is required for the download, and you will need a license in order to run the toolchain once installed. + +# Supported toolchains + +The example is supported and tested on the following toolchains: + + * Arm Compiler for Embedded v6.23 (armclang). + * Arm GNU toolchain v14.2. + +# Supported FVPs + +The example is supported and tested on FVP_BaseR_AEMv8R Fast Models [11.28.23 (Feb 17 2025)] + +## Cache Coherency + +This example relies on the `FVP_BaseR_AEMv8R` implementing fully coherent caches. If run on hardware or models without full cache coherency, additional cache maintenance or non‑cacheable mappings would be required for correctness. + +## What It Demonstrates + +- Privilege separation between kernel privileged code and unprivileged tasks using the Armv8-R‑Profile MPU. +- Enforced read‑only flash regions for both privileged and unprivileged code, with a separate region for FreeRTOS system calls. +- Privileged‑only RAM for kernel data (e.g., TCBs) versus unprivileged user task stacks. + +## How It Works + +- MPU setup happens in `Source/portable/GCC/ARM_CR82/port.c` during `vSetupMPU()`, using linker‑defined region boundaries from `gnu_linker_script.ld` (GNU) or `armclang_linker_script.sct` (ArmClang). +- Regions configured include: + - `__privileged_functions_*__`: privileged flash, read‑only to privileged code. + - `__syscalls_flash_*__`: unprivileged‑accessible flash containing FreeRTOS system calls. + - `__unprivileged_flash_*__`: unprivileged flash, read‑only by all. + - `__privileged_sram_*__`: RAM reserved for kernel data, privileged‑only access. +- The exception path (`App_Fault_Handler`) detects a data abort from a lower exception level and redirects to `vRecoverFromException()`, which prints a message and issues an SVC to call `vTaskDelete()` for the offending task. + +Important: Any user‑defined MPU region must be aligned to 64 bytes and shouldn't be less than 64 bytes in size. This demo explicitly aligns unprivileged task stacks and the shared queue handle array to 64 bytes to satisfy the MPU granularity and alignment requirements. + +## Building and Running + +First, run the following command to clone FreeRTOS repository: + +```bash +git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules +``` + +Run the following commands to build the example: + +```bash +cd FreeRTOS/FreeRTOS/Demo/ThirdParty/Partner-Supported-Demos/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG +rm -rf build && cmake -B build --toolchain=_toolchain.cmake . && cmake --build build +``` + +### Running + +Execute the following script to run the example: +```bash +./run.sh +``` + +### Expected Output (Excerpt) + +Your console will show FVP startup messages followed by task activity and recovery messages similar to: + +``` +Starting MPU demo with exception recovery. + +[Core: x] Second Task: Attempting to access its own unprivileged stack. +[Core: y] First Task: Attempting to raise its privilege level by accessing its privileged data. +[Core: x] Third Task: Attempting to write to a read-only region. +[Core: z] Second Task: Successfully accessed its own unprivileged stack. +[Core: x] First Task: Recovered from the data abortion exception gracefully. +[Core: y] Third Task: Recovered from the data abortion exception gracefully. +[Core: x] Second Task: Attempting to access first task unprivileged stack. +[Core: z] Second Task: Recovered from the data abortion exception gracefully. +``` + +Each offending task is deleted after recovery while the system remains alive. + +## Configuration — Up To 4 Cores + +FreeRTOS is built for SMP and the FVP model can start 1–4 Cortex‑R82 cores. Keep firmware and model in sync: + +- `config/FreeRTOSConfig.h`: set `#define configNUMBER_OF_CORES ` +- `fvp_config.txt`: set `cluster0.NUM_CORES=` + +Both values must match. Rebuild and run as usual. + +## Notes on MPU Regions + +- Minimum MPU granularity and alignment: 64 bytes. Ensure any user‑defined region base and size are 64‑byte aligned. This demo aligns unprivileged stacks and the shared handle array accordingly. + +- Keep user regions minimal. Only map the specific buffers that must be shared between unprivileged tasks. + +## License + +This example is released under the **MIT License**. diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/armclang_linker_script.sct b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/armclang_linker_script.sct new file mode 100644 index 0000000..350d2a0 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/armclang_linker_script.sct @@ -0,0 +1,92 @@ +#! armclang --target=aarch64-arm-none-eabi -march=armv8-r -E -x c +/* + * Copyright (c) 2021-2025 Arm Limited. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define __ROM_START ( 0x80000000 ) +#define __RAM_START ( 0x00000000 ) +#define __STACK_SIZE ( 0x00010000 ) +#define __HEAP_SIZE ( 0x00020000 ) + +;=============================================================================== +; LOAD REGION: on-board flash 0x80000000 (4 MB) +;=============================================================================== +LOAD_REGION __ROM_START +{ + ;-- Code + RO data --------------------------------------------------------- + ER_ROM_CODE __ROM_START ALIGN 64 + { + *.o (.vectors +First) + *(privileged_functions) + } + + ER_IROM_PRIVILEGED_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ER_IROM_FREERTOS_SYSTEM_CALLS +0 ALIGN 64 + { + *(freertos_system_calls) + } + + ER_IROM_FREERTOS_SYSTEM_CALLS_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ER_IROM_UNPRIVILEGED +0 ALIGN 64 + { + *(+RO) + } + + ER_IROM_UNPRIVILEGED_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ER_IRAM_PRIVILEGED __RAM_START ALIGN 64 + { + *(privileged_data) + } + + ER_IRAM_PRIVILEGED_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ER_IRAM_DATA_UNPRIVILEGED +0 ALIGN 64 + { + *(.data*) + *(+RW) + } + + ER_IRAM_DATA_UNPRIVILEGED_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ER_IRAM_BSS_UNPRIVILEGED +0 ALIGN 64 + { + *(+ZI) + } + + ER_IRAM_BSS_UNPRIVILEGED_ALIGN +0 ALIGN 64 EMPTY 0x0 + { + } + + ;-- Stack ------------------------------------------------------------------ + ARM_LIB_STACK +0 ALIGN 16 EMPTY (__STACK_SIZE) { + } + + ;-- Heap ------------------------------------------------------------------- + ARM_LIB_HEAP +0 ALIGN 16 EMPTY (__HEAP_SIZE) { + } +} diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/armclang_toolchain.cmake b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/armclang_toolchain.cmake new file mode 100644 index 0000000..7a1959d --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/armclang_toolchain.cmake @@ -0,0 +1,23 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_ARCH armv8-r) + +# Use the ARM Compiler 6 front-end +set(CMAKE_C_COMPILER armclang) +set(CMAKE_CXX_COMPILER armclang++) +set(CMAKE_ASM_COMPILER armclang) + +set(CMAKE_C_STANDARD 11) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# Common flags for compilation +# -Wno-pointer-to-int-cast and -Wno-int-to-pointer-cast are added to suppress warnings in FreeRTOS MPU code (i.e., mpu_wrappers_v2.c) not the Arm Cortex-R82 port code. +set(CMAKE_C_FLAGS "--target=aarch64-arm-none-eabi -mcpu=cortex-r82 -mlittle-endian -mstrict-align -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast" ) +set(CMAKE_ASM_FLAGS "--target=aarch64-arm-none-eabi -mcpu=cortex-r82 -mstrict-align" ) + +set(CMAKE_EXE_LINKER_FLAGS_INIT "--entry=_boot" ) +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_INIT}") diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h new file mode 100644 index 0000000..859b4d2 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h @@ -0,0 +1,229 @@ +/* + * FreeRTOS V202212.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2024-2025 Arm Limited and/or its affiliates + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/****************************************************************************** +* See http://www.freertos.org/a00110.html for an explanation of the +* definitions contained in this file. +******************************************************************************/ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- +* Application specific definitions. +* +* These definitions should be adjusted for your particular hardware and +* application requirements. +* +* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE +* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. +* https://www.FreeRTOS.org/a00110.html +*----------------------------------------------------------*/ + +/* Ensure definitions are only used by the compiler, and not by the assembler. */ +#if !defined(__ASSEMBLER__) + #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) + extern uint32_t SystemCoreClock; + void vAssertCalled( const char * pcFile, unsigned long ulLine ); + #endif +#endif + +/* See https://freertos.org/a00110.html#configPROTECTED_KERNEL_OBJECT_POOL_SIZE for details. */ +#define configPROTECTED_KERNEL_OBJECT_POOL_SIZE 150 +/* See https://freertos.org/a00110.html#configSYSTEM_CALL_STACK_SIZE for details. */ +#define configSYSTEM_CALL_STACK_SIZE 128 + +/* Cortex M33 port configuration. */ +#define configENABLE_MPU 1 +#define configTOTAL_MPU_REGIONS 16 +#define configENABLE_FPU 1 +#define configUSE_TASK_FPU_SUPPORT 2 +#define configENABLE_TRUSTZONE 0 +#define configENABLE_MVE 0 + +/* Run FreeRTOS on the secure side and never jump to the non-secure side. */ +#define configRUN_FREERTOS_SECURE_ONLY 0 + +/* Constants related to the behaviour or the scheduler. */ +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#define configUSE_PREEMPTION 1 +#define configUSE_TIME_SLICING 1 +#define configMAX_PRIORITIES ( 10 ) +#define configIDLE_SHOULD_YIELD 1 +#define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_64_BITS + +/* Constants that describe the hardware and memory usage. */ +#define configCPU_CLOCK_HZ SystemCoreClock +#define configMINIMAL_STACK_SIZE ( ( uint16_t ) 512 ) +#define configMAX_TASK_NAME_LEN ( 12 ) +#define configTOTAL_HEAP_SIZE ( 0x20000 ) +#define configAPPLICATION_ALLOCATED_HEAP 0 + +/* Constants that build features in or out. */ +#define configUSE_MUTEXES 1 +#define configUSE_TICKLESS_IDLE 0 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_NEWLIB_REENTRANT 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_QUEUE_SETS 0 +#define configUSE_TASK_NOTIFICATIONS 1 +#define configUSE_TRACE_FACILITY 1 +#define configNUM_TX_DESCRIPTORS 15 +#define configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN 2 + +/* Constants that define which hook (callback) functions should be used. */ +#define configUSE_IDLE_HOOK 1 +#define configUSE_TICK_HOOK 1 +#define configUSE_MALLOC_FAILED_HOOK 1 + +/* Constants provided for debugging and optimisation assistance. */ +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ ); +#define configQUEUE_REGISTRY_SIZE 20 + +/* Software timer definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 20 +#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) + +/* Set the following definitions to 1 to include the API function, or zero + * to exclude the API function. NOTE: Setting an INCLUDE_ parameter to 0 is + * only necessary if the linker does not automatically remove functions that are + * not referenced anyway. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 0 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_uxTaskGetStackHighWaterMark2 1 +#define INCLUDE_xTaskGetIdleTaskHandle 0 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xSemaphoreGetMutexHolder 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTimerGetTimerDaemonTaskHandle 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskAbortDelay 1 + +/* This demo makes use of one or more example stats formatting functions. These + * format the raw data provided by the uxTaskGetSystemState() function in to + * human readable ASCII form. See the notes in the implementation of vTaskList() + * within FreeRTOS/Source/tasks.c for limitations. */ +#define configUSE_STATS_FORMATTING_FUNCTIONS 1 + +/* Dimensions a buffer that can be used by the FreeRTOS+CLI command interpreter. + * See the FreeRTOS+CLI documentation for more information: + * https://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/ */ +#define configCOMMAND_INT_MAX_OUTPUT_SIZE 2048 + +/* Interrupt priority configuration follows...................... */ + +/* Interrupt priorities used by the kernel port layer itself. These are generic +* to all Cortex-M ports, and do not rely on any particular library functions. */ +#define configKERNEL_INTERRUPT_PRIORITY ( configUNIQUE_INTERRUPT_PRIORITIES - 1 ) + +/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! + * See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ +#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configMAX_API_CALL_INTERRUPT_PRIORITY ) + +/* Constants related to the generation of run time stats. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() 0 + +/* Adjust configTICK_RATE_HZ and pdMS_TO_TICKS to simulate a tick per ms on a fast model */ +#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) +#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( xTimeInMs * 10 ) ) + +/* Enable dynamic allocation. */ +#define configSUPPORT_DYNAMIC_ALLOCATION 1 + +/* Hardware specific configurations. */ +#define configFPU_D32 0 +#define configUNIQUE_INTERRUPT_PRIORITIES 32 +#define configINTERRUPT_CONTROLLER_BASE_ADDRESS 0xAF000000UL +#define configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET 0x1000 + +#define configNUMBER_OF_CORES 4 + +/* SMP specific configurations. */ +#if ( configNUMBER_OF_CORES > 1 ) + #define configUSE_CORE_AFFINITY 0 + #define configRUN_MULTIPLE_PRIORITIES 1 + #define configUSE_PASSIVE_IDLE_HOOK 0 + #define configTIMER_SERVICE_TASK_CORE_AFFINITY 0 +#endif + +#if !defined(__ASSEMBLER__) + #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) + /* + * The application must provide a function that configures a peripheral to + * create the FreeRTOS tick interrupt, then define configSETUP_TICK_INTERRUPT() + * in FreeRTOSConfig.h to call the function. FreeRTOS_Tick_Handler() must + * be installed as the peripheral's interrupt handler. + */ + void vConfigureTickInterrupt( void ); + #define configSETUP_TICK_INTERRUPT() vConfigureTickInterrupt() + #endif +#endif + +/* + * Interrupts that are assigned a priority at or below + * configMAX_API_CALL_INTERRUPT_PRIORITY (which counter-intuitively in the ARM + * generic interrupt controller [GIC] means a priority that has a numerical + * value above configMAX_API_CALL_INTERRUPT_PRIORITY) can call FreeRTOS safe API + * functions and will nest. + * + * Interrupts that are assigned a priority above + * configMAX_API_CALL_INTERRUPT_PRIORITY (which in the GIC means a numerical + * value below configMAX_API_CALL_INTERRUPT_PRIORITY) cannot call any FreeRTOS + * API functions, will nest, and will not be masked by FreeRTOS critical + * sections (although it is necessary for interrupts to be globally disabled + * extremely briefly as the interrupt mask is updated in the GIC). + * + * FreeRTOS functions that can be called from an interrupt are those that end in + * "FromISR". FreeRTOS maintains a separate interrupt safe API to enable + * interrupt entry to be shorter, faster, simpler and smaller. + * + * For the purpose of setting configMAX_API_CALL_INTERRUPT_PRIORITY 255 + * represents the lowest priority. + */ +#define configMAX_API_CALL_INTERRUPT_PRIORITY 18ULL + +#ifndef __ASSEMBLER__ + void vClearTickInterrupt( void ); + #define configCLEAR_TICK_INTERRUPT() vClearTickInterrupt() +#endif /* __ASSEMBLER__ */ + +#endif /* FREERTOS_CONFIG_H */ diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/crt_replacements.c b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/crt_replacements.c new file mode 100644 index 0000000..d43aa92 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/crt_replacements.c @@ -0,0 +1,136 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#if defined( __GNUC__ ) && !defined( __clang__ ) + #include + #include +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ + +int fputc( int character, FILE *pxFile ) +{ + ( void )pxFile; // Unused parameter as required by the C standard + + register uint64_t ulSysWriteCode __asm__( "x0" ) = 0x03; // SYS_WRITEC: semihosting write character command + register const char *pcCharAddress __asm__( "x1" ) = ( const char * )&character; // Address of character to send + __asm volatile ( + "hlt #0xF000\n" // Issue semihosting call using HLT instruction + : "+r"( ulSysWriteCode ) + : "r"( pcCharAddress ) + : "memory" + ); + return character; +} + +int puts( const char *pcString ) +{ + const char *pcChar = pcString; + while ( *pcChar ) { + fputc( *pcChar++, stdout ); + } + fputc( '\n', stdout ); // Append newline to the output string + return 0; // Standard puts() returns non-negative on success +} + +#if defined( __GNUC__ ) && !defined( __clang__ ) + /* Non-re-entrant version provided by RDIMON. */ + extern int _fstat ( int /*fd*/, struct stat * /*buf*/ ); + + /* ------------------------------------------------------------------------- */ + int _fstat_r ( struct _reent *pxReent, int fd, struct stat *pxStatBuffer ) + { + // Create a 16-byte aligned temporary buffer for the stat structure needed by semihosting. + struct stat xAlignedStat __attribute__( ( aligned ( 16 ) ) ); + + int iFstatResult = _fstat( fd, &xAlignedStat ); // Perform semihosting call to get file status + + if ( iFstatResult == 0 ) { // Success: copy stat data safely + memcpy( pxStatBuffer, &xAlignedStat, sizeof( struct stat ) ); + } + else { // Failure: set error code from errno + pxReent->_errno = errno; + } + return iFstatResult; + } + + /* Helper: Returns true if the provided pointer is naturally 8-byte aligned. */ + static inline int prvIsEightByteAligned( const void *pvAddr ) + { + return ( ( ( uintptr_t )pvAddr ) & 0x7U ) == 0U; // Check alignment to an 8-byte boundary + } + + void *memmove( void *pvDestination, const void *pvSource, size_t xCount ) + { + unsigned char *pucDest = ( unsigned char * )pvDestination; + const unsigned char *pucSrc = ( const unsigned char * )pvSource; + + if ( pucDest == pucSrc || xCount == 0 ) + return pvDestination; + + if ( pucDest < pucSrc ) { // -------- forward copy -------- + if ( prvIsEightByteAligned( pucDest ) && prvIsEightByteAligned( pucSrc ) ) { + while ( xCount >= 8 ) { + *( uint64_t * )pucDest = *( const uint64_t * )pucSrc; + pucDest += 8; + pucSrc += 8; + xCount -= 8; + } + } + while ( xCount-- ) { // Copy remaining bytes forward + *pucDest++ = *pucSrc++; + } + } else { // -------- backward copy -------- + pucDest += xCount; + pucSrc += xCount; + if ( prvIsEightByteAligned( pucDest ) && prvIsEightByteAligned( pucSrc ) ) { + while ( xCount >= 8 ) { + pucDest -= 8; + pucSrc -= 8; + xCount -= 8; + *( uint64_t * )pucDest = *( const uint64_t * )pucSrc; + } + } + while ( xCount-- ) { // Copy remaining bytes in reverse order + *--pucDest = *--pucSrc; + } + } + return pvDestination; + } + + /* memcpy() may assume no overlap, so alias to memmove() */ + void *memcpy( void *pvDestination, const void *pvSource, size_t xCount ) + { + return memmove( pvDestination, pvSource, xCount ); + } + + /* Replacement for memset – linker will rename all calls to this symbol. */ + void *__wrap_memset( void *pvDestination, int value, size_t xCount ) + { + unsigned char *pucDest = ( unsigned char * )pvDestination; + unsigned char ucValue = ( unsigned char )value; + volatile size_t xCountNumber = xCount; + + /* Fast 8-byte stores when destination is naturally aligned. */ + if ( prvIsEightByteAligned( pucDest ) ) + { + uint64_t ullEightBytePattern = 0x0101010101010101ULL * ucValue; // Prepare an 8-byte pattern with all bytes set to ucValue + + while ( xCountNumber >= 8 ) + { + *( uint64_t * )pucDest = ullEightBytePattern; + pucDest += 8; + xCountNumber -= 8; + } + } + + /* Copy any remaining bytes (or if fully unaligned). */ + while ( xCountNumber-- > 0 ) + *pucDest++ = ucValue; + + return pvDestination; + } +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/fvp_config.txt b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/fvp_config.txt new file mode 100644 index 0000000..5e6bcba --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/fvp_config.txt @@ -0,0 +1,15 @@ +cluster0.has_aarch64=1 +cluster0.VMSA_supported=0 +cluster0.NUM_CORES=4 +cluster0.gicv3.SRE-enable-action-on-mmap=2 +cluster0.gicv3.cpuintf-mmap-access-level=2 +cluster0.gicv3.extended-interrupt-range-support=1 +cluster0.has_pl2=0 +cluster0.gicv3.SRE-EL2-enable-RAO=1 +gic_distributor.GICD_CTLR-DS-1-means-secure-only=1 +gic_distributor.has-two-security-states=0 +bp.refcounter.non_arch_start_at_default=1 +bp.vis.disable_visualisation=1 +bp.vis.rate_limit-enable=0 +cache_state_modelled=1 +semihosting-enable=1 diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/gnu_linker_script.ld b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/gnu_linker_script.ld new file mode 100644 index 0000000..61d3af0 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/gnu_linker_script.ld @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021-2025 Arm Limited. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Memory regions */ +MEMORY +{ + ROM (rwx) : ORIGIN = 0x80000000, LENGTH = 4M /* System ROM */ + RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 256M /* System RAM */ +} + +/* Sections */ +SECTIONS +{ + . = ORIGIN(ROM); /* Place text at ROM base (0x8000_0000) */ + + /* Code section */ + . = ALIGN(64); + .privileged_functions : ALIGN(64) + { + __privileged_functions_start__ = .; + KEEP(*(.vectors)) /* Vector table */ + KEEP(*(.init)) + KEEP(*(.fini)) + *(privileged_functions) + . = ALIGN(64); + __privileged_functions_end__ = .; + } > ROM + + .syscalls_flash : ALIGN(64) + { + __syscalls_flash_start__ = .; + *(freertos_system_calls) + . = ALIGN(64); + __syscalls_flash_end__ = .; + } > ROM + + .unprivileged_flash : ALIGN(64) + { + __unprivileged_flash_start__ = .; + *(.text*) /* Code */ + *(.rodata*) /* Read-only data */ + . = ALIGN(64); + __unprivileged_flash_end__ = .; + } > ROM + + .privileged_sram : + { + __privileged_sram_start__ = .; + __data_start__ = .; + *(privileged_data) + . = ALIGN(64); + __privileged_sram_end__ = .; + } > RAM AT> ROM /* load in ROM, run in RAM */ + _sidata = LOADADDR(.privileged_sram); + + .unprivileged_sram : + { + __unprivileged_sram_start__ = .; + *(.data*) + *(+RW) + . = ALIGN(64); + __unprivileged_sram_end__ = .; + __data_end__ = .; + } > RAM AT> ROM /* load in ROM, run in RAM */ + + /* Uninitialized data (BSS) */ + .bss : + { + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(64); + __bss_end__ = .; + } > RAM + + PROVIDE(end = __bss_end__); /* Alias for `_end`, commonly used in `_sbrk()` */ + + /* Stack section */ + .stack (NOLOAD) : ALIGN(16) + { + . += 0x10000; /* 64KB stack */ + } > RAM + + /* Heap section (can be used for dynamic memory allocation) */ + .heap (NOLOAD) : + { + . += 0x20000; /* 128KB heap */ + } > RAM +} + +/* Provide symbols for startup code */ +PROVIDE(__el1_stack = ADDR(.stack)); +PROVIDE(_el1_stack_end = ADDR(.stack) + SIZEOF(.stack)); diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/gnu_toolchain.cmake b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/gnu_toolchain.cmake new file mode 100644 index 0000000..fd2831d --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/gnu_toolchain.cmake @@ -0,0 +1,23 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR cortex-r82) + +set(CMAKE_C_COMPILER "aarch64-none-elf-gcc") +set(CMAKE_CXX_COMPILER "aarch64-none-elf-g++") +set(CMAKE_ASM_COMPILER "aarch64-none-elf-gcc") + +set(CMAKE_C_STANDARD 11) + +# -Wno-return-type, -Wno-unused-parameter, -Wno-attributes are added as aarch64-none-elf-gcc does not support __attribute__((naked)) which results in warnings. +# -Wno-pointer-to-int-cast and -Wno-int-to-pointer-cast are added to suppress warnings in FreeRTOS MPU code (i.e., mpu_wrappers_v2.c) not the Arm Cortex-R82 port code. +set(CMAKE_C_FLAGS "-mcpu=cortex-r82 -mabi=lp64 -mlittle-endian -Wall -Wextra -mstrict-align -Wno-return-type -Wno-unused-parameter -Wno-attributes -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") +set(CMAKE_ASM_FLAGS "-mcpu=cortex-r82 -mabi=lp64 -Wall") + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +set(CMAKE_EXE_LINKER_FLAGS_INIT + "-specs=rdimon.specs -Wl,-e,_boot,--wrap=memset,--emit-relocs -lc -lrdimon") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_INIT}") diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/main.c b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/main.c new file mode 100644 index 0000000..aa8cd77 --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/main.c @@ -0,0 +1,559 @@ +/* Copyright 2023-2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "portmacro.h" + +/* GIC includes. */ +#include "gic.h" + +#define portINITIAL_PSTATE_EL0 ( 0x0ULL ) /* EL0, SP_EL0 */ +#define GENERIC_TIMER_IRQ ( 30UL ) /* Default IRQ for CNTP_EL0 */ +#define SGI0_IRQ ( 0UL ) /* SGI0 IRQ */ +#define GENERIC_TIMER_IRQ_PRIORITY ( portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) /* priority for CNTP_EL0 */ +#define GENERIC_TIMER_FREQ ( 24000000 ) /* Frequency for Generic Timer */ +#define TIMER_CTRL_ENABLE ( 1UL << 0 ) /* Timer ENABLE bit */ +#define TIMER_CTRL_IMASK ( 1UL << 1 ) /* Timer IMASK bit */ +#define LOG_QUEUE_LENGTH ( 100 ) /* Length of the logging queue */ +#define LOG_QUEUE_IDX ( 0 ) /* Index of the logging queue handle in the shared array */ +#define NUMBER_OF_SHARED_ELEMENTS ( 10 ) /* Number of shared elements in the shared array */ +#define FIRST_TASK_HANDLE_IDX ( 1 ) /* Index assigned to the first task handle */ +#define SECOND_TASK_HANDLE_IDX ( 2 ) /* Index assigned to the second task handle */ +#define THIRD_TASK_HANDLE_IDX ( 3 ) /* Index assigned to the third task handle */ + +typedef struct +{ + uint8_t ucText[128]; + uint8_t ucCore; +} LogMsg_t; + +#if defined(__ARMCC_VERSION) + /* Externs needed by the MPU setup code. These are defined in Scatter-Loading + * description file (armclang.sct). */ + extern uint64_t Image$$ER_ROM_CODE$$Base; + extern uint64_t Image$$ER_IROM_PRIVILEGED_ALIGN$$Limit; + extern uint64_t Image$$ER_IROM_FREERTOS_SYSTEM_CALLS$$Base; + extern uint64_t Image$$ER_IROM_FREERTOS_SYSTEM_CALLS_ALIGN$$Limit; + extern uint64_t Image$$ER_IROM_UNPRIVILEGED$$Base; + extern uint64_t Image$$ER_IROM_UNPRIVILEGED_ALIGN$$Limit; + + extern uint64_t Image$$ER_IRAM_PRIVILEGED$$Base; + extern uint64_t Image$$ER_IRAM_PRIVILEGED_ALIGN$$Limit; + extern uint64_t Image$$ER_IRAM_BSS_UNPRIVILEGED$$Base; + extern uint64_t Image$$ER_IRAM_BSS_UNPRIVILEGED_ALIGN$$Limit; + + /* Privileged flash. */ + const uint64_t * __privileged_functions_start__ = ( uint64_t * ) &( Image$$ER_ROM_CODE$$Base ); + const uint64_t * __privileged_functions_end__ = ( uint64_t * ) ( ( uint64_t ) &( Image$$ER_IROM_PRIVILEGED_ALIGN$$Limit ) - 0x1 ); /* Last address in privileged Flash region. */ + + /* Flash containing system calls. */ + const uint64_t * __syscalls_flash_start__ = ( uint64_t * ) &( Image$$ER_IROM_FREERTOS_SYSTEM_CALLS$$Base ); + const uint64_t * __syscalls_flash_end__ = ( uint64_t * ) ( ( uint64_t ) &( Image$$ER_IROM_FREERTOS_SYSTEM_CALLS_ALIGN$$Limit ) - 0x1 ); /* Last address in Flash region containing system calls. */ + + /* Unprivileged flash. Note that the section containing system calls is + * unprivileged so that unprivileged tasks can make system calls. */ + const uint64_t * __unprivileged_flash_start__ = ( uint64_t * ) &( Image$$ER_IROM_UNPRIVILEGED$$Base ); + const uint64_t * __unprivileged_flash_end__ = ( uint64_t * ) ( ( uint64_t ) &( Image$$ER_IROM_UNPRIVILEGED_ALIGN$$Limit ) - 0x1 ); /* Last address in un-privileged Flash region. */ + + /* RAM with priviledged access only. This contains kernel data. */ + const uint64_t * __privileged_sram_start__ = ( uint64_t * ) &( Image$$ER_IRAM_PRIVILEGED$$Base ); + const uint64_t * __privileged_sram_end__ = ( uint64_t * ) ( ( uint64_t ) &( Image$$ER_IRAM_PRIVILEGED_ALIGN$$Limit ) - 0x1 ); /* Last address in privileged RAM. */ +#else + /* When building with GCC, these symbols come from gnu_linker_script.ld. */ + extern uint64_t __privileged_flash_start__[]; + extern uint64_t __privileged_flash_end__[]; + extern uint64_t __syscalls_flash_start__[]; + extern uint64_t __syscalls_flash_end__[]; + extern uint64_t __unprivileged_flash_start__[]; + extern uint64_t __unprivileged_flash_end__[]; + extern uint64_t __privileged_sram_start__[]; + extern uint64_t __privileged_sram_end__[]; +#endif + +static TaskHandle_t xFirstTaskHandle = NULL; +static TaskHandle_t xSecondTaskHandle = NULL; +static TaskHandle_t xThirdTaskHandle = NULL; +static TaskHandle_t xLoggerTaskHandle = NULL; + +/* User defined task stack. */ +static StackType_t xFirstTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 64 ) ) ); +static StackType_t xSecondTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 64 ) ) ); +static StackType_t xThirdTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 64 ) ) ); + +/* User defined shared queue handles array. */ +static QueueHandle_t xQueueHandlesArray[ NUMBER_OF_SHARED_ELEMENTS ] __attribute__( ( aligned( 64 ) ) ); + +static uint64_t prvGetTimerClockHz ( void ); +static void prvSetTimerClockHz ( uint64_t ullPhysicalTimerFreq ); +extern void FreeRTOS_Tick_Handler( void ); +extern void FreeRTOS_SGI_Handler( void ); + +#if ( configNUMBER_OF_CORES == 1 ) + extern TaskHandle_t pxCurrentTCB; +#else + extern TaskHandle_t pxCurrentTCBs[ configNUMBER_OF_CORES ]; +#endif + +void vAssertCalled( const char * pcFile, + unsigned long ulLine ) +{ + printf( "ASSERT failed! file %s:%lu, \r\n", pcFile, ulLine ); + + taskENTER_CRITICAL(); + { + volatile unsigned long looping = 0; + + /* Use the debugger to set ul to a non-zero value in order to step out + * of this function to determine why it was called. */ + while( looping == 0LU ) + { + portNOP(); + } + } + taskEXIT_CRITICAL(); +} + +static void * prvTinyMemCpy( void * pvDst, const void * pvSrc, size_t xNumberOfElements ) +{ + uint8_t * pucDestination = ( uint8_t * ) pvDst; + const uint8_t * pucSource = ( const uint8_t * ) pvSrc; + while ( xNumberOfElements-- ) { + *pucDestination++ = *pucSource++; + } + return pucDestination; +} + +static size_t prvTinyStrnlen( const char * pcString, size_t xMaxLength ) +{ + size_t xLength = 0; + + if( pcString == NULL ) + { + return 0; + } + + while( ( xLength < xMaxLength ) && ( pcString[ xLength ] != '\0' ) ) + { + xLength++; + } + + return xLength; +} + +static void prvLogMsg( const char * pcMessage ) +{ + LogMsg_t xLogMsg; + + /* Leave room for the terminating NUL character. */ + size_t xMaxCopyLength = sizeof( xLogMsg.ucText ) - 1U; + size_t xMsgLength = prvTinyStrnlen( pcMessage, xMaxCopyLength ); + prvTinyMemCpy( xLogMsg.ucText, pcMessage, xMsgLength ); + xLogMsg.ucText[ xMsgLength ] = '\0'; + xLogMsg.ucCore = ucPortGetCoreID(); + + (void) xQueueSend( xQueueHandlesArray[ LOG_QUEUE_IDX ], &xLogMsg, portMAX_DELAY ); +} + +/* Privileged logger task: receives log messages and prints them. */ +static void prvLoggerTask( void * arg ) +{ + ( void ) arg; + LogMsg_t xLogMsg; + + while( 1) + { + if( xQueueReceive( xQueueHandlesArray[ LOG_QUEUE_IDX ], &xLogMsg, portMAX_DELAY ) == pdPASS ) + { + printf( "[Core: %d] %s\r\n", xLogMsg.ucCore, xLogMsg.ucText ); + } + } +} + +void vRecoverFromException( void ) +{ + TaskHandle_t xCurrentTaskHandle = MPU_xTaskGetCurrentTaskHandle(); + + if ( xCurrentTaskHandle == ( TaskHandle_t ) FIRST_TASK_HANDLE_IDX ) + { + prvLogMsg( "First Task: Recovered from the data abortion exception gracefully." ); + } + else if ( xCurrentTaskHandle == ( TaskHandle_t ) SECOND_TASK_HANDLE_IDX ) + { + prvLogMsg( "Second Task: Recovered from the data abortion exception gracefully." ); + } + else if ( xCurrentTaskHandle == ( TaskHandle_t ) THIRD_TASK_HANDLE_IDX ) + { + prvLogMsg( "Third Task: Recovered from the data abortion exception gracefully." ); + } + else + { + prvLogMsg( "Unknown Task: Recovered from the data abortion exception gracefully." ); + } + + /* Delete the offending task. */ + __asm__ volatile ( + "SVC %0 \n" /* SVC to vTaskDelete. */ + : + : "i" ( portSVC_DELETE_CURRENT_TASK ) + : "memory" + ); +} + +static void prvFirstTask( void * arg ) +{ + /* Prevent the compiler warning about the unused parameter. */ + ( void ) arg; + while( 1 ) + { + prvLogMsg( "First Task: Attempting to raise its privilege level by accessing its privileged data." ); + /* First task deliberately attempts to raise its privilege level by modifying its own TCB, + * which resides in privileged RAM. This should trigger an MPU violation exception where + * the exception will be recovered gracefully and the task will be deleted by + * vRecoverFromException function. + */ + #if ( configNUMBER_OF_CORES == 1 ) + xMPU_SETTINGS * pxMpuSettings = xTaskGetMPUSettings( pxCurrentTCB ); + #else + xMPU_SETTINGS * pxMpuSettings = xTaskGetMPUSettings( pxCurrentTCBs[ ucPortGetCoreID() ] ); + #endif + + pxMpuSettings->ullTaskFlags |= portTASK_IS_PRIVILEGED_FLAG; + } +} + +static void prvSecondTask( void * arg ) +{ + /* Prevent the compiler warning about the unused parameter. */ + ( void ) arg; + while( 1 ) + { + prvLogMsg( "Second Task: Attempting to access its own unprivileged stack." ); + /* Second task attempts to access its own stack, this should be successful + * as the second task has access to its own stack. + */ + xSecondTaskStack[0] = 0xDEADBEEF; + prvLogMsg( "Second Task: Successfully accessed its own unprivileged stack." ); + + prvLogMsg( "Second Task: Attempting to access first task unprivileged stack." ); + /* Second task attempts to access the stack of the first task, which + * it does not have access to. This should trigger an MPU violation exception where the + * exception will be recovered gracefully and the task will be + * deleted by vRecoverFromException function. + */ + xFirstTaskStack[0] = 0xDEADBEEF; + } +} + +static void prvThirdTask( void * arg ) +{ + /* Prevent the compiler warning about the unused parameter. */ + ( void ) arg; + while( 1 ) + { + prvLogMsg( "Third Task: Attempting to write to a read-only region." ); + /* Third task attempts to write to a read-only region in unprivileged flash. + * This should trigger an MPU violation exception where the exception + * will be recovered gracefully and the task will be deleted by + * vRecoverFromException function. + */ + * ( ( uint64_t * ) __unprivileged_flash_start__ ) = 0xDEADBEEF; + } +} + +int main() +{ + printf( "Starting MPU demo with exception recovery.\r\n\n" ); + + TaskParameters_t xFirstTaskParameters = + { + .pvTaskCode = prvFirstTask, + .pcName = NULL, + .usStackDepth = configMINIMAL_STACK_SIZE, + .pvParameters = NULL, + .uxPriority = ( tskIDLE_PRIORITY + 1 ), + .puxStackBuffer = xFirstTaskStack, + .xRegions = + { + /* Base address, size, parameters. */ + { xQueueHandlesArray, NUMBER_OF_SHARED_ELEMENTS * sizeof( QueueHandle_t ), tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER | tskMPU_REGION_INNER_SHAREABLE }, + } + }; + + if(xTaskCreateRestricted( &( xFirstTaskParameters ), &xFirstTaskHandle ) == pdFAIL) + { + return EXIT_FAILURE; + } + + TaskParameters_t xSecondTaskParameters = + { + .pvTaskCode = prvSecondTask, + .pcName = NULL, + .usStackDepth = configMINIMAL_STACK_SIZE, + .pvParameters = NULL, + .uxPriority = ( tskIDLE_PRIORITY + 1 ), + .puxStackBuffer = xSecondTaskStack, + .xRegions = + { + /* Base address, size, parameters. */ + { xQueueHandlesArray, NUMBER_OF_SHARED_ELEMENTS * sizeof( QueueHandle_t ), tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER | tskMPU_REGION_INNER_SHAREABLE }, + } + }; + + if(xTaskCreateRestricted( &( xSecondTaskParameters ), &xSecondTaskHandle ) == pdFAIL) + { + return EXIT_FAILURE; + } + + TaskParameters_t xThirdTaskParameters = + { + .pvTaskCode = prvThirdTask, + .pcName = NULL, + .usStackDepth = configMINIMAL_STACK_SIZE, + .pvParameters = NULL, + .uxPriority = ( tskIDLE_PRIORITY + 1 ), + .puxStackBuffer = xThirdTaskStack, + .xRegions = + { + /* Base address, size, parameters. */ + { xQueueHandlesArray, NUMBER_OF_SHARED_ELEMENTS * sizeof( QueueHandle_t ), tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER | tskMPU_REGION_INNER_SHAREABLE }, + } + }; + + if(xTaskCreateRestricted( &( xThirdTaskParameters ), &xThirdTaskHandle ) == pdFAIL) + { + return EXIT_FAILURE; + } + + if(xTaskCreate( prvLoggerTask, + NULL, + configMINIMAL_STACK_SIZE, + NULL, + ( ( tskIDLE_PRIORITY + 2 ) | portPRIVILEGE_BIT ), + &xLoggerTaskHandle ) == pdFAIL ) + { + return EXIT_FAILURE; + } + + /* Dedicated logging queue.. */ + xQueueHandlesArray[ LOG_QUEUE_IDX ] = xQueueCreate( LOG_QUEUE_LENGTH, sizeof( LogMsg_t ) ); + + if( xQueueHandlesArray[ LOG_QUEUE_IDX ] == NULL ) + { + return EXIT_FAILURE; + } + + vTaskStartScheduler(); + + /* If all is well, the scheduler will now be running, and the following + * line will never be reached. If the following line does execute, then + * there was insufficient FreeRTOS heap memory available for the idle and/or + * timer tasks to be created. See the memory management section on the + * FreeRTOS web site for more details. NOTE: This demo uses static allocation + * for the idle and timer tasks so this line should never execute. + */ + for( ; ; ) + { + } +} + +/** + * Dummy implementation of the callback function vApplicationStackOverflowHook(). + */ +#if ( configCHECK_FOR_STACK_OVERFLOW > 0 ) + void vApplicationStackOverflowHook( TaskHandle_t xTask, + char * pcTaskName ) + { + ( void ) xTask; + ( void ) pcTaskName; + + /* Assert when stack overflow is enabled but no application defined function exists */ + configASSERT( 0 ); + } +#endif + +/*---------------------------------------------------------------------------*/ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + +/* + * vApplicationGetIdleTaskMemory gets called when configSUPPORT_STATIC_ALLOCATION + * equals to 1 and is required for static memory allocation support. + */ + + __WEAK void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, + StackType_t ** ppxIdleTaskStackBuffer, + StackType_t * pulIdleTaskStackSize ) + { + /* Idle task control block and stack */ + static StaticTask_t Idle_TCB = { 0 }; + static StackType_t Idle_Stack[ configMINIMAL_STACK_SIZE ] = { 0 }; + + *ppxIdleTaskTCBBuffer = &Idle_TCB; + *ppxIdleTaskStackBuffer = &Idle_Stack[ 0 ]; + *pulIdleTaskStackSize = ( uint32_t ) configMINIMAL_STACK_SIZE; + } + +/* + * vApplicationGetTimerTaskMemory gets called when configSUPPORT_STATIC_ALLOCATION + * equals to 1 and is required for static memory allocation support. + */ + __WEAK void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer, + StackType_t ** ppxTimerTaskStackBuffer, + StackType_t * pulTimerTaskStackSize ) + { + /* Timer task control block and stack */ + static StaticTask_t Timer_TCB = { 0 }; + static StackType_t Timer_Stack[ configTIMER_TASK_STACK_DEPTH ] = { 0 }; + + *ppxTimerTaskTCBBuffer = &Timer_TCB; + *ppxTimerTaskStackBuffer = &Timer_Stack[ 0 ]; + *pulTimerTaskStackSize = ( uint32_t ) configTIMER_TASK_STACK_DEPTH; + } +#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + +void vApplicationTickHook( void ) +{ + /* Provide a stub for this function. */ +} + +void vApplicationIdleHook( void ) +{ + const TickType_t xKitHitCheckPeriod = pdMS_TO_TICKS( 1000UL ); + static TickType_t xTimeNow, xLastTimeCheck = 0; + + if( ( xTimeNow - xLastTimeCheck ) > xKitHitCheckPeriod ) + { + xLastTimeCheck = xTimeNow; + } + + /* Exit. Just a stub. */ +} + +void vApplicationMallocFailedHook( void ) +{ + /* Provide a stub for this function. */ +} + +void vConfigureTickInterrupt( void ) +{ + uint64_t ulTickInterval; + uint32_t ulControlRegister = 0U; + + __asm volatile ( "dsb sy" ::: "memory" ); + prvSetTimerClockHz( GENERIC_TIMER_FREQ ); + + /* Calculate tick interval based on configured FreeRTOS tick rate */ + ulTickInterval = ( prvGetTimerClockHz() / configTICK_RATE_HZ ); + + /* Disable CNTP timer before configuring */ + __asm volatile ( "msr cntp_ctl_el0, xzr" ); + + /* Set the timer interval (time until next interrupt) */ + __asm volatile ( "msr cntp_tval_el0, %0" :: "r" ( ulTickInterval ) ); + + /* Enable the timer interrupt in the GIC */ + vGIC_InitDist(); + vGIC_PowerUpRedistributor(); + vGIC_SetPriority( GENERIC_TIMER_IRQ, GENERIC_TIMER_IRQ_PRIORITY ); + vGIC_EnableIRQ( GENERIC_TIMER_IRQ ); + vGIC_EnableCPUInterface(); + + /* Enable the timer without masking interrupts */ + __asm volatile ( "msr cntp_ctl_el0, %0" :: "r" ( ulControlRegister | TIMER_CTRL_ENABLE ) ); + __asm volatile ( "isb sy" ::: "memory" ); +} + +static uint64_t prvGetTimerClockHz ( void ) +{ + uint64_t ullPhysicalTimerFreq; + __asm volatile ( "mrs %0, cntfrq_el0" : "=r" ( ullPhysicalTimerFreq ) ); + return ullPhysicalTimerFreq; +} + +static void prvSetTimerClockHz ( uint64_t ullPhysicalTimerFreq ) +{ + __asm volatile ( "msr cntfrq_el0, %0" :: "r" ( ullPhysicalTimerFreq ) ); +} + +void vApplicationIRQHandler( uint32_t ulICCIAR ) +{ + /* The ID of the interrupt is obtained by bitwise anding the ICCIAR value + with 0x3FF. */ + uint32_t ulInterruptID = ulICCIAR & 0x3FFUL; + if( ulInterruptID == GENERIC_TIMER_IRQ ) + { + FreeRTOS_Tick_Handler(); + } + #if ( configNUMBER_OF_CORES > 1 ) + else if( ulInterruptID == SGI0_IRQ ) + { + FreeRTOS_SGI_Handler(); + } + #endif + else + { + /* Handle other interrupts as needed. */ + printf( "Unhandled interrupt ID: %u\r\n", ulInterruptID ); + } +} + +void vClearTickInterrupt( void ) +{ + /* Disable CNTP timer interrupt before re-configuring */ + uint64_t ulControlRegister = 0U; + ulControlRegister |= TIMER_CTRL_IMASK; + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "msr cntp_ctl_el0, %0" :: "r" ( ulControlRegister ) ); + + /* Calculate tick interval based on configured FreeRTOS tick rate */ + uint64_t ulTickInterval = ( prvGetTimerClockHz() / configTICK_RATE_HZ ); + /* Set the timer interval (time until next interrupt) */ + __asm volatile ( "msr cntp_tval_el0, %0" :: "r" ( ulTickInterval ) ); + + /* Enable the timer without masking interrupts */ + ulControlRegister &= ~( TIMER_CTRL_IMASK ); + ulControlRegister |= TIMER_CTRL_ENABLE; + __asm volatile ( "msr cntp_ctl_el0, %0" :: "r" ( ulControlRegister ) ); + __asm volatile ( "isb sy" ::: "memory" ); +} + +void App_Fault_Handler( void ) +{ + /* This handler deals with data aborts caused by MPU violations. + * It is placed in the vector table in place of the SWI handler. + * The handler checks the exception class to ensure it is a data + * abort from a lower exception level, then adjusts the return + * address to skip the faulting instruction before returning + * from the exception. + */ + __asm volatile ( + " STP X0, X1, [SP, #-0x10]! \n" /* Save X0 and X1 temporarily as they are used in the handler. */ + " MRS X0, ESR_EL1 \n" + " LSR X1, X0, # 26 \n" /* Extract exception class. */ + " CMP X1, # 0x24 \n" /* 0x24 = Data abortion from low level. */ + " B.NE svc_handler \n" /* Indicate a svc. */ + " MSR SPSR_EL1, %0 \n" /* Set PSTATE to EL0, SP_EL0. */ + " adrp X0, vRecoverFromException \n" + " add X0, X0, :lo12:vRecoverFromException \n" /* X0 = &vRecoverFromException */ + " MSR ELR_EL1, X0 \n" + " LDP X0, X1, [SP], #0x10 \n" /* Restore X0 and X1. */ + " ERET \n" + " svc_handler: \n" /* Route SVC calls to FreeRTOS_SWI_Handler. */ + " CMP X1, # 0x15 \n" + " B.NE fault_loop \n" + " LDP X0, X1, [SP], #0x10 \n" /* Restore X0 and X1. */ + " B FreeRTOS_SWI_Handler \n" + " fault_loop: \n" + " B fault_loop \n" + : + : "r" ( portINITIAL_PSTATE_EL0 ) + : "memory", "x0", "x1" + ); +} diff --git a/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/run.sh b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/run.sh new file mode 100755 index 0000000..77f7c9e --- /dev/null +++ b/CORTEX_R82_SMP_EXTENDED_MPU_FVP_GCC_ARMCLANG/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Copyright 2025 Arm Limited and/or its affiliates +# SPDX-License-Identifier: MIT + +FVP_BaseR_AEMv8R -a build/cortex_r82_smp_extended_mpu_fvp_example.axf --config ./fvp_config.txt