2020年5月7日 星期四

編譯 ARM HF Linux 核心

ARM 工具鏈說明 (2021/04/30 改版)

Debian 支援三個不同版本的 ARM 架構,分別是

armel - 沒有 FPU 的 ARM CPU
armhf - 有 FPU,可以硬體執行浮點運算的 ARM CPU
arm64 - 64 位元的 ARM CPU

以本學期的課程來說,我們以 armhf 架構為主來進行工具鏈 (toolchain) 的安裝,這些工具鏈一樣是用來編譯 ARM 版本的 Linux 核心以及其它應用程式,是建構嵌入式系統的基本工具。

1. 安裝

請以 root 權限安裝底下套件:

gcc-arm-linux-gnueabihf
cpp-arm-linux-gnueabihf
g++-arm-linux-gnueabihf

安裝完畢後可以測試這三個套件的可執行檔,指令如下:

$arm-linux-gnueabihf-gcc -v

$arm-linux-gnueabihf-cpp -v
$arm-linux-gnueabihf-g++ -v

上述的輸出有一個非常重要的重點在此:

Using built-in specs.
COLLECT_GCC=arm-linux-gnueabihf-g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/arm-linux-gnueabihf/6/lto-wrapper
Target: arm-linux-gnueabihf

其中的 Target: arm-linux-gnueabihf 這行告訴我們這是針對 armhf 平台所開發的編譯器。如果你用之前教的 gcc -v 會看到其內容如下:

Target: x86_64-linux-gnu

這表示其編譯目標平台是 x86_64。

2. 測試 armhf 編譯器

2.1 建立 hello.c

請建立 hello.c,其內容如下:

#include <stdio.h>

void main(void)
{
 printf("Hello World\n");
}


2.2 編譯 hello.c 為 armhf 之可執行檔

$ arm-linux-gnueabihf-gcc hello.c -o hello.arm

此時會出現 hello.arm 這個可執行檔,其檔案資訊如下:

$ file hello.arm
hello.arm: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamicall
y linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha
1]=155090b58f9384c7e128b74e743a7993155658fc, not stripped

這個可執行檔是 arm 32 位元版本的可執行檔,從 X86 平台編譯出 ARM 平台可執行檔的流程稱為 cross compile (交叉編譯器)。

此時如果我們要執行 hello.arm 這個程式的話,我們會看到底下輸出:

$ ./hello.arm
bash: ./hello.arm: 無法執行二進位檔案: 可執行檔格式錯誤

(或是英文顯示)

./hello.arm
bash: ./hello.arm: cannot execute binary file: Exec format error

這表示我們的平台目前沒有支援 ARM 32 的可執行檔,必須另外再進行處理才可以支援。

2.3 將 Debian 新增 armhf 架構

Debian 支援硬體架構:

https://wiki.debian.org/SupportedArchitectures

首先我們要觀察目前系統所支援的外掛架構 (非原生 x86_64 架構) 有哪些,請執行:

# dpkg --print-foreign-architectures

理論上不會有任何輸出,因為我們還沒加入 armhf 架構的支援。在大一 Linux 課程中有教大家如何加入 i386 架構的支援,其說明在 157 頁第六章。

要新增 armhf 架構的話,請以 root 權限執行:

# dpkg --add-architecture armhf

此時再執行 dpkg --print-foreign-architectures 指令時會看到有新增一個 armhf
架構,如底下所示:

# dpkg --print-foreign-architectures
armhf

接下來要安裝支援的軟體,請執行

# apt-get update
# apt-get upgrade
# apt install qemu-system:armhf qemu-user:armhf qemu-user-static

安裝所需要的軟體,然後就可以準備執行 hello.arm 這個可執行檔。


2.4 測試 armhf 可執行檔

要在 X86_64 系統中執行 arm 的可執行檔,請執行底下指令:

$ qemu-arm ./hello.arm
Hello World

3. 編 ARM 版 Linux 核心


3.1 下載
請在家目錄建立 ~/qemu_image/arm 目錄,然後在底下建立 kernel 目錄並將 linux-4.4.268.tar.xz 這個檔案解壓縮並放入此目錄,指令如下:

$ mkdir -p ~/qemu_image/arm/kenel
$ tar xfva linux-4.4.268.tar.xz  -C ~/qemu_image/arm/kenel

3.2 解壓縮

$ cd ~/qemu_image/arm/kernel
~/qemu_image/arm/kernel $ tar xfva linux-3.18.123.tar.xz

此時會出現一個 linux-4.4.268 目錄,接下來要設定編譯參數。

3.3 設定核心編譯參數 (依照預設值即可)

3.3.1 虛擬 ARM 平台介紹

核心編譯與 ARM 平台有密切的關係,如果沒有編對平台則無法開機,我們這學期所使用的虛擬 ARM 平台為 qemu-system-arm 已支援的平台,要查詢支援平台的指令如下:

$ qemu-system-arm -M ?

其輸出中有一行:

vexpress-a9

這是指 Versatile Express 平台,其 CPU 為 Cortex A9。

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.boards.express/index.html

我們這學期以此虛擬平台作為上課使用的開發平台。

3.3.2 設定核心編譯參數

請切換至 linux-3.18.123 目錄,然後執行:

~/qemu_image/arm/kernel/linux-3.18.123 $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig

其輸出如下:

ROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  SHIPPED scripts/kconfig/zconf.tab.c
  SHIPPED scripts/kconfig/zconf.lex.c
  SHIPPED scripts/kconfig/zconf.hash.c
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#

接著要手動執行核心編譯參數設定

~/qemu_image/arm/kernel/linux-3.18.123 $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

請執行底下修改,否則之後無法開機:

    General setup  --->
        [*] open by fhandle syscalls

    System Type  --->
        [ ] Enable the L2x0 outer cache controller

-*- Enable the block layer  --->
        [*]   Support for large (2TB+) block devices and files


接著離開核心編譯環境,並儲存參數設定。

3.4 編譯核心

請執行:

~/qemu_image/arm/kernel/linux-4.4.268 $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImage LOADADDR=0x60000000 -j 8

~/qemu_image/arm/kernel/linux-4.4.268 $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs modules -j 8

** 如果嫌每次都要打 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 這串指令太長的話,也可以把這串指令設定在 .bashrc 中,請在 .bashrc 中新增底下設定:

alias makearmhf='make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-'

之後,我們只要打 makearmhf 就等於 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 這串指令。

=============================================================================
故障排除:

如果在編譯核心時遇上以下錯誤:

"mkimage" command not found - U-Boot images will not be built
arch/arm/boot/Makefile:79: recipe for target 'arch/arm/boot/uImage' failed
make[1]: *** [arch/arm/boot/uImage] Error 1
arch/arm/Makefile:314: recipe for target 'uImage' failed
make: *** [uImage] Error 2

這是因為沒有 mkimage 這個指令,請上 packages.debian.org 查詢此指令的套件並安裝,安裝完畢後再執行一次 make uImage 指令。

# apt-get install u-boot-tools

=============================================================================
成果驗收:

1. 核心編譯完畢後會出現底下訊息:

Image Name:   Linux-4.4.268
Created:      Fri Apr 30 11:29:49 2021
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    3512976 Bytes = 3430.64 KiB = 3.35 MiB
Load Address: 60000000
Entry Point:  60000000
  Image arch/arm/boot/uImage is ready


我們可以驗收一下 uImage 這個檔案,指令如下:

~/qemu_image/arm/kernel/linux-4.4.268 $ file arch/arm/boot/uImage
arch/arm/boot/uImage: u-boot legacy uImage, Linux-4.4.268, Linux/ARM, OS Kernel Image (Not compressed), 3512976 bytes, Fri Apr 30 03:29:49 2021, Load Address: 0x60000000, Entry Point: 0x60000000, Header CRC: 0x45384E4C, Data CRC: 0x07228E4B


從上面的檔案可以看到這是一個 Linux/ARM 的 OS 核心,版本為 4.4.268

**** 請注意,核心編好 !===== 核心可以用,我們之後會驗證這個核心能不能開機 ****

3.5 編譯核心模組

核心要正常工作必須要有相對應的核心模組,否則無法載入驅動程式,

# Compile Kernel Modules
time make -s ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules -j $CPU_CORE

# Install Kernel Modules
time make -s ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_install INSTALL_MOD_PATH=../modules/ -j $CPU_CORE

cd ../modules
time tar cfa ../modules-$KERNEL_VERSION.tar.xz lib

編譯完成後我們會有一個名為 modules-核心編號.tar.xz 的檔案,此檔案可以用來取代原始 rootfs 中 /lib/modules 目錄中舊版的驅動核心模組。因此我們必須將舊版驅動核心模組移除,再放入我們剛剛編完之驅動核心模組。


3.6 測試編好的 Linux 核心模組能否開機

請切換到 linux-KERNEL_VERSION/arch/arm/boot 底下,然後執行:

$ qemu-system-arm -M vexpress-a9 -m 256 -nographic -kernel zImage

$ qemu-system-arm -M vexpress-a9 -m 256M -dtb dts/vexpress-v2p-ca9.dtb -nographic -kernel zImage -append "console=ttyAMA0"


此時會出現開機畫面,然後當掉,如底下所示:

pulseaudio: set_sink_input_volume() failed
pulseaudio: Reason: Invalid argument
pulseaudio: set_sink_input_mute() failed
pulseaudio: Reason: Invalid argument
Booting Linux on physical CPU 0x0
Initializing cgroup subsys cpuset
Linux version 4.4.268 (herman@120-117-72-71) (gcc version 8.3.0 (Debian 8.3.0-2) ) #1 SMP Fri Apr 30 11:24:29 CST 2021
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
Machine model: V2P-CA9
Memory policy: Data cache writeback
CPU: All CPU(s) started in SVC mode.
PERCPU: Embedded 11 pages/cpu @8fdc1000 s13964 r8192 d22900 u45056
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 65024
Kernel command line: console=ttyAMA0

..


ALSA device list:
  #0: ARM AC'97 Interface PL041 rev0 at 0x10004000, irq 33
input: ImExPS/2 Generic Explorer Mouse as /devices/platform/smb/smb:motherboard/smb:motherboard:iofpga@7,00000000/10007000.kmi/serio1/input/input2
VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
Please append a correct "root=" boot option; here are the available partitions:
1f00          131072 mtdblock0  (driver?)
1f01           32768 mtdblock1  (driver?)
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.4.268 #1
Hardware name: ARM-Versatile Express
[<80015ab0>] (unwind_backtrace) from [<80012604>] (show_stack+0x10/0x14)
[<80012604>] (show_stack) from [<804b4510>] (dump_stack+0x94/0xa8)
[<804b4510>] (dump_stack) from [<804b0624>] (panic+0x9c/0x1f4)
[<804b0624>] (panic) from [<8063e2b4>] (mount_block_root+0x1c0/0x250)
[<8063e2b4>] (mount_block_root) from [<8063e444>] (mount_root+0x100/0x108)
[<8063e444>] (mount_root) from [<8063e59c>] (prepare_namespace+0x150/0x198)
[<8063e59c>] (prepare_namespace) from [<8063deac>] (kernel_init_freeable+0x24c/0x25c)
[<8063deac>] (kernel_init_freeable) from [<804b60f8>] (kernel_init+0x8/0xe4)
[<804b60f8>] (kernel_init) from [<8000f390>] (ret_from_fork+0x14/0x24)
---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

此時表示 kernel 可以開機,但是因為還沒有跟整個系統整合在一起,所以會當機,我們按 Ctrl-a 再按 x 即可退出。


4. 放到你的「虛擬」嵌入式 ARM 平台,並使其開機

待續

沒有留言:

張貼留言