8步轻松晋级AMD MPSoC Linux内核调试专家
介绍
AMD MPSoC Linux一般使用PetaLinux编译Linux系统,包括Linux内核、DTS、文件系统。
PetaLinux内部集成Yocto,自动下载、配置、编译各种软件包。 它简化了编译流程,也导致有些工程师找不到软件包的源代码,不知道如何调试软件。
在PetaLinux工程的目录下,根据下列步骤,既能找到Linux内核源代码,还能在Linux内核源代码中添加自己的调试信息。
步骤
步骤1:获取Linux内核源代码
请在PetaLinux工程的目录下执行下列命令。{plnx-tool-root} 表示PetaLinux工具的安装目录。
代码语言:javascript复制source {plnx-tool-root}/settings.sh
petalinux-devtool modify linux-xlnx
较旧的PetaLinux版本,不支持命令“petalinux-devtool”,需要使用Yocto的命令。请在PetaLinux工程的目录下执行下列命令。
代码语言:javascript复制source components/yocto/layers/poky/oe-init-build-env
devtool modify linux-xlnx
执行的记录如下:
代码语言:javascript复制hankf@XSZGS4:zcu106-v231-bsp-peta$ petalinux-devtool modify linux-xlnx
[INFO] Sourcing buildtools
[INFO] Silentconfig project
[INFO] Generating kconfig for rootfs
[INFO] Silentconfig rootfs
[INFO] Generating plnxtool conf file
[INFO] Generating workspace directory
[INFO] devtool modify linux-xlnx
NOTE: Starting bitbake server...
NOTE: Started PRServer with DBfile: /proj/hankf/zcu106/v231/zcu106-v231-bsp-peta/build/cache/prserv.sqlite3, Address: 127.0.0.1:33903, PID: 23603
NOTE: Started PRServer with DBfile: /proj/hankf/zcu106/v231/zcu106-v231-bsp-peta/build/cache/prserv.sqlite3, Address: 127.0.0.1:45135, PID: 23668
Loading cache: 100% | | ETA: --:--:--
Loaded 0 entries from dependency cache.
Parsing recipes: 100% |########################################################################################################################################################| Time: 0:00:25
Parsing of 4357 .bb files complete (0 cached, 4357 parsed). 6288 targets, 301 skipped, 1 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies
Initialising tasks: 100% |#####################################################################################################################################################| Time: 0:00:05
Sstate summary: Wanted 0 Local 0 Mirrors 0 Missed 0 Current 146 (0% match, 100% complete)
NOTE: Executing Tasks
NOTE: Tasks Summary: Attempted 656 tasks of which 656 didn't need to be rerun and all succeeded.
INFO: Copying kernel config to workspace
INFO: Recipe linux-xlnx now set up to build from /proj/hankf/zcu106/v231/zcu106-v231-bsp-peta/components/yocto/workspace/sources/linux-xlnx
执行成功后,根据提示信息,在目录“components/yocto/workspace/sources/linux-xlnx”,能找到Linux内核源代码。
代码语言:javascript复制hankf@XSZGS4:linux-xlnx$ tree -L 1 -d
.
arch
block
certs
crypto
Documentation
drivers
fs
include
init
io_uring
ipc
kernel
lib
LICENSES
mm
net
oe-local-files
rust
samples
scripts
security
sound
tools
usr
virt
25 directories
步骤2:对Linux内核源代码进行排版
Linux内核源代码有自己的排版风格,比如判断语句下如果只有一行语句,就没有大括号。在添加调试代码时,只复制打印语句到这种判断语句后面,就会出现逻辑错误。如果判断语句下的语句块都被大括号包含,那么就不会出现上述的错误。Linux下有排版工具“astyle”,可以自动把判断语句下的语句块都用大括号包含起来。因此,先对Linux内核源代码进行排版,能减少后续添加调试用的打印语句的难度,减少出错的可能性。
调试的代码,通常是Linux内核源代码的驱动程序,因此可以只对驱动程序进行排版。进入Linux内核的“driver”目录,执行下列命令,对Linux内核源代码进行排版。 astyle的“otbs”风格会为判断、循环语句添加大括号。之后添加打印语句时,只需要复制黏贴,更加简单方便。
代码语言:javascript复制find ./ -name "*.c" | xargs -P 8 -i -t astyle -s --style=otbs {}
find ./ -name "*.h" | xargs -P 8 -i -t astyle -s --style=otbs {}
也可以在Linux内核源代码的其它目录,执行同样命令排版。 注意,不要在Linux内核源代码的“include”目录,执行上述操作。
步骤3:查找Linux内核代码
建议参考AMD Xilinx Linux Drivers,找到Xilinx的各种设备的驱动程序的源代码文件。
如果有错误信息,可以搜索错误信息,查找Linux内核代码中的具体文件。
还可以根据DTS的“compatible”信息查找Linux内核代码中的具体文件。比如要调试以太网的驱动程序,根据"xlnx,zynqmp-gem"搜索代码,就能找到源代码的文件“net/ethernet/cadence/macb_main.c”。
代码语言:javascript复制hankf@XSZGS4:drivers$ grep -rn "xlnx,zynqmp-gem"
net/ethernet/cadence/macb_main.c:4930: { .compatible = "xlnx,zynqmp-gem", .data = &zynqmp_config},
步骤4:添加调试信息
可以根据需要添加调试信息。
如果不清楚添加怎么添加调试信息,可以在关键函数的入口、出口;以及关键的分支选择处添加下列语句中的一句。它们输出函数名、行号,能帮助我们理解代码的运行流程。四条语句的功能一样,区别是Linux的打印级别不一样。 建议使用“KERN_DEBUG”打印调试信息。
代码语言:javascript复制printk(KERN_DEBUG "Func: s line:m:", __func__, __LINE__);
printk(KERN_INFO "Func: s line:m:", __func__, __LINE__);
printk(KERN_WARNING "Func: s line:m:", __func__, __LINE__);
printk(KERN_ERR "Func: s line:m:", __func__, __LINE__);
修改Linux内核源代码后,再编译PetaLinux的工程,得到的Linux文件,就包含相关的打印信息。
步骤5:设置Linux内核调试信息的打印级别和缓冲区大小
在U-Boot的参数“bootargs”中,能设置Linux内核调试信息的打印级别和缓冲区大小。 下列命令中的“loglevel=5”修改打印级别为5,“log_buf_len=1M”修改打印缓冲区大小为1MB。 注意,其它参数可能与工程相关,不一定完全一样。
代码语言:javascript复制setenv bootargs "console=ttyPS0,115200 earlycon root=/dev/ram0 rw loglevel=5 log_buf_len=1M" && saveenv && boot
只有小于loglevel的信息才会被打印到串口(标准输出设备)。 如果设置“loglevel=5”, KERN_NOTICE(5)、KERN_INFO(6)、KERN_DEBUG(7)的信息不会被打印到串口, KERN_WARNING(4)、KERN_ERR(3)的信息会被打印到串口。
如果发现打印信息不完整,建议增大打印缓冲区。
步骤6:查看Linux内核调试信息
Linux启动过程,默认会打印信息。
如果后来还想查看启动信息,或者查看级别不够输出到串口的调试信息,可以使用命令"dmesg"查看内核信息。
步骤7:查看设备树(DTB/DTS)
Linux内核的工作,受设备树(DTB/DTS)影响。DTS相当于是Linux内核的配置信息。 编译DTS能生成DTB。DTS的编译过程,也有文件包含、条件选择等过程。要检查真正的DTS,最好把DTB反汇编成DTS。
使用下列命令,可以把DTB反汇编成DTS。
代码语言:javascript复制dtc -I dtb -O dts -o system.dtb.dts system.dtb
步骤8:修改DTS
如果发现DTS不符合要求,需要更改DTS。PetaLinux建议在文件“project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi”里更改DTS。按DTS语法,可以通过引用标签(Label)、节点信息(Node Name),来修改dts。
比如PetaLinux在文件“components/plnx_workspace/device-tree/device-tree/zynqmp.dtsi”里定义了如下以太网的节点:
代码语言:javascript复制 gem3: ethernet@ff0e0000 {
compatible = "xlnx,zynqmp-gem", "cdns,gem";
status = "disabled";
interrupt-parent = <&gic>;
interrupts = <0 63 4>, <0 63 4>;
reg = <0x0 0xff0e0000 0x0 0x1000>;
clock-names = "pclk", "hclk", "tx_clk", "rx_clk", "tsu_clk";
#address-cells = <1>;
#size-cells = <0>;
iommus = <&smmu 0x877>;
power-domains = <&zynqmp_firmware PD_ETH_3>;
resets = <&zynqmp_reset ZYNQMP_RESET_GEM3>;
reset-names = "gem3_rst";
};
以太网的节点信息(Node Name)是“ethernet@ff0e0000”,标签(Label)是“gem3”。
在文件“system-user.dtsi”里,引用标签“gem3”,增加以太网的MAC地址的示例:
代码语言:javascript复制&gem3 {
local-mac-address = [00 0a 35 00 00 00];
};
在文件“system-user.dtsi”里,引用节点信息(Node Name)“ethernet@ff0e0000”, 增加以太网的MAC地址的示例:
代码语言:javascript复制ethernet@ff0e0000 {
local-mac-address = [00 0a 35 00 00 00];
};
DTS的更多信息,请参考 devicetree specification 。
总结
在常见的嵌入式系统开发中,工程师主要完成集成工作,不会修改Linux驱动程序的代码。
借用上面的办法,可以快速的在Linux驱动程序添加打印,跟踪Linux驱动程序的执行流程,然后排除配置参数错误、硬件引起的错误。