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 挂起流程的工作方式。

../../_images/suspend.svg

此流程图解释了 amd s2idle 恢复流程的工作方式。

../../_images/resume.svg

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。未满足的约束将显示在内核日志中,并且可以通过处理内核环形缓冲区的日志记录工具(如 dmesgjournalctl)查看。”

如果在这些消息刷新之前系统在进入/退出时冻结,一个有用的调试策略是取消绑定 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 的预期进行编程。要实现此目的,应使用设置为 yCONFIG_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

内部

发生软件同步泛洪事件

此信息在启动时由内核读取并打印到系统日志中。当发生随机重启时,此消息有助于确定要调试的下一个组件。