如何使用 IOPMP(Input/Output Physical Memory Protection)

IOPMP 是 RISC-V 架构下针对外设 DMA/总线主控(Bus Master) 的物理内存保护机制,核心目标是约束外设对内存的访问权限,防止恶意或误操作的外设越权读写敏感内存区域。它的设计逻辑与 PMP(Physical Memory Protection,针对 CPU 核心的内存保护)一脉相承,但专门面向外设,由处于 M-Mode(机器模式)的 Secure Monitor 负责配置和管理。

结合你提供的链接内容,下面从 核心原理、使用前提、配置步骤、典型应用场景 四个维度,详细说明如何使用 IOPMP。

1、IOPMP 核心原理(理解使用逻辑的基础)

  1. 保护对象

    • 针对具备 DMA 功能Bus Master 接口 的外设(如网卡、存储控制器、DMA 控制器)。
    • 这些外设可以不经过 CPU 核心,直接通过总线访问物理内存,是内存安全的重要风险点。
  2. 核心机制

    • IOPMP 通过配置一组“权限规则”,定义每个外设允许访问的内存地址范围、访问类型(读/写/执行)。
    • 所有外设的内存访问请求,都会经过 IOPMP 模块的校验:符合规则则放行,违反规则则拦截并上报异常(通常触发 M-Mode 中断)。
  3. 管理主体

    • IOPMP 是特权级硬件模块,只有处于 M-Mode 的软件(如 Secure Monitor、Hypervisor 或裸机内核)有权限配置其规则寄存器。
    • S-Mode(监督模式)或 U-Mode(用户模式)的软件无法直接修改 IOPMP 配置,确保规则的安全性。

2、使用 IOPMP 的前提条件

  1. 硬件支持

    • 目标 SoC 必须集成 IOPMP 硬件模块,且外设的 Bus Master 接口需连接到 IOPMP 校验链路。
    • 确认 RISC-V 内核的扩展支持(部分厂商会在 PMP 基础上扩展 IOPMP 功能)。
  2. 软件环境

    • 运行在 M-Mode 的安全监控程序(Secure Monitor)裸机操作系统内核,负责 IOPMP 的初始化、规则配置和异常处理。
    • 明确外设的编号、总线 ID 与 IOPMP 规则的绑定关系(不同外设对应不同的规则集)。

3、IOPMP 配置与使用的核心步骤

IOPMP 的使用流程遵循 “初始化 → 规则配置 → 权限校验 → 异常处理” 的闭环,具体步骤如下:

步骤 1:IOPMP 模块初始化(M-Mode 执行)

  1. 使能 IOPMP 模块

    • 写入 IOPMP 控制寄存器(如 iopmpcfg0),设置全局使能位(Enable),开启外设访问的校验功能。
    • 配置校验模式:如“严格模式”(违反规则立即拦截)或“日志模式”(仅上报不拦截,用于调试)。
  2. 关联外设与规则集

    • 基于外设的 Bus ID设备编号,建立“外设 - IOPMP 规则”的映射关系。
    • 例如:将网卡的 Bus ID=0x01 绑定到 IOPMP 规则 0~3,将存储控制器的 Bus ID=0x02 绑定到规则 4~7。

步骤 2:配置 IOPMP 访问规则(核心操作)

IOPMP 的规则配置与 PMP 类似,通过地址寄存器(IOPMPADDR)权限寄存器(IOPMPCFG) 定义内存访问的“白名单”。每条规则包含三个核心要素:地址范围 + 访问权限 + 匹配模式

  1. 定义地址范围

    • 写入 IOPMPADDR 寄存器,设置内存区域的起始地址或结束地址(具体取决于匹配模式)。
    • 支持两种匹配模式:
      • 自然对齐模式:地址按 2^N 对齐,适用于连续的大内存块(如 DMA 缓冲区)。
      • 任意范围模式:通过起始地址和长度定义非对齐区域(部分 IOPMP 实现支持)。
  2. 配置访问权限

    • 写入 IOPMPCFG 寄存器,为当前规则设置权限位,常用权限包括:
      权限位含义
      R (Read)允许外设读取该内存区域
      W (Write)允许外设写入该内存区域
      X (Execute)允许外设执行该内存区域(极少用,外设一般不执行代码)
      L (Lock)锁定规则,锁定后无法修改(防止恶意篡改规则)
  3. 规则优先级与覆盖

    • 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 校验

  1. 外设发起内存访问

    • 当外设(如网卡)需要 DMA 传输数据时,会向总线发送内存访问请求(携带目标地址、访问类型、Bus ID)。
  2. IOPMP 自动校验

    • IOPMP 模块根据外设的 Bus ID 找到绑定的规则集,依次匹配规则:
      • 若匹配到允许的规则 → 放行访问请求;
      • 若未匹配到任何规则,或匹配到禁止的规则 → 拦截请求,并向 M-Mode 上报 IOPMP 异常中断

步骤 4:IOPMP 异常处理(M-Mode 执行)

  1. 异常响应

    • M-Mode 软件捕获 IOPMP 异常后,读取异常状态寄存器(如 iopmpcause),获取以下信息:
      • 违规外设的 Bus ID;
      • 违规的内存地址;
      • 违规的访问类型(读/写/执行)。
  2. 异常处理策略

    • 调试阶段:打印违规信息,定位外设误操作或规则配置错误;
    • 生产阶段:禁止违规外设的 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 典型应用场景

  1. DMA 缓冲区隔离

    • 为每个外设的 DMA 缓冲区配置独立的 IOPMP 规则,防止外设访问其他外设的缓冲区或系统敏感内存(如内核栈、页表)。
  2. 安全分区隔离

    • 在异构系统中(如 TrustZone 类似的安全分区),通过 IOPMP 阻止非安全外设访问安全分区的内存,保障敏感数据(如密钥、证书)的安全。
  3. 外设权限最小化

    • 遵循“最小权限原则”,只为外设配置完成功能所需的最小内存访问权限,降低攻击面。

5、使用 IOPMP 的关键注意事项

  1. 规则配置的安全性

    • 所有 IOPMP 规则必须在 M-Mode 配置,且敏感规则建议设置 Lock 位,防止被 S-Mode/U-Mode 软件篡改。
    • 避免配置“允许访问全内存”的宽松规则,这会完全丧失 IOPMP 的保护意义。
  2. 性能影响

    • IOPMP 是硬件级校验,对性能的影响极小,但规则数量过多(如超过 16 条)可能会增加匹配延迟,建议按需精简规则。
  3. 与 PMP 的协同

    • IOPMP 保护外设的内存访问,PMP 保护 CPU 核心的内存访问,两者结合才能实现 SoC 级的内存安全防护。

6 IOPMP寄存器描述

配置地址的寄存器定义(基于Nuclei IOPMP规范)

IOPMP 中与地址配置直接相关的寄存器,核心围绕 “内存域绑定”“地址规则定义”“匹配模式配置” 三大核心场景设计,均为内存映射寄存器,需通过 AHB-Lite 总线编程配置。以下是具体寄存器的定义、功能及关键特性,结合规范细节逐一说明:

6.1、核心地址配置寄存器分类与关联逻辑

地址配置寄存器按功能可分为 “内存域-条目绑定寄存器”“地址条目寄存器”“全局控制与错误日志寄存器” 三类,其关联逻辑为:

  1. 通过“内存域-条目绑定寄存器”定义每个内存域(MD)绑定的地址规则条目范围;
  2. 通过“地址条目寄存器”配置单个条目的地址范围、匹配模式及权限;
  3. 全局控制寄存器开启地址校验功能,错误日志寄存器记录地址配置或访问违规信息。

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_ADDRxENTRY_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、寄存器配置约束与注意事项

  1. 地址对齐约束ENTRY_ADDRx 的地址值需与 ENTRY_CFGx 中的匹配模式一致,否则会导致地址范围解析错误(如 NA4 模式配置非 4 字节对齐地址,会触发“地址配置无效”错误);
  2. 总线协议兼容:所有寄存器通过 AHB-Lite 总线配置,需遵循 AHB-Lite 协议的传输规范(如地址位宽 12 位,数据位宽 32 位),配置时需确保 HTRANS(传输类型)、HWRITE(读写控制)等信号合法;
  3. 权限一致性ENTRY_CFGx 中的 R/W/X 权限需与 SRCMD 表中配置的基础权限一致,若存在冲突,以条目级权限为准(如 SRCMD 表允许读,条目配置禁止读,则最终禁止读访问);
  4. 锁定机制ENTRY_CFGx 的 LOCK 位置 1 后,该条目及对应的 ENTRY_ADDRx 均不可修改,需重启系统或通过 M-Mode 特权操作解锁(部分硬件不支持解锁,需谨慎配置);
  5. 条目索引优先级:地址匹配时按条目索引从小到大顺序执行,低索引条目优先生效,配置时需避免地址范围重叠(若重叠,仅首个匹配条目生效)。

6.4、寄存器配置流程(地址配置闭环)

  1. 配置 MDFCFGx:定义每个内存域绑定的条目范围及使能状态;
  2. 配置 ENTRY_ADDRx:为每个条目写入物理地址基准值,确保符合匹配模式对齐要求;
  3. 配置 ENTRY_CFGx:设置匹配模式、访问权限、使能位及锁定位(按需);
  4. 配置 IOPMP_CTRL:置位 GLOBAL_EN 位,开启地址校验功能;
  5. 验证与排查:通过 ERR_INFOERR_MFR 寄存器监控地址访问状态,若存在违规,调整 ENTRY_ADDRxENTRY_CFGx 配置。

这些寄存器共同构成 IOPMP 地址配置的核心链路,通过“内存域-条目-地址”的三级映射,实现对不同外设的物理内存访问精细化控制,是 RISC-V 嵌入式系统中内存安全防护的关键硬件基础。

7、IOPMP 寄存器配置示例表

以下示例基于 Nuclei IOPMP 规范,覆盖 单外设 DMA 地址保护、多内存域隔离、敏感数据防护 三大核心场景,包含完整寄存器配置值、配置说明及验证方法,可直接参考用于裸机开发或仿真测试。

7.1、通用配置前提

  1. 系统物理地址宽度(PA_SIZE):32 位;
  2. AHB-Lite 总线配置地址:0x10000000(寄存器基地址);
  3. 最大内存域数量:63 个(示例使用 MD0~MD2);
  4. 最大地址条目数:16 个(示例使用 Entry0~Entry5);
  5. RRID 配置范围:1~65535(示例使用 RRID=0x01~0x03)。

7.2、场景1:单外设 DMA 地址保护(基础场景)

场景目标

让网卡(RRID=0x01)仅能访问指定 DMA 缓冲区(0x80000000~0x8000FFFF,64KB),禁止访问其他内存区域。

寄存器名称寄存器地址配置值(十六进制)配置说明
MDFCFG0(MD0 配置)0x100001000x01000001- [31:24] = 0x01:MD0 使能;
- [23:16] = 0x00:条目起始索引=0;
- [15:0] = 0x0001:条目数量=1(绑定 Entry0)
ENTRY_ADDR0(Entry0 地址)0x100000100x80000000DMA 缓冲区起始地址(NA4 模式,4字节对齐)
ENTRY_CFG0(Entry0 配置)0x100000900x0000001B- [0] = 1:允许读;
- [1] = 1:允许写;
- [2] = 0:禁止执行;
- [3] = 0:不锁定;
- [4] = 1:条目使能;
- [6:5] = 00:匹配模式=NA4
SRCMD_0x01(RRID=0x01 映射)0x100002040x00000000- 内存域索引=0(绑定 MD0);
- 基础权限=读+写(与 Entry0 权限一致)
IOPMP_CTRL(全局控制)0x100000000x00000003- [0] = 1:全局使能;
- [1] = 1:严格模式(违规直接拦截)
ERR_CFG(错误配置)0x100000040x00000003- [0] = 1:使能违规中断;
- [1] = 1:违规返回总线错误
验证方法
  1. 网卡发起 DMA 写操作(地址=0x80001234):访问成功,无异常;
  2. 网卡发起 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 配置)0x100001000x01000001MD0 使能,绑定 Entry0(条目起始=0,数量=1)
ENTRY_ADDR00x100000100x80000000DMA1 起始地址(NA4 模式)
ENTRY_CFG00x100000900x0000001B读+写权限,NA4 模式,条目使能
MDFCFG1(MD1 配置)0x100001040x01010001- [31:24] = 0x01:MD1 使能;
- [23:16] = 0x01:条目起始索引=1;
- [15:0] = 0x0001:条目数量=1(绑定 Entry1)
ENTRY_ADDR10x100000180x80010000DMA2 起始地址(NA4 模式)
ENTRY_CFG10x100000940x0000001B读+写权限,NA4 模式,条目使能
SRCMD_0x01(RRID=0x01)0x100002040x00000000映射 MD0,基础权限=读+写
SRCMD_0x02(RRID=0x02)0x100002080x00000001映射 MD1,基础权限=读+写
IOPMP_CTRL0x100000000x00000003全局使能+严格模式
验证方法
  1. 网卡访问 DMA2(0x80011234):触发“Rule Not Hit”错误;
  2. 存储控制器访问 DMA1(0x80001234):触发“Rule Not Hit”错误;
  3. 两者访问自身 DMA 区域:访问成功。

7.4、场景3:敏感数据防护(安全场景)

场景目标
  • 安全芯片(RRID=0x03)→ 绑定 MD2,可访问密钥存储区(0x90000000~0x90000FFF,4KB);
  • 其他外设(RRID≠0x03)禁止访问该密钥区;
  • 密钥区规则锁定,防止篡改。
寄存器名称寄存器地址配置值(十六进制)配置说明
MDFCFG2(MD2 配置)0x100001080x01020001- MD2 使能;
- 条目起始索引=2(绑定 Entry2);
- 条目数量=1
ENTRY_ADDR20x100000200x90000000密钥区起始地址(NAPOT 模式,4KB 对齐)
ENTRY_CFG20x100000980x0000003B- [0] = 1:允许读;
- [1] = 0:禁止写(仅可读);
- [2] = 0:禁止执行;
- [3] = 1:锁定规则(不可修改);
- [4] = 1:条目使能;
- [6:5] = 01:匹配模式=NAPOT
SRCMD_0x03(RRID=0x03)0x1000020C0x00000002映射 MD2,基础权限=读(与 Entry2 一致)
ENTRY_ADDR3(Entry3 地址)0x100000280x90000000辅助规则:禁止其他外设访问密钥区
ENTRY_CFG30x1000009C0x00000030- 权限=0(禁止所有访问);
- 匹配模式=NAPOT;
- 条目使能,锁定规则
IOPMP_CTRL0x100000000x00000003全局使能+严格模式
验证方法
  1. 安全芯片访问密钥区(0x90000123):读操作成功,写操作被拦截;
  2. 网卡(RRID=0x01)访问密钥区:触发“Illegal Access”错误;
  3. 尝试修改 ENTRY_CFG2:因 LOCK 位=1,修改无效。

7.5、场景4:范围匹配模式(TOR)配置(灵活地址场景)

场景目标

让 UART 外设(RRID=0x04)访问非对齐地址范围(0x80021234~0x80025678),支持灵活地址块保护。

寄存器名称寄存器地址配置值(十六进制)配置说明
MDFCFG3(MD3 配置)0x1000010C0x01030002- MD3 使能;
- 条目起始索引=4;
- 条目数量=2(绑定 Entry4、Entry5,TOR 模式需两个连续条目)
ENTRY_ADDR4(起始地址)0x100000300x80021234TOR 模式起始地址(非对齐)
ENTRY_CFG40x100000A00x0000005B- 读+写权限;
- 匹配模式=TOR([6:5]=10);
- 条目使能
ENTRY_ADDR5(结束地址)0x100000380x80025678TOR 模式结束地址
ENTRY_CFG50x100000A40x00000050- 权限位无效(TOR 模式仅起始条目权限生效);
- 匹配模式=TOR;
- 条目使能
SRCMD_0x04(RRID=0x04)0x100002100x00000003映射 MD3,基础权限=读+写
IOPMP_CTRL0x100000000x00000003全局使能+严格模式
验证方法
  1. UART 访问 0x80023456(地址范围内):访问成功;
  2. UART 访问 0x80020000(地址范围外):触发“Rule Not Hit”错误。

7.6、配置关键注意事项

  1. 地址对齐:NA4 模式需 4 字节对齐,NAPOT 模式需 2^N 字节对齐,否则地址范围解析错误;
  2. 权限一致性:SRCMD 表的基础权限需与 Entry 条目权限一致,若冲突以 Entry 权限为准;
  3. 规则锁定:敏感场景(如密钥区)需置位 ENTRY_CFGx 的 LOCK 位,防止规则被篡改;
  4. 条目索引:不同内存域的条目索引不可重叠,避免规则冲突;
  5. 错误排查:配置后若出现异常,可通过 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;
}
/* * Vhost-user RDMA device : init and packets forwarding * * Copyright (C) 2025 KylinSoft Inc. and/or its affiliates. All rights reserved. * * Author: Xiong Weimin <xiongweimin@kylinos.cn> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #ifndef VHOST_RDMA_H_ #define VHOST_RDMA_H_ #include <stdint.h> #include <stdbool.h> #include <rte_byteorder.h> #include <rte_common.h> #include <rte_vhost.h> #include <rte_interrupts.h> #include <rte_atomic.h> #include <rte_spinlock.h> #include <rte_mempool.h> #include <rte_ring.h> #include <rte_bitmap.h> #include "vhost_rdma_ib.h" #include "eal_interrupts.h" #ifdef __cplusplus extern "C" { #endif /** * @brief Number of vhost queues. * * One CTRL VQ + 64 CQ + 64 TX + 64 RX event queues */ #define NUM_VHOST_QUEUES 193 /** * @brief Maximum GID table length */ #define VHOST_MAX_GID_TBL_LEN 512 /** * @brief Port PKey table length (single entry for default) */ #define VHOST_PORT_PKEY_TBL_LEN 1 /** * @brief Number of RDMA ports supported (currently only one) */ #define NUM_OF_VHOST_RDMA_PORT 1 #define MAX_VHOST_RDMA_DEV_NUM 16 #define VIRTIO_NET_F_ROCE 48 #define VHOST_NET_ROCE_CTRL_QUEUE 0 #define VHOST_RDMA_GID_TYPE_ILLIGAL (-1u) #define DEFAULT_IB_MTU VHOST_RDMA_IB_MTU_1024 #define VHOST_NET_RXQ 0 #define VHOST_NET_TXQ 1 /* VIRTIO_F_EVENT_IDX is NOT supported now */ #define VHOST_RDMA_FEATURE ((1ULL << VIRTIO_F_VERSION_1) |\ (1ULL << VIRTIO_RING_F_INDIRECT_DESC) | \ (1ULL << VHOST_USER_F_PROTOCOL_FEATURES) | \ (1ULL << VHOST_USER_PROTOCOL_F_STATUS) | \ (1ULL << VIRTIO_NET_F_CTRL_VQ) | \ (1ULL << VIRTIO_NET_F_ROCE)) __rte_always_inline uint32_t roundup_pow_of_two(uint32_t n) { return n < 2 ? n : (1u << (32 - __builtin_clz (n - 1))); } /** * @brief Counter types for statistics in vhost RDMA device */ enum vhost_rdma_counters { VHOST_RDMA_CNT_SENT_PKTS, VHOST_RDMA_CNT_RCVD_PKTS, VHOST_RDMA_CNT_DUP_REQ, VHOST_RDMA_CNT_OUT_OF_SEQ_REQ, VHOST_RDMA_CNT_RCV_RNR, VHOST_RDMA_CNT_SND_RNR, VHOST_RDMA_CNT_RCV_SEQ_ERR, VHOST_RDMA_CNT_COMPLETER_SCHED, VHOST_RDMA_CNT_RETRY_EXCEEDED, VHOST_RDMA_CNT_RNR_RETRY_EXCEEDED, VHOST_RDMA_CNT_COMP_RETRY, VHOST_RDMA_CNT_SEND_ERR, VHOST_RDMA_CNT_LINK_DOWNED, VHOST_RDMA_CNT_RDMA_SEND, VHOST_RDMA_CNT_RDMA_RECV, VHOST_RDMA_NUM_OF_COUNTERS }; struct vhost_rdma_net_dev { int vid; uint64_t features; size_t hdr_len; bool started; struct rte_vhost_memory *mem; struct vhost_user_queue *queues; }__rte_cache_aligned; struct vhost_user_queue { struct rte_vhost_vring vring; uint16_t last_avail_idx; uint16_t last_used_idx; uint16_t id; bool enabled; }; /** * @brief Configuration structure exposed to guest via virtio config space * * All fields are in little-endian byte order. */ struct vhost_rdma_config { uint32_t phys_port_cnt; /**< Physical port count */ uint64_t sys_image_guid; /**< System image GUID */ uint32_t vendor_id; /**< Vendor ID */ uint32_t vendor_part_id; /**< Vendor part number */ uint32_t hw_ver; /**< Hardware version */ uint64_t max_mr_size; /**< Max memory region size */ uint64_t page_size_cap; /**< Page size capabilities */ uint32_t max_qp; /**< Max number of QPs */ uint32_t max_qp_wr; /**< Max work requests per QP */ uint64_t device_cap_flag; /**< Device capability flags */ uint32_t max_send_sge; /**< Max SGEs in send WR */ uint32_t max_recv_sge; /**< Max SGEs in recv WR */ uint32_t max_sge_rd; /**< Max SGEs for RD operations */ uint32_t max_cq; /**< Max completion queues */ uint32_t max_cqe; /**< Max entries per CQ */ uint32_t max_mr; /**< Max memory regions */ uint32_t max_pd; /**< Max protection domains */ uint32_t max_qp_rd_atom; /**< Max RDMA read-atoms per QP */ uint32_t max_res_rd_atom; /**< Max responder resources */ uint32_t max_qp_init_rd_atom; /**< Max initiator RD atoms */ uint32_t atomic_cap; /**< Atomic operation support */ uint32_t max_mw; /**< Max memory windows */ uint32_t max_mcast_grp; /**< Max multicast groups */ uint32_t max_mcast_qp_attach; /**< Max QPs per multicast group */ uint32_t max_total_mcast_qp_attach;/**< Total multicast attachments */ uint32_t max_ah; /**< Max address handles */ uint32_t max_fast_reg_page_list_len; /**< Fast registration page list len */ uint32_t max_pi_fast_reg_page_list_len; /**< PI fast reg list len */ uint16_t max_pkeys; /**< Max partition keys */ uint8_t local_ca_ack_delay; /**< Local CA ACK delay */ uint8_t reserved[5]; /* Pad to 8-byte alignment before variable area */ uint8_t reserved1[64]; /**< Reserved for future use */ }; /** * @brief Device attributes (host-native format, not exposed directly) */ struct vhost_rdma_dev_attr { uint64_t max_mr_size; uint64_t page_size_cap; uint32_t hw_ver; uint32_t max_qp_wr; uint64_t device_cap_flags; uint32_t max_qps; uint32_t max_cqs; uint32_t max_send_sge; uint32_t max_recv_sge; uint32_t max_sge_rd; uint32_t max_cqe; uint32_t max_mr; uint32_t max_mw; uint32_t max_pd; uint32_t max_qp_rd_atom; uint32_t max_qp_init_rd_atom; uint32_t max_ah; uint32_t max_fast_reg_page_list_len; uint8_t local_ca_ack_delay; }; /** * @brief Port-level attributes */ struct vhost_rdma_port_attr { uint32_t bad_pkey_cntr; /**< Bad PKey counter */ uint32_t qkey_viol_cntr; /**< QKey violation counter */ }; /** * @brief GID entry with type indicator */ struct vhost_rdma_gid { #define VHOST_RDMA_GID_TYPE_INVALID ((uint32_t)(-1)) uint32_t type; /**< GID type: RoCEv1, RoCEv2, etc. */ uint8_t gid[16]; /**< 128-bit GID value */ }; /** * @brief Generic object pool for managing RDMA resources */ struct vhost_rdma_pool { void *objs; /**< Array of allocated objects */ uint32_t num; /**< Number of objects in pool */ uint32_t size; /**< Size of each object */ struct rte_bitmap *bitmap; /**< Bitmap tracking free slots */ void *bitmap_mem; /**< Memory backing the bitmap */ void (*cleanup)(void *arg); /**< Optional cleanup function */ }; /** * @brief Main RDMA vhost device structure */ struct vhost_rdma_device { int vid; /**< Vhost-Rdma device ID */ int started; /**< Device start state */ volatile bool stopped; /**< Stop flag for threads */ volatile int inuse; /**< Reference count */ /* Memory and resource management */ struct rte_vhost_memory *mem; /**< Guest physical memory map */ struct rte_mempool *mbuf_pool; /**< mbuf pool for packet I/O */ struct rte_ring *tx_ring; /**< TX ring for outbound packets */ struct rte_ring *rx_ring; /**< RX ring for inbound packets */ /* Queues */ struct vhost_user_queue vqs[NUM_VHOST_QUEUES]; /**< All vhost queues */ struct vhost_user_queue *rdma_vqs; /**< Shortcut to RDMA queues */ struct vhost_user_queue *cq_vqs; /**< Shortcut to CQ notification queues */ struct vhost_user_queue *qp_vqs; /**< Shortcut to QP data queues */ struct rte_ring *task_ring; /**< Task scheduling ring */ /* Interrupt handling for control plane */ struct rte_intr_handle ctrl_intr_handle; /**< Control interrupt handle */ int ctrl_intr_registered; /**< Whether interrupt is registered */ /* Virtio-net configuration (exposed to guest) */ struct virtio_net_config config; /**< Generic virtio-net config */ struct vhost_rdma_config rdma_config; /**< RDMA-specific config */ uint32_t max_inline_data; /**< Max inline data size */ /* Device attributes (cached from config) */ struct vhost_rdma_dev_attr attr; /**< Cached device attributes */ /* Single port support */ struct vhost_rdma_port_attr port_attr; /**< Port-level counters */ rte_spinlock_t port_lock; /**< Lock for port access */ unsigned int mtu_cap; /**< MTU capability */ struct vhost_rdma_gid gid_tbl[VHOST_MAX_GID_TBL_LEN]; /**< GID table */ struct vhost_rdma_qp *qp_gsi; /**< Global shared inbox QP? */ /* Resource pools */ struct vhost_rdma_pool pd_pool; /**< Protection domain pool */ struct vhost_rdma_pool mr_pool; /**< Memory region pool */ struct vhost_rdma_pool cq_pool; /**< Completion queue pool */ struct vhost_rdma_pool qp_pool; /**< Queue pair pool */ struct vhost_rdma_pool ah_pool; /**< Address handle pool */ /* Statistics counters */ rte_atomic64_t stats_counters[VHOST_RDMA_NUM_OF_COUNTERS]; }; #define vhost_rdma_drop_ref(obj, dev, type) \ do { \ if (rte_atomic32_dec_and_test(&(obj)->refcnt)) { \ struct vhost_rdma_pool* pool = &(dev)->type##_pool; \ if (pool->cleanup) { \ pool->cleanup(obj); \ } \ vhost_rdma_pool_free(pool, (obj)->type##n); \ } \ }while(0) #define vhost_rdma_add_ref(obj) rte_atomic32_inc(&(obj)->refcnt) /** * @brief Check if there is a new available descriptor in the virtqueue. * * This function compares the current avail->idx from the guest with the last * processed index. If they differ, at least one new descriptor is ready. * * @param vq Pointer to the virtual queue. * @return true if a new descriptor is available, false otherwise. */ static __rte_always_inline bool vhost_rdma_vq_is_avail(struct vhost_user_queue *vq) { return vq->vring.avail->idx != vq->last_avail_idx; } /** * @brief Get pointer to element at given index in a generic data ring. * * Used for accessing pre-allocated memory pools where each element has fixed size. * * @param queue Pointer to the queue containing data buffer. * @param idx Index of the desired element. * @return Pointer to the data at position idx. */ static __rte_always_inline void * vhost_rdma_queue_get_data(struct vhost_rdma_queue *queue, size_t idx) { return queue->data + queue->elem_size * idx; } /** * @brief Retrieve the next available descriptor index from the avail ring. * * Reads the descriptor index at the current position in the avail ring, * increments last_avail_idx, and returns the descriptor index. * * @param vq Pointer to the virtual queue. * @return Index of the first descriptor in the incoming request chain. */ static __rte_always_inline uint16_t vhost_rdma_vq_get_desc_idx(struct vhost_user_queue *vq) { uint16_t desc_idx; uint16_t last_avail_idx; /* Mask with ring size to handle wraparound */ last_avail_idx = vq->last_avail_idx & (vq->vring.size - 1); desc_idx = vq->vring.avail->ring[last_avail_idx]; /* Advance the local index tracker */ vq->last_avail_idx++; return desc_idx; } /** * @brief Get the next descriptor in the chain, if any. * * Checks the VRING_DESC_F_NEXT flag. If set, returns pointer to the next * descriptor using the 'next' field as an index into the descriptor table. * * @param table Base address of the descriptor table. * @param desc Current descriptor. * @return Pointer to next descriptor, or NULL if end of chain. */ static __rte_always_inline struct vring_desc * vhost_rdma_vring_get_next_desc(struct vring_desc *table, struct vring_desc *desc) { if (desc->flags & VRING_DESC_F_NEXT) return &table[desc->next]; return NULL; } /** * @brief Add a used descriptor entry to the used ring. * * Records that a buffer has been consumed by the host/device, including its * original descriptor index and the number of bytes written. * * Uses memory barriers to ensure ordering before updating used->idx. * * @param vq Virtual queue. * @param idx Descriptor index being returned. * @param len Number of bytes written (for writeable descriptors). */ static __rte_always_inline void vhost_rdma_queue_push(struct vhost_user_queue *vq, uint16_t idx, uint32_t len) { struct vring_used *used = vq->vring.used; uint16_t slot = used->idx & (vq->vring.size - 1); used->ring[slot].id = idx; used->ring[slot].len = len; /* Full memory barrier before incrementing idx to ensure visibility */ rte_smp_mb(); used->idx++; rte_smp_mb(); } /** * @brief Notify the frontend (guest) about used descriptor updates. * * Calls into the DPDK vhost library to signal the guest via eventfd or doorbell. * * @param vid Virtual host device ID. * @param vq Pointer to the virtual queue that needs notification. */ static __rte_always_inline void vhost_rdma_queue_notify(int vid, struct vhost_user_queue *vq) { rte_vhost_vring_call(vid, vq->id); } /** * @brief Translate Guest Physical Address (GPA) to Virtual VA in host. * * Wrapper around DPDK's rte_vhost_va_from_guest_pa(). This function performs * address translation using the guest memory map provided through vhost-user. * * @param mem Pointer to vhost memory region mapping. * @param gpa Guest physical address to translate. * @param len [in/out] On input: requested length; on output: actual mapped length. * @return Host virtual address corresponding to GPA, or 0 on failure. */ static __rte_always_inline uint64_t gpa_to_vva(struct rte_vhost_memory *mem, uint64_t gpa, uint64_t *len) { assert(mem != NULL); return rte_vhost_va_from_guest_pa(mem, gpa, len); } int vhost_rdma_construct(struct vhost_rdma_device *dev, const char *path, int idx); void vhost_rdma_net_construct(struct vhost_user_queue *queues, int idx); void vs_vhost_rdma_net_setup(int vid); void vhost_rdma_destroy(const char* path); int vhost_rdma_pool_init(struct vhost_rdma_pool* pool, const char* name, uint32_t num, uint32_t size, bool start_zero, void (*cleanup)(void*)); void* vhost_rdma_pool_get(struct vhost_rdma_pool* pool, uint32_t idx); void vhost_rdma_pool_free(struct vhost_rdma_pool* pool, uint32_t idx); void* vhost_rdma_pool_alloc(struct vhost_rdma_pool* pool, uint32_t *idx); void vhost_rdma_pool_destroy(struct vhost_rdma_pool* pool); extern struct vhost_rdma_device g_vhost_rdma_dev[MAX_VHOST_RDMA_DEV_NUM]; extern struct vhost_rdma_net_dev g_vhost_rdma_net_dev[MAX_VHOST_RDMA_DEV_NUM]; #ifdef __cplusplus } #endif #endif /* VHOST_RDMA_H_ */ 这段也是
12-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值