By Falcon of TinyLab.org

May 14, 2019

注:泰晓科技新增 Debugging+Tracing 专辑,全面连载各类 debug 实战技能。本文是该专辑的第一篇,欢迎持续关注。通过该专辑,您可以学习到分析和解决实际问题的方式、方法、工具和技巧。

背景介绍

前段时间为 Linux Lab 新增了 5.0.10 for ARM64/virt board,期间遇到了启动死机问题,本文对该问题进行详解。

在继续阅读之前,建议准备好 Linux Lab,方便同时做实验。为复现该问题,请先注释掉 boards/virt/Makefile 中的 QEMU 所在行,或者键入如下命令确保使用旧版本的 qemu。

$ export QEMU=

问题现象

编译完 Linux 5.0.10 并通过 Linux Lab 运行时:

$ make BOARD=virt

$ make boot V=1

直接卡死了,只看到 Qemu 的输出日志,没有看到任何内核日志输出 ;-(

分析过程

理清问题基本信息

首先需要承认,面对这种不按套路出牌的 Linux 启动状况(不确定性),这个时候谁都可能“惶恐不安”。接着告诉自己,问题必须解决,冷静!

有两个信息先捋一下:之前的 Linux v4.5.5 ok,启动完美

网上已经有同学完美启动了 5.0 for ARM64

所以,问题可能原因:Linux v5.0.10 相比 v4.5.5 有变更引入了衰退

Linux Lab 所使用的环境跟网上其他同学的环境有差异,可能差异在编译器和 Qemu。

接下来有两个思路:用二分法找出引入问题的变更,即从 v4.5.5 到 v5.0.10 之间找出第一个启动不了的内核版本

升级 Linux Lab 中的编译器和 Qemu 到最新版本

可是,虽然这两个工作都可行,但还都蛮耗费时间(第2个相对而言没那么耗时),所以,不能逃避,正面扛着看看!

定下分析方式

ok,先保持内核版本不变、环境不变,正面分析到底 Linux 内核卡死在哪里?!

打开 early Logging 机制

竟然内核什么都没有输出,那么先想办法输出点东西。

死得这么早,可能是普通串口初始化之前就挂了,要在这之前就打印东西,能想到是 early printk:

$ make boot V=1 XKCLI="earlycon"

earlycon 是内核最新的 early printk 逻辑,说明文档在 linux-stable/Documentation/admin-guide/kernel-parameters.txt:

earlycon= [KNL] Output early console device and options.

[ARM64] The early console is determined by the stdout-path property in device tree's chosen node, or determined by the ACPI SPCR table.

[X86] When used with no options the early console is determined by the ACPI SPCR table.

加上以后,很幸运,有东西咕噜咕噜滚出来了:

$ make boot V=1 XKCLI=earlycon

make _boot

make[1]: Entering directory `/labs/linux-lab'

sudo qemu-system-aarch64 -M virt -m 128M -net nic,model=virtio -net tap -device virtio-net-device,netdev=net0,mac=c0:2f:fd:e8:dc:ce -netdev tap,id=net0 -smp 2 -cpu cortex-a57 -kernel /labs/linux-lab/output/aarch64/linux-v5.0.10-virt/arch/arm64/boot/Image -no-reboot -initrd /labs/linux-lab/prebuilt/root/aarch64/cortex-a57/rootfs.cpio.gz -append 'route=172.17.0.5 root=/dev/ram0 earlycon console=ttyAMA0' -nographic

...

Booting Linux on physical CPU 0x0000000000 [0x411fd070]

Linux version 5.0.10-dirty (ubuntu@5016aaa36868) (gcc version 4.9.3 20150413 (prerelease) (Linaro GCC 4.9-2015.05)) #6 SMP Sun May 5 06:42:26 UTC 2019

Machine model: linux,dummy-virt

earlycon: pl11 at MMIO 0x0000000009000000 (options '')

printk: bootconsole [pl11] enabled

...

------------[ cut here ]------------

kernel BUG at /labs/linux-lab/linux-stable/arch/arm64/kernel/traps.c:425!

Internal error: Oops - BUG: 0 [#1] SMP

Modules linked in:

Process swapper (pid: 0, stack limit = 0x(____ptrval____))

CPU: 0 PID: 0 Comm: swapper Not tainted 5.0.10-dirty #37

Hardware name: linux,dummy-virt (DT)

pstate: 00000085 (nzcv daIf -PAN -UAO)

pc : do_undefinstr+0x280/0x2c0

lr : do_undefinstr+0x168/0x2c0

...

Call trace:

do_undefinstr+0x280/0x2c0

el1_undef+0x10/0x78

__cpuinfo_store_cpu+0x80/0x1d0

cpuinfo_store_boot_cpu+0x28/0x54

smp_prepare_boot_cpu+0x38/0x40

start_kernel+0x170/0x450

Code: 2a154035 17ffffb5 a9025bb5 f9001bb7 (d4210000)

---[ end trace 16cff5c8dd5a6423 ]---

Kernel panic - not syncing: Attempted to kill the idle task!

---[ end Kernel panic - not syncing: Attempted to kill the idle task! ]---

出错 Log 分析

出错日志有非常关键的 Calltrace 信息,初一看,发现是 undefinstr 异常,并且有触发异常的具体位置:

__cpuinfo_store_cpu+0x80/0x1e4

通过如下命令找到代码所在文件:

$ cd linux-stable

$ grep __cpuinfo_store_cpu -nur arch/arm64/

arch/arm64/kernel/cpuinfo.c:328:static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)

arch/arm64/kernel/cpuinfo.c:386:__cpuinfo_store_cpu(info);

arch/arm64/kernel/cpuinfo.c:393:__cpuinfo_store_cpu(info);

打开代码初步查看如下:

$ vim arch/arm64/kernel/cpuinfo.c +328

info->reg_cntfrq = arch_timer_get_cntfrq();

...

info->reg_id_aa64mmfr0 = read_cpuid(ID_AA64MMFR0_EL1);

info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);

info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1);

info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1);

info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1);

info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1);

初步猜测,可能是部分寄存器访问出错,但是具体哪一个错了还需要进一步确认。

定位代码出错位置

接下来需要通过 Backtrace 中的 __cpuinfo_store_cpu+0x80 找到准确的出错位置,也就是代码行。

这个寻找过程我们很早有专门的文章介绍,可以查阅:如何快速定位 Linux Panic 出错的代码行。

方法有几种,但是实际发现用 objdump 最为准确,gdb 在这个例子里有一行的偏差。

在这之前,必须确保 vmlinux 是带符号的,否则这些工具没法帮我们定位出错文件和代码行。要让 vmlinux 带符号,必须在 boards/virt/linux_v5.0.10_defconfig 中开启如下配置后重新编译内核:

CONFIG_DEBUG_KERNEL=y

CONFIG_DEBUG_INFO=y

CONFIG_KALLSYMS=y

重新配置和编译内核:

$ make kernel-defconfig

$ make kernel

接下来,回到 Linux Lab 主目录,开始定位问题所在的代码行。先得请 nm 帮找出 __cpuinfo_store_cpu 的地址:

$ aarch64-linux-gnu-nm output/aarch64/linux-v5.0.10-virt/vmlinux | grep __cpuinfo_store_cpu

ffffff80100919c0 t __cpuinfo_store_cpu

同时根据异常日志中的 “__cpuinfo_store_cpu+0x80/0x1d0” 计算出 start-address 和 stop-address,0x80 为出错位置,0x1d0 为函数 size,我们找出这个范围。

$ echo "obase=16;ibase=10;$((0x80100919c0+0x80))" | bc -l

8010091A40

$ echo "obase=16;ibase=10;$((0x80100919c0+0x1d0))" | bc -l

8010091B90

接着,请出 objdump 这尊大神,start-address 设置为函数入口,stop-address 设置为

$ aarch64-linux-gnu-objdump -dS output/aarch64/linux-v5.0.10-virt/vmlinux --start-address=0xffffff80100919c0 --stop-address=0xffffff8010091b90

...

info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);

ffffff8010091a3c:f901ba60 strx0, [x19, #880]

ffffff8010091a40:d5380740 mrsx0, id_aa64mmfr2_el1

info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1);

ffffff8010091a44:f901be60 strx0, [x19, #888]

ffffff8010091a48:d5380400 mrsx0, id_aa64pfr0_el1

info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1);

...

找到 “ffffff8010091a40” 所在行:

ffffff8010091a40:d5380740 mrsx0, id_aa64mmfr2_el1

info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1);

分析出错原因

看上去像是该寄存器访问异常。用 vim 打开注释掉该行并重新编译,重新启动,发现继续出错:

Call trace:

do_undefinstr+0x280/0x2c0

el1_undef+0x10/0x78

__cpuinfo_store_cpu+0x90/0x1c8

cpuinfo_store_boot_cpu+0x28/0x54

smp_prepare_boot_cpu+0x38/0x40

start_kernel+0x170/0x450

代码偏移为 0x90,出错位置为:0xffffff8010091b50,同样计算 start-address:0xffffff80100919c0 和 stop-address:0xffffff8010091b88,用 objdump 找到如下位置:

$ aarch64-linux-gnu-objdump -dS output/aarch64/linux-v5.0.10-virt/vmlinux --start-address=0xffffff80100919c0 --stop-address=0xffffff8010091b88

...

ffffff8010091a48:d5380422 mrsx2, id_aa64pfr1_el1

info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1);

ffffff8010091a4c:f901c662 strx2, [x19, #904]

ffffff8010091a50:d5380482 mrsx2, id_aa64zfr0_el1

info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1);

ffffff8010091a54:f901ca62 strx2, [x19, #912]

出错位置的代码如下:

ffffff8010091a50:d5380482 mrsx2, id_aa64zfr0_el1

info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1);

验证解决方法可行性

同样注释掉这部分,重新编译之后就可以正常启动了。需做修改如下:

$ cd linux-stable

$ git diff

-info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1);

+//info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1);

...

-info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1);

+//info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1);

启动日志如下:

..

Welcome to Linux Lab

linux-lab login: root

#

# uname -a

Linux linux-lab 5.0.10-dirty #39 SMP Sun May 5 13:00:38 UTC 2019 aarch64 GNU/Linux

至此,临时解决方案 ok。已经把上面两笔内核的修改制作成了 patch,并放到了boards/virt/patch/linux/v5.0/ 下,另外,在 boards/virt/Makefile 中配置了 “KP=1”,后续配置时会据此自动打 patch,打上后再编译就可启动了。

问题复盘

正面分析发现是 Linux 内核代码中有相关寄存器的操作引起了 undefinstr,这部分寄存器官方内核是支持的,说明真实硬件没有问题,所以需要继续看看运行环境部分,也就是这里的 Qemu 模拟器,很大可能是 Qemu 版本问题。

之前 Linux Lab 自带版本是 2.5:

$ qemu-system-aarch64 --version

QEMU emulator version 2.5.0 (Debian 1:2.5+dfsg-5ubuntu4), Copyright (c) 2003-2008 Fabrice Bellard

所以接下来要升级 Qemu 版本,由于未能找到编译好的 Qemu 版本,我们自行编译,并且给 Linux Lab 添加了 Qemu 编译支持,这里不做介绍。

编译完一个新的 v2.12.0 以后测试发现,可以直接正常启动不做改动的 Linux 5.0.10,所以证实了我们的猜测。

$ prebuilt/qemu/aarch64/v2.12.0/bin/qemu-system-aarch64 --version

QEMU emulator version 2.12.0

Copyright (c) 2003-2017 Fabrice Bellard and the QEMU Project developers

小结

本文详细介绍了一个典型的 Linux 内核死机问题。在实际产品研发过程中,死机问题会千奇百怪,死机路径可能随机变化,死机概率也因使用环境有高有低,所以,一方面要不断积累解决问题的技术经验,另外一方面尤其重要的是,沉着冷静,在面对类似难题时,坚定解决问题的信心。

欢迎添加作者微信 lzufalcon,进一步交流探讨!

qemu运行linux内核很慢,bugfix: Qemu 运行 ARM Linux 5.0 必现启动死机相关推荐

  1. Linux内核很吊之 module_init解析 (下)【转】

    转自:https://blog.csdn.net/richard_liujh/article/details/46758073 版权声明:本文为博主原创文章,未经博主允许不得转载. https://b ...

  2. Linux内核很吊之 module_init解析 (下)

    Linux内核很吊之 module_init解析 (下) 转载 2017年01月23日 13:58:21 标签: 95 编辑 删除 转自: http://blog.csdn.net/richard_l ...

  3. Linux内核精选文章向读者汇报 | 相遇Linux

    本微信公众号精选人气文章向各位读者汇报: 人气最火: Linux实时补丁即将合并进Linux 5.3 投资自己: OS部门Linux死锁/hungtask/soft_hard/lockup分享视频报名 ...

  4. Linux源代码逐条解释,Linux内核源代码解释让你真正了解linux.ppt

    Linux内核源代码解释让你真正了解linux.ppt 解读Linux内核源代码让您真正明白Linux操作系统 序 一些基本概念 操作系统的基本概念 I386系统的基本概念 Linux简介 源码阅读和 ...

  5. 如何安装新linux内核,详解Debian系统中安装Linux新内核的流程

    一直对Linux内核很有兴趣,但苦于入门不易,认真看了ldd前5章突然就来感觉了,光看不练不顶用,首先就需要环境搭建. 使用的是Debian 5.0,内核2.6.26,欲安装的新内核为2.6.28,这 ...

  6. linux内核_Linux驱动编程的本质就是Linux内核编程

    由于Linux驱动编程的本质属于Linux内核编程,因此我们非常有必要熟悉Linux内核以及Linux内核的特点. 这篇文章将会帮助读者打下Linux驱动编程的基础知识. 本篇文章分为如下三个小节进行 ...

  7. dtb文件linux位置,dtb文件的由来与ARM Linux 3.x的设备树(Device Tree)

    1. ARM Device Tree起源 Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称"this whole ARM thing is a f*ckin ...

  8. Linux 内核实验室 —— 基于 Docker/Qemu 的极速 Linux 内核学习、开发和测试环境

    介绍: 项目简介 本项目致力于创建一个基于 Docker + QEMU 的 Linux 实验环境,方便大家学习.开发和测试 Linux 内核. Linux Lab 是一个开源软件,不提供任何保证,请自 ...

  9. elipse调试linux内核,debug eclipse cdt + qemu虚拟机调试linux内核

    debug eclipse cdt + qemu虚拟机调试linux内核 (17页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.90 积分 A scr ...

  10. eclips调试linux内核,使用Eclipse调试Qemu及Linux Kernel

    1. 下载Qemu代码 1. 下载版本 下载qemu-2.6.0版本代码,下载地址 2. 解压tar jxvg qemu-2.6.0.tar.bz2 3. 配置 配置命令如下./configure – ...

最新文章

  1. ActiveMQ依赖JDK版本关系
  2. 小程序云服务器选什么系统好,小程序云服务器操作系统选择
  3. https和server-status配置案例
  4. Apache Flink 零基础入门(七)Flink中keyBy三种方式指定key
  5. 20个Flutter实例视频教程-第03节: 不规则底部工具栏制作-1
  6. java生成flash_针对 Flash 开发者的最新 Capuchin 计划资源
  7. 1015. 德才论 (25)-PAT乙级真题
  8. Java多线程知识点整理(Lock锁)
  9. PCL之点云配准--ICP
  10. flex +overflow在firefox与edge中不生效的问题
  11. mysql授权与回收_MySQL 授权,回收权限,查看权限
  12. 2023考研高数接力题典1800习题讲解
  13. 1600k 打印头测试软件,巧修 LQ-1600K打印头.doc
  14. 11个LOGO设计灵感网站推荐,帮你提高LOGO设计工作效率
  15. VLIW Microprocessor Hardware Design
  16. 网站常用JSON嵌套形式
  17. [Vue warn]: Unknown custom element: <helptext> - did you register the component correctly? For recu
  18. 1688API详情接口调用示例
  19. 蚂蚁区块链正式升级为蚂蚁链,究竟在下怎样的一盘大棋?
  20. 英语语法汇总(14.it的用法)

热门文章

  1. (二十六)Storm常见错误及处理方法
  2. cuda10安装——在CentOS上安装的艰难过程
  3. SSM大学生心理健康服务平台毕业设计-附源码071131
  4. 开关三极管的导通和截止条件
  5. 三角网格上高斯曲率和平均曲率
  6. 2019春第四次课程设计实验报告
  7. 树莓派连接USB摄像头问题
  8. 三相pwm整流器。采用电压电流双闭环,SVPWM调制
  9. ViewPage使用(一)
  10. 【NodeJs】NodeJs中base16转码