在玩转 PVE(Proxmox VE)All in One 或是高帧率云游戏主机的路上,AMD 显卡(如 RX 580、RX 5700XT、RX 6600XT 等)因其出色的性价比和对 macOS 的友好度,成为了很多折腾党的不二之选。
然而,当你满怀信心在 PVE 里建好 Windows 11 虚拟机,勾选了 PCIe 直通,开机装完 AMD 驱动后,迎来的却大概率是设备管理器里那个冰冷的黄色感叹号——“由于该设备有问题,Windows 已将其停止。(代码 43)”。
这几乎是每个搞显卡直通的人都会撞上的“成人礼”。本文不讲虚的空话,直接从底层原理出发,给出全网最详尽的排查步骤,并提供针对 AMD 显卡特有**“重置缺陷(Reset Bug)”的解决脚本**。
一、 为什么 AMD 显卡直通会报 Code 43?
要解决问题,先要揪出幕后黑手。AMD 显卡直通报 Code 43,主要有以下四个元凶:
- 宿主机抢占驱动:PVE 宿主机在开机时已经加载了
amdgpu或radeon驱动,把显卡“据为己有”,导致直通给 VM 时设备状态不干净。 - KVM 虚拟化特征暴露:AMD 驱动检测到自己运行在虚拟机里,主动“罢工”(虽然近年 AMD/NVIDIA 官方声称放开了虚拟化限制,但安全起见,依然需要对 CPU 进行隐藏伪装)。
- 缺少干净的 VBIOS:物理显卡的 VBIOS(ROM)在宿主机引导时已经被污染或读取不完整,虚拟机拿不到正确的 ROM 映射。
- AMD 著名的 Reset Bug(重置缺陷):这是最头疼的问题。AMD 很多显卡不支持标准的 FLR(Function Level Reset)。虚拟机第一次开机正常,一旦重启或关机,显卡无法恢复到初始未初始化状态,再次开机必然 Code 43。
二、 排查与解决步骤:四步法
第一步:彻底解放宿主机,完善 IOMMU 配置
必须确保 PVE 宿主机完全不触碰这张显卡。
1. 修改 GRUB / Systemd-boot
编辑 PVE 的启动配置文件。
如果是 GRUB 引导:
nano /etc/default/grub
找到 GRUB_CMDLINE_LINUX_DEFAULT,修改为:
- Intel CPU:
quiet intel_iommu=on iommu=pt initcall_blacklist=sysfb_init - AMD CPU:
quiet amd_iommu=on iommu=pt initcall_blacklist=sysfb_init
避坑提示:
initcall_blacklist=sysfb_init非常关键!它能防止 PVE 系统在启动时抢先占用显卡的 UEFI 帧缓冲区,避免直通时显卡被死锁。
保存后更新 grub:
update-grub
2. 屏蔽 AMD 驱动
在 PVE 宿主机中,将 AMD 显卡驱动拉入黑名单,强制让 vfio-pci 接管显卡。
nano /etc/modprobe.d/pve-blacklist.conf
添加以下内容:
blacklist radeon
blacklist amdgpu
3. 绑定 GPU 硬件 ID 到 VFIO
使用 lspci -nn 找到你的显卡和其自带音频设备的 ID。例如:
01:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Navi 23 [Radeon RX 6600/6600 XT / 6600M] [1002:73ff] (rev c1)
01:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Navi 21 HDMI Audio [1002:ab28]
这里的 1002:73ff 和 1002:ab28 就是我们要锁定的 ID。
编辑 vfio.conf:
nano /etc/modprobe.d/vfio.conf
写入:
options vfio-pci ids=1002:73ff,1002:ab28 disable_vga=1
保存后,运行 update-initramfs -u -k all 并重启 PVE 宿主机。
第二步:正确配置 Win11 虚拟机硬件
进入 PVE 网页后台,点击你的 Windows 11 虚拟机(假设 VM ID 为 101),严格按照以下标准调整硬件配置:
- 机器 (Machine):选择
q35(建议使用较高版本如pc-q35-8.1,不要用默认的 i440fx)。 - BIOS:必须选择
OVMF (UEFI)。 - 添加 EFI 磁盘,并确保勾选了
Pre-enrolled keys。 - 处理器 (CPU):类别(Type)选择
host。 - 添加 PCI 设备:
- 选择你的 AMD 显卡(只选
.0的那个,会自动带上.1的音频)。 - 勾选 All Functions(全部功能)。
- 勾选 ROM-Bar。
- 勾选 PCI-Express。
- 不要在主卡直通前盲目勾选
Primary GPU(主 GPU 选项建议在配置好 Romfile 且安装好驱动后,再行决定是否勾选,防止调试期间黑屏无输出)。
- 选择你的 AMD 显卡(只选
第三步:伪装 KVM 虚拟化与加载干净的 Romfile
Windows 的 AMD 驱动对虚拟化极为敏感,我们需要对虚拟机配置文件进行“修剪”。
1. 隐藏 KVM 特征
在 PVE 命令行下,编辑虚拟机的配置文件:
nano /etc/pve/qemu-server/101.conf
在第一行或合适位置添加或修改:
cpu: host,hidden=1,flags=+amd-no-ssb
并在文件底部,加入以下几行,欺骗 Hyper-V 厂商 ID:
args: -cpu 'host,hv_vendor_id=authenticamd,kvm=off'
2. 配置专属 VBIOS(Romfile)
由于宿主机启动时可能已经对显卡的 VBIOS 进行了破坏,虚拟机拿到的 VBIOS 不完整就会引发 Code 43。
- 去 TechPowerUp VBIOS Database 找到你对应显卡型号的 ROM 文件下载。
- 将下载好的
.rom文件(例如rx6600.rom)上传到 PVE 的/usr/share/kvm/目录下。 - 修改虚拟机配置文件:
找到nano /etc/pve/qemu-server/101.confhostpci0那一行,在末尾加上,romfile=rx6600.rom。例如:hostpci0: 0000:01:00,pcie=1,romfile=rx6600.rom,x-vga=1
此时,启动 Windows 11 虚拟机,通常情况下,你的 AMD 显卡已经能够正常工作,Code 43 应该已经消失。
但是!如果你点击“重新启动”虚拟机,它极有可能在第二次启动时再次死锁并报 Code 43。这就是接下来要解决的终极难题。
三、 终极武器:编写 AMD 显卡重置脚本 (vendor-reset)
AMD 显卡的 Reset Bug 导致显卡在 VM 关机后无法回到低电平初始状态。由 GNIF 开发的开源项目 vendor-reset 通过内核模块介入,可以在 VM 启动和关闭时,强行对 AMD 显卡实施底层的硬件级重置。
1. 在 PVE 宿主机上安装 vendor-reset
登录 PVE 命令行,安装编译所需的依赖和内核头文件:
apt update
apt install -y git dkms pve-headers build-essential
克隆 vendor-reset 仓库并编译安装:
cd /usr/src
git clone https://github.com/gnif/vendor-reset.git
cd vendor-reset
dkms install .
检查模块是否编译并加载成功:
modinfo vendor-reset
如果能输出一堆关于 vendor-reset 的信息,说明安装成功。
2. 配置开机自启并应用重置
我们需要让 PVE 在开机时自动加载这个模块:
echo "vendor-reset" >> /etc/modules
为了确保安全,我们需要写一个重置策略配置脚本,并在 PVE 每次开机时,将你的 AMD 显卡绑定到 vendor-reset 的重置策略中。
编写 PVE 显卡重置服务脚本
在 PVE 宿主机上,创建脚本:
nano /usr/local/bin/pve-amd-reset.sh
写入以下脚本内容(请根据你的实际 PCI 槽位 ID 修改,这里以 0000:01:00.0 为例):
#!/bin/bash
# 你的 AMD 显卡 PCI 地址,用 lspci 确认
GPU_PCI_ADDR="0000:01:00.0"
echo "============================================="
echo "Initializing AMD GPU Reset Policy..."
echo "============================================="
# 检查 vendor-reset 模块是否加载
if ! lsmod | grep -q "vendor_reset"; then
echo "Error: vendor-reset module is not loaded! Attempting to load..."
modprobe vendor-reset
if [ $? -ne 0 ]; then
echo "Critial: Failed to load vendor-reset module!"
exit 1
fi
fi
# 检查设备是否存在
if [ ! -d "/sys/bus/pci/devices/${GPU_PCI_ADDR}" ]; then
echo "Error: Device ${GPU_PCI_ADDR} not found!"
exit 1
fi
# 解除当前的驱动绑定(防止意外占用)
if [ -e "/sys/bus/pci/devices/${GPU_PCI_ADDR}/driver/unbind" ]; then
echo "Unbinding device from current driver..."
echo "${GPU_PCI_ADDR}" > "/sys/bus/pci/devices/${GPU_PCI_ADDR}/driver/unbind"
fi
# 将显卡重新绑定到 vfio-pci
echo "Binding device to vfio-pci..."
echo "vfio-pci" > "/sys/bus/pci/devices/${GPU_PCI_ADDR}/driver_override"
# 配置 vendor-reset 的重置方式
# 针对 Polaris (RX580) 使用 'pm_method',针对 Navi (RX5000/6000) 通常使用 'device_specific'
RESET_METHOD_PATH="/sys/bus/pci/devices/${GPU_PCI_ADDR}/reset_method"
if [ -f "${RESET_METHOD_PATH}" ]; then
echo "Current reset method: $(cat ${RESET_METHOD_PATH})"
echo "device_specific" > "${RESET_METHOD_PATH}"
echo "New reset method applied: $(cat ${RESET_METHOD_PATH})"
else
echo "Warning: ${RESET_METHOD_PATH} not found!"
fi
echo "AMD GPU Reset Policy initialized successfully."
赋予可执行权限:
chmod +x /usr/local/bin/pve-amd-reset.sh
配置 Systemd 开机服务
为了让该脚本每次开机自动运行,我们为其编写一个 Systemd 服务:
nano /etc/systemd/system/pve-amd-reset.service
写入以下内容:
[Unit]
Description=Configure AMD GPU Reset Policy for PVE VM Passthrough
After=pve-cluster.service
Before=pve-ha-crm.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/pve-amd-reset.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
启用并运行该服务:
systemctl daemon-reload
systemctl enable --now pve-amd-reset.service
通过 systemctl status pve-amd-reset.service 观察服务运行状态,如果显示 active (exited) 且输出了 New reset method applied: device_specific,则大功告成!
四、 联动虚拟机:PVE Hook 脚本自动重置显卡(双保险)
为了确保万无一失,我们还可以利用 PVE 的 Hook Script 机制,在虚拟机开机前和关机后,自动执行一次硬件重置。
1. 编写 VM Hook 脚本
创建 /var/lib/vz/snippets/amd-reset-hook.sh:
mkdir -p /var/lib/vz/snippets
nano /var/lib/vz/snippets/amd-reset-hook.sh
写入以下代码(这是一个通用的 PVE 虚拟机 Hook 脚本模板):
#!/bin/bash
VMID=$1
PHASE=$2
# 配置你的 GPU PCI 地址
GPU_PCI_ADDR="0000:01:00.0"
if [ "$PHASE" = "pre-start" ]; then
echo "VM $VMID is starting. Forcing AMD GPU reset before VM boot..."
if [ -f "/sys/bus/pci/devices/${GPU_PCI_ADDR}/init_reset" ]; then
echo 1 > "/sys/bus/pci/devices/${GPU_PCI_ADDR}/init_reset"
echo "Reset signal sent to ${GPU_PCI_ADDR}"
else
# 备用方案:通过 pci-sysfs 触发重置
echo 1 > "/sys/bus/pci/devices/${GPU_PCI_ADDR}/reset"
echo "Standard PCI reset signal sent to ${GPU_PCI_ADDR}"
fi
fi
if [ "$PHASE" = "post-stop" ]; then
echo "VM $VMID has stopped. Cleaning up and resetting GPU..."
if [ -f "/sys/bus/pci/devices/${GPU_PCI_ADDR}/init_reset" ]; then
echo 1 > "/sys/bus/pci/devices/${GPU_PCI_ADDR}/init_reset"
fi
fi
exit 0
赋予权限:
chmod +x /var/lib/vz/snippets/amd-reset-hook.sh
2. 绑定 Hook 脚本到虚拟机
将该脚本绑定到你的 Win11 虚拟机(假设 VM ID 为 101):
qm set 101 --hookscript local:snippets/amd-reset-hook.sh
五、 总结与终极验证
通过以上一套组合拳,你已经完成了以下深度的调优工作:
- 屏蔽宿主机抢占:彻底杜绝了 PVE 宿主机初始化时锁死显卡的隐患。
- 底层隐藏:完美隐藏了 KVM 虚拟化标识,通过了 AMD 驱动的虚拟化检测。
- 干净 VBIOS:给虚拟机喂了未被污染的 Romfile。
- 底层硬重置:利用
vendor-reset内核级模块和 Hook 脚本,彻底干掉了由于 VM 重启引发的 AMD Reset Bug。
现在,你可以登录 Win11 虚拟机,无论是冷启动、热重启,还是高强度的 3D 跑分和游戏测试,那个恶心的 Code 43 错误都将不复存在。显卡直通,至此完美毕业!