2010年6月23日水曜日

x86のSEPはPAEが有効になっていないと使えない

linuxではx86のコールゲートに絡む箇所をどんな風に実装してるのかと思って調べてたら、
linux-gate.so.1って仮想dsoに辿り着いた。
ここに書かれているように、ユーザプロセスが発行するシステムコール呼び出しは全てvsyscall()というカーネル関数を通って行われる。(コールゲートは使ってない)

ところで、Pentium Proからはシステムコール呼び出しの遅さを改善する機能が追加されていて、割り込みを使った場合よりも高速に処理できるsysenter/sysexit命令が使える。

/proc/*/maps でメモリマップをみてみると、[vsdo]とマークされた領域があってここにはlinux-gate.so.1がマッピングされておりvsyscall()のマシンコードがある。実際に適当なプロセス(試しにruby)を起動させてgdbでアタッチして該当のメモリ空間を覗いてみたらvsyscall()内でシステムコール呼び出ししてる箇所がある。

でも、sysenter じゃなくて、int 0x80 となってた。

/proc/cpuinfo で確認すると cpuの機能一覧(自分のマシンは core 2 duo mobile T7250 )に "sep" がなく無効になっている。
サブのPentium 4 ではちゃんと sep が有効になっており、gdbで確認したところsysenter命令になってたから何かしらの原因がある。

いろいろ調べてたら↓のコードにたどり着いた。

カーネルソース arch/x86/kernel/cpu/common.c
#ifdef CONFIG_X86_32
/*
*  emulation of NX with segment limits unfortunately means
*  we have to disable the fast system calls, due to the way that
*  sysexit clears the segment limits on return.
*  If we have NX, then we don't need to do this.
*/
#ifdef CONFIG_X86_PAE
  if (!test_cpu_cap(c, X86_FEATURE_NX))
#endif
    clear_cpu_cap(c, X86_FEATURE_SEP);
#endif /*CONFIG_X86_32*/

カーネルのコンフィグを確認すると X86_PAE が無効になってたから、これが原因のよう。
HIGHMEM4G -> HIGHMEM64G に変更してリビルドしてカーネルを再起動してみたら sep が有効になったので間違いない。
gdbで__kernel_vsyscall()の逆アセンブルコードをみてみると、int 80 が sysenterになる。

では、paeとsepの関係性はなんだろう?