自作CPU & 自作OSをやっていく (2) - 64ビットRISC-Vの "Hello World" をRustで作った
2020年1月から、趣味エンジニアリング活動として自作CPUと自作OSをやっていく。
今回は、64ビットRISC-V (RV64I) の “Hello World” をRustで作ったので、そのリポジトリの紹介。
自作CPU & 自作OS タグで、この前後の進捗とか目指しているもの(初回記事)とかを追えるようにしている。
冒頭にも貼ったが、
のリポジトリに公開している。READMEに全部書いてあるが、特徴を抜粋すると
Dockerfile
を書いているので、面倒なコンパイラツールチェインなどのインストールを自前でしないで良い。- 64ビットなRISC-Vターゲットへコンパイルされる(世のサンプルは32ビット版が多いのでちょっと参考になるかも)。
- RustのStableビルドを使う。Nightlyビルドは使わない。
- xargo を使わず
cargo
を使う。 - Visual Studio Codeを使う人なら、Remote Development Containerで走るように設定ファイルを同梱している。
あたり。
参考にさせていただいた RustでRISC-V OS自作!はじめの一歩 とファイル構成は同じなので、比較しながら読めば十分に挙動は追えるはず(解説をサボった)。こちらの記事との差分は、
- 64ビット版であること。
- QEMUで
-machine sifive_u
ではなく-machine virt
を使った。それに伴いUARTのアドレスが0x10013000
から0x10000000
に変わった。 - リンカスクリプトで無駄を省いたりコメント追加したりした。あと
MEMORY
コマンドを使った(と言っても定義して実際に使ったのはRAMだけだが…)。 boot.s
で、スタック領域を確保するときにla
アセンブリ疑似命令を使うようにした。
あたり。
苦労したこと・学んだこと
- RustのStableビルドで勝負するなら、インラインアセンブリが使えないので、生アセンブリファイルを書く必要がある。
- 生アセンブリファイルを書いた瞬間に、RISC-V用のコンパイラツールチェイン が必要になる。アセンブリファイルのアセンブルのため、その結果のオブジェクトファイルを
rustc
でコンパイルした別のオブジェクトファイルとリンクするため。rustc
がバックエンドに使ってるLLVMでRISC-V用のオブジェクトファイル生成もリンクもできているわけなので、本来的には不要なはず。build.rs
をもっと上手に書けば不要にできるのか…?🤷
- QEMUのバージョンの違いで “Hello World” が出力されたりされなかったり…
- UARTのアドレスを規定するのがQEMUだと理解するまでは「謎挙動」に見えて苦しんでいた。 https://gitlab.freedesktop.org/spice/qemu/blob/e24f44dbeab8e54c72bdaedbd35453fb2a6c38da/hw/riscv/virt.c#L51-63 を見つけてようやく意味がわかった。
- スタック領域確保、64ビットにした途端に動かなくなった…
- 最終的に
la
を使って回避したが、 RustでRISC-V OS自作!はじめの一歩 のままlui
とori
使う形式でも動きそうなもんだけどな…🤔
- 最終的に
- リンカスクリプト何もわからん状態だったが、 リンカスクリプトの書き方 読んでだいぶ分かるようになった気がするありがたい。
- SiFiveはRISC-Vクロスコンパイル用のビルド済みgccもqemuを提供してくれていてとてもありがたいけど、Ubuntu 14.04前提なのが厳しかった。特に
riscv64-unknown-elf-gdb
とqemu-system-riscv64
起動に必要な動的ライブラリの適切なバージョンを引っ張ってくるので地獄を見た(最初はrust
dockerイメージで頑張ってたけど匙投げてubuntu:14.04
使うようにしてから苦行程度に落ち着いた)。