LeavaTailの日記

LeavaTailの日記

Linuxエンジニアを目指した技術者の備忘録

QEMUでARM64用Linuxカーネルを起動する

はじめに

Linuxカーネルは様々なアーキテクチャに対応している。
その中でもARMアーキテクチャでは、スマートフォンやゲーム機器などモバイル機器に広くから採用されている。
しかし、アーキテクチャ毎に命令セットが異なるため、ARM用にビルドされたバイナリを別のアーキテクチャで実行することはできない。

そこで、プロセッサエミュレータでもあるQEMUを用いてARM環境を構築し、ARM用にビルドされたLinuxカーネルを動かす方法を解説する。
また本記事では、以下の動作をする環境を目指す。

f:id:LeavaTail:20201213230935p:plain
カーネル起動ワークフローとメモリマップイメージ図

変更履歴

  • 2020/01/26: 記事公開
  • 2020/12/11: ブログタイトルを "ARM64用" に訂正
  • 2020/12/14: 実行環境をUbuntu18.04からUbuntu20.04に更新

環境構成

ホスト環境x86_64アーキテクチャに構築する。

f:id:LeavaTail:20201213231153p:plain
実行環境

本記事は、下記の環境とソフトウェアバージョンに基づいて説明する。

環境 パラメータ
ホスト環境 x86_64
ホストOS Ubuntu 20.04
QEMU QEMU emulator version 4.2.1
ターゲットボード virt
linux 5.4.83
Busybox 1_32_stable
Docker version 19.03.13
Docker image ubuntu:20.04

ロスコンパイル環境の構築

  1. Docker imageからコンテナを作成する

     leava@ubuntu-bionic:~$ docker run --rm --name=kbuild -h "kbuild" -v /srv:/work -it ubuntu:20.04 /bin/bash 
    
  2. ARM64用のgccコンパイラをインストールする

     root@kbuild:/# apt update
     root@kbuild:/# apt install gcc-aarch64-linux-gnu 
    
  3. コンテナ内部でLinuxカーネルをビルドするために必要なパッケージをインストールする

    root@kbuild:/# apt install build-essential bc bison flex libncurses-dev libelf-dev libssl-dev git wget
    
  4. LinuxカーネルをARM64用にセットアップする

     root@kbuild:/# wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.83.tar.xz 
     root@kbuild:/# tar xf linux-5.4.83.tar.xz -C /work/
     root@kbuild:/# cd work/linux-5.4.83
     root@kbuild:/work/linux-5.4.83# export ARCH="arm64"
     root@kbuild:/work/linux-5.4.83# export CROSS_COMPILE="aarch64-linux-gnu-"
    
  5. カーネルをビルドする。

     root@kbuild:/work/linux-5.4.83# make defconfig
     root@kbuild:/work/linux-5.4.83# make -j `getconf _NPROCESSORS_ONLN` Image dtbs modules
    

上記のコマンドによって、/srvディレクトリに Kernel ImageとDevice Tree Bolb(DTB)、Loadable Moduleが生成される。

QEMUカーネルを起動させる

  1. ARM64用QEMUをインストールする

     leava@ubuntu-bionic:~$ sudo apt install qemu-system-aarch64
    
  2. 作成したカーネルQEMUで実行する

     leava@ubuntu-bionic:~$ qemu-system-aarch64 \
         -M virt \
         -cpu cortex-a53 \
         -kernel /srv/linux-5.4.83/arch/arm64/boot/Image \
         -nographic \
         -append "console=ttyAMA0"
    

利用したオプションは下記の通り。
また、デバイスツリーを指定していないにも関わらず起動できているのは、QEMUのvirtがデバイスツリーを自動生成しているからである。

オプション 概要
-M 利用するターゲットボードを指定する。今回は汎用ボードのvirtを使用する。
-cpu CPUプロセッサを指定する。今回はARM64系のcortex-a53を使用する
-kernel カーネルイメージを指定する。先ほどビルドしたイメージを使用する。
-nographic GUIを立ち上げない
-append カーネルパラメータ。 ARMアーキテクチャのシリアルポートデバイスttyAMA0を使用する。

上記のコマンドを実行すると、kernel Panicしてしまう。

    [    0.440663] VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
    [    0.440874] Please append a correct "root=" boot option; here are the available partitions:
    [    0.441240] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
    [    0.441534] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.4.83 #1
    [    0.441648] Hardware name: linux,dummy-virt (DT)
    [    0.441849] Call trace:
    [    0.441927]  dump_backtrace+0x0/0x140
    [    0.442063]  show_stack+0x14/0x20
    [    0.442143]  dump_stack+0xb4/0x114
    [    0.442212]  panic+0x158/0x324
    [    0.442275]  mount_block_root+0x1d0/0x284
    [    0.442349]  mount_root+0x124/0x158
    [    0.442421]  prepare_namespace+0x12c/0x18c
    [    0.442489]  kernel_init_freeable+0x210/0x23c
    [    0.442553]  kernel_init+0x10/0x100
    [    0.442619]  ret_from_fork+0x10/0x1c
    [    0.442977] Kernel Offset: disabled
    [    0.443166] CPU features: 0x0002,24002004
    [    0.443266] Memory Limit: none
    [    0.443628] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---

initramfsを用意する

上記のメッセージを確認すると、「rootファイルシステムがない」からPanicしたことがわかるので、C-a xQEMUを終了する。

そこで、BusyBoxを利用して最低限起動できるrootファイルシステムを作成する。
BusyBoxは、UNIX系ユーティリティツール単一の実行ファイルにまとめたパッケージで、組込みLinuxでも利用されている。

  1. BusyBoxを取得する。

     root@kbuild:/# cd work
     root@kbuild:/work#  git clone git://git.busybox.net/busybox
     root@kbuild:/work# cd busybox
     root@kbuild:/work/busybox# git checkout remotes/origin/1_32_stable
    
  2. ビルド用の設定を修正する。最低限の動作を目指しているので、デフォルトからCONFIG_STATICを有効にするのみでよい。(CONFIG_STATICSetting->Build static binary (no shared libs)を有効にすることで設定される)

     root@kbuild:/work/busybox# make defconfig
     root@kbuild:/work/busybox# make menuconfig
    
  3. BusyBoxをビルドし、イメージを_installに生成する。

     root@kbuild:/work/busybox# make
     root@kbuild:/work/busybox# make install
    
  4. initramfsとして最低限必要なファイルやディレクトリを作成する。

     root@kbuild:/work/busybox# cd _install
     root@kbuild:/work/busybox/_install# mkdir proc
     root@kbuild:/work/busybox/_install# mkdir sys
     root@kbuild:/work/busybox/_install# mkdir dev
     root@kbuild:/work/busybox/_install# sudo mknod dev/null c 1 3
     root@kbuild:/work/busybox/_install# cat <<EOF > init
     #!/bin/sh
     mount -t proc none /proc
     mount -t sysfs none /sys
     /sbin/mdev -s
     exec  /bin/sh
     EOF
     root@kbuild:/work/busybox/_install# chmod +x init
    
  5. initramfsを生成する。

     root@kbuild:/work/busybox/_install# find . | cpio -o --format=newc > ../rootfs.img
    
  6. 作成したイメージを利用して、再度QEMUを実行する。

     leava@ubuntu-bionic:~$ qemu-system-aarch64 \
         -M virt \
         -cpu cortex-a53 \
         -kernel /srv/linux-5.4.83/arch/arm64/boot/Image \
         -initrd /srv/busybox/rootfs.img \
         -nographic \
         -append "console=ttyAMA0"
    
オプション 概要
-initrd 初期 RAM ディスク。BusyBoxで作成したイメージを使用する。

上記のコマンドを実行すると、シェルが立ち上がる。

    [    0.425197] sdhci: Copyright(c) Pierre Ossman
    [    0.425800] Synopsys Designware Multimedia Card Interface Driver
    [    0.427183] sdhci-pltfm: SDHCI platform and OF driver helper
    [    0.429410] ledtrig-cpu: registered to indicate activity on CPUs
    [    0.431969] usbcore: registered new interface driver usbhid
    [    0.432146] usbhid: USB HID core driver
    [    0.440648] NET: Registered protocol family 17
    [    0.441712] 9pnet: Installing 9P2000 support
    [    0.442051] Key type dns_resolver registered
    [    0.443071] registered taskstats version 1
    [    0.443167] Loading compiled-in X.509 certificates
    [    0.449869] input: gpio-keys as /devices/platform/gpio-keys/input/input0
    [    0.451992] rtc-pl031 9010000.pl031: setting system clock to 2020-12-13T15:31:16 UTC (1607873476)
    [    0.455420] ALSA device list:
    [    0.455514]   No soundcards found.
    [    0.457913] uart-pl011 9000000.pl011: no DMA platform data
    [    0.648255] Freeing unused kernel memory: 4992K
    [    0.648750] Run /init as init process
    /bin/sh: can't access tty; job control turned off
    / # 

おわりに

実際にARMアーキテクチャを所持していなくても、QEMUでARM用Linuxカーネルを起動させることができた。今回の記事では紹介できなかったが、gdbデバッグが容易にできたりとデバッグ時にも大変有効であるので積極的に利用していきたい。

今回はBusyBoxのデフォルト設定でrootファイルシステムも生成したが、こちらは今後の課題となってくる。

参考