首页
社区
课程
招聘
[原创]NVIDIA FECS 上下文切换固件逆向笔记:9797 条指令全注释(AD102/Ada)
发表于: 2天前 282

[原创]NVIDIA FECS 上下文切换固件逆向笔记:9797 条指令全注释(AD102/Ada)

2天前
282

一、这是什么

NVIDIA 的 GPU 里跑着一个小单片机(Falcon 核),专职当"上下文切换管家"——把引擎的几万个寄存器状态在内存和引擎之间搬进搬出。本文逆向的就是它的固件:ctxsw_ucode/ada/ad102/net/NETA_fecs_ucode_img.h(公开驱动源码包里以 C 数组内嵌)。

规模:IMEM 30,715 字节 / 9,797 条指令;DMEM 5,656 字节。全部指令已逐条注释(98.2% 带中文语义),结构化 C 15,396 行 / 170 个函数。产物见附件。

二、固件从哪来

抽出两个二进制段:

  • fecs_inst.bin 30,715 B:IMEM 指令区,覆盖 0x0–0x77FB
  • fecs_data.bin 5,656 B:DMEM 初值,非零仅 0x0–0x9F0,开头是构建戳 "Nov 19 2021 19:36:12"

版本锁 MAJORV=0x58703102 与驱动账本一致。反汇编用 envytools envydis(fuc6),先拿驱动包里的 ACR 语料(官方 objdump + 裸镜像 2246 条)逐字节校准后才信任全量输出。

三、体系结构:睡-醒-干活-再睡

入口 0x0:栈顶 0x2800 → lcall main(0x7747) → 返回即停机。main 是经典嵌入式循环:

init(0x2BA3)
for(;;) {
    sleep $p2                ; 低功耗等待;p2 = "三环全空"
    raw = iord(0x18700)      ; 命令寄存器
    D[0x12D4] = (raw>>16)&0x1F
    lcall 0x1FC9             ; ① 主分派器:排空环 B
    lcall 0x4656             ; ② 周期任务 A:排空环 C
    lcall 0x7292             ; ③ 周期任务 B:排空环 A(ctxsw 在这里)
    p2 = 三对读写指针全部相等  ; 没活才干睡
}

四、中断模型:每引擎一位 + 一台分拣机

中断处理器(0x1D81)读 I[0x200]:

  • bit2 = 主机消息:从硬件队列 I[0x1A00]/I[0x1900] 取 (命令,参数),经分拣器 sub_1D12 路由
  • bit8+n = 第 n 个引擎的事件位:
    • bit0 → 环A(0x10000,0) ← 这就是上下文切换事件
    • bit1 → 环B(0x4) bit2 → 环C(0x10002) bit3 → 引擎错误上报(码 0x20/0x22) bit6/7 → 环B

三条内部环是固件自己的软件队列,16 槽 × 8B =(命令,参数),指针带环绕位:

环 数组 读/写指针 生产者 消费者 满码
A 0x132C 0x136C / 0x1370 sub_60B 周期任务B 7292 0x10
B 0x1374 0x13B4 / 0x13B8 sub_681 主分派器 1FC9 0x12
C 0x15B8 0x15F8 / 0x15FC sub_1C96 周期任务A 4656 0x13

五、ctxsw 的触发条件(反汇编为证)

事件 → 环A(0x10000) → 周期任务B:
0x747F: cmp b32 $r10 $r9        ; 命令 == 0x10000 ?
0x7485: lbra 0x76E1             ; → lcall 0x70E5 判定

0x70E5 判定核心(原文):
0x70F8  iord $r11 I[0x2C000]    ; 现役上下文描述符(bit31=有效)
0x70FF  iord $r1  I[0x2C100]    ; 请求的上下文描述符
0x7123  cmp 两个地址部分
0x7125  bra e 0x7162            ; 相同 → 旁路(不切)
0x7138  lcall 0x64AD            ; ① 保存/准备
0x713C  iowr I[0x2C000] $r1     ; ★ 提交:现役 ← 请求
0x7154  lcall 0x5850            ; ② 恢复/验证

切换四条件:① 现役描述符有效 ② 请求描述符有效 ③ 地址不同 ④ 门控 D[0x13F0] 未锁定。
地址相同走旁路(0x7162):清 0x2DC00、外部写 BAR0 0x4041B4、置 D[0x12BC]——"无需切换"的回执。

六、切换执行:编程搬运机构 + XD 块搬移

两个千行级函数是左右手(0x64AD / 0x5850,骨架同模):

  • 忙标志 I[0x21200] 写 0x40000 / 0x80000;通道选择 I[0x1AC00] 写 1 / 8
  • 轨迹端口 I[0x20A00] 打签名 0xC0FFEEB0 / 0xC0FFEEB1(固件作者的彩蛋)
  • 用引擎号(I[0x2DE00]&0x3F)拼命令字,向 I[0x22A00] 下发单元复位/使能序列(0x37_08 / 0x32_0D / 0x36_09 …)
  • 0x5850 调 0x1196"状态存储编程器"(全固件只被调 1 次,带幂等缓存),建 DMEM 0x1528 处 12 字节/引擎的状态表,写全局描述符 0x418810 / 0x419848 / 0x419E00(BAR0 地址)
  • 块数据走 XD 搬移单元(sub_2DE1):设 $xdbase/$xtargets,xdld/xdst 选方向,xdwait 等完成
  • 结束前三处影子对比,不符报诊断码 0x32 / 0x38 / 0x40

诊断通道 sub_5C0:错误码写 I[0x20600] → 触发 I[0x30700]=1 → 等 I[0x30600] ack;码 0x24/0x48/0x49 = 致命停机。

七、与驱动账本对上了

同目录 NETA_ctx_state_store.h(2MB)就是固件要执行的"搬运清单"。全量解析:52 段,合计 5,463 个 32 位字 ≈ 22KB/上下文。

  • 按层次:GPC 2264 / SYS 1273 / FBP 817 / TPC 258+119 / PPC 86 …
  • 按功能:性能监视 PM 占 63%(PMMGPC 1476 + PMMSYS 804 + PMMFBP 804 + 控制路由)
    • 每实例 9 种寄存器共 66 字,实例数 GPC 22 / SYS 12 / FBP 12 —— 与微码遍历的"引擎 0x8000、槽 0x200"步进一一对应

最硬的交叉验证:微码写 BAR0 0x405830,在账本里查到它就是 NV_PGRAPH_PRI_DS_TGA_CONSTRAINTLOGIC_BETA——微码的"引擎空间"= BAR0 寄存器空间,一字不差。而 0x418810 / 0x418EA0 / 0x419xxx 等地址不在账本里——它们是 ctxsw 硬件自身的控制寄存器,不是上下文状态。

八、产物(见附件压缩包)

  • fecs_annotated.html / .txt:全部 9,797 条指令逐条注释(函数据调用目标切分,端口/DMEM/函数名全标注)
  • fecs_doc.html:图文版完整笔记(含 4 张 SVG 结构图,浏览器打开)
  • fecs_inst.asm:原始反汇编;fecs_inst.c / fecs_structured.c:C 转译(平铺/结构化)
  • 工具脚本:extract.py(抽段)asm2c.py(转C)cfstruct.py(结构化)annotate.py(逐条注释)dis.py / scan.py

九、还没坐实的(欢迎接力)

  1. 12 字节状态表三字段与账本逐字段对齐
  2. I[0x22A00] 命令流逐条语义
  3. 0x64AD / 0x5850 谁是 save 谁是 restore 的决定性证据
  4. 上层触发场景(GSP 固件侧)

本文全部结论以反汇编实测为准,推断处已标注;工具链与地址均可回查。欢迎讨论指正。


[招生]科锐逆向工程师培训(2026年11月13日实地,远程教学同时开班, 第57期)

上传的附件:
收藏
点赞 ・0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回