自作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起動に必要な動的ライブラリの適切なバージョンを引っ張ってくるので地獄を見た(最初はrustdockerイメージで頑張ってたけど匙投げてubuntu:14.04使うようにしてから苦行程度に落ち着いた)。