摘要: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;
}
点击关注,第一工夫理解华为云陈腐技术~