[PR]DoCoMoご利用の方必見!:無料の運命鑑定≪スピリチュアルの館≫

32bit割り込みハンドラの設定では、 割り込みハンドラのインストール方法を説明しました。
では、割り込みハンドラはどのように実装すれば良いのでしょうか?
これを実装するには、まず割り込み発生時にCPUが行う処理を 知る必要があります。

今回はDPLを0に設定した 32bitの割り込みゲートに限定して説明します。
割り込みが起きたときのCPUのモードにより、 呼び出されたときのスタックの内容は変わります。
CPUのモードは次の3種類です。

割り込みが起きたときの スタックの内容は次の表のとおりです。
呼び出し元のモードは、エラーコードがなければ EFLAGS.VMとCSの下位2bitで判断できます。

スタックアドレス対象内容
(+00h)(kernel,user,v86)(一部例外の場合のみ)エラーコード
+00hkernel,user,v86呼び出し元のEIPです。
+04hkernel,user,v86呼び出し元のCSです。
+08hkernel,user,v86呼び出し元のEFLAGSです。
+0Chuser,v86呼び出し元のESPです。
+10huser,v86呼び出し元のSSです。
+14hv86呼び出し元のESです。
+18hv86呼び出し元のDSです。
+1Chv86呼び出し元のFSです。
+20hv86呼び出し元のGSです。


各モードでCPUが行う処理は次のとおりです。

  • カーネル内部(CPL=0)から呼び出される場合
  • ユーザプロセス(CPL>0)から呼び出される場合
  • 仮想86モード(EFLAGS.VM=1)から呼び出される場合

  • どの状況から呼び出された場合でも、 IRET(IRETD) を使えば呼び出し元に戻ることができます。
    逆に、CPL=0のときに、スタックの内容をユーザプロセスや仮想86モードの内容に あわせてから IRET を実行すれば、好きなモードに切り替えることができます。

    割り込み発生時のCPUの処理がわかりましたので、 割り込みハンドラを書いてみることにします。
    いくつかの例を示します。
    はじめの例は、タイマ割り込み(IRQ0)のたびに _timer_handler を呼び出すハンドラです。
    呼び出し元の種類にかかわらず処理が変わらないハンドラであれば、 レジスタを保存し、必要な処理を行い、最後にIRETを実行すれば 良いことになります。

    これで、timer_handler は C++ で書くことができます。
    PIC への EOI は _timer_handler 内で送る必要があります。
    最初の cld がないと、C++ で書いた場合に memcpy 等が正しく動かなくなります。 これがなくても大抵の状況では動いてしまうため、 入れ忘れると再現しにくいバグの原因になります。
    DS,ESに設定している10hはデータ用のセレクタです。 データ用のセレクタとして別の値を使っている場合は、 ここを書き換える必要があります。

    次の例は、#GP(0)のハンドラです。

    タイマの例と同様に、gpfault_handlerもC++で書くことができます。
    gpfault_handlerはスタックの内容にアクセスする必要がありますので、 途中にある push esp でスタックのアドレスをpushしています。
    gpfault_handlerのプロトタイプは次のとおりです。 gpfault_handlerの引数からスタックにアクセスできます。
    次の例は、割り込みハンドラではなく 仮想86モードへの切り替えを行うコードです。
    仮想86モードを使うには、このモード切り替えに加えて #GP(0)のハンドラを適切に実装する必要があります。
    EAX レジスタの値を破壊したくない場合は 多少の書き換えが必要になります。



    2005-09-11: ページ作成 (最終更新)
    Copyright (C) 2005 hoverkid, all rights reserved.
    [PR]初回鑑定無料キャンペーン中!:多ジャンルのプロ占い師による本格鑑定。