ARM's Memory Protection Unit.
More...
ARM's Memory Protection Unit.
Module Operation
The Memory Protection Unit (MPU) API provides functions to configure the MPU. The MPU is tightly coupled to the Cortex-M processor core and provides a means to establish access permissions on regions of memory.
Up to eight memory regions can be defined. Each region has a base address and a size. The size is specified as a power of 2 between 32 bytes and 4 GB, inclusive. The region's base address must be aligned to the size of the region. Each region also has access permissions. Code execution can be allowed or disallowed for a region. A region can be configured for read-only access, read/write access, or no access for both privileged and user modes. Access permissions can be used to create an environment where only kernel or system code can access certain hardware registers or sections of code.
The MPU creates 8 sub-regions within each region. Any sub-region or combination of sub-regions can be disabled, allowing creation of ``holes'' or complex overlaying regions with different permissions. The sub-regions can also be used to create an unaligned beginning or ending of a region by disabling one or more of the leading or trailing sub-regions.
Once the regions are defined and the MPU is enabled, any access violation of a region causes a memory management fault, and the fault handler is acted.
Basic Operation Modes
The MPU APIs provide a means to enable and configure the MPU and memory protection regions.
Generally, the memory protection regions should be defined before enabling the MPU. The regions can be configured by calling MPU_setRegion() once for each region to be configured.
A region that is defined by MPU_setRegion() can be initially enabled or disabled. If the region is not initially enabled, it can be enabled later by calling MPU_enableRegion(). An enabled region can be disabled by calling MPU_disableRegion(). When a region is disabled, its configuration is preserved as long as it is not overwritten. In this case, it can be enabled again with MPU_enableRegion() without the need to reconfigure the region.
Care must be taken when setting up a protection region using MPU_setRegion(). The function writes to multiple registers and is not protected from interrupts. Therefore, it is possible that an interrupt which accesses a region may occur while that region is in the process of being changed. The safest way to protect against this is to make sure that a region is always disabled before making any changes. Otherwise, it is up to the caller to ensure that MPU_setRegion() is always called from within code that cannot be interrupted, or from code that is not be affected if an interrupt occurs while the region attributes are being changed.
The attributes of a region that have already been programmed can be retrieved and saved using the MPU_getRegionCount() function. This function is intended to save the attributes in a format that can be used later to reload the region using the MPU_setRegion() function. Note that the enable state of the region is saved with the attributes and takes effect when the region is reloaded.
When one or more regions are defined, the MPU can be enabled by calling MPU_enableModule(). This function turns on the MPU and also defines the behavior in privileged mode and in the Hard Fault and NMI fault handlers. The MPU can be configured so that when in privileged mode and no regions are enabled, a default memory map is applied. If this feature is not enabled, then a memory management fault is generated if the MPU is enabled and no regions are configured and enabled. The MPU can also be set to use a default memory map when in the Hard Fault or NMI handlers, instead of using the configured regions. All of these features are selected when calling MPU_enableModule(). When the MPU is enabled, it can be disabled by calling MPU_disableModule().
Finally, if the application is using run-time interrupt registration (see Interrupt_registerInterrupt()), then the function MPU_registerInterrupt() can be used to install the fault handler which is called whenever a memory protection violation occurs. This function also enables the fault handler. If compile-time interrupt registration is used, then the Interrupt_enableInterrupt() function with the parameter FAULT_MPU must be used to enable the memory management fault handler. When the memory management fault handler has been installed with MPU_disableModule(), it can be removed by calling MPU_unregisterInterrupt().
Programming Example
The DriverLib package contains a variety of different code examples that demonstrate the usage of the MPU module. These code examples are accessible under the examples/ folder of the SDK release as well as through TI Resource Explorer if using Code Composer Studio. These code examples provide a comprehensive list of use cases as well as practical applications involving each module.
Below is a very brief code example showing how to configure the MPU module to define a new memory region and set it as read only:
#define MPU_CONFIG_PRIV_DEFAULT MPU_CTRL_PRIVDEFENA_Msk |
#define MPU_CONFIG_HARDFLT_NMI MPU_CTRL_HFNMIENA_Msk |
#define MPU_CONFIG_NONE 0 |
#define MPU_RGN_SIZE_32B (4 << 1) |
#define MPU_RGN_SIZE_64B (5 << 1) |
#define MPU_RGN_SIZE_128B (6 << 1) |
#define MPU_RGN_SIZE_256B (7 << 1) |
#define MPU_RGN_SIZE_512B (8 << 1) |
#define MPU_RGN_SIZE_1K (9 << 1) |
#define MPU_RGN_SIZE_2K (10 << 1) |
#define MPU_RGN_SIZE_4K (11 << 1) |
#define MPU_RGN_SIZE_8K (12 << 1) |
#define MPU_RGN_SIZE_16K (13 << 1) |
#define MPU_RGN_SIZE_32K (14 << 1) |
#define MPU_RGN_SIZE_64K (15 << 1) |
#define MPU_RGN_SIZE_128K (16 << 1) |
#define MPU_RGN_SIZE_256K (17 << 1) |
#define MPU_RGN_SIZE_512K (18 << 1) |
#define MPU_RGN_SIZE_1M (19 << 1) |
#define MPU_RGN_SIZE_2M (20 << 1) |
#define MPU_RGN_SIZE_4M (21 << 1) |
#define MPU_RGN_SIZE_8M (22 << 1) |
#define MPU_RGN_SIZE_16M (23 << 1) |
#define MPU_RGN_SIZE_32M (24 << 1) |
#define MPU_RGN_SIZE_64M (25 << 1) |
#define MPU_RGN_SIZE_128M (26 << 1) |
#define MPU_RGN_SIZE_256M (27 << 1) |
#define MPU_RGN_SIZE_512M (28 << 1) |
#define MPU_RGN_SIZE_1G (29 << 1) |
#define MPU_RGN_SIZE_2G (30 << 1) |
#define MPU_RGN_SIZE_4G (31 << 1) |
#define MPU_RGN_PERM_EXEC 0x00000000 |
#define MPU_RGN_PERM_NOEXEC 0x10000000 |
#define MPU_RGN_PERM_PRV_NO_USR_NO 0x00000000 |
#define MPU_RGN_PERM_PRV_RW_USR_NO 0x01000000 |
#define MPU_RGN_PERM_PRV_RW_USR_RO 0x02000000 |
#define MPU_RGN_PERM_PRV_RW_USR_RW 0x03000000 |
#define MPU_RGN_PERM_PRV_RO_USR_NO 0x05000000 |
#define MPU_RGN_PERM_PRV_RO_USR_RO 0x06000000 |
#define MPU_SUB_RGN_DISABLE_0 0x00000100 |
#define MPU_SUB_RGN_DISABLE_1 0x00000200 |
#define MPU_SUB_RGN_DISABLE_2 0x00000400 |
#define MPU_SUB_RGN_DISABLE_3 0x00000800 |
#define MPU_SUB_RGN_DISABLE_4 0x00001000 |
#define MPU_SUB_RGN_DISABLE_5 0x00002000 |
#define MPU_SUB_RGN_DISABLE_6 0x00004000 |
#define MPU_SUB_RGN_DISABLE_7 0x00008000 |
#define MPU_RGN_DISABLE 0 |
#define NVIC_MPU_TYPE_DREGION_S 8 |
void MPU_enableModule |
( |
uint32_t |
mpuConfig | ) |
|
Enables and configures the MPU for use.
- Parameters
-
mpuConfig | is the logical OR of the possible configurations. |
This function enables the Cortex-M memory protection unit. It also configures the default behavior when in privileged mode and while handling a hard fault or NMI. Prior to enabling the MPU, at least one region must be set by calling MPU_setRegion() or else by enabling the default region for privileged mode by passing the MPU_CONFIG_PRIV_DEFAULT flag to MPU_enableModule(). Once the MPU is enabled, a memory management fault is generated for memory access violations.
The mpuConfig parameter should be the logical OR of any of the following:
- MPU_CONFIG_PRIV_DEFAULT enables the default memory map when in privileged mode and when no other regions are defined. If this option is not enabled, then there must be at least one valid region already defined when the MPU is enabled.
- MPU_CONFIG_HARDFLT_NMI enables the MPU while in a hard fault or NMI exception handler. If this option is not enabled, then the MPU is disabled while in one of these exception handlers and the default memory map is applied.
- MPU_CONFIG_NONE chooses none of the above options. In this case, no default memory map is provided in privileged mode, and the MPU is not enabled in the fault handlers.
- Returns
- None.
References ASSERT, MPU_CONFIG_HARDFLT_NMI, and MPU_CONFIG_PRIV_DEFAULT.
void MPU_disableModule |
( |
void |
| ) |
|
Disables the MPU for use.
This function disables the Cortex-M memory protection unit. When the MPU is disabled, the default memory map is used and memory management faults are not generated.
- Returns
- None.
uint32_t MPU_getRegionCount |
( |
void |
| ) |
|
Gets the count of regions supported by the MPU.
This function is used to get the total number of regions that are supported by the MPU, including regions that are already programmed.
- Returns
- The number of memory protection regions that are available for programming using MPU_setRegion().
References NVIC_MPU_TYPE_DREGION_S.
void MPU_enableRegion |
( |
uint32_t |
region | ) |
|
Enables a specific region.
- Parameters
-
region | is the region number to enable. Valid values are between 0 and 7 inclusively. |
This function is used to enable a memory protection region. The region should already be configured with the MPU_setRegion() function. Once enabled, the memory protection rules of the region are applied and access violations cause a memory management fault.
- Returns
- None.
References ASSERT.
void MPU_disableRegion |
( |
uint32_t |
region | ) |
|
Disables a specific region.
- Parameters
-
region | is the region number to disable. Valid values are between 0 and 7 inclusively. |
This function is used to disable a previously enabled memory protection region. The region remains configured if it is not overwritten with another call to MPU_setRegion(), and can be enabled again by calling MPU_enableRegion().
- Returns
- None.
References ASSERT.
void MPU_setRegion |
( |
uint32_t |
region, |
|
|
uint32_t |
addr, |
|
|
uint32_t |
flags |
|
) |
| |
Sets up the access rules for a specific region.
- Parameters
-
region | is the region number to set up. |
addr | is the base address of the region. It must be aligned according to the size of the region specified in flags. |
flags | is a set of flags to define the attributes of the region. |
This function sets up the protection rules for a region. The region has a base address and a set of attributes including the size. The base address parameter, addr, must be aligned according to the size, and the size must be a power of 2.
- Parameters
-
region | is the region number to set. Valid values are between 0 and 7 inclusively. |
The flags parameter is the logical OR of all of the attributes of the region. It is a combination of choices for region size, execute permission, read/write permissions, disabled sub-regions, and a flag to determine if the region is enabled.
The size flag determines the size of a region and must be one of the following:
- MPU_RGN_SIZE_32B
- MPU_RGN_SIZE_64B
- MPU_RGN_SIZE_128B
- MPU_RGN_SIZE_256B
- MPU_RGN_SIZE_512B
- MPU_RGN_SIZE_1K
- MPU_RGN_SIZE_2K
- MPU_RGN_SIZE_4K
- MPU_RGN_SIZE_8K
- MPU_RGN_SIZE_16K
- MPU_RGN_SIZE_32K
- MPU_RGN_SIZE_64K
- MPU_RGN_SIZE_128K
- MPU_RGN_SIZE_256K
- MPU_RGN_SIZE_512K
- MPU_RGN_SIZE_1M
- MPU_RGN_SIZE_2M
- MPU_RGN_SIZE_4M
- MPU_RGN_SIZE_8M
- MPU_RGN_SIZE_16M
- MPU_RGN_SIZE_32M
- MPU_RGN_SIZE_64M
- MPU_RGN_SIZE_128M
- MPU_RGN_SIZE_256M
- MPU_RGN_SIZE_512M
- MPU_RGN_SIZE_1G
- MPU_RGN_SIZE_2G
- MPU_RGN_SIZE_4G
The execute permission flag must be one of the following:
- MPU_RGN_PERM_EXEC enables the region for execution of code
- MPU_RGN_PERM_NOEXEC disables the region for execution of code
The read/write access permissions are applied separately for the privileged and user modes. The read/write access flags must be one of the following:
- MPU_RGN_PERM_PRV_NO_USR_NO - no access in privileged or user mode
- MPU_RGN_PERM_PRV_RW_USR_NO - privileged read/write, user no access
- MPU_RGN_PERM_PRV_RW_USR_RO - privileged read/write, user read-only
- MPU_RGN_PERM_PRV_RW_USR_RW - privileged read/write, user read/write
- MPU_RGN_PERM_PRV_RO_USR_NO - privileged read-only, user no access
- MPU_RGN_PERM_PRV_RO_USR_RO - privileged read-only, user read-only
The region is automatically divided into 8 equally-sized sub-regions by the MPU. Sub-regions can only be used in regions of size 256 bytes or larger. Any of these 8 sub-regions can be disabled, allowing for creation of ``holes'' in a region which can be left open, or overlaid by another region with different attributes. Any of the 8 sub-regions can be disabled with a logical OR of any of the following flags:
- MPU_SUB_RGN_DISABLE_0
- MPU_SUB_RGN_DISABLE_1
- MPU_SUB_RGN_DISABLE_2
- MPU_SUB_RGN_DISABLE_3
- MPU_SUB_RGN_DISABLE_4
- MPU_SUB_RGN_DISABLE_5
- MPU_SUB_RGN_DISABLE_6
- MPU_SUB_RGN_DISABLE_7
Finally, the region can be initially enabled or disabled with one of the following flags:
- MPU_RGN_ENABLE
- MPU_RGN_DISABLE
As an example, to set a region with the following attributes: size of 32 KB, execution enabled, read-only for both privileged and user, one sub-region disabled, and initially enabled; the flags parameter would have the following value:
(MPU_RGN_SIZE_32K | MPU_RGN_PERM_EXEC | MPU_RGN_PERM_PRV_RO_USR_RO | MPU_SUB_RGN_DISABLE_2 | MPU_RGN_ENABLE)
- Note
- This function writes to multiple registers and is not protected from interrupts. It is possible that an interrupt which accesses a region may occur while that region is in the process of being changed. The safest way to handle this is to disable a region before changing it. Refer to the discussion of this in the API Detailed Description section.
- Returns
- None.
References ASSERT.
void MPU_getRegion |
( |
uint32_t |
region, |
|
|
uint32_t * |
addr, |
|
|
uint32_t * |
pflags |
|
) |
| |
Gets the current settings for a specific region.
- Parameters
-
region | is the region number to get. Valid values are between 0 and 7 inclusively. |
addr | points to storage for the base address of the region. |
pflags | points to the attribute flags for the region. |
This function retrieves the configuration of a specific region. The meanings and format of the parameters is the same as that of the MPU_setRegion() function.
This function can be used to save the configuration of a region for later use with the MPU_setRegion() function. The region's enable state is preserved in the attributes that are saved.
- Returns
- None.
References ASSERT.
void MPU_registerInterrupt |
( |
void(*)(void) |
intHandler | ) |
|
Registers an interrupt handler for the memory management fault.
- Parameters
-
intHandler | is a pointer to the function to be called when the memory management fault occurs. |
This function sets and enables the handler to be called when the MPU generates a memory management fault due to a protection region access violation.
- See Also
- Interrupt_registerInterrupt() for important information about registering interrupt handlers.
- Returns
- None.
References ASSERT, FAULT_MPU, and Interrupt_registerInterrupt().
void MPU_unregisterInterrupt |
( |
void |
| ) |
|
void MPU_enableInterrupt |
( |
void |
| ) |
|
void MPU_disableInterrupt |
( |
void |
| ) |
|