IOPMP 是 RISC-V 架构下针对外设 DMA/总线主控(Bus Master) 的物理内存保护机制,核心目标是约束外设对内存的访问权限,防止恶意或误操作的外设越权读写敏感内存区域。它的设计逻辑与 PMP(Physical Memory Protection,针对 CPU 核心的内存保护)一脉相承,但专门面向外设,由处于 M-Mode(机器模式)的 Secure Monitor 负责配置和管理。
结合你提供的链接内容,下面从 核心原理、使用前提、配置步骤、典型应用场景 四个维度,详细说明如何使用 IOPMP。
1、IOPMP 核心原理(理解使用逻辑的基础)
-
保护对象
- 针对具备 DMA 功能 或 Bus Master 接口 的外设(如网卡、存储控制器、DMA 控制器)。
- 这些外设可以不经过 CPU 核心,直接通过总线访问物理内存,是内存安全的重要风险点。
-
核心机制
- IOPMP 通过配置一组“权限规则”,定义每个外设允许访问的内存地址范围、访问类型(读/写/执行)。
- 所有外设的内存访问请求,都会经过 IOPMP 模块的校验:符合规则则放行,违反规则则拦截并上报异常(通常触发 M-Mode 中断)。
-
管理主体
- IOPMP 是特权级硬件模块,只有处于 M-Mode 的软件(如 Secure Monitor、Hypervisor 或裸机内核)有权限配置其规则寄存器。
- S-Mode(监督模式)或 U-Mode(用户模式)的软件无法直接修改 IOPMP 配置,确保规则的安全性。
2、使用 IOPMP 的前提条件
-
硬件支持
- 目标 SoC 必须集成 IOPMP 硬件模块,且外设的 Bus Master 接口需连接到 IOPMP 校验链路。
- 确认 RISC-V 内核的扩展支持(部分厂商会在 PMP 基础上扩展 IOPMP 功能)。
-
软件环境
- 运行在 M-Mode 的安全监控程序(Secure Monitor) 或 裸机操作系统内核,负责 IOPMP 的初始化、规则配置和异常处理。
- 明确外设的编号、总线 ID 与 IOPMP 规则的绑定关系(不同外设对应不同的规则集)。
3、IOPMP 配置与使用的核心步骤
IOPMP 的使用流程遵循 “初始化 → 规则配置 → 权限校验 → 异常处理” 的闭环,具体步骤如下:
步骤 1:IOPMP 模块初始化(M-Mode 执行)
-
使能 IOPMP 模块
- 写入 IOPMP 控制寄存器(如
iopmpcfg0),设置全局使能位(Enable),开启外设访问的校验功能。 - 配置校验模式:如“严格模式”(违反规则立即拦截)或“日志模式”(仅上报不拦截,用于调试)。
- 写入 IOPMP 控制寄存器(如
-
关联外设与规则集
- 基于外设的 Bus ID 或 设备编号,建立“外设 - IOPMP 规则”的映射关系。
- 例如:将网卡的 Bus ID=0x01 绑定到 IOPMP 规则 0~3,将存储控制器的 Bus ID=0x02 绑定到规则 4~7。
步骤 2:配置 IOPMP 访问规则(核心操作)
IOPMP 的规则配置与 PMP 类似,通过地址寄存器(IOPMPADDR) 和权限寄存器(IOPMPCFG) 定义内存访问的“白名单”。每条规则包含三个核心要素:地址范围 + 访问权限 + 匹配模式。
-
定义地址范围
- 写入
IOPMPADDR寄存器,设置内存区域的起始地址或结束地址(具体取决于匹配模式)。 - 支持两种匹配模式:
- 自然对齐模式:地址按 2^N 对齐,适用于连续的大内存块(如 DMA 缓冲区)。
- 任意范围模式:通过起始地址和长度定义非对齐区域(部分 IOPMP 实现支持)。
- 写入
-
配置访问权限
- 写入
IOPMPCFG寄存器,为当前规则设置权限位,常用权限包括:权限位 含义 R (Read) 允许外设读取该内存区域 W (Write) 允许外设写入该内存区域 X (Execute) 允许外设执行该内存区域(极少用,外设一般不执行代码) L (Lock) 锁定规则,锁定后无法修改(防止恶意篡改规则)
- 写入
-
规则优先级与覆盖
- IOPMP 规则按寄存器编号从低到高匹配,一旦匹配到某条规则,立即停止后续匹配。
- 建议先配置窄范围的严格规则,再配置宽范围的宽松规则,避免权限覆盖。
配置示例(伪代码)
// 假设:为网卡(Bus ID=0x01)配置规则,允许访问 0x80000000~0x8000FFFF 的 DMA 缓冲区 // 步骤1:绑定外设 Bus ID 到规则 0 iopmp_bus_map[0] = 0x01; // 步骤2:设置规则 0 的地址范围(0x80000000,自然对齐 64KB) IOPMPADDR0 = 0x80000000 >> 2; // 地址寄存器通常右移 2 位(按字对齐) // 步骤3:设置规则 0 的权限:允许读/写,不允许执行,不锁定 IOPMPCFG0 = IOPMPCFG_R | IOPMPCFG_W; // 步骤4:使能规则 0 IOPMPCFG0 |= IOPMPCFG_ENABLE;
步骤 3:外设访问与 IOPMP 校验
-
外设发起内存访问
- 当外设(如网卡)需要 DMA 传输数据时,会向总线发送内存访问请求(携带目标地址、访问类型、Bus ID)。
-
IOPMP 自动校验
- IOPMP 模块根据外设的 Bus ID 找到绑定的规则集,依次匹配规则:
- 若匹配到允许的规则 → 放行访问请求;
- 若未匹配到任何规则,或匹配到禁止的规则 → 拦截请求,并向 M-Mode 上报 IOPMP 异常中断。
- IOPMP 模块根据外设的 Bus ID 找到绑定的规则集,依次匹配规则:
步骤 4:IOPMP 异常处理(M-Mode 执行)
-
异常响应
- M-Mode 软件捕获 IOPMP 异常后,读取异常状态寄存器(如
iopmpcause),获取以下信息:- 违规外设的 Bus ID;
- 违规的内存地址;
- 违规的访问类型(读/写/执行)。
- M-Mode 软件捕获 IOPMP 异常后,读取异常状态寄存器(如
-
异常处理策略
- 调试阶段:打印违规信息,定位外设误操作或规则配置错误;
- 生产阶段:禁止违规外设的 Bus Master 功能,或重置外设,防止进一步的越权访问。
异常处理示例(伪代码)
void iopmp_exception_handler() { uint32_t bus_id = IOPMPCAUSE_BUS_ID; // 获取违规外设 Bus ID uint32_t addr = IOPMPCAUSE_ADDR; // 获取违规地址 uint32_t type = IOPMPCAUSE_TYPE; // 获取违规类型 printk("IOPMP Violation: Bus ID=%x, Addr=%x, Type=%s\n", bus_id, addr, (type == 0) ? "READ" : "WRITE"); // 处理:禁用该外设的 Bus Master 功能 peripheral_disable_bus_master(bus_id); }
4、IOPMP 典型应用场景
-
DMA 缓冲区隔离
- 为每个外设的 DMA 缓冲区配置独立的 IOPMP 规则,防止外设访问其他外设的缓冲区或系统敏感内存(如内核栈、页表)。
-
安全分区隔离
- 在异构系统中(如 TrustZone 类似的安全分区),通过 IOPMP 阻止非安全外设访问安全分区的内存,保障敏感数据(如密钥、证书)的安全。
-
外设权限最小化
- 遵循“最小权限原则”,只为外设配置完成功能所需的最小内存访问权限,降低攻击面。
5、使用 IOPMP 的关键注意事项
-
规则配置的安全性
- 所有 IOPMP 规则必须在 M-Mode 配置,且敏感规则建议设置
Lock位,防止被 S-Mode/U-Mode 软件篡改。 - 避免配置“允许访问全内存”的宽松规则,这会完全丧失 IOPMP 的保护意义。
- 所有 IOPMP 规则必须在 M-Mode 配置,且敏感规则建议设置
-
性能影响
- IOPMP 是硬件级校验,对性能的影响极小,但规则数量过多(如超过 16 条)可能会增加匹配延迟,建议按需精简规则。
-
与 PMP 的协同
- IOPMP 保护外设的内存访问,PMP 保护 CPU 核心的内存访问,两者结合才能实现 SoC 级的内存安全防护。
6 IOPMP寄存器描述
配置地址的寄存器定义(基于Nuclei IOPMP规范)
IOPMP 中与地址配置直接相关的寄存器,核心围绕 “内存域绑定”“地址规则定义”“匹配模式配置” 三大核心场景设计,均为内存映射寄存器,需通过 AHB-Lite 总线编程配置。以下是具体寄存器的定义、功能及关键特性,结合规范细节逐一说明:
6.1、核心地址配置寄存器分类与关联逻辑
地址配置寄存器按功能可分为 “内存域-条目绑定寄存器”“地址条目寄存器”“全局控制与错误日志寄存器” 三类,其关联逻辑为:
- 通过“内存域-条目绑定寄存器”定义每个内存域(MD)绑定的地址规则条目范围;
- 通过“地址条目寄存器”配置单个条目的地址范围、匹配模式及权限;
- 全局控制寄存器开启地址校验功能,错误日志寄存器记录地址配置或访问违规信息。
6.2、具体寄存器定义(含功能、位宽、关键位说明)
(1)内存域-条目绑定寄存器:MDFCFGx(Memory Domain Function Configuration Register)
核心功能
定义第 x 个内存域(MDx,x 为内存域索引,0~62,支持 1~63 个内存域)绑定的地址规则条目(Entry)起始索引和结束索引,建立“内存域-条目”的关联关系。
关键参数
- 寄存器地址:由 SoC 厂商定义(内存映射地址,通过 AHB-Lite 总线访问);
- 位宽:32 位(规范推荐,具体以硬件实现为准);
- 核心位定义(按功能分区):
位段范围 位段名称 功能描述 [15:0] ENTRY_CNT 该内存域绑定的地址条目数量(如值为 m 表示绑定 m 个条目),与起始索引配合确定条目范围 [23:16] ENTRY_START 该内存域绑定的条目起始索引(如值为 s 表示从条目 s 开始) [31:24] MD_EN 内存域使能位(1=使能该内存域的地址规则,0=禁用,禁用后该内存域下所有条目失效)
配置示例
若 MDFCFG1(MD1 对应的寄存器)配置为 0x01000008,则表示:
- MD1 使能(
[31:24] = 0x01); - 条目起始索引为 0(
[23:16] = 0x00); - 条目数量为 8(
[15:0] = 0x0008); - 最终 MD1 绑定条目 0~7。
(2)地址条目寄存器:ENTRY_ADDRx 与 ENTRY_CFGx(Entry Address/Configuration Register)
每个地址规则条目(Entry)由一对寄存器组成,x 为条目索引(0~N-1,N 为 IOPMP 支持的最大条目数,可配置),用于定义单个条目的地址范围、匹配模式及访问权限。
1. ENTRY_ADDRx(地址寄存器)
核心功能
存储该条目对应的物理地址基准值,地址范围的计算依赖 ENTRY_CFGx 中配置的匹配模式。
关键参数
- 寄存器地址:按条目索引递增分配(如条目 0 为
0x10000010,条目 1 为0x10000018,间隔 8 字节); - 位宽:与系统物理地址宽度(PA_SIZE)一致(通常 32 位或 64 位,适配 AXI 总线地址传输规范);
- 核心特性:
- 地址值需符合匹配模式的对齐要求(如 NA4 模式需 4 字节对齐,NAPOT 模式需 2^N 字节对齐);
- 无预留位,全位段用于存储物理地址信息,地址有效性由
ENTRY_CFGx的使能位控制。
2. ENTRY_CFGx(配置寄存器)
核心功能
配置该条目的匹配模式、访问权限、使能状态及锁定状态,是地址规则生效的关键控制寄存器。
关键参数
- 寄存器地址:与
ENTRY_ADDRx成对分配(如条目 0 为0x10000090,条目 1 为0x10000094,间隔 4 字节); - 位宽:32 位(规范推荐);
- 核心位定义(按功能分区):
位段范围 位段名称 功能描述 [0] R 读权限位(1=允许读访问,0=禁止) [1] W 写权限位(1=允许写访问,0=禁止) [2] X 执行权限位(1=允许执行访问,0=禁止;外设场景极少使用) [3] LOCK 规则锁定位(1=锁定该条目配置,禁止修改;0=可修改) [4] EN 条目使能位(1=该条目生效,0=该条目失效,不参与地址匹配) [6:5] MATCH_MODE 地址匹配模式(00=NA4,01=NAPOT,10=TOR,11=保留) [7] ERR_SUPPRESS 错误抑制位(1=该条目匹配失败时不触发异常,0=触发异常) [31:8] RESERVED 预留位(需置 0,硬件不解析)
匹配模式与地址范围计算规则
结合 ENTRY_ADDRx 的地址值,不同匹配模式对应的地址范围计算方式如下:
- NA4(4 字节自然对齐):地址范围 =
ENTRY_ADDRx~ENTRY_ADDRx + 3,强制 4 字节对齐,适用于小块内存精准保护; - NAPOT(非连续对齐):地址范围 =
ENTRY_ADDRx~ENTRY_ADDRx + (2^(k+1) - 1),其中 k 为地址值中连续“1”的位数(需提前配置地址对齐),支持灵活大小的地址块; - TOR(范围匹配):需两个连续条目配合(条目 x 存储起始地址,条目 x+1 存储结束地址),地址范围 =
ENTRY_ADDRx~ENTRY_ADDR(x+1),支持非对齐地址范围。
(3)全局控制与错误日志寄存器(地址配置关联寄存器)
此类寄存器虽不直接配置地址,但影响地址规则的生效与异常排查,需配合地址配置寄存器使用。
1. IOPMP_CTRL(全局控制寄存器)
核心功能
控制 IOPMP 全局使能状态,是地址校验功能的总开关。
关键位定义(与地址配置相关)
| 位号 | 位名称 | 功能描述 |
|---|---|---|
| [0] | GLOBAL_EN | 全局使能位(1=开启 IOPMP 地址校验,所有生效条目参与匹配;0=关闭校验,所有地址访问直接放行) |
| [1] | STRICT_MODE | 严格模式位(1=违规访问直接拦截;0=日志模式,仅记录违规不拦截) |
2. ERR_INFO(错误信息寄存器)
核心功能
记录最近一次地址访问违规的详细信息,用于排查地址配置错误或非法访问。
关键位定义(与地址相关)
| 位段范围 | 位段名称 | 功能描述 |
|---|---|---|
| [31:0] | VIOL_ADDR | 违规访问的目标物理地址 |
| [47:32] | RRID | 发起违规访问的外设 RRID |
| [51:48] | ERR_TYPE | 错误类型(01=地址范围不匹配,10=权限不足,11=地址配置无效) |
3. ERR_MFR(后续错误跟踪寄存器)
核心功能
跟踪后续多次地址访问违规的 RRID,支持批量排查多外设地址违规场景,位宽与 ERR_INFO 配合(通常 64 位)。
6.3、寄存器配置约束与注意事项
- 地址对齐约束:
ENTRY_ADDRx的地址值需与ENTRY_CFGx中的匹配模式一致,否则会导致地址范围解析错误(如 NA4 模式配置非 4 字节对齐地址,会触发“地址配置无效”错误); - 总线协议兼容:所有寄存器通过 AHB-Lite 总线配置,需遵循 AHB-Lite 协议的传输规范(如地址位宽 12 位,数据位宽 32 位),配置时需确保
HTRANS(传输类型)、HWRITE(读写控制)等信号合法; - 权限一致性:
ENTRY_CFGx中的 R/W/X 权限需与 SRCMD 表中配置的基础权限一致,若存在冲突,以条目级权限为准(如 SRCMD 表允许读,条目配置禁止读,则最终禁止读访问); - 锁定机制:
ENTRY_CFGx的 LOCK 位置 1 后,该条目及对应的ENTRY_ADDRx均不可修改,需重启系统或通过 M-Mode 特权操作解锁(部分硬件不支持解锁,需谨慎配置); - 条目索引优先级:地址匹配时按条目索引从小到大顺序执行,低索引条目优先生效,配置时需避免地址范围重叠(若重叠,仅首个匹配条目生效)。
6.4、寄存器配置流程(地址配置闭环)
- 配置
MDFCFGx:定义每个内存域绑定的条目范围及使能状态; - 配置
ENTRY_ADDRx:为每个条目写入物理地址基准值,确保符合匹配模式对齐要求; - 配置
ENTRY_CFGx:设置匹配模式、访问权限、使能位及锁定位(按需); - 配置
IOPMP_CTRL:置位 GLOBAL_EN 位,开启地址校验功能; - 验证与排查:通过
ERR_INFO和ERR_MFR寄存器监控地址访问状态,若存在违规,调整ENTRY_ADDRx或ENTRY_CFGx配置。
这些寄存器共同构成 IOPMP 地址配置的核心链路,通过“内存域-条目-地址”的三级映射,实现对不同外设的物理内存访问精细化控制,是 RISC-V 嵌入式系统中内存安全防护的关键硬件基础。
7、IOPMP 寄存器配置示例表
以下示例基于 Nuclei IOPMP 规范,覆盖 单外设 DMA 地址保护、多内存域隔离、敏感数据防护 三大核心场景,包含完整寄存器配置值、配置说明及验证方法,可直接参考用于裸机开发或仿真测试。
7.1、通用配置前提
- 系统物理地址宽度(PA_SIZE):32 位;
- AHB-Lite 总线配置地址:
0x10000000(寄存器基地址); - 最大内存域数量:63 个(示例使用 MD0~MD2);
- 最大地址条目数:16 个(示例使用 Entry0~Entry5);
- RRID 配置范围:1~65535(示例使用 RRID=0x01~0x03)。
7.2、场景1:单外设 DMA 地址保护(基础场景)
场景目标
让网卡(RRID=0x01)仅能访问指定 DMA 缓冲区(0x80000000~0x8000FFFF,64KB),禁止访问其他内存区域。
| 寄存器名称 | 寄存器地址 | 配置值(十六进制) | 配置说明 |
|---|---|---|---|
MDFCFG0(MD0 配置) | 0x10000100 | 0x01000001 | - [31:24] = 0x01:MD0 使能; - [23:16] = 0x00:条目起始索引=0; - [15:0] = 0x0001:条目数量=1(绑定 Entry0) |
ENTRY_ADDR0(Entry0 地址) | 0x10000010 | 0x80000000 | DMA 缓冲区起始地址(NA4 模式,4字节对齐) |
ENTRY_CFG0(Entry0 配置) | 0x10000090 | 0x0000001B | - [0] = 1:允许读; - [1] = 1:允许写; - [2] = 0:禁止执行; - [3] = 0:不锁定; - [4] = 1:条目使能; - [6:5] = 00:匹配模式=NA4 |
SRCMD_0x01(RRID=0x01 映射) | 0x10000204 | 0x00000000 | - 内存域索引=0(绑定 MD0); - 基础权限=读+写(与 Entry0 权限一致) |
IOPMP_CTRL(全局控制) | 0x10000000 | 0x00000003 | - [0] = 1:全局使能; - [1] = 1:严格模式(违规直接拦截) |
ERR_CFG(错误配置) | 0x10000004 | 0x00000003 | - [0] = 1:使能违规中断; - [1] = 1:违规返回总线错误 |
验证方法
- 网卡发起 DMA 写操作(地址=0x80001234):访问成功,无异常;
- 网卡发起 DMA 读操作(地址=0x00001234,敏感内存):触发“Illegal Access”错误,
ERR_INFO寄存器记录违规地址=0x00001234、RRID=0x01。
7.3、场景2:多内存域隔离(多外设场景)
场景目标
- 网卡(RRID=0x01)→ 绑定 MD0,仅访问 DMA1(0x80000000~0x8000FFFF);
- 存储控制器(RRID=0x02)→ 绑定 MD1,仅访问 DMA2(0x80010000~0x8001FFFF);
- 两者权限隔离,禁止跨 DMA 区域访问。
| 寄存器名称 | 寄存器地址 | 配置值(十六进制) | 配置说明 |
|---|---|---|---|
MDFCFG0(MD0 配置) | 0x10000100 | 0x01000001 | MD0 使能,绑定 Entry0(条目起始=0,数量=1) |
ENTRY_ADDR0 | 0x10000010 | 0x80000000 | DMA1 起始地址(NA4 模式) |
ENTRY_CFG0 | 0x10000090 | 0x0000001B | 读+写权限,NA4 模式,条目使能 |
MDFCFG1(MD1 配置) | 0x10000104 | 0x01010001 | - [31:24] = 0x01:MD1 使能; - [23:16] = 0x01:条目起始索引=1; - [15:0] = 0x0001:条目数量=1(绑定 Entry1) |
ENTRY_ADDR1 | 0x10000018 | 0x80010000 | DMA2 起始地址(NA4 模式) |
ENTRY_CFG1 | 0x10000094 | 0x0000001B | 读+写权限,NA4 模式,条目使能 |
SRCMD_0x01(RRID=0x01) | 0x10000204 | 0x00000000 | 映射 MD0,基础权限=读+写 |
SRCMD_0x02(RRID=0x02) | 0x10000208 | 0x00000001 | 映射 MD1,基础权限=读+写 |
IOPMP_CTRL | 0x10000000 | 0x00000003 | 全局使能+严格模式 |
验证方法
- 网卡访问 DMA2(0x80011234):触发“Rule Not Hit”错误;
- 存储控制器访问 DMA1(0x80001234):触发“Rule Not Hit”错误;
- 两者访问自身 DMA 区域:访问成功。
7.4、场景3:敏感数据防护(安全场景)
场景目标
- 安全芯片(RRID=0x03)→ 绑定 MD2,可访问密钥存储区(0x90000000~0x90000FFF,4KB);
- 其他外设(RRID≠0x03)禁止访问该密钥区;
- 密钥区规则锁定,防止篡改。
| 寄存器名称 | 寄存器地址 | 配置值(十六进制) | 配置说明 |
|---|---|---|---|
MDFCFG2(MD2 配置) | 0x10000108 | 0x01020001 | - MD2 使能; - 条目起始索引=2(绑定 Entry2); - 条目数量=1 |
ENTRY_ADDR2 | 0x10000020 | 0x90000000 | 密钥区起始地址(NAPOT 模式,4KB 对齐) |
ENTRY_CFG2 | 0x10000098 | 0x0000003B | - [0] = 1:允许读; - [1] = 0:禁止写(仅可读); - [2] = 0:禁止执行; - [3] = 1:锁定规则(不可修改); - [4] = 1:条目使能; - [6:5] = 01:匹配模式=NAPOT |
SRCMD_0x03(RRID=0x03) | 0x1000020C | 0x00000002 | 映射 MD2,基础权限=读(与 Entry2 一致) |
ENTRY_ADDR3(Entry3 地址) | 0x10000028 | 0x90000000 | 辅助规则:禁止其他外设访问密钥区 |
ENTRY_CFG3 | 0x1000009C | 0x00000030 | - 权限=0(禁止所有访问); - 匹配模式=NAPOT; - 条目使能,锁定规则 |
IOPMP_CTRL | 0x10000000 | 0x00000003 | 全局使能+严格模式 |
验证方法
- 安全芯片访问密钥区(0x90000123):读操作成功,写操作被拦截;
- 网卡(RRID=0x01)访问密钥区:触发“Illegal Access”错误;
- 尝试修改
ENTRY_CFG2:因 LOCK 位=1,修改无效。
7.5、场景4:范围匹配模式(TOR)配置(灵活地址场景)
场景目标
让 UART 外设(RRID=0x04)访问非对齐地址范围(0x80021234~0x80025678),支持灵活地址块保护。
| 寄存器名称 | 寄存器地址 | 配置值(十六进制) | 配置说明 |
|---|---|---|---|
MDFCFG3(MD3 配置) | 0x1000010C | 0x01030002 | - MD3 使能; - 条目起始索引=4; - 条目数量=2(绑定 Entry4、Entry5,TOR 模式需两个连续条目) |
ENTRY_ADDR4(起始地址) | 0x10000030 | 0x80021234 | TOR 模式起始地址(非对齐) |
ENTRY_CFG4 | 0x100000A0 | 0x0000005B | - 读+写权限; - 匹配模式=TOR([6:5]=10); - 条目使能 |
ENTRY_ADDR5(结束地址) | 0x10000038 | 0x80025678 | TOR 模式结束地址 |
ENTRY_CFG5 | 0x100000A4 | 0x00000050 | - 权限位无效(TOR 模式仅起始条目权限生效); - 匹配模式=TOR; - 条目使能 |
SRCMD_0x04(RRID=0x04) | 0x10000210 | 0x00000003 | 映射 MD3,基础权限=读+写 |
IOPMP_CTRL | 0x10000000 | 0x00000003 | 全局使能+严格模式 |
验证方法
- UART 访问 0x80023456(地址范围内):访问成功;
- UART 访问 0x80020000(地址范围外):触发“Rule Not Hit”错误。
7.6、配置关键注意事项
- 地址对齐:NA4 模式需 4 字节对齐,NAPOT 模式需 2^N 字节对齐,否则地址范围解析错误;
- 权限一致性:SRCMD 表的基础权限需与 Entry 条目权限一致,若冲突以 Entry 权限为准;
- 规则锁定:敏感场景(如密钥区)需置位
ENTRY_CFGx的 LOCK 位,防止规则被篡改; - 条目索引:不同内存域的条目索引不可重叠,避免规则冲突;
- 错误排查:配置后若出现异常,可通过
ERR_INFO寄存器查询违规 RRID、地址及错误类型。
8、IOPMP 配置完整RISCV裸机代码模板
#include <stdint.h>
#include <stddef.h>
/************************** 第一步:定义 IOPMP 相关寄存器(模拟硬件) **************************/
// 注:真实硬件中,这些寄存器为内存映射IO,地址由SoC厂商定义
#define IOPMP_CTRL (*(volatile uint32_t *)0x10000000) // IOPMP 全局控制寄存器
#define IOPMP_CAUSE (*(volatile uint32_t *)0x10000004) // IOPMP 异常状态寄存器
#define IOPMP_ADDR(n) (*(volatile uint64_t *)(0x10000010 + (n)*8)) // IOPMP 地址寄存器(最多16条规则)
#define IOPMP_CFG(n) (*(volatile uint32_t *)(0x10000090 + (n)*4)) // IOPMP 权限寄存器(最多16条规则)
// IOPMP 控制寄存器位定义
#define IOPMP_CTRL_ENABLE (1 << 0) // 全局使能位(1=开启校验,0=关闭校验)
#define IOPMP_CTRL_STRICT (1 << 1) // 校验模式(1=严格模式,拦截违规;0=日志模式,仅上报)
#define IOPMP_CTRL_IRQ_EN (1 << 2) // 异常中断使能(1=开启,0=关闭)
// IOPMP 异常状态寄存器位定义(高16位=Bus ID,低16位=违规类型/地址标识)
#define IOPMP_CAUSE_BUS_ID_MASK (0xFFFF << 16)
#define IOPMP_CAUSE_VIOL_TYPE_MASK (0x000F << 8)
#define IOPMP_CAUSE_ADDR_VALID (1 << 0)
// IOPMP 权限寄存器位定义
#define IOPMP_CFG_R (1 << 0) // 允许读
#define IOPMP_CFG_W (1 << 1) // 允许写
#define IOPMP_CFG_X (1 << 2) // 允许执行(外设极少使用)
#define IOPMP_CFG_LOCK (1 << 3) // 锁定规则(不可修改)
#define IOPMP_CFG_ENABLE (1 << 4) // 使能当前规则
#define IOPMP_CFG_MATCH_NAT (1 << 5) // 匹配模式:自然对齐(默认)
#define IOPMP_CFG_MATCH_ARB (1 << 6) // 匹配模式:任意范围
// 最大规则数(可根据硬件调整,通常16条)
#define IOPMP_MAX_RULES 16
/************************** 第二步:定义全局数据结构(外设映射/规则缓存) **************************/
// 外设 Bus ID 与 IOPMP 规则集的映射表
typedef struct {
uint32_t bus_id; // 外设 Bus ID
uint8_t rule_start; // 起始规则编号
uint8_t rule_end; // 结束规则编号
uint8_t enable; // 映射使能位
} IOPMP_Bus_Map;
// 全局 IOPMP 总线映射表(支持16个外设)
static IOPMP_Bus_Map iopmp_bus_map[16] = {0};
// DMA 缓冲区定义(模拟外设访问的合法内存区域)
#define DMA_BUFFER_BASE 0x80000000
#define DMA_BUFFER_SIZE 0x00010000 // 64KB(0x80000000 ~ 0x8000FFFF)
#define DMA_BUFFER_END (DMA_BUFFER_BASE + DMA_BUFFER_SIZE - 1)
/************************** 第三步:IOPMP 核心工具函数实现 **************************/
/**
* @brief 绑定外设 Bus ID 到 IOPMP 规则集
* @param bus_id: 外设 Bus ID
* @param rule_start: 起始规则编号(0 ~ IOPMP_MAX_RULES-1)
* @param rule_end: 结束规则编号(需 >= rule_start)
* @retval 0=成功,-1=失败(参数非法/规则被占用)
*/
int32_t iopmp_bind_bus_to_rules(uint32_t bus_id, uint8_t rule_start, uint8_t rule_end) {
// 参数合法性检查
if (rule_start >= IOPMP_MAX_RULES || rule_end >= IOPMP_MAX_RULES || rule_end < rule_start) {
return -1;
}
// 检查规则是否被占用
for (int i = 0; i < 16; i++) {
if (iopmp_bus_map[i].enable &&
((rule_start >= iopmp_bus_map[i].rule_start && rule_start <= iopmp_bus_map[i].rule_end) ||
(rule_end >= iopmp_bus_map[i].rule_start && rule_end <= iopmp_bus_map[i].rule_end))) {
return -1; // 规则已被占用
}
}
// 查找空闲映射项并绑定
for (int i = 0; i < 16; i++) {
if (!iopmp_bus_map[i].enable) {
iopmp_bus_map[i].bus_id = bus_id;
iopmp_bus_map[i].rule_start = rule_start;
iopmp_bus_map[i].rule_end = rule_end;
iopmp_bus_map[i].enable = 1;
return 0;
}
}
return -1; // 映射表已满
}
/**
* @brief 配置单条 IOPMP 规则(地址+权限+匹配模式)
* @param rule_num: 规则编号(0 ~ IOPMP_MAX_RULES-1)
* @param addr: 内存区域起始地址
* @param perm: 访问权限(IOPMP_CFG_R/W/X/LOCK 组合)
* @param match_mode: 匹配模式(IOPMP_CFG_MATCH_NAT/ARB)
* @retval 0=成功,-1=失败(参数非法/规则被锁定)
*/
int32_t iopmp_config_rule(uint8_t rule_num, uint64_t addr, uint32_t perm, uint8_t match_mode) {
// 参数合法性检查
if (rule_num >= IOPMP_MAX_RULES) {
return -1;
}
// 检查规则是否被锁定
if (IOPMP_CFG(rule_num) & IOPMP_CFG_LOCK) {
return -1; // 规则已锁定,不可修改
}
// 1. 配置地址寄存器(自然对齐模式下,地址右移2位(按字对齐))
if (match_mode == IOPMP_CFG_MATCH_NAT) {
IOPMP_ADDR(rule_num) = addr >> 2;
} else {
IOPMP_ADDR(rule_num) = addr; // 任意范围模式,直接写入起始地址
}
// 2. 配置权限与匹配模式
IOPMP_CFG(rule_num) = perm | match_mode | IOPMP_CFG_ENABLE;
return 0;
}
/**
* @brief IOPMP 模块全局初始化
*/
void iopmp_init(void) {
// 1. 复位 IOPMP 寄存器
IOPMP_CTRL = 0;
IOPMP_CAUSE = 0;
for (int i = 0; i < IOPMP_MAX_RULES; i++) {
IOPMP_ADDR(i) = 0;
IOPMP_CFG(i) = 0;
}
// 2. 全局配置:使能 IOPMP + 严格模式 + 开启异常中断
IOPMP_CTRL = IOPMP_CTRL_ENABLE | IOPMP_CTRL_STRICT | IOPMP_CTRL_IRQ_EN;
// 3. 绑定示例外设:网卡(Bus ID=0x01)到规则 0~1
if (iopmp_bind_bus_to_rules(0x01, 0, 1) != 0) {
while (1); // 绑定失败,挂起
}
// 4. 配置示例规则 0:允许网卡访问 DMA 缓冲区(读/写,自然对齐)
uint32_t dma_perm = IOPMP_CFG_R | IOPMP_CFG_W; // 仅允许读/写,不锁定(调试用)
if (iopmp_config_rule(0, DMA_BUFFER_BASE, dma_perm, IOPMP_CFG_MATCH_NAT) != 0) {
while (1); // 规则配置失败,挂起
}
// 5. 配置示例规则 1:禁止访问内核敏感内存(0x00000000 ~ 0x000FFFFF)
uint32_t kernel_perm = 0; // 无权限(禁止任何访问)
if (iopmp_config_rule(1, 0x00000000, kernel_perm, IOPMP_CFG_MATCH_NAT) != 0) {
while (1); // 规则配置失败,挂起
}
}
/**
* @brief IOPMP 异常中断处理函数(M-Mode 执行)
*/
void __attribute__((interrupt)) iopmp_exception_handler(void) {
// 1. 读取异常状态,解析 Bus ID 与违规类型
uint32_t cause = IOPMP_CAUSE;
uint32_t bus_id = (cause & IOPMP_CAUSE_BUS_ID_MASK) >> 16;
uint32_t viol_type = (cause & IOPMP_CAUSE_VIOL_TYPE_MASK) >> 8;
// 2. 打印违规信息(裸机环境下可通过串口输出,此处模拟)
const char* viol_type_str = "";
switch (viol_type) {
case 1: viol_type_str = "READ VIOLATION"; break;
case 2: viol_type_str = "WRITE VIOLATION"; break;
case 3: viol_type_str = "EXECUTE VIOLATION"; break;
default: viol_type_str = "UNKNOWN VIOLATION"; break;
}
// 3. 处理违规:禁用该外设的 Bus Master 功能(模拟)
for (int i = 0; i < 16; i++) {
if (iopmp_bus_map[i].enable && iopmp_bus_map[i].bus_id == bus_id) {
iopmp_bus_map[i].enable = 0; // 禁用映射,后续访问直接拦截
break;
}
}
// 4. 清除异常状态寄存器
IOPMP_CAUSE = 0;
}
/**
* @brief 模拟外设 DMA 访问(测试 IOPMP 校验功能)
* @param bus_id: 外设 Bus ID
* @param addr: 访问目标地址
* @param type: 访问类型(1=读,2=写)
* @retval 0=成功(允许访问),-1=失败(被拦截)
*/
int32_t peripheral_dma_access(uint32_t bus_id, uint64_t addr, uint8_t type) {
// 1. 查找外设对应的规则集
IOPMP_Bus_Map* bus_map = NULL;
for (int i = 0; i < 16; i++) {
if (iopmp_bus_map[i].enable && iopmp_bus_map[i].bus_id == bus_id) {
bus_map = &iopmp_bus_map[i];
break;
}
}
if (bus_map == NULL) {
return -1; // 外设未绑定,直接拦截
}
// 2. 遍历规则集,进行权限校验
for (int i = bus_map->rule_start; i <= bus_map->rule_end; i++) {
// 跳过未使能的规则
if (!(IOPMP_CFG(i) & IOPMP_CFG_ENABLE)) {
continue;
}
// 3. 地址匹配校验(简化版:自然对齐模式)
uint64_t rule_addr = IOPMP_ADDR(i) << 2;
uint64_t rule_addr_end = 0;
if (IOPMP_CFG(i) & IOPMP_CFG_MATCH_NAT) {
// 自然对齐模式:计算地址范围(64KB 固定,可扩展)
rule_addr_end = rule_addr + DMA_BUFFER_SIZE - 1;
} else {
// 任意范围模式:此处简化处理(真实硬件需配置结束地址)
rule_addr_end = rule_addr + 0xFFFF;
}
// 4. 地址在规则范围内,校验访问权限
if (addr >= rule_addr && addr <= rule_addr_end) {
switch (type) {
case 1: // 读操作
if (IOPMP_CFG(i) & IOPMP_CFG_R) {
return 0; // 权限通过,允许访问
}
break;
case 2: // 写操作
if (IOPMP_CFG(i) & IOPMP_CFG_W) {
return 0; // 权限通过,允许访问
}
break;
default:
break;
}
}
}
// 5. 未匹配到合法规则,触发 IOPMP 异常
IOPMP_CAUSE = (bus_id << 16) | (type << 8) | IOPMP_CAUSE_ADDR_VALID;
return -1;
}
/************************** 第四步:主函数(测试 IOPMP 功能) **************************/
int main(void) {
// 1. 初始化 IOPMP 模块
iopmp_init();
// 2. 测试1:网卡(Bus ID=0x01)访问合法 DMA 缓冲区(写操作)
int32_t ret1 = peripheral_dma_access(0x01, DMA_BUFFER_BASE + 0x1234, 2);
if (ret1 == 0) {
// 访问成功(裸机环境下可通过串口打印"Test 1 Pass")
}
// 3. 测试2:网卡(Bus ID=0x01)访问内核敏感内存(读操作)
int32_t ret2 = peripheral_dma_access(0x01, 0x00001234, 1);
if (ret2 == -1) {
// 访问被拦截,异常处理函数已执行(裸机环境下可通过串口打印"Test 2 Pass")
}
// 4. 无限循环(保持程序运行)
while (1) {
;
}
return 0;
}
813

被折叠的 条评论
为什么被折叠?



