2012-02-14 17 views
2

IDTを介してカーネル割り込みを処理しようとしています。 私はLinux上でIntel x86を使用しています。x86:割り込みハンドラループ

私は自分のIDTと私の割り込みエントリを設定して、割り込みハンドラを表示するためにいくつかのテストを開始しました。

int $0x0を試してみると、私のハンドラが呼び出されますが、プッシュされたエラーコードでいくつかの例外を試すと、無限ループに入ります。

スキーマは以下の通りです:例外が到着すると

、私のハンドラの最初の部分は、ASMにあり、一般的なCの一部を呼び出します。

my_handler.c

void handler(int i) 
{ 
    printf("Exception %d caught\n", i); 
} 

my_handlers.S

common: 
    pushal 

    pushl %ds 
    pushl %es 
    pushl %fs 
    pushl %gs 

    addl $48, %esp     // 4 4-bytes segments pushed 
            // + 8 4-bytes registers (pushal) 
`         // esp points on exception code 

    call handler     // call the C handler with exception code 

    subl $48, %esp 

    popl %gs 
    popl %fs 
    popl %es 
    popl %ds 

    popal 

    addl $8, %esp     // 4-byte error code + 4-byte exception number 
    iret 


exception_de_handler: 
    pushl $0      // Fake error code 
    pushl $0      // interrupt number 
    jmp common 

exception_gp_handler: 
    // error code is pushed by µproc. 
    pushl $13      // interrupt number 
    jmp common 

exception_pf_handler: 
    // error code is pushed by µproc. 
    pushl $14      // interrupt number 
    jmp common 

私はfollowigコードを実行しようとした場合:

それは動作します
int* a = 0x0; 
*a = 42; 

、exceutionは*a = 42;

後に再開

しかし、私はしようとした場合:

int* a = 0x0; 
*a = 42; 
*a = 1337; 

それは無限ループに入る:

Exception 14 caught 
Exception 13 caught 
Exception 13 caught 
Exception 13 caught 
Exception 13 caught 
     ..... 
Exception 13 caught 
Exception 13 caught 
Exception 13 caught 
     ..... 

は、なぜ最初の例外ページ違反(14)は、一般保護(13)にループし、その後処理されますか?

ありがとうございました。

+1

LinuxでIDTを手動で操作しているのはなぜですか?それはおそらくうまく終わることはできません。割り込みのためにLinuxのデバイスドライバAPIを使用する必要があります。 (カーネルモードから 'printf'をどのように呼び出すことができるかわかりません - あなたが実際に行ったことの詳細な説明が必要な場合があります) – zwol

+0

@Zack - 良い点 – Stewart

答えて

2

私はあなたのスタックを台無しにしていると思います。あなたは、割り込みハンドラであなたのスタックで何をするかについて非常に注意する必要があります。スタックのすべての方法を巻くために、スタックポインタに0x48を追加

プッシュエラーコード(CPUで行うことができます) プッシュREGS プッシュセグメントREGS

- :この場合、それはあなたが以下のかと思われますエラーコードを指し示すようにバックアップします。

は、あなたのC関数を呼び出す

これは、セグメントレジスタが中に保存したあなたのスタックの一部を「解放」されることが有効である何を。実際には、あなたもCの関数で心配する必要はありません。すべてはリターンアドレスがコール命令でスタックにプッシュされ、あなたがCコールに入る前にdsとesのレコードを吹き飛ばすからです。 Cコールから戻ってくるときは、コールスタックを整理しようとしますが、あなたはそれをうまくやっていません。なぜなら、あなたはすでにそれを混乱させているからです。 (ハンドラが_cdecl呼び出し規約を使用していると仮定します)。

これにより、dsの偽の値がポップされます。これをdsにロードすると、CPUはGDTに対して値をチェックし、無効であることを検出します。現時点では、GPF(例外13)が発生しています。ある程度、スタックを回復し(CPUはあなたのためにSSを探しています)、dsの古い値を残します - 実際にdsを変更することは決してありません。これによりprintfを再び実行することができます。

スタックを整列することについてもっと注意する必要があります。スタックポインターに追加するたびに、その範囲にあったデータは永遠に失われたとみなす必要があります。予期せぬ割り込みが、あなたをトリップさせようとしています。

関連する問題