内存保护单元(MPU)
概述
内存保护单元 (Memory Protection Unit,MPU) 是 Arm Cortex-M 系列 MCU 提供的一个组件,用于通过软件定义来实现硬件保护。
各个 CPU 的 MPU 条目数量不同,具体如下表所示:
CPU |
MPU 条目数量 |
---|---|
KM0 |
4 |
KM4 |
|
CPU |
MPU 条目数量 |
---|---|
KR4 |
不支持,使用 PMP 配置 |
KM4 |
|
CPU |
MPU 条目数量 |
---|---|
KR4 |
不支持,使用 PMP 配置 |
KM4 |
|
DSP |
|
CPU |
MPU 条目数量 |
---|---|
KR4 |
不支持,使用 PMP 配置 |
KM4 |
|
DSP |
|
CPU |
MPU 条目数量 |
---|---|
KM0 |
4 |
KM4 |
|
CA32 |
不支持,使用 MMU 配置 |
CPU |
MPU 条目数量 |
---|---|
KM4NS |
8 |
KM4TZ |
|
API 参考
SDK 中提供了 mpu_region_config 结构体,用于设置 MPU 的区域内存属性。下表列出了 mpu_region_config 结构体的成员变量:
名称 |
类型 |
描述 |
---|---|---|
region_base |
uint32_t |
MPU 区域基地址,32 字节对齐 |
region_size |
uint32_t |
MPU 区域大小,32 字节对齐 |
xn |
uint8_t |
执行禁止属性(Execute Never)
|
ap |
uint8_t |
访问权限
|
sh |
uint8_t |
普通内存的共享属性
|
attr_idx |
uint8_t |
内存属性间接索引 取值范围为 0 ~ 7,具体属性在 mpu_init() 中定义且支持自定义。典型定义如下:
|
mpu_init
void mpu_init(void)
初始化 MPU 区域内存属性为典型值
参数:无
返回值:无
mpu_set_mem_attr
void mpu_set_mem_attr(uint8_t attr_idx, uint8_t mem_attr)
修改 MPU 区域内存属性
参数:
- attr_idx:
内存属性间接索引,取值范围为 0 ~ 7
- mem_attr:
MPU_MEM_ATTR0 ~ MPU_MEM_ATTR7,也可以自定义
返回值:无
mpu_region_cfg
void mpu_region_cfg(uint8_t region_num, mpu_region_config *pmpu_cfg)
依据给定参数配置 MPU 区域内存属性
参数:
- region_num:
MPU 条目索引
- pmpu_cfg:
指向已配置的 mpu_region_config 结构体
返回值:无
mpu_entry_free
void mpu_entry_free(u32 entry_index)
释放 MPU 条目对应的软件标记,此时 MPU 条目将标记为可用
参数:
- entry_index:
MPU 条目索引
返回值:无
mpu_entry_alloc
char mpu_entry_alloc(void)
分配一个空闲的 MPU 条目
参数:无
返回值:MPU 条目索引,如果分配失败则返回 -1
使用说明
系统启动时,在 ameba_app_start.c
的 app_start()
函数中,会调用 mpu_init()
函数来初始化 MPU。
MPU 初始化完成后,函数 app_mpu_nocache_init()
依据 已定义的非缓存数据缓冲区 来配置非缓存数据缓冲区。
备注
不管是 I-Cache 还是 D-Cache,均不会缓存 MCU 内部 ROM 的地址。
如果运行过程中代码或数据被误踩,想要借助 MPU 区域只读属性来辅助分析,可以按照以下步骤设置 MPU 区域:
定义新变量和结构体
定义一个变量
char mpu_entry
用于存储 MPU 实体索引;定义一个结构体
mpu_region_config mpu_cfg
用于存储区域内存属性;
调用
mpu_entry = mpu_entry_alloc()
分配一个空闲的 MPU 实体,-1 表示分配失败设置区域内存属性的结构体
mpu_cfg.region_base = start_addr; // 需要保护的起始地址,32 字节对齐 mpu_cfg.region_size = size_bytes; // 需要保护的大小,32 字节对齐 mpu_cfg.xn = MPU_EXEC_ALLOW; mpu_cfg.ap = MPU_UN_PRIV_RO; // 只读属性可以避免误踩 mpu_cfg.sh = MPU_NON_SHAREABLE; mpu_cfg.attr_idx = MPU_MEM_ATTR_IDX_WT_T_RA;
调用
mpu_region_cfg(mpu_entry,&mpu_cfg)
配置 MPU 区域内存属性