事情起因
事情的开始,是TualatinPentium送了我一个奇葩的硬件:A9-9820板U套装。
本期嘉宾:
CPU特写:
众所周知,在Linux社区中,AMD显卡在Linux下是一等公民是普遍事实,但是,在AMD A9-9820上,则是彻头彻尾的例外:完全无法正常驱动GPU。
这个并不是常见的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
对比其他原版内核构建的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,后面会考
看了一眼作者的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 块 | DCE | GFX | 适用 |
|---|---|---|---|---|
| BONAIRE | pp_smu | dce_v8_2 | gfx_v7_2 | 独立卡 |
| KAVERI | kv_smu | dce_v8_1 | gfx_v7_1 | APU |
| KABINI/MULLINS | kv_smu | dce_v8_3 | gfx_v7_2 | APU |
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 bosw_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 7drm debug=0会关闭[drm:drm_atomic_*]等调试刷屏。dmesg -n 7允许 info 及更高优先级通过控制台/netconsole,但排除 debug。
然后我就按照这个办法,拉锯了三四天,搞得筋疲力尽,还是毫无进展。这个阶段非常地枯燥,基本就是三点一线:Codex提出假设和验证并落实代码——我进行内核编译和安装——在目标机器上面启动,测试,抓取日志。
因为整个解决问题的过程太过于枯燥,而且我本人没有学过C语言,只能是让Codex来解决,但是我并不太懂具体应该怎么修复,所以基本上GPT说是什么就是什么了。
整个尝试的过程非常漫长,我只把让Codex尝试过的方法和结果罗列出来:
一、早期尝试(解决 WC memtype 和 VRAM 映射错误)
修改 APU 的 WC memtype 预留条件
- 在
amdgpu_object.c和amdgpu_device.c的三处条件中增加!(adev->flags & AMD_IS_APU),跳过arch_io_reserve_memtype_wc()。 - 结果:仍触发复位,且后续发现映射仍是
ioremap_wc(),并非真正的 WB。
- 在
强制使用
memremap(..., MEMREMAP_WB)替代ioremap_wc()- 针对 Kryptos 的 VRAM aperture 改用 WB 映射。
- 结果:
memremap失败(地址不在 E820 中),导致-12。
保留真实 PCI BAR0(256 MiB)替代被覆盖的错误 aperture
- 禁止
gmc_v7_0_mc_init()用MC_VM_FB_OFFSET覆盖aper_base,保留 BAR0 的物理地址。 - 结果:消除了
memremap错误,但后续 GART 表写入仍复位。
- 禁止
尝试
vramlimit参数vramlimit=2不重启(因提前 ENOMEM),vramlimit=8、128仍重启。- 结论:不解决根本问题,只是提前失败。
二、GART 页表写入方式的穷举测试
拆分 64 位 PTE 为两个 32 位
writel()- 避免原生的
writeq()可能引发的 64 位写合并问题。 结果:复位点略变,但仍在约 80~90 个 PTE 后复位。
Godavari golden table**- 使用 Windows 中匹配的
godavari_golden_registers替换 Kabini 表,并修正一
- 使用 Windows 中匹配的
- 避免原生的
调整写入顺序(正序、倒序、偶数/奇数页优先)
- 通过模块参数改变 PTE 写入顺序。
- 结果:复位点随顺序改变,但未消除,排除固定地址故障。
插入不同内存屏障(
wmb()、mb()、sfence、mfence)- 尝试每批、每页、每半写后增加屏障。
- 结果:屏障类型和频率不影响复位位置,排除写合并未排空。
改变批量提交大小(每 1/8/32/64 页提交一次)
- 测试不同的 WC 批处理粒度。
- 结果:依然在相近累计页数复位,排除批次大小。
清除 PTE 的
VALID位(写入全零或仅 flags)- 测试是否因有效位导致 VMC 解析错误。
- 结果:全零 PTE 仍在相同累计数复位,排除 PTE 内容。
增加 PTE 写入间隔(10ms 延迟)
- 放慢写入速度,试图避免硬件过载。
- 结果:仍复位,排除写入速率。
尝试不同的写入后端(
memcpy_toio、writew、writeb、批量memcpy)- 替换
writel()为其他 MMIO 原语。 - 结果:
memcpy_toio能写到更多页(约 265 页),但最终仍复位,说明事务形态影响边界,但非根因。
- 替换
三、ASIC 类型和固件切换
强制
force_asic_type为 Kabini、Bonaire、Kaveri、Mullins- 尝试不同芯片枚举,期望走不同的初始化路径。
- 结果:Bonaire 早期失败,Kaveri/Mullins 与 Kabini 复位点几乎相同,排除简单换类型。
切换 RLC 固件(Kabini vs Mullins)
- 通过模块参数单独选择 RLC 固件。
- 结果:不影响复位,仍停在相同位置。
切换 CP 微码(PFP/ME/CE/MEC)固件族
- 尝试 Kaveri、Bonaire 等固件。
- 结果:忙状态改变,但复位/卡住位置不变。
四、IOMMU 和 BIOS 设置
关闭 BIOS 中的 GFX IOMMU
- 对比开启和关闭时的 DMA 地址和复位点。
- 结果:地址变化,但复位位置不变,排除 IOMMU 翻译。
内核参数
amd_iommu=off- 完全禁用 IOMMU。
- 结果:同 BIOS 关闭,复位依旧。
五、内存硬件排除
更换不同内存条、单通道测试
- 排除内存条兼容性或双通道问题。
- 结果:复位位置完全相同,排除普通 DIMM 故障。
六、VMC 寄存器访问和时钟门控
读取 VMC 寄存器状态
- 发现
VM_L2_CNTL、VM_CONTEXT0_CNTL等写入后读回始终为 0。 - 结论:VMC 单元未正确配置或未解锁。
- 发现
尝试 VMC 软复位(
SRBM_SOFT_RESET)- 先 assert 再 deassert VMC 复位位。
- 结果:复位位可操作,但 VMC 寄存器仍不可写。
尝试
MM_INDEX/MM_DATA间接访问 VMC 寄存器- 模拟可能的间接窗口。
- 结果:仍读回 0,排除访问方式错误。
解除 MC/VMC 时钟门控和 memory light sleep
- 操作与 Linux
mc_cg_registers[]相同的 9 个寄存器,关闭门控位。 - 结果:门控位可写,但 VMC 寄存器仍为零(此方法最终未被采用为最终修复)。
- 操作与 Linux
七、Windows 驱动逆向与差异移植
分析 Windows 驱动(atikmdag.sys)的设备记录
- 发现
0x154C的配置指针与 Mullins0x985x完全相同,而非 Kabini 或 Kaveri。 - 提取
external_rev=0xC3,修正 Linux 的 revision 映射。
- 发现
移植 Godavari golden table
- 使用 Windows 中匹配的
godavari_golden_registers替换 Kabini 表,并修正一个错误寄存器(0x260c0→0x260d)。 - 结果:golden 寄存器可写,但 VMC 仍不可写,未解决复位。
- 使用 Windows 中匹配的
修改 clear-state/CSB 中的 stencil 值
- 根据 Windows 驱动,将
DB_STENCILREFMASK和DB_STENCILREFMASK_BF设为0x01000000(Linux 为 0)。 - 结果:改变了 CP 忙状态,但 RPTR 仍停在 0x48,未解决。
- 根据 Windows 驱动,将
采用 Windows 的 NOP 填充方式
- 单 DWORD 用
PACKET2(0),多 DWORD 用一条长PACKET3_NOP,替代 Linux 的重复0xffff1000。 - 结果:仍无法执行第一条命令,但定位到 CP 前端问题。
- 单 DWORD 用
八、最终有效方案:系统内存 GART 页表
将 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 初始化并注册 amdgpu0x0f到0x1ff全部停在同一点:GFX 初始化分配 67,584 字节 CP table BO 时 pin 失败
但是后续的测试中发现,只要触碰到 BAR0 的 8 MiB 边界后就会发生平台级复位。为了解决这个问题,我让Codex逆向了Windows的驱动,发现了之前最开始就走错的地方:
Windows 安装包确实把设备挂在 Kaveri_Desktop 下,但 atikmdag.sys 内部的 0x154C 记录并没有复用标准 Kaveri 的两组配置指针,而是与 Mullins 0x9851 完全一致。
内核对比结果(By-Codex):
| 项目 | Kabini | Mullins | Kryptos 判断 |
|---|---|---|---|
| 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 registers | Kalindi | Godavari | Windows 明确选择 Godavari |
| PFP/ME/CE/MEC 固件 | Kabini | 实际软链接到 Kabini | 相同 |
| SDMA/UVD/VCE 固件 | 通用 Bonaire 固件 | 同一文件 | 相同 |
| RLC 固件 | kabini_rlc.bin | 独立 mullins_rlc.bin | 尚需验证 |
| DPM | KV 通用路径 | 多一个 NB DPM 开关 | 后期再验证 |
| external revision | 0x81-0x85 | 0xa1+ | 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,和 LinuxWREG32()的寻址方式一致。 - 参考Windows的完整初始化次序已经还原:它先写页表基址和 Context,再清理若干 VM 状态、写地址范围及 aperture,最后才启用 L2,并执行 invalidate。Linux 当前恰好先启用 L2,再写 Context。更重要的是,Windows 使用的 L2 常量也与主线 CIK 不同。并做出适配性修补。
- Kryptos
0x154c虽然同属FAMILY_KV,但配置指针是0x0191ba40 / 0x00622488,与0x985xMullins 组一致,而不是原生 Kabini 或 Kaveri。 - 分析早期寄存器序列:Kryptos 的早期寄存器序列与 Linux 的
godavari_golden_registers精确匹配。 - Windows KMD 中 Kryptos
154c的两个配置指针与大量985xMullins 设备完全相同,和原生 Kabini983x不同。其 golden table 又精确对应 Linuxgodavari_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