DKENNY 发表于 2024-8-15 16:23

探索MPU:功能解析、寄存器配置与编程应用

本帖最后由 DKENNY 于 2024-8-15 16:43 编辑

#申请原创# @21小跑堂

最近学习了有关MPU的内容,本帖将对MPU的功能,相关寄存器的配置,以及在实际编程中如何运用MPU这三个方面作一个简单的分享。
## MPU 的功能内存保护单元(MPU)能够将内存划分为多个区域,并为每个区域设定不同的访问权限,从而提升系统的可靠性。它的主要用途包括:- 区分特权用户和普通用户的访问区域,这样可以提高操作系统的稳定性。- 设置只读区域,以防止重要数据被误修改。- 监测堆和栈的溢出情况,以确保系统安全。
## MPU相关寄存器(基地址:0xE000ED90)MPU配备了一系列寄存器。接下来,我们将结合这些寄存器详细阐述MPU的功能。寄存器组如下表所示。
寄存器名类型复位值寄存器功能描述
MPU_TYPEROAMPU 类型寄存器
MPU_CTRLRW0x00000000MPU控制寄存器
MPU_RNRRW-MPU区域号寄存器(region号)
MPU_RBARRW-MPU区域基地址寄存器
MPU_RASRRW-MPU区域属性和大小寄存器
MPU_RBAR_A1RW-MPU_RBAR的别名1
MPU_RASR_A1RW-MPU_RASR的别名1
MPU_RBAR_A2RW-MPU_RBAR的别名2
MPU_RASR_A2RW-MPU_RASR的别名2
MPU_RBAR_A3RW-MPU_RBAR的别名3
MPU_RASR_A3RW-MPU_RASR的别名3

下面详细介绍各个寄存器。
### MPU 类型寄存器(MPU_TYPE,地址:0xE000ED90)
名称
位段功能描述

Bits保留
IREGIONbitsMPU 支持的指令 region 数量。因为 ARMv7‐M 只使用单个统一的 MPU,此位段一直为0
DREGIONbitsMPU支持的区域数量。如果该字段的读数为0,则处理器不实现MPU

Bits保留位
SEPARATEbit是否支持单独的指令和数据地址映射。

MPU寄存器是只读的,我们主要关注DREGION位。通过检查这个位,我们可以确定是否启用了MPU。如果这个字段的读取值为零,那么就意味着MPU没有被使用。如果MPU被启用,那么通常情况下,它会返回MPU支持的区域数量,Cortex_M4最多可以支持8个区域。
可使用jlink读取该寄存器地址,以查看该芯片内核是否支持MPU,如下是APM32F407的读取情况:

可以看到读取的数据是0x800,对应的DREGION值为8,说明MPU支持的region最大数量为8。
### MPU控制寄存器(MPU_CTRL,地址:0xE000ED94)
名称位段功能描述

Bits保留
PRIVDEFENAbit设置特权访问模式下MPU的策略1=启用默认内存映射作为特权访问的背景区域。0=禁用默认内存映射,超出制定区域的任何数据或指令访问都将引起错误。
HFNMIENAbit设置在不可屏蔽中断(NMI)和硬 件中断中MPU策略。1= 在不可屏蔽中断和硬件中断服务函数中MPU依然有效0=在不可屏蔽中断和硬件中断服务函数中MPU依然失效
ENABLEbitMPU 使能位,1=使能,0=不使能

1. 寄存器概述:   - 在MPU_CTRL寄存器中,ENABLE和HFNMIENA位的作用已有明确说明,因此不再详细讨论。
2. 重点讨论PRIVDEFEN位:   - Cortex内核具有两种执行模式:特权模式和用户模式。   - 特权模式:具有更高的访问权限。   - 用户模式:权限较低,限制较多。
3. MPU的配置:   - PRIVDEFENA位用于配置特权模式下的MPU访问策略。   - 当MPU启用时,内存空间可以被划分为1到8个区域(region0~region7)。   - 一些内存区域可能未被划分,这些未划分的区域称为背景区域(region-1)。
4. PRIVDEFENA位的作用:   - 当PRIVDEFENA位设为1时:   - 允许特权模式访问背景区域(region-1)。   - 当PRIVDEFENA位设为0时:   - 禁止特权模式访问背景区域(region-1)。
### MPU区域号寄存器(MPU_RNR,地址:0xE000ED98)
名称位段功能描述

Bits保留
REGIONbits指定设置的内存区域

1. MPU的区域配置:   - MPU支持配置1到8个区域(region)。   - 每个区域的设置是独立的,可以根据需要进行个别配置。
2. 配置步骤:   - 在进行区域配置之前,必须先通过MPU_RNR寄存器选择要设置的区域编号。   - 选择好区域编号后,再对MPU_RBAR和MPU_RASR寄存器进行配置。
### MPU区域基地址寄存器(MPU_RBAR,地址:0xE000ED9C)
名称位段功能描述
ADDRbits区域的基地址
VALIDbit决定REGION字段设置的区域编 号是否覆盖MPU_RNR寄存器设置的区域编号
REGIONbitsMPU 区域编号复写字段

1. ADDR位段:   - 位段:bits。   - 功能描述:存储区域的基地址。
2. VALID位段:   - 位段:bit。   - 功能描述:决定REGION字段设置的区域编号是否覆盖MPU_RNR寄存器设置的区域编号。
3. REGION位段:   - 位段:bits。   - 功能描述:用于复写MPU区域编号。
4. 寄存器的便利性:   - 通过设置REGION为区域编号,VALID为1,提供了一种更为便捷的方式来修改区域编号。   - 这种方式可以更快地设置区域编号。
### MPU区域属性和大小寄存器(MPU_RASR, 地址:0xE000EDA0)
名称位段功能描述

bits保留
XNbits禁止取指

bits保留
APbits访问权限

bits保留
TEXbits扩展类型
Sbits是否可共享,1=可共享,0=不可 共享
Cbits(Cachable)可缓存,1= 可缓存,0=不可缓存。
Bbits(Bufferable)可传冲1=可缓冲,0=不可缓冲
SRDbits子区域使能位,对于256字节或更大的区域,该字段的每个位控制是否启用八个相等的子区域

Bits保留位
SIZEbits区域的大小
ENABLEbit区域使能位,1,使能区域。0,禁用区域

1. MPU_RASR寄存器简介- 功能:MPU_RASR寄存器主要负责配置MPU区域的属性。- 重要性:这是一个关键的过程,涉及许多需要深入理解的概念。
2. SRD位的作用- 子区域配置:SRD位用于配置子区域。- 区域划分:    - 所有超过128字节的区域被划分为8个等大小的子区域。    - 每个子区域的大小相同。
3. SRD的位解释- 位段:    - SRD寄存器的8位:从第8位到第15位。- 控制功能:    - 如果设置为1:启用相应的子区域(region)。    - 如果设置为0:禁用相应的子区域(region)。
区域属性B、C、S、TEX的设置,详细解释如下表。
TEXCB存储器类型描述可共享
00000严格按照顺序严格按照顺序可共享
00001设备共享的设备可共享
00010普通外部内存或内部内存,写通型内存,没有写分配由S位决定
00011普通外部内存或内部内存,写回型内存,没有写分配由S位决定
00100普通外部内存或内部内存,Non-cachable型内存由S位决定
00101保留保留,保留
00110IMPL EMENTATI ON DEFINEDIMPL EMENTATI ON DEFINEDIMPL EMENTATIONDEFINED
00111普通外部内存或内部内存,写回型内存,有读、写分配由S位决定
01000设备不可共享设备不可共享
01001保留保留保留
0101X保留保留保留
011XX保留保留保留
1BBAA普通带缓存的内存,AA,针对内部内存,BB针对外部内存,由S位决定

1. 内存特性:   - 写通型内存(Write-Through):   - 当Cache被激活,且内存A被设定为写通型时,CPU向内存A写入数据。如果Cache中已经为内存A的数据分配了对应空间,那么数据会同时写入Cache和内存。
   - 写回型内存(Write-Back):   - 当Cache被激活,且内存A被设定为写回型时,CPU向内存A写入数据。如果Cache中已经为内存A的数据分配了对应空间,那么数据会被写入Cache,但不会立刻写入内存。
   - 非缓存(Non-cachable):   - 这种类型的内存在CPU进行读写操作时不会经过Cache。
2. Cache策略:   - 读分配(Read Allocate):   - Cache被激活时会启用。意味着当CPU需要读取内存B的数据时,如果在Cache中找不到,则会直接从内存中读取,并在Cache中备份。
   - 写分配(Write Allocate):   - 功能与读分配类似,即在Cache被激活时,如果进行写操作且Cache中没有对应数据,则会从内存中读取数据到Cache后再进行写入。
3. TEX配置:   - 当TEX的最高位设为1时,可以分别配置外部内存(片外内存)和内部内存(片内内存)的属性。   - 此时,TEX的后两位(BB)用于设定外部内存的属性,而C和B位(AA)用于设定内部内存的属性。
无论是AA还是BB,它们的每个数值都有相同的含义,如下表所示。
AA 或 BB编码可缓存策略
00不可缓存
01写回,读分配和写分配
10写通,读分配,无写分配
11写回,读分配,无写分配

访问权限AP,字段AP用于设置访问权限,同样我们用一张表来说明该位,如下表所示。
AP特权用户访问权限普通用户访问权限注意事项
000不可访问不可访问任何访问都会生成权限错误
001Read/write不可访问只允许特权用户访问
010Read/writeRead-only非特权用户只读
011Read/writeRead/write完全访问
100UNPREDICTABLEUNPREDICTABLE保留
101Read-only不可访问特权用户只读
110Read-onlyRead-only只读
111Read-onlyRead-only只读

MPU的设置的主要任务就是配置MPU_RASR寄存器,MPU_RASR寄存器寄存器主要分为区域属性设置和访问权限设置。
## MPU的使用MPU的配置流程可以参考如下的流程图:

如下是一个简单的MPU配置例程代码,配套使用的开发板是APM32F407IG。

Main函数实现:
void MPU_Config(void)
{
    ARM_MPU_Disable();
    MPU->RBAR = ARM_MPU_RBAR(0,0x08004000);
    MPU->RASR = ARM_MPU_RASR(0,ARM_MPU_AP_PRO,0,0,1,1,0,ARM_MPU_REGION_SIZE_1KB);

    ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk);

    NVIC_EnableIRQ(MemoryManagement_IRQn);
}

void USARTInit(void)
{
    /* USART Initialization */
    USART_Config_T usartConfigStruct;

    /* USART configuration */
    USART_ConfigStructInit(&usartConfigStruct);
    usartConfigStruct.baudRate = 115200;
    usartConfigStruct.mode = USART_MODE_TX_RX;
    usartConfigStruct.parity = USART_PARITY_NONE;
    usartConfigStruct.stopBits = USART_STOP_BIT_1;
    usartConfigStruct.wordLength = USART_WORD_LEN_8B;
    usartConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;

    /* COM1 init*/
    APM_COMInit(COM1, &usartConfigStruct);
}

/*!
* @brief    Main program
*
* @param   None
*
* @retval    None
*/
int main(void)
{
    USARTInit();

    MPU_Config();

    *(uint32_t *)0x08004000 = 0x12;

    while (1)
    {
    }
}
其中ARM相关的MPU操作函数在其相关的内核文件中,可以自行去了解,这里就不介绍了。
中断服务函数实现:/*!
* @brief    This function handles Memory Manage exception
*
* @param   None
*
* @retval    None
*
*/
void MemManage_Handler(void)
{
    /* Go to infinite loop when Memory Manage exception occurs */
    while (1)
    {
    }
}

其中涉及到宏定义参数介绍如下:#define ARM_MPU_RBAR(Region, BaseAddress)- Region,MPU区域编号,最多可以设置8个Region ,取值范围为(0到7)- BaseAddress,区域的起始地址。
#define ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size)
宏ARM_MPU_RASR与宏ARM_MPU_RBAR分别用于设置MPU_RBAR寄存器和MPU_RASR寄存器。我们知道宏定义中每个参数的含义即可,其他工作有宏定义自动完成。宏ARM_MPU_RASR的参数介绍如下:- DisableExec,禁用区域(region),1=禁用指定的区域,0=启用指定的区域。- AccessPermission,设置数据访问权限。
#define ARM_MPU_AP_NONE 0u
#define ARM_MPU_AP_PRIV 1u
#define ARM_MPU_AP_URO2u
#define ARM_MPU_AP_FULL 3u
#define ARM_MPU_AP_PRO5u
#define ARM_MPU_AP_RO   6u
通过宏定义的名字我们可以知道设置的访问权限。- TypeExtField、IsShareable、IsCacheable、IsBufferable,依次设置MPU_RASR寄存器的TEX、S、C、B字段。- SubRegionDisable,禁用次区域。1=禁用,0=启用次区域。- Size,区域大小。
在配置MPU(Memory Protection Unit)区域时,有两个关键点需要注意:
1. 区域重叠:如果两个或多个MPU区域有重叠部分,那么区域号较大的设置将会覆盖区域号较小的设置。这是因为在处理重叠区域时,MPU会优先考虑区域号较大的设置。例如,如果区域0和区域1有重叠,那么在重叠部分,区域1的设置将会被使用。
2. 区域号的最大值:在配置MPU区域时,区域号的值必须在有效范围内,通常是0到7(包含)。如果超过这个范围,可能会导致不可预测的错误。这是因为MPU硬件只设计了固定数量的区域(在这个例子中是8个),超出这个范围的区域号是无效的。
总的来说,这两点都是为了确保MPU配置的正确性和系统的稳定性。
上面的例程代码主要实现了以下功能:
- MPU_Config(void):这个函数用于配置内存保护单元(MPU)。在函数内部,首先禁用了MPU,然后设置了一个具有特定属性的内存区域,这个区域的基地址是0x08004000,大小是1KB,访问权限被设置为仅允许特权级别的访问。最后,启用了MPU和默认的内存管理错误中断。
- USARTInit(void):这个函数用于初始化USART(通用同步异步收发器)。在函数内部,首先定义了一个USART的配置结构体,然后初始化了这个结构体,并设置了一些参数,如波特率、模式、奇偶校验、停止位、字长和硬件流控制。最后,使用这个配置结构体初始化了COM1。
- main(void): 这是程序的主函数。在函数内部,首先调用了USARTInit()和MPU_Config()函数,然后试图向地址0x08004000写入一个值。由于这个地址被MPU设置为只允许特权级别的访问,所以这个写操作会引发内存管理错误中断,然后程序将会进入MemManage_Handler()函数。
- MemManage_Handler(void):这是一个中断处理函数,用于处理内存管理错误中断。在这个函数内部,程序进入了一个无限循环,也就是说,一旦发生了内存管理错误中断,程序就会停止在这个函数中。
实验现象由于在main()函数中尝试向受保护的内存地址写入数据,这将触发内存管理错误中断,然后程序将进入MemManage_Handler()函数,并在该函数中无限循环。因此,程序的实际效果将是一旦开始运行,就会“冻结”在MemManage_Handler()函数中。


如上图的实验现象正好符合我们设置的需求。

## 问题
1、如何区分特权用户和普通用户的访问区域?
具体来说,我们可以通过MPU给特权用户和普通用户设定不同的内存区域,分配不同的访问权限。比如,特权用户可以访问一个既能读也能写的内存区域,而普通用户只能访问一个只能读或只能写的区域。这样,如果普通用户试图进入特权用户的内存区域,系统就会直接拒绝这个请求,保护特权用户的数据不被非法访问。

在实际编程时,我们需要写一些代码来设置MPU,来区分不同用户可以访问的区域。这通常涉及到对MPU内部设置的读写操作,以定义哪些区域允许访问,哪些区域被限制。

2、如何使用MPU监测堆和栈的溢出情况以确保系统安全?
- 1.配置MPU区域
    - 划分内存区域: 将内存划分为多个区域,分别用于极其重要的任务(如操作系统、关键应用程序、堆、栈等)。
    - 设置属性: 为每个区域设置访问权限。例如,可以将堆和栈区域设为可读写,但不可执行。其他重要区域,例如程序代码区域,应该为只读,并且不可写。
- 2.监测堆和栈的使用
    - 栈溢出监测: 在栈的底部设置一个保护区域,通常是一个无效的内存地址。通过监测这些地址的访问情况,可以检测到栈的溢出。如果系统访问了保护区域,可以及时报警并采取措施(如重启系统)。
    - 堆溢出监测: 在堆内存分配中,可以在每次分配之前和之后插入控制代码,以检查当前的堆指针和堆边界。如果堆指针越界并触及保护区域,则表示发生了堆溢出,可以记录相关信息并进行处理。
- 3.硬件异常处理
    - 异常处理: 通过MPU,如果系统接收到访存错误的异常(例如访问被保护的内存区域),可以触发相应的中断服务程序(ISR)。在ISR中,可以实现错误日志记录、系统重启或进入安全模式等功能。

## 总结
这篇帖子主要介绍了内存保护单元(MPU)的核心功能及其相关寄存器的详细信息。MPU 是一种硬件组件,主要用于系统内存管理,以提高系统的安全性和稳定性。MPU通过为不同的内存区域设置访问权限,限制应用程序对系统内存的访问,从而防止错误或恶意代码的运行对系统造成影响。
同时,内存保护单元(MPU)是现代计算系统中不可或缺的组成部分,它通过提供灵活的内存访问控制和保护机制,增强了系统的安全性和稳定性。合理配置MPU对于构建高可靠、高安全性的嵌入式和实时系统至关重要。

以上就是本次分享的全部内容,有关MPU的相关内容,欢迎各位讨论交流。

附件:




kai迪皮 发表于 2024-8-20 13:53

学到了,顶好文
页: [1]
查看完整版本: 探索MPU:功能解析、寄存器配置与编程应用