摘要: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内核为例,均可以在开源站点kernel_liteos_m: LiteOS kernel for devices with few resources, such as the MCU | 适用于MCU等各种资源极小设备的LiteOS内核 获取。鸿蒙轻内核支持的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:鸿蒙轻内核的任务栈的溢出检察官相关推荐

  1. 带你剖析鸿蒙轻内核任务栈的源代码

    摘要:本文带领大家一起学习了鸿蒙轻内核的任务栈.任务上下文的基础概念,剖析了任务栈初始化的代码. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列七 任务及任务调度(1)任务栈>,原文作者 ...

  2. 鸿蒙轻内核M核的故障管家:Fault异常处理

    摘要:本文先简单介绍下Fault异常类型,向量表及其代码,异常处理C语言程序,然后详细分析下异常处理汇编函数实现代码. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十八 Fault异常处理& ...

  3. 鸿蒙轻内核源码分析:虚拟文件系统 VFS

    本文分享自华为云社区<鸿蒙轻内核M核源码分析系列二一 01 虚拟文件系统VFS>,作者:zhushy . VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际 ...

  4. 带你熟悉鸿蒙轻内核Kconfig使用指南

    摘要:本文介绍了Kconfig的基础知识,和鸿蒙轻内核的图形化配置及进阶的使用方法. 本文分享自华为云社区<鸿蒙轻内核Kconfig使用笔记>,作者: zhushy. 1. Kconfig ...

  5. 鸿蒙轻内核源码分析:MMU协处理器

    摘要:本系列首先了解下ARM CP15协处理器的知识,接着介绍下协处理器相关的汇编指令,最后分析下MMU相关汇编代码. 本文分享自华为云社区<鸿蒙轻内核A核源码分析系列六 MMU协处理器> ...

  6. 鸿蒙轻内核源码分析:虚拟内存

    摘要:本文以代码+文字的形式,介绍虚拟内存管理的结构体.相关宏定义,分析内核虚拟地址空间和用户进程虚拟地址空间如何初始化等内容. 本文分享自华为云社区<鸿蒙轻内核A核源码分析系列四(2) 虚拟内 ...

  7. 鸿蒙轻内核虚拟内存基础知识:虚拟内存进程空间编号

    摘要:本文介绍了OpenHarmony鸿蒙轻内核LiteOS-A的虚拟地址空间编号知识,详细分析进程空间编号的申请与释放操作. 本文分享自华为云社区<鸿蒙轻内核A核源码分析系列四 (1) 虚拟内 ...

  8. 万字解读鸿蒙轻内核物理内存模块

    摘要:本文首先了解了物理内存管理的结构体,接着阅读了物理内存如何初始化,然后分析了物理内存的申请.释放和查询等操作接口的源代码. 本文分享自华为云社区<鸿蒙轻内核A核源码分析系列三 物理内存&g ...

  9. 鸿蒙轻内核源码分析:异常钩子模块系统中断异常,如何转储异常信息

    摘要:本篇介绍下鸿蒙轻内核中异常钩子模块发生系统中断异常时如何转储异常信息. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十七(3) 异常信息ExcInfo>,作者: zhushy. ...

最新文章

  1. 【原创】Kakfa utils源代码分析(二)
  2. Android学习记录:SQLite数据库、res中raw的文件调用
  3. Java设计模式-命令模式Command
  4. linux rcu机制,Linux RCU机制详解 (透彻)
  5. Windows编程—Windows驱动中定时器的使用
  6. 容器编排技术 -- Kubernetes kubectl scale 命令详解
  7. 实现python扩展的C API方法过程全纪录(windows)
  8. 44.分治算法练习:  一元三次方程求解
  9. 30天扣篮训练计划_明日之后:网易CH用心良苦?狼人画出“辐射高校30天计划”,绝了...
  10. 在raspberry pi上使用自己的内核
  11. matlab中phi什么意思,球面座標matlab中Theta和Phi角的範圍?
  12. WEB前端是干什么的?
  13. C语言程序设计教程_第四章:键盘输入与屏幕输出_笔记整理
  14. 【感悟随笔】我只是一直很努力
  15. html的fa图标代码,font awesome
  16. 使用Python读取pdf文件
  17. 解决调用组件,组件内容不加载的问题
  18. visual studio 2022 离线安装包 下载
  19. 玩转教育计算机二级,全国计算机等级考试二级msoffice高级应用教材《玩转office轻松过二级》配套模拟软件使用说明.doc...
  20. 装机之 BIOS、EFI与UEFI详解

热门文章

  1. keil c语言绝对值函数,keil编写C程序是不是不能在函数内定义变量啊,求大神
  2. spring催办业务_业务后台系统之流程设计
  3. 量子计算机网络指数时间,科普:量子计算机是这样计算的
  4. 大话西游2服务器维护,大话西游2:9.10维护解读:灵犀调整全服上线,去疾调整是好是坏?...
  5. android 推流地址可以多人用,Android直播实现(一)Android端推流、播放
  6. thinkpad重装系统不引导_4个步骤,轻松解决电脑重装系统【蓝屏】
  7. [Swift通天遁地]一、超级工具-(13)使用PKHUD制作各种动态提示窗口
  8. javascript鼠标拖拽的那些事情
  9. ASP.NET MVC 学习之路-5
  10. 解决 spring mvc 3.0 结合 hibernate3.2 使用tx:annotation-driven声明式事务无法提交的问题(转载)...