我正在尝试为 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/Kconfig
A10 部分的末尾添加了以下行:
select ARCH_SUPPORTS_BIG_ENDIAN
事实证明,这足以构建和启动大端内核,因为内核本身会将 CPU 切换到大端模式。然而,它是通过setend
指令来实现的——它的范围仅适用于内核本身,而不适用于用户空间(如 Murray Jensen 的答案中的链接所解释的那样)。
我要尝试一下:
通过异常跳转到内核的解压器入口点,或者(如果我失败)
修补内核,使其将进程生成为大端设置(尽管我在那里闻到了雷区的味道......)。