Hello,
I’m looking at the “Cortex-Rx” MPU port (for a Cortex-R5F target), specifically where we program the MPU regions in portASM.S around portable/GCC/ARM_CRx_MPU/portASM.S#L118
MOV R5, #portFIRST_CONFIGURABLE_REGION
123:
LDMIA R1!, { R2-R4 } /* R2 = ulRegionSize, R3 = ulRegionAttribute, R4 = ulRegionBaseAddress. */
MCR p15, #0, R5, c6, c2, #0 /* MPU Region Number Register. */
MCR p15, #0, R4, c6, c1, #0 /* MPU Region Base Address Register. */
MCR p15, #0, R3, c6, c1, #4 /* MPU Region Access Control Register. */
MCR p15, #0, R2, c6, c1, #2 /* MPU Region Size and Enable Register. */
ADD R5, R5, #1
CMP R5, #portNUM_CONFIGURABLE_REGIONS
BLE 123b
Here we load and program the MPU regions as part of the context restore. The FreeRTOS port allows us to specify a per-task configuration of some MPU regions that are loaded above at run-time. This means the multiple MPU registers we are reprogramming hold a ‘stale’ value from the last executing task.
I noticed we do not disable each MPU entry while we reprogram it, so we are potentially writing the MPU registers for a region that is already active. It’s then possible a partially written MPU entry creates an abort.
Consider this scenario: we execute out of 1 MiB RAM in the 0x70000000 address range, and have say 1 MiB of peripheral memory at address 0x50000000. We might want to disallow execution of the 1 MiB of peripheral space (make it RW, no X) and allow execution of the RAM (make it RWX). Maybe each task configures two of its MPU regions to facilitate this.
If ever, during a context switch we begin writing the “RAM RWX” entry into the same MPU region that previously had the “peripheral RW” entry, the above operations program the 0x70000000 base address while we have the active but ‘stale’ peripheral permissions of “read-write, no-execute” in the access control register. We are then executing out of memory marked as no-execute and should encounter a prefetch abort.
I believe I am observing an equivalent of this scenario in a real R5F target.
Zeroing the MPU region enable register before reprogramming the MPU region avoids this, but affects throughput/performance.
Is there a FreeRTOS “best practice” or recommendation that would avoid this? i.e. does this break the rules? Or could this motivate a patch?
Thanks,
Brandon