編譯aarch64 Linux內核並基於qemu運行

核心流程

首先,本文主要講述如何編譯Linux內核並在qemu虛擬機上運行。這裡針對的架構是aarch64

本文的實驗平台是Ubuntu 16.04

為了達成目標,我們需要有qemubuildrootlinux安裝包或源碼。

首先確保qemu-system-aarch64命令可用,可以通過在命令行執行qemu-system-aarch64 --version判斷。

下載buildroot源碼,鏈接見下文平台工具。假設其絕對路徑保存在變數BUILD_ROOT_PATH中。執行以下命令

cd $BUILD_ROOT_PATH
make menuconfig

在彈出的配置介面中,設置Target option ---> Target ArchitectureAArch64 (little endian);設置Toolchain ---> Toolchain typeExternal toolchain,這時我們可以看到Toolchain ---> Toolchain的值為linaro AArch64 xxxx.xx;設置System configuration ---> Enable root login with password開啟,並設置System configuration ---> Root passwordxxxx(任意的你喜歡的密碼);設置System configuration ---> Run a getty (login prompt) after boot ---> TTY port的值為ttyAMA0(這一條非常重要,不然虛擬機可能啟動不了);設置Target packages ---> Show packages that are also provided by busybox開啟;設置Target packages ---> Debugging, profiling and benchmark ---> strace開啟;設置Filesystem images ---> cpio the root filesystem開啟。

在配置完成之後,執行

make

注意:這裡可能需要配置wget代理。

生成的rootfs.cpio在目錄$BUILD_ROOT_PATH/output/images下面。

下載Linux內核源碼,鏈接見下文平台工具。假設其絕對路徑保存在變數LINUX_KERNEL_PATH中。執行以下命令

cd $LINUX_KERNEL_PATH
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make defconfig
vim .config

在打開的文件中,找到CONFIG_CMDLINE這個配置選項,設置其值為console=ttyAMA0;找到CONFIG_INITRAMFS_SOURCE這個配置選項,設置其值為$BUILD_ROOT_PATH/output/images/rootfs.cpio(注意,這裡要自己展開變數BUILD_ROOT_PATH);設置CONFIG_DEBUG_INFO配置項為y

配置結束後,執行以下命令

ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j16

執行結束後,我們可以看到生成了一些文件,其中包括$LINUX_KERNEL_PATH/vmlinux$LINUX_KERNEL_PATH/arch/arm64/boot/Image

在當前目錄創建一個shell腳本,避免重複輸入指令。其文件名為start.sh,其內容為

qemu-system-aarch64 -machine virt -cpu cortex-a57 \
	-machine type=virt -nographic -smp 1 \
	-m 2048 \
	-kernel ./arch/arm64/boot/Image \
	--apend "console=ttyAMA0" \
	$1 $2

執行./startup.sh,這是可以用qemu啟動linux內核。在進入命令行之前,需要輸入buildroot login: 的值,其值為root,然後需要輸入Password: ,這是前文構建rootfs.cpio的時候,配置項System configuration ---> Root password的值。然後就可以進入命令行執行以下常用命令了。(注意,需要先cd /

如果要退出qemu,可以先按Ctrl + A,然後按X

為了在主機和qemu虛擬機之間共享文件,我們可以創建一個目錄,其絕對路徑為SHARED_FILE_PATH。然後執行以下命令

cd $BUILD_ROOT_PATH
vim .config

修改BR2_ROOTFS_OVERLAY配置項的值為$SHARED_FILE_PATH(注意,自行展開變數)。

保存後執行以下命令重新創建 rootfs.cpio

rm $BUILD_ROOT_PATH/output/images/rootfs.*
make

然後需要重新編譯內核,即

cd $LINUX_KERNEL_PATH
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j16

依然通過./start.sh啟動虛擬機跑linux內核,進入命令行後,執行cd /; ls,我們可以看到$SHARED_FILE_PATH目錄下的文件。

重新執行./start.sh -s -S進入qemu的調試狀態,然後開一個新的shell,輸入命令

cd $LINUX_KERNEL_PATH
aarch64-linux-gnu-gdb ./vmlinux -ex "target remote :1234"

現在,可以像以往一樣使用gdb進行調試了……

下面介紹如何編譯一個linux內核模組。

cd $SHARED_FILE_PATH
vim hello.c

hello.c的內容為

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>

int helloinit(void)
{
	printk(KERN_DEBUG "hello world !!\n");
	return 0;
}

void helloexit(void)
{
	printk(KERN_DEBUG "goodbye world !!\n");
}

module_init(helloinit);
module_exit(helloexit);

MODULE_LICENSE("GPL");

然後在當前目錄創建Makefile內容如下

ifneq (${KERNELRELEASE},)
obj-m := hello.o
else
KERNEL_SOURCE := $LINUX_KERNEL_PATH # 注意自行展開變數LINUX_KERNEL_PATH
PWD := $(shell pwd)

default:
	${MAKE} -C ${KERNEL_SOURCE} M=${PWD} modules

clean:
	${MAKE} -C ${KERNEL_SOURCE} M=${PWD} clean
endif

然後執行交叉編譯命令

ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make

OK,重新編譯rootfs.cpiolinux內核,然後運行qemu。在進入linux命令行後,執行以下命令

cd /
insmod hello.ko
dmesg | tail      # 可以看到列印資訊 hello world !!
rmmod hello.ko
dmesg | tail      # 可以看到列印資訊 goodbye world !!

相關經驗

Linux Kernel

  • 使用qemu並開啟gdb server功能之後,在gdb窗口輸入b start_kernel,進入最初的內核初始化函數。

平台工具

參考