I am trying to use the MPU in an ARM Cortex-M4 device. The device was running without MPU and was in privileged mode. When I enable the MPU and try to switch to unprivileged mode there is a hard fault and MUNSTEKERR bis is set. I am not sure why this happened and how to solve this...
Here is my function to enable MPU:
#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */
#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */
#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */
#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */
#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */
#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */
#define MPU_AP_NOACCESS 0x0
#define MPU_AP_PRIVONLY 0x1
#define MPU_AP_USERREADONLY 0x2
#define MPU_AP_FULLACCESS 0x3
#define MPU_AP_READONLY 0x6
#define MPU_RASR_AP_NOACCESS (MPU_AP_NOACCESS << MPU_RASR_AP_Pos)
#define MPU_RASR_AP_PRIVONLY (MPU_AP_PRIVONLY << MPU_RASR_AP_Pos)
#define MPU_RASR_AP_USERREADONLY (MPU_AP_USERREADONLY << MPU_RASR_AP_Pos)
#define MPU_RASR_AP_FULLACCESS (MPU_AP_FULLACCESS << MPU_RASR_AP_Pos)
#define MPU_RASR_AP_READONLY (MPU_AP_READONLY << MPU_RASR_AP_Pos)
#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */
#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */
#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */
#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */
#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */
#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */
#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */
#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */
#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */
#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */
#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */
#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */
#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */
#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */
#define MPU_SIZE_64BYTES 0x05
#define MPU_RASR_SIZE_64BYTES (MPU_SIZE_64BYTES << MPU_RASR_SIZE_Pos)
#define MPU_RASR_ENABLE_TRUE 0x1
#define MPU_RASR_ENABLE_FALSE 0x0
#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */
#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */
#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */
#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */
void MPU_Registers_Init(uint32_t addr)
{
MPU_Type *MpuReg = MPU;
uint32_t base_addr = addr;
uint32_t region_number = 0x0; // from 0~7 8 region available
MPU->RBAR |= base_addr & MPU_RBAR_ADDR_Msk;
MPU->RBAR |= ( 0x0u << MPU_RBAR_VALID_Pos) & MPU_RBAR_VALID_Msk;
MPU->RBAR |= ( region_number << MPU_RBAR_REGION_Pos) & MPU_RBAR_REGION_Msk;
uint32_t xn = 0x1u; // instruction fetches disabled
uint32_t tex = 0x000u;
uint32_t c = 0x1u;
uint32_t b = 0x0u; // no write-back
uint32_t s = 0x1u; // shareable
uint32_t srd = 0xFF; // disable Subregion
MPU->RASR |= (xn << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk;
MPU->RASR |= MPU_RASR_AP_USERREADONLY; //p:RW up:RO
MPU->RASR |= (tex << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk;
MPU->RASR |= (s << MPU_RASR_S_Pos) & MPU_RASR_S_Msk;
MPU->RASR |= (c << MPU_RASR_C_Pos) & MPU_RASR_C_Msk;
MPU->RASR |= (b << MPU_RASR_B_Pos) & MPU_RASR_B_Msk;
MPU->RASR |= (srd << MPU_RASR_SRD_Pos) & MPU_RASR_SRD_Msk;
MPU->RASR |= MPU_RASR_SIZE_64BYTES;
MPU->RASR |= MPU_RASR_ENABLE_TRUE;
MPU->CTRL |= (0x01u << MPU_CTRL_PRIVDEFENA_Pos) & MPU_CTRL_PRIVDEFENA_Msk;
MPU->CTRL |= (0x01u << MPU_CTRL_ENABLE_Pos) & MPU_CTRL_ENABLE_Msk;
}
and this is how I try to switch to unprivileged mode:
uint32_t RegControl = __get_CONTROL();
__set_CONTROL( RegControl | CONTROL_nPRIV_Msk );
After the "__set_CONTROL" I get a hard fault and the CFSR shows MUNSTKERR and IACCVIOL:
The address 0x20001900 is in RAM, in a big static uint8 array.
According to my test, the MPU it self works fine in privileged mode, and the privileged switching works find without MPU, but combining them together will cause some issue.
By the way, I also tried to switch to unprivileged mode first, then enable the MPU by svc call. And I got a similar issue, the difference is IACCVIOL is gone.