プロセス1実行編⑤(システムコール) alltraps (Xv6を読む~OSコードリーディング~)
前回
jupiteroak.hatenablog.com
トップページ
jupiteroak.hatenablog.com
trap.asm(一部抜粋)
https://github.com/mit-pdos/xv6-public/blob/master/trapasm.S#L5
#include "mmu.h" # vectors.S sends all traps here. .globl alltraps alltraps: # Build trap frame. pushl %ds pushl %es pushl %fs pushl %gs pushal # Set up data segments. movw $(SEG_KDATA<<3), %ax movw %ax, %ds movw %ax, %es # Call trap(tf), where tf=%esp pushl %esp call trap ....
alltrapsサブルーチンは、割り込み・例外ハンドラに相当する処理の一部です。
alltrapsサブルーチンは、割り込み・例外ハンドラの開始手続きを行います(割り込み・例外が発生したプロセスで使用されていたハードウェアコンテキストの退避を完了させ、割り込み・例外・システムコールサービスルーチンを呼び出します)。
処理の内容
ユーザモードで使用していたハードウェアコンテキストの退避を完了させる
.globl alltraps alltraps: # Build trap frame. pushl %ds pushl %es pushl %fs pushl %gs pushal
pushl %ds
push命令により、ユーザモードで使用していたdsレジスタの値をカーネルスタックに退避させます。
pushl %es
push命令により、ユーザモードで使用していたesレジスタの値をカーネルスタックに退避させます。
pushl %fs
push命令により、ユーザモードで使用していたfsレジスタの値をカーネルスタックに退避させます。
pushl %gs
push命令により、ユーザモードで使用していたgsレジスタの値をカーネルスタックに退避させます。
pushal
pushal命令により、ユーザモードで使用していた汎用レジスタ(eax ecx edx ebx esp ebp esi edi)の値をカーネルスタックに退避させます。
以上の処理をもって、ユーザモードで使用していたハードウェアコンテキストの退避が完了します。
カーネルスタックの状態
下位アドレス側 | |
ユーザモードで使っていたediレジスタの値 | |
ユーザモードで使っていたesiレジスタの値 | |
ユーザモードで使っていたebpレジスタの値 | |
ユーザモードで使っていたespレジスタの値 | |
ユーザモードで使っていたebxレジスタの値 | |
ユーザモードで使っていたedxレジスタの値 | |
ユーザモードで使っていたecxレジスタの値 | |
ユーザモードで使っていたeaxレジスタの値 | |
ユーザモードで使っていたgsレジスタの値 | |
ユーザモードで使っていたfsレジスタの値 | |
ユーザモードで使っていたesレジスタの値 | |
ユーザモードで使っていたdsレジスタの値 | |
64(割り込みベクタ番号の値) | |
0(ハードウェアエラーコードの代わりの値) | |
ユーザモードで使っていたeipレジスタの値 | |
ユーザモードで使っていたcsレジスタの値 | |
ユーザモードで使っていたeflagsレジスタの値 | |
ユーザモードで使っていたespレジスタの値 | |
ユーザモードで使っていたssレジスタの値 | |
上位アドレス側 |
カーネルモードへの移行を完了させる
movw $(SEG_KDATA<<3), %ax movw %ax, %ds movw %ax, %es
movw $(SEG_KDATA<<3), %ax
カーネルデータセグメントを指定するセレクタ値SEG_KDATA<<3をaxレジスタにセットします。
axレジスタを使って、カーネルデータセグメントを指定するセレクタ値SEG_KDATA<<3をdsレジスタとesレジスタにセットします。
movw %ax, %ds
axレジスタにセットされたカーネルデータセグメントを指定するセレクタ値(SEG_KDATA<<3)をdsレジスタにセットします。
movw %ax, %es
axレジスタにセットされたカーネルデータセグメントを指定するセレクタ値(SEG_KDATA<<3)をesレジスタにセットします。
割り込み・例外ハンドラ(への入り口となる処理)へ制御が移る時に、IDTのエントリ64から取得したセレクタ値SEG_KCODE<<3(tvinit関数内のSETGATEマクロの第三引数(sel)で指定した値)がcsレジスタにセットされるので、CPUの現行特権レベルはすでにカーネルモード(CPL = 0)になっていましたが、ds、esレジスタにSEG_KDATA<<3をセットしたことで、カーネルモードへの移行が完了したとみなせます。
割り込み・例外・システムコールサービスルーチンを呼び出す
pushl %esp call trap
pushl %esp
push命令により、espレジスタの値をカーネルスタックに退避させます。
push命令を実行する前のespレジスタの値は、カーネルスタックのトップアドレスの値であり、ユーザーモードで使用していたハードウェアコンテキストのフレームにおける先頭アドレス値と一致しています。
ユーザーモードで使用していたハードウェアコンテキストのフレームにおける先頭アドレス値は trap関数の引数として利用するので(ユーザーモードで使用していたハードウェアコンテキストをtrap関数で参照するので)、push命令を実行して、espレジスタの値(ユーザーモードで使用していたハードウェアコンテキストのフレームにおける先頭アドレス値)を退避させてから、trap関数(割り込み・例外・システムコールサービスルーチンに相当する処理)を呼び出します。
pushl %espを実行する前のカーネルスタックの状態
下位アドレス側 | |
ハードウェアコンテキストフレームの先頭アドレス | ユーザモードで使っていたediレジスタの値 |
ユーザモードで使っていたesiレジスタの値 | |
ユーザモードで使っていたebpレジスタの値 | |
ユーザモードで使っていたespレジスタの値 | |
ユーザモードで使っていたebxレジスタの値 | |
ユーザモードで使っていたedxレジスタの値 | |
ユーザモードで使っていたecxレジスタの値 | |
ユーザモードで使っていたeaxレジスタの値 | |
ユーザモードで使っていたgsレジスタの値 | |
ユーザモードで使っていたfsレジスタの値 | |
ユーザモードで使っていたesレジスタの値 | |
ユーザモードで使っていたdsレジスタの値 | |
64(割り込みベクタ番号の値) | |
0(ハードウェアエラーコードの代わりの値) | |
ユーザモードで使っていたeipレジスタの値 | |
ユーザモードで使っていたcsレジスタの値 | |
ユーザモードで使っていたeflagsレジスタの値 | |
ユーザモードで使っていたespレジスタの値 | |
ユーザモードで使っていたssレジスタの値 | |
上位アドレス側 |
pushl %espを実行した後のカーネルスタックの状態
下位アドレス側 | |
trap関数の引数値=ハードウェアコンテキストフレームの先頭アドレス値 | |
ハードウェアコンテキストフレームの先頭アドレス | ユーザモードで使っていたediレジスタの値 |
ユーザモードで使っていたesiレジスタの値 | |
ユーザモードで使っていたebpレジスタの値 | |
ユーザモードで使っていたespレジスタの値 | |
ユーザモードで使っていたebxレジスタの値 | |
ユーザモードで使っていたedxレジスタの値 | |
ユーザモードで使っていたecxレジスタの値 | |
ユーザモードで使っていたeaxレジスタの値 | |
ユーザモードで使っていたgsレジスタの値 | |
ユーザモードで使っていたfsレジスタの値 | |
ユーザモードで使っていたesレジスタの値 | |
ユーザモードで使っていたdsレジスタの値 | |
64(割り込みベクタ番号の値) | |
0(ハードウェアエラーコードの代わりの値) | |
ユーザモードで使っていたeipレジスタの値 | |
ユーザモードで使っていたcsレジスタの値 | |
ユーザモードで使っていたeflagsレジスタの値 | |
ユーザモードで使っていたespレジスタの値 | |
ユーザモードで使っていたssレジスタの値 | |
上位アドレス側 |
call trap
call命令によって、trap関数(割り込み・例外・システムコールサービスルーチンに相当する処理)を呼び出します。
execのシステムコールの成功・失敗に関わらず、execのシステムコールの処理が終わったら、trap関数から制御が戻ってきます。
カーネルスタックの状態
下位アドレス側 | |
リターンアドレスの値 | |
trap関数の引数値=ハードウェアコンテキストフレームの先頭アドレス値 | |
ハードウェアコンテキストフレームの先頭アドレス | ユーザモードで使っていたediレジスタの値 |
ユーザモードで使っていたesiレジスタの値 | |
ユーザモードで使っていたebpレジスタの値 | |
ユーザモードで使っていたespレジスタの値 | |
ユーザモードで使っていたebxレジスタの値 | |
ユーザモードで使っていたedxレジスタの値 | |
ユーザモードで使っていたecxレジスタの値 | |
ユーザモードで使っていたeaxレジスタの値 | |
ユーザモードで使っていたgsレジスタの値 | |
ユーザモードで使っていたfsレジスタの値 | |
ユーザモードで使っていたesレジスタの値 | |
ユーザモードで使っていたdsレジスタの値 | |
64(割り込みベクタ番号の値) | |
0(ハードウェアエラーコードの代わりの値) | |
ユーザモードで使っていたeipレジスタの値 | |
ユーザモードで使っていたcsレジスタの値 | |
ユーザモードで使っていたeflagsレジスタの値 | |
ユーザモードで使っていたespレジスタの値 | |
ユーザモードで使っていたssレジスタの値 | |
上位アドレス側 |