内存保护单元 (MPU)

IC:

概述

内存保护单元 (Memory Protection Unit,MPU) 是 Arm Cortex-M 系列 MCU 提供的一个组件,用于通过软件定义来实现硬件保护。各个 CPU 的 MPU 条目数量不同,具体如下表所示:

CPU

MPU条目数量

KM0

4

KM4

  • Secure World: 4

  • Non-secure World: 8

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)

  • MPU_EXEC_ALLOW:允许在该区域执行程序

  • MPU_EXEC_NEVER:禁止在该区域执行程序

ap

uint8_t

访问权限

  • MPU_PRIV_RW:仅特权代码可读/写

  • MPU_UN_PRIV_RW:任何权限级别均可读/写

  • MPU_PRIV_RO:仅特权代码可读

  • MPU_UN_PRIV_RO:任何权限级别均可读

sh

uint8_t

普通内存的共享属性

  • MPU_NON_SHAREABLE:不可共享

  • MPU_OUT_SHAREABLE:外部可共享

  • MPU_INR_SHAREABLE:内部可共享

attr_idx

uint8_t

内存属性间接索引

取值范围为 0 ~ 7,具体属性在 mpu_init() 中定义且支持自定义。典型定义如下:

  • 0:MPU_MEM_ATTR_IDX_NC, 定义普通内存属性为非缓存

  • 1:MPU_MEM_ATTR_IDX_WT_T_RA, 定义普通内存属性为写透传,读分配

  • 2:MPU_MEM_ATTR_IDX_WB_T_RWA, 定义普通内存属性为写回,读和写分配

  • 3 ~ 7:MPU_MEM_ATTR_IDX_DEVICE, 定义设备属性为非聚集(Non-Gathering)、非重排序(Non-Reordering)、非早期写确认(Non-Early Write Acknowledge)

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.capp_start() 函数中,会调用 mpu_init() 函数来初始化 MPU。 MPU 初始化完成后,函数 app_mpu_nocache_init() 依据 已定义的非缓存数据缓冲区 来配置非缓存数据缓冲区。

备注

不管是 I-Cache 还是 D-Cache,均不会缓存 MCU 内部 ROM 的地址。

如果运行过程中代码或数据被误踩,想要借助 MPU 区域只读属性来辅助分析,可以按照以下步骤设置 MPU 区域:

  1. 定义新变量和结构体

    • 定义一个变量 char mpu_entry; 用于存储 MPU 实体索引;

    • 定义一个结构体 mpu_region_config mpu_cfg; 用于存储区域内存属性;

  2. 调用 mpu_entry = mpu_entry_alloc(); 分配一个空闲的 MPU 实体,-1表示分配失败;

  3. 设置区域内存属性的结构体;

    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;
    
  4. 调用 mpu_region_cfg(mpu_entry,&mpu_cfg); 配置 MPU 区域内存属性。