2021年4月5日月曜日

ゼロからのOS自作入門をRustで書いていく3章~UEFIライブラリがしんどい件について

最新のリポジトリはこちら。(作業完了時のコミット→Day3のコミット


 マジでこの苦しみを分かち合いたい。

この辺の人達もだいたい同じところで苦戦してる。

【ゼロからのOS自作入門】MikanOSをRustに移植する 1章・2章

MikanOSをRustでやってますが、しんどい。

Rustの人たち3人よれば文殊の知恵になったらいいな。
3人目の俺がポンコツなのでならないかもしれないけど。

というわけでさらなる4人目のRust使いがこのあたりしんどい・・・ってなったときの道標として↑の記事読んでも詰まった部分を書いていく。

何度も言うけど、UEFIライブラリ(uefi-rs)で詰まった。

詰まりどころじゃない部分のソースもgithubに載せておくので、コピペして使ってOK

3章(3日目)は1~2日目とは違って、カーネルファイルをロードするというそれっぽいことをします。が、Rust使いには茨の道だったという話を書いていくつもり。

ファイル読み込みは苦行

事前に言っておくと、uefi-rsを使う以上、カーネル側のkernel_main関数もEFI_API呼び出し規約で合わせて置かないと、うまくカーネル側で受け取れないので結構な時間ハマることになる。普通にハマった。呼び出し規約については「MikanOSをRustでやってますが、しんどい。」の記事に書いてあるので省略する。


みかん本でもUEFIのファイル開くところは深く触れられてないので、正直ここを理解しつつ移植するのは骨が折れた。
ファイル読み込み関数(open_file)でやっていること
このあたりはUEFI規格書に書いてあるが、ライブラリのほうと整合させるのに苦労した。簡潔すぎてドキュメントの読み方がわからん。
  1. LoadedImage ProtocolをBootServicesのhandle_protocolメソッドで取得する
  2. 取得したLoadedImage Protocolのインスタンスのdeviceメソッドを叩くとデバイスオブジェクトが取得できる(どのデバイスを開くか)
  3. SimpleFileSystem Protocolの引数に2で取得したデバイスオブジェクトを引数にすると、ファイルシステムが開ける
  4. SimpleFileSystemオブジェクトのopen_volumeを叩くとディレクトリオブジェクトが取れる
  5. ディレクトリオブジェクトを使って「ファイルハンドル」オブジェクトを取得する
    1. なお、ここで取得している「ファイルハンドル」はファイルだけでなく「ディレクトリ」である可能性もあるので本当はオブジェクトの種別をちゃんとチェックしないといけない。現状は決め打ちでやっている。
  6. ファイルオブジェクトをRegularFileオブジェクトに変換する
  7. RegularFileオブジェクトを返却する
というのが、open_file関数でやっていること。
かつ、その上位にあるFileReaderWriterはRegularFileオブジェクトをラップするオブジェクトになっている。

殆どは、RegularFileオブジェクトに存在するメソッドを叩いているだけだが、ファイルサイズ取得メソッド(get_size)だけは少し毛色が違うので大きめになっている
具体的にやっていること
  1. get_infoメソッドを使い、FileInfoを取得する。(公式ドキュメントがわかりづらすぎる件。)
    1. このメソッドは、確保しているバッファが少ないと、失敗したときにerrorオブジェクト内に必要なバッファサイズを返却するという仕様。
    2. とりあえず動的にバッファを確保してFileInfoをしまうバッファを作った。
      1. そして書いてる途中に気づいたけど、確保したメモリを開放してないからメモリリークしてる。だめじゃん
  2. 取得したFileInfoオブジェクトのfile_sizeメソッドを呼び出してファイルサイズを取得している。

これでようやく、カーネルをロードできるようになった。

あとは「MikanOSをRustでやってますが、しんどい。」の記事に習って、ロードするだけ。

で、色々やりたいことをやればよろしい。

UEFIで動くHSV色空間からRGBに変換する部品は2年くらい前に作ってあったので、それを動かしてみた。

モジュール分割してみる

とりあえずローダとカーネルでファイルが分かれているので分割したい。
でも分割するのはファイルだけで、共通部分は共通で使いたい。
なので以下の分割方針を取ることにした
  • ブートローダ:バイナリクレート
  • カーネル:バイナリクレート
  • その他共通部品:ライブラリクレート
    • アセンブラ関数やフレームバッファと言ったブートローダやカーネルで共通に使うものを入れておくライブラリ
というわけで方針が決まったのでざっと分割すると今のリポジトリみたいな感じになる。

苦行だけど、できると楽しい。
神書籍だと思う。

おまけ

ライブラリクレートは、バイナリクレート経由でビルドされることによって、バイナリクレート側のビルド設定ファイルが適用されてビルドされる。
ただし、ビルドスクリプトはライブラリクレート固有のものになる。

つまりこういう事
ビルド設定:バイナリクレートの設定(build-stdなどのライブラリ設定)が継承される
ビルドスクリプト:ライブラリクレートのものが使われる

ライブラリクレートはUEFI側とカーネル側、つまるところ、PEファイルとELFファイル両方にリンクできる形式でビルドされなければならない。
とりあえず、PEファイルELFファイルに対してコンパイラとリンカを個別に使って解決することにした。

0 件のコメント:

コメントを投稿