Raspberry Pi 3+ をつかったり、 qemu-system-aarch64 を使ったりして、 ほんのちょっとベアメタルに挑戦したけど、仕切り直ししたくなったので、開発環境の構築からやりなおす。
今回は、環境一式そろえるところから、デバッグできることを確認するところまで。
前提環境
構築予定環境を決める
ビルド環境と実行環境を決める。
ツールチェイン選定
Aarch64 のベアメタルに使えるツールチェインはいくつかあるらしい。
それぞれのツールチェインの目的とか特徴とか言えれば良いのだけれど、そんな知識はない。
なんとなく、 GNU Toolchain – Arm Developer
を使っていくことにする。
実行環境選定
この前コンパイルした 、Xilinx/qemu で実行するのがよさそう。
Raspberry Pi 3 も UltraScale+ も Cortex-A53 っぽいし、 Xilinx Quick Emulator User Guide を読めば比較的簡単にベアメタルプログラムの実行ができそうな気がする。
構成検討
慣れ親しんだ環境でやろうということで、以下の構成とする。
ビルド環境
OS: Windows 10 Pro
ツールチェイン: GNU Toolchain – Arm Developer
シェル環境: MSYS2
実行環境
という感じでやっていく。
環境構築
Windows 用バイナリが 32bit 用のものしかないけど問題ないでしょう。
GNU Toolchain – Arm Developer にアクセス
GNU toolchain for A-Profile
-> Downloads
を選択
gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf.tar.xz
をダウンロードして任意の場所に展開
この記事では ~/app/gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf
に展開
~/app/gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf/bin
にパスを通す
MSYS2 インストール
MSYS2 homepage から、 msys2-x86_64-20180531.exe
をダウンロード。
インストール先フォルダ: C:\msys64_aarch64_gnu_toolchain
スタートメニューのショートカット: MSYS2 64bit Aarch64 GNU Toolchain
pacman -Syu
して再起動
pacman -Su
ビルドに必要なパッケージをインストール
make
をインストール。
ツールチェインへパスを通す
MSYS2 上で、次のコマンドを実行し、ツールチェインへのパスを通す。 必要であれば .bashrc
への登録もする。
export PATH=$PATH :~/app/gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf/bin
プログラムの作成
何もせずに無限ループするプログラムを作成する。
プロジェクトディレクトリ構成
first_step/
+- Makefile : ビルド手順を書くやつ
+- boot.S : プログラム本体
+- memmap : リンカスクリプト
boot.S
// ブート時のエントリーポイント
.global start
start:
// スタックポインタの開始位置指定
mov sp, #0x80000
// 何もせず無限ループ
b halt
// 無限ループ
halt:
wfe
b halt
memmap
MEMORY
{
ram : ORIGIN = 0x80000, LENGTH = 0x1000000
}
SECTIONS
{
.text : { *(.text*) } > ram
.bss : { *(.bss*) } > ram
}
Makefile
CROSS := aarch64-elf
AS = $( CROSS ) -as
LD = $( CROSS ) -ld
OBJDUMP = $( CROSS ) -objdump
OBJCOPY = $( CROSS ) -objcopy
AFLAGS = -g3
all: kernel8.img
boot.o: boot.S
$( AS ) $( AFLAGS ) $< -o $@
kernel8.elf : memmap boot.o
$( LD ) boot.o -T memmap -o kernel8.elf
kernel8.img : kernel8.elf
$( OBJCOPY ) $< -O binary kernel8.img
.PHONY: clean
clean:
rm -rf *.o *.elf *.img
プログラムのビルド
MSYS2 の環境で、プロジェクトディレクトリに移動して make
するだけ。 (MSYS2 上での作業)
cd /PATH/TO/first_step
make
ビルドに成功すると、boot.o
と kernel8.elf
, kernel8.img
が生成される。 QEMU での実行時に必要なのは kernel8.elf
。
実行とデバッグ
デバッグ用の docker コンテナを立ち上げて、実行とデバッグを行う。
QEMU を GDB 接続待ち状態で起動する
プロジェクトディレクトリをマウントしつつ、デバッグを可能とするオプションを付けてコンテナを起動。 (PowerShell 上での作業)
docker run -it --rm -v " $( pwd ) :/work" --workdir "/work" --name "work" --cap-add=SYS_PTRACE --security-opt="seccomp=unconfined" mikoto2000/qemu-xilinx
起動したコンテナで、QEMU を実行。
qemu-system-aarch64 -M arm-generic-fdt -nographic -serial mon:stdio -dtb /var/dts/LATEST/SINGLE_ARCH/zcu102-arm.dtb -device loader,file=./kernel8.elf,cpu-num=0 -device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4 -s -S
-M arm-generic-fdt
: デバイスツリーでマシン定義しますよという指定
-nographic
: GUI 無しで起動
-serial mon:stdio
: シリアル通信を QEMU モニタに統合
-dtb /var/dts/LATEST/SINGLE_ARCH/zcu102-arm.dtb
: マシン情報が記載されたファイルを指定
-device loader,file=./kernel8.img,cpu-num=0
: どのバイナリをどの CPU で実行するかの指定
-device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4
: Cortex-A53 の CPU0 を開始するための設定(レジスタ CRF_APB.RST_FPD_APU
の設定)
-s
: GDB からの接続を、 localhost:1234
で待ち受ける
-S
: 起動と同時に一時停止する
詳細は Xilinx Quick Emulator User Guide 参照。
QEMU に GDB で接続する
同じコンテナに接続し、 GDB で QEMU に接続する。 (PowerShell 上での作業)
docker exec -it work /bin/bash
GDB で QEMU に接続。
gdb-multiarch -q --eval-command="target remote localhost:1234" ./kernel8.elf
-q
: ロゴを表示しない
--eval-command="target remote localhost:1234"
: GDB 起動後に実行するコマンド
今回は、起動のたびに GDB UI 上で接続コマンドを打つのが面倒だったので起動時に渡すようにした
GDB を使ったステップ実行
デバッグ中のターミナルログ
root@4934294dab10 :/work# gdb-multiarch -q --eval-command="target remote localhost:1234" ./kernel8.elf
Reading symbols from ./kernel8.elf...done.
Remote debugging using localhost:1234
start () at boot.S:6
6 mov sp, #0x80000
( gdb ) b start
Breakpoint 1 at 0x80000: file boot.S, line 6.
( gdb ) b halt
Breakpoint 2 at 0x80008: file boot.S, line 12.
( gdb ) n
Thread 1 hit Breakpoint 2, halt () at boot.S:12
12 wfe
( gdb )
13 b halt
( gdb )
Thread 1 hit Breakpoint 2, halt () at boot.S:12
12 wfe
( gdb )
13 b halt
( gdb )
... (snip)
作ったプログラムの先頭から実行され、無限ループに入ることが確認できた。
QEMU モニタによるレジスタ確認
GDB によるデバッグ中でも、 QEMU モニタを使って各種情報を取得できる。 試しにレジスタ情報を表示する。
QEMU を起動しているターミナルで、 CTRL-a c
と、キーを押すと QEMU モニタに命令を出せるようになる。
( qemu ) info registers
PC= 000000000008000c SP= 0000000000080000
X00= 0000000000000000 X01= 0000000000000000 X02= 0000000000000000 X03= 0000000000000000
X04= 0000000000000000 X05= 0000000000000000 X06= 0000000000000000 X07= 0000000000000000
X08= 0000000000000000 X09= 0000000000000000 X10= 0000000000000000 X11= 0000000000000000
X12= 0000000000000000 X13= 0000000000000000 X14= 0000000000000000 X15= 0000000000000000
X16= 0000000000000000 X17= 0000000000000000 X18= 0000000000000000 X19= 0000000000000000
X20= 0000000000000000 X21= 0000000000000000 X22= 0000000000000000 X23= 0000000000000000
X24= 0000000000000000 X25= 0000000000000000 X26= 0000000000000000 X27= 0000000000000000
X28= 0000000000000000 X29= 0000000000000000 X30= 0000000000000000
PSTATE= 400003cd -Z-- EL3h
q00= 0000000000000000:0000000000000000 q01= 0000000000000000:0000000000000000
q02= 0000000000000000:0000000000000000 q03= 0000000000000000:0000000000000000
q04= 0000000000000000:0000000000000000 q05= 0000000000000000:0000000000000000
q06= 0000000000000000:0000000000000000 q07= 0000000000000000:0000000000000000
q08= 0000000000000000:0000000000000000 q09= 0000000000000000:0000000000000000
q10= 0000000000000000:0000000000000000 q11= 0000000000000000:0000000000000000
q12= 0000000000000000:0000000000000000 q13= 0000000000000000:0000000000000000
q14= 0000000000000000:0000000000000000 q15= 0000000000000000:0000000000000000
q16= 0000000000000000:0000000000000000 q17= 0000000000000000:0000000000000000
q18= 0000000000000000:0000000000000000 q19= 0000000000000000:0000000000000000
q20= 0000000000000000:0000000000000000 q21= 0000000000000000:0000000000000000
q22= 0000000000000000:0000000000000000 q23= 0000000000000000:0000000000000000
q24= 0000000000000000:0000000000000000 q25= 0000000000000000:0000000000000000
q26= 0000000000000000:0000000000000000 q27= 0000000000000000:0000000000000000
q28= 0000000000000000:0000000000000000 q29= 0000000000000000:0000000000000000
q30= 0000000000000000:0000000000000000 q31= 0000000000000000:0000000000000000
FPCR : 00000000 FPSR: 00000000
( qemu )
こんな感じ。
以上。
参考資料