FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

QEMU

QEMUによるメモリアクセスを実現する仕組み

QEMUによる分岐命令の実現方法が分かったので、次はロードストア命令について実現方法を見ていきたいと思う。例えばRISC-VにおけるLD命令とSD命令を実行する場合、どのようにx86命令に変換されるのだろうか? LD x10, 0(data) SD x10, 8(data) 以下のような…

QEMUが分岐命令を処理する仕組み(Relocationによるラベルの解決)

QEMUで分岐命令を取り扱う場合、主に以下のような手順で取り扱われることを見てきた。 source1に新しいオペランドを確保する。gen_get_gpr(source1, a->rs1)によりsource1にrs1の情報を格納する。 source2に新しいオペランドを確保する。gen_get_gpr(source2…

QEMUのTCG(Tiny Code Generator)を読み解く(7. QEMUの分岐命令の取り扱い)

QEMUの調査続き。次は分岐命令をどのように処理しているのか見ていこう。 例えばBEQ命令がどのようにx86に処理されるのかを見てみよう。 qemu/target/riscv/insn_trans/trans_rvi.inc.c static bool trans_beq(DisasContext *ctx, arg_beq *a) { return gen_…

QEMUのTCG(Tiny Code Generator)を読み解く(6. QEMUのTCGが生成される仕組み)

QEMUはTCG(Tiny Code Generator)と呼ばれる仕組みを使ってゲストマシンの機械語をホストマシンの機械語に変換している。ゲストマシンの機械語は、QEMU実行中にTCGに変換され、これをなるべく最小のホスト機械語に変換することでほぼネイティブなホスト機械…

QEMUのTCG(Tiny Code Generator)を読み解く(5. x86のアプリケーションブートコードの解析)

前回の、QEMUにおけるプロローグコードおよびターゲット関数呼び出しのルーチンを見て、もう少しx86のアセンブリコードについて勉強してみようと思った。 QEMUの実行ログを見ると、最初のブートコードは以下のようになっている。 PROLOGUE: [size=45] 0x7f13…

QEMUのTCG(Tiny Code Generator)を読み解く(4. QEMUがカーネルを呼ぶ際に実行するPrologueコードを読み解く)

TCGの続き。続いて、TCGを使ってどのようにRISC-Vのコードをx86上で実行しているのかをチェックする。このためには、まずは関数のプロローグを調べていく必要があるだろう。 デバッグ情報を出力しながらQEMUを実行する。 $ qemu-system-riscv64 --machine vi…

QEMUのTCG(Tiny Code Generator)を読み解く(3. TCGContextによりホスト命令がターゲット命令に変換される流れ)

TCGの続き。TCGによるエンコードをもっと詳しく見るために、TCGContextを眺めてみる。 qemu/include/tcg/tcg.h struct TCGContext { uint8_t *pool_cur, *pool_end; TCGPool *pool_first, *pool_current, *pool_first_large; int nb_labels; ... TCGTemp *fr…

QEMUのTCG(Tiny Code Generator)を読み解く(2. TCGが生成したx86機械語を読む)

TCGの続き。TCGによるエンコードをもっと詳しく見るために、最適化を抑制すべくいろいろ変更してみた。 git diff tcg/i386/tcg-target.inc.c diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index ec083bddcf..d86ccdf05b 100644 --- …

QEMUのTCG(Tiny Code Generator)を読み解く

QEMUは高速なエミュレーションが可能な理由の一つに、TCGを使った高速なバイナリ変換機構がある。TCGの役割は、ターゲットバイナリからTCG(Tiny Code Generator)を用いた中間表現に変換し、ホスト形式のバイナリに変換する2つの機構が存在している。 TCGがど…

QEMUに入門してみる(20. CSRレジスタの追加)

QEMU実装の続き。CSR命令の実装が足りていないので、いくつか主要なものを実装していく。現時点ではmhartidしか実装していないので追加していく。csr.cにはCSR命令追加のテンプレートが用意されているのでこれを使っていく。 qemu/target/myriscvx/csr.c /* …

QEMUに入門してみる(19. 分岐命令の実装方法)

QEMU実装の続き。命令実装を進めていく中で、次に実装すべきは分岐命令だ。QEMUで分岐命令をどのように実装するかについて検討を行う。 まずは分岐命令の定義だ。分岐命令はデコードテーブルの中で以下のようにして定義する。 qemu/target/myriscvx/insn32.d…

QEMUに入門してみる(18. ディスアセンブル機能の実装)

前回のQEMUの実行ログを見ると、命令のニーモニックが出力されていなかった。どうしたら命令のディスアセンブルが表示されるんだろう?ということでいろいろ調査した。 IN: Priv: 3; Virt: 0 0x0000000000001000: OBJD-T: 9702000093850202732540f1 このOBJD…

QEMUに入門してみる(17. CSR操作命令が実行される手順の解析)

とりあえずQEMUのブートコードだけ動かしたいので、次はCSR命令を実装しなければならない。RISC-VのCSR命令がQEMUでどのように実行されているかを解析しよう。 まず、CSR命令をinsn32.decodeに追加する。これでCSR命令のデコーダが自動的に追加され、trans_x…

QEMUに入門してみる(16. 命令デコーダの追加)

前回の続き。所望の場所から命令をフェッチできるようになったので、次は命令デコーダを追加していきたい。QEMUにおける命令デコーダはDSLで記述されており、MYRISCVXの場合はqemu/target/myriscvx/insn32.decodeに置いている。これはRISC-Vにおけるデコーダ…

QEMUに入門してみる(15. QEMUにおけるリセットベクタの追加方法)

前回QEMUのトレース情報を取得しながらシミュレーションをスタートできるようになったが、よく見てみると最初の命令でいきなりIllegal Instructionで例外に飛んでいる。当たり前だ、そこに命令は配置されておらず、しかもデコーダもろくに実装していないので…

QEMUに入門してみる(14. トレース関数の自動生成方法)

QEMUにはトレース出力用の関数を自動的に生成するフレームワークが存在している。これは命令トレースを出力するのとは異なり、各種イベントを取得するためのフレームワークを生成するものだ。 まず、取りたいイベントを定義しなければならない。target/myris…

QEMUに入門してみる(13. 割り込み・例外ハンドラの実装)

QEMUのデバッグ続き。次に落ちたのは以下の部分。GDBで確認する。 $ gdb ${QEMU_BUILD}/myriscvx64-softmmu/qemu-system-myriscvx64 Starting program: ${QEMU_BUILD}/myriscvx64-softmmu/qemu-system-myriscvx64 --machine virt --d in_asm --nographic --k…

QEMUに入門してみる(12. QEMUでのコールバック関数追加方法の調査)

QEMUの続き。QEMUのRISC-V対応においてバイナリのシミュレーション方法とログの出し方が分かったわけだが、これをMYRISCVXに移植したい。つまり同じコマンドをqemu-system-myriscvx64に適用することを考える。 まずは変換のための初期化関数から。 qemu/targ…

QEMUに入門してみる(11. QEMUでのコールバック関数追加方法の調査)

QEMUの続き。QEMUのRISC-V対応においてバイナリのシミュレーション方法とログの出し方が分かったわけだが、これをMYRISCVXに移植したい。つまり同じコマンドをqemu-system-myriscvx64に適用することを考える。 ./qemu-system-myriscvx64 --machine none --d …

QEMUに入門してみる(10. QEMUでのベアメタルバイナリの実行方法調査)

QEMUの続き。前回、QEMUを使ってLinux向けにコンパイルされたバイナリを実行することができるようになったが、ベアメタルのバイナリを動かすことはまだできていない。目標としてはとりあえずriscv-tests関係のプログラムを動かすことができるようになりたい…

QEMUに入門してみる(9. QEMUでの詳細トレースの出し方)

QEMUの続き。QEMUを使ってRISC-Vのアプリケーションやベンチマークを走らせたい。とりあえずは普通のRISC-Vのバイナリで動作を確認するところから始めよう。 main.c #include <stdio.h> int main() { printf("Hello World\n"); return 0; } $ riscv64-unknown-linux-g</stdio.h>…

QEMUに入門してみる(8. translateに必要な関数の確認)

QEMUの続き。独自ターゲットでビルドしてみる。前回の続き。 gen_load()とgen_store()で使用されている関数を定義しなければならない。 gen_get_gpr():GPRから値を取得する関数。 /* Wrapper for getting reg values - need to check of reg is zero since …

QEMUに入門してみる(7. translateに必要な関数の確認)

QEMUの続き。独自ターゲットでビルドしてみる。前回の続き。 QEMUのデコーダを作っていく。QEMUのデコーダはDSLを使って記述するらしい。translate.cで呼び出されているecode_insn32()を作っていく。 qemu/target/myriscvx/translate.c static void decode_o…

QEMUに入門してみる(6. translateに必要な関数の確認)

QEMUの続き。独自ターゲットでビルドしてみる。前回の続き trans_xxx()の関数がみんな消えてしまったのはおそらく最適化で消されたので、一応念のためDebugビルドしてオブジェクトをダンプしてみた。 $ ../configure --enable-debug --disable-pie --target-…

QEMUに入門してみる(5. translateに必要な関数の確認)

QEMUの続き。独自ターゲットでビルドしてみる。static const TranslatorOps myriscvx_tr_opsで必要な関数を調査する。 必要な関数を確認しながら追加していく。myriscvx_tr_breakpoint_check()の中で、デバッグ例外を発生させることが必要だ。 static bool m…

QEMUに入門してみる(4. 独自コンフィグレーションを用意して必要なファイルを確認)

QEMUの続き。独自ターゲットでビルドしてみる。static const TranslatorOps myriscvx_tr_opsで必要な関数を調査する。 一つずつ見ていこう。myriscvx_tr_init_disas_context()を見てみる。Disasということから、これはディスアセンブル(というかログ?)を…

QEMUに入門してみる(3. 独自コンフィグレーションを用意して必要なファイルを確認)

QEMUの続き。独自ターゲットでビルドしてみる。 次に追加したのはmyriscvx_cpu_fp_enable()である。これはどうも浮動小数点を使用したかを確認するらしい。浮動小数点命令が有効になっていても、mstatus.fsが0になっていればfalseになる。つまりこれはコンテ…

QEMUに入門してみる(2. 独自コンフィグレーションを用意して必要なファイルを確認)

QEMUの続き。独自ターゲットでビルドしてみる。myriscvx64コンフィグレーションを用意した。 MYRISCVXCPUはCPUMYRISCVXStateを包むラッパーのようなものらしい。envメンバ変数としてMYRISCVXStateを含んでおり、cfgメンバはRISC-Vのオプションを指定できるよ…

QEMUに入門してみる(2. 独自コンフィグレーションを用意して必要なファイルを確認)

QEMUの続き。独自ターゲットでビルドしてみる。myriscvx64コンフィグレーションを用意した。 ../configure --disable-werror --target-list=myriscvx64-softmmu make -j$(nproc) default_configs/myriscvx64-softmmu.mak # Default configuration for myrisc…

QEMUに入門してみる(1. RISC-V用ビルドの確認)

QEMUのソースコードをGitHubからダウンロードして中身を見てみる。riscvと名のつくディレクトリがどの程度あるか探してみた。 $ find . -name riscv ./hw/riscv ./include/hw/riscv ./linux-user/riscv ./target/riscv ./tcg/riscv ビルドするには以下を実行…