摘要:MPU(Memory Protection Unit,内存保护单元)把内存映射为一系列内存区域,定义这些内存区域的维洲,大小,拜访权限和内存相熟信息。

本文分享自华为云社区《鸿蒙轻内核M核源码剖析系列十六 MPU内存保护单元》,作者: zhushy。

MPU(Memory Protection Unit,内存保护单元)把内存映射为一系列内存区域,定义这些内存区域的维洲,大小,拜访权限和内存相熟信息。MPU反对对每个内存区域进行独立的属性设置,容许内存区域重,能够导出内存属性。无关MPU的详细信息能够参考官网材料站点,比方对应Cortex-M3的文档地位为:https://developer.arm.com/doc...。

在鸿蒙轻内核中,MPU用于工作栈的溢出检测。本文次要剖析鸿蒙轻内核MPU模块的源码。本文中所波及的源码,以OpenHarmony LiteOS-M内核为例,均能够在开源站点https://gitee.com/openharmony... 获取。鸿蒙轻内核反对的ARM Cortex-M芯片架构都反对MPU的,代码都是一样的,以kernel\arch\arm\cortex-m4\gcc\los_mpu.c为例进行解说。

1、MPU枚举、构造体定义和罕用宏定义

1.1 MPU枚举、构造体定义

在文件kernel\arch\include\los_mpu.h定义MPU相干的构造体。⑴处定义MPU内存区域的拜访权限,无关拜访权限能够拜访官网https://developer.arm.com/doc...,特地是上述页面的表格Table 4.47. AP encoding理解更多。⑵处定义MPU的是否可执行属性枚举,⑶处定义MPU内存区域是否能够共享属性枚举,⑷定义内存区域的类型属性枚举,⑸处的构造体用于定义MPU内存区域。

⑴ 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宏

MPU外设的一些宏定义有HAL Drivers定义,比方对于Cortex-M4,地位为Drivers\CMSIS\Core\Include\core_cm4.h。MPU构造体定义如下,对于MPU寄存器的详细信息能够拜访https://developer.arm.com/doc...,查看页面上的Table 4.38. MPU registers summary。下文在解说代码时会波及MPU的各个寄存器。

#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

另外,MPU反对8个内存区域,kernel\arch\arm\cortex-m4\gcc\los_mpu.c文件中定义的宏如下:

#define MPU_MAX_REGION_NUM 8

2、MPU罕用操作

MPU罕用操作函数蕴含使能MPUHalMpuEnable、失能MPUHalMpuDisable,设置指定的内存区域属性HalMpuSetRegion,失能指定的内存区域HalMpuDisableRegion和获取未应用的内存区域编号HalMpuUnusedRegionGet。

2.1 使能MPUHalMpuEnable

该函数使能MPU性能,⑴处对MPU管制寄存器MPU Control Register进行操作,通过对寄存器相干的bit位进行赋值来使能MPU。无关该寄存器倡议具体浏览https://developer.arm.com/doc...。⑵处代码使能MemoryFault异样。接着执行的数据同步屏障__DSB()和指令同步屏障__ISB(),具体的能够查阅ARM的DMB,DSB,ISB等指令。

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 失能指定的内存区域HalMpuDisableRegion

HalMpuDisableRegion函数执行后不再对指定的内存区域进行MPU爱护,⑴处校验参数合法性。⑵处没有应用的MPU内存区域无奈失能。⑶处获取MPU的类型寄存器,具体能够拜访https://developer.arm.com/doc...。

⑷处示意MPU的数据内存区域(MPU data regions)数量不为空时,执行⑸处代码更新MPU内存区域编号寄存器(MPU Region Number Register
)为指定的内存区域编号,具体的信息能够参考https://developer.arm.com/doc...。而后执行⑹处代码更新MPU内存区域属性和大小寄存器(MPU Region Attribute and Size Register
),具体能够参考https://developer.arm.com/doc...。⑺处把全局变量数组中指定的区域编号设置为未应用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 设置指定的内存区域属性HalMpuSetRegion

HalMpuSetRegion函数设置指定的内存区域的属性。⑴处对参数进行合法性校验。⑵处如果MPU类型寄存器中示意的数据内存区域的数量为0,无奈持续设置内嵌区域,间接返回LOS_NOK。⑶处调用函数HalMpuEncodeSize依据内存区域的理论大小值获取编码大小,该值后续会被赋值给MPU属性和大小寄存器的size位。⑷判断内存区域须要绝对内存区域大小进行内存对齐,否则返回LOS_NOK。

⑸处计算基地址寄存器的数据,无关基地址寄存器(MPU Region Base Address Register),能够拜访https://developer.arm.com/doc...理解更多。⑹处计算属性和大小寄存器的数值。⑺处如果指定的内存区域被应用,间接返回LOS_NOK。⑻处设置MPU相干的寄存器,并标记该内存区域已应用。代码如下:

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依据内存区域理论大小获取size属性值

HalMpuEncodeSize函数依据内存区域理论大小获取size属性值,对应的计算公式为:(Region size in bytes) = 2^(SIZE+1),详细信息能够拜访MPU属性和大小寄存器官网材料页面的Table 4.44. Example SIZE field values。32bytes对应4,1KB对应5,…,4GB对应31。

⑴处示意内存区域大小不能大于4GB,而后判断是否绝对32字节进行内存对齐。⑵处先右移2位,而后while循环,执行⑶每向右循环一位,size属性大小减少1。

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依据size属性值和配置参数计算属性和大小寄存器的值

HalMpuGetRASR依据size属性值和配置参数计算属性和大小寄存器的值。⑴处依据配置的拜访权限计算AP(ACCESS permission),而后计算属性和大小寄存器的值,最初执行⑶给寄存器赋值。

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;}

点击关注,第一工夫理解华为云陈腐技术~