18. 调试 AMD Zen 系统¶
18.1. 简介¶
本文档介绍了可用于调试 AMD Zen 系统问题的技术。它旨在供开发人员和技术用户使用,以帮助识别和解决问题。
18.2. S3 vs s2idle¶
在 AMD 系统上,无法同时支持挂起到 RAM (S3) 和挂起到空闲 (s2idle)。要确认您的系统支持哪种模式,您可以查看 cat /sys/power/mem_sleep
。如果显示 s2idle [deep]
,则支持 *S3*。如果显示 [s2idle]
,则支持 *s2idle*。
在支持 *S3* 的系统上,将利用固件将所有硬件置于适当的低功耗状态。
在支持 *s2idle* 的系统上,内核将负责将设备转换为适当的低功耗状态。当所有设备都处于适当的低功耗状态时,硬件将转换为硬件睡眠状态。
在挂起周期之后,您可以通过查看 cat /sys/power/suspend_stats/last_hw_sleep
来了解在硬件睡眠状态下花费了多少时间。
此流程图解释了 AMD s2idle 挂起流程的工作方式。
此流程图解释了 amd s2idle 恢复流程的工作方式。
18.3. s2idle 调试工具¶
由于可能发生问题的地点很多,因此在 amd-debug-tools 上创建了一个调试工具,可以帮助测试常见问题并提供建议。
如果您遇到 s2idle 问题,最好从此开始,并按照其发现结果中的说明进行操作。如果您继续遇到问题,请使用此脚本生成的报告向 drm/amd gitlab 提交错误报告。
18.4. 来自 IRQ 的虚假 s2idle 唤醒¶
虚假唤醒通常会将 IRQ 设置为 /sys/power/pm_wakeup_irq
。这可以与 /proc/interrupts
匹配,以确定哪个设备唤醒了系统。
如果这不足以调试问题,则可以设置以下 sysfs 文件,以增加唤醒过程的详细程度
# echo 1 | sudo tee /sys/power/pm_debug_messages
# echo 1 | sudo tee /sys/power/pm_print_times
进行这些更改后,内核将显示可以追溯到内核 s2idle 循环代码的消息,并显示唤醒时任何活动的 GPIO 源。
如果唤醒是由 ACPI SCI 引起的,则可能需要额外的 ACPI 调试。以下命令可以启用额外的跟踪数据
# echo enable | sudo tee /sys/module/acpi/parameters/trace_state
# echo 1 | sudo tee /sys/module/acpi/parameters/aml_debug_output
# echo 0x0800000f | sudo tee /sys/module/acpi/parameters/debug_level
# echo 0xffff0000 | sudo tee /sys/module/acpi/parameters/debug_layer
18.5. 来自 GPIO 的虚假 s2idle 唤醒¶
如果在唤醒系统时 GPIO 处于活动状态,理想情况下您应该查看原理图以确定它与哪个设备相关联。如果原理图不可用,另一种策略是查看 ACPI _EVT() 条目,以确定当该 GPIO 处于活动状态时通知哪个设备。
对于一个假设的例子,假设 GPIO 59 唤醒了系统。您可以查看 SSDT 以确定当 GPIO 59 处于活动状态时通知哪个设备。
首先将 GPIO 编号转换为十六进制。
$ python3 -c "print(hex(59))"
0x3b
接下来确定哪个 ACPI 表具有 _EVT
条目。例如
$ sudo grep EVT /sys/firmware/acpi/tables/SSDT*
grep: /sys/firmware/acpi/tables/SSDT27: binary file matches
解码此表
$ sudo cp /sys/firmware/acpi/tables/SSDT27 .
$ sudo iasl -d SSDT27
然后查看该表,并找到 GPIO 0x3b 的匹配条目。
Case (0x3B)
{
M000 (0x393B)
M460 (" Notify (\\_SB.PCI0.GP17.XHC1, 0x02)\n", Zero, Zero, Zero, Zero, Zero, Zero)
Notify (\_SB.PCI0.GP17.XHC1, 0x02) // Device Wake
}
您可以在这种情况下看到,当 GPIO 59 处于活动状态时,设备 \_SB.PCI0.GP17.XHC1
收到通知。很明显这是一个 XHCI 控制器,但要更进一步,您可以通过将其与 ACPI 匹配来确定它是哪个 XHCI 控制器。
$ grep "PCI0.GP17.XHC1" /sys/bus/acpi/devices/*/path
/sys/bus/acpi/devices/device:2d/path:\_SB_.PCI0.GP17.XHC1
/sys/bus/acpi/devices/device:2e/path:\_SB_.PCI0.GP17.XHC1.RHUB
/sys/bus/acpi/devices/device:2f/path:\_SB_.PCI0.GP17.XHC1.RHUB.PRT1
/sys/bus/acpi/devices/device:30/path:\_SB_.PCI0.GP17.XHC1.RHUB.PRT1.CAM0
/sys/bus/acpi/devices/device:31/path:\_SB_.PCI0.GP17.XHC1.RHUB.PRT1.CAM1
/sys/bus/acpi/devices/device:32/path:\_SB_.PCI0.GP17.XHC1.RHUB.PRT2
/sys/bus/acpi/devices/LNXPOWER:0d/path:\_SB_.PCI0.GP17.XHC1.PWRS
在这里您可以看到它与 device:2d
匹配。查看 physical_node
以确定实际的 PCI 设备是什么。
$ ls -l /sys/bus/acpi/devices/device:2d/physical_node
lrwxrwxrwx 1 root root 0 Feb 12 13:22 /sys/bus/acpi/devices/device:2d/physical_node -> ../../../../../pci0000:00/0000:00:08.1/0000:c2:00.4
所以这就是它:与此 GPIO 唤醒相关的 PCI 设备是 0000:c2:00.4
。
amd_s2idle.py
脚本将为您捕获大多数这些工件。
18.6. s2idle PM 调试消息¶
在 AMD 系统上的 s2idle 流程中,ACPI LPS0 驱动程序负责检查所有 uPEP 约束。未通过的 uPEP 约束不会阻止 s0i3 进入。这意味着如果某些约束未得到满足,即使存在一些已知问题,内核也可能会尝试进入 s2idle。
要激活 PM 调试,可以在启动时指定 pm_debug_messagess
内核命令行选项,或写入 /sys/power/pm_debug_messages
。未满足的约束将显示在内核日志中,并且可以通过处理内核环形缓冲区的日志记录工具(如 dmesg
或 journalctl
)查看。”
如果在这些消息刷新之前系统在进入/退出时冻结,一个有用的调试策略是取消绑定 amd_pmc
驱动程序,以防止通知平台启动 s0i3 进入。这将阻止系统在进入或退出时冻结,并让您查看所有失败的约束。
cd /sys/bus/platform/drivers/amd_pmc
ls | grep AMD | sudo tee unbind
执行此操作后,运行挂起周期并特别注意以下方面的错误
ACPI: LPI: Constraint not met; min power state:%s current power state:%s
18.7. s2idle 问题的历史示例¶
为了帮助了解可能发生的问题类型以及如何调试它们,以下是一些已解决的 s2idle 问题的历史示例。
18.7.1. 核心离线¶
一位最终用户报告说,使内核离线会阻止系统正确进入 s0i3。这是使用内部 AMD 工具进行调试的,以捕获和显示来自硬件的指标流,显示当内核离线时发生的变化。确定硬件没有收到离线内核处于最深状态的通知,因此它阻止了 CPU 进入最深状态。该问题被调试为将内核置于 C3 状态的缺失命令。
commit d6b88ce2eb9d2 (“ACPI: processor idle: Allow playing dead in C3 state”)
18.7.2. 恢复后损坏¶
Rembrandt 出现的一个大问题是恢复后出现图形损坏。发生这种情况的原因是 PSP 和驱动程序职责的错位。PSP 将保存和恢复 DMCUB,但驱动程序假定它需要在恢复时重置 DMCUB。实际上,这也是早期芯片组的错位,但未观察到。
commit 79d6b9351f086 (“drm/amd/display: Don’t reinitialize DMCUB on s0ix resume”)
18.7.3. 背靠背挂起失败¶
当使用触发 IRQ 以唤醒的唤醒源时,pinctrl-amd 驱动程序中的一个错误可能会捕获 IRQ 的错误状态,并阻止系统正确地返回睡眠状态。
commit b8c824a869f22 (“pinctrl: amd: Don’t save/restore interrupt status and wake status bits”)
18.7.4. 5 分钟后基于定时器的虚假唤醒¶
HPET 用于为系统编程唤醒源,但是这导致 5 分钟后出现虚假唤醒。要使用的正确警报是 ACPI 警报。
commit 3d762e21d5637 (“rtc: cmos: Use ACPI alarm for non-Intel x86 systems too”)
18.7.5. 恢复后磁盘消失¶
从 s2idle 恢复后,NVME 磁盘将消失。这是由于 BIOS 未指定 _DSD StorageD3Enable 属性。这导致 NVME 驱动程序无法在挂起时将磁盘置于预期状态,并且在恢复时失败。
commit e79a10652bbd3 (“ACPI: x86: Force StorageD3Enable on more products”)
18.7.6. 虚假 IRQ1¶
许多 Renoir、Lucienne、Cezanne 和 Barcelo 平台都存在平台固件错误,其中 IRQ1 在 s0i3 恢复期间被触发。
这已在平台固件中修复,但许多系统未收到任何更多平台固件更新。
commit 8e60615e89321 (“platform/x86/amd: pmc: Disable IRQ1 wakeup for RN/CZN”)
18.7.7. 硬件超时¶
除了接受来自 amd-pmc 驱动程序的值外,硬件还执行许多操作。由于与硬件的通信路径是一个邮箱,因此它可能无法足够快地响应。此问题表现为挂起失败
PM: dpm_run_callback(): acpi_subsys_suspend_noirq+0x0/0x50 returns -110
amd_pmc AMDI0005:00: PM: failed to suspend noirq: error -110
通过比较空闲掩码的值来识别计时问题。
commit 3c3c8e88c8712 (“platform/x86: amd-pmc: Increase the response register timeout”)
18.7.8. 无法在面板打开的情况下达到硬件睡眠状态¶
在某些 Strix 系统上,观察到某些面板会阻止系统进入硬件睡眠状态,如果在该序列期间内部面板已打开。
即使面板在挂起期间已关闭,它也暴露了一个计时问题,即中断导致显示硬件唤醒并阻止低功耗状态进入。
commit 40b8c14936bd2 (“drm/amd/display: Disable unneeded hpd interrupts during dm_init”)
18.8. 运行时功耗问题¶
运行时功耗受许多因素影响,包括但不限于 PCIe 主动状态电源管理 (ASPM) 的配置、显示亮度、CPU 的 EPP 策略以及设备的电源管理。
18.8.1. ASPM¶
为了获得最佳的运行时功耗,ASPM 应按照硬件供应商 BIOS 的预期进行编程。要实现此目的,应使用设置为 y
的 CONFIG_PCIEASPM_DEFAULT
编译 Linux 内核,并且不应修改 sysfs 文件 /sys/module/pcie_aspm/parameters/policy
。
最值得注意的是,如果任何设备的 L1.2 配置不正确,SoC 将无法进入最深度的空闲状态。
18.8.2. EPP 策略¶
energy_performance_preference
sysfs 文件可用于设置 CPU 的效率或性能偏差。当更偏向性能时,这与电池寿命有直接关系。
18.9. BIOS 调试消息¶
大多数 OEM 机器没有用于输出内核或 BIOS 调试消息的串行 UART。但是,BIOS 调试消息对于理解 BIOS 错误和调用 BIOS AML 的 Linux 内核驱动程序的错误都很有用。
由于大多数 OEM AMD 系统上的 BIOS 都基于 AMD 参考 BIOS,因此用于导出调试消息的基础架构通常与 AMD 参考 BIOS 相同。
18.9.1. 手动解析¶
通常有一个 ACPI 方法 \M460
,AML 的不同路径将调用该方法以将消息发送到 BIOS 串行日志。此方法采用 7 个参数,第一个参数是一个字符串,其余参数是可选整数
Method (M460, 7, Serialized)
这是一个 BIOS AML 可能使用 \M460
调用的字符串示例
M460 (" OEM-ASL-PCIe Address (0x%X)._REG (%d %d) PCSA = %d\n", DADR, Arg0, Arg1, PCSA, Zero, Zero)
通常在执行时,\M460
方法会将其他参数填充到字符串中。为了从 Linux 内核获取这些消息,ACPICA 中添加了一个钩子,可以捕获发送到 \M460
的*参数*,并将它们打印到内核环形缓冲区。例如,以下消息可以发送到内核环形缓冲区
extrace-0174 ex_trace_args : " OEM-ASL-PCIe Address (0x%X)._REG (%d %d) PCSA = %d\n", ec106000, 2, 1, 1, 0, 0
为了获取这些消息,您需要使用 CONFIG_ACPI_DEBUG
进行编译,然后打开以下 ACPICA 跟踪参数。这可以在内核命令行上或在运行时完成
acpi.trace_method_name=\M460
acpi.trace_state=method
注意:这些在启动时可能非常嘈杂。如果您在内核命令上打开这些参数,请同时考虑将 CONFIG_LOG_BUF_SHIFT
调大到更大的尺寸,例如 17,以避免丢失早期启动消息。
18.9.2. 工具辅助解析¶
如上所述,手动解析可能很乏味,尤其是在有很多消息的情况下。为了帮助解决这个问题,已经在 amd-debug-tools 上创建了一个工具来帮助解析消息。
18.10. 随机重启问题¶
当发生随机重启时,重启的高级原因存储在一个寄存器中,该寄存器将持续到下一次启动。
- 重启原因分为 6 类
软件引起的
电源状态转换
引脚引起的
硬件引起的
远程重置
内部 CPU 事件
位 |
类型 |
原因 |
---|---|---|
0 |
引脚 |
热敏引脚 BP_THERMTRIP_L 被触发 |
1 |
引脚 |
电源按钮被按下 4 秒钟 |
2 |
引脚 |
关机引脚被触发 |
4 |
远程 |
收到远程 ASF 关机命令 |
9 |
内部 |
内部 CPU 热限制被触发 |
16 |
引脚 |
系统重置引脚 BP_SYS_RST_L 被触发 |
17 |
软件 |
软件发出 PCI 重置 |
18 |
软件 |
软件将 0x4 写入重置控制寄存器 0xCF9 |
19 |
软件 |
软件将 0x6 写入重置控制寄存器 0xCF9 |
20 |
软件 |
软件将 0xE 写入重置控制寄存器 0xCF9 |
21 |
ACPI-状态 |
发生 ACPI 电源状态转换 |
22 |
引脚 |
键盘重置引脚 KB_RST_L 被触发 |
23 |
内部 |
发生内部 CPU 关机事件 |
24 |
硬件 |
系统在失败启动计时器到期之前未能启动 |
25 |
硬件 |
硬件看门狗计时器到期 |
26 |
远程 |
收到远程 ASF 重置命令 |
27 |
内部 |
未更正的错误导致数据结构同步泛洪事件 |
29 |
内部 |
FCH 和 MP1 未能进行温重置握手 |
30 |
内部 |
发生奇偶校验错误 |
31 |
内部 |
发生软件同步泛洪事件 |
此信息在启动时由内核读取并打印到系统日志中。当发生随机重启时,此消息有助于确定要调试的下一个组件。