MPU:鸿蒙轻内核的任务栈的溢出检察官
摘要:MPU(Memory Protection Unit,内存保护单元)把内存映射为一系列内存区域,定义这些内存区域的维洲,大小,访问权限和内存熟悉信息。
本文分享自华为云社区《鸿蒙轻内核M核源码分析系列十六 MPU内存保护单元》,作者: zhushy。
MPU(Memory Protection Unit,内存保护单元)把内存映射为一系列内存区域,定义这些内存区域的维洲,大小,访问权限和内存熟悉信息。MPU支持对每个内存区域进行独立的属性设置,允许内存区域重,可以导出内存属性。有关MPU的详细信息可以参考官方资料站点,比如对应Cortex-M3的文档位置为:https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit。
在鸿蒙轻内核中,MPU用于任务栈的溢出检测。本文主要分析鸿蒙轻内核MPU模块的源码。本文中所涉及的源码,以OpenHarmony LiteOS-M内核为例,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。鸿蒙轻内核支持的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/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-access-permission-attributes,特别是上述页面的表格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/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit,查看页面上的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/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-control-register。⑵处代码使能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/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-type-register。
⑷处表示MPU的数据内存区域(MPU data regions)数量不为空时,执行⑸处代码更新MPU内存区域编号寄存器(MPU Region Number Register
)为指定的内存区域编号,详细的信息可以参考https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-region-number-register。然后执行⑹处代码更新MPU内存区域属性和大小寄存器(MPU Region Attribute and Size Register
),详细可以参考https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-region-attribute-and-size-register。⑺处把全局变量数组中指定的区域编号设置为未使用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/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-region-base-address-register了解更多。⑹处计算属性和大小寄存器的数值。⑺处如果指定的内存区域被使用,直接返回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;
}
MPU:鸿蒙轻内核的任务栈的溢出检察官的更多相关文章
- 鸿蒙轻内核M核的故障管家:Fault异常处理
摘要:本文先简单介绍下Fault异常类型,向量表及其代码,异常处理C语言程序,然后详细分析下异常处理汇编函数实现代码. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十八 Fault异常处理& ...
- 从五大结构体,带你掌握鸿蒙轻内核动态内存Dynamic Memory
摘要:本文带领大家一起剖析了鸿蒙轻内核的动态内存模块的源代码,包含动态内存的结构体.动态内存池初始化.动态内存申请.释放等. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列九 动态内存Dyna ...
- 鸿蒙轻内核定时器Swtmr:不受硬件和数量限制,满足用户需求
摘要:本文通过分析鸿蒙轻内核定时器模块的源码,掌握定时器使用上的差异. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十四 软件定时器Swtmr>,作者:zhushy . 软件定时器(S ...
- 深层剖析鸿蒙轻内核M核的动态内存如何支持多段非连续性内存
摘要:鸿蒙轻内核M核新增支持了多段非连续性内存区域,把多个非连续性内存逻辑上合一,用户不感知底层的不同内存块. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列九 动态内存Dynamic Mem ...
- 带你熟悉鸿蒙轻内核Kconfig使用指南
摘要:本文介绍了Kconfig的基础知识,和鸿蒙轻内核的图形化配置及进阶的使用方法. 本文分享自华为云社区<鸿蒙轻内核Kconfig使用笔记>,作者: zhushy. 1. Kconfig ...
- 鸿蒙轻内核M核源码分析:LibC实现之Musl LibC
摘要:本文学习了LiteOS-M内核Musl LibC的实现,特别是文件系统和内存分配释放部分. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十九 Musl LibC>,作者:zhus ...
- 鸿蒙轻内核源码分析:文件系统LittleFS
摘要:本文先介绍下LFS文件系统结构体的结构体和全局变量,然后分析下LFS文件操作接口. 本文分享自华为云社区<# 鸿蒙轻内核M核源码分析系列二一 02 文件系统LittleFS>,作者: ...
- 鸿蒙轻内核源码分析:文件系统FatFS
摘要:本文为大家介绍FatFS文件系统结构体的结构体和全局变量,并分析FatFS文件操作接口. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列二一 03 文件系统FatFS>,作者:zh ...
- js中非死循环引起的栈调用溢出问题
一般情况下,仅从代码上看只要不出现死循环,是不会出现堆栈调用溢出的.但是某些情况下列外,比如下面这段代码: var a = 99; function b (){ a --; if (a > 0) ...
- CVE-2010-2883Adobe Reader和Acrobat CoolType.dll栈缓冲区溢出漏洞分析
Adobe Acrobat和Reader都是美国Adobe公司开发的非常流行的PDF文件阅读器. 基于Window和Mac OS X的Adobe Reader和Acrobat 9.4之前的9.x ...
随机推荐
- JVM-JAVA基本类型
1 package javap.fload; 2 3 import static jdk.nashorn.internal.objects.Global.Infinity; 4 5 public cl ...
- Redis宕机恢复
AOF(Append Only File) Redis持久化:AOF日志 用 AOF 方法进行故障恢复的时候,需要逐一把操作日志都执行一遍.如果操作日志非常多,Redis 就会恢复得很缓慢,影响到正常 ...
- [Python急救站课程]健康食谱搭配
健康食谱搭配输出 diet = ['西红柿', '花椰菜', '黄瓜', '牛排', '虾仁'] for x in range(0, 5): for y in range(0, 5): if not ...
- coco漫画获取隐藏的图片链接
网站分析 打开目标网站:https://www.cocomanhua.com/, 随便打开一部漫画: https://www.cocomanhua.com/10330/1/205.html F12 打 ...
- Electron-React18-MacOS桌面管理系统|electron27+react仿mac桌面
基于React18+Electron27+ArcoDesign仿macOS桌面端系统框架ElectronMacOS. electron-react-macOs 基于electron27.x+vite4 ...
- 配置postcss-pxtorem报:options has an unknown property 'plugins'
闲聊: 小颖最近在坐大屏相关的项目,要写适配,之前用的:postcss-px2rem.px2rem-loader,和朋友闲聊呢他说他们也在写大屏,不过他们用的 postcss-pxtorem,在写另外 ...
- STA分析-复制
1 静态时序分析(Static Timing Analysis)静态时序分析(Static Timing Analysis):静态执行对于数字设计时序的分析,不依赖于施加在输入端口上的激励,验证设计是 ...
- 【开源项目推荐】-支持GPT的智能数据库客户端与报表工具——Chat2DB
2023年是人工智能爆火的一年,ChatGPT为首的一系列的大模型的出现,让生成式人工智能彻底火了一把.但有人会说,GPT对于我们数据开发来说并没有什么作用啊? 今天为大家推荐的开源项目,就是GPT在 ...
- Redis 学习笔记1:数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及 zset(sorted set:有序集合). 一.Redis 数据类型-STRING 1.常用 ...
- 华企盾DSC可能造成系统蓝屏奔溃常见处理方法
1.蓝屏先卸载我们DSC客户端检测是否是我们影响的 2.如果是我们影响的查到版本说明是否有处理过 3.是否有一些不常用的杀毒软件卸载试试 4.如果使用一些USB驱动类的先把USB的注册表改一下试试 5 ...