0x19f (Shinya Kato) の日報

主にプログラミング関連の話をします

【メモ】UNIX V6コードリーディング(割り込み, p154〜159, その2)

今回の内容

前回の続きから.
アセンブリで書かれたtrapcallを詳しく見ていきます.

0x19f.hatenablog.com

前回解決できなかった疑問点

nofaultが設定されている場合の割り込み箇所への復帰方法について, 前回の記事には,

もう一つわからないことがあって, この後のトラップハンドラからどうやってユーザープログラムに復帰するのかということ.

なんてことを書いたのですが, そもそも前提が間違ってました.
nofaultを設定した上で命令を実行するのはカーネルプロセスでした.
このnofaultというのは, カーネルプロセスが例外が起きるとまずい命令を実行する前に設定するというものみたいです.
ここからは推測ですが, こういった命令を実行する直前にカーネルnofaultにエラーハンドラを設定しカーネルスタックに戻り先のアドレスを積んだ状態で命令を実行するのではないかと.
すると, 例外が起きた場合には, スタックに戻り先のアドレスのみが積まれた状態でnofaultが指すアドレスにジャンプし, その処理の最後にrtsで戻り先に復帰するという風になるんじゃないでしょうか.

こっちの方はわかってないのですが, 読み進めていく上ではあまり問題なさそうなので一旦スルーします.

ここが若干謎で, SR0の1ビット目が1になるとMMUが有効になるらしい.
しかし, なぜこのタイミングでSR0に1を格納しているのかがわからない.

trapnofaultが設定されていない場合

nofaultに値が入っていない場合は, 前回軽く書いた通りSR0, SR2を退避, SR0を初期化してjsr命令でcall1へとジャンプします.
この時, 古いr0の値がスタックに積まれ, r0には_trap(C言語で書かれたtrap関数のアドレス)が格納されることに注意してください.

call1にジャンプすると, spをひとつ上にずらし, プロセッサ優先度を0にし, callの途中へとジャンプします.
ここからの処理は後述するcallでの処理とほぼ同じです.
callの1行目を実行した直後とスタック, r0の状態は同じ形になっている点に注意.

最終的なスタックの状態は以下のようになっており, r0にはtrap関数のアドレスが入っています.

sp トラップ種別を含んだPSW
古いr0
割り込まれたプロセスのpc
割り込まれたプロセスのPSW

callの処理

前々回ぐらいのおさらいになるのですが, callに処理が移った時のスタックの状態は以下のようになっています.

sp 古いr0
割り込まれたプロセスのpc
割り込まれたプロセスのPSW

ここにPSWの値が積まれて以下のような状態になります.

sp PSW
古いr0
割り込まれたプロセスのpc
割り込まれたプロセスのPSW

ここからはtrapnofaultが設定されていない場合と同じ処理になります.
スタックが同じような形をしているところに気をつけてください.

まず, 古いr1がスタックに積まれます.
次にmfpi命令を用いて割り込まれたプロセスのspをスタックに積みます.
mfpi命令はPSWの前のモードの仮想アドレス空間から値を取得して現在のモードのスタックに積む命令です.
ここで得たspを用いて割込みハンドラの中でスタックを操作するのかな?と予想しています.
さらに, SPWをスタックに積み, 下位5ビット以外を0でクリアします.

ここで, 割り込まれたプロセスがユーザーモードだったかカーネルモードだったかで処理が分岐するのですが, やっていることは大きくは変わらないです.

ユーザーモードだった場合は, jsr命令で現在のpcをスタックに積みながらr0に設定された割り込みハンドラにジャンプします.
ここで積んだpcが割り込みハンドラからの戻り先になります.
割り込みハンドラから処理が戻ると, コンテキストスイッチを行います.
割り込みによってプロセスの実行可能状態や優先度が変化した分を考慮するためのものかなと考えています.
最後に, mtpi命令で前モードのspの値を戻します.

カーネルモードだった場合は, PSWの前モードをユーザーモードに書き換えてから, 割み込みハンドラへジャンプします.
ここは上記と同様にjsrを用いています.

割り込みハンドラが呼び出される時のスタックは以下のような状態になります.
実際にはこれがハンドラ関数の引数として扱われます.
具体的にどのように使われるのかはclock, trapなどの関数を見ていくことになるかと思います.

sp pc(割り込みハンドラから返る先)
マスクされたPSW
割り込まれたプロセスのsp
古いr1
PSW
古いr0
割り込まれたプロセスのpc
割り込まれたプロセスのPSW

ここからは両モード共通です.
スタックに積んでいたr1, r0を復帰してrtt命令で割り込まれた箇所に復帰します.

まとめ

ソースコードを読むだけだと理解しづらいので, 簡単にまとめておきます.

  • カーネルは例外が起こりうる命令を実行する前にnofaultを設定することがある.
  • nofaultが設定されているトラップは設定されたハンドラによって処理される.
  • そうでないトラップはtrap関数によって処理される.
  • 割り込みはそれぞれの割り込みハンドラによって処理される.

次回の予定

割り込みハンドラ(clock)とトラップハンドラ(trap)を読んでいく予定.