FPGA開発日記

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

QEMU5.1.0でのRISC-Vテストの動かし方とOpenSBIについて

ふとした理由でQEMU5.1.0でRISC-Vのテストコードを動かしたくてやり方をいろいろと調べていたのだがどうも見つからない。とりあえず以下のオプションを足してテストコードを動かしてみる。

$ git checkout v5.1.0
$ ../configure --target-list=riscv64-softmmu --disable-docs
$ make -j4

以下のようにしてみた。

$ ./riscv64-softmmu/qemu-system-riscv64 --kernel /home/msyksphinz/work/riscv/riscv-tools/riscv-tests/build-rvg/benchmarks/dhrystone.riscv
qemu-system-riscv64: Unable to load the RISC-V firmware "opensbi-riscv64-spike-fw_jump.elf"

このエラーの意味が最初はさっぱりわからなかったのだが、どうもQEMUの最新バージョンではファームウェアを指定しなければLinuxのブートなどを全般的にできなくなっているようだ。ソースコードを読みつつ(というかソースコードを読まなければこれの解決方法が分からなかった)色々と試行した結果以下のようにしてみた

./riscv64-softmmu/qemu-system-riscv64 --bios none --kernel /home/msyksphinz/work/riscv/riscv-tools/riscv-tests/build-rvg/benchmarks/dhrystone.riscv
qemu-system-riscv64: HTIF tohost must be 8 bytes

えー、HITFtohostなんて8バイトに設定してあると思うんだけどな。

riscv64-unknown-elf-readelf -a /home/msyksphinz/work/riscv/riscv-tools/riscv-tests/build-rvg/benchmarks/dhrystone.riscv | grep -e tohost -e fromhost
  [ 2] .tohost           PROGBITS         0000000080001000  00002000
   01     .tohost .text .text.startup .rodata.str1.8 .rodata .sbss .bss
    65: 0000000080001040     0 NOTYPE  GLOBAL DEFAULT    2 fromhost
    67: 0000000080001000     0 NOTYPE  GLOBAL DEFAULT    2 tohost
    70: 0000000080002928    20 FUNC    GLOBAL DEFAULT    3 tohost_exit

あれ、サイズとしてはtohostfromhostはゼロバイトなのか。じゃあエラーが発生してもおかしくはない。

とりあえずQEMU側を以下のように変更して問題が発生しないように調整してみた。

-Subproject commit 22ead3e0bfdb87516656453336160e0a37b066bf
+Subproject commit 22ead3e0bfdb87516656453336160e0a37b066bf-dirty
diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c
index ca87a5cf9f..d789295d2d 100644
--- a/hw/riscv/riscv_htif.c
+++ b/hw/riscv/riscv_htif.c
@@ -48,15 +48,15 @@ void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
     if (strcmp("fromhost", st_name) == 0) {
         address_symbol_set |= 1;
         fromhost_addr = st_value;
-        if (st_size != 8) {
+        if (st_size != 8 && st_size != 0) {
             error_report("HTIF fromhost must be 8 bytes");
             exit(1);
         }
     } else if (strcmp("tohost", st_name) == 0) {
         address_symbol_set |= 2;
         tohost_addr = st_value;
-        if (st_size != 8) {
-            error_report("HTIF tohost must be 8 bytes");
+        if (st_size != 8 && st_size != 0) {
+            error_report("HTIF tohost must be 8 bytes, st_size=%ld", st_size);
             exit(1);
         }
     }
./riscv64-softmmu/qemu-system-riscv64 --bios none --kernel /home/msyksphinz/work/riscv/riscv-tools/riscv-tests/build-rvg/benchmarks/dhrystone.riscv
qemu-system-riscv64: Failed to bind socket: Permission denied

ぬ、いろいろオプションを追加してみた。

./riscv64-softmmu/qemu-system-riscv64 --d in_asm --nographic --bios none --kernel /home/msyksphinz/work/riscv/riscv-tools/riscv-tests/build-rvg/benchmarks/dhrystone.riscv
0x0000000080002894:  ffffe797          auipc           a5,-8192        # 0x80000894
0x0000000080002898:  76e7b623          sd              a4,1900(a5)
0x000000008000289c:  ffffe697          auipc           a3,-8192        # 0x8000089c
0x00000000800028a0:  7a468693          addi            a3,a3,1956
0x00000000800028a4:  0006b783          ld              a5,0(a3)
0x00000000800028a8:  fe078ee3          beqz            a5,-4           # 0x800028a4

----------------
IN: putchar
Priv: 3; Virt: 0
0x00000000800028a4:  0006b783          ld              a5,0(a3)
0x00000000800028a8:  fe078ee3          beqz            a5,-4           # 0x800028a4

色々動き出したが、ここで止まってしまったようだ。putchar()で止まってしまっている。おそらくRISC-V版Dhrystoneに埋め込まれているputchar()の動きにQEMUが対応していないからだろう。何故ならば、BIOSのオプションをnoneにしてしまっているが本当はここに何らかのファームウェアを指定しなければならない。

https://github.com/riscv/opensbi

OpenSBIというのはその名の通りRISC-V向けのスーパバイザインタフェースのことで、RISC-Vのマシンモード向けの操作をプラットフォーム向けに規定する。Linuxなどはスーパバイザモードとユーザモードの動作を記述しているわけだが、マシンモードでは、例えば文字の出力の方法などを規定している。

$ git clone git@github.com:riscv/opensbi.git
$ cd opensbi
$ make CROSS_COMPILE=riscv64-unknown-elf- PLATFORM=generic

これでbuild/platform/generic/firmware/fw_jump.elfが完成する。しかしこれをどのように使っていけばよいのだろう?

$ ./build/riscv64-softmmu/qemu-system-riscv64 --d in_asm --nographic --machine spike \
    --bios /home/msyksphinz/work/riscv/opensbi/build/platform/generic/firmware/fw_payload.elf \
    --kernel /home/msyksphinz/work/riscv/riscv-tools/riscv-tests/build-rvg/benchmarks/dhrystone.riscv
rom: requested regions overlap (rom phdr #0: /home/msyksphinz/work/riscv/riscv-tools/riscv-tests/build-rvg/benchmarks/dhrystone.riscv. free=0x0000000080017a60, addr=0x0000000080000000)
qemu-system-riscv64: rom check and register reset failed

まず、QEMUがどのようなメモリマップになっているのかを確認する。このためにはDTBを出力してダンプしてみよう。

$ ./riscv64-softmmu/qemu-system-riscv64 --d in_asm --nographic --machine spike,dumpdtb=qemu-riscv64-spike.dtb --bios none
$ ./riscv64-softmmu/qemu-system-riscv64 --d in_asm --nographic --machine virt,dumpdtb=qemu-riscv64-virt.dtb --bios none
$ dtc qemu-riscv64-spike.dtb > qemu-riscv64-spike.dts
$ dtc qemu-riscv64-virt.dtb > qemu-riscv64-virt.dts
  • qemu-riscv64-spike.dts
/dts-v1/;

/ {
        #address-cells = < 0x02 >;
        #size-cells = < 0x02 >;
        compatible = "ucbbar,spike-bare-dev";
        model = "ucbbar,spike-bare,qemu";

        chosen {
                bootargs = [ 00 ];
        };
...
        htif {
                compatible = "ucb,htif0";
        };
};

うーん、htif0が入っているのは分かった。これをどのように接続すればいいんだ?メモリマップされているわけでもなさそうなので...すこし調査が必要だ。