カーネルを自作していますが、最初のページエラー割り込みハンドラのあと、IRETを実行すると割り込み13(一般保護)が発生し、エラーコードは0x18です。何が問題なのかわかりません。スタックにプッシュされたコンテンツはCPUからのものです。
割り込みが発生したときのレジスタの状態と、レジスタが格納されていたメモリは次のとおりです。さらに、ページ エラー割り込みハンドラから IRET が返されます。
%ESP が IRET 実行前と割り込み発生前と同じであることは確かです。
例外がIRET
それ自体からのものである場合IRET
、保存されたセグメントレジスタの1つを復元できない可能性がありますが、値(8または0x18、ところで?)はどういうわけか間違っています。保護モードでレジスタを(再)初期化したことがないIRET
か、GDTに何かが起こった前にハンドラーがレジスタを不正な値に設定したため、間違っている可能性があります...
編集ESP
:図から、ページフォールトハンドラーが実行前に例外コード(のアドレスの値4)を削除しなかったことがわかりIRET
ます。したがって、IRET
4をの新しい値EIP
、0x1000018をの新しい値CS
、0x23をの新しい値として解釈しますEFLAGS
が、これら3つのレジスタには0x1000018、0x23、および0x3206を使用する必要があります。明らかに、データセグメントセレクター(0x1000018は0x0018への切り捨て後と解釈されます)をロードできずCS
、これにより#GP(0x18)が発生します。
アレクセイの拡張:
一部の割り込みが発生すると (他の割り込みは発生しません)、4 バイトのエラー コードが自動的にスタックにプッシュされます。ページ フォールトもその 1 つです。
このエラー コードには、割り込みに関する追加情報が含まれています。
Intel Manual Volume 3 System Programming Guide - 325384-056US 2015 年 9 月表 6-1. 「プロテクト モードの例外と割り込み」列の「エラー コード」では、どの割り込みがエラー コードをプッシュし、どの割り込みがプッシュしないかを正確に示します。
38.9.2.2「ページ・フォルト・エラー・コード」では、エラーの意味を説明しています。
したがって、次のいずれかが必要になります。
pop %eax
/* Do something with %eax */
iret
または、エラー コードを無視する場合:
add $4, %esp
iret
最小限の例については、このページ ハンドラーを参照して、pop
.
上記を、スタックをポップする必要のない分割エラー例外と比較してください。
int $14
単純に を実行すると、余分なバイトがプッシュされないことに注意してください。これは実際の例外でのみ発生します。
これに対処する適切な方法は、0
これを行わない割り込みのスタックにダミーのエラー コードをプッシュして、物事を統一することです。James Molloy のチュートリアルはまさにそれを行います。
Linux カーネル 4.2 も同様のことをしているようです。arch/x86/entry/entry64.S の下では、次のように割り込みをモデル化しhas_error_code
ます。
trace_idtentry page_fault do_page_fault has_error_code=1
次に、同じファイルで次のように使用します。
.ifeq \has_error_code
pushq $-1 /* ORIG_RAX: no syscall to restart */
.endif
と押しhas_error_code=0
ます。
関連する質問:割り込みハンドラから戻る前に、特定の例外によってスタックにプッシュされたエラー コードをポップする必要がありますか?