Coresight CPU 调试模块¶
- 作者:
Leo Yan <leo.yan@linaro.org>
- 日期:
2017 年 4 月 5 日
简介¶
Coresight CPU 调试模块在 ARMv8-a 架构参考手册(ARM DDI 0487A.k)“H 部分:外部调试”中定义,CPU 可以集成调试模块,主要用于两种模式:自托管调试和外部调试。通常,外部调试模式众所周知,即外部调试器通过 JTAG 端口与 SoC 连接;另一方面,程序可以探索依赖于自托管调试模式的调试方法,本文档重点介绍这部分。
调试模块提供基于采样的性能分析扩展,可用于采样 CPU 程序计数器、安全状态和异常级别等;通常,每个 CPU 都有一个专用的调试模块连接。基于自托管调试机制,当内核发生 panic 时,Linux 内核可以从 mmio 区域访问这些相关寄存器。内核 panic 的回调通知程序将转储每个 CPU 的相关寄存器;最后,这有助于对 panic 进行辅助分析。
实现¶
在驱动程序注册期间,它使用 EDDEVID 和 EDDEVID1 这两个设备 ID 寄存器来确定是否实现了基于采样的性能分析。在某些平台上,此硬件功能已完全或部分实现;如果不支持此功能,则注册将失败。
在编写本文档时,调试驱动程序主要依赖于从三个采样寄存器:EDPCSR、EDVIDSR 和 EDCIDSR 从内核 panic 回调通知程序收集的信息:从 EDPCSR,我们可以获取程序计数器;EDVIDSR 包含安全状态、异常级别、位宽等信息;EDCIDSR 是上下文 ID 值,其中包含 CONTEXTIDR_EL1 的采样值。
该驱动程序支持在 AArch64 或 AArch32 模式下运行的 CPU。它们之间的寄存器命名约定略有不同,AArch64 使用 ‘ED’ 作为寄存器前缀(ARM DDI 0487A.k,第 H9.1 章),AArch32 使用 ‘DBG’ 作为前缀(ARM DDI 0487A.k,第 G5.1 章)。驱动程序统一使用 AArch64 命名约定。
ARMv8-a(ARM DDI 0487A.k)和 ARMv7-a(ARM DDI 0406C.b)具有不同的寄存器位定义。因此,该驱动程序整合了两个差异
如果 PCSROffset=0b0000,则在 ARMv8-a 上未实现 EDPCSR 的功能;但是 ARMv7-a 定义“PCSR 采样由一个值偏移,该值取决于指令集状态”。对于 ARMv7-a,驱动程序会进一步检查 CPU 是否使用 ARM 或 thumb 指令集运行并校准 PCSR 值,偏移量的详细描述在 ARMv7-a ARM(ARM DDI 0406C.b)第 C11.11.34 章 “DBGPCSR,程序计数器采样寄存器”。
如果 PCSROffset=0b0010,则 ARMv8-a 定义“已实现 EDPCSR,并且采样没有应用偏移量,并且在 AArch32 状态下不采样指令集状态”。因此,在 ARMv8 上,如果 EDDEVID1.PCSROffset 为 0b0010 并且 CPU 在 AArch32 状态下运行,则不会采样 EDPCSR;当 CPU 在 AArch64 状态下运行时,将采样 EDPCSR 并且不应用任何偏移量。
时钟和电源域¶
在访问调试寄存器之前,我们应确保已正确启用时钟和电源域。在 ARMv8-a ARM(ARM DDI 0487A.k)“H9.1 调试寄存器”章节中,调试寄存器分布在两个域中:调试域和 CPU 域。
+---------------+
| |
| |
+----------+--+ |
dbg_clock -->| |**| |<-- cpu_clock
| Debug |**| CPU |
dbg_power_domain -->| |**| |<-- cpu_power_domain
+----------+--+ |
| |
| |
+---------------+
对于调试域,用户使用 DT 绑定 “clocks” 和 “power-domains” 来指定调试逻辑的相应时钟源和电源。驱动程序根据需要调用 pm_runtime_{put|get} 操作来处理调试电源域。
对于 CPU 域,不同的 SoC 设计具有不同的电源管理方案,最终会对外部调试模块产生重大影响。因此,我们可以分为以下几种情况
在具有健全的电源控制器的系统上,该控制器可以正确处理 CPU 电源域,CPU 电源域可以由驱动程序中的寄存器 EDPRCR 控制。驱动程序首先写入位 EDPRCR.COREPURQ 以启动 CPU,然后写入位 EDPRCR.CORENPDRQ 以模拟 CPU 电源关闭。因此,这可以确保在访问调试相关寄存器期间正确启动 CPU 电源域;
如果群集上的所有 CPU 都已关闭电源,则某些设计会关闭整个群集,包括调试寄存器中应保持在调试电源域中的部分。在这些情况下,EDPRCR 中的位不受尊重,因此这些设计不支持调试电源关闭,这与 CoreSight / 调试设计人员的预期方式不同。这意味着即使检查 EDPRSR 也有可能在目标寄存器未供电时导致总线挂起。
在这种情况下,在调试寄存器未供电时访问它们是灾难的根源;因此,我们需要在启动时或用户在运行时启用模块时防止 CPU 低功耗状态。有关详细用法信息,请参阅“如何使用模块”一章。
设备树绑定¶
有关详细信息,请参见 Documentation/devicetree/bindings/arm/arm,coresight-cpu-debug.yaml。
如何使用模块¶
如果要在启动时启用调试功能,可以将 “coresight_cpu_debug.enable=1” 添加到内核命令行参数中。
该驱动程序也可以作为模块工作,因此可以在 insmod 模块时启用调试
# insmod coresight_cpu_debug.ko debug=1
当启动时或 insmod 模块您尚未启用调试时,该驱动程序会使用 debugfs 文件系统提供一个旋钮来动态启用或禁用调试
要启用它,请将 “1” 写入 /sys/kernel/debug/coresight_cpu_debug/enable
# echo 1 > /sys/kernel/debug/coresight_cpu_debug/enable
要禁用它,请将 “0” 写入 /sys/kernel/debug/coresight_cpu_debug/enable
# echo 0 > /sys/kernel/debug/coresight_cpu_debug/enable
正如 “时钟和电源域” 一章中所述,如果您正在使用一个平台,该平台具有空闲状态来关闭调试逻辑的电源,并且电源控制器无法很好地处理 EDPRCR 的请求,那么您应首先在启用 CPU 调试功能之前约束 CPU 空闲状态;因此可以确保访问调试逻辑。
如果要在启动时限制空闲状态,可以在内核命令行中使用 “nohlt” 或 “cpuidle.off=1”。
在运行时,您可以使用以下方法禁用空闲状态
可以通过 PM QoS 子系统禁用 CPU 空闲状态,更具体地说,可以通过使用 “/dev/cpu_dma_latency” 接口(有关详细信息,请参见 PM 服务质量接口)。如 PM QoS 文档中所述,请求的参数将一直有效,直到文件描述符被释放。例如
# exec 3<> /dev/cpu_dma_latency; echo 0 >&3
...
Do some work...
...
# exec 3<>-
也可以从应用程序执行相同的操作。
从 cpuidle sysfs 禁用特定 CPU 的特定空闲状态(请参阅 CPU 空闲时间管理)
# echo 1 > /sys/devices/system/cpu/cpu$cpu/cpuidle/state$state/disable
输出格式¶
这是调试输出格式的示例
ARM external debug module:
coresight-cpu-debug 850000.debug: CPU[0]:
coresight-cpu-debug 850000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock)
coresight-cpu-debug 850000.debug: EDPCSR: handle_IPI+0x174/0x1d8
coresight-cpu-debug 850000.debug: EDCIDSR: 00000000
coresight-cpu-debug 850000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0)
coresight-cpu-debug 852000.debug: CPU[1]:
coresight-cpu-debug 852000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock)
coresight-cpu-debug 852000.debug: EDPCSR: debug_notifier_call+0x23c/0x358
coresight-cpu-debug 852000.debug: EDCIDSR: 00000000
coresight-cpu-debug 852000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0)