Skip to content

Commit 9115d37

Browse files
committed
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 <Ahmed.Ismail@arm.com>
1 parent 7f83264 commit 9115d37

17 files changed

Lines changed: 2146 additions & 0 deletions
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright 2023-2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
cmake_minimum_required(VERSION 3.15)
6+
7+
add_library(bsp INTERFACE)
8+
9+
target_sources(bsp
10+
INTERFACE
11+
${CMAKE_CURRENT_SOURCE_DIR}/Source/port_asm_vectors.S
12+
${CMAKE_CURRENT_SOURCE_DIR}/Source/boot.S
13+
${CMAKE_CURRENT_SOURCE_DIR}/Source/startup.S
14+
${CMAKE_CURRENT_SOURCE_DIR}/Source/gic.c
15+
)
16+
17+
target_include_directories(bsp
18+
INTERFACE
19+
${CMAKE_CURRENT_SOURCE_DIR}/Include
20+
)
21+
22+
target_link_libraries(bsp
23+
INTERFACE
24+
freertos_kernel
25+
)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/* Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
2+
* SPDX-License-Identifier: MIT
3+
*/
4+
5+
#ifndef GIC_H
6+
#define GIC_H
7+
8+
#include "FreeRTOSConfig.h"
9+
10+
#if ( configNUMBER_OF_CORES == 1 )
11+
#define ucPortGetCoreID() ( 0 ) /* Single core system, always core 0 */
12+
#endif
13+
14+
#define GICD_BASE ( 0xAF000000UL ) /* Base of GIC Distributor on BaseR FVP */
15+
#define GICR_BASE_PER_CORE( core ) ( 0xAF100000UL + ( 0x20000UL * ( core ) ) ) /* Base of GIC Redistributor per core on BaseR FVP */
16+
#define SGI_BASE ( 0x10000UL ) /* SGI Base */
17+
#define GICD_CTLR ( 0x000 ) /* Distributor Control Register */
18+
#define GICR_WAKER ( 0x14 ) /* ReDistributor Wake Register */
19+
#define GICR_PWRR ( 0x24 ) /* ReDistributor Power Register */
20+
#define GICR_IGROUPR0 ( SGI_BASE + 0x80 ) /* Interrupt Group Registers */
21+
#define GICR_ISENABLER0 ( SGI_BASE + 0x100 ) /* Interrupt Set-Enable Registers */
22+
#define GICR_IPRIORITYR( n ) ( SGI_BASE + ( 0x400 + ( 4 * n ) ) ) /* Interrupt Priority Registers */
23+
#define GICR_IGRPMODR0 ( SGI_BASE + 0xD00 ) /* Distributor Interrupt group modifier Register */
24+
25+
#define GICD_CTLR_ENABLEGRP1NS_BIT ( 1U ) /* GICD_CTRL.EnableGrp1NS bit */
26+
#define GICD_CTLR_ENABLEGRP1S_BIT ( 2U ) /* GICD_CTRL.EnableGrp1S bit */
27+
#define GICD_CTLR_ARES_BIT ( 4U ) /* GICD_CTRL.ARE_S bit */
28+
#define GICD_CTLR_DS_BIT ( 6U ) /* GICD_CTRL.DS bit */
29+
30+
#define GICR_PWRR_RDPD_BIT ( 0U ) /* GICR_PWRR.RDPD bit */
31+
32+
#define GICR_WAKER_PS_BIT ( 1U ) /* GICR_WAKER.PS bit */
33+
#define GICR_WAKER_CA_BIT ( 2U ) /* GICR_WAKER.CA bit */
34+
35+
#define GIC_MAX_INTERRUPT_ID ( 31UL ) /* Maximum Interrupt ID for PPIs and SGIs */
36+
#define GIC_WAIT_TIMEOUT ( 1000000U ) /* Timeout for waiting on GIC operations */
37+
38+
/**
39+
* Assigns the specified interrupt to Group 1 and enables it
40+
* in the Redistributor for the local core.
41+
*/
42+
void vGIC_EnableIRQ( uint32_t ulInterruptID );
43+
44+
/**
45+
* Enables signaling of Group-1 interrupts at EL1 via ICC_IGRPEN1_EL1.
46+
*/
47+
void vGIC_EnableCPUInterface( void );
48+
49+
/**
50+
* Initializes the GIC Distributor:
51+
* - Enables Group-1 Non-Secure and Group-1 Secure interrupts
52+
* - Enables Affinity Routing (ARE_S) and Disable Security (DS) bits
53+
*/
54+
void vGIC_InitDist( void );
55+
56+
/**
57+
* Powers up and wakes the Redistributor for the current core:
58+
* 1. Clears the Redistributor power-down bit and waits for RDPD=0
59+
* 2. Clears the Processor-Sleep bit and waits for Children-Asleep=0
60+
*/
61+
void vGIC_PowerUpRedistributor( void );
62+
63+
/**
64+
* Sets the priority of the specified SGI/PPI (INTID 0‑31) in the local
65+
* Redistributor bank via GICR_IPRIORITYR.
66+
* For shared peripheral interrupts (SPI, INTID ≥ 32) use the GICD_IPRIORITYR path.
67+
*
68+
* @param ulInterruptID The ID of the interrupt to set the priority for.
69+
* @param ulPriority The priority value to set.
70+
*/
71+
void vGIC_SetPriority( uint32_t ulInterruptID, uint32_t ulPriority );
72+
73+
/**
74+
* Powers up the GIC Redistributor, Sets up the priority for SGI0,
75+
* sets SGI0 to be a Group 1 interrupt, and enables delivery of Group-1 IRQs to EL1.
76+
*/
77+
void vGIC_SetupSgi0( void );
78+
79+
#endif /* GIC_H */
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
/******************************************************************************
2+
* Copyright (c) 2014 - 2020 Xilinx, Inc. All rights reserved.
3+
* Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
4+
* SPDX-License-Identifier: MIT
5+
******************************************************************************/
6+
#if defined(__ARMCC_VERSION)
7+
/* Externs needed by the MPU setup code. These are defined in Scatter-Loading
8+
* description file (armclang.sct). */
9+
.set __el1_stack, Image$$ARM_LIB_STACK$$Base
10+
.set _el1_stack_end, Image$$ARM_LIB_HEAP$$Base
11+
#endif
12+
13+
#include "FreeRTOSConfig.h"
14+
15+
.global _prestart
16+
.global _boot
17+
18+
.global __el1_stack
19+
.global _vector_table
20+
21+
.set EL1_stack, __el1_stack
22+
23+
.set EL1_stack_end, _el1_stack_end
24+
25+
.set vector_base, _vector_table
26+
27+
/*
28+
* N_CPUS_SHIFT must equal log2(configNUMBER_OF_CORES). It represents the
29+
* number of bits required to index the core that owns a particular slice
30+
* of the shared EL1 stack pool.
31+
*
32+
* To avoid overlapping stack regions, the code assumes
33+
* configNUMBER_OF_CORES is a power‑of‑two. The static check below forces
34+
* a build‑time error if that assumption is broken.
35+
*/
36+
#if ( (configNUMBER_OF_CORES & (configNUMBER_OF_CORES - 1)) != 0 )
37+
#error "configNUMBER_OF_CORES must be a power‑of‑two"
38+
#endif
39+
40+
/* Compute log2(configNUMBER_OF_CORES). */
41+
#if (configNUMBER_OF_CORES == 1)
42+
.set N_CPUS_SHIFT, 0
43+
#elif (configNUMBER_OF_CORES == 2)
44+
.set N_CPUS_SHIFT, 1
45+
#elif (configNUMBER_OF_CORES == 4)
46+
.set N_CPUS_SHIFT, 2
47+
#else
48+
#error "Unsupported configNUMBER_OF_CORES value — must be a power‑of‑two up to 4"
49+
#endif
50+
51+
.set SCTLR_EL1_SPAN, 23 /* SCTLR_EL1.SPAN bit position */
52+
.set SCTLR_EL1_NTWE, 18 /* SCTLR_EL1.nTWE bit position */
53+
.set SCTLR_EL1_NTWI, 16 /* SCTLR_EL1.nTWI bit position */
54+
.set SCTLR_EL1_CACHE, 2 /* SCTLR_EL1.C bit position */
55+
56+
.section .boot,"ax"
57+
58+
_prestart:
59+
_boot:
60+
start:
61+
/* Clear all GP registers (x0–x30) for a known initial state */
62+
mov x0, #0
63+
mov x1, #0
64+
mov x2, #0
65+
mov x3, #0
66+
mov x4, #0
67+
mov x5, #0
68+
mov x6, #0
69+
mov x7, #0
70+
mov x8, #0
71+
mov x9, #0
72+
mov x10, #0
73+
mov x11, #0
74+
mov x12, #0
75+
mov x13, #0
76+
mov x14, #0
77+
mov x15, #0
78+
mov x16, #0
79+
mov x17, #0
80+
mov x18, #0
81+
mov x19, #0
82+
mov x20, #0
83+
mov x21, #0
84+
mov x22, #0
85+
mov x23, #0
86+
mov x24, #0
87+
mov x25, #0
88+
mov x26, #0
89+
mov x27, #0
90+
mov x28, #0
91+
mov x29, #0
92+
mov x30, #0
93+
94+
mrs x0, currentEL
95+
/* Check we’ve come from EL1 (currentEL==0x4), otherwise fault */
96+
cmp x0, #0x4
97+
beq InitEL1
98+
99+
b error
100+
InitEL1:
101+
/* Set vector table base address */
102+
ldr x1, =vector_base
103+
msr VBAR_EL1,x1
104+
105+
mrs x0, CPACR_EL1
106+
/* Allow FP/SIMD at both EL1 and EL0: CPACR_EL1.FPEN[21:20] = 0b11 */
107+
orr x0, x0, #(0x3 << 20)
108+
msr CPACR_EL1, x0 /* Enable FP/SIMD access at EL1 and EL0 */
109+
isb
110+
111+
/* Clear FP status flags (FPSR) to avoid spurious exceptions on first use */
112+
msr FPSR, xzr
113+
114+
/* Define stack pointer for current exception level */
115+
#if configNUMBER_OF_CORES > 1
116+
/* Divide the EL1 stack region equally among all cores, then set SP based on MPIDR_EL1[7:0] */
117+
/* x0 = log2(N_CPUS) is assumed to be a build-time constant */
118+
mov x0, N_CPUS_SHIFT /* log2(#cores) */
119+
/* load overall stack limits */
120+
ldr x2, =EL1_stack /* low address of the shared stack pool */
121+
ldr x3, =EL1_stack_end /* high address (one past the pool) */
122+
/* x1 = total size of the pool, x1 >> N_CPUS_SHIFT = size per core */
123+
sub x1, x3, x2 /* total_stack_size */
124+
lsr x1, x1, x0 /* slice_size = total/#cores */
125+
/* x4 = this CPU’s index (Aff0 field of MPIDR_EL1) */
126+
mrs x4, MPIDR_EL1
127+
and x4, x4, #0xFF /* core_id ∈ {0 … N_CPUS-1} */
128+
cmp x4, #configNUMBER_OF_CORES
129+
b.hs error
130+
/* x0 = slice_size * core_id → how far to step back from the top */
131+
mul x0, x1, x4
132+
/* sp = top_of_pool – offset (so core 0 gets the very top) */
133+
sub x3, x3, x0 /* x3 = initial SP for this core */
134+
bic x3, x3, #0xF /* keep the mandated 16-byte alignment */
135+
mov sp, x3
136+
#else
137+
ldr x2, =EL1_stack_end
138+
mov sp, x2
139+
#endif
140+
141+
/* Enable ICC system-register interface (SRE=1) and disable FIQ/IRQ bypass (DFB/DIB) */
142+
mov x0, #0x7
143+
msr ICC_SRE_EL1, x0
144+
145+
/* Invalidate I and D caches */
146+
ic IALLU
147+
bl invalidate_dcaches
148+
dsb sy
149+
isb
150+
151+
/* Unmask SError interrupts (clear DAIF.A bit) */
152+
mrs x1,DAIF
153+
bic x1,x1,#(0x1<<8)
154+
msr DAIF,x1
155+
156+
/* Configure SCTLR_EL1:
157+
* - Enable data cache (C=1)
158+
* - Allow EL0 to execute WFE/WFI (Set nTWE/nTWI so they don't trap)
159+
* - Set SPAN so PSTATE.PAN is preserved on exception entry
160+
*/
161+
mrs x1, SCTLR_EL1
162+
orr x1, x1, #(1 << SCTLR_EL1_SPAN) /* SPAN = 1 → The value of PSTATE.PAN is left unchanged on taking an exception to EL1 */
163+
orr x1, x1, #(1 << SCTLR_EL1_NTWE) /* nTWE = 1 → WFE at EL0 does not trap */
164+
orr x1, x1, #(1 << SCTLR_EL1_NTWI) /* nTWI = 1 → WFI at EL0 does not trap */
165+
orr x1, x1, #(1 << SCTLR_EL1_CACHE) /* C = 1 → enable data cache */
166+
msr SCTLR_EL1, x1
167+
isb
168+
169+
/* Branch to C-level startup (zero BSS, init data, etc.) */
170+
bl _startup
171+
172+
/* If we ever get here, something went wrong—hang forever */
173+
error:
174+
b error
175+
176+
invalidate_dcaches:
177+
178+
dmb ISH
179+
mrs x0, CLIDR_EL1 /* x0 = CLIDR */
180+
ubfx w2, w0, #24, #3 /* w2 = CLIDR.LoC */
181+
cmp w2, #0 /* LoC is 0? */
182+
b.eq invalidateCaches_end /* No cleaning required */
183+
mov w1, #0 /* w1 = level iterator */
184+
185+
invalidateCaches_flush_level:
186+
add w3, w1, w1, lsl #1 /* w3 = w1 * 3 (right-shift for cache type) */
187+
lsr w3, w0, w3 /* w3 = w0 >> w3 */
188+
ubfx w3, w3, #0, #3 /* w3 = cache type of this level */
189+
cmp w3, #2 /* No cache at this level? */
190+
b.lt invalidateCaches_next_level
191+
192+
lsl w4, w1, #1
193+
msr CSSELR_EL1, x4 /* Select current cache level in CSSELR */
194+
isb /* ISB required to reflect new CSIDR */
195+
mrs x4, CCSIDR_EL1 /* w4 = CSIDR */
196+
197+
ubfx w3, w4, #0, #3
198+
add w3, w3, #2 /* w3 = log2(line size) */
199+
ubfx w5, w4, #13, #15
200+
ubfx w4, w4, #3, #10 /* w4 = Way number */
201+
clz w6, w4 /* w6 = 32 - log2(number of ways) */
202+
203+
invalidateCaches_flush_set:
204+
mov w8, w4 /* w8 = Way number */
205+
invalidateCaches_flush_way:
206+
lsl w7, w1, #1 /* Fill level field */
207+
lsl w9, w5, w3
208+
orr w7, w7, w9 /* Fill level field */
209+
lsl w9, w8, w6
210+
orr w7, w7, w9 /* Fill way field */
211+
dc CISW, x7 /* Invalidate by set/way to point of coherency */
212+
subs w8, w8, #1 /* Decrement way */
213+
b.ge invalidateCaches_flush_way
214+
subs w5, w5, #1 /* Descrement set */
215+
b.ge invalidateCaches_flush_set
216+
217+
invalidateCaches_next_level:
218+
add w1, w1, #1 /* Next level */
219+
cmp w2, w1
220+
b.gt invalidateCaches_flush_level
221+
222+
invalidateCaches_end:
223+
ret
224+
225+
.end

0 commit comments

Comments
 (0)