土日の勉強ノート

AI、機械学習、最適化、Pythonなどについて、技術調査、技術書の理解した内容、ソフトウェア/ツール作成について書いていきます

QEMUで組み込みLinux(Buildroot+BusyBox+U-Boot)をinitramfsで起動する

前回 は、QEMU で Arm の組み込み Linux(Buildroot+BusyBox)で、U-Boot を追加しました。

今回は、initramfsを追加していきます。

それでは、やっていきます。

はじめに

「QEMUを動かす」の記事一覧です。良かったら参考にしてください。

QEMUを動かすの記事一覧
・第1回:STM32(ARM Cortex-M)をQEMUで動かす(環境構築編)
・第2回:STM32(ARM Cortex-M)をQEMUで動かす(ソースコード確認編)
・第3回:STM32(ARM Cortex-M)をQEMUで動かす(スタートアップルーチン編)
・第4回:STM32(ARM Cortex-M)をQEMUで動かす(リンカスクリプト編)
・第5回:STM32(ARM Cortex-M)のELFファイルの内容を確認する
・第6回:STM32(ARM Cortex-M)のELFファイル⇔バイナリの変換を行う
・第7回:STM32(ARM Cortex-M)のバイナリから構築したELFファイルをQEMUで動かす
・第8回:QEMUのビルドに必要なxpm(xPack Project Manager)について学ぶ
・第9回:QEMUをソースからビルドして動かす
・第10回:QEMUのソースコードを変更してSTM32の動作を変える
・第11回:QEMUに似たRenodeというOSSの組込みデバイスエミュレータを試す
・第12回:QEMUに似たRenodeでSTM32をGDBを使ってデバッグする
・第13回:QEMUに似たRenodeでSTM32をバイナリファイルで動かす
・第14回:QEMUに似たRenodeをソースからビルドする
・第15回:QEMUに似たRenodeでVSCodeを使ってデバッグする
・第16回:QEMUに似たRenodeでVSCodeを使ってRenode自体をデバッグする
・第17回:QEMUで組み込みLinux(Buildroot+BusyBox)をやってみる
・第18回:QEMUで組み込みLinux(Buildroot+BusyBox)をやってみる、の補足
・第19回:QEMUで組み込みLinux(Buildroot+BusyBox)にU-Bootを追加する
・第20回:QEMUで組み込みLinux(Buildroot+BusyBox+U-Boot)で起動する
・第21回:QEMUで組み込みLinux(Buildroot+BusyBox+U-Boot)をinitramfsで起動する ← 今回

以下は、Buildroot の Documentation のリンクです。

https://buildroot.org/downloads/manual/manual.html

環境は、VirtualBox に入れた Ubuntu 22.04 です。

Buildroot を使います。Buildroot に含まれている U-Boot と、QEMU を使います。

環境は以下の通りです。

ツール 設定値
Buildroot qemu_arm_vexpress_defconfig
U-Boot vexpress_ca9x4
QEMU vexpress-a9

今回は、BusyBox で作る initramfs を追加します。

initramfsの調査

initramfs については、kenerl のドキュメントがあります。

https://www.kernel.org/doc/Documentation/filesystems/ramfs-rootfs-initramfs.txt

「What is initramfs?」を和訳してもらいます(from ChatGPT)。

initramfsとは?

すべての2.6 Linuxカーネルには、gzip圧縮された「cpio」形式のアーカイブが含まれています。カーネルが起動すると、このアーカイブはrootfsに展開(ここで言う展開とは、gzip の解凍や cpio の展開のこと)されます。展開後、カーネルはrootfs内に「init」というファイルがあるかを確認し、あればそれをPID 1として実行します。このinitプロセスはシステムを起動し、実際のルートデバイスを見つけてマウントする役割を担います(このマウントされたものが最終的なルートファイルシステム)。もしinitプログラムが見つからなければ、カーネルは従来の方法でルートパーティションを見つけ、/sbin/initの変種を実行します。

initramfsと旧initrdの違い

旧initrdは常に別ファイルであったが、initramfsアーカイブはLinuxカーネルイメージにリンクされている。 旧initrdはgzippedファイルシステムイメージ(例えばext2)であったが、新しいinitramfsアーカイブはgzipped cpioアーカイブである。 旧initrdのプログラムはカーネルに戻ることを期待されていたが、initramfsのinitプログラムはカーネルに戻ることを期待されていない。 別のルートデバイスに切り替える際、initrdはpivot_rootを使用していたが、initramfsはrootfsであるためpivot_rootもアンマウントもできない。代わりにrootfsの内容を削除し、新しいルートで上書きマウントし、新しいinitを実行する。 このプロセスは複雑なため、klibcパッケージはこれを支援するプログラム(utils/run_init.c)を導入しました。多くのパッケージ(例:busybox)はこのコマンドを「switch_root」と名付けています。

initramfs へのデータ投入

2.6 カーネル ビルド プロセスでは、常に gzip 圧縮された cpio 形式の initramfs アーカイブが作成され、結果のカーネル バイナリにリンクされます。デフォルトでは、このアーカイブは空です (x86 では 134 バイトを消費します)。

構成オプション CONFIG_INITRAMFS_SOURCE (menuconfig の General Setup にあり、usr/Kconfig にあります) を使用すると、結果のバイナリに自動的に組み込まれる initramfs アーカイブのソースを指定できます。

このオプションは、既存の gzip 圧縮された cpio アーカイブ、アーカイブするファイルを含むディレクトリ、または次の例のようなテキスト ファイル指定を指定できます。

内容が少し古いですね。

読んだ後に、丁寧な日本語訳があることを知りました。

http://archive.linux.or.jp/JF/JFdocs/kernel-docs-2.6/filesystems/ramfs-rootfs-initramfs.txt.htmlarchive.linux.or.jp

また、以下のサイトで理解が深まります。

gihyo.jp

initramfs の役割は、カーネルに組み込むカーネルモジュールを減らして、それは initramfs に持たせるということでしょうか。

今回に限って言うと、今回使っているカーネルは、単体でルートファイルシステムを展開できているので、これから initramfs を追加しよう思いますが、「無くてもいいもの」ということになります(必要ではないけど理解するために追加してみるという位置づけ)。

さらに、initramfs の実装面を解説しているサイトもありました。

qiita.com

ここを見ると、カーネルは initramfs を含んでいて、ブートローダーに渡された initramfs があれば、そちらを採用(上書きして使う)するということのようです。

追加の initramfs(ブートローダで渡される方)は、様々なデバイス(に存在するルートファイルシステム)に対応する機能追加の役割ということでしょうか。

さらに詳しいサイトがありました。

www.gcd.org

initramfs は、/init が呼ばれるようです。initramfs に BusyBox が使われるのは特別なことではないようです。

また、他に分かったことがあったら、ここに追記しようと思います。

initramfsの構築

今回も参考にさせて頂くサイトです。

leavatail.hatenablog.com

BusyBox で作れるんですね。では、やっていきます。

BusyBox の公式サイトです。

busybox.net

参考サイトでは 1_32_stable を使われていますが、現在は 1_36_stable が最新です。

Deployment の Source Control を見ます。

Git のクローン方法、チェックアウト方法が書かれています。このチェックアウト方法は補完が効かないんですよね、なんででしょうね、ということで、ここでは補完が効く方法でチェックアウトしてます。

$ git clone git://busybox.net/busybox.git
$ cd busybox/
$ git checkout 1_36_stable
Branch '1_36_stable' set up to track remote branch '1_36_stable' from 'origin'.
Switched to a new branch '1_36_stable'

公式サイトには、明確なビルド手順のページが無く、FAQ にビルド方法が書かれています。

「How do I build Busybox with a cross-compiler?」を見ると、ARCH が使われてないのかと思いましたが、ARCH でページ内検索すると、ARCH=arm をしてるところがあったので、必要そうです。

では、デフォルトのコンフィグを適用します。

$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- defconfig

続いて、menuconfig でスタティックリンクの設定を行います。

$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig

static で検索すると、以下のように出ます。デフォルトは static ではないようです。

Symbol: STATIC [=n]
Prompt: Build static binary (no shared libs)
  Defined at Config.in:362
  Location:
    -> Settings

チェック(*)を付けて Exit すると .config が更新されます。

では、ビルドします。

$ make -j $(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
(省略)
  AR      util-linux/lib.a
  LINK    busybox_unstripped
Static linking against glibc, can't use --gc-sections
Trying libraries: m resolv rt
 Library m is needed, can't exclude it (yet)
 Library resolv is needed, can't exclude it (yet)
 Library rt is not needed, excluding it
 Library m is needed, can't exclude it (yet)
 Library resolv is needed, can't exclude it (yet)
Final link with: m resolv

最後のメッセージを見ると、エラーが出たのかと思いましたが、正常終了のようです。

$ echo $?
0

make install について、公式サイトを見ましたが、明確には書かれないようです。

FAQ の「I am observing a bug in BusyBox on an obscure platform. Help.」の手順に make install が含まれている程度でしょうか。

まぁ、やってみます。

$ make -j $(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- install
  ./_install//bin/arch -> busybox
  ./_install//bin/ash -> busybox
(省略)
--------------------------------------------------
You will probably need to make your busybox binary
setuid root to ensure all configured applets will
work properly.
--------------------------------------------------

「設定されたすべてのアプレットが適切に動作することを保証するには、busybox バイナリを setuid root にする必要がある可能性があります。」という注意書きがありました。

_install に作られたようですので、見てみます。

$ cd _install/
$ tree
.
|-- bin
|   |-- arch -> busybox
|   |-- ash -> busybox
|   |-- base32 -> busybox
|   |-- base64 -> busybox
(省略)

$ tree -d
.
|-- bin
|-- sbin
`-- usr
    |-- bin
    `-- sbin

5 directories

それらしい感じに出来てます。

init スクリプトを探します。

$ find . -name *init*
./sbin/run-init
./sbin/init
$ ll sbin/init
lrwxrwxrwx 1 daisuke daisuke 14  76 19:05 sbin/init -> ../bin/busybox*

BusyBox に /sbin/init が用意されているようです。

参考サイトでは、_install 直下に init というスクリプトを用意しているようです(つまり、/init となるはず)。

先ほど見た Qiita の参考サイトでは、カーネルは、/sbin/init、/etc/init、/bin/init、/bin/sh の順に init スクリプトを探す、と書かれていたと思います(さらに調べたところ、/init で正しいようです)。

分からなくなったので、動かしながら確認していこうと思います。

とりあえず、init スクリプトは作らず、このまま initramfs を作ります。

$ find . | cpio -o --format=newc > ../rootfs.img

BusyBox のトップに rootfs.img が生成されました。

では、動かしていきます。

これまでのおさらい

Buildroot のトップディレクトリから output/images に移動したディレクトリで実行するとします。

$ cd output/images
$ ls
rootfs.ext2  start-qemu.sh  u-boot.bin  vexpress-v2p-ca9.dtb  zImage

Buildroot+BusyBoxのみ(U-Boot無し)

実際は1行で入力してますが、見にくいので、ここでは改行を入れています。

$ ../host/bin/qemu-system-arm -M vexpress-a9 -smp 1 -m 256 \
  -kernel zImage \
  -dtb vexpress-v2p-ca9.dtb \
  -drive file=rootfs.ext2,if=sd,format=raw \
  -append "console=ttyAMA0,115200 rootwait root=/dev/mmcblk0" \
  -net nic,model=lan9118 -net user \
  -serial stdio

Buildroot+BusyBox+U-Boot

こちらも実際は1行で入力しています。U-Boot の autoboot を何らかのキーを押して止めて、=> の行を入力します。

$ ../host/bin/qemu-system-arm -M vexpress-a9 -smp 1 -m 256 \
  -kernel ../build/uboot-2024.01/u-boot \
  -device loader,file=zImage,addr=0x62000000 \
  -device loader,file=vexpress-v2p-ca9.dtb,addr=0x63000000 \
  -drive file=rootfs.ext2,if=sd,format=raw \
  -net nic,model=lan9118 -net user \
  -serial stdio
=> setenv bootargs console=ttyAMA0,115200 rootwait root=/dev/mmcblk0
=> bootz  0x62000000 - 0x63000000

Buildroot+BusyBoxのみ(U-Boot無し)にinitramfsを追加する

Buildroot のトップディレクトリ/output/images に、initramfs のイメージの rootfs.img をコピーしておきます。

まずは、U-Boot 無しの環境で動かします。

-initrd rootfs.img を追加しただけです。

$ ../host/bin/qemu-system-arm -M vexpress-a9 -smp 1 -m 256 \
  -kernel zImage \
  -dtb vexpress-v2p-ca9.dtb \
  -initrd rootfs.img \
  -drive file=rootfs.ext2,if=sd,format=raw \
  -append "console=ttyAMA0,115200 rootwait root=/dev/mmcblk0" \
  -net nic,model=lan9118 -net user \
  -serial stdio

普通に Buildroot が起動してしまいました。起動ログを確認してみます。

initramfs を追加する前と、追加した後の差分は以下です。

+Unpacking initramfs...
 hw perfevents: enabled with armv7_cortex_a9 PMU driver, 7 counters available
 workingset: timestamp_bits=30 max_order=16 bucket_order=0
 squashfs: version 4.0 (2009/01/31) Phillip Lougher
 jffs2: version 2.2. (NAND) c 2001-2006 Red Hat, Inc.
 9p: Installing v9fs 9p2000 file system support
 io scheduler mq-deadline registered
 io scheduler kyber registered
 OF: graph: no port node found in /bus@40000000/motherboard-bus@40000000/iofpga@7,00000000/i2c@16000/dvi-transmitter@60
 sii902x 0-0060: supply iovcc not found, using dummy regulator
 sii902x 0-0060: supply cvcc12 not found, using dummy regulator
+Freeing initrd memory: 2132K

initramfs が動いてるようなログは出ています。

参考サイトをよく見ると、-append に root=/dev/mmcblk0 の指定が削除されていました。これが想定した動きと異なる原因のようです。

これを削除して、もう一度やってみます。

$ ../host/bin/qemu-system-arm -M vexpress-a9 -smp 1 -m 256 \
  -kernel zImage \
  -dtb vexpress-v2p-ca9.dtb \
  -initrd rootfs.img \
  -drive file=rootfs.ext2,if=sd,format=raw \
  -append "console=ttyAMA0,115200 rootwait" \
  -net nic,model=lan9118 -net user \
  -serial stdio
(省略)
input: ImExPS/2 Generic Explorer Mouse as /devices/platform/bus@40000000/bus@40000000:motherboard-bus@40000000/bus@40000000:motherboard-bus@40000000:iofpga@7,00000000/10007000.kmi/serio1/input/input2
drm-clcd-pl111 10020000.clcd: DVI muxed to daughterboard 1 (core tile) CLCD
drm-clcd-pl111 10020000.clcd: initializing Versatile Express PL111
Waiting for root device ...

起動せず、止まってしまいました。initramfs に /init のスクリプトが無いためのようです。

では、initramfs に最低限の /init を準備して、動作を確認してみます。

BusyBox の _install 直下に init というファイルを作ります。

「Hello, initramfs」と表示して、1秒ごとに日時を表示し続けるだけです。

#!/bin/sh

echo Hello, initramfs

while true
do
  sleep 1
  date
done

では、先ほどと同じように、rootfs.img として固めます。

$ find . | cpio -o --format=newc > ../rootfs.img

Buildroot の output/images に移動します。コピーは面倒なので、シンボリックリンクを貼っておきます。

$ cd buildroot-2024.02.3/output/images/
$ ln -s ../../../busybox/rootfs.img
$ ../host/bin/qemu-system-arm -M vexpress-a9
-smp 1 -m 256 -kernel zImage -dtb vexpress-v2p-ca9.dtb -initrd rootfs.img -drive file=rootfs.ext2,if=sd,format=raw -append
"console=ttyAMA0,115200 rootwait" -net nic,model=lan9118 -net user -serial stdio
(省略)
Freeing unused kernel image (initmem) memory: 1024K
Run /init as init process
Failed to execute /init (error -13)
Run /sbin/init as init process
can't run '/etc/init.d/rcS': No such file or directory
can't open /dev/tty2: No such file or directory
can't open /dev/tty4: No such file or directory
can't open /dev/tty3: No such file or directory
can't open /dev/tty2: No such file or directory
can't open /dev/tty4: No such file or directory
can't open /dev/tty3: No such file or directory
can't open /dev/tty2: No such file or directory

うまくいきませんでした。「Hello, initramfs」が表示されませんでした。

/init は実行されたようなのですが、うーん、少し調べてみます。

作成した /init スクリプトに実行権限を付けてなかったことが原因でした。

$ chmod +x ./init
$ find . | cpio -o --format=newc > ../rootfs.img
$ cd -
$ ../host/bin/qemu-system-arm -M vexpress-a9 -smp 1 -m 256 -kernel zImage -dtb vexpress-v2p-ca9.dtb -initrd rootfs.img -drive file=rootfs.ext2,if=sd,format=raw -append "console=ttyAMA0,115200 rootwait" -net nic,model=lan9118 -net user -serial stdio
(省略)
Freeing unused kernel image (initmem) memory: 1024K
Run /init as init process
Hello, initramfs
Sun Jul  7 11:00:38 UTC 2024
Sun Jul  7 11:00:39 UTC 2024
Sun Jul  7 11:00:40 UTC 2024
Sun Jul  7 11:00:41 UTC 2024
(省略)

想定した通りに動いています。

次は、カーネルのブートパラメータの rdinit で、initramfs の /init 以外を実行するようにしてみます。

具体的には、-append に「rdinit=/bin/sh」を追加して、/bin/sh(シェル)を起動してみます。

$ ../host/bin/qemu-system-arm -M vexpress-a9 -smp 1 -m 256 \
  -kernel zImage \
  -dtb vexpress-v2p-ca9.dtb \
  -initrd rootfs.img \
  -drive file=rootfs.ext2,if=sd,format=raw \
  -append "console=ttyAMA0,115200 rootwait rdinit=/bin/sh" \
  -net nic,model=lan9118 -net user \
  -serial stdio
(省略)
Freeing unused kernel image (initmem) memory: 1024K
Run /bin/sh as init process
/bin/sh: can't access tty; job control turned off
~ # ls
bin      dev      init     linuxrc  root     sbin     usr
# ls /dev/
console

うまくいきました。

次は、Buildroot(ルートファイルシステム)を起動してみます。

参考サイトによると、/dev、/proc、/sys を作り、Buildroot をマウントし、/dev、/proc、/sys を Buildroot に移動し、switch_root を実行しているようです。

最小限から試してみます。本来なら、/dev/mmcblk0 を適当なディレクトリにマウントし、そこに switch_root する必要があるようですが、もしかしたら出来るかな、と思って試します。

initramfs の /init を以下のようにしました。

#!/bin/sh

echo Hello, initramfs

exec switch_root -c /dev/console /dev/mmcblk0 /sbin/init

-append の「rdinit=/bin/sh」は削除します。

$ find . | cpio -o --format=newc > ../rootfs.img
$ cd -
$ ../host/bin/qemu-system-arm -M vexpress-a9 -smp 1 -m 256 \
  -kernel zImage \
  -dtb vexpress-v2p-ca9.dtb \
  -initrd rootfs.img \
  -drive file=rootfs.ext2,if=sd,format=raw \
  -append "console=ttyAMA0,115200 rootwait" \
  -net nic,model=lan9118 -net user \
  -serial stdio
(省略)
Freeing unused kernel image (initmem) memory: 1024K
Run /init as init process
Hello, initramfs
switch_root: can't change directory to '/dev/mmcblk0': No such file or directory
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100
CPU: 0 PID: 1 Comm: switch_root Not tainted 6.1.44 #1
Hardware name: ARM-Versatile Express
 unwind_backtrace from show_stack+0x10/0x14
 show_stack from dump_stack_lvl+0x40/0x4c
 dump_stack_lvl from panic+0x108/0x318
 panic from make_task_dead+0x0/0x17c
---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100 ]---

さすがにダメでした。では、initramfs の /init を以下のように書き換えます。

#!/bin/sh

echo Hello, initramfs

mount /dev/mmcblk0 /mnt/newroot
exec switch_root -c /dev/console /mnt/newroot /sbin/init

BusyBox の _install に /mnt/newroot というディレクトリを作っておきます。

$ mkdir -p mnt/newroot
$ find . | cpio -o --format=newc > ../rootfs.img
$ cd -
$ ../host/bin/qemu-system-arm -M vexpress-a9 -smp 1 -m 256 -kernel zImage -dtb vexpress-v2p-ca9.dtb -initrd rootfs.img -drive file=rootfs.ext2,if=sd,format=raw -append "console=ttyAMA0,115200 rootwait" -net nic,model=lan9118 -net user -serial stdio
(省略)
Freeing unused kernel image (initmem) memory: 1024K
Run /init as init process
Hello, initramfs
mount: mounting /dev/mmcblk0 on /mnt/newroot failed: No such file or directory
BusyBox v1.36.1 (2024-07-06 18:36:47 JST) multi-call binary.

Usage: switch_root [-c CONSOLE_DEV] NEW_ROOT NEW_INIT [ARGS]

Free initramfs and switch to another root fs:
chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,
execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.

        -c DEV  Reopen stdio to DEV after switch
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100
CPU: 0 PID: 1 Comm: switch_root Not tainted 6.1.44 #1
Hardware name: ARM-Versatile Express
 unwind_backtrace from show_stack+0x10/0x14
 show_stack from dump_stack_lvl+0x40/0x4c
 dump_stack_lvl from panic+0x108/0x318
 panic from make_task_dead+0x0/0x17c
---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100 ]---

ダメでした。やはり、/dev/mmcblk0 が見つからないということでしょうか。

シェルに戻して、手動でやってみます。

$ ../host/bin/qemu-system-arm -M vexpress-a9 -smp 1 -m 256 \
  -kernel zImage \
  -dtb vexpress-v2p-ca9.dtb \
  -initrd rootfs.img \
  -drive file=rootfs.ext2,if=sd,format=raw \
  -append "console=ttyAMA0,115200 rootwait rdinit=/bin/sh" \
  -net nic,model=lan9118 -net user \
  -serial stdio
(省略)
Freeing unused kernel image (initmem) memory: 1024K
Run /bin/sh as init process
/bin/sh: can't access tty; job control turned off
~ # ls
bin      dev      init     linuxrc  mnt      root     sbin     usr
~ # mount -t devtmpfs devtempfs /dev
~ # ls /dev
console          ptype            tty34            tty8
cpu_dma_latency  ptypf            tty35            tty9
full             random           tty36            ttyAMA0
gpiochip0        rtc0             tty37            ttyAMA1
gpiochip1        snd              tty38            ttyAMA2
gpiochip2        tty              tty39            ttyAMA3
gpiochip3        tty0             tty4             ttyp0
hwrng            tty1             tty40            ttyp1
input            tty10            tty41            ttyp2
kmsg             tty11            tty42            ttyp3
mem              tty12            tty43            ttyp4
mmcblk0          tty13            tty44            ttyp5
mtd0             tty14            tty45            ttyp6
mtd0ro           tty15            tty46            ttyp7
mtd1             tty16            tty47            ttyp8
mtd1ro           tty17            tty48            ttyp9
mtdblock0        tty18            tty49            ttypa
mtdblock1        tty19            tty5             ttypb
null             tty2             tty50            ttypc
ptmx             tty20            tty51            ttypd
ptyp0            tty21            tty52            ttype
ptyp1            tty22            tty53            ttypf
ptyp2            tty23            tty54            ubi_ctrl
ptyp3            tty24            tty55            urandom
ptyp4            tty25            tty56            usbmon0
ptyp5            tty26            tty57            vcs
ptyp6            tty27            tty58            vcs1
ptyp7            tty28            tty59            vcsa
ptyp8            tty29            tty6             vcsa1
ptyp9            tty3             tty60            vcsu
ptypa            tty30            tty61            vcsu1
ptypb            tty31            tty62            zero
ptypc            tty32            tty63
ptypd            tty33            tty7
~ # mount /dev/mmcblk0 /mnt/newroot
mount: mounting /dev/mmcblk0 on /mnt/newroot failed: No such file or directory
~ # ls /dev/mmcblk0
/dev/mmcblk0
~ # ls /mnt/newroot
~ # mount -t ext4 /dev/mmcblk0 /mnt/newroot
EXT4-fs (mmcblk0): warning: mounting unchecked fs, running e2fsck is recommended
EXT4-fs (mmcblk0): mounted filesystem without journal. Quota mode: disabled.
~ # ls /mnt/newroot/
bin         lib         lost+found  opt         run         tmp
dev         lib32       media       proc        sbin        usr
etc         linuxrc     mnt         root        sys         var
~ # umount /mnt/newroot/
EXT4-fs (mmcblk0): unmounting filesystem.
~ # ls /mnt/newroot/
~ # mount -t ext2 /dev/mmcblk0 /mnt/newroot
EXT2-fs (mmcblk0): warning: mounting unchecked fs, running e2fsck is recommended
ext2 filesystem being mounted at /mnt/newroot supports timestamps until 2038 (0x7fffffff)
~ # ls /mnt/newroot/
bin         lib         lost+found  opt         run         tmp
dev         lib32       media       proc        sbin        usr
etc         linuxrc     mnt         root        sys         var

なぜマウントできないのかな、と思ったら、まず、/dev を devtmpfs としてマウントが必要だったようです。

また、ルートファイルシステムのマウントには、-t の指定が必要なようです。

あと、ext2 なのか、ext4 なのかは、これだけでは判断できません。まずは、ext2 でやってみたいと思います。

では、initramfs の /init を書き換えます。

#!/bin/sh

echo Hello, initramfs

mount -t devtmpfs devtempfs /dev
mount -t ext2 /dev/mmcblk0 /mnt/newroot
exec switch_root -c /dev/console /mnt/newroot /sbin/init

では、やってみます。

$ find . | cpio -o --format=newc > ../rootfs.img
$ cd -
$ ../host/bin/qemu-system-arm -M vexpress-a9
-smp 1 -m 256 -kernel zImage -dtb vexpress-v2p-ca9.dtb -initrd rootfs.img -drive file=rootfs.ext2,if=sd,format=raw -append
"console=ttyAMA0,115200 rootwait" -net nic,model=lan9118 -net user -serial stdio
(省略)
Freeing unused kernel image (initmem) memory: 1024K
Run /init as init process
Hello, initramfs
EXT2-fs (mmcblk0): warning: mounting unchecked fs, running e2fsck is recommended
ext2 filesystem being mounted at /mnt/newroot supports timestamps until 2038 (0x7fffffff)
switch_root: can't open '/dev/console': No such file or directory
can't open /dev/null: No such file or directory
can't open /dev/null: No such file or directory
can't open /dev/null: No such file or directory
can't open /dev/null: No such file or directory
can't open /dev/ttyAMA0: No such file or directory
can't open /dev/ttyAMA0: No such file or directory
can't open /dev/ttyAMA0: No such file or directory
can't open /dev/ttyAMA0: No such file or directory
can't open /dev/ttyAMA0: No such file or directory

/dev を新しいルートに移動してないことが原因のようです。

initramfs の /init に追加します。

#!/bin/sh

echo Hello, initramfs

mount -t devtmpfs devtempfs /dev
mount -t ext2 /dev/mmcblk0 /mnt/newroot
mount -n -o move /dev /mnt/newroot/dev
exec switch_root -c /dev/console /mnt/newroot /sbin/init

では、やってみます。

$ find . | cpio -o --format=newc > ../rootfs.img
$ cd -
$ ../host/bin/qemu-system-arm -M vexpress-a9
-smp 1 -m 256 -kernel zImage -dtb vexpress-v2p-ca9.dtb -initrd rootfs.img -drive file=rootfs.ext2,if=sd,format=raw -append
"console=ttyAMA0,115200 rootwait" -net nic,model=lan9118 -net user -serial stdio
(省略)
Freeing unused kernel image (initmem) memory: 1024K
Run /init as init process
Hello, initramfs
EXT2-fs (mmcblk0): warning: mounting unchecked fs, running e2fsck is recommended
ext2 filesystem being mounted at /mnt/newroot supports timestamps until 2038 (0x7fffffff)
(省略)
Welcome to Buildroot
buildroot login: root
# ls -aF /proc/
./             25/            45/            device-tree@   mtd
../            26/            46/            devices        net@
1/             27/            47/            diskstats      pagetypeinfo
10/            28/            5/             driver/        partitions
11/            29/            51/            execdomains    self@
117/           3/             52/            fb             slabinfo
119/           30/            6/             filesystems    softirqs
12/            31/            7/             fs/            stat
124/           32/            73/            interrupts     swaps
13/            33/            77/            iomem          sys/
14/            34/            8/             ioports        sysrq-trigger
15/            35/            9/             irq/           sysvipc/
16/            36/            asound/        kallsyms       thread-self@
17/            37/            buddyinfo      kmsg           timer_list
18/            38/            bus/           kpagecount     tty/
19/            39/            cgroups        kpageflags     uptime
2/             4/             cmdline        loadavg        version
20/            40/            config.gz      locks          vmallocinfo
21/            41/            consoles       meminfo        vmstat
22/            42/            cpu/           misc           zoneinfo
23/            43/            cpuinfo        modules
24/            44/            crypto         mounts@
# ls -aF /sys/
./        block/    class/    devices/  fs/       module/
../       bus/      dev/      firmware/ kernel/   power/

無事に、Buildroot が起動しました。特にエラーは見当たりません。

initramfs の /init に、/proc と /sys を追加せずにやりましたが、特に問題なさそうです。

initramfs で、/proc と /sys を使う場合は必要ということだと思います。

最小限の構成で失敗しながら追加していくことで、理解が深まりました。

おわりに

今回は、initramfs についての調査と、実際に BusyBox を使って initramfs を作って動作させてみました。

BusyBox のロゴを探してみたところ、段ボール箱の画像のようです。ありがたく使わせて頂きます。

最後になりましたが、エンジニアグループのランキングに参加中です。

気楽にポチッとよろしくお願いいたします🙇

今回は以上です!

最後までお読みいただき、ありがとうございました。