【问题标题】:Builtin Platform Driver __initcall Not Called on Linux Kernel InitLinux Kernel Init 上未调用内置平台驱动程序 __initcall
【发布时间】:2021-05-15 21:46:30
【问题描述】:

背景

我正在通过 Yocto 为一些供应商提供的嵌入式硬件提供 Linux 内核。我已将映像配置为通过带有 initramfs 和 no rootfs 的 fitImage 启动(有持久存储,但这完全是供用户空间应用程序使用的)。想想 PXE 实时图像,您就不会太远了。

在我的 initramfs 映像超过 ~128MB 标记之前,一切进展顺利。在此之下,一切都按预期启动,并且所有驱动程序都没有问题。超过此标记,内核仍会启动,但许多驱动程序(尽管不是全部)未绑定。这是相当令人困惑的,因为所有驱动程序都静态地内置在内核中(此平台上不使用任何模块)。不幸的是,其中一个模块运行平台看门狗,导致完全可预测的重启。

到目前为止,我已经验证了所有符号都存在于 vmlinux 映像中:

$ objdump -x vmlinux | grep mtk_wdt
0000000000000000 l    df *ABS*  0000000000000000 mtk_wdt.c
ffffff800880ac40 l     F .text  000000000000004c mtk_wdt_stop
ffffff800880ac90 l     F .text  0000000000000040 mtk_wdt_shutdown
ffffff80091de778 l     F .init.text     0000000000000020 mtk_wdt_driver_init
ffffff800880acd0 l     F .text  000000000000004c mtk_wdt_ping
ffffff800880ad20 l     F .text  0000000000000070 mtk_wdt_set_timeout
ffffff800880ad90 l     F .text  0000000000000074 mtk_wdt_start
ffffff800880ae08 l     F .text  0000000000000144 mtk_wdt_resume
ffffff800880af50 l     F .text  0000000000000120 mtk_wdt_suspend
ffffff800880b070 l     F .text  0000000000000080 mtk_wdt_remove
ffffff80088977a8 l     F .text  0000000000000210 mtk_wdt_isr
ffffff80091fe0a0 l     F .exit.text     000000000000001c mtk_wdt_driver_exit
ffffff800880b4f0 l     F .text  0000000000000310 mtk_wdt_probe
ffffff8008c2acd8 l     O .rodata        0000000000000028 mtk_wdt_info
ffffff8008c2ad00 l     O .rodata        0000000000000050 mtk_wdt_ops
ffffff8008c2ad98 l     O .rodata        00000000000000b8 mtk_wdt_pm_ops
ffffff8008c2ae50 l     O .rodata        0000000000000190 mtk_wdt_dt_ids
ffffff80093a3cb8 l     O .data  00000000000000b0 mtk_wdt_driver
ffffff800a199368 l     O .bss   0000000000000008 mtk_wdt1
ffffff8009285598 l     O .init.data     0000000000000008 __initcall_mtk_wdt_driver_init6

此外,我在 fitImage 程序集之前、fitImage 程序集之后和解压缩到系统内存之后(通过引导加载程序)对二进制内核(例如 linux.bin)、initramfs 和设备树进行了 sha256 校验和;都匹配。据我所知,构建的内容就是解包和启动的内容。

此外,我启用了initcall_debug,当我看到其他 __initcall() 时,毫无疑问,未绑定的驱动程序丢失了。

我知道设备存在于设备树中并且配置正确。启动后,我在看门狗启动之前获得了大约 5 秒的控制台访问权限;只是有足够的时间来完成一两个命令。在“工作”图像(initramfs ~128MB)上,/sys/bus/platform/devices 的内容是相同的,我可以看到(除其他外),我的看门狗:

$ ls -lha /sys/bus/platform/devices
...
lrwxrwxrwx 1 root root 0 Jan  1 00:00 10007000.watchdog -> ../../../devices/platform/10007000.watchdog

执行相同的测试但比较 /sys/bus/platform/devices 显示未 __initcall() 的驱动程序丢失。

我检查过的其他一些集体事项:

  • 设备树。工作图像和损坏图像都使用相同的 DTB。如上所述,我还验证了内存中的设备树。不使用设备树覆盖。
  • 实际内存负载偏移量。一切都在应该的地方,每个区域都有足够的空间。我可以在内存中移动内核,但问题仍然存在,无论位置如何。
  • 记性不好。这种故障在多个单元中同样发生。
  • 压缩错误。无论内核/initramfs 压缩如何,问题都会出现。目前我正在测试所有未压缩的内容,以尽量减少破损点。
  • 签名错误。我已禁用签名验证(毕竟应用于 fitImage 分区映像);那里也没有骰子。
  • 尝试将 initramfs 直接捆绑到内核中。不用找了。现在我有 initramfs 内置在 fitImage 中,但在其他方面独立加载和验证。
  • 错误的内核命令行参数。我正在使用root=/dev/ram initrd=0x48000000,384M,并一直追踪到init/initramfs.c,在那里完成了拆包。我能够验证传递的偏移量确实在正确的虚拟内存空间中,总和为 384M。
  • 根据this 论坛帖子更新内核链接描述文件。我可以看到通过 objdump 在 vmlinux 中生成的 .initramfs 部分,但问题仍然存在。

鉴于以上所有,我唯一不知道如何验证的是从 vmlinux 跳转到 linux.bin。这是通过 objcopy 在 Yocto 中完成的,如下所示:

[ -n "${vmlinux_path}" ] && ${OBJCOPY} -O binary -R .note -R .comment -S "${vmlinux_path}" linux.bin

问题

  1. 如何验证给定符号是否包含在最终的 linux.bin 中?
  2. 什么机制会影响构建时包含或排除给定符号?
  3. 内核构建和运行时的哪些部分受 initramfs 大小的影响?
  4. 是否有任何其他工具/技术/部落智慧可以帮助调试这种情况?

编辑 1

以下是所有内容所在位置和空间利用率的基本内存映射。如上所述,在 cmets 中,我可以将内核、DTB 和 initramfs 重新定位到(几乎)任意位置,但问题仍然存在。

0x40000000 - 0x40001000 = Bootloader arg area (Fixed usage)
0x40080000 - 0x41EDFFFF = Kernel (~12MB / 29.5MB used)
0x41E00000 - 0x42FF5FFF = Trampoline (96 bytes / ~6MB used)
0x42FF6000 - 0x42FFFFFF = ATF BL3-1 (Fixed usage)
0x43000000 - 0x43FFFFFF = Trusted OS (~476K / 16M used)
0x44000000 - 0x44FFFFFF = DTB (~77.3K / 16M used)
0x45000000 - 0x47FFFFFF = Trusted OS memory (dynamic)
0x48000000 - 0x5FFFFFFF = Initramfs (~129MB / 384MB used)
0x60000000 - MEM END    = Free

【问题讨论】:

  • 首先我要检查的(根据我的经验)是:引导加载程序(它可能会剪切文件),解压的 initramfs 不会与内核和 DTB 发生冲突的内存偏移量,解压后的 vmlinux 不会发生冲突使用 initramfs,硬件的内存区域(如果有间隙)。在上面没问题之后,寻找有关硬件的勘误表(听起来像是 MMU / DMA / ... 问题或硬件限制)。
  • 检查的一个选项是将所有内容合并到一个图像文件中(DTB + vmlinuz + initramfs)。并首先尝试硬编码看门狗驱动程序不要触发。大多数看门狗都可以选择禁用它们。
  • @0andriy:我相当肯定没有剪裁和碰撞,因为 sha256 和是在所有解包/重新定位之后执行的。我还可以将内核、initramfs 和 DTB 重新定位到内存中的任何空闲(映射)空间,问题仍然存在。我也尝试将所有内容打包到一张图片中,但没有成功。
  • @0andriy:硬编码看门狗是一个有趣的想法。所有驱动程序和早期初始化的东西都随着内核启动而被丢弃(我对 __init 和 __initcal 的理解)。一旦内核达到稳定状态,我如何验证它们在初始化时是否存在?

标签: linux linux-kernel embedded-linux yocto debug-symbols


【解决方案1】:

因此,像大多数内核问题一样,真正的问题并不在我认为的位置。事实证明,问题是由 init 列表中较早的其他驱动程序之一挂起核心引起的,从而阻止了看门狗驱动程序的注册。这如何受 initramfs 影响超出了我的范围,这是它自己的问题。

对于以后遇到此问题的任何人,我上面的具体问题的答案如下:

  1. 如何验证给定符号是否包含在最终的 linux.bin 中?

我无法弄清楚如何静态地执行此操作。也就是说,我可以在运行时通过在init/main.c 中添加printk()s 到do_initcall_level 来打印init 函数的地址。然后可以将打印的地址与 vmlinux 上 objdump 的输出进行比较(有关咒语,请参阅我的问题)。

可以在here 找到对 initcall 过程非常有用且深入的描述。

注意你也可以打开initcall_debug,它会打印出每个函数名。就我而言,我想要原始地址,这就是我选择 printk() 方法的原因。

  1. 哪些机制会影响构建时包含或排除给定符号?

其中大部分归结为您的 .config。绝大多数包含/排除是通过预处理器完成的。其他有用的项目是include/asm-generic/vmlinux.lds.h 的链接器脚本公共标头和您设备的平台链接器脚本arch/<arch>/*/*.lds

  1. 内核构建和运行时的哪些部分受 initramfs 大小的影响?

关于这个,还是不知道。

  1. 是否有其他工具/技术/部落智慧可以帮助调试这种情况?

别慌

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-24
    • 2018-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    相关资源
    最近更新 更多