事情起因
事情的开始,是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
压缩包解压密码是:www.nekopara.uk
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.
The download link has been fixed; you can now download it using wget or curl, following the on-screen instructions. 😊
Thank you, They are on MEGA for now. I won't delete these files.
https://mega.nz/file/5RVmHLoC#YNrSF48aIoz6Fw3Bf-rxVtVWIMDfVkZrPJJaxDMIzVs
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
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!
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.
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.
你这个真的牛逼了,到后面逆向的路子太野了 (☆ω☆)
假期折腾来玩玩的罢了,白白浪费我假期一周多的时间  ̄﹃ ̄
当事人来了喵
确实是当事人了(),不愧是答辩硬件,炸我一脸屎。 (╯‵□′)╯︵┴─┴