作りながら学ぶOSカーネル―保護モードプログラミングの基本と実践
を、暫く前からだらだらと読み進めていて、ようやく4章の割り込みの所に到達した。
で、PIC(Programmable Interrupt Controller)の初期化と割り込みハンドラのソースコードの部分で基礎的な知識が無かった故に「??」となってしまった部分があるのでメモしておく。
103pからの「ソース4-6 kernel2.asm」で、デフォルトの割り込みハンドラ"isr_ignore"とTimer用割り込みハンドラ"isr_32_timer"で、
MOV AL, 0x20 OUT 0x20, AL
というのが出てくるが、これの解説がどうも載っていない。コメントも無い。
また100pから始まる「ソース4-5 boot2.asm」にもPICの初期化処理で"OUT"命令が出てくるが、そのIOポートが次の4種類出てくる。
OUT 0x20, AL OUT 0x21, AL OUT 0xA0, AL OUT 0xA1, AL
この違いについて調べてみた。本の解説にもあるとおり、割り込みはIntelの8259Aというコントローラ(とその互換chip)が担当しているとの事で、PIC 8259Aを中心に色々Googleに訊いてまわる事になった。
8259Aの物理的な構造としてD0-D7pin, A0pinが今回の件に関連している。PICの初期化や内部レジスタの操作にはA0pin, D0-D7pinで行う。データバスを介して行われるPICの処理命令(Command Words)とA0pinの関係についてまずまとめる。初期化処理系(ICWx)は一度のみ、操作処理系(OCWx)は初期化完了後は何度でも呼べる。
前述のboot2.asm, kernel2.asmで使われているのはICW1-4, OCW1-2である。それぞれをOUTで指定する時のIOポートアドレスをA0pinの状態と比べてみる。
ICW1 | A0=0 | OUT 0x20/0xA0 |
ICW2 | A0=1 | OUT 0x21/0xA1 |
ICW3 | A0=1 | OUT 0x21/0xA1 |
ICW4 | A0=1 | OUT 0x21/0xA1 |
OCW1 | A0=1 | OUT 0x21/0xA1 |
OCW2 | A0=0 | OUT 0x20/0xA0 |
このように、0x20/0xA0ならA0=0, 0x21/0xA1ならA0=1というように対応している。
結論から言うと、このA0pin/D0-D7pinとIOポート番号の対応はBIOSのPOST処理により設定される。
命令の種類として、A0pin = 0 のものは主に初期化処理や内部レジスタの状態を変更する命令、 A0pin = 1 のものは内部レジスタの値を直接操作したり、アドレスoffsetを操作する透過的なデータ操作命令として、以下のような分類にしているようだ。A0pinのON/OFF切り替えはCPU内部で行われているものと思われる(*1)。
0x20/0xA0 | 初期化命令, EOI指示(ICW1, OCW2, OCW3) |
0x21/0xA1 | マスク/値の設定(ICW2, ICW3, ICW4, OCW1) |
ICW4は追加命令なので0x20/0xA0の方がふさわしいように思えるが、それ以外はおおむね分類として合っているように思われる。
ここで、boot2.asm, kernel2.asm のソースに戻って見直してみる。
まずboot2.asmでは、ICW1-4の設定を行うところは順に 0x20/0xA0(ICW1), 0x21/0xA1(ICW2-4) へOUTしている。
また
mov al, 0xFF ; スレーブPICの全ての割り込みを out 0xA1, al ; 防いでおく。 dw 0x00eb, 0x00eb mov al, 0xFB ; マスターPICのIRQ2番を除いた、 out 0x21, al ; 全ての割り込みを防いでおく。
ここの部分は OCW1、mask設定になる為これも 0x21/0xA1へのOUTとなっている。
kernel2.asmの無限ループへ入る直前、
mov al, 0xFE ; 防いでおいた割り込み中、 out 0x21, al ; タイマーだけを有効にする。
こちらもOCW1, mask設定で 0x21へのOUTとなっている。
今回の調査に駆り立てたきっかけである、isr_ignore, isr_32_timerの次の箇所を読み解いてみる。
mov al, 0x20 out 0x20, al
まず0x20へのOUTで、0x20というコマンド値は OCW2, EOI指定である。8259Aの仕様では0x20は「非指定EOI」である。
ここでEOIの意味について簡単にまとめておく。
8259Aの内部レジスタには以下の3つがある。
PICはEOIを受け付けると、該当するISRビットを"0"にする。これにより、次の割り込みが受け付けられるようになる。EOIには次の3種類がある。
boot2.asmのICW4ではAEOIビットがズバリ、"0"になっている。このため割り込みハンドラの中でOCW2, EOIコマンドを発行する必要があった。非指定EOIを使っているのは、割り込み番号を特に指定する必要のない小規模な実験コードだからだろう。
IOポート番号や8259Aの仕様など、非常にハード寄りの部分の調査になった為、それなりに大変だった。ここまで調べたが、かといって8259Aの仕様を完全に理解したのかと言われると否。今回はA0pinとIOポート番号のmappingがどこで行われているのかを最優先にして資料を流し読みしたため、ICWやOCWの各命令詳細やPICの内部ダイアグラムまで追っかけたわけではない。
486アーキテクチャなどのハード系はインターネットが一般に広まる1995年以前に完成していた為、Google先生に訊いてもなかなか日本語で分かりやすい資料が見つからず、結局英語資料の方が役に立った。
しかし近年のアーキテクチャではAPICとしてCPUのコアに統合されているらしく、これを使うと数十の割り込みコントローラを連結させたりする事も出来るらしい。非常に奥が深い世界なのだと思い知らされた。