はじめに
QEMU で自分が書いたベアメタルプログラムを動かしていると、意図せず複数コアが動いてしまい頭を抱えてしまいました。
解決方法が分かったので、とりあえず書き残しておきます。
原因と解決法
原因は、 -kernel オプションに ELF ファイルを渡していたことでした。
解決法は -kernel オプションで渡すバイナリを ELF ファイルではなく kernel8.img という名前のイメージファイルにすることです。
ELF ファイルからイメージファイルへの変換は aarch64-linux-gnu-objcopy のコマンドでできます。
バイナリファイルを変換し, qemu でバイナリを動かすコマンドは以下の通りです。
なお、イメージ名を kernel8.img にする必要があるのか否かは確認できてませんが、実際の Raspberry Pi では 64 bit モードで動かす際にこのファイル名にする必要があるため、それに合わせておいた方がいいと思います。
$ ## 変換元ファイルの確認 (必須ではない)
$ file kernel.elf
kernel.elf: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, with debug_info, not stripped
$
$ ## ELF--> image 変換
$ aarch64-linux-gnu-objcopy -O binary kernel.elf kernel8.img
$
$ ## 変換後ファイルの確認 (必須ではない)
$ file kernel8.img
kernel8.img: data
$
$ ## QEMU の実行
$ ~/qemu-system-aarch64 -M raspi3 -nographic -kernel kernel8.img
(おまけ) 経緯など
以前書いた記事 で意図せず複数コアが動いてしまっているために UART の表示が崩れてしまっていたのですが、これを直したくて今回の話に至ります。
ちなみに、上記の -kernel オプションに関する公式のドキュメントは見つけられなかったものですが、Github 上にある下記の Makefile と コミットメッセージを見てこのことに気づけました。
- mikoto2000/raspberrypi_bare_metal
(おまけ2) 実際の Raspberry Pi の挙動と比べる
そもそも実際の Raspberry Pi は ELF からいきなり起動しませんし、起動時からマルチコアが動くわけでもありません。
今回のように kernel8.img ファイルを渡した時の挙動の方が実際の Raspberry Pi の挙動に近いと思います。
おそらく ELF を渡すのはユーザープログラムなどを試すときに使うのが本来の想定なのかなと思います。
おわりに
とりあえずこれで実際の Raspberry Pi と同様にシングルコアでブートするようになりました。
今後も少しずつ ARM で遊んでみたいと思います。