qemu と BusyBox で Arm 用 Linux を起動させる
Arm の 32ビットです。
登場人物
今回の登場人物は 4 人です。
- qemu (エミュレータ)
- Linux (カーネル)
- BusyBox (ユーザーランド)
- initramfs (rootfs)
qemu を用いてカーネルを起動させます。カーネルは rootfs をマウントし、起動します。ユーザーはログインするとユーザーランドのコマンドが使えるようになります。
カーネルとユーザーランドと initramfs の違いについては手前味噌ですが記事に書きました。
- Linux の「ユーザーランド」という言葉をよく聞くけどちゃんと理解してなかったのでメモ
- 「Linux を(わりと)シンプルな構成でビルドして Qemu で起動する」では何をやっているのか。そして initramfs とは何か
環境
ubuntu 20.04 LTS
開発環境をインストールする
Linux や BusyBox をビルドするために必要なコマンドをインストールします。
% sudo apt-get install gcc-arm-linux-gnueabi flex bison libncurses-dev libssl-dev qemu-system-arm
Linux をビルドする
github から取得します。ソースコードはこちら https://github.com/torvalds/linux
% git clone git@github.com:torvalds/linux.git
参考にしたウェブサイト(記事末尾参照)がよく versatile_defconfig を使っているので versatile_defconfig にします。たぶん qemu でエミュレートできればなんでもいいかと。
% make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- versatile_defconfig
% make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
BusyBox をビルドする
ユーザーランドとして使う BusyBox です。 ソースコードはこちら https://www.busybox.net/
% git clone https://git.busybox.net/busybox
% cd busybox
% make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- defconfig
スタティックリンクさせるために設定を変更します。
% make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
メニューをたどり、 Build static binary
をチェックします。
Settings
Build Options
[*] Build static binary (no shared libs)
ビルドします。
% make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
インストールします。インストール先はデフォルトでは busybox/_install です。
% make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- install
インストール先にはお馴染みのディレクトリやファイルが設置されます。
% ls busybox/_install/
bin linuxrc sbin sys usr
initramfs を作る
BusyBox をビルドしたバイナリを rootfs として使います。
先ほど作った busybox/_install に移動して作業します。
% cd busybox/_install
% find . | cpio -o -H newc | gzip > ../rootfs.img
qemu を実行する (1)
じつはファイルやディレクトリが不足していますが、ひとまず現状で qemu を実行します。ファイルの PATH は適当に読み替えてください。
なお qemu から抜けるときは ctrl+a x
で抜けられます。
% qemu-system-arm \
-M versatilepb \
-m 128M \
-kernel ./linux/arch/arm/boot/zImage \
-dtb ./linux/arch/arm/boot/dts/versatile-pb.dtb \
-initrd busybox/rootfs.img \
-append "rdinit=/sbin/init" \
-nographic
起動するとプロンプトが表示されます。 ls は実行できますが、 ps や mount は実行できません。必要なディレクトリが無いためです。
/ # ls
bin dev linuxrc root sbin usr
/ # ps
PID USER TIME COMMAND
ps: can't open '/proc': No such file or directory
/ # mount
mount: no /proc/mounts
ディレクトリが不足しているの作ります。(qemu は実行したままです)
/ # mkdir /proc
/ # mount -t proc none /proc
/ # mkdir /sys
/ # mount -t sysfs none /sys
/ # mdev -s
ps や mount が実行できるようになりました。
/ # ps
PID USER TIME COMMAND
1 0 0:00 /bin/sh
2 0 0:00 [kthreadd]
3 0 0:00 [kworker/0:0-eve]
4 0 0:00 [kworker/0:0H]
5 0 0:00 [kworker/u2:0-ev]
6 0 0:00 [mm_percpu_wq]
7 0 0:00 [ksoftirqd/0]
8 0 0:00 [netns]
9 0 0:00 [inet_frag_wq]
10 0 0:00 [oom_reaper]
11 0 0:00 [writeback]
12 0 0:00 [kcompactd0]
13 0 0:00 [kblockd]
14 0 0:00 [rpciod]
15 0 0:00 [kworker/0:1-eve]
16 0 0:00 [kworker/u3:0]
17 0 0:00 [xprtiod]
18 0 0:00 [kswapd0]
19 0 0:00 [kworker/u2:1]
20 0 0:00 [nfsiod]
21 0 0:00 [card0-crtc0]
22 0 0:00 [kworker/0:2-pm]
23 0 0:00 [kworker/0:3-eve]
24 0 0:00 [kworker/0:4-eve]
26 0 0:00 [irq/59-mmci-pl1]
27 0 0:00 [irq/49-mmci-pl1]
38 0 0:00 ps
/ # mount
none on / type rootfs (rw)
none on /proc type proc (rw,relatime)
none on /sys type sysfs (rw,relatime)
qemu を実行する (2)
先ほどは qemu で実行したあとにディレクトリを作りましたが、事前に作っておくと楽です。
% cd busybox/_install/
% mkdir proc sys dev run etc etc/init.d
% cd _install/dev
% sudo mknod -m 600 console c 5 1
% sudo mknod -m 666 null c 1 3
% sudo mknod -m 666 tty c 5 0
% sudo mknod ram b 1 0
以下の内容で etc/init.d/rcS を作成します。 etc/init.d/rcS は /sbin/init (Linux が起動したときに最初に実行されるプログラム) から実行されるスクリプトです。
#!/bin/sh
mount -v --bind /dev /dev
mount -v --bind /dev/pts /dev/pts
mount -vt proc proc /proc
mount -vt sysfs sysfs /sys
mount -vt tmpfs tmpfs /run
/sbin/mdev -s
+x
しておきます。
% chmod +x etc/init.d/rcS
rootfs.img を作り直します。
% cd busybox/_install
% find . | cpio -o -H newc | gzip > ../rootfs.img
qemu を実行します。
% qemu-system-arm \
-M versatilepb \
-m 128M \
-kernel ./linux/arch/arm/boot/zImage \
-dtb ./linux/arch/arm/boot/dts/versatile-pb.dtb \
-initrd busybox/rootfs.img \
-append "rdinit=/sbin/init root=/dev/ram" \
-nographic
ls はこの通り。
/ # ls
bin etc proc run sys
dev linuxrc root sbin usr
起動してすぐに ps が実行できます。
/ # ps
PID USER TIME COMMAND
1 0 0:00 init
2 0 0:00 [kthreadd]
3 0 0:00 [kworker/0:0-eve]
4 0 0:00 [kworker/0:0H]
5 0 0:00 [kworker/u2:0-ev]
6 0 0:00 [mm_percpu_wq]
7 0 0:00 [ksoftirqd/0]
8 0 0:00 [netns]
9 0 0:00 [inet_frag_wq]
10 0 0:00 [oom_reaper]
11 0 0:00 [writeback]
12 0 0:00 [kcompactd0]
13 0 0:00 [kblockd]
14 0 0:00 [rpciod]
15 0 0:00 [kworker/0:1-eve]
16 0 0:00 [kworker/u3:0]
17 0 0:00 [xprtiod]
18 0 0:00 [kswapd0]
19 0 0:00 [kworker/u2:1]
20 0 0:00 [nfsiod]
21 0 0:00 [card0-crtc0]
22 0 0:00 [kworker/0:2-pm]
23 0 0:00 [kworker/0:3-eve]
24 0 0:00 [kworker/0:4-eve]
26 0 0:00 [irq/59-mmci-pl1]
27 0 0:00 [irq/49-mmci-pl1]
35 0 0:00 -/bin/sh
36 0 0:00 init
37 0 0:00 init
38 0 0:00 init
40 0 0:00 ps
mount も見れます。
/ # mount
none on / type rootfs (rw)
none on /dev type rootfs (rw)
proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
試行錯誤した記録
/bin/sh: can't access tty; job control turned off
当初カーネルパラメータに rdinit=/bin/sh
を指定したんですが、 Linux は起動するものの can't access tty
と言われます。 用途によると思いますが rdinit=/bin/init
が正しいようです。
% qemu-system-arm \
-M versatilepb \
-m 128M \
-kernel ./linux/arch/arm/boot/zImage \
-dtb ./linux/arch/arm/boot/dts/versatile-pb.dtb \
-initrd busybox/rootfs.img \
-append "rdinit=/bin/sh" \
-nographic
/bin/sh: can't access tty; job control turned off
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
initramfs を使う場合 /dev/ram が必要 且つ qemu で起動するときカーネルパラメータに root=/dev/ram
が必要ぽいです。そうしないとパニックします。
% qemu-system-arm \
-M versatilepb \
-m 128M \
-kernel ./linux/arch/arm/boot/zImage \
-dtb ./linux/arch/arm/boot/dts/versatile-pb.dtb \
-initrd busybox/rootfs.img \
-append "rdinit=/sbin/init" \
-nographic
略
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:
0100 4096 ram0
(driver?)
0101 4096 ram1
(driver?)
0102 4096 ram2
(driver?)
0103 4096 ram3
(driver?)
0104 4096 ram4
(driver?)
0105 4096 ram5
(driver?)
0106 4096 ram6
(driver?)
0107 4096 ram7
(driver?)
0108 4096 ram8
(driver?)
random: fast init done
0109 4096 ram9
(driver?)
010a 4096 ram10
(driver?)
010b 4096 ram11
(driver?)
010c 4096 ram12
(driver?)
010d 4096 ram13
(driver?)
010e 4096 ram14
(driver?)
010f 4096 ram15
(driver?)
1f00 65536 mtdblock0
(driver?)
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
CPU: 0 PID: 1 Comm: swapper Not tainted 5.14.0-rc5+ #1
Hardware name: ARM-Versatile (Device Tree Support)
[<c001b0f8>] (unwind_backtrace) from [<c0018080>] (show_stack+0x10/0x14)
[<c0018080>] (show_stack) from [<c0477dfc>] (panic+0xe8/0x2e4)
[<c0477dfc>] (panic) from [<c05c55bc>] (mount_block_root+0x1d4/0x274)
[<c05c55bc>] (mount_block_root) from [<c05c5740>] (mount_root+0xe4/0x12c)
[<c05c5740>] (mount_root) from [<c05c58b4>] (prepare_namespace+0x12c/0x180)
[<c05c58b4>] (prepare_namespace) from [<c047bf3c>] (kernel_init+0x10/0x100)
[<c047bf3c>] (kernel_init) from [<c0008548>] (ret_from_fork+0x14/0x2c)
Exception stack(0xc082bfb0 to 0xc082bff8)
bfa0: 00000000 00000000 00000000 00000000
bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bfe0: 00000000 00000000 00000000 00000000 00000013 00000000
---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---
参考
- QEMUでARM64用Linuxカーネルを起動する - LeavaTailの日記
- QEMUでARM用Linuxカーネルを起動する - LeavaTailの日記
- Busybox for ARM on QEMU | Freedom Embedded
- Linux From Scratch - Version 10.1 - 第7章 chroot への移行と一時的ツールの追加ビルド - 7.3. 仮想カーネルファイルシステムの準備 busybox/_install/dev に作成するデバイスの参考です
- Linux From Scratch - Version 6.1 Chapter - 6. Installing Basic System Software - 6.8. Populating /dev version 6.1 なので古いです
Discussion