Rustで自作組込みOSを書くプロジェクト「ErkOS」を公開しました

だいぶ前からRustでOSを書こうとしていて、ようやく一山越えて形になってきたのでレポジトリを公開しました(気まぐれでまた非公開にするかもしれませんが)。

構造

appディレクトリに実際のボードで動かしているデモが入っています。ボードはNucleo-F429ZIを利用しています。
qemu_appはqemu用のデモです。
nightlyコンパイラと必要なツールチェーンをインストールすればcargo buildでコンパイルでき、qemuのデモはcargo runとすれば動くと思います。

kernelモジュールがスケジューラだったり、プロセスなどのOSの機能を実装したものになっています、
archがARMv7-M固有の機能のためのライブラリ(NVICやSystickなどもこちら)、deviceはボード固有のペリフェラルドライバとなっています。
rtlogThe Embedonomiconのチュートリアルでつくったモジュールに手を加えたものです。
utilはアーキテクチャやボードに依存しない機能を入れるためのモジュールです。

機能としては

  • スレッドモードで動くプロセスをラウンドロビンスケジューラでスケジュールできる
    • 10ms毎のSystick割り込みでプロセスが切り替わる
  • プロセスからSVC命令でカーネルの機能を呼び出せる
    • シリアルをつかったprint命令
    • プロセス自身を特定のIRQが呼び出されるまでスケジュールされないようにする命令
  • 実行するプロセスがない場合は割り込み待ちのスリープ状態に遷移する
    あたりが実装できました。こうしてみるとまだまだ機能は足りないですが、おおよその骨格は出来上がってきたのかなあと思ってます。

実装

異なる種類のボードで動かすことを想定して、コアとなる機能を実装したkernelモジュールはボード固有の機能に依存しないように気を配りました。また、スケジューラも地味に独立したモジュールにしました。これは将来、スケジューリングポリシをユーザーが選べるようにするのを目標にしたからです(以前つくったT-Visorでも同じようなことを目指しました)。
本当はアーキテクチャとの依存性もなくし、Arm以外のアーキテクチャへの対応もできるようにしたかったのですが、とりあえずは諦めました。

ペリフェラルのインターフェースは当初はできるだけRust Embeddedチームの成果物を使う予定でした。svd2rustやcortex-mのクレートはよくできていて、これらを使うことでペリフェラルを簡単に扱ういい感じのインターフェースができます。
しかし前に述べたように、独自のモジュールを使っています。これはsvd2rustなどの方針として、ペリフェラルを触るためのインターフェースが実行時にシングルトン化されています。また、各々のインターフェースは実行時にしか取得できません。
これがかなり問題で、割り込みハンドラとmain関数で両方でデバイスを触りたい、といった時にどのように共有させるかというのが問題になります。no_std環境のため、MutexやArcといったものは使えないのも厳しいです。一応、Option型のグローバル変数として共有すればいいのですが、unsafeで囲ってnullチェックもしなければならない、とそれなりにめんどくさいです。
もちろん、むやみやたらにインターフェースを共有するのもそれはそれでよくないのですが、とりあえず動くものを書きたかったのと、自分の勉強にもなるだろうということで自前のものを使う、という判断にしました。

Rustはテストのシステムもあるのですが、テストはstdに依存しているためそのままでは実行できません。そこで、Writing an OS in RustのTestingを参考にQEMUを利用してテストが走る環境をつくりました。
しかしながら、やはりアーキテクチャやペリフェラルに直接関わるテストはどう書けばいいのか思いつかず、現状QEMUを利用するテストはないです。
代わりに、utilはstd環境でも動くようにアーキテクチャへの依存を切って普通にテストを書きました。

今後の展望

まだまだ機能が足りていないなあという感じです。とりあえず今やりたいと思っていることは

  • ヒープ領域が存在しないのでallocを実装する
  • プロセス間の通信
  • 優先度を考慮したスケジューリング
  • ペリフェラルドライバの充実

その他、他のボードやアーキテクチャへの移植もゆくゆくはやっていきたいですね