はじめに
突然ですが、
- ARM で動く (Type-1) VMM を作りたい
- –> ARM で OS なしで自分のコードが動かしたい (ベアメタルプログラミング)
- –> デバッグなどのために QEMU で OS なしに自分のコードを動かしたい
- –> QEMU で Raspberry Pi エミュレーション環境の構築と動作確認をしたい <– 今ここ
という経緯があり QEMU で Raspberry Pi 3 用のイメージを動かしたくなったのでやってみました。
参考ページ
- QEMUでRaspberry Pi 3のUARTをベアメタルで動かす by @eggman: https://qiita.com/eggman/items/045bf5525fcf78209b79
今回も、ほぼ参考ページの通りやっただけの記事になっております..
環境
- OS (Distribution): Ubuntu 18.04.4 LTS
- Linux kernel: Linux uefi-ubuntu 4.15.0-96-generic
QEMU の ビルド
Ubuntu 18.04.4 の apt でインストール可能な QEMU は Raspberry Pi 3 のエミュレーションに未対応のため、自分で QEMU をビルドします。
ビルドの準備
まずは、QEMU のビルドに必要なパッケージを apt でインストールします。
apt build-dep <パッケージ名>
で <パッケージ名>
に該当するもののビルドに必要なパッケージをまとめてインストールできるようです (参考にしたページを見て知りました..) 。
ただ、Ubuntu での apt のデフォルト設定では、ビルド時の依存関係に関するレポジトリが設定されていないので、まずはこれを設定します。
以下のページを参考に /etc/apt/sources.list
を書き換えてみました。
- apt build-dep がエラーになるときの対処方法 by @tetsukoba: https://qiita.com/tetsu_koba/items/904b03e908b040ed447d
僕の環境は Ubuntu 16.04 からアップデートしたものなので、参考ページの /etc/apt/sources.list
と少し様子が違いましたが、
deb
から始まる行をコピペして deb-src
にしたものを足していけば大丈夫そうです。
あとは、 apt update
して apt build-dep
を実行します。
まとめると、実行するコマンドは以下のようになります。
$ sudo vim /etc/apt/sources.list #上記の通り編集
$ sudo apt update
$ sudo apt build-dep qemu
ソースコードのダウンロード & ビルド
次に、QEMU のソースコードを git clone
でダウンロードし、 v5.0.0 (執筆時の最新バージョン)タグにチェックアウトしてビルドします。
回線やマシンのスペックによりますが、git clone と make はそこそこ時間がかかると思います。
コマンドは以下の通りです。
$ cd ~
$ git clone git://git.qemu.org/qemu.git
$ cd qemu
$ git checkout -b v5.0.0-release refs/tags/v5.0.0
$ git submodule update --init
$ ./configure --prefix=`pwd`/build --target-list=arm-softmmu,aarch64-softmmu --audio-drv-list=alsa
$ make -j5
(2021.05.09 追記: 最近の QEMU は Ninja というビルドツールを使うらしく、これをセットアップしないと ./configure が失敗するようです。こちらのページが参考になりました。QEMUコンパイルガイド - コードワールド)
ちなみに、make の -j
オプションはコンパイル時の並列数を指定するものです。
CPU のコアがたくさんあるなら大きめの数字にすると速くビルドが終わるかもしれないです。
僕は、コア数 + 1 をよく指定します (適切な値については諸説あるらしい)。
システム全体で使えるようにするなら、この後に sudo make install
を実行するといいんですが、僕はシステムに入っている QEMU と混ざるのは嫌なので、インストールせずに進めます。
ビルドされた QEMU のバイナリがどこにあるのかわからなかったので、以下のコマンドで見つけます。
$ find . -name 'qemu-system-*'
./aarch64-softmmu/qemu-system-aarch64
./arm-softmmu/qemu-system-arm
qemu-system-aarch64
というのが QEMU の実行ファイルです。
以下のようにして、ビルドした QEMU が raspberry pi 3 のエミュレートに対応していることを確認します。
$ ./aarch64-softmmu/qemu-system-aarch64 -machine help | grep rasp
raspi2 Raspberry Pi 2B
raspi3 Raspberry Pi 3B
サンプルバイナリの準備と実行
今回は 参考ページにあるサンプルコード をビルドして QEMU 上で動かしてみます。
サンプルコードのビルド
次に 参考ページにあるサンプルコード から QEMU 上で試しに動かす ELF バイナリを作ります。
aarch64 向けのクロスコンパイラをインストールし、それを使ってサンプルコードをビルドします。
$ sudo apt install gcc-aarch64-linux-gnu
$ mkdir ~/raspberrypi-baremetal-sample-src
$ cd ~/raspberrypi-baremetal-sample-src
$ # ↑で適当に作ったディレクトリにソースを用意
$ ls
Makefile boot.S kernel.c linker.ld
$ make
aarch64-linux-gnu-gcc -mcpu=cortex-a53 -fpic -ffreestanding -c boot.S -o boot.o
aarch64-linux-gnu-gcc -mcpu=cortex-a53 -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -c kernel.c -o kernel.o
kernel.c: In function 'mmio_write':
kernel.c:6:4: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
*(volatile uint32_t*)reg = data;
^
kernel.c: In function 'mmio_read':
kernel.c:12:11: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
return *(volatile uint32_t*)reg;
^
aarch64-linux-gnu-gcc -T linker.ld -o kernel.elf -ffreestanding -O2 -nostdlib boot.o kernel.o
$
ビルドが成功すれば、 kernel.elf
というバイナリファイルができます。
(なんか warning 出ていますが、気にせず進めます。)
このサンプルは
- UART に "Hello kernel World!" を表示する。
- その後入力待ちになり、入力した文字をそのまま表示する。
という動きをします。
ELF バイナリを QEMU で動かしてみる
では先ほどビルドした ELF バイナリを実際に動かしてみましょう。
$ ~/qemu/aarch64-softmmu/qemu-system-aarch64 -M raspi3 -nographic -kernel ./kernel.elf
HHHHelellello, kernel World!
ello, kernel World!
lo, kernel Woro, kld!
ernel World!
QEMU: Terminated
$
なんだか表示がひどいことになっていますが、これは 4 コアで同時に "Hello kernel World!" を表示した結果です(たぶん)。
上の実行例ではやっていませんが、"Hello kernel World!" (x4 だったはずのなにか) の表示後にキー入力すると、その文字がそのまま表示されます。
Ctrl-a x の順でキー入力をすると、QEMU を終了できます。
ちなみに、表示が崩れるのでコアを1つにしてようと QEMU の -smp
オプションを付けてみましたがだめでした。
raspi3 のエミュレーション時はコア数は 4 で固定のようです。
同様にメモリも 1 GiB で固定のようでした(もしかしたら変える方法あるかもしれまんせんが..)
おわりに
というわけで、QEMU 上で Raspberry Pi 3 用のバイナリを動かしてみました。
しばらくこれで ARM のベアメタルプログラミングで遊べるといいですね。