Skip to content

Commit

Permalink
Enable ARMv7-M MPU ports to place FreeRTOS kernel code outside of fla…
Browse files Browse the repository at this point in the history
…sh (FreeRTOS#46)

Problem Description
-------------------
The current flash organization in ARMv7-M MPU ports looks as follows:

__FLASH_segment_start__ ------->+-----------+<----- __FLASH_segment_start__
                                |  Vector   |
                                |   Table   |
                                |     +     |
                                |   Kernel  |
                                |    Code   |
                                +-----------+<-----  __privileged_functions_end__
                                |           |
                                |           |
                                |           |
                                |   Other   |
                                |   Code    |
                                |           |
                                |           |
                                |           |
   __FLASH_segment_end__ ------>+-----------+

The FreeRTOS kernel sets up the following MPU regions:

* Unprivileged Code - __FLASH_segment_start__ to __FLASH_segment_end__.
* Privileged Code - __FLASH_segment_start__ to __privileged_functions_end__.

The above setup assumes that the FreeRTOS kernel code
(i.e. privileged_functions) is placed at the beginning of the flash and,
therefore, uses __FLASH_segment_start__ as the starting location of the
privileged code. This prevents a user from placing the FreeRTOS kernel
code outside of flash (say to an external RAM) and still have vector
table at the beginning of flash (which is many times a hardware
requirement).

Solution
--------
This commit addresses the above limitation by using a new variable
__privileged_functions_start__ as the starting location of the
privileged code. This enables users to place the FreeRTOS kernel code
wherever they choose.

The FreeRTOS kernel now sets up the following MPU regions:

* Unprivileged Code - __FLASH_segment_start__ to __FLASH_segment_end__.
* Privileged Code - __privileged_functions_start__ to __privileged_functions_end__.

As a result, a user can now place the kernel code to an external RAM. A
possible organization is:

                                 Flash              External RAM
                              +------------+        +-----------+<------ __privileged_functions_start__
                              |   Vector   |        |           |
                              |   Table    |        |           |
                              |            |        |           |
__FLASH_segment_start__ ----->+------------+        |   Kernel  |
                              |            |        |    Code   |
                              |            |        |           |
                              |            |        |           |
                              |            |        |           |
                              |   Other    |        |           |
                              |    Code    |        +-----------+<------ __privileged_functions_end__
                              |            |
                              |            |
                              |            |
  __FLASH_segment_end__ ----->+------------+

Note that the above configuration places the vector table in an unmapped
region. This is okay because we enable the background region, and so the
vector table will still be accessible to the privileged code and not
accessible to the unprivileged code (vector table is only needed by the
privileged code).

Backward Compatibility
----------------------
The FreeRTOS kernel code now uses a new variable, namely
__privileged_functions_start__, which needs to be exported from linker
script to indicate the starting location of the privileged code. All of
our existing demos already export this variable and therefore, they will
continue to work.

If a user has created a project which does not export this variable,
they will get a linker error for unresolved symbol
__privileged_functions_start__. They need to export a variable
__privileged_functions_start__ with the value equal to
__FLASH_segment_start__.

Issue
-----
https://sourceforge.net/p/freertos/feature-requests/56/

Signed-off-by: Gaurav Aggarwal <aggarg@amazon.com>
  • Loading branch information
aggarg authored Apr 6, 2020
1 parent 464695a commit 334de5d
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 24 deletions.
12 changes: 6 additions & 6 deletions portable/GCC/ARM_CM3_MPU/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ __attribute__(( weak )) void vPortSetupTimerInterrupt( void )

static void prvSetupMPU( void )
{
extern uint32_t __privileged_functions_start__[];
extern uint32_t __privileged_functions_end__[];
extern uint32_t __FLASH_segment_start__[];
extern uint32_t __FLASH_segment_end__[];
Expand All @@ -593,7 +594,7 @@ extern uint32_t __privileged_data_end__[];
/* Check the expected MPU is present. */
if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE )
{
/* First setup the entire flash for unprivileged read only access. */
/* First setup the unprivileged flash for unprivileged read only access. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */
( portMPU_REGION_VALID ) |
( portUNPRIVILEGED_FLASH_REGION );
Expand All @@ -603,16 +604,15 @@ extern uint32_t __privileged_data_end__[];
( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |
( portMPU_REGION_ENABLE );

/* Setup the first 16K for privileged only access (even though less
* than 10K is actually being used). This is where the kernel code is
* placed. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */
/* Setup the privileged flash for privileged only access. This is where
* the kernel code is * placed. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ ) | /* Base address. */
( portMPU_REGION_VALID ) |
( portPRIVILEGED_FLASH_REGION );

portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) |
( portMPU_REGION_CACHEABLE_BUFFERABLE ) |
( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |
( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__ ) ) |
( portMPU_REGION_ENABLE );

/* Setup the privileged data RAM region. This is where the kernel data
Expand Down
13 changes: 7 additions & 6 deletions portable/GCC/ARM_CM4_MPU/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -640,13 +640,15 @@ static void prvSetupMPU( void )
#if defined( __ARMCC_VERSION )
/* Declaration when these variable are defined in code instead of being
* exported from linker scripts. */
extern uint32_t * __privileged_functions_start__;
extern uint32_t * __privileged_functions_end__;
extern uint32_t * __FLASH_segment_start__;
extern uint32_t * __FLASH_segment_end__;
extern uint32_t * __privileged_data_start__;
extern uint32_t * __privileged_data_end__;
#else
/* Declaration when these variable are exported from linker scripts. */
extern uint32_t __privileged_functions_start__[];
extern uint32_t __privileged_functions_end__[];
extern uint32_t __FLASH_segment_start__[];
extern uint32_t __FLASH_segment_end__[];
Expand All @@ -656,7 +658,7 @@ static void prvSetupMPU( void )
/* Check the expected MPU is present. */
if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE )
{
/* First setup the entire flash for unprivileged read only access. */
/* First setup the unprivileged flash for unprivileged read only access. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */
( portMPU_REGION_VALID ) |
( portUNPRIVILEGED_FLASH_REGION );
Expand All @@ -666,16 +668,15 @@ static void prvSetupMPU( void )
( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |
( portMPU_REGION_ENABLE );

/* Setup the first nK for privileged only access (even though less
than 10K is actually being used). This is where the kernel code is
placed. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */
/* Setup the privileged flash for privileged only access. This is where
the kernel code is placed. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ ) | /* Base address. */
( portMPU_REGION_VALID ) |
( portPRIVILEGED_FLASH_REGION );

portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) |
( portMPU_REGION_CACHEABLE_BUFFERABLE ) |
( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |
( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__ ) ) |
( portMPU_REGION_ENABLE );

/* Setup the privileged data RAM region. This is where the kernel data
Expand Down
12 changes: 6 additions & 6 deletions portable/IAR/ARM_CM4F_MPU/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ __weak void vPortSetupTimerInterrupt( void )

static void prvSetupMPU( void )
{
extern uint32_t __privileged_functions_start__[];
extern uint32_t __privileged_functions_end__[];
extern uint32_t __FLASH_segment_start__[];
extern uint32_t __FLASH_segment_end__[];
Expand All @@ -515,7 +516,7 @@ extern uint32_t __privileged_data_end__[];
/* Check the expected MPU is present. */
if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE )
{
/* First setup the entire flash for unprivileged read only access. */
/* First setup the unprivileged flash for unprivileged read only access. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */
( portMPU_REGION_VALID ) |
( portUNPRIVILEGED_FLASH_REGION );
Expand All @@ -525,16 +526,15 @@ extern uint32_t __privileged_data_end__[];
( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |
( portMPU_REGION_ENABLE );

/* Setup the first 16K for privileged only access (even though less
* than 10K is actually being used). This is where the kernel code is
* placed. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */
/* Setup the privileged flash for privileged only access. This is where
* the kernel code is placed. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ ) | /* Base address. */
( portMPU_REGION_VALID ) |
( portPRIVILEGED_FLASH_REGION );

portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) |
( portMPU_REGION_CACHEABLE_BUFFERABLE ) |
( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |
( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__ ) ) |
( portMPU_REGION_ENABLE );

/* Setup the privileged data RAM region. This is where the kernel data
Expand Down
12 changes: 6 additions & 6 deletions portable/RVDS/ARM_CM4_MPU/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,7 @@ __asm void vPortEnableVFP( void )

static void prvSetupMPU( void )
{
extern uint32_t __privileged_functions_start__;
extern uint32_t __privileged_functions_end__;
extern uint32_t __FLASH_segment_start__;
extern uint32_t __FLASH_segment_end__;
Expand All @@ -647,7 +648,7 @@ extern uint32_t __privileged_data_end__;
/* Check the expected MPU is present. */
if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE )
{
/* First setup the entire flash for unprivileged read only access. */
/* First setup the unprivileged flash for unprivileged read only access. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */
( portMPU_REGION_VALID ) |
( portUNPRIVILEGED_FLASH_REGION );
Expand All @@ -657,16 +658,15 @@ extern uint32_t __privileged_data_end__;
( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |
( portMPU_REGION_ENABLE );

/* Setup the first 16K for privileged only access (even though less
* than 10K is actually being used). This is where the kernel code is
* placed. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */
/* Setup the privileged flash for privileged only access. This is where
* the kernel code is placed. */
portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ ) | /* Base address. */
( portMPU_REGION_VALID ) |
( portPRIVILEGED_FLASH_REGION );

portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) |
( portMPU_REGION_CACHEABLE_BUFFERABLE ) |
( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |
( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__ ) ) |
( portMPU_REGION_ENABLE );

/* Setup the privileged data RAM region. This is where the kernel data
Expand Down

0 comments on commit 334de5d

Please sign in to comment.