Zephyr×Rustのインテグレーションにチャレンジ!⑧~続システムコール調査~

はじめに

ZephyrとRustのインテグレーションに挑戦しています。

これまでで、アプリケーションをRustで書いてきました。 ここからは、Driverを書く方法を調査していきます。

前回までのあらすじ

アプリケーションに公開するDriver APIを作るため、システムコールの実装を調査しています。 システムコール実装のために必要な要素のうち、__syscallプレフィックスのついたプロトタイプ宣言について調査しました。

  • __syscallプレフィックスのついたプロトタイプ宣言。ビルドプロセス内でgen_syscall.pyによって処理されます。
  • 実装関数。実際のシステムコール実装です。
  • ハンドラ関数。引数を検証してから、実装関数に渡すためのラッパーです。

実装関数

実装関数を作る際に気を付けることは、名前の付け方です。 _impl_プレフィックスシステムコールAPI、にする必要があります。

プロトタイプ宣言は自動生成されるため、自分でプロトタイプ宣言を作る必要はありません。

ハンドラ関数

ハンドラ関数は、ユーザースレッドがシステムコールを発行した場合、kernel側で動作します。 ユーザースレッドがスーパーバイザモードに移行するため、ソフトウェア割り込みを発生させると、共通のシステムコールエントリポイントは、システムコールIDを使って、適切なハンドラ関数を見つけて、ジャンプします。

ハンドラ関数の目的は、与えられた引数を検証することです。

  • kernel objectポインタが与えられた場合、システムコールを呼び出したスレッドが、パーミッションを持っているかどうか、検証します。
  • ユーザー空間から与えられたバッファの場合、システムコールを呼び出したスレッドが、読み/書きのパーミッションを持っているかどうか、検証します。
  • そのほかの引数では、値が制限範囲内にあるかどうか、検証します。

引数検証

引数を検証するためのマクロが定義されています。 いくつか紹介します。

  • Z_SYSCALL_OBJ()。kernel objectが期待した通りの型か、呼び出しスレッドは適切なパーミッションを持っているか、初期化されているか、を検証します。
  • Z_SYSCALL_MEMORY_READ()。指定サイズのメモリバッファ全体に対して、読み込み権限があるかどうか、を検証します。
  • Z_SYSCALL_MEMORY_WRITE()Z_SYSCALL_MEMORY_READ()と似ていますが、書き込み権限があるかどうか、を検証します。

検証に失敗した場合、Z_OOPS()が呼び出され、呼び出しスレッドをkillします。

ハンドラの宣言

_SYSCALL_HANDLER()マクロを使って宣言できます。

Z_SYSCALL_HANDLER(k_sem_init, sem, initial_count, limit)
{
    ...
}

ということで、既存のシステムコールを利用するならともかく、新しいシステムコールをRustで作ろうとすると、苦労しそうですね…。