Coresight CPU 调试模块

作者:

Leo Yan <leo.yan@linaro.org>

日期:

2017 年 4 月 5 日

简介

Coresight CPU 调试模块定义在 ARMv8-a 架构参考手册 (ARM DDI 0487A.k) 的“H 部分:外部调试”章节中,CPU 可以集成调试模块,主要用于两种模式:自托管调试 (self-hosted debug) 和外部调试 (external debug)。通常,外部调试模式为人们所熟知,即外部调试器通过 JTAG 端口连接到 SoC;另一方面,程序可以探索依赖自托管调试模式的调试方法,本文档将重点关注这部分内容。

该调试模块提供基于采样的性能分析扩展,可用于采样 CPU 程序计数器、安全状态和异常级别等;通常每个 CPU 都带有一个专用的调试模块用于连接。基于自托管调试机制,当内核发生 panic 时,Linux 内核可以从 mmio 区域访问这些相关寄存器。内核 panic 的回调通知器将为每个 CPU 转储相关寄存器;最终这有助于 panic 的辅助分析。

实现

  • 在驱动注册期间,它使用 EDDEVID 和 EDDEVID1 — 两个设备 ID 寄存器来判断是否实现了基于采样的性能分析功能。在某些平台上,此硬件功能是完全或部分实现的;如果不支持此功能,则注册将失败。

  • 在编写本文档时,调试驱动程序主要依赖于内核 panic 回调通知器从三个采样寄存器(EDPCSR、EDVIDSR 和 EDCIDSR)收集的信息:从 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 文件系统提供一个开关,以便动态启用或禁用调试功能

要启用它,请向 /sys/kernel/debug/coresight_cpu_debug/enable 写入 ‘1’

# echo 1 > /sys/kernel/debug/coresight_cpu_debug/enable

要禁用它,请向 /sys/kernel/debug/coresight_cpu_debug/enable 写入 ‘0’

# 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)