How to configure the armv8-m or m33 's mpu, when freertos switches context?

mpu in armv8-m has there features:
1, mpu regions can not be overlapped;
2,by no mapping an address to an mpu region, the address is automatically nonaccessible;

freertos vRestoreContextOfFirstTask function,
/* code snippet*/

        "	dmb												\n"/* Complete outstanding transfers before disabling MPU. */
        "	ldr r2, xMPUCTRLConst2							\n"/* r2 = 0xe000ed94 [Location of MPU_CTRL]. */
        "	ldr r4, [r2]									\n"/* Read the value of MPU_CTRL. */
        "	bic r4, #1										\n"/* r4 = r4 & ~1 i.e. Clear the bit 0 in r4. */
        "	str r4, [r2]									\n"/* Disable MPU. */
        "													\n"
        "	adds r1, #4										\n"/* r1 = r1 + 4. r1 now points to MAIR0 in TCB. */
        "	ldr  r3, [r1]									\n"/* r3 = *r1 i.e. r3 = MAIR0. */
        "	ldr  r2, xMAIR0Const2							\n"/* r2 = 0xe000edc0 [Location of MAIR0]. */
        "	str  r3, [r2]									\n"/* Program MAIR0. */
        "	ldr  r2, xRNRConst2								\n"/* r2 = 0xe000ed98 [Location of RNR]. */
        "	movs r3, #4										\n"/* r3 = 4. */
        "	str  r3, [r2]									\n"/* Program RNR = 4. */
        "	adds r1, #4										\n"/* r1 = r1 + 4. r1 now points to first RBAR in TCB. */
        "	ldr  r2, xRBARConst2							\n"/* r2 = 0xe000ed9c [Location of RBAR]. */
        "	ldmia r1!, {r4-r11}								\n"/* Read 4 set of RBAR/RLAR registers from TCB. */
        "	stmia r2!, {r4-r11}								\n"/* Write 4 set of RBAR/RLAR registers using alias registers. */
        "													\n"
        "	ldr r2, xMPUCTRLConst2							\n"/* r2 = 0xe000ed94 [Location of MPU_CTRL]. */
        "	ldr r4, [r2]									\n"/* Read the value of MPU_CTRL. */
        "	orr r4, #1										\n"/* r4 = r4 | 1 i.e. Set the bit 0 in r4. */
        "	str r4, [r2]									\n"/* Enable MPU. */
        "	dsb												\n"/* Force memory writes before continuing. */

/* code end*/

question:
the previous code just configures the partial region(tskTCB->xMPUSettings), which it is not whole address at the system. is it right?
in my opinion, mpu regions include two section: task used regions, and system used regions. when task schedules, all regions shall be configured in the armv8-m/cortex m33. thx

I’m not sure I understand your question - but there are two sets of MPU regions - those that are fixed (and do things like protect the kernel) and those that are part of the tasks context so get switched in and out (i.e. the MPU regions get reprogrammed) as the task is switched in and out. There is more info here: Using FreeRTOS on ARMv8-M Microcontrollers - FreeRTOS

As Richard explained, we only need to re-program per-task MPU regions which are MPU region number 4 and above. And you can see that the code does that by setting RNR to 4. Are you facing any problem?

freertos mpu setting is not perfect. for instance:

/* Create a routine, which need configure mpu*/
void vStartTZDemo( void )
{
    static StackType_t xSecureCallingTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
    TaskParameters_t xSecureCallingTaskParameters =
    {
        .pvTaskCode     = prvSecureCallingTask,
        .pcName         = "SecCalling",
        .usStackDepth   = configMINIMAL_STACK_SIZE,
        .pvParameters   = NULL,
        .uxPriority     = tskIDLE_PRIORITY,
        .puxStackBuffer = xSecureCallingTaskStack,
        .xRegions       =
        {
            { ulNonSecureCounter, 32, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
            { 0,                  0,  0                                                      },
            { 0,                  0,  0                                                      },
        }
    };

    /* Create an unprivileged task which calls secure functions. */
    xTaskCreateRestricted( &( xSecureCallingTaskParameters ), NULL );
}
/*function over*/

in the cortex m33, if mpu just configures the variable (ulNonSecureCounter), the system will cause fault. my understand is: at least, mpu need configure this task’s text section(vStartTZDemo RO section). is it right?

The following is the detailed description based on the previous funtion:
1, call flow

xSecureCallingTaskParameters.xRegions -->xTaskCreateRestricted() -->prvInitialiseNewTask() --> vPortStoreTaskMPUSettings()   pxNewTCB->xMPUSettings

2, using xMPUSettings at the routine: PendSV_Handler

dmb						
ldr r3, xMPUCTRLConst
ldr r4, [r3]			
bic r4, #1				
str r4, [r3]			
						
adds r1, #4				
ldr r4, [r1]			
ldr r3, xMAIR0Const		
str r4, [r3]			
ldr r3, xRNRConst		
movs r4, #4				
str r4, [r3]			
adds r1, #4				
ldr r3, xRBARConst		
ldmia r1!, {r4-r11}		
stmia r3!, {r4-r11}		
						
ldr r3, xMPUCTRLConst
ldr r4, [r3]			
orr r4, #1				
str r4, [r3]			
dsb						
/* Attention: just configure the several regions, and the count is portTOTAL_NUM_REGIONS */

3, freertos kernel just configures a few regions, that does not include task code space region, task other resource region, and so on. That is the problem.

thanks

MPU need configure more regions, apart from the regions based on TaskParameters_t.xRegions. freertos kernel mpu configuration can not resolve resource coupling issue. right?

What is the type of ulNonSecureCounter? The first member of the xRegions is start address of the region: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/include/task.h#L130

If it is uint32_t, it should it be &ulNonSecureCounter. If that does not work, can you share your complete demo so that I can give it a try? Which hardware platform are you using?

ulNonSecureCounter is an array, and the hardware platform is cortex m33. the following is the git repo link:
“https:==github.com=BailiShanghai=arm_cortex_m33_tz_mpu” (new user can not put links in posts, so “=” replace with"/")

actually, i focus on mpu setting of freertos at the m33. thx

That seems like the demo from us. Are you facing any problem while using it? Are you seeing a crash?

1 Like

hardfault exception

Firstly:
I just realize that it is my misunderstanding. mpu setting at the routine: vRestoreContextOfFirstTask is SUPPLEMENTAL configuration. I think mistakenly that vRestoreContextOfFirstTask overrides the previous setting of prvSetupMPU.

Secondly:
zi section partly is configured at the mpu. I think that is not right. and u?

prvSetupMPU configures the zi section, based on scatter file and port.c
2.0 link script

LR_APP 0x00200000 ; load region
{
    ER_IROM_NS_PRIVILEGED +0 ALIGN 32
    {
        *.o(RESET, +First)
        *(InRoot$$Sections) ; All sections that must be in a root region
        *(privileged_functions)
    }

    ER_IROM_NS_PRIVILEGED_ALIGN +0 ALIGN 32 EMPTY 0x0
    {
    }

    ER_IROM_NS_FREERTOS_SYSTEM_CALLS +0 ALIGN 32
    {
        *(freertos_system_calls)
    }

    ER_IROM_NS_FREERTOS_SYSTEM_CALLS_ALIGN +0 ALIGN 32 EMPTY 0x0
    {
    }

    ER_IROM_NS_UNPRIVILEGED +0 ALIGN 32
    {
        *(+RO)
    }

    ER_IROM_NS_UNPRIVILEGED_ALIGN +0 ALIGN 32 EMPTY 0x0
    {
    }

    ER_IRAM_NS_PRIVILEGED 0x20200000 ALIGN 32
    {
        *(privileged_data)
    }

    ER_IRAM_NS_PRIVILEGED_ALIGN +0 ALIGN 32 EMPTY 0x0
    {
    }

    ER_IRAM_NS_UNPRIVILEGED +0 ALIGN 32
    {
        *(+RW, +ZI)
    }

    ER_IRAM_NS_UNPRIVILEGED_ALIGN +0 ALIGN 32 EMPTY 0x0
    {
    }
}

2.1 variables related with prvSetupMPU

const uint32_t * __unprivileged_flash_start__		= ( uint32_t * ) &( Image$$ER_IROM_NS_UNPRIVILEGED$$Base );
const uint32_t * __unprivileged_flash_end__			= ( uint32_t * ) ( ( uint32_t ) &( Image$$ER_IROM_NS_UNPRIVILEGED_ALIGN$$Limit ) - 0x1 ); /* Last address in un-privileged Flash region. */

2.2 code related with prvSetupMPU , configure at the first time

            portMPU_RNR_REG = portUNPRIVILEGED_FLASH_REGION;
            portMPU_RBAR_REG = ( ( ( uint32_t ) __unprivileged_flash_start__ ) & portMPU_RBAR_ADDRESS_MASK ) |
                               ( portMPU_REGION_NON_SHAREABLE ) |
                               ( portMPU_REGION_READ_ONLY );
            portMPU_RLAR_REG = ( ( ( uint32_t ) __unprivileged_flash_end__ ) & portMPU_RLAR_ADDRESS_MASK ) |
                               ( portMPU_RLAR_ATTR_INDEX0 ) |
                               ( portMPU_RLAR_REGION_ENABLE );

2.3 code of mpu_demo.c, it will be configured at the second time

static uint8_t ucSharedMemory[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( 32 ) ) );

void vStartMPUDemo( void )
{
    static StackType_t xROAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
    static StackType_t xRWAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
    TaskParameters_t xROAccessTaskParameters =
    {
        .pvTaskCode     = prvROAccessTask,
        .pcName         = "ROAccess",
        .usStackDepth   = configMINIMAL_STACK_SIZE,
        .pvParameters   = NULL,
        .uxPriority     = tskIDLE_PRIORITY,
        .puxStackBuffer = xROAccessTaskStack,
        .xRegions       =
        {
            { ucSharedMemory,       32, tskMPU_REGION_READ_ONLY | tskMPU_REGION_EXECUTE_NEVER  },
            { ucROTaskFaultTracker, 32, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
            { 0,                    0,  0                                                      },
        }
    };
    TaskParameters_t xRWAccessTaskParameters =
    {
        .pvTaskCode     = prvRWAccessTask,
        .pcName         = "RWAccess",
        .usStackDepth   = configMINIMAL_STACK_SIZE,
        .pvParameters   = NULL,
        .uxPriority     = tskIDLE_PRIORITY,
        .puxStackBuffer = xRWAccessTaskStack,
        .xRegions       =
        {
            { ucSharedMemory, 32, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
            { 0,              0,  0                                                      },
            { 0,              0,  0                                                      },
        }
    };

    /* Create an unprivileged task with RO access to ucSharedMemory. */
    xTaskCreateRestricted( &( xROAccessTaskParameters ), NULL );

    /* Create an unprivileged task with RW access to ucSharedMemory. */
    xTaskCreateRestricted( &( xRWAccessTaskParameters ), NULL );
}

As I explained before, we only re-program the MPU regions which are per task. In your specific case, 2.1 and 2.2 program the MPU region for unprivileged flash while 2.3 programs the MPU regions for granting access to sections of RAM.

If you believe that FreeRTOS has a bug, would you please update this Keil Simulator project to help me repro it - FreeRTOS/FreeRTOS/Demo/CORTEX_MPU_M33F_Simulator_Keil_GCC at main · FreeRTOS/FreeRTOS · GitHub

Thanks.