一、介绍MPU

        STM32H743的MPU(Memory Protection Unit,内存保护单元)是内核级硬件安全机制,核心作用是划分内存区域限制访问权限(比如只读/只写/可执行)、校验访问主体(CPU特权/非特权模式),防止程序错误(如数组越界、野指针)或恶意代码破坏关键内存(如内核数据、外设寄存器),是提升系统稳定性和安全性的核心组件。

        内核外设,所以在STM32的参考手册中没有相关介绍。需要去看ARM内核手册。

一、MPU的核心基础

1. MPU的归属与核心能力

        STM32H743基于ARM Cortex-M7内核,MPU是Cortex-M7内核的标配模块主要能力:

        划分最多8个主区域(Region) + 支持子区域(Subregion)细分(每个主区域可拆分为8个子区域);

        对每个区域设置访问权限(特权/非特权、读/写/执行);

        检测非法访问(如非特权代码写内核数据、执行RAM中的数据),触发硬件故障中断(HardFault)

        支持不同内存类型(Normal/Device/Strongly Ordered)的属性配置(匹配总线访问特性)。

2. 关键概念

术语

通俗解释

特权模式(Privileged)

内核的“管理员模式”,能访问所有内存/外设,通常是操作系统内核、中断服务程序运行的模式

非特权模式(Unprivileged)

内核的“普通用户模式”,只能访问MPU允许的内存,通常是应用任务运行的模式

主区域(Region)

MPU划分的最小独立内存块,最小粒度32字节,且地址必须是“区域大小的整数倍”

子区域(Subregion)

把1个主区域拆成8个相等的小块,可单独禁用某几个子区域(灵活适配不规则内存)

内存类型

Normal(普通内存,如SRAM)、Device(外设寄存器)、Strongly Ordered(严格有序)

XN(Execute Never)

“禁止执行”,标记该区域不能作为代码运行(防止RAM中注入恶意代码执行)

二、STM32H743的内存布局

        H743的核心内存区域,MPU保护的对象就是这些区域:

内存类型

地址范围(核心)

用途

保护需求示例

ITCM RAM

0x00000000 - 0x0007FFFF

指令缓存RAM(可执行)

禁止非特权写、允许执行

DTCM RAM

0x20000000 - 0x2007FFFF

数据缓存RAM

禁止执行(XN)、特权可写

AXI SRAM

0x24000000 - 0x240FFFFF

高速数据RAM

非特权只读、禁止执行

SRAM1/2/3

0x30000000 - 0x300FFFFF

通用数据RAM

按任务划分访问权限

外设寄存器

0x40000000 - 0x5FFFFFFF

外设控制/状态寄存器

仅特权可写、禁止执行

Flash

0x08000000 - 0x08FFFFFF

程序存储

禁止非特权写、允许执行

三、MPU的核心配置步骤

        MPU配置的核心是“定义区域→设置权限→使能MPU”,以下是基于HAL库的极简示例(核心逻辑):

步骤1:禁用MPU(配置前必须)

MPU_Disable(); // 内核函数,禁用MPU

步骤2:配置一个内存区域(示例:保护AXI SRAM)

MPU_Region_InitTypeDef MPU_InitStruct = {0};

// 1. 配置区域基本信息
MPU_InitStruct.Enable = MPU_REGION_ENABLE;    // 使能该区域
MPU_InitStruct.Number = MPU_REGION_NUMBER0;   // 选择第0个主区域
MPU_InitStruct.BaseAddress = 0x24000000;      // 区域起始地址(AXI SRAM起始)
MPU_InitStruct.Size = MPU_REGION_SIZE_1MB;    // 区域大小(1MB,匹配AXI SRAM)
MPU_InitStruct.SubRegionDisable = 0x00;       // 不禁用任何子区域(8个子区域都启用)
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; // 内存类型扩展字段(Normal内存)

// 2. 配置访问权限
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW_UNPRIV_RO; // 特权可读写、非特权只读
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;    // 禁止执行(XN=1)
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;          // 非共享(SRAM独占)
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;              // 可缓存(提升性能)
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;            // 可缓冲

// 3. 应用配置
HAL_MPU_ConfigRegion(&MPU_InitStruct);

步骤3:使能MPU(配置完成后)

// 使能MPU,同时允许特权模式访问默认区域(未配置的区域)
MPU_Enable(MPU_PRIVILEGED_DEFAULT);

四、避坑点

1. 区域地址/大小必须对齐

        起始地址必须是“区域大小的整数倍”(比如配置1MB区域,地址必须是0x24000000、0x24100000等,不能是0x24000001);

        区域大小只能选内核支持的固定值(32B、64B、128B、256B、512B、1KB、2KB…最大4GB),不能自定义。

2. 禁用MPU后,所有保护失效

        调试阶段如果触发HardFault,可先禁用MPU排查是否是MPU权限配置错误;

        产品发布前必须确保MPU使能,且权限配置严谨。

3. 中断服务程序(ISR)运行在特权模式

        ISR默认是特权模式,所以MPU对ISR的限制仅看“特权权限”,需确保ISR访问的内存有特权权限

        如果ISR需要写某块RAM,该区域的特权权限必须设为RW(读写)。

4. XN位(禁止执行)必须开启数据区

        所有数据RAM(DTCM、AXI SRAM、SRAM1/2/3)都要设置DisableExec = 1,防止黑客将代码注入RAM并执行;

        Flash区域通常开启执行(DisableExec=0),但要禁止非特权写。

5. 触发HardFault?先查MPU

        如果程序跑飞触发HardFault,优先检查:

        是否访问了MPU禁止的区域(如非特权写只读区域);

        区域地址/大小是否对齐;

        权限配置是否与访问行为冲突(如执行RAM中的数据)。

五、使用建议

简单场景

        配置“保护Flash禁止非特权写”+“保护RAM禁止执行”,这是最基础且必要的配置;

        用调试器(如ST-Link)查看HardFault的栈信息,定位MPU违规的具体地址。

结合RTOS使用

        如果用FreeRTOS等RTOS,可给不同任务分配非特权模式,通过MPU隔离任务内存,防止一个任务崩溃影响整个系统;

        RTOS的内核部分运行在特权模式,仅开放必要的内存区域给非特权任务

二、什么场景下使用MPU

        STM32H743的MPU(内存保护单元)不是“所有场景都必须用”,而是针对需要提升系统稳定性、安全性、可靠性的场景 设计的硬件机制。

一、核心场景1:防止程序错误导致的系统崩溃(最常用)

适用场景

        程序中有数组越界、野指针、栈溢出等常见bug;

        复杂项目(如多任务、大代码量)中,难以通过纯软件排查所有内存错误;

        产品要求“不能因为局部代码错误导致整机死机”(如工业控制、智能家居)。

具体例子
  1. 保护关键数据区:比如把存储系统参数的SRAM区域配置为“仅特权模式可写”,非特权的应用代码即使出错,也无法篡改关键参数(如校准值、设备ID);
  2. 禁止RAM执行代码:所有数据RAM(DTCM、AXI SRAM)开启XN(禁止执行),即使野指针把数据覆盖成指令,CPU也无法执行,避免程序跑飞成随机指令;
  3. 限制栈大小:给任务栈分配独立的MPU区域,一旦栈溢出访问到区域外内存,立即触发HardFault,提前定位问题(而非系统静默崩溃)。
配置思路
// 示例:保护系统参数区(0x30000000-0x30000FFF,4KB)
MPU_InitStruct.BaseAddress = 0x30000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_4KB;
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW_UNPRIV_RO; // 非特权只读
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;

二、核心场景2:隔离特权/非特权代码(适配RTOS/多任务)

适用场景

        使用FreeRTOS、RT-Thread等RTOS,需要“任务隔离”(一个任务崩溃不影响其他任务/内核);

        系统分为“内核层(特权)”和“应用层(非特权)”,需限制应用层的访问范围;

        第三方代码/用户自定义代码运行在非特权模式,防止其破坏系统内核。

具体例子
  1. RTOS任务隔离:给每个应用任务分配独立的MPU区域(如任务栈、任务私有数据),仅开放该任务需要的内存/外设,即使任务出错,也只能触发自身的HardFault,内核和其他任务仍能正常运行;
  2. 内核保护:把RTOS内核代码/数据所在的Flash/SRAM配置为“仅特权模式访问”,非特权任务无法修改内核的TCB(任务控制块)、调度器等关键数据;
  3. 外设隔离:比如让“电机控制任务”仅能访问TIM/PWM外设寄存器,“通信任务”仅能访问UART/SPI外设寄存器,防止越权操作外设。
配置思路
// 示例:非特权任务仅能访问UART4外设寄存器(0x40004C00-0x40004FFF,16KB)
MPU_InitStruct.BaseAddress = 0x40004C00;
MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW_UNPRIV_RW; // 非特权可读写
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; // Device内存(外设寄存器)

三、核心场景3:提升系统安全性(防篡改/防注入)

适用场景

        产品面向商用/工业场景,需要防止恶意代码注入、关键代码/数据被篡改;

        符合功能安全要求(如IEC 61508),需通过硬件机制降低故障风险;

        存储敏感数据(如密钥、密码)的内存区域需要严格保护。

具体例子
  1. Flash写保护:把存储程序的Flash区域配置为“仅特权模式可写(烧录/升级时),日常运行时设为只读”,防止非特权代码篡改程序;
  2. 敏感数据区只读:把存储密钥的RAM区域配置为“仅特权模式可写(初始化时),运行时设为只读”,非特权代码只能读取、无法修改;
  3. 禁止外设寄存器非特权写:所有外设寄存器默认设为“仅特权可写”,仅开放必要外设给非特权代码,防止恶意修改外设配置(如关闭看门狗)。
配置思路
// 示例:Flash程序区(0x08000000-0x080FFFFF,1MB)日常只读
MPU_InitStruct.BaseAddress = 0x08000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_1MB;
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RO_UNPRIV_RO; // 全只读
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; // 允许执行(程序区)

四、核心场景4:适配不同内存类型的访问规则

适用场景

        系统中混合使用Normal(SRAM)、Device(外设)、Strongly Ordered(特殊外设)内存;

        需要优化内存访问性能(如开启缓存),同时保证外设访问的正确性(禁止缓存)。

具体例子
  1. SRAM开启缓存:Normal类型的SRAM配置为“可缓存、可缓冲”,提升访问速度;
  2. 外设寄存器禁止缓存:Device类型的外设寄存器配置为“不可缓存、不可缓冲”,确保读写操作立即生效(避免缓存导致的外设控制延迟);
  3. 严格有序内存保护:对DMA、以太网等高速外设的内存区域配置为“共享、不可缓存”,防止数据一致性问题。
配置思路
// 示例:外设寄存器区(0x40000000-0x400FFFFF,16MB)禁止缓存
MPU_InitStruct.BaseAddress = 0x40000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_16MB;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; // Device内存
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; // 禁止缓存
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; // 禁止缓冲
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW_UNPRIV_RO;

五、什么时候可以不用MPU?

  1. 简单的裸机项目(如LED闪烁、串口打印),代码量小、无内存安全风险;
  2. 纯调试阶段,优先排查代码逻辑bug,再考虑MPU保护;
  3. 对性能要求极致且无安全需求(如纯算法运算),MPU的少量开销可忽略,但建议至少开启“禁止RAM执行”。

总结:MPU使用场景的核心判断

只要你的项目满足以下任一条件,就建议使用MPU:

  • 代码量较大/多任务,担心内存错误导致崩溃;
  • 有安全/稳定要求(商用、工业、功能安全);
  • 使用RTOS,需要任务隔离;
  • 存储敏感数据/运行第三方代码。

二、寄存器

MPU的寄存器说明截取自《STM32H7编程手册》

列名

注解

Address

这个寄存器在处理器里的“地址”(你要操作这个寄存器,就得通过这个地址找到它)

Name

寄存器的名字(比如MPU_TYPE就是“MPU类型寄存器”)

Type

寄存器的操作类型:
- RO=只读(只能看,不能改);
- RW=读写(能看也能改)

Required privilege

操作这个寄存器需要的权限:这里都是“Privileged(特权级)”——只有系统级代码(比如内核)能操作,普通应用程序没权限

Reset value

处理器刚上电(复位)时,这个寄存器里默认的值

Description

这个寄存器是干嘛用的

MPU的作用是 给内存划分“区域”,给每个区域设置权限(比如哪些代码能读/写/执行这块内存),下面是实现这个功能的核心寄存器:

  • MPU_TYPE(地址0xE000ED90)
    • 类型是“只读”,复位值是0x00000800
    • 作用:告诉你这个处理器的MPU有啥功能(比如支持多少个内存区域),是“MPU的身份信息”
  • MPU_CTRL(地址0xE000ED94)
    • 类型是“读写”,复位值是0x00000000(默认是0,代表MPU没开启)
    • 作用:控制MPU的开关(比如写1开启MPU,写0关闭)
  • MPU_RNR(地址0xE000ED98)
    • 类型是“读写”
    • 作用:选择你要配置的“内存区域编号”(比如你要配置第1个区域,就往这里写1)
  • MPU_RBAR(地址0xE000ED9C)
    • 类型是“读写”
    • 作用:设置你选中的那个内存区域的起始地址(比如你要保护0x20000000开始的内存,就把这个地址写进来)
  • MPU_RASR(地址0xE000EDA0)
    • 类型是“读写”
    • 作用:设置这个内存区域的大小权限(比如这块内存是1KB,还是只能读不能写)

        剩下的MPU_RBAR_A1MPU_RASR_A1这些,是前面MPU_RBAR/MPU_RASR的“别名寄存器”——功能和对应的主寄存器一样,只是地址不同,方便快速配置多个区域。

比较重要的两个寄存器是:MPU_RASR 和控制寄存器。

MPU_RASR:

控制寄存器:

三、代码

/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-04-14     whj4674672   first version
 */
#include <rtthread.h>
#include "stm32h7xx.h"

/**
 * @brief       设置某个区域的MPU保护
 * @param       baseaddr:MPU保护区域的起始地址(虚拟地址)
 * @param       size:MPU保护区域的大小(以32位的方式,单位为字节),可取的值参考:CORTEX_MPU_Region_Size
 * @param       rnum:MPU保护区编号,范围:0~7,总共可指定8个保护区,可取的值参考:CORTEX_MPU_Region_Number
 * @param       de:禁止指令执行;0,允许指令执行;1,禁止指令执行
 * @param       ap:访问权限,取值范围参考:可取的值参考:CORTEX_MPU_Region_Permission_Attributes
 *   @arg       MPU_REGION_NO_ACCESS,无访问(特权&用户都不允许访问)
 *   @arg       MPU_REGION_PRIV_RW,仅支持特权读写访问
 *   @arg       MPU_REGION_PRIV_RW_URO,禁止用户写访问(特权可读写访问)
 *   @arg       MPU_REGION_FULL_ACCESS,全访问(特权&用户都可访问)
 *   @arg       MPU_REGION_PRIV_RO,仅支持特权读访问
 *   @arg       MPU_REGION_PRIV_RO_URO,只读(特权&用户都不允许写)
 *   @arg       更多:STM32F7参考手册,4.6节Table 89.
 * @param       sen  : 是否支持共享;MPU_ACCESS_NOT_SHAREABLE,不支持;MPU_ACCESS_SHAREABLE,支持
 * @param       cen  : 是否支持cache;MPU_ACCESS_NOT_CACHEABLE,不支持;MPU_ACCESS_CACHEABLE,支持
 * @param       ben  : 是否支持buffer;MPU_ACCESS_NOT_BUFFERABLE,不支持;MPU_ACCESS_BUFFERABLE,支持
 * @retval      0,成功.
 *              其他,失败.
 */
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;

    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.DisableExec = de;                             /* 是否允许指令执行 */
    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 = (uint8_t)ap;               /* 设置访问权限, */
    mpu_region_init_handle.IsShareable = sen;                            /* 是否共享 */
    mpu_region_init_handle.IsCacheable = cen;                            /* 是否开启cache? */
    mpu_region_init_handle.IsBufferable = ben;                           /* 是否开启buffer */
    HAL_MPU_ConfigRegion(&mpu_region_init_handle);                       /* 设置MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);                              /* 开启MPU */
    return 0;
}

/**
 * @brief       设置需要保护的存储器区域
 * @param       无
 * @note        ʺ对部分存储器区域进行MPU保护,防止误操作导致硬件异常
 *              因为LCD等不一定支持cache,避免存取显示数据错误等情况.
 */
int mpu_memory_protection(void)
{
    /* 保护第1个DTCM,共128K字节 */
    mpu_set_protection( 0x20000000,                 /* 基地址 */
                        MPU_REGION_SIZE_128KB,      /* 长度 */
                        MPU_REGION_NUMBER1, 0,      /* NUMER1,允许指令执行 */
                        MPU_REGION_FULL_ACCESS,     /* 全访问 */
                        MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共享 */
                        MPU_ACCESS_CACHEABLE,       /* 允许cache */
                        MPU_ACCESS_BUFFERABLE);     /* 允许缓存 */

    /* 保护第2个AXI SRAM,共512K字节 */
    mpu_set_protection( 0x24000000,                 /* 基地址 */
                        MPU_REGION_SIZE_512KB,      /* 长度 */
                        MPU_REGION_NUMBER2, 0,      /* NUMER2,允许指令执行 */
                        MPU_REGION_FULL_ACCESS,     /* 全访问 */
                        MPU_ACCESS_SHAREABLE,       /* 允许共享 */
                        MPU_ACCESS_CACHEABLE,       /* 允许cache */
                        MPU_ACCESS_NOT_BUFFERABLE); /* 禁止缓存 */
    
    /* 保护第3个SRAM1~SRAM3,共512K字节 */
    mpu_set_protection( 0x30000000,                 /* 基地址 */
                        MPU_REGION_SIZE_512KB,      /* 长度 */
                        MPU_REGION_NUMBER3, 0,      /* NUMER3,允许指令执行 */
                        MPU_REGION_FULL_ACCESS,     /* 全访问 */
                        MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共享 */
                        MPU_ACCESS_CACHEABLE,       /* 允许cache */
                        MPU_ACCESS_BUFFERABLE);     /* 允许缓存 */
                        
    /* 保护第4个SRAM4,共64K字节 */
    mpu_set_protection( 0x38000000,                 /* 基地址 */
                        MPU_REGION_SIZE_64KB,       /* 长度 */
                        MPU_REGION_NUMBER4, 0,      /* NUMER4,允许指令执行 */
                        MPU_REGION_FULL_ACCESS,     /* 全访问 */
                        MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共享 */
                        MPU_ACCESS_CACHEABLE,       /* 允许cache */
                        MPU_ACCESS_BUFFERABLE);     /* 允许缓存 */
                        
    /* 保护MCU LCD屏所在的FMC存储区域,共64M字节 */
    mpu_set_protection( 0x60000000,                 /* 基地址 */
                        MPU_REGION_SIZE_64MB,       /* 长度 */
                        MPU_REGION_NUMBER5, 0,      /* NUMER5,允许指令执行 */
                        MPU_REGION_FULL_ACCESS,     /* 全访问 */
                        MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共享 */
                        MPU_ACCESS_NOT_CACHEABLE,   /* 禁止cache */
                        MPU_ACCESS_NOT_BUFFERABLE); /* 禁止缓存 */
                        
    /* 保护SDRAM存储区域,共32M字节 */
    mpu_set_protection( 0xC0000000,                 /* 基地址 */
                        MPU_REGION_SIZE_32MB,       /* 长度 */
                        MPU_REGION_NUMBER6, 0,      /* NUMER6,允许指令执行 */
                        MPU_REGION_FULL_ACCESS,     /* 全访问 */
                        MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共享 */
                        MPU_ACCESS_CACHEABLE,       /* 允许cache */
                        MPU_ACCESS_BUFFERABLE);     /* 允许缓存 */

                        
    /* 保护第7个NAND FLASH存储区域,共256M字节 */
    mpu_set_protection( 0x80000000,                 /* 基地址 */
                        MPU_REGION_SIZE_256MB,      /* 长度 */
                        MPU_REGION_NUMBER7, 1,      /* NUMER7,禁止指令执行 */
                        MPU_REGION_FULL_ACCESS,     /* 全访问 */
                        MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共享 */
                        MPU_ACCESS_NOT_CACHEABLE,   /* 禁止cache */
                        MPU_ACCESS_NOT_BUFFERABLE); /* 禁止缓存 */
		return 0;					
}
INIT_BOARD_EXPORT(mpu_memory_protection);

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐