
PVE 9.2 + Ryzen 7000 核显动态直通 fnOS 完整教程(实测版)
目标:fnOS 虚拟机运行时独占 AMD 核显做视频硬解;VM 关闭 / 宿主机重启 / 异常断电后核显自动回归宿主机,物理屏幕可随时 debug。
方案:hookscript 动态绑定(纯 sysfs,不依赖 driverctl),不做任何持久化的 vfio 绑定、不拉黑 amdgpu、不设开机自启。
实测成果:飞牛影视 4K HEVC 播放 CPU 0% 占用,负载全在 GPU。
一、实测环境
项 值 宿主机 Proxmox VE 9.2(内核 7.0.14-15-pve),ext4 + GRUB 引导 CPU AMD Ryzen 7 7700(Raphael,核显 device id 1002:164e,RDNA2/gfx1035)主板 MSI(BIOS 需支持 Above 4G Decoding) 核显 PCI 地址 0000:12:00.0(下文全部以此为例,替换成你自己的)VM ID 102,fnOS 1.2.0505(legacy/SeaBIOS 引导),8C / 8G,VirtIO SCSI既有直通 0000:11:00.0SATA 控制器挂 hostpci0(fnOS 数据盘依赖,不能动)→ 核显挂 hostpci1最终成果 vainfo 见 radeonsi + HEVC VLD;影视 4K 硬解 CPU 0% 版本硬门槛:fnOS ≥ v1.1.29(2026-04 推送)才支持 AMD GPU 影视硬转码。旧版本最多 "识别到" 显卡,无法硬解——先在 fnOS 网页端升级系统和影视 App,其他都白搭。
二、方案与原理
核显作为 PCI 设备直通给 VM,由 hookscript 在 VM 生命周期中动态切换驱动归属:
- VM start 前:核显从宿主机
amdgpu解绑 → 交给vfio-pci→ QEMU 接管 - VM stop 后:从
vfio-pci解绑 → 归还amdgpu→ 宿主机 TTY / 显示恢复 - 绑定状态不落盘:宿主机重启 / 断电后核显天然回归
amdgpu,永远保底可用
Raphael 这类 APU 核显没有独立 vBIOS 芯片(vBIOS 内嵌在主板 BIOS 里),直通后 guest 读不到 → 必须从宿主机提取 vBIOS 用
romfile喂给 QEMU(本文核心步骤之一)。明确不做的事(保证任何情况下宿主机可救):
- ❌ 不写 amdgpu blacklist
- ❌ 不写
vfio-pci ids=预绑定 - ❌ VM 不设
onboot: 1
三、BIOS 设置
- Above 4G Decoding → 开启(必须)
- CSM / Legacy Boot → 关闭,纯 UEFI
- Resizable BAR → 先不动;直通翻车时再回来关
- SVM / IOMMU 相关选项保持默认开启
四、宿主机准备
4.1 IOMMU 内核参数
确认引导方式:
[ -d /boot/efi ] && echo "EFI" || echo "BIOS" proxmox-boot-tool status 2>/dev/nullGRUB(ext4 安装,本机实测值)——编辑
/etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt pcie_acs_override=downstream"生效并重启:
update-grub rebootsystemd-boot(ZFS 安装):编辑
/etc/kernel/cmdline追加同样参数后proxmox-boot-tool refresh并重启。💡 新内核上
amd_iommu=on会报Unknown option - 'on',无害可忽略;真正起作用的是iommu=pt,AMD 平台 BIOS 开了 IOMMU 即默认启用。验证:
dmesg | grep -iE "AMD-Vi|iommu" | head -5 # 有 "Interrupt remapping enabled" / "iommu ... enabled" 即可 # 已有其他直通设备在正常工作,也说明 IOMMU 是通的4.2 vfio 模块
/etc/modules确保包含三行:vfio vfio_iommu_type1 vfio_pciupdate-initramfs -u -k all4.3 确认核显地址与复合体布局
lspci -nn | grep -i "12:00"Raphael 通常是多功能设备:
12:00.0 VGA compatible controller [1002:164e] ← 只直通这个 12:00.1 Audio device [1002:1640] ← HDMI 音频(可不带,QEMU 常自动带上) 12:00.2 Encryption controller [1022:1649] ← PSP,留宿主机 12:00.3/.4 USB controller ← 留宿主机,勿动五、提取 vBIOS(APU 必做)
这是 APU 直通与独显直通最大的差异点。 核显 ROM 不在设备上,guest 拿不到 ROM 时 amdgpu 会报
Unable to locate a BIOS ROM后拒绝初始化。唯一可靠的提取方法是 debugfs(要求宿主机
amdgpu正在驱动核显——开机后、VM 启动前最佳):# 1. 确认核显在 amdgpu 手里 lspci -k -s 12:00.0 # Kernel driver in use: amdgpu # 2. 从 debugfs 导出(amdgpu init 时已把 vBIOS 拷进内存) cat $(find /sys/kernel/debug/dri -name amdgpu_vbios | head -1) > /usr/share/kvm/raphael.rom # 3. 验证 ls -l /usr/share/kvm/raphael.rom # 几十~几百 KB od -A x -t x1 /usr/share/kvm/raphael.rom | head -2 # 开头必须 55 aaROM 合格标准:
55 aa开头;头文件第 3 字节 × 512 = 文件大小(如55 aa 57 e9→ 0x57×512 = 44544,头尾自洽)。补齐到 64K(2 的幂,避免个别内核 / 固件拒绝非对齐尺寸):
python3 -c "data=open('/usr/share/kvm/raphael.rom','rb').read(); open('/usr/share/kvm/raphael-64k.rom','wb').write(data+b'\xff'*(65536-len(data)))" ls -l /usr/share/kvm/raphael-64k.rom # 65536⚠️ 不要用 sysfs dump(
echo 1 > /sys/bus/pci/devices/.../rom):APU 上会Permission denied——核显没有暴露 ROM BAR 给 sysfs。
⚠️ TechPowerUp vBIOS 库没有 Raphael 核显的 ROM(其 "RX660 iGPU Raphael" 条目实为 RX 6600 独显 deviceid73FF的 BIOS,勿用)。
💡 find 无输出时先mount -t debugfs none /sys/kernel/debug再试。六、hookscript(动态绑定核心)
6.1 脚本
mkdir -p /var/lib/vz/snippets cat > /var/lib/vz/snippets/gpu-passthrough.sh <<'EOF' #!/bin/bash # 动态核显直通:两阶段均实测驱动归属,杜绝假成功 GPU="0000:12:00.0" # ← 改成你的核显地址 LOG=/var/lib/vz/snippets/gpu-passthrough.log DEV=/sys/bus/pci/devices/$GPU OVR=$DEV/driver_override log() { echo "$(date '+%F %T') [$1] $2" >> $LOG; } cur() { local l; l=$(readlink "$DEV/driver" 2>/dev/null); if [ -n "$l" ]; then basename "$l"; else echo "(unbound)"; fi; } case "$1" in pre-start) if [ "$(cur)" = "amdgpu" ]; then echo "$GPU" > /sys/bus/pci/drivers/amdgpu/unbind 2>>$LOG || { log "$1" "FAILED: amdgpu unbind"; exit 1; } fi echo "" > "$OVR" 2>>$LOG echo vfio-pci > "$OVR" 2>>$LOG || { log "$1" "FAILED: set override"; exit 1; } echo "$GPU" > /sys/bus/pci/drivers/vfio-pci/bind 2>>$LOG \ || echo "$GPU" > /sys/bus/pci/drivers_probe 2>>$LOG sleep 1 if [ "$(cur)" = "vfio-pci" ]; then log "$1" "bound $GPU to vfio-pci (verified)" else log "$1" "FAILED: driver is '$(cur)' (expect vfio-pci)" exit 1 fi ;; post-stop) sleep 2 c="$(cur)" if [ "$c" = "amdgpu" ]; then log "$1" "already on amdgpu, nothing to do" exit 0 fi if [ "$c" = "vfio-pci" ]; then for i in 1 2 3 4 5; do echo "$GPU" > /sys/bus/pci/drivers/vfio-pci/unbind 2>>$LOG || { log "$1" "unbind try $i failed"; sleep 2; } c="$(cur)"; [ "$c" != "vfio-pci" ] && break done if [ "$c" = "vfio-pci" ]; then log "$1" "FAILED: still on vfio-pci after retries" exit 1 fi fi # 空串清除("none" 在新内核是字面量不是清除符!);清空后读回是 "(null)",属正常 echo "" > "$OVR" 2>>$LOG clr="$(cat "$OVR" 2>/dev/null)" if [ -n "$clr" ] && [ "$clr" != "(null)" ]; then log "$1" "FAILED: override not cleared (still '$clr')" exit 1 fi echo "$GPU" > /sys/bus/pci/drivers/amdgpu/bind 2>>$LOG \ || echo "$GPU" > /sys/bus/pci/drivers_probe 2>>$LOG sleep 1 if [ "$(cur)" = "amdgpu" ]; then log "$1" "released $GPU back to amdgpu (verified)" else log "$1" "FAILED: driver is '$(cur)' (expect amdgpu)" exit 1 fi ;; esac exit 0 EOF chmod +x /var/lib/vz/snippets/gpu-passthrough.sh6.2 为什么这么写(新内核 sysfs 的坑,全部实测)
driver_override在设备目录下(/sys/bus/pci/devices/.../driver_override),不在driver软链指向的驱动目录里- 设备处于绑定态时写空串清 override 会 EINVAL;清 override 必须在设备 unbound 状态下做
echo none > driver_override不是清除——"none" 是字面量值,会挡住之后所有驱动的 bind(报 ENODEV 且 dmesg 零痕迹,极难排查)- 清空后
cat读回是字面量(null)(内核 NULL 指针的 sysfs 打印),校验逻辑必须放行它 drivers_probe写入几乎恒返回 0,不能作为成功依据——每阶段结束必须readlink driver实测设备归属
⚠️ 不要用 driverctl:实测在新内核上
unset-override失败也返回 exit 0(假成功),会把设备永久留在 vfio 上还骗你 "已归还"。6.3 修改脚本后的必做自检
换脚本 / 改脚本后,务必手动验证两个方向(不经过 qm):
bash -x /var/lib/vz/snippets/gpu-passthrough.sh pre-start lspci -k -s 12:00.0 | grep "Kernel driver" # 期望: vfio-pci tail -1 /var/lib/vz/snippets/gpu-passthrough.log # 期望: bound ... (verified) bash -x /var/lib/vz/snippets/gpu-passthrough.sh post-stop lspci -k -s 12:00.0 | grep "Kernel driver" # 期望: amdgpu tail -1 /var/lib/vz/snippets/gpu-passthrough.log # 期望: released ... (verified)两个
(verified)都拿到,脚本才算在这台内核上验证通过。之后每次qm stop后都tail一下日志确认有新条目——hookscript 文件为空 / 残缺时会被静默跳过(VM 照样能启动,极难察觉)。七、VM 配置
7.1 创建 / 改造 VM
项 值 说明 机型 i440fx(默认) fnOS 是 legacy 引导就别动 machine,最稳 BIOS SeaBIOS(默认) OVMF 仅适合 EFI 安装的 fnOS CPU host,≥4 核 内存 ≥8G 磁盘 VirtIO SCSI 网络 VirtIO 显示 无(none) x-vga=1 的配套要求,见 7.2 PCI 设备 见 7.2 核显挂 hostpci1(hostpci0 若已有 SATA 直通别动) 关键命令(VMID / 地址替换成自己的):
qm set 102 --onboot 0 # 铁律:不自启 qm set 102 --hookscript local:snippets/gpu-passthrough.sh qm set 102 --vga none qm set 102 --hostpci1 0000:12:00.0,romfile=raphael-64k.rom,x-vga=1 qm config 102 # 核对最终配置应包含:
onboot: 0 hookscript: local:snippets/gpu-passthrough.sh hostpci0: 0000:11:00.0 # 既有 SATA 直通(如有) hostpci1: 0000:12:00.0,romfile=raphael-64k.rom,x-vga=1 vga: none7.2 为什么必须
x-vga=1+vga none(本方案最隐蔽的坑)核显是 VGA class 设备。在 i440fx 机型下,它的 ROM BAR 会落在 x86 legacy VGA BIOS 影子区 0xC0000——那里早就被虚拟显卡(Bochs/SeaVGABIOS,约 39K)占着。结果就是:
- 只写
romfile不够:QEMU 命令行里明明带着 romfile,但 guest 读 ROM BAR 拿到的是 SeaVGABIOS(头部55 aa 4d e9),amdgpu 解析不出 ATOM 表,照样Unable to locate a BIOS ROM x-vga=1让你喂的 vBIOS 升级为主 VGA BIOS,虚拟显卡退场(guest 里00:02.0Bochs 消失),ROM BAR 读回55 aa 57 e9,amdgpu 立刻认
代价:PVE 的 noVNC 控制台黑屏(虚拟显卡没了,by design)。fnOS 走 SSH / 网页管理,不受任何影响。回退:
qm set 102 --vga std并去掉 x-vga。备选路线(q35):
qm set 102 --machine q35 --vga std+--hostpci1 ...,romfile=...,pcie=1。q35 下 ROM BAR 能拿到独立 MMIO 地址避开 0xC0000。适合 EFI 安装的 fnOS 或 i440fx 路线翻车时。注意pcie=1是 q35 专属参数。八、启动与验证
8.1 启动
qm start 102 qm status 102 # 期望: status: running启动瞬间出现这两条属正常,忽略:
error writing '1' to '.../12:00.0/reset': Inappropriate ioctl for device—— Raphael 没有硬件复位机制,QEMU 提示后继续11:00.0 BAR 5: failed to create dma-buf ... Invalid argument—— QEMU 10.x 新特性在非页对齐 BAR 上的已知警告,上游已降级为 warning,正常直通走 mmap 兜底不受影响
8.2 fnOS 内验证(SSH)
# 1. GPU 在,且只有它(Bochs 虚拟显卡应消失) lspci | grep -i vga # 期望: 00:11.0 VGA compatible controller: AMD/ATI Raphael # 2. 驱动初始化成功(无 Fatal error) dmesg | grep -iE "amdgpu|BIOS ROM" | tail -8 # 期望: Initialized amdgpu 3.64.0 ... / vcn_dec、vcn_enc、jpeg_dec ring 就绪 # 不应出现: Unable to locate a BIOS ROM / Fatal error during GPU init # 3. 渲染节点(核心判据) ls -l /dev/dri # 期望: card0 + renderD128 # 4. VAAPI 入口(没有就 apt install -y vainfo) vainfo 2>&1 | head -15 # 期望: Driver version: Mesa Gallium ... (radeonsi, raphael_mendocino ...) # VAProfileHEVCMain : VAEntrypointVLD ← 4K HEVC 硬解入口Cannot find any crtc or sizes是 headless(无显示器)的正常提示,无害。8.3 影视硬解
飞牛影视 → 设置 → 转码 → 开启硬件解码 → 播一部 4K HEVC → CPU 个位数百分比乃至 0% = 硬解生效。
九、热切稳定性测试
for i in 1 2 3; do qm stop 102; sleep 30; qm start 102; sleep 30; done每轮检查:
- fnOS 里 renderD128 还在、硬解正常
- stop 后宿主机
lspci -k -s 12:00.0回到amdgpu,日志新增released ... (verified) - 物理屏幕 TTY 正常
⚠️ 循环间隔必须 30 秒起步。Raphael 没有硬件复位机制,间隔过短的连续 stop/start 会把设备 / VM 状态打坏(实测 5 秒间隔连跑 3 轮后 VM 内存异常)。翻车了不用慌:宿主机整机重启即完全恢复。
十、注意事项(踩坑实录浓缩)
- fnOS 版本门槛:AMD 硬解需 fnOS ≥ 1.1.29,影视 App 同步升级。旧版本一切白搭。
- APU 没有独立 vBIOS:必须 debugfs 提取(见第五节);sysfs dump 和显卡 ROM 数据库两条路都走不通。
- romfile 单独不够:VGA 设备的 ROM BAR 会撞进 0xC0000 影子区被虚拟显卡的 SeaVGABIOS 冒充——必须
x-vga=1+vga none(或切 q35 + pcie=1)。判别方法:guest 里读 ROM BAR,头部4d e9(39K)= SeaVGABIOS 撞车;57 e9(或你提取的尺寸字段)= 自己的 vBIOS 到位。 - 不要用 driverctl:新内核上假成功(失败也 exit 0)。直接写 sysfs 也有四个语义坑(见 6.2),本文 v6 脚本已全部规避。
- hookscript 静默失败风险:文件为空或残缺时 hookscript 会被静默跳过,且设备残留 vfio 时 QEMU 反而能启动——改完脚本必须
bash -x双向自检(6.3),日常 stop 后 tail 日志确认新条目。 - Raphael 无硬件复位:启动时的 reset ENOTTY 警告无害;连续热切必须间隔 ≥30 秒;异常后宿主机重启即恢复。
- 无害警告清单(见到别慌):reset ENOTTY、SATA BAR 5 dma-buf EINVAL、
amd_iommu=onUnknown option、headless 下Cannot find any crtc or sizes。 - noVNC 黑屏:
vga: none的必然结果,fnOS 用 SSH / 网页管理不受影响。 - 两条铁律:VM 永远
onboot: 0;永远不往/etc/modprobe.d/加 amdgpu blacklist 或 vfio 预绑定——这是 "宿主机永远可救" 的底线。 - 复合体设备只直通 .0:12:00.2(PSP)、.3/.4(USB)留宿主机, guest 内不需要,直通了反而添乱。
十一、排错速查
症状 原因 处理 影视 "未检测到 GPU" fnOS < 1.1.29 先升级系统和影视 App 影视不认,但 renderD128 在、vainfo 正常 App / 版本适配 升级到最新;查飞牛社区显卡验证表 dmesg 报 Unable to locate a BIOS ROMvBIOS 没进 guest 查 romfile 是否挂上( ps -ef | grep kvm | grep romfile);再查 ROM BAR 内容(见注意事项 3)ROM BAR 读出头 55 aa 4d e9(约 39K)SeaVGABIOS 占了 0xC0000 x-vga=1+vga none(或 q35+pcie=1)amdgpu/bind报 ENODEV 且 dmesg 无 probe 痕迹driver_override 残留挡路 cat .../driver_override看值;unbound 态空串清除stop 后设备仍在 vfio-pci hookscript 没跑 / 失败 tail 日志查 FAILED;手动跑 bash -x 脚本 post-stop;无效则重启宿主机fnOS 里 BAR can't reservei440fx PCI 窗口不足 切 q35: qm set 102 --machine q35(fnOS legacy 引导通常可直起;不行回退--machine pc)start 报 VM is locked异常断电残留锁 qm unlock 102 && qm start 102VM 快速热切后内存异常 / 不稳 Raphael 无复位,状态未归零 宿主机重启即恢复;日常间隔 ≥30 秒 十二、日常使用速查
场景 操作 核显归属 日常 qm start 102后不管它fnOS(硬解) 需要排查宿主机 qm stop 102,等 30 秒宿主机 TTY PVE 炸了 / 断电重启 什么都不用做 宿主机(绑定不落盘,天然回归) 排查完恢复 qm start 102fnOS 看归还是否成功 tail /var/lib/vz/snippets/gpu-passthrough.log找 (verified)条目教程基于 2026-09 实测整理:PVE 9.2 / 内核 7.0.14-15-pve / Ryzen 7 7700 / fnOS 1.2.0505,最终成果 4K HEVC 硬解 CPU 0%。
- VM start 前:核显从宿主机
- 收藏支持反对打赏
- 暂无回复