👻

qemu と BusyBox で Arm 用 Linux を起動させる

2021/08/12に公開

Arm の 32ビットです。

登場人物

今回の登場人物は 4 人です。

  • qemu (エミュレータ)
  • Linux (カーネル)
  • BusyBox (ユーザーランド)
  • initramfs (rootfs)

qemu を用いてカーネルを起動させます。カーネルは rootfs をマウントし、起動します。ユーザーはログインするとユーザーランドのコマンドが使えるようになります。

カーネルとユーザーランドと 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) ]---

参考

Discussion