プロセス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レジスタの値
上位アドレス側


次回
jupiteroak.hatenablog.com