解压失败时如何找到 ARM Linux 入口点?

Posted

技术标签:

【中文标题】解压失败时如何找到 ARM Linux 入口点?【英文标题】:How do I find ARM Linux entry point when it fails to uncompress? 【发布时间】:2013-08-25 01:44:39 【问题描述】:

我正在尝试在带有 i.MX6 的自定义板上通过 U-boot 启动 Linux(CPU 内核是 ARM Cortex A9)

我们似乎已经成功移植了 Das U-Boot(2009.08)。但是在最后一条 U-Boot 消息中引导 Linux 失败:“正在启动内核 ...”

这是我的相关环境:

bootargs=console=ttymxc1,115200 vmalloc=400M root=/dev/mmcblk0p1 rootwait consoleblank=0 earlyprintk video=mxcfb0:dev=lcd,LCD-ORTUS,if=RGB24 video=mxcfb1:dev=hdmi,1280x720M@60,if=RGB24 calibration tsdev=tsc2004 fbmem=10M,28M
bootcmd=ext2load mmc 0:1 10800000 /boot/uImage ; bootm 10800000

开机输出是

Loading file "/boot/uImage" from mmc device 0:1 (xxa1)  
4043552 bytes read  
## Booting kernel from Legacy Image at 10800000 ...  
   Image Name:   Linux-3.0.35  
   Image Type:   ARM Linux Kernel Image (uncompressed)  
   Data Size:    4043488 Bytes =  3.9 MB  
   Load Address: 10008000  
   Entry Point:  10008000  
   Verifying Checksum ... OK  
   Loading Kernel Image ... OK  
OK  

Starting kernel ...  

当我在地址 80008000 处对内核进行 objdump 时,我看到入口点位于 arch/arm/kernel/head.S,而不是 arch/arm/boot/compressed/head.S

我看到的是,内核甚至没有解压缩。我尝试在compressed/head.S 中添加一些寄存器操作代码来向 GPIO 发送信号,但没有响应。

我的问题是,如何确保 U-Boot 调用了正确的入口点?

完全相同的内核二进制文件在飞思卡尔的参考板上成功启动,使用相同的 U-Boot 命令。

编辑:我在 U-Boot 中添加了一些痕迹。在调用内核之前,指针 theKernel 是 10008000 而不是 10800000。这是否意味着 U-boot 跳错了位置?

【问题讨论】:

你有 JTAG 吗?如果是这样,只需调试它。 我认为kernel需要解压后才会启动,启动kernel print后,kernel会跳转到u-boot从kernel image header获取的kernel入口函数。您将压缩内核从引导介质复制到 10800000,bootm 命令将解压缩并将内核放在正确的加载地址,即 10008000,距离 RAM 起始地址 32K 的边界。如果它没有启动,那么很可能原因是机器 ID。我想知道正确的原因是什么? 【参考方案1】:

我们似乎已经成功移植了 Das U-Boot。

有证据表明这是一个错误的假设。

在调用内核之前,指针 theKernel 是 10008000 而不是 10800000。

您使用的是哪个版本的 U-Boot? 在 U-Boot 的 2012.10 和 2013.04 版本中,变量 theKernel 仅由 AVR32 和 MIPS 等架构的代码声明和使用。 没有应该使用theKernel 的ARM 代码。

u-boot-2012.10$ find . -print | xargs grep theKernel
./arch/avr32/lib/bootm.c:   void    (*theKernel)(int magic, void *tagtable);
./arch/avr32/lib/bootm.c:   theKernel = (void *)images->ep;
./arch/avr32/lib/bootm.c:          theKernel, params_start);
./arch/avr32/lib/bootm.c:   theKernel(ATAG_MAGIC, params_start);
./arch/microblaze/lib/bootm.c:  void    (*theKernel) (char *, ulong, ulong);
./arch/microblaze/lib/bootm.c:  theKernel = (void (*)(char *, ulong,    ulong))images->ep;
./arch/microblaze/lib/bootm.c:      (ulong) theKernel, rd_data_start, (ulong) of_flat_tree);
./arch/microblaze/lib/bootm.c:  theKernel (commandline, rd_data_start, (ulong) of_flat_tree);
./arch/mips/lib/bootm.c:    void (*theKernel) (int, char **, char **, int *);
./arch/mips/lib/bootm.c:    theKernel = (void (*)(int, char **, char **, int *))images->ep;
./arch/mips/lib/bootm.c:        (ulong) theKernel);
./arch/mips/lib/bootm.c:    theKernel(linux_argc, linux_argv, linux_env, 0);
./arch/mips/lib/bootm_qemu_mips.c:  void (*theKernel) (int, char **, char **, int *);
./arch/mips/lib/bootm_qemu_mips.c:  theKernel = (void (*)(int, char **, char **, int *))images->ep;
./arch/mips/lib/bootm_qemu_mips.c:      (ulong) theKernel);
./arch/mips/lib/bootm_qemu_mips.c:  theKernel(0, NULL, NULL, 0);
./arch/nds32/lib/bootm.c:   void    (*theKernel)(int zero, int arch, uint params);
./arch/nds32/lib/bootm.c:   theKernel = (void (*)(int, int, uint))images->ep;
./arch/nds32/lib/bootm.c:          (ulong)theKernel);
./arch/nds32/lib/bootm.c:   theKernel(0, machid, bd->bi_boot_params);
u-boot-2012.10$

请说明您如何能够跟踪不应在 ARM 处理器上定义或分配的变量。

U-Boot 打印“Starting kernel ...”后的下一个输出应该是“Uncompressing Linux...”。 对于飞思卡尔架构,此文本输出取决于 U-Boot 将 machine type number(又名 arch_id)正确传递给内核。 您需要验证此machine type number 是否在 U-Boot 中正确定义。

您的 U-Boot 配置文件是什么样的?

我尝试添加一些寄存器操作代码来向压缩/head.S 中的 GPIO 发送信号,但没有响应。

您是否仔细检查了此代码以确保其按预期工作? 您是否尝试过 U-Boot 命令行中的 GPIO 操作?

我的问题是,如何确保 U-Boot 调用了正确的入口点?

对于 ARM 架构,它是跳转到 bootm 命令中指定的地址。 由于 uImage 加载地址和 bootm 指定了相同的 0x10800000 地址,这应该很好(假设 U-Boot 已正确配置并为 ARM 构建)。

在调用内核之前,指针 theKernel 是 10008000 而不是 10800000。这是否意味着 U-boot 跳错了位置?

是的。 如果您检查源代码(对于 AVR32 或 MIPS),您会发现 theKernel 是从图像标题分配的,特别是入口点值。 U-Boot 然后会跳转到这个位置。 但真正的问题是您的 ARM Cortex A9 不应该使用此代码或此变量。 似乎 U-Boot 没有为正确的拱门配置和/或机器类型可能没有正确定义。

更正

正如 OP 所指出的,旧版本的 U-Boot 确实使用了变量 theKernel,即使对于 ARM 架构也是如此。

U-Boot输出的那一行:

   Loading Kernel Image ... OK  

表示 U-Boot 已经(成功地)将内核映像(没有映像信息头)从地址为 0x10800000 的bootm(加上头长度的偏移量 0x40)复制到加载地址 0x10008000。此内存移动操作由 common/cmd_bootm.c 中的过程bootm_load_os() 执行。

因此,您报告的 0x10008000 的值对于 theKernel 是正确的。 没有迹象表明 U-Boot 跳到了错误的位置。

如前所述,您应该验证机器类型是否正确定义。该值将在arch/arm/plat-mxc/include/mach/uncompress.h 中的__arch_decomp_setup() 中使用,以便在内核启动之前的解压过程中输出文本。

【讨论】:

谢谢你的详细解释。我还将编辑我的问题,因为我们使用的是 U-Boot 2009.08,这是飞思卡尔官方支持的版本。在那里,theKernel 用于名为 lib_arm 的子树中。此外,我们使用 Cortex A8 架构来启动 A9,这对于其他基于 mx6 的主板来说似乎非常好。 您的修改解决了问题!我不知道我必须将自定义板定义添加到 mach/uncompress.h @Atilla Filiz 如果您添加了用于使事情正常工作的 arch_id 和机器类型,那就太好了。 @CaptainBli - 从 U-Boot 传递到内核的机器类型的实际值不如确保它被识别重要。 OP 显然为他的定制板创建了一种新的机器类型,但错过了一个需要编辑的源文件。提示:grep 原始板机器类型的整个源代码树(内核和引导加载程序)以查找需要更新的代码。 @sawdust 你也可以看看this【参考方案2】:

您似乎没有在引导 vmlinux 内核文件,因此您不必担心入口点。映像开头的解压代码将根据需要重新定位内核,并在完成后跳转到正确的入口点。您只需跳过 uBoot 似乎正确执行的图像的开头即可。

我会打开内核调试,尤其是 earlyprintk 和低级调试选项,然后再次尝试引导。至少你能看到它在哪里塞满了。

编辑:正如所指出的,我的回答仅适用于 uBoot 首先正确地做这件事。在这种情况下,有可能不是。也许您可以创建并尝试启动一个虚拟“内核”,它只是打开一些 LED 或将一些寄存器值输出到串行(特别是 r0、r1 和 r2)。然后你至少可以检查和/或排除 uBoot 是罪魁祸首。

【讨论】:

“uBoot 似乎运行正确。” -- 支持该结论的证据为零。 “我会打开内核调试” -- OP 明确指出没有迹象表明内核已解压缩。由于内核尚未开始引导,因此您不能期望从“内核调试”功能中获得任何东西。您只是提供对这种特定情况没有帮助的通用建议。【参考方案3】:

难道U-Boot正在加载的文件实际上是vmlinux文件的二进制映像,而不是自解压的zImage/bzImage?这只是一个猜测,我不是这方面的专家。

我最近在 Unix Stack Exchange 上提出的这个问题可能会让您感兴趣:https://unix.stackexchange.com/questions/197225/is-vmlinuz-and-bzimage-really-the-same

【讨论】:

以上是关于解压失败时如何找到 ARM Linux 入口点?的主要内容,如果未能解决你的问题,请参考以下文章

如何安装arm-linux-gcc

如何在 conda 环境中安装库时修复未找到入口点

如何使用更改的入口点(sys.path)在 Python 中导入文件?

如何找到 Spring Boot 应用程序的入口点?

安装交叉编译工具

arm-fsl-linux-gnueabi交叉编译器安装