bird/vgpu-unlock-blackwell

GitHub: bird/vgpu-unlock-blackwell

针对NVIDIA RTX 5090消费级GPU的vGPU解锁研究,通过19个二进制补丁成功绕过全部CPU端驱动验证,但最终受限于芯片上被硬件熔断关闭的VF PRIV寄存器,揭示了Blackwell架构vGPU由GSP固件直接驱动所带来的根本性障碍。

Stars: 0 | Forks: 0

# vgpu-unlock-blackwell 旨在消费级 NVIDIA Blackwell GPU (RTX 5090 / GB202) 上启用 vGPU 功能的研究项目。 **状态:受硬件限制。** 整个软件管线已可运行,但当访问在消费级芯片上被熔断关闭的硬件寄存器时,GSP 固件会崩溃。请参阅下文的[发现](#findings)。 ## 这是什么 一组二进制补丁、内核模块修改和自动化脚本,可绕过 NVIDIA 595.58.03 驱动中所有 CPU 端的 vGPU 验证,从而实现: - vGPU 类型注册(跨 3 种调度模式的 60 种 PRO 6000 配置文件) - 通过 VFIO 创建 mdev 设备 - 客户机 VM PCI 可见性(QEMU 将 GPU 视为 10de:2b85) - 设备 ID 欺骗(0x2b85 消费级 -> 0x2bb5 数据中心级) - 向 GSP 固件发送具有完整且有效参数的 BOOTLOAD RPC - GSP 固件接受并处理 BOOTLOAD 约 4 秒钟 GSP 在访问被熔断关闭的 VF PRIV 寄存器时崩溃。有关为什么在 Turing 上有效的 [vgpu_unlock](https://github.com/DualCoder/vgpu_unlock) 方法在这里行不通的原因,请参阅 [为什么 Blackwell 不同](#why-blackwell-is-different)。 ## 要求 - NVIDIA RTX 5090 (GB202) 或类似的 Blackwell 消费级 GPU - NVIDIA 驱动程序 595.58.03(kernel-open 源码) - 支持 KVM、VFIO、mdev 的 Linux 内核 6.x - `pci=realloc` 内核启动参数 ## 文件 | 文件 | 描述 | |------|-------------| | `nvidia-vgpu-vfio-stub.c` | 内核模块:VFIO 存根,mdev 驱动,设备 ID 欺骗,BOOTLOAD 编排(约 1770 行) | | `apply-binary-patches.py` | 针对 `nv-kernel.o_binary` 的 19 个二进制补丁(自验证,无专有代码) | | `patch-kbuild.sh` | Kbuild 修改,用于将存根编译进 nvidia.ko | | `vgpu-type-register.c` | 用户态工具,用于通过 NVA081 API 注册 vGPU 类型(约 590 行) | | `setup-vgpu.sh` | 完整设置:加载模块,注册类型,创建 mdev | | `teardown-vgpu.sh` | 干净卸载,禁用 SR-IOV 并卸载模块 | | `build-vgpu.sh` | 构建自动化 | | `test-vm-lspci.sh` | 启动 Alpine Linux VM 并扫描 PCI | | `boot-vm.sh` | QEMU 启动脚本 | ## 设置 ``` # 1. 下载 NVIDIA 595.58.03 驱动并解压 kernel-open # 2. 将我们的文件复制到 kernel-open 目录中 # 3. 应用补丁: python3 apply-binary-patches.py nvidia/nv-kernel.o_binary bash patch-kbuild.sh # 4. 在 conftest 中设置 NV_VGPU_KVM_BUILD=1: sed -i 's/#undef NV_VGPU_KVM_BUILD/#define NV_VGPU_KVM_BUILD 1/' conftest/generic.h # 5. 构建: make modules -j$(nproc) NV_KERNEL_MODULES="nvidia" CONFIG_OBJTOOL= # 6. 编译类型注册工具: gcc -o vgpu-type-register vgpu-type-register.c # 7. 运行 setup: sudo ./setup-vgpu.sh ``` ## 二进制补丁(共 19 个) | # | 函数 | VA | 描述 | |---|----------|-----|-------------| | 1-4 | kvgpumgrPgpuAddVgpuType | 0x37caff+ | 跳过 GSP 错误,PCI ID,标志,初始化检查 | | 5-7 | kvgpumgrCreateRequestVgpu | 0x384cb7+ | 跳过 PCI ID,标志,边界检查 | | 8 | kvgpumgrSendAllVgpuTypesToGsp | 0x37c684 | 存根,返回 NV_OK | | 9-10 | kvgpumgrGetPgpuIndex | 0x37c331+ | 跳过设备 ID 和标志检查 | | 11-12 | BOOTLOAD 处理程序 | 0x37b1d7+ | 存根用于 pre/post-bootload VF 资源分配 | | 13 | kfifoChidMgrFreeSystemChids | 0x1e5480 | 容忍 NULL 通道堆 | | 14 | destroyKernelHostVgpuDeviceShare | 0x379d60 | 防止在卸载时 GSP SHUTDOWN 挂起 | | 15-16 | BOOTLOAD 处理程序 | 0x37b068+ | 预分配 RPC 缓冲区,跳过 memset | | 17-18 | BOOTLOAD 处理程序 | 0x37b0a6+ | 跳过 memGetByHandle 循环,边界检查 | | 19 | BOOTLOAD 处理程序 | 0x37b0b8 | 加载 FB 基址,跳转至 BAR 填充 | ## 发现 ### 什么有效 - **相同的二进制文件**:GRID 和消费级驱动共享相同的 `nv-kernel.o_binary` 和 `gsp_ga10x.bin`(已通过 MD5 验证)。每个消费级驱动中都存在 vGPU 代码。 - **CPU 端绕过**:所有 19 个补丁成功绕过了验证。设备 ID 欺骗(0x2b85 -> 0x2bb5)在每一层都被接受。 - **GSP RPC 交付**:BOOTLOAD RPC(命令 0x20804001)带着完整参数到达 GSP:dbdf、gfid、vgpuType、numChannels、FB 地址、段信息。 - **GSP 处理**:GSP 接受 RPC 并运行 4 秒钟——在撞上硬件墙之前完成了大量的初始化工作。 - **时间分片模式**:`SET_VGPU_MIG_TIMESLICE_MODE`(命令 0x2080400f)在消费级硬件上成功执行。 - **客户机可见性**:通过 QEMU 启动的 VM 在其 PCI 总线上看到具有正确 vendor/device/class 的 GPU。 ### 阻碍因素 GSP 固件在访问位于 `0x111xxx` 的 `NV_PPRIV` 寄存器时崩溃(Xid 119): ``` REG 0x111424 via RM aperture: 0xbadf1002 REG 0x111424 via raw BAR0: 0xbadf1002 REG 0x000000 via RM aperture: 0x1b2000a1 (BOOT_0 -- works fine) Error code: NV_PPRIV_SYS_PRI_ERROR_CODE_FECS_PRI_TIMEOUT ``` 这些寄存器在消费级 GPU 上被**硬件熔断关闭**。通过读取 RM 的特权 IO 孔径(`osGpuReadReg032`)和原始 BAR0 ioremap 均证实了这一点——两者都返回完全一致的 BADF 值。一个正常工作的寄存器(位于 0x000000 的 `NV_PMC_BOOT_0`)通过这两种路径都返回了正确的值,证明读取机制是有效的,且这并不是 PRIV 防火墙问题。 ### 已测试并排除的情况 | 假设 | 测试 | 结果 | |-----------|------|--------| | PRIV 防火墙(非熔断) | 通过 `osGpuReadReg032` 读取 vs 原始 BAR0 | 两者返回相同的 BADF | | FB 地址冲突 | 在 4GB 和 16GB VRAM 偏移量处测试 | 崩溃情况相同 | | SR-IOV 对比时间分片调度 | 测试标志为 0x008e, 0x0045, 0x0020 的类型 | 全部崩溃 | | 缺少时间分片模式启用 | 调用命令 0x2080400f(返回 NV_OK) | 仍然崩溃 | | GSP 中设备 ID 不匹配 | 两者均为 GB202(从 BOOT_0 获取的芯片 ID 0x1b2) | 芯片匹配 | | VF 存在/不存在 | 使用和不使用 `pci_enable_sriov` | 均崩溃 | | 不正确的 RPC 缓冲区 | 导出所有字段,验证完整 | 缓冲区有效 | ### 为什么 Blackwell 不同 由 [DualCoder](https://github.com/DualCoder) 发起的原始 [vgpu_unlock](https://github.com/DualCoder/vgpu_unlock) 项目成功在 Turing 消费级 GPU 上启用了 vGPU。由于根本性的架构变化,该方法**无法在 Blackwell 上奏效**: | | Turing (vgpu_unlock 有效) | Blackwell (受阻) | |---|---|---| | vGPU 插件运行于 | **CPU**(内核模块) | **GSP**(RISC-V 固件) | | 插件代码可修改 | 是——内核模块中的二进制补丁 | 否——WPR 内存 + FMC 签名验证 | | 硬件寄存器访问 | 通过可打补丁的 OS 函数(`osGpuReadReg032`) | 来自 RISC-V 核心的直接 MMIO | | 寄存器访问拦截 | 钩住读取函数,返回欺骗值 | 不可能——固件拥有原始硬件访问权限 | | VF PRIV 寄存器 | 不需要(Turing vGPU 在 PF 上进行时间分片) | 在 BOOTLOAD 期间**总是被访问**,无论调度模式如何 | | SR-IOV 要求 | 无(Turing 消费级没有 SR-IOV) | 消费级有 SR-IOV (totalvfs=1) 但 VF PRIV 寄存器被熔断关闭 | 在 Turing 上,vGPU 插件作为内核模块中的 CPU 代码运行。当它试图读取一个被熔断的寄存器时,`vgpu_unlock` 项目可以拦截该读取并返回一个有效值。在 Blackwell 上,vGPU 插件运行在具有直接内存映射硬件访问权限的专用 RISC-V 核心上的 GSP 固件内。没有可以拦截的软件层。 ## 关键技术细节 - **OBJGPU 设备 ID**:偏移 +0x7f6(16 位 LE 字段) - **PGPU 表**:`[g_pSys+0x1f8]`,每个条目步长 0xbbb80,PCI ID 位于 +0x48 - **sVgpuUsmTypes**:设备 0x2bb5 的 60 个条目(类型 1460-1620,3 个标志组) - **GSP BOOTLOAD RPC**:命令 0x20804001,通过 `rpcRmApiControl_GSP` 传递 0x19d8 字节的参数 - **NVA084 BOOTLOAD ctrl**:命令 0xa084010d,参数 0x648 字节 - **SET_VGPU_MIG_TIMESLICE_MODE**:命令 0x2080400f(在消费级 GPU 上成功) - **PCI 重置恢复**:`echo 1 > /sys/bus/pci/devices/.../reset`(在 GSP 崩溃后无需重启即可恢复 GPU) - **熔断寄存器**:0x111424 (VF PRIV),0x009080 (PRI 超时),0x088000 (BIF)——通过 RM 孔径和原始 BAR0 读取均返回 0xbadf ## 贡献 / 后续步骤 软件层面已解决。剩下的挑战在于硬件。继续这项工作最具可操作性的路径是: ### FPGA 拦截器(最有希望) 放置在 GPU 和 PCIe 插槽之间的 FPGA 板可以拦截并仿真对被熔断寄存器的访问。当 GSP 通过 PRIV 环读取 `0x111424` 并遇到 PRI 超时时,一个监视寄存器总线的 FPGA 可以注入一个有效的响应。这将需要: - 具备 PCIe Gen5 能力的 FPGA(或一个接入 PRIV 环信号的转接卡) - 从数据中心 GPU(或从 GSP 固件的初始化代码)逆向工程预期的寄存器值 - 构建 GSP 在 BOOTLOAD 期间触及的所有熔断地址的寄存器映射 GSP 仅运行约 4 秒就崩溃了,表明需要仿真的寄存器访问次数相对较少。 ### GSP 固件分析 GSP 崩溃缓冲区包含带时间戳的跟踪条目,如果了解 GSP 固件的运行时内存布局,这些条目就可以被解码。GSP 邮箱寄存器(`MAILBOX(0)` = 崩溃时的 PC?)可以映射到 `gsp_ga10x.bin` RISC-V ELF 中的特定函数,从而揭示究竟是哪个寄存器访问导致了崩溃以及 GSP 期望的值。 ### 替代 GPU 架构 Ada Lovelace (RTX 4090) 使用类似的 GSP 架构,但可能有不同的熔丝编程。Hopper(H100 与消费级变体)是另一个候选者。本项目中的方法(二进制补丁,设备 ID 欺骗,RPC 注入)可以直接迁移——只需更改特定的补丁地址。 ### 驱动版本探索 较旧的 Blackwell 驱动版本或测试版可能具有较少寄存器依赖的不同 GSP 固件。补丁脚本的自验证方法使测试变得简单:如果原始字节不匹配,补丁将干净地失败。 ## 许可证 我们的原创代码(存根,补丁,脚本,工具)在 MIT 许可证下发布。 本项目不包含任何 NVIDIA 专有二进制文件。您必须单独获取 NVIDIA 驱动程序。二进制补丁在特定的字节偏移量处修改驱动程序——不分发任何专有代码。 ## 致谢 本研究建立在 [DualCoder](https://github.com/DualCoder) 针对 Turing GPU 的 [vgpu_unlockhttps://github.com/DualCoder/vgpu_unlock) 项目以及更广泛的 GPU 虚拟化研究社区的基础之上。本 README 中的 Turing 与 Blackwell 架构比较直接阐明了为什么那种开创性的方法无法扩展到带有 GSP 固件的现代 NVIDIA GPU 上。
标签:Blackwell架构, GB202, GPU虚拟化, GSP固件, KVM, Linux内核, mdev, QEMU, RTX 5090, SR-IOV, VFIO, vGPU, Web报告查看器, 二进制补丁, 内联执行, 安全渗透, 客户端加密, 应用安全, 底层研究, 数据中心显卡, 消费级显卡, 硬件熔断寄存器, 硬件破解, 硬件解锁, 绕过验证, 虚拟化技术, 设备ID伪装, 身份验证强制, 逆向工具