前回( RISC-V 開発環境をbuildrootとcrosstool-ngで用意する )はカーネルをqemuで起動したので、今度はWebブラウザ上で動作するRISC-VエミュレータであるところのTinyEMU( https://bellard.org/tinyemu/ )で起動してみる。
-
https://sophisticated-overjoyed-treatment.glitch.me/run.html
- ビルドしたTinyEMUとLinuxカーネル(rootfsが無いので直ぐpanicする)
- dmesg: https://gist.github.com/okuoku/23c2fbd2400d21949fbb5e80c686149d#file-dmesg-txt
親のカーネルpanicより見た。
tl;dr
今回はTinyEMU標準のI/Oライブラリは一切使用せず、EmscriptenのC言語側からVMを構成して起動する形を取ってみた。
- ブートローダはTinyEMU付属のものを使用する必要がある 。付属のローダには改造が施されていて、普通のローダではコンソールが出ない。
- ビルド自体はEmscripten 2.0.2 の
emcc
で単にビルドしてEmscripten標準のHTML出力でも動作する。
TinyEMU のビルド
ビルド自体は特に難しいところはなく、適当に *.c
をコンパイルして出力すれば動くものができる。
emcc jsemu.c softfp.c virtio.c fs.c fs_net.c fs_wget.c fs_utils.c simplefb.c pci.c ^
json.c block_net.c iomem.c cutils.c aes.c sha256.c riscv_cpu.c riscv_machine.c machine.c ^
--llvm-opts 2 -Wall -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -MMD -fno-strict-aliasing ^
-DCONFIG_FS_NET -O3 --memory-init-file 0 --closure 0 -s NO_EXIT_RUNTIME=1 ^
-s "EXPORTED_FUNCTIONS=['_console_queue_char','_vm_start','_fs_import_file','_display_key_event','_display_mouse_event','_display_wheel_event','_net_write_packet','_net_set_carrier','_main']" ^
-s "EXTRA_EXPORTED_RUNTIME_METHODS=[\"ccall\", \"cwrap\"]" ^
--js-library js/lib.js -s WASM=1 -s TOTAL_MEMORY=67108864 -s ALLOW_MEMORY_GROWTH=1 ^
-DMAX_XLEN=32 -DCONFIG_RISCV_MAX_XLEN=32 -s ASSERTIONS=1 --emrun -g4 ^
--source-map-base http://localhost:6931/ --preload-file kernel --preload-file bbl32.bin emmain.c -o run.html
初期設定ルーチン(いわゆる main
)
通常のTinyEMUでは、VMの設定はJavaScript側から実施するが、ちょっとネイティブ版と統一の上で不便だったので main
関数を用意することにした。
-
https://github.com/okuoku/patched-tinyemu-proto/blob/302211483e374b61dd686e1662b59a33eb5fc501/emmain.c
- 作成した
emmain.c
- 作成した
ブートローダー bbl32.bin
と Linuxカーネル kernel
は一旦Emscriptenの --preload-file
アプリ側に埋め込み、C言語のファイルI/Oで読み込んでいる。
kernel
は前回( https://qiita.com/okuoku/items/3133c75d26c57394fd1a )buildrootでビルドしたものがそのまま使用できるが、ブートローダ bbl32.bin
はTinyEMU付属( https://bellard.org/tinyemu/diskimage-linux-riscv-2018-09-23.tar.gz )のものを使用しなければならない。
TinyEMU固有(?)のHTIF実装
TinyEMUはいわゆるUARTデバイスをエミュレートしておらず、Spike(RISC-V公式のエミュレータ)が実装していたHTIF(Host-Target IF)を実装している。
ただ、qemu等の他の実装と異なり、TinyEMUのHTIF実装はアドレスが決め打ちになっており、ブートローダの方をパッチしてアドレスを伝達している。
-
https://github.com/okuoku/patched-tinyemu-proto/blob/302211483e374b61dd686e1662b59a33eb5fc501/riscv_machine.c#L69
- TinyEMU内では、物理アドレス
0x40008000
にHTIFのレジスタを配置している
- TinyEMU内では、物理アドレス
#define HTIF_BASE_ADDR 0x40008000
-
https://gist.github.com/okuoku/23c2fbd2400d21949fbb5e80c686149d#file-riscv-pk-diff-L14-L33
- TinyEMU付属のBBLでは使用されるシンボルを直接パッチしている(TinyEMU付属のパッチ)
diff --git a/bbl/bbl.lds b/bbl/bbl.lds
index 26f5816..615c3dc 100644
--- a/bbl/bbl.lds
+++ b/bbl/bbl.lds
@@ -43,15 +43,10 @@ SECTIONS
_etext = .;
/*--------------------------------------------------------------------*/
- /* HTIF, isolated onto separate page */
+ /* HTIF I/Os */
/*--------------------------------------------------------------------*/
- . = ALIGN(0x1000);
- .htif :
- {
- PROVIDE( __htif_base = .);
- *(.htif)
- }
- . = ALIGN(0x1000);
+ tohost = 0x40008000;
+ fromhost = 0x40008008;
このリンカスクリプトへのパッチで、 tohost
と fromhost
のアドレスを固定化している。
-
https://github.com/riscv/riscv-pk/blob/ac2c910b18c3e36cfd85080472e78ad2fe484325/machine/htif.c#L11-L12
- ブートローダ内での
tohost
fromhost
の宣言
- ブートローダ内での
volatile uint64_t tohost __attribute__((section(".htif")));
volatile uint64_t fromhost __attribute__((section(".htif")));
本来、これらのアドレスはエミュレータがロードしたタイミングで取得される。
-
https://github.com/qemu/qemu/blob/c47edb8dda0660180f86df4defae2a1f60e345db/hw/riscv/riscv_htif.c#L45-L63
- qemuのSpikeマシンモデルで使用されるパッチコード
そもそもHTIF自体が旧い仕様なので今となってはどうでも良いのかもしれないが、もうちょっと真面目なプロトコルが欲しいところでもある。
HTIFを32bitで使う
HTIFは64bit巾のインターフェースであるため、本来32bitアーキテクチャでは使用できない。(書き込みが2つ以上のCPUで競合すると安全に処理できない)
TinyEMUではマルチプロセッサをサポートしていないためこの辺は特に気にしていないようで、 レジスタを32bit巾で宣言し、上位ワードが書き込まれたタイミングで発動する コードとすることで32bit/64bit両対応としている。
- TinyEmu: https://github.com/okuoku/patched-tinyemu-proto/blob/302211483e374b61dd686e1662b59a33eb5fc501/riscv_machine.c#L163-L166
- qemu: https://github.com/qemu/qemu/blob/c47edb8dda0660180f86df4defae2a1f60e345db/hw/riscv/riscv_htif.c#L210-L214
... qemuはマルチプロセッサをサポートしているので同じ方針ではダメな気もするが。。
かんそう
RISC-V 32bitの環境が揃ったのが最近すぎて64bitに比べて色々遅れているというのは覚悟していたけど、HTIFのような純粋なデバッグ用I/Fが32bit非サポートというのはちょっと予想外だった。
TODO: npmで配る用に -s SINGLE_FILE=1
して、かつ、カーネルやブートローダは外部から与えられるようにしないといけない。