Abstract: MPU (Memory Protection Unit, memory protection unit) maps memory into a series of memory areas, defining the dimensions, size, access rights and memory familiarity information of these memory areas.

This article is shared from the Huawei Cloud Community " Light Kernel M Core Source Code Analysis Series 16 MPU Memory Protection Unit ", author: zhushy.

MPU (Memory Protection Unit, memory protection unit) maps memory into a series of memory regions, defining the dimensions, size, access rights, and memory familiarity information of these memory regions. The MPU supports independent attribute settings for each memory area, allows the memory area to be heavy, and can export memory attributes. For more information about MPU, please refer to the official information site. For example, the location of the corresponding Cortex-M3 document is: https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection- unit.

In Hongmeng light kernel, MPU is used for overflow detection of task stack. This article mainly analyzes the source code of Hongmeng Light Kernel MPU module. The source code involved in this article, taking the OpenHarmony LiteOS-M kernel as an example, can be obtained from the open source site https://gitee.com/openharmony/kernel_liteos_m . The ARM Cortex-M chip architecture supported by Hongmeng Light Core supports MPU, and the code is the same. Take kernel\arch\arm\cortex-m4\gcc\los_mpu.c as an example to explain.

1. MPU enumeration, structure definition and common macro definition

1.1 MPU enumeration, structure definition

Define MPU-related structures in the file kernel\arch\include\los_mpu.h. ⑴ Define the access permissions of the MPU memory area. For access permissions, please visit the official website https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-access -permission-attributes, especially Table 4.47. AP encoding on the above page to learn more. ⑵ defines whether the MPU can execute attribute enumeration, ⑶ defines whether the MPU memory area can share attribute enumeration, ⑷ defines the type attribute enumeration of the memory area, and ⑸ the structure is used to define the MPU memory area.

⑴ typedef enum {
        MPU_RW_BY_PRIVILEGED_ONLY = 0,
        MPU_RW_ANY = 1,
        MPU_RO_BY_PRIVILEGED_ONLY = 2,
        MPU_RO_ANY = 3,
    } MpuAccessPermission;

⑵  typedef enum {
        MPU_EXECUTABLE = 0,
        MPU_NON_EXECUTABLE = 1,
    } MpuExecutable;

⑶  typedef enum {
        MPU_NO_SHARE = 0,
        MPU_SHARE = 1,
    } MpuShareability;

⑷  typedef enum {
        MPU_MEM_ON_CHIP_ROM = 0,
        MPU_MEM_ON_CHIP_RAM = 1,
        MPU_MEM_XIP_PSRAM = 2,
        MPU_MEM_XIP_NOR_FLASH = 3,
        MPU_MEM_SHARE_MEM = 4,
    } MpuMemType;

⑸  typedef struct {
        UINT32 baseAddr;
        UINT64 size; /* armv7 size == 2^x (5 <= x <= 32)  128B - 4GB */
        MpuAccessPermission permission;
        MpuExecutable executable;
        MpuShareability shareability;
        MpuMemType memType;
    } MPU_CFG_PARA;

1.2 MPU Macro

Some macro definitions of MPU peripherals are defined by HAL Drivers. For example, for Cortex-M4, the location is Drivers\CMSIS\Core\Include\core_cm4.h. The MPU structure is defined as follows. For detailed information about MPU registers, please visit https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit, check the Table on the page 4.38. MPU registers summary. When explaining the code below, each register of the MPU will be involved.

#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U)
  #define MPU_BASE          (SCS_BASE +  0x0D90UL)                    /*!< Memory Protection Unit */
  #define MPU               ((MPU_Type       *)     MPU_BASE      )   /*!< Memory Protection Unit */
#endif

In addition, MPU supports 8 memory areas. The macros defined in the kernel\arch\arm\cortex-m4\gcc\los_mpu.c file are as follows:

#define MPU_MAX_REGION_NUM 8

2. Common operations of MPU

Common MPU operation functions include enabling MPUHalMpuEnable, disabling MPUHalMpuDisable, setting the specified memory region attribute HalMpuSetRegion, disabling the designated memory region HalMpuDisableRegion, and obtaining the unused memory region number HalMpuUnusedRegionGet.

2.1 Enable MPUHalMpuEnable

This function enables the MPU function, (1) the MPU control register MPU Control Register is operated, and the MPU is enabled by assigning the relevant bits of the register. For this register, it is recommended to read https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-control-register in detail. (2) The code enables MemoryFault to be abnormal. Then execute the data synchronization barrier __DSB() and instruction synchronization barrier __ISB(). For details, you can refer to ARM's DMB, DSB, ISB and other instructions.

VOID HalMpuEnable(UINT32 defaultRegionEnable)
{
    UINT32 intSave = HalIntLock();
⑴  MPU->CTRL = (MPU_CTRL_ENABLE_Msk | ((defaultRegionEnable << MPU_CTRL_PRIVDEFENA_Pos) & MPU_CTRL_PRIVDEFENA_Msk));
⑵  SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
    __DSB();
    __ISB();
    HalIntRestore(intSave);
}
2.2 失能MPUHalMpuDisable
代码很简单,直接把MPU控制寄存器赋值为0来失能MPU功能。

VOID HalMpuDisable(VOID)
{
    UINT32 intSave = HalIntLock();
    MPU->CTRL = 0;
    __DSB();
    __ISB();
    HalIntRestore(intSave);
}

2.3 Disable the designated memory area HalMpuDisableRegion

After the HalMpuDisableRegion function is executed, MPU protection is no longer performed on the specified memory area, and the legality of the parameters is verified at (1). ⑵ The unused MPU memory area cannot be disabled. (3) Get the MPU type register. For details, please visit https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-type-register.

⑷When the number of MPU data regions (MPU data regions) is not empty, execute ⑸ code to update the MPU Region Number Register (MPU Region Number Register).
) Is the designated memory region number. For detailed information, please refer to https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-region-number- register. Then execute the code at ⑹ to update the MPU memory region attribute and size register (MPU Region Attribute and Size Register
), for details, please refer to https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-region-attribute-and-size-register. ⑺ Set the area number specified in the global variable array to unused 0.

UINT32 HalMpuDisableRegion(UINT32 regionId)
{
    volatile UINT32 type;
    UINT32 intSave;

⑴  if (regionId >= MPU_MAX_REGION_NUM) {
        return LOS_NOK;
    }

    intSave = HalIntLock();
⑵  if (!g_regionNumBeUsed[regionId]) {
        HalIntRestore(intSave);
        return LOS_NOK;
    }

⑶  type = MPU->TYPE;
⑷  if ((MPU_TYPE_DREGION_Msk & type) != 0) {
⑸      MPU->RNR = regionId;
⑹      MPU->RASR = 0;
        __DSB();
        __ISB();
    }
⑺  g_regionNumBeUsed[regionId] = 0; /* clear mpu region used flag */
    HalIntRestore(intSave);
    return LOS_OK;
}

2.4 Set the specified memory region attribute HalMpuSetRegion

The HalMpuSetRegion function sets the attributes of the specified memory region. ⑴ Check the legality of the parameters. (2) If the number of data memory areas indicated in the MPU type register is 0, the embedded area cannot be set, and LOS_NOK is returned directly. (3) Call the function HalMpuEncodeSize to obtain the encoding size according to the actual size value of the memory area, which will be assigned to the size bit of the MPU attribute and size register later. ⑷Judging that the memory area needs to be aligned relative to the size of the memory area, otherwise it returns LOS_NOK.

⑸ Calculate the data of the base address register. For the base address register (MPU Region Base Address Register), you can visit https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory- protection-unit/mpu-region-base-address-register to learn more. ⑹ Calculate the value of attribute and size register. ⑺ If the specified memory area is used, return LOS_NOK directly. ⑻ Set MPU-related registers, and mark the memory area has been used. The code is as follows:

UINT32 HalMpuSetRegion(UINT32 regionId, MPU_CFG_PARA *para)
{
    UINT32 RASR;
    UINT32 RBAR;
    UINT32 RNR;
    UINT32 encodeSize;
    UINT32 intSave;
    UINT64 size;

⑴  if ((regionId >= MPU_MAX_REGION_NUM) || (para == NULL)) {
        return LOS_NOK;
    }

⑵  if ((MPU_TYPE_DREGION_Msk & MPU->TYPE) == 0) {
        return LOS_NOK;
    }

    RNR = regionId;
⑶  encodeSize = HalMpuEncodeSize(para->size);
    if (encodeSize == 0) {
        return LOS_NOK;
    }
⑷  size = para->size - 1;              /* size aligned after encode check */
    if ((para->baseAddr & size) != 0) { /* base addr should aligned to region size */
        return LOS_NOK;
    }
⑸  RBAR = para->baseAddr & MPU_RBAR_ADDR_Msk;
⑹  RASR = HalMpuGetRASR(encodeSize, para);
    intSave = HalIntLock();
⑺  if (g_regionNumBeUsed[regionId]) {
        HalIntRestore(intSave);
        return LOS_NOK;
    }
⑻  MPU->RNR = RNR;
    MPU->RBAR = RBAR;
    MPU->RASR = RASR;
    __DSB();
    __ISB();
    g_regionNumBeUsed[regionId] = 1; /* Set mpu region used flag */
    HalIntRestore(intSave);
    return LOS_OK;
}

2.4.1 HalMpuEncodeSize obtains the size attribute value according to the actual size of the memory area

The HalMpuEncodeSize function obtains the size attribute value according to the actual size of the memory area. The corresponding calculation formula is: (Region size in bytes) = 2^(SIZE+1). For detailed information, please visit Table 4.44 on the MPU attribute and size register official website data page. Example SIZE field values. 32bytes corresponds to 4, 1KB corresponds to 5,..., 4GB corresponds to 31.

⑴ indicates that the size of the memory area cannot be greater than 4GB, and then judge whether the memory is aligned relative to 32 bytes. (2) The position is first shifted to the right by 2 bits, and then the while loop is executed. (3) The size of the attribute increases by 1 every time it is looped to the right.

STATIC UINT32 HalMpuEncodeSize(UINT64 size)
{
    UINT32 encodeSize = 0;
⑴  if (size > SIZE_4G_BYTE) {
        return 0;
    }
    if ((size & 0x1F) != 0) { /* size should aligned to 32 byte at least. */
        return 0;
    }
⑵  size = (size >> 2);
    while (size != 0) {
        if (((size & 1) != 0) && ((size & 0xFFFFFFFE) != 0)) { /* size != 2^x (5 <= x <= 32)  128B - 4GB */
            return 0;
        }
⑶      size = (size >> 1);
        encodeSize++;
    }
    return encodeSize;
}

2.4.2 HalMpuGetRASR calculates the value of the attribute and size register according to the value of the size attribute and configuration parameters

HalMpuGetRASR calculates the value of the attribute and size register according to the value of the size attribute and configuration parameters. (1) Calculate AP (ACCESS permission) according to the configured access permission, then calculate the value of the attribute and size register, and finally execute (3) to assign the value to the register.

STATIC UINT32 HalMpuEncodeAP(MpuAccessPermission permission)
{
    UINT32 ap;
    switch (permission) {
        case MPU_RW_BY_PRIVILEGED_ONLY:
            ap = MPU_AP_RW_USER_FORBID;
            break;
        case MPU_RW_ANY:
            ap = MPU_AP_RW_USER_RW;
            break;
        case MPU_RO_BY_PRIVILEGED_ONLY:
            ap = MPU_AP_RO_USER_FORBID;
            break;
        case MPU_RO_ANY:
            ap = MPU_AP_RO_USER_RO;
            break;
        default:
            ap = MPU_AP_RW_USER_RW;
            break;
    }
    return ap;
}
STATIC VOID HalMpuRASRAddMemAttr(MPU_CFG_PARA *para, UINT32 *RASR)
{
    BOOL cachable = 0;
    BOOL buffable = 0;
    switch (para->memType) {
        case MPU_MEM_ON_CHIP_ROM:
        case MPU_MEM_ON_CHIP_RAM:
            cachable = 1;
            buffable = 0;
            break;
        case MPU_MEM_XIP_PSRAM:
            cachable = 1;
            buffable = 1;
            break;
        case MPU_MEM_XIP_NOR_FLASH:
            cachable = 0;
            buffable = 1;
            break;
        default:
            break;
    }
    (*RASR) |= ((cachable << MPU_RASR_C_Pos) | (buffable << MPU_RASR_B_Pos));
}

STATIC UINT32 HalMpuGetRASR(UINT32 encodeSize, MPU_CFG_PARA *para)
{
    UINT32 RASR;
    UINT32 ap;
⑴  ap = HalMpuEncodeAP(para->permission);
    RASR = MPU_RASR_ENABLE_Msk;
    RASR |= ((encodeSize << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk);
    RASR |= ((ap << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) | ((para->executable << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) |
        ((para->shareability << MPU_RASR_S_Pos) & MPU_RASR_S_Msk);
⑶  HalMpuRASRAddMemAttr(para, &RASR);
    return RASR;
}

Click to follow, and learn about Huawei Cloud's fresh technology for the first time~


华为云开发者联盟
1.4k 声望1.8k 粉丝

生于云,长于云,让开发者成为决定性力量