内存保护单元(memory protection unit),简称:MPU。
MPU可以理解为一个内存管家,他可以把你的内存保护起来,也可以分配权限,限制你对内存空间的操作
它可以设置不同存储区域的存储器访问权限(特权级、用户级),设置存储器(内存和外设)属性(可缓存、可缓冲、可共享)
① 阻止用户应用程序破坏操作系统使用的数据
② 阻止一个任务访问其他任务的数据区,从而隔离任务
③ 把关键数据区域设置为只读,从根本上解决被破坏的可能
④ 检测意外的存储访问,如堆栈溢出、数组越界等
⑤ 将SRAM或RAM空间定义为不可执行,防止代码注入攻击
如果你对内存的操作超过权限就会进入中断MemManage去执行中断服务函数![](https://i-blog.csdnimg.cn/direct/610a5c9be6664a8e912df4761d72891a.png)
CPU通过Cache这个高级缓存区就是mpu的具体机制
TEX若为0/1会进入不同的权限设置模式
Cache(高级缓存)是提升STM32性能的关键一步。
M7内核芯片做了一级Cache支持,Cache分为数据缓存D-Cache和指令缓存I-Cache。
Cache支持4种基本操作:1,使能;2,禁止; 3,清空; 4,无效化
代码解析:
原理是定义一个在地址0x2002000的数组,然后把这个数组设成不准写入,再写入该数组进入中断MemManage去执行中断服务函数
main.c
#include "stdlib.h"
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/MPU/mpu.h"
#if !(__ARMCC_VERSION >= 6010050) /* 不是AC6编译器,即使用AC5编译器时 */
uint8_t mpudata[128] __attribute__((at(0X20002000))); /* 定义一个数组 */
#else
uint8_t mpudata[128] __attribute__((section(".bss.ARM.__at_0X20002000"))); /* 定义一个数组 */
#endif
int main(void)
{
uint8_t key = 0;
uint8_t t = 0;
sys_cache_enable(); /* 打开L1-Cache */
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(240, 2, 2, 4); /* 设置时钟, 480Mhz */
delay_init(480); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
printf("\r\n\r\nMPU closed!\r\n"); /* 提示MPU关闭 */
mpu_memory_protection();
while (1)
{
key = key_scan(0);
if (key == WKUP_PRES) /* 使能MPU保护数组 mpudata */
{
mpu_set_protection(0X20002000, MPU_REGION_SIZE_128B, MPU_REGION_NUMBER0, MPU_INSTRUCTION_ACCESS_ENABLE, MPU_REGION_PRIV_RO_URO,
MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_NOT_CACHEABLE, MPU_ACCESS_BUFFERABLE); /* 只读,禁止共用,禁止cache,允许缓冲 */
printf("MPU open!\r\n"); /* 提示MPU打开 */
}
else if (key == KEY0_PRES) /* 向数组中写入数据,如果开启了MPU保护的话会进入内存访问错误! */
{
printf("Start Writing data...\r\n");
sprintf((char *)mpudata, "MPU test array %d", t);
printf("Data Write finshed!\r\n");
}
else if (key == KEY1_PRES) /* 从数组中读取数据,不管有没有开启MPU保护都不会进入内存访问错误! */
{
printf("Array data is:%s\r\n", mpudata);
}
else
{
delay_ms(10);
}
t++;
if ((t % 50) == 0)
{
LED0_TOGGLE(); /* LED0取反 */
}
}
}
mpu.c
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/MPU/mpu.h"
/**
* @brief 设置某个区域的MPU保护
* @param baseaddr: MPU保护区域的基址(首地址)
* @param size:MPU保护区域的大小(必须是32的倍数,单位为字节)
* @param rnum:MPU保护区编号,范围:0~15,最大支持16个保护区域
* @param de:禁止指令访问;0,允许指令访问;1,禁止指令访问
* @param ap:访问权限,访问关系如下:
* @arg 0,无访问(特权&用户都不可访问)
* @arg 1,仅支持特权读写访问
* @arg 2,禁止用户写访问(特权可读写访问)
* @arg 3,全访问(特权&用户都可访问)
* @arg 4,无法预测(禁止设置为4!!!)
* @arg 5,仅支持特权读访问
* @arg 6,只读(特权&用户都不可以写)
* @note 详见:STM32H7编程手册.pdf,4.6.6节,Table 91.
* @param sen:是否允许共用;0,不允许;1,允许
* @param cen:是否允许cache;0,不允许;1,允许
* @param ben:是否允许缓冲;0,不允许;1,允许
* @retval 0, 成功; 1, 错误;
*/
uint8_t mpu_set_protection(uint32_t baseaddr, uint32_t size, uint32_t rnum, uint8_t de, uint8_t ap, uint8_t sen, uint8_t cen, uint8_t ben)
{
MPU_Region_InitTypeDef mpu_region_init_handle; /* MPU初始化句柄 */
HAL_MPU_Disable(); /* 配置MPU之前先关闭MPU,配置完成以后在使能MPU */
mpu_region_init_handle.Enable = MPU_REGION_ENABLE; /* 使能该保护区域 */
mpu_region_init_handle.Number = rnum; /* 设置保护区域 */
mpu_region_init_handle.BaseAddress = baseaddr; /* 设置基址 */
mpu_region_init_handle.Size = size; /* 设置保护区域大小 */
mpu_region_init_handle.SubRegionDisable = 0X00; /* 禁止子区域 */
mpu_region_init_handle.TypeExtField = MPU_TEX_LEVEL0; /* 设置类型扩展域为level0 */
mpu_region_init_handle.AccessPermission = ap; /* 设置访问权限 */
mpu_region_init_handle.DisableExec = de; /* 是否允许指令访问 */
mpu_region_init_handle.IsShareable = sen; /* 是否允许共用 */
mpu_region_init_handle.IsCacheable = cen; /* 是否允许cache */
mpu_region_init_handle.IsBufferable = ben; /* 是否允许缓冲 */
HAL_MPU_ConfigRegion(&mpu_region_init_handle); /* 配置MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); /* 开启MPU */
return 0;
}
/**
* @brief 设置需要保护的存储块
* @note 必须对部分存储区域进行MPU保护,否则可能导致程序运行异常
* 比如MCU屏不显示,摄像头采集数据出错等等问题
* @param 无
* @retval 无
*/
void mpu_memory_protection(void)
{
/* 保护整个DTCM,共128K字节,允许指令访问,禁止共用,允许cache,允许缓冲 */
mpu_set_protection(0x20000000, MPU_REGION_SIZE_128KB, MPU_REGION_NUMBER1, MPU_INSTRUCTION_ACCESS_ENABLE,
MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_CACHEABLE, MPU_ACCESS_BUFFERABLE);
/* 保护整个AXI SRAM,共512K字节,允许指令访问,禁止共用,允许cache,允许缓冲 */
mpu_set_protection(0x24000000, MPU_REGION_SIZE_512KB,MPU_REGION_NUMBER2, MPU_INSTRUCTION_ACCESS_ENABLE,
MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_CACHEABLE, MPU_ACCESS_BUFFERABLE);
/* 保护整个SRAM1~SRAM3,共288K字节,允许指令访问,禁止共用,允许cache,允许缓冲 */
mpu_set_protection(0x30000000, MPU_REGION_SIZE_512KB,MPU_REGION_NUMBER3, MPU_INSTRUCTION_ACCESS_ENABLE,
MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_CACHEABLE, MPU_ACCESS_BUFFERABLE);
/* 保护整个SRAM4,共64K字节,允许指令访问,禁止共用,允许cache,允许缓冲 */
mpu_set_protection(0x38000000, MPU_REGION_SIZE_64KB, MPU_REGION_NUMBER4, MPU_INSTRUCTION_ACCESS_ENABLE,
MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_CACHEABLE, MPU_ACCESS_BUFFERABLE);
/* 保护MCU LCD屏所在的FMC区域,,共64M字节,允许指令访问,禁止共用,禁止cache,禁止缓冲 */
mpu_set_protection(0x60000000, MPU_REGION_SIZE_64MB, MPU_REGION_NUMBER5, MPU_INSTRUCTION_ACCESS_ENABLE,
MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_NOT_CACHEABLE, MPU_ACCESS_NOT_BUFFERABLE);
/* 保护SDRAM区域,共64M字节,允许指令访问,禁止共用,允许cache,允许缓冲 */
mpu_set_protection(0XC0000000, MPU_REGION_SIZE_64MB, MPU_REGION_NUMBER6, MPU_INSTRUCTION_ACCESS_ENABLE,
MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_CACHEABLE, MPU_ACCESS_BUFFERABLE);
/* 保护整个NAND FLASH区域,共256M字节,禁止指令访问,禁止共用,禁止cache,禁止缓冲 */
mpu_set_protection(0X80000000, MPU_REGION_SIZE_256MB, MPU_REGION_NUMBER7, MPU_INSTRUCTION_ACCESS_DISABLE,
MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_NOT_CACHEABLE, MPU_ACCESS_NOT_BUFFERABLE);
}
/**
* @brief MemManage错误处理中断
* @note 进入此中断以后,将无法恢复程序运行!!
*
* @param 无
* @retval 无
*/
void MemManage_Handler(void)
{
LED1(0); /* 点亮LED1(GREEN LED) */
printf("Mem Access Error!!\r\n"); /* 输出错误信息 */
delay_ms(1000);
printf("Soft Reseting...\r\n"); /* 提示软件重启 */
delay_ms(1000);
NVIC_SystemReset(); /* 软复位 */
}