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.c 和 amdgpu_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=8、128 仍重启。
    • 结论:不解决根本问题,只是提前失败。

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

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

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

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

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

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

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

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

    • 放慢写入速度,试图避免硬件过载。
    • 结果:仍复位,排除写入速率。
  7. 尝试不同的写入后端(memcpy_toio、writew、writeb、批量 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_CNTL、VM_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 表,并修正一个错误寄存器(0x260c0 → 0x260d)。
    • 结果:golden 寄存器可写,但 VMC 仍不可写,未解决复位。
  3. 修改 clear-state/CSB 中的 stencil 值

    • 根据 Windows 驱动,将 DB_STENCILREFMASK 和 DB_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
  • 0x0f 到 0x1ff 全部停在同一点: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_2、dce_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:

  • KABINI、MULLINS、KAVERI 最终都停在同一点: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_CNTL 和 VM_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_STENCILREFMASK、DB_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 内存写目标始终为 0xcafedead,RPTR/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
压缩包解压密码是:www.nekopara.uk

评论区 (11)
  • Micecheese 的头像
    Micecheese 2026-10-28 15:44

    I appreciate the attempt and wish the best in case you revisit this, For archival purposes can you email a new download link for the source code? It could help if someone ever offers to create a modified driver. Also to upload into archive.org to share a safe link.

  • crashniels 的头像
    crashniels 2026-10-17 22:40

    Hello. I see you are trying to get this APU working with amdgpu. We suspect this might be the same or similar APU as the PS4. You might want to check out the ps4linux kernel and see if you find anything similar. Maybe we can get this APU working and mainlined. (ノ°ο°)ノ
    Discord: @crashniels

    • GTX690战术核显卡导弹 的头像
      crashniels

      Hi! Thanks for the suggestion! (ノ°ο°)ノ

      Yes, there are some architectural similarities between this APU and the PS4's APU, but they are not identical. The GPU in the A9-9820 is codenamed Kryptos, and although both are based on AMD's GCN architecture, they have different hardware configurations and GPU implementations. So I don't think we can simply reuse the same driver initialization code without further investigation.

      I actually looked into the ps4linux kernel code when I was working on this. From what I could tell at the time, it seemed to use a different, PS4-specific approach to GPU initialization rather than simply relying on an existing initialization path in the standard kernel.

      I also reverse-engineered parts of the Windows driver and found that the Kryptos GPU reuses a significant amount of initialization logic and configuration from Mullins. Unfortunately, despite many attempts, I eventually hit a wall with GPU register access. Some critical VMC registers consistently read back as zero after being written, and I couldn't get past that issue.

      I'm really glad you're interested in working on this and would like to help get the GPU working properly in Linux, potentially even getting support upstream! However, I'm currently at university, while the hardware is back at my home, so I can't easily access it for debugging and testing. My academic workload is also quite heavy at the moment, so I'm afraid I won't be able to contribute very much in the near future.

      I've included the relevant files at the end of my article, but the download link appears to be broken now. If you'd like a copy, please leave another comment on my blog or send me an email, and I'll send you a working download link. The archive includes the modified Linux kernel source and the Windows graphics driver I used for my investigation.

      If you'd prefer not to discuss anything publicly in the comments, feel free to email me as well.

      Thanks again for your interest, and I hope we can eventually get this unusual APU working properly under Linux!

      • crashniels 的头像
        crashniels 2026-10-18 00:50
        GTX690战术核显卡导弹

        Hi. A new download link would be amazing. As for the GPU init: I tested the Hawaii registers and firmware and it did boot with them but the performance was not good and buggy. My guess is that it is a Bonaire / Hawaii Hybrid APU with bits taken from Kaveri / Mullins and maybe more. There is also the PS4 Pro APU which has some parts from Polaris10 as well... ╮(╯▽╰)╭
        I don't have the board yet but it would be interesting to see if the Windows driver is even setting the correct values. Mullins init code would only set the minimum for the APU to work.

        • GTX690战术核显卡导弹 的头像
          crashniels

          Sorry for the late reply. |´・ω・)ノ I've sent a temporary download link to your email address. Please check your inbox.
          If you haven't received the message, it may have been marked as spam. Please check your email. Thank you.

  • mxdyeah 的头像
    mxdyeah 2026-09-19 03:17

    你这个真的牛逼了,到后面逆向的路子太野了 (☆ω☆)

  • TualatinPentium 的头像
    TualatinPentium 2026-08-31 20:21

    当事人来了喵