使用 u-boot 获取大端 Linux 构建以在 ARM 上启动

使用 u-boot 获取大端 Linux 构建以在 ARM 上启动

我正在尝试为 ARM 制作一个大端的 Linux 发行版。由于我使用的是 Gentoo,所以交叉编译再简单不过了。我已经构建了所有这些,但后来却陷入了启动它/内核的困境。

我的目标是配备 AllWinner A10 CPU 的 Cubieboard。我使用 u-boot 作为引导加载程序。由于 u-boot 不支持大端 ARM,因此我在将控制权传递给内核之前对其进行了修补:

diff -Naur u-boot-2016.01-1/arch/arm/lib/bootm.c u-boot-2016.01-2/arch/arm/lib/bootm.c
--- u-boot-2016.01-1/arch/arm/lib/bootm.c       2016-01-12 15:06:54.000000000 +0100
+++ u-boot-2016.01-2/arch/arm/lib/bootm.c       2017-07-09 14:13:29.675865446 +0200
@@ -315,7 +315,16 @@
                                                          0, machid, r2);
                } else
 #endif
+               {
+                       {
+                               unsigned long v;
+                               __asm volatile ("mrc p15, 0, %0, c1, c0, 0\n\t"
+                                               "orr %0, %0, #(1 << 7)\n\t" /* Switch to bigendian */
+                                               "mcr p15, 0, %0, c1, c0, 0" : "=&r" (v));
+                       }
+
                        kernel_entry(0, machid, r2);
+               }
        }
 #endif
 }

我最初使用了一些不同的语法,但最终从 APEX 引导加载程序(其arm-kernel-shim)中获取了已知可用的代码。 (尽管如此,该寄存器与我拥有的以及我在 ARM 文档中读到的相同。)

另外,由于 u-boot 需要采用小端字节序,因此我为其准备了另一个工具链 - target arm-linux-gnueabihf。据我所知,u-boot 本身可以正常启动。

(主线)内核是使用 target 的工具链编译的armeb-linux-gnueabihf。从编译后的映像(arch/arm/boot/Image在内核源/构建树中),我构建了一个可启动映像(使用mkimage我的 u-boot 构建):

mkimage -C none -A arm -T kernel -n Linux-4.9.9-gentoo -d /usr/armeb-linux-gnueabihf/usr/src/linux/arch/arm/boot/Image -ep 0x48000000 -a 0x48000000 /usr/armeb-linux-gnueabihf/boot/uimage

我也从内核中获取了 DTB 文件:

cp /usr/armeb-linux-gnueabihf/usr/src/linux/arch/arm/boot/dts/sun4i-a10-cubieboard.dtb /usr/armeb-linux-gnueabihf/boot/

当我将其全部加载到 µSD 卡并尝试启动时,我在串行控制台上得到以下输出:

U-Boot SPL 2016.01 (Jul 16 2017 - 13:52:00)
DRAM: 1024 MiB
CPU: 1008000000Hz, AXI/AHB/APB: 3/2/2
Trying to boot from MMC


U-Boot 2016.01 (Jul 16 2017 - 13:52:00 +0200) Allwinner Technology

CPU:   Allwinner A10 (SUN4I)
I2C:   ready
DRAM:  1 GiB
MMC:   SUNXI SD/MMC: 0
In:    serial
Out:   serial
Err:   serial
SCSI:  SUNXI SCSI INIT
SATA link 0 timeout.
AHCI 0001.0100 32 slots 1 ports 3 Gbps 0x1 impl SATA mode
flags: ncq stag pm led clo only pmp pio slum part ccc apst
Net:   eth0: ethernet@01c0b000
starting USB...
USB0:   USB EHCI 1.00
USB1:   USB OHCI 1.0
USB2:   USB EHCI 1.00
USB3:   USB OHCI 1.0
scanning bus 0 for devices... 1 USB Device(s) found
scanning bus 2 for devices... 1 USB Device(s) found
Hit any key to stop autoboot:  0
=> setenv bootargs console=tty0 console=ttyS0,115200 earlyprintk hdmi.audio=EDID:0 disp.screen0_output_mode=EDID:1280x800p60 root=PARTUUID=AC9D6C6F-01 rootwait panic=10
=> ext2load mmc 0 0x48000000 boot/uimage
5025856 bytes read in 592 ms (8.1 MiB/s)
=> ext2load mmc 0 0x51000000 boot/sun4i-a10-cubieboard.dtb
28542 bytes read in 237 ms (117.2 KiB/s)
=> bootm 0x48000000 - 0x51000000
## Booting kernel from Legacy Image at 48000000 ...
   Image Name:   Linux-4.9.9-gentoo
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    5025792 Bytes = 4.8 MiB
   Load Address: 48000000
   Entry Point:  48000000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 51000000
   Booting using the fdt blob at 0x51000000
   Loading Kernel Image ... OK
   Loading Device Tree to 49ff6000, end 49ffff7d ... OK

Starting kernel ...

它就这样挂在那里——没有进展,没有输出,什么也没有。我的问题是,如何从这里开始/如何找出实际发生的情况?我是否遗漏了什么,我做错了什么(或没有做某事)?

我还尝试了一些但没有成功的事情:

  • 对内核映像进行字交换(如 APEX 所做的那样)(导致undefined instruction启动时),

  • 使用压缩的内核映像,

  • 使用旧版 FEX 文件而不是 FDT。


2017/07/21 更新:我已经部分成功地解决了我的问题。我从 Tom Rini 的评论中得到了提示,并尝试将 zImage 打包到 uImage 中,从而使内核启动。通过自定义 init 程序(只是一个简单的 Hello World 编译的 BE 或 LE),我证实了另一个 Tom Rini 的怀疑:我原来的内核不是大端,而是小端。为了解决这个问题,我将以下两行添加到内核的顶部.config

CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y
CONFIG_CPU_BIG_ENDIAN=y

我的灵感来自于Nvidia Jetson TK1 操作方法。我还在arch/arm/mach-sunxi/KconfigA10 部分的末尾添加了以下行:

select ARCH_SUPPORTS_BIG_ENDIAN

事实证明,这足以构建和启动大端内核,因为内核本身会将 CPU 切换到大端模式。然而,它是通过setend指令来实现的——它的范围仅适用于内核本身,而不适用于用户空间(如 Murray Jensen 的答案中的链接所解释的那样)。

我要尝试一下:

  • 通过异常跳转到内核的解压器入口点,或者(如果我失败)

  • 修补内核,使其将进程生成为大端设置(尽管我在那里闻到了雷区的味道......)。

答案1

对 ARM 还不太了解,但根据,“CP15 系统控制寄存器 (SCR) 中的 EE 位确定异常时设置的字节顺序(即操作系统本身的字节顺序)。”...所以在我看来,您需要通过异常以某种方式输入加载的内核映像,即您不能只是跳转到该地址(正如 U-Boot 似乎所做的那样 - 请boot_jump_linux()参阅)。

相关内容