Linux下A9-9820 GPU驱动适配失败记录封面图

Linux下A9-9820 GPU驱动适配失败记录

事情起因

事情的开始,是TualatinPentium送了我一个奇葩的硬件:A9-9820板U套装。
本期嘉宾:
1.jpg
CPU特写:
2.jpg
众所周知,在Linux社区中,AMD显卡在Linux下是一等公民是普遍事实,但是,在AMD A9-9820上,则是彻头彻尾的例外:完全无法正常驱动GPU。
3.jpg
这个并不是常见的APU,而是Xbox One定制的APU,AMD大约在2018年前后为了清库存,改了名字,弄了A9-9820/RX-8120这两个型号,打包卖给OEM的CPU。当时好像说是有厂商想打造廉价游戏机,然后项目黄了,这些CPU连带板子就流出来了。Windows驱动是有的,但是停更很久了,最后的版本是2019年的驱动,已经算是挺老的了。正因如此,我便想试试能不能在Linux上让这个GPU可以正常工作,然后可以测试一下新的游戏。

资料获取

查阅资料得知,这个APU的GPU部分核心是GCN1.0的Sea Islands家族衍生物,代号是Kryptos。总体的资料非常少,能找到有图吧老哥尝试给这个玩意装Linux后GPU驱动失败的帖子,和我实际遇到的情况一模一样。另外能查到最有价值(其实后面看是大坑)的线索是这个仓库:linux-skorion-A9-9820
4.png
对比其他原版内核构建的PKGBUILD文件,主要的修改点是在prepare()函数插入了下面这段:

  # 强行注入 A9-9820 (Kryptos) 支持,识别为 Bonaire 架构
  find . -path "*/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c" -exec sed -i '/{0x1002, 0x6640/i \  {0x1002, 0x154C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE},' {} +
  # === 新增:暴力干掉所有 tools/ 目录下的 -Werror ===
  echo "正在移除 tools 目录下的 -Werror..."
  find tools/ -type f -name "Makefile*" -exec sed -i 's/-Werror//g' {} +
  find tools/ -type f -name "*.mk" -exec sed -i 's/-Werror//g' {} +
  # === 针对 libbpf 可能写死的情况再加一道保险 ===
  find tools/bpf/ -type f -exec sed -i 's/-Werror//g' {} +

如果真的是这么简单重新编译一次内核就可以解决问题,这篇文章的画风就完全不同了。事实证明比想象中要困难得多,而且这个仓库的修补内容存在严重误导。这也让我在后续的探索中走了不少弯路。
记住这里出现的CHIP_BONAIRE,后面会考
5.png
看了一眼作者的GitHub头像,现在写记录的时候就觉得合情合理了,果然是个⑨
此外,实际测试,Linux下lspci显示设备名称是RX 350,AMDGPUvBIOS显示是113-AmdCato-007

[root@Xbox-One ~]# lspci 
00:00.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Kryptos/Cato/Garfield/Garfield+/Arlene/Pooky Root Complex
00:01.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Kryptos [Radeon RX 350]
00:01.1 Audio device: Advanced Micro Devices, Inc. [AMD/ATI] Device 154d
00:02.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Kryptos/Cato/Garfield/Garfield+/Arlene/Pooky UMI PCIe Dummy Host Bridge
00:11.0 SATA controller: Advanced Micro Devices, Inc. [AMD] FCH SATA Controller [AHCI mode] (rev 40)
00:12.0 USB controller: Advanced Micro Devices, Inc. [AMD] FCH USB OHCI Controller (rev 11)
00:12.2 USB controller: Advanced Micro Devices, Inc. [AMD] FCH USB EHCI Controller (rev 11)
00:13.0 USB controller: Advanced Micro Devices, Inc. [AMD] FCH USB OHCI Controller (rev 11)
00:13.2 USB controller: Advanced Micro Devices, Inc. [AMD] FCH USB EHCI Controller (rev 11)
00:14.0 SMBus: Advanced Micro Devices, Inc. [AMD] FCH SMBus Controller (rev 13)
00:14.1 IDE interface: Advanced Micro Devices, Inc. [AMD] FCH IDE Controller
00:14.2 Audio device: Advanced Micro Devices, Inc. [AMD] FCH Azalia Controller (rev 01)
00:14.3 ISA bridge: Advanced Micro Devices, Inc. [AMD] FCH LPC Bridge (rev 11)
00:14.4 PCI bridge: Advanced Micro Devices, Inc. [AMD] FCH PCI Bridge (rev 40)
00:14.5 USB controller: Advanced Micro Devices, Inc. [AMD] FCH USB OHCI Controller (rev 11)
00:14.7 SD Host controller: Advanced Micro Devices, Inc. [AMD] FCH SD Flash Controller
00:15.0 PCI bridge: Advanced Micro Devices, Inc. [AMD] Hudson PCI to PCI bridge (PCIE port 0)
00:15.1 PCI bridge: Advanced Micro Devices, Inc. [AMD] Hudson PCI to PCI bridge (PCIE port 1)
00:16.0 USB controller: Advanced Micro Devices, Inc. [AMD] FCH USB OHCI Controller (rev 11)
00:16.2 USB controller: Advanced Micro Devices, Inc. [AMD] FCH USB EHCI Controller (rev 11)
00:18.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Kryptos/Cato/Garfield/Garfield+/Arlene/Pooky HT Configuration
00:18.1 Host bridge: Advanced Micro Devices, Inc. [AMD] Kryptos/Cato/Garfield/Garfield+/Arlene/Pooky Address Maps
00:18.2 Host bridge: Advanced Micro Devices, Inc. [AMD] Kryptos/Cato/Garfield/Garfield+/Arlene/Pooky DRAM Configuration
00:18.3 Host bridge: Advanced Micro Devices, Inc. [AMD] Kryptos/Cato/Garfield/Garfield+/Arlene/Pooky Miscellaneous Configuration
00:18.4 Host bridge: Advanced Micro Devices, Inc. [AMD] Kryptos/Cato/Garfield/Garfield+/Arlene/Pooky PM Configuration
00:18.5 Host bridge: Advanced Micro Devices, Inc. [AMD] Kryptos/Cato/Garfield/Garfield+/Arlene/Pooky NB Performance Monitor
03:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168/8211/8411 PCI Express Gigabit Ethernet Controller (rev 15)

原版内核启用amdgpu模块的结果就是:

[  331.964247] ACPI: video: Video Device [VGA] (multi-head: yes  rom: no  post: no)
[  331.964896] input: Video Bus as /devices/pci0000:00/acpi.video_bus.0/input/input15
[  357.203193] amdgpu: Virtual CRAT table created for CPU
[  357.203248] amdgpu: Topology: Add CPU node
[  357.203717] amdgpu 0000:00:01.0: initializing kernel modesetting (IP DISCOVERY 0x1002:0x154C 0x1002:0x154C 0x00).
[  357.203748] amdgpu 0000:00:01.0: register mmio base: 0xFEA00000
[  357.203754] amdgpu 0000:00:01.0: register mmio size: 524288
[  361.203794] amdgpu 0000:00:01.0: [drm] *ERROR* discovery failed: -2
[  361.203831] amdgpu 0000:00:01.0: Fatal error during GPU init
[  361.204296] amdgpu 0000:00:01.0: probe with driver amdgpu failed with error -2

考虑到这个玩意是GCN架构,Linux那边amdgpu对GCN1.0还算是在支持范围内,我就在想能不能针对性适配,让内核可以正确识别这个GPU,并正常工作。由于前面我说找到了一个仓库,看了一下核心部分就是注入{0x1002, 0x154C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}让内核把这个GPU当作CHIP_BONAIRE处理。似乎很简单?但是,试试就逝世,这就是我把我两周ChatGPT Plus Codex额度烧完,最后还竹篮打水一场空噩梦的开始。

初步尝试编译内核

当时我看到了这个仓库,感觉如获至宝,赶紧想办法在我用的发行版上面复现,看看能不能成功驱动起来这个APU。
我照着葫芦画瓢,给CachyOS的LTS内核包PKGBUILD增加了参考仓库得来的注入命令,开始编译内核,并安装,结果,开机就给我泼了一盆冷水——开机第一次就直接卡死了。
因为这个APU性能太差了,拿来编译内核简直是噩梦,我第一次尝试编译,跑了4个小时都没跑完,然后我就拿我的游戏本编译,CPU是R7 5800H,比这破玩意快多了,四十分钟编译完成,结果发现编译出来的内核,编译机器上可以启动,而到这个测试机器上就无法启动。我一开始以为是补丁的问题,后面才知道是掉坑里了——根本原因:CONFIG_X86_NATIVE_CPU 。配置内核编译的时候,只适配了编译内核的机器,编译内核的机器CPU更新,因此启用了很多新特性,老CPU微架构不兼容:这是initramfs阶段卡死的几乎确定的原因。
CachyOS PKGBUILD 的 prepare() 中,_processor_opt 默认为空,走入 else 分支(linux-cachyos-lts/PKGBUILD:286):
scripts/config -d GENERIC_CPU -d MZEN4 -e X86_NATIVE_CPU
这会启用CONFIG_X86_NATIVE_CPU=y,即用-march=native编译整个内核,生成的机器码只能在构建机器同款或更新 CPU 上执行。
所以,需要改成 CONFIG_GENERIC_CPU=y + CONFIG_X86_64_VERSION=1,才能让老CPU正常启动系统。

解决了问题,我一开始以为胜利就在眼前:因为已经可以过initramfs并开始启动系统了,但是第二次失败接踵而至:又卡死了。卡死的位置在运行这个任务的时候:
A stop job is running for Rule-based Manager for Device Events and File
对应原版,执行到这个部分是会直接报错:

[   31.830013] amdgpu 0000:00:01.0: [drm] *ERROR* discovery failed: -2
[   31.830027] amdgpu 0000:00:01.0: Fatal error during GPU init
[   31.830214] amdgpu 0000:00:01.0: probe with driver amdgpu failed with error -2

报错完后继续启动,所以我就开始怀疑应该是补丁的问题了。

尝试修复加入补丁系统卡死

借助 AI,我得到初步结论,设置把 A9-9820 的 GPU 的设备 ID 0x154C绑定到CHIP_BONAIRE,会导致内核使用独立显卡的方式去初始化这个核显,因为AMD核显和独显的初始化方式是不一样的,所以有可能这就是导致系统卡死的原因。
独立显卡 Bonaire 的初始化路径:

  • family = AMDGPU_FAMILY_CI(Sea Islands 独显)
  • SMU 用 pp_smu(独立卡电源管理)
  • DCE 用 dce_v8_2

而 A9-9820 是 APU 集显,应该走 APU 路径:

  • family = AMDGPU_FAMILY_KV(Kaveri APU)
  • SMU 用 kv_smu(APU 电源管理)
  • DCE 用 dce_v8_1

于是,我增加了AMD_IS_APU这个参数,和CHIP_BONAIRE进行组合,也就是:CHIP_BONAIRE | AMD_IS_APU
这次就不卡死了,算是当时我看来一个还可以的进展,但是依然没能让核显正常工作,日志报错:

[   12.234954] amdgpu: Virtual CRAT table created for CPU
[   12.234993] amdgpu: Topology: Add CPU node
[   12.235458] amdgpu 0000:00:01.0: amdgpu: initializing kernel modesetting (BONAIRE 0x1002:0x154C 0x1002:0x154C 0x00).
[   12.235485] amdgpu 0000:00:01.0: amdgpu: register mmio base: 0xFEA00000
[   12.235489] amdgpu 0000:00:01.0: amdgpu: register mmio size: 524288
[   12.235643] amdgpu 0000:00:01.0: amdgpu: detected ip block number 0 <common_v1_0_0> (cik_common)
[   12.235649] amdgpu 0000:00:01.0: amdgpu: detected ip block number 1 <gmc_v7_0_0> (gmc_v7_0)
[   12.235654] amdgpu 0000:00:01.0: amdgpu: detected ip block number 2 <ih_v2_0_0> (cik_ih)
[   12.235659] amdgpu 0000:00:01.0: amdgpu: detected ip block number 3 <gfx_v7_2_0> (gfx_v7_0)
[   12.235663] amdgpu 0000:00:01.0: amdgpu: detected ip block number 4 <sdma_v2_0_0> (cik_sdma)
[   12.235667] amdgpu 0000:00:01.0: amdgpu: detected ip block number 5 <smu_v1_0_0> (powerplay)
[   12.235672] amdgpu 0000:00:01.0: amdgpu: detected ip block number 6 <dce_v8_2_0> (dce_v8_0)
[   12.235676] amdgpu 0000:00:01.0: amdgpu: detected ip block number 7 <uvd_v4_2_0> (uvd_v4_2)
[   12.235680] amdgpu 0000:00:01.0: amdgpu: detected ip block number 8 <vce_v2_0_0> (vce_v2_0)
[   12.253007] amdgpu 0000:00:01.0: amdgpu: Fetched VBIOS from ROM BAR
[   12.253014] amdgpu: ATOM BIOS: 113-AmdCato-007
[   12.253479] amdgpu 0000:00:01.0: amdgpu: early_init of IP block <powerplay> failed -22
[   12.253487] amdgpu 0000:00:01.0: amdgpu: Fatal error during GPU init
[   12.253491] amdgpu 0000:00:01.0: amdgpu: amdgpu: finishing device.

尝试添加内核启动参数amdgpu.cik_support=1 radeon.cik_support=0 amdgpu.dpm=0也是一样的结果。
不过,这一次尝试也让我对这个芯片有了初步的认识:或许CHIP_BONAIRE并不合适,而是需要找更合适的初始化路径。而且参考日志,VBIOS 显示是"113-AmdCato-007","Cato" 是 Kabini/Mullins APU 的 VBIOS 代号。我顺藤摸瓜让AI帮我解析了Linux内核相关的代码,找到了原因:
CHIP_BONAIRE | AMD_IS_APU 虽然设了 AMD_IS_APU,但 cik_set_ip_blocks 中 Bonaire 分支用的是 pp_smu(独立卡电源管理),而 APU 应该用 kv_smu。pp_smu 的 early_init 在 APU 硬件上返回 -22 (-EINVAL) → 致命错误。
对比 cik_set_ip_blocks 中各芯片的 SMU 选择:

芯片SMU IP 块DCEGFX适用
BONAIREpp_smudce_v8_2gfx_v7_2独立卡
KAVERIkv_smudce_v8_1gfx_v7_1APU
KABINI/MULLINSkv_smudce_v8_3gfx_v7_2APU

VBIOS 是 "Cato"(Kabini/Mullins),GFX 应该是 gfx_v7_2(与 Bonaire 相同),但 SMU 必须是 kv_smu,DCE 应该是 dce_v8_3。
那下一步,就试试看Kabini吧。

尝试使用Kabini的逻辑初始化GPU

{0x1002, 0x154C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE | AMD_IS_APU}

改成

{0x1002, 0x154C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI | AMD_IS_APU}

后,进度又往前走了一些:

  • 好消息:可以认到显存了
  • 坏消息:屏幕会定格,不再刷新

但是实际上系统还活着,我之前为了方便调试故意开启了SSH服务,这下派上用场了,抓到的日志如下:

[   12.354963] amdgpu: Virtual CRAT table created for CPU
[   12.354997] amdgpu: Topology: Add CPU node
[   12.355423] amdgpu 0000:00:01.0: amdgpu: initializing kernel modesetting (KABINI 0x1002:0x154C 0x1002:0x154C 0x00).
[   12.355449] amdgpu 0000:00:01.0: amdgpu: register mmio base: 0xFEA00000
[   12.355453] amdgpu 0000:00:01.0: amdgpu: register mmio size: 524288
[   12.355670] amdgpu 0000:00:01.0: amdgpu: detected ip block number 0 <common_v1_0_0> (cik_common)
[   12.355676] amdgpu 0000:00:01.0: amdgpu: detected ip block number 1 <gmc_v7_0_0> (gmc_v7_0)
[   12.355681] amdgpu 0000:00:01.0: amdgpu: detected ip block number 2 <ih_v2_0_0> (cik_ih)
[   12.355685] amdgpu 0000:00:01.0: amdgpu: detected ip block number 3 <gfx_v7_2_0> (gfx_v7_0)
[   12.355689] amdgpu 0000:00:01.0: amdgpu: detected ip block number 4 <sdma_v2_0_0> (cik_sdma)
[   12.355694] amdgpu 0000:00:01.0: amdgpu: detected ip block number 5 <smu_v1_0_0> (kv_dpm)
[   12.355698] amdgpu 0000:00:01.0: amdgpu: detected ip block number 6 <dce_v8_3_0> (dce_v8_0)
[   12.355702] amdgpu 0000:00:01.0: amdgpu: detected ip block number 7 <uvd_v4_2_0> (uvd_v4_2)
[   12.355706] amdgpu 0000:00:01.0: amdgpu: detected ip block number 8 <vce_v2_0_0> (vce_v2_0)
[   12.372959] amdgpu 0000:00:01.0: amdgpu: Fetched VBIOS from ROM BAR
[   12.372966] amdgpu: ATOM BIOS: 113-AmdCato-007
[   12.373430] kfd kfd: amdgpu: KABINI  not supported in kfd
[   12.394741] amdgpu 0000:00:01.0: vgaarb: deactivate vga console
[   12.394749] amdgpu 0000:00:01.0: amdgpu: Trusted Memory Zone (TMZ) feature not supported
[   12.394755] amdgpu 0000:00:01.0: amdgpu: PCIE atomic ops is not supported
[   12.395173] amdgpu 0000:00:01.0: amdgpu: vm size is 64 GB, 2 levels, block size is 10-bit, fragment size is 9-bit
[   12.395184] amdgpu 0000:00:01.0: amdgpu: VRAM: 2048M 0x0000000503000000 - 0x0000000582FFFFFF (2048M used)
[   12.395190] amdgpu 0000:00:01.0: amdgpu: GART: 1024M 0x0000000000000000 - 0x000000003FFFFFFF
[   12.395224] Modules linked in: amdgpu(+) amdxcp i2c_algo_bit drm_ttm_helper ttm drm_exec drm_panel_backlight_quirks gpu_sched drm_suballoc_helper drm_buddy drm_display_helper cec video wmi
[   12.395316]  ? gmc_v7_0_get_vbios_fb_size+0x37/0x60 [amdgpu b4e2b151282b577116982d879ae211f075ca2010]
[   12.396717]  amdgpu_bo_init+0x3e/0x90 [amdgpu b4e2b151282b577116982d879ae211f075ca2010]
[   12.398029]  ? amdgpu_gmc_get_vbios_allocations+0xad/0x160 [amdgpu b4e2b151282b577116982d879ae211f075ca2010]
[   12.399301]  gmc_v7_0_sw_init+0x366/0x570 [amdgpu b4e2b151282b577116982d879ae211f075ca2010]
[   12.400573]  amdgpu_device_init+0x23fb/0x3290 [amdgpu b4e2b151282b577116982d879ae211f075ca2010]
[   12.401820]  amdgpu_driver_load_kms+0x18/0x70 [amdgpu b4e2b151282b577116982d879ae211f075ca2010]
[   12.403051]  amdgpu_pci_probe+0x1ed/0x4e0 [amdgpu b4e2b151282b577116982d879ae211f075ca2010]
[   12.404332]  ? __pfx_amdgpu_init+0x10/0x10 [amdgpu b4e2b151282b577116982d879ae211f075ca2010]
[   12.405564]  ? amdgpu_init+0x36/0xff0 [amdgpu b4e2b151282b577116982d879ae211f075ca2010]
[   12.406985] [drm:amdgpu_bo_init [amdgpu]] *ERROR* Unable to set WC memtype for the aperture base
[   12.408243] amdgpu 0000:00:01.0: amdgpu: sw_init of IP block <gmc_v7_0> failed -22
[   12.408252] amdgpu 0000:00:01.0: amdgpu: amdgpu_device_ip_init failed
[   12.408256] amdgpu 0000:00:01.0: amdgpu: Fatal error during GPU init
[   12.408261] amdgpu 0000:00:01.0: amdgpu: amdgpu: finishing device.
[   12.408540] amdgpu 0000:00:01.0: probe with driver amdgpu failed with error -22

我本来以为还有几步操作就可以成功驱动起这个GPU,没想到从这里开始,就陷入了战争泥潭。

噩梦的开始:加载即重启

为了修复*ERROR* Unable to set WC memtype for the aperture base,让这个GPU能正常工作,我开始尝试让Codearts往上面加补丁了(因为Codex烧额度,我感觉强度似乎还没上去就没用),噩梦开始。

Codearts尝试修复

最开始,是设置一个 is_app_apu 补丁:在 gmc_v7_0.c 的 visible_vram_size 赋值之前注入:

if (adev->flags & AMD_IS_APU)
    adev->gmc.is_app_apu = true;
  • 让 amdgpu_bo_init 跳过对系统内存地址设置 WC memtype 的调用
  • 与 gmc_v9_0.c 中 Raven+ APU 的处理方式一致

然后,接下来就是新错误登场:

[   12.256592] amdgpu: Virtual CRAT table created for CPU
[   12.256631] amdgpu: Topology: Add CPU node
[   12.257117] amdgpu 0000:00:01.0: amdgpu: initializing kernel modesetting (KABINI 0x1002:0x154C 0x1002:0x154C 0x00).
[   12.257145] amdgpu 0000:00:01.0: amdgpu: register mmio base: 0xFEA00000
[   12.257149] amdgpu 0000:00:01.0: amdgpu: register mmio size: 524288
[   12.257275] amdgpu 0000:00:01.0: amdgpu: detected ip block number 0 <common_v1_0_0> (cik_common)
[   12.257281] amdgpu 0000:00:01.0: amdgpu: detected ip block number 1 <gmc_v7_0_0> (gmc_v7_0)
[   12.257286] amdgpu 0000:00:01.0: amdgpu: detected ip block number 2 <ih_v2_0_0> (cik_ih)
[   12.257291] amdgpu 0000:00:01.0: amdgpu: detected ip block number 3 <gfx_v7_2_0> (gfx_v7_0)
[   12.257295] amdgpu 0000:00:01.0: amdgpu: detected ip block number 4 <sdma_v2_0_0> (cik_sdma)
[   12.257300] amdgpu 0000:00:01.0: amdgpu: detected ip block number 5 <smu_v1_0_0> (kv_dpm)
[   12.257304] amdgpu 0000:00:01.0: amdgpu: detected ip block number 6 <dce_v8_3_0> (dce_v8_0)
[   12.257308] amdgpu 0000:00:01.0: amdgpu: detected ip block number 7 <uvd_v4_2_0> (uvd_v4_2)
[   12.257312] amdgpu 0000:00:01.0: amdgpu: detected ip block number 8 <vce_v2_0_0> (vce_v2_0)
[   12.274560] amdgpu 0000:00:01.0: amdgpu: Fetched VBIOS from ROM BAR
[   12.274570] amdgpu: ATOM BIOS: 113-AmdCato-007
[   12.275074] kfd kfd: amdgpu: KABINI  not supported in kfd
[   12.296752] amdgpu 0000:00:01.0: vgaarb: deactivate vga console
[   12.296761] amdgpu 0000:00:01.0: amdgpu: Trusted Memory Zone (TMZ) feature not supported
[   12.296766] amdgpu 0000:00:01.0: amdgpu: PCIE atomic ops is not supported
[   12.297200] amdgpu 0000:00:01.0: amdgpu: vm size is 64 GB, 2 levels, block size is 10-bit, fragment size is 9-bit
[   12.297212] amdgpu 0000:00:01.0: amdgpu: VRAM: 2048M 0x0000000503000000 - 0x0000000582FFFFFF (2048M used)
[   12.297218] amdgpu 0000:00:01.0: amdgpu: GART: 1024M 0x0000000000000000 - 0x000000003FFFFFFF
[   12.297447] amdgpu 0000:00:01.0: amdgpu: amdgpu: 2048M of VRAM memory ready
[   12.297454] amdgpu 0000:00:01.0: amdgpu: amdgpu: 6955M of GTT memory ready.
[   12.297530] amdgpu 0000:00:01.0: amdgpu: (-12) failed to allocate kernel bo
[   12.297534] amdgpu 0000:00:01.0: amdgpu: sw_init of IP block <gmc_v7_0> failed -12
[   12.297538] amdgpu 0000:00:01.0: amdgpu: amdgpu_device_ip_init failed
[   12.297542] amdgpu 0000:00:01.0: amdgpu: Fatal error during GPU init
[   12.297547] amdgpu 0000:00:01.0: amdgpu: amdgpu: finishing device.
[   12.297832] amdgpu 0000:00:01.0: probe with driver amdgpu failed with error -12

(-12) failed to allocate kernel bo
sw_init of IP block <gmc_v7_0> failed -12
-12 = -ENOMEM(内存不足)。GART 表分配失败。
然后发现错误是前面设置了gmc.is_app_apu = true的副作用。。。
华为云!!!Codearts!!!

Codex登场

有一说一,Codex还是有点东西的,上来先用这个命令查看机器内存布局:

cat /proc/iomem
dmesg | grep -Ei 'BIOS-e820|e820|PAT|MTRR|ioremap|memtype'

结果如下:

[root@Xbox-One miku]# cat /proc/meminfo | grep -E 'MemTotal|Cma|MemAvailable'
MemTotal:       14244860 kB
MemAvailable:   13643096 kB
CmaTotal:              0 kB
CmaFree:               0 kB
[root@Xbox-One miku]# dmesg | grep -E 'VRAM|GART|aper|visible|bo_init|kernel bo' 
[   12.297212] amdgpu 0000:00:01.0: amdgpu: VRAM: 2048M 0x0000000503000000 - 0x0000000582FFFFFF (2048M used)
[   12.297218] amdgpu 0000:00:01.0: amdgpu: GART: 1024M 0x0000000000000000 - 0x000000003FFFFFFF
[   12.297224] [drm] Detected VRAM RAM=2048M, BAR=2048M
[   12.297447] amdgpu 0000:00:01.0: amdgpu: amdgpu: 2048M of VRAM memory ready
[   12.297525] [drm] GART: num cpu pages 262144, num gpu pages 262144
[   12.297530] amdgpu 0000:00:01.0: amdgpu: (-12) failed to allocate kernel bo

结果很明显,BIOS是给核显划分了固定的显存。然后,Codex开始了修理工作:

  • 清理旧的 is_app_apu=true 补丁,保留 VRAM manager。
  • 仅对 1002:154c 跳过三处 WC reserve/free,其他 AMD APU 不受影响。
  • 对 Kryptos aperture 使用 memremap(..., MEMREMAP_WB),兼容 System RAM direct map 和 reserved RAM。
  • 使用 memunmap() 对称释放,并在 WB 映射失败时输出明确错误。

然后还是继续失败报错:

[root@Xbox-One miku]# dmesg | grep -i amdgpu
[   12.212067] amdgpu: Virtual CRAT table created for CPU
[   12.212105] amdgpu: Topology: Add CPU node
[   12.212535] amdgpu 0000:00:01.0: amdgpu: initializing kernel modesetting (KABINI 0x1002:0x154C 0x1002:0x154C 0x00).
[   12.212562] amdgpu 0000:00:01.0: amdgpu: register mmio base: 0xFEA00000
[   12.212566] amdgpu 0000:00:01.0: amdgpu: register mmio size: 524288
[   12.212794] amdgpu 0000:00:01.0: amdgpu: detected ip block number 0 <common_v1_0_0> (cik_common)
[   12.212800] amdgpu 0000:00:01.0: amdgpu: detected ip block number 1 <gmc_v7_0_0> (gmc_v7_0)
[   12.212805] amdgpu 0000:00:01.0: amdgpu: detected ip block number 2 <ih_v2_0_0> (cik_ih)
[   12.212836] amdgpu 0000:00:01.0: amdgpu: detected ip block number 3 <gfx_v7_2_0> (gfx_v7_0)
[   12.212841] amdgpu 0000:00:01.0: amdgpu: detected ip block number 4 <sdma_v2_0_0> (cik_sdma)
[   12.212846] amdgpu 0000:00:01.0: amdgpu: detected ip block number 5 <smu_v1_0_0> (kv_dpm)
[   12.212851] amdgpu 0000:00:01.0: amdgpu: detected ip block number 6 <dce_v8_3_0> (dce_v8_0)
[   12.212855] amdgpu 0000:00:01.0: amdgpu: detected ip block number 7 <uvd_v4_2_0> (uvd_v4_2)
[   12.212860] amdgpu 0000:00:01.0: amdgpu: detected ip block number 8 <vce_v2_0_0> (vce_v2_0)
[   12.230204] amdgpu 0000:00:01.0: amdgpu: Fetched VBIOS from ROM BAR
[   12.230211] amdgpu: ATOM BIOS: 113-AmdCato-007
[   12.230681] kfd kfd: amdgpu: KABINI  not supported in kfd
[   12.251933] amdgpu 0000:00:01.0: vgaarb: deactivate vga console
[   12.251949] amdgpu 0000:00:01.0: amdgpu: Trusted Memory Zone (TMZ) feature not supported
[   12.251954] amdgpu 0000:00:01.0: amdgpu: PCIE atomic ops is not supported
[   12.252335] amdgpu 0000:00:01.0: amdgpu: vm size is 64 GB, 2 levels, block size is 10-bit, fragment size is 9-bit
[   12.252345] amdgpu 0000:00:01.0: amdgpu: VRAM: 2048M 0x0000000503000000 - 0x0000000582FFFFFF (2048M used)
[   12.252351] amdgpu 0000:00:01.0: amdgpu: GART: 1024M 0x0000000000000000 - 0x000000003FFFFFFF
[   12.252658] Modules linked in: amdgpu(+) amdxcp i2c_algo_bit drm_ttm_helper ttm drm_exec drm_panel_backlight_quirks gpu_sched drm_suballoc_helper drm_buddy drm_display_helper cec video wmi
[   12.252750]  amdgpu_ttm_init+0x43c/0x5f0 [amdgpu c7aa2d667817fe2b1ae23004c569e2c60385e309]
[   12.254102]  ? amdgpu_gmc_get_vbios_allocations+0xad/0x160 [amdgpu c7aa2d667817fe2b1ae23004c569e2c60385e309]
[   12.255400]  gmc_v7_0_sw_init+0x366/0x570 [amdgpu c7aa2d667817fe2b1ae23004c569e2c60385e309]
[   12.256712]  amdgpu_device_init+0x23fb/0x3290 [amdgpu c7aa2d667817fe2b1ae23004c569e2c60385e309]
[   12.257932]  amdgpu_driver_load_kms+0x18/0x70 [amdgpu c7aa2d667817fe2b1ae23004c569e2c60385e309]
[   12.259142]  amdgpu_pci_probe+0x1ed/0x4e0 [amdgpu c7aa2d667817fe2b1ae23004c569e2c60385e309]
[   12.260402]  ? __pfx_amdgpu_init+0x10/0x10 [amdgpu c7aa2d667817fe2b1ae23004c569e2c60385e309]
[   12.261588]  ? amdgpu_init+0x36/0xff0 [amdgpu c7aa2d667817fe2b1ae23004c569e2c60385e309]
[   12.262928] amdgpu 0000:00:01.0: amdgpu: failed to map Kryptos VRAM aperture as WB
[   12.262933] amdgpu 0000:00:01.0: amdgpu: sw_init of IP block <gmc_v7_0> failed -12
[   12.262938] amdgpu 0000:00:01.0: amdgpu: amdgpu_device_ip_init failed
[   12.262943] amdgpu 0000:00:01.0: amdgpu: Fatal error during GPU init
[   12.262949] amdgpu 0000:00:01.0: amdgpu: amdgpu: finishing device.
[   12.263230] amdgpu 0000:00:01.0: probe with driver amdgpu failed with error -12
[root@Xbox-One miku]# dmesg | grep -E 'VRAM|GART|aper|visible|bo_init|kernel bo'
[   12.252345] amdgpu 0000:00:01.0: amdgpu: VRAM: 2048M 0x0000000503000000 - 0x0000000582FFFFFF (2048M used)
[   12.252351] amdgpu 0000:00:01.0: amdgpu: GART: 1024M 0x0000000000000000 - 0x000000003FFFFFFF
[   12.252357] [drm] Detected VRAM RAM=2048M, BAR=2048M
[   12.262928] amdgpu 0000:00:01.0: amdgpu: failed to map Kryptos VRAM aperture as WB
[root@Xbox-One miku]# cat /proc/iomem
00000000-00000fff : Reserved
00001000-0009ffff : System RAM
000a0000-000dffff : PCI Bus 0000:00
  000c0000-000cd3ff : Video ROM
000f0000-000fffff : System ROM
00100000-3ac02fff : System RAM
3ac03000-3b1f1fff : Reserved
3b1f2000-3d8bffff : System RAM
3d8c0000-3d926fff : Reserved
3d927000-3d936fff : ACPI Tables
3d937000-3ddf1fff : ACPI Non-volatile Storage
3ddf2000-3df7efff : Reserved
3df7f000-3e106fff : System RAM
3e107000-3e11efff : Reserved
3e11f000-3e148fff : System RAM
3e149000-3e4b3fff : Reserved
3e4b4000-3e886fff : System RAM
3e887000-3eff7fff : Reserved
3eff8000-3effffff : System RAM
3f000000-3fffffff : RAM buffer
40000000-bfffffff : pnp 00:01
c0000000-ffffffff : PCI Bus 0000:00
  c0000000-cfffffff : 0000:00:01.0
  d0000000-d07fffff : 0000:00:01.0
  e0000000-efffffff : PCI ECAM 0000 [bus 00-ff]
    e0000000-efffffff : pnp 00:00
  fe900000-fe9fffff : PCI Bus 0000:03
    fe900000-fe903fff : 0000:03:00.0
    fe904000-fe904fff : 0000:03:00.0
      fe904000-fe904fff : r8169
  fea00000-fea7ffff : 0000:00:01.0
  feaa0000-feaa3fff : 0000:00:14.2
    feaa0000-feaa3fff : ICH HD audio
  feaa4000-feaa7fff : 0000:00:01.1
    feaa4000-feaa7fff : ICH HD audio
  feaa8000-feaa80ff : 0000:00:16.2
    feaa8000-feaa80ff : ehci_hcd
  feaa9000-feaa9fff : 0000:00:16.0
    feaa9000-feaa9fff : ohci_hcd
  feaaa000-feaaa0ff : 0000:00:14.7
    feaaa000-feaaa0ff : mmc0
  feaab000-feaabfff : 0000:00:14.5
    feaab000-feaabfff : ohci_hcd
  feaac000-feaac0ff : 0000:00:13.2
    feaac000-feaac0ff : ehci_hcd
  feaad000-feaadfff : 0000:00:13.0
    feaad000-feaadfff : ohci_hcd
  feaae000-feaae0ff : 0000:00:12.2
    feaae000-feaae0ff : ehci_hcd
  feaaf000-feaaffff : 0000:00:12.0
    feaaf000-feaaffff : ohci_hcd
  feab0000-feab07ff : 0000:00:11.0
    feab0000-feab07ff : ahci
  feb80000-febfffff : amd_iommu
  fec00000-fec003ff : IOAPIC 0
  fec01000-fec013ff : IOAPIC 1
  fec10000-fec10fff : Reserved
    fec10000-fec10fff : pnp 00:04
  fed00000-fed003ff : HPET 0
    fed00000-fed003ff : PNP0103:00
  fed61000-fed70fff : pnp 00:04
  fed80000-fed8ffff : Reserved
    fed80000-fed8ffff : pnp 00:04
  fee00000-fee00fff : pnp 00:04
  ff000000-ffffffff : pnp 00:04
100000000-43effffff : System RAM
  233200000-2349f00af : Kernel code
  234a00000-2359cafff : Kernel rodata
  235a00000-235ce3b3f : Kernel data
  2366db000-236bfffff : Kernel bss
43f000000-43fffffff : RAM buffer
[root@Xbox-One miku]# dmesg | grep -Ei 'BIOS-e820|e820|PAT|MTRR|ioremap|memtype'
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009ffff] usable
[    0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000003d8bffff] usable
[    0.000000] BIOS-e820: [mem 0x000000003d8c0000-0x000000003d926fff] reserved
[    0.000000] BIOS-e820: [mem 0x000000003d927000-0x000000003d936fff] ACPI data
[    0.000000] BIOS-e820: [mem 0x000000003d937000-0x000000003ddf1fff] ACPI NVS
[    0.000000] BIOS-e820: [mem 0x000000003ddf2000-0x000000003df7efff] reserved
[    0.000000] BIOS-e820: [mem 0x000000003df7f000-0x000000003e106fff] usable
[    0.000000] BIOS-e820: [mem 0x000000003e107000-0x000000003e11efff] reserved
[    0.000000] BIOS-e820: [mem 0x000000003e11f000-0x000000003e148fff] usable
[    0.000000] BIOS-e820: [mem 0x000000003e149000-0x000000003e4b3fff] reserved
[    0.000000] BIOS-e820: [mem 0x000000003e4b4000-0x000000003e886fff] usable
[    0.000000] BIOS-e820: [mem 0x000000003e887000-0x000000003eff7fff] reserved
[    0.000000] BIOS-e820: [mem 0x000000003eff8000-0x000000003effffff] usable
[    0.000000] BIOS-e820: [mem 0x00000000e0000000-0x00000000efffffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000feb80000-0x00000000fec01fff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000fec10000-0x00000000fec10fff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000fed80000-0x00000000fed8ffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000ff000000-0x00000000ffffffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000100000000-0x000000043effffff] usable
[    0.000000] efi: Remove mem36: MMIO range=[0xe0000000-0xefffffff] (256MB) from e820 map
[    0.000000] e820: remove [mem 0xe0000000-0xefffffff] reserved
[    0.000000] efi: Remove mem37: MMIO range=[0xfeb80000-0xfec01fff] (0MB) from e820 map
[    0.000000] e820: remove [mem 0xfeb80000-0xfec01fff] reserved
[    0.000000] efi: Not removing mem38: MMIO range=[0xfec10000-0xfec10fff] (4KB) from e820 map
[    0.000000] efi: Not removing mem39: MMIO range=[0xfed80000-0xfed8ffff] (64KB) from e820 map
[    0.000000] efi: Remove mem40: MMIO range=[0xff000000-0xffffffff] (16MB) from e820 map
[    0.000000] e820: remove [mem 0xff000000-0xffffffff] reserved
[    0.001167] e820: update [mem 0x00000000-0x00000fff] usable ==> reserved
[    0.001172] e820: remove [mem 0x000a0000-0x000fffff] usable
[    0.001594] Found optimal setting for mtrr clean up
[    0.001603] MTRR map: 6 entries (3 fixed + 3 variable; max 20), built from 9 variable MTRRs
[    0.001607] x86/PAT: Configuration [0-7]: WB  WC  UC- UC  WB  WP  UC- WT
[    0.001912] e820: update [mem 0x3f000000-0xffffffff] usable ==> reserved
[    0.062495] e820: update [mem 0x3ac03000-0x3b1f1fff] usable ==> reserved
[    0.430327] le9 Unofficial (le9uo) working set protection 1.15a by Masahito Suzuki (forked from hakavlad's original le9 patch)
[    0.472315] PCI: Using E820 reservations for host bridge windows
[    0.509845] e820: reserve RAM buffer [mem 0x3ac03000-0x3bffffff]
[    0.509850] e820: reserve RAM buffer [mem 0x3d8c0000-0x3fffffff]
[    0.509853] e820: reserve RAM buffer [mem 0x3e107000-0x3fffffff]
[    0.509857] e820: reserve RAM buffer [mem 0x3e149000-0x3fffffff]
[    0.509860] e820: reserve RAM buffer [mem 0x3e887000-0x3fffffff]
[    0.509863] e820: reserve RAM buffer [mem 0x3f000000-0x3fffffff]
[    0.509865] e820: reserve RAM buffer [mem 0x43f000000-0x43fffffff]
[    2.130747] systemd[1]: Reached target Path Units.
[   15.949985] systemd[1]: Dispatch Password Requests to Console Directory Watch skipped, unmet condition check ConditionPathExists=!/run/plymouth/pid
[   16.007575] systemd[1]: Clear Stale Hibernate Storage Info skipped, unmet condition check ConditionPathExists=/sys/firmware/efi/efivars/HibernateLocation-8cf2644b-4b0b-428f-9387-6d876050dc67
[   16.032660] systemd[1]: TPM PCR NvPCR Initialization Separator skipped, unmet condition check ConditionPathExists=/etc/initrd-release
[   18.864609] scsi host4: pata_atiixp
[   18.864981] scsi host5: pata_atiixp
[   18.865079] ata5: PATA max UDMA/100 cmd 0x1f0 ctl 0x3f6 bmdma 0xf100 irq 14 lpm-pol 0
[   18.865084] ata6: PATA max UDMA/100 cmd 0x170 ctl 0x376 bmdma 0xf108 irq 15 lpm-pol 0

为了修复这个问题,Codex给我的修复方案是:

  • 不再尝试将 0x503000000 作为 CPU 物理地址进行 WB 映射。
  • Kryptos 改为保留真实 PCI BAR0 作为 CPU 可见显存窗口。
  • BIOS 设置的 2GB 仍由 CONFIG_MEMSIZE 识别为总 VRAM。预期日志应类似:

    Detected VRAM RAM=2048M, BAR=256M

    这是正常状态,TTM 会把需要 CPU 访问的 BO 限制在 256MB 可见窗口内。

  • 没有再修改 WC reserve/free、ioremap_wc()iounmap()

接下来就是噩梦的开始:加载驱动触发机器重置

Codex给出的Debug方案是:
先禁止 amdgpu 自动加载,让系统正常进入并建立 SSH,然后手动加载模块,同时把 dmesg -w 输出保存在另一台机器上。即使目标机随后硬锁或重启,已经通过 SSH 发出的最后几行仍保留在客户端。
在Linux启动参数追加:

modprobe.blacklist=amdgpu rd.driver.blacklist=amdgpu panic=0 nowatchdog nmi_watchdog=0 loglevel=7 ignore_loglevel drm.debug=0x1ff
  • blacklist 让系统先正常启动。
  • panic=0 阻止 panic 后自动重启。
  • nowatchdog nmi_watchdog=0 避免锁死检测器自动重启。
  • 后三个参数增加 DRM 日志。

目标机器启动后,在另一台机器运行:

ssh root@目标机器IP 'stdbuf -oL dmesg --follow-new --human' | tee amdgpu-crash.log

保持这个终端运行。
然后再开一个 SSH 终端,手动触发驱动加载:

modprobe amdgpu

即使目标机硬锁或重启,amdgpu-crash.log 中已经传出来的内容仍会保留。

不得不说,这确实是一个好办法。不过这样子会被simple-framebuffer的 DRM debug 信息刷屏,解决办法是测试前执行:

echo 0 > /sys/module/drm/parameters/debug
dmesg -n 7
  • drm debug=0 会关闭 [drm:drm_atomic_*] 等调试刷屏。
  • dmesg -n 7 允许 info 及更高优先级通过控制台/netconsole,但排除 debug。

然后我就按照这个办法,拉锯了三四天,搞得筋疲力尽,还是毫无进展。这个阶段非常地枯燥,基本就是三点一线:Codex提出假设和验证并落实代码——我进行内核编译和安装——在目标机器上面启动,测试,抓取日志。
因为整个解决问题的过程太过于枯燥,而且我本人没有学过C语言,只能是让Codex来解决,但是我并不太懂具体应该怎么修复,所以基本上GPT说是什么就是什么了。
整个尝试的过程非常漫长,我只把让Codex尝试过的方法和结果罗列出来:

一、早期尝试(解决 WC memtype 和 VRAM 映射错误)

  1. 修改 APU 的 WC memtype 预留条件

    • amdgpu_object.camdgpu_device.c 的三处条件中增加 !(adev->flags & AMD_IS_APU),跳过 arch_io_reserve_memtype_wc()
    • 结果:仍触发复位,且后续发现映射仍是 ioremap_wc(),并非真正的 WB。
  2. 强制使用 memremap(..., MEMREMAP_WB) 替代 ioremap_wc()

    • 针对 Kryptos 的 VRAM aperture 改用 WB 映射。
    • 结果memremap 失败(地址不在 E820 中),导致 -12
  3. 保留真实 PCI BAR0(256 MiB)替代被覆盖的错误 aperture

    • 禁止 gmc_v7_0_mc_init()MC_VM_FB_OFFSET 覆盖 aper_base,保留 BAR0 的物理地址。
    • 结果:消除了 memremap 错误,但后续 GART 表写入仍复位。
  4. 尝试 vramlimit 参数

    • vramlimit=2 不重启(因提前 ENOMEM),vramlimit=8128 仍重启。
    • 结论:不解决根本问题,只是提前失败。

二、GART 页表写入方式的穷举测试

  1. 拆分 64 位 PTE 为两个 32 位 writel()

    • 避免原生的 writeq() 可能引发的 64 位写合并问题。
    • 结果:复位点略变,但仍在约 80~90 个 PTE 后复位。
      Godavari golden table**

      • 使用 Windows 中匹配的 godavari_golden_registers 替换 Kabini 表,并修正一
  2. 调整写入顺序(正序、倒序、偶数/奇数页优先)

    • 通过模块参数改变 PTE 写入顺序。
    • 结果:复位点随顺序改变,但未消除,排除固定地址故障。
  3. 插入不同内存屏障(wmb()mb()sfencemfence

    • 尝试每批、每页、每半写后增加屏障。
    • 结果:屏障类型和频率不影响复位位置,排除写合并未排空。
  4. 改变批量提交大小(每 1/8/32/64 页提交一次)

    • 测试不同的 WC 批处理粒度。
    • 结果:依然在相近累计页数复位,排除批次大小。
  5. 清除 PTE 的 VALID 位(写入全零或仅 flags)

    • 测试是否因有效位导致 VMC 解析错误。
    • 结果:全零 PTE 仍在相同累计数复位,排除 PTE 内容。
  6. 增加 PTE 写入间隔(10ms 延迟)

    • 放慢写入速度,试图避免硬件过载。
    • 结果:仍复位,排除写入速率。
  7. 尝试不同的写入后端(memcpy_toiowritewwriteb、批量 memcpy

    • 替换 writel() 为其他 MMIO 原语。
    • 结果memcpy_toio 能写到更多页(约 265 页),但最终仍复位,说明事务形态影响边界,但非根因。

三、ASIC 类型和固件切换

  1. 强制 force_asic_type 为 Kabini、Bonaire、Kaveri、Mullins

    • 尝试不同芯片枚举,期望走不同的初始化路径。
    • 结果:Bonaire 早期失败,Kaveri/Mullins 与 Kabini 复位点几乎相同,排除简单换类型。
  2. 切换 RLC 固件(Kabini vs Mullins)

    • 通过模块参数单独选择 RLC 固件。
    • 结果:不影响复位,仍停在相同位置。
  3. 切换 CP 微码(PFP/ME/CE/MEC)固件族

    • 尝试 Kaveri、Bonaire 等固件。
    • 结果:忙状态改变,但复位/卡住位置不变。

四、IOMMU 和 BIOS 设置

  1. 关闭 BIOS 中的 GFX IOMMU

    • 对比开启和关闭时的 DMA 地址和复位点。
    • 结果:地址变化,但复位位置不变,排除 IOMMU 翻译。
  2. 内核参数 amd_iommu=off

    • 完全禁用 IOMMU。
    • 结果:同 BIOS 关闭,复位依旧。

五、内存硬件排除

  1. 更换不同内存条、单通道测试

    • 排除内存条兼容性或双通道问题。
    • 结果:复位位置完全相同,排除普通 DIMM 故障。

六、VMC 寄存器访问和时钟门控

  1. 读取 VMC 寄存器状态

    • 发现 VM_L2_CNTLVM_CONTEXT0_CNTL 等写入后读回始终为 0。
    • 结论:VMC 单元未正确配置或未解锁。
  2. 尝试 VMC 软复位(SRBM_SOFT_RESET

    • 先 assert 再 deassert VMC 复位位。
    • 结果:复位位可操作,但 VMC 寄存器仍不可写。
  3. 尝试 MM_INDEX/MM_DATA 间接访问 VMC 寄存器

    • 模拟可能的间接窗口。
    • 结果:仍读回 0,排除访问方式错误。
  4. 解除 MC/VMC 时钟门控和 memory light sleep

    • 操作与 Linux mc_cg_registers[] 相同的 9 个寄存器,关闭门控位。
    • 结果:门控位可写,但 VMC 寄存器仍为零(此方法最终未被采用为最终修复)。

七、Windows 驱动逆向与差异移植

  1. 分析 Windows 驱动(atikmdag.sys)的设备记录

    • 发现 0x154C 的配置指针与 Mullins 0x985x 完全相同,而非 Kabini 或 Kaveri。
    • 提取 external_rev=0xC3,修正 Linux 的 revision 映射。
  2. 移植 Godavari golden table

    • 使用 Windows 中匹配的 godavari_golden_registers 替换 Kabini 表,并修正一个错误寄存器(0x260c00x260d)。
    • 结果:golden 寄存器可写,但 VMC 仍不可写,未解决复位。
  3. 修改 clear-state/CSB 中的 stencil 值

    • 根据 Windows 驱动,将 DB_STENCILREFMASKDB_STENCILREFMASK_BF 设为 0x01000000(Linux 为 0)。
    • 结果:改变了 CP 忙状态,但 RPTR 仍停在 0x48,未解决。
  4. 采用 Windows 的 NOP 填充方式

    • 单 DWORD 用 PACKET2(0),多 DWORD 用一条长 PACKET3_NOP,替代 Linux 的重复 0xffff1000
    • 结果:仍无法执行第一条命令,但定位到 CP 前端问题。

八、最终有效方案:系统内存 GART 页表

  1. 将 GART 页表从 VRAM(BAR0 映射)改为系统内存(dma_alloc_coherent

    • 使用 dma_alloc_coherent() 分配 64 KiB 页表,将其 DMA 地址写入 VM 页表基址寄存器。
    • kryptos_gart_table_mode=1:用 dma_alloc_coherent() 在系统内存分配 GART 页表。
    • 把真实 DMA 地址写入 CIK 页表基址,避免依赖正在构建的 GART。
    • 系统页表使用普通内存写入和 dma_wmb(),不再经过 BAR0/WC。
    • init/fini 按分配类型对称处理。
    • 结果不再复位,GART 初始化成功,所有 GTT BO 映射正常,后续 IH、GFX 等 IP 可继续初始化(尽管 GFX ring 测试仍超时,但那是另一层问题,不再导致硬重启)。

到这里,我感觉看到了胜利的曙光,还以为再努力一把就能让这个问题解决,没想到却掉入了一个更加无底洞的深渊。

不过现在写这篇技术笔记的时候,思考了一下,感觉应该最开始的故障点并没有解决:之前导致硬件复位应该和显存寄存器不可写有很大关系,因为后续Codex把 GART 页表移动到了系统内存,不再经过 BAR0/WC,这次就工作正常了。而后续寄存器大战,失败的点正是寄存器不可写。说明早期导致复位的故障和 VRAM 不可写/读取为0有很大的关联:内存控制器挂死并整机复位。前面搞的东西,有可能都是错误的,只不过是临时解决问题罢了,没能挖出根源。

终极噩梦:寄存器大战

因为前面提到的解决了重置问题,GMC开始工作,CP也能开始工作,我还以为说准备就能成功了,结果烧光了Token也没能解决问题。
随后的测试,真的让人感觉希望就在眼前:

  • 系统 DMA GART 页表成功分配
  • 原先必定复位的 map 4 已完成 256/256
  • map 5、map 6 也完成,三个 IB pool 初始化成功。
  • 0x7 已完成 GMC/IH 初始化并注册 amdgpu
  • 0x0f0x1ff 全部停在同一点:GFX 初始化分配 67,584 字节 CP table BO 时 pin 失败

但是后续的测试中发现,只要触碰到 BAR0 的 8 MiB 边界后就会发生平台级复位。为了解决这个问题,我让Codex逆向了Windows的驱动,发现了之前最开始就走错的地方:
Windows 安装包确实把设备挂在 Kaveri_Desktop 下,但 atikmdag.sys 内部的 0x154C 记录并没有复用标准 Kaveri 的两组配置指针,而是与 Mullins 0x9851 完全一致。
内核对比结果(By-Codex):

项目KabiniMullinsKryptos 判断
IP 组合gfx_v7_2dce_v8_3完全相同两者都匹配
GFX 拓扑1 SE、1 SH、最多 2 CU、1 RB、1 MEC完全相同匹配
GMC/IH/SDMA同一套实现同一套实现可共同复用
显示2 CRTC、3 音频端点相同比 Kaveri 合理
Golden registersKalindiGodavariWindows 明确选择 Godavari
PFP/ME/CE/MEC 固件Kabini实际软链接到 Kabini相同
SDMA/UVD/VCE 固件通用 Bonaire 固件同一文件相同
RLC 固件kabini_rlc.bin独立 mullins_rlc.bin尚需验证
DPMKV 通用路径多一个 NB DPM 开关后期再验证
external revision0x81-0x850xa1+Windows 指定 0xc3

不过,现在我再看表格,对于GFX拓扑应该是存疑的,因为Kryptos核显规模要大得多

也就是意味着,Windows驱动初始化的逻辑,很大程度上是复用了Mullins微架构的,不是Kabini,不是Kaveri,更不是Bonaire!!!这说明,一开始那个仓库直接简单粗暴把芯片类型设置为Bonaire完全就是乱来,果然是个⑨!!!
所以到目前,基本上思路清晰了:以Mullins的初始化逻辑为基线,对Mullins和Kryptos存在差异的部分进行针对性的修补。随后,我让Codex继续逆向分析Windows的驱动,找到了一些信息:
设备表里标准 Kaveri 的公共 profile 被 19 条 0x13xx 记录使用;另一套 profile 被 41 条 0x985x Mullins 记录使用,而第 42 条就是 0x154C。其中 0x154C 的两个 profile 指针与 Mullins 0x9851 完全相同,只是它自己的 raw/external revision 是 0x25/0xc3。其中,0x154C就是Kryptos的ID。
得到以上的信息,我赶紧按照Codex的建议使用 Mullins 的初始化逻辑进行测试,并测试了Kabini和Kaveri,结果测出来了终极BOSS:

  • KABINIMULLINSKAVERI 最终都停在同一点:rlc_resume 和微码加载成功,但 gfx ring test 超时 -110
  • 使用 kryptos_gfx_bo_mode=2 后不再整机复位,说明此前的复位确实主要由危险的 VRAM CPU 写入触发。

更关键的是,日志中 gfx ring 已放在 GTT,但初始化后的 VM 状态显示:

VM_L1_TLB_CNTL = 0x25b
VM_L2_CNTL     = 0
VM_L2_CNTL2    = 0
VM_L2_CNTL3    = 0
VM_CONTEXT0    = 0
VM_CONTEXT1_15 = 0

这足以解释 gfx ring 超时:CPU 创建了 GTT PTE,但 GPU 的 L2/VM context 看起来没有真正启用,CP 无法从 0x470000 左右的 GTT 地址读取命令环。

咚咚咚,VMC都是0,而且确定CPU写入 VRAM 的 BAR0 会触发重置!
后续也确认: VM_L2_CNTLVM_L2_CNTL3 写入后立即读回 0;与此同时同一阶段的 MC L1 寄存器可以正常写入。整个 0x500–0x56x VMC 寄存器区像不存在一样,而 0x819 一类 MC 寄存器正常。非常的反常和奇怪!
后续,Codex尝试多个方向排查问题,均没能解决问题。我们尝试过不少分析推测和解决方案:

  • 分析Windows驱动KMD寄存器写入方式:对 0x500 这类低地址寄存器,Windows 最终仍是 BAR + reg*4 的普通 MMIO,和 Linux WREG32() 的寻址方式一致。
  • 参考Windows的完整初始化次序已经还原:它先写页表基址和 Context,再清理若干 VM 状态、写地址范围及 aperture,最后才启用 L2,并执行 invalidate。Linux 当前恰好先启用 L2,再写 Context。更重要的是,Windows 使用的 L2 常量也与主线 CIK 不同。并做出适配性修补。
  • Kryptos 0x154c 虽然同属 FAMILY_KV,但配置指针是 0x0191ba40 / 0x00622488,与 0x985x Mullins 组一致,而不是原生 Kabini 或 Kaveri。
  • 分析早期寄存器序列:Kryptos 的早期寄存器序列与 Linux 的 godavari_golden_registers 精确匹配。
  • Windows KMD 中 Kryptos 154c 的两个配置指针与大量 985x Mullins 设备完全相同,和原生 Kabini 983x 不同。其 golden table 又精确对应 Linux godavari_golden_registers。所以从硬件配置看,它确实更像 Mullins/Godavari。
  • 在已经恢复出的 Windows 设备选择层,Kryptos 与 Mullins 使用完全相同的 primary/secondary profile 指针,这一层可以确认是 100% 复用
  • Kryptos 专有部分主要是 PCI ID、revision、内存放置和少量寄存器值。
  • Windows CIK clear-state 表与 Linux 基本一致,但 DB_STENCILREFMASKDB_STENCILREFMASK_BF 使用 0x01000000,Linux 使用 0
  • CP/GART 已能正常取指,RPTR=0x48 证明不是整体 VM 地址错误。

但是结果是残酷的:

  • VMC reset 确实成功置位并解除:SRBM_SOFT_RESET 回读正常。
  • reset 后 VM_L2_CNTL、Context、aperture 寄存器仍拒绝写入并回读为零。
  • 两套 Windows VMC 写入顺序都无法让 VM_L2_CNTL、页表基址等寄存器保留写入值,说明问题不只是 VMC 常量或初始化顺序。
  • gart_enable 当场开始,VM_L2_CNTL(0x500)VM_CONTEXT0_CNTL(0x504)、页表基址和范围寄存器的写入都读回 0;只有 MC_VM_MX_L1_TLB_CNTL(0x819) 等 MC 寄存器能保持写入,也就是说当前真正可疑点已经前移到 VMC 寄存器访问/寄存器布局。
  • CP_RB0_RPTR 仍固定在 0x48,ring test 超时 -110
  • KRYPTOS + Mullins CP 固件运行,PFP 内存写目标始终为 0xcafedeadRPTR/WPTR=0x48/0x100,最终正常超时卸载。

总结起来,问题还是:

  • MC L1 配置成功;
  • CPU 侧 GART PTE 正常;
  • VM_L2_CNTL、VMID0 页表 base/end/cntl 等关键寄存器完全没有保留写入值;
  • 这个状态一直持续到 ring 超时。

没有任何进展。
看着日志中永恒不变的一系列:

amdgpu 0000:00:01.0: amdgpu: Kryptos VMC status: point=read-after stage=before-gtt-recover map=0 writes=0 name=VM_CONTEXT0_CNTL reg=0x504 value=0x00000000 direct=0x00000000 indexed=0x00000000 access_mode=1

说明VMC寄存器仍全部为零
再看看已经烧完的两周Codex额度,我,破防了。

尾声

烧了两周额度的Codex,浪费了宝贵的一周的暑假时间,到最后还是没能驱动起来A9-9820的GPU,虽然结果很失望,但是也并不是一无所获。我从最开始对Linux内核内部是怎么工作的一无所知,到现在大概了解了一些硬件的初始化流程,还学会了编译和安装内核,也算是折腾失败的结果里面,不幸中的万幸吧。
相关的代码和文件我已经整理到了附件,里面还有对应的Windows显卡驱动和Linux内核源码,修改后应该是可以用makepkg直接构建内核软件包进行安装测试的,感兴趣的朋友可以看一下,我也希望能有这方面的大佬可以指点迷津。
A9-9820-files.zip

评论区 (0)