Home 使用串口调试Linux内核
Post
Cancel

使用串口调试Linux内核

前置要求及环境配置

被调试的主机安装待调试内核,称为target,另一台装有稳定的Linux内核,称为host。

target主板上带有原生com口,不能使用USB转接;需要一台USB转串口母线,连接target串口和host

本文使用的target架构为aarch64, 系统为openeuler,内核为Linux-5.10, host架构为x86-64, 系统为ubuntu 20.04,内核为Linux-5.4。但理论上本文所述的方法在Linux 5.x的发行版上是通用的。

确定串口是否可用

在target上安装minicom:

1
sudo yum install minicom -y

在target上,通过sudo minicom -D /dev/ttyAMA0启动,在host上监听/dev/ttyUSB0,可以接收到来自target minicom的输入,同理,在target上也可以收到host的输入,到这一步可以确认串口和串口线是没问题的,通过ctrl+A X 退出minicom。

如何确定串口是/dev/ttyAMA0? 猜的,但是可以通过遍历/dev下的所有串口设备来验证。

在windows上,可以使用uartAssist来监听串口。

安装必备工具

1
apt-get install gcc make libncurses5-dev openssl libssl-dev build-essential pkg-config libc6-dev bison flex libelf-dev

调试内核

target的参数设置

  1. 首先检查/proc/sys/kernel/sysrq,如果为0,则sysrq没有开启,后续在使用echo g触发sysrq时传递的信号将被忽略,需要将其修改为1:echo 1 > /proc/sys/kernel/sysrq

  2. 在target上进入kgdb模式:
    1
    2
    
    sudo su
    echo ttyAMA0,115200 > /sys/module/kgdboc/parameters/kgdboc
    

    在dmesg中可以看到输出为KGDB: Registered I/O driver kgdboc ,说明target的串口没有问题。

  3. 在target上使系统hang住,以在host上能够通过gdb连接并调试:
    1
    
    echo g > /proc/sysrq-trigger
    

此时可以看到:

1
2
Entering kdb (current=0xffff00209622e040, pid 1716) on processor 5 due to Keyboard Entry
[5]kdb>

host的环境准备

  1. 在host上准备内核调试环境,包括gdb、target上使用的vmlinux以及相应的ko文件。由于我们的host与target架构不同,因此需要使用gdb-multiarch并显式设置architecture为aarch64,而非gdb。

  2. 在host上安装agent-proxy,作用是将串口分割为多个功能,这里使用host的5550和5551端口,其中5550用于连接target的console,5551用于连接target的kgdb监听端口
    1
    2
    3
    
    git clone http://git.kernel.org/pub/scm/utils/kernel/kgdb/agent-proxy.git
    cd agent-proxy;make
    sudo ./agent-proxy 5550^5551 0 /dev/ttyUSB0,115200
    
  3. 在host上连接target的console:
    1
    
    sudo telnet localhost 5550
    
  4. 在host上启动gdb-multiarch并连接5551端口:
    1
    2
    
    gdb-multiarch vmlinux
    target remote:5551
    

发现连接失败并报错:

1
2
3
4
gef> target remote:5551
Remote debugging using :5551
Ignoring packet error, continuing...
Remote replied unexpectedly to 'vMustReplyEmpty': vMustReplyEmpty

同时,target上报错:

1
[5]kdb> g+$qSupporteddiag: -22: Permission denied

原因是因为access to debugger functions is prohibited by default in you kernel,有两种解决方案:

  1. 将cmd_enable设置为1,当前可用,但系统重启后失效
    1
    
    echo 1 > /sys/module/kdb/parameters/cmd_enable
    
  2. 添加一个启动参数,系统重启后生效:
    1
    
    kdb.cmd_enable=1
    

在target的kdb console中输入go使系统退出此状态,进行上述修改后重新echo g > /proc/sysrq-trigger,再次使用gdb连接target。

在gdb上执行:

1
set architecture aarch64

参数可以在set architecture中找到,但是这里设置arm-v8a会有问题,原因未知。

然后就可以愉快的进入调试了,后续的调试过程与qemu上基本相同,不再赘述。

将输出重定向到串口

经过上述操作后,连接5550端口的terminal输出为乱码,而target的系统日志默认输出到屏幕,导致panic时无法查看现场和之前的printk输出,因此需要使屏幕输出重定向到串口。

通过修改/etc/default/grub,在GRUB_CMDLINE_LINUX即Linux的启动参数中添加console=ttyAMA0,115200,使系统日志同时输出到屏幕和串口。

重新生成grub.cfg:sudo grub2-mkconfig -o /boot/efi/EFI/openEuler/grub.cfg,重启后,即可在串口看到输出的系统日志。

目前暂未解决的问题

  1. 无法在gdb-multiarch中查看系统寄存器如TTBR1_EL1、TCR_EL1等的信息,暂不清楚是gdb-multiarch的限制还是串口的限制。
  2. 无法像在qemu中一样在gdb一侧使整个内核挂起,目前只能在target一侧通过echo g > /proc/sysrq-trigger来挂起内核。

    References

    【1】Using kgdb, kdb and the kernel debugger internals — The Linux Kernel documentation

This post is licensed under CC BY 4.0 by the author.