第7回 IOAPICそしてHPET

つわけで、IOAPICとHPETのアドレスがとれたので、これを色々やるなー。タイマとして、HPETの説明と、それの割り込みの実践とあわせてIOAPICをやる。

http://d.hatena.ne.jp/tanakmura/20090604#1244139118
↑これを理解してないと、多分以下の文章は意味不明なので、一応読んでおくべきだと思うなー。まあ上リンク先の文章が理解できる文章だとは思えないがっ…


IPAPICについては、これ↓が、資料かなー。(以後、82093AA)
http://www.intel.com/design/chipsets/datashts/290566.htm

ただ、現在は既にICHの一部になってしまっておるなー。半導体集積技術勝利の瞬間であった。
(http://www.intel.com/assets/pdf/datasheet/307013.pdf の 10.5 らへん)


まず、IRQとはなんぞや、という説明をしておこう。IRQとは、詳しくはWikipediaを参照してくれ(多分。Wikipediaへのパケットはファイアーヲールで弾かれるようになっているため、何が書いてあるかは知らない)。

いや、実はうちもあんまりIRQとINTNの区別はついてないが…組み込み屋失格の瞬間であった。
(…よくわからないのでうやむやにして書いておこう)


82093AAのP15を見ていただきたい。これのINTNとかなってるピンに、ピコっと電気を入れてやると、それがIRQの発生の瞬間であった。
といっても、説明を省略しすぎだがー。ちゃんとした文章になるときはここにエッジトリガや、ハイアクティブの話が入るはずなのだ。

めんどいので、エッジトリガ、ハイアクティブ固定で話を進めるとしておくぅー。

IOAPICは中に、redirect tableというのが入っておって、そのテーブルの一個一個のエントリがIRQと対応しておるのよ。対応はそのまま、IRQ 9とかだと、9番目のテーブルエントリ、といった感じね。
説明は、82039AAのP11あたりを各自読んでおいてね!(ちゃんとした文章になるときはここにちゃんと話が入るはずなのだわ)

で、この、エントリの下位8ビットで表現される、0-255の値が、そのまま、x86割り込みベクタの値に対応するわ。Desitination Field[63:56]で指定したAPIC IDを持つLocal APICに、Delivery Mode[10:8]で指定した方法で、Interrupt Vector[7:0]で指定した番号のx86割り込みが入る、という感じですわね。
その他の機能については…内緒よ…。調べてないのでわからないとも言うわね。


あと設定方法なんやけど、各レジスタには、番号が付いておって、Index Register(ICH7では0xFEC00000)にその番号を、Data Register(ICH7では0xFEC00010)にデータを読み書きすることでレジスタへの読み書きができるんよ。覚えといてや。
リダイレクトテーブルのエントリは、24個あって、2ワード(64bit)づつ0x10 - 0x3fまで配置してあるよ。この文章をうまく解読して、理解してや。


IRQとハードウェアの割り当てについては、うまくAir Readingして、把握するしかなく、適当に資料を読み当たるしかない。


さて、理解できたかな?上の文で理解できたという人はエスパーだと思うので、今すぐこちらの電話番号まで→ 0x-fec0-0010 おかけまちがいのないよう!



次にHPETだなー。High Precision Event Timerなので、個人的には、「HPなET」であるべきで、「HなPET」で区切るのは変だと思うのだが…まあ、ネタの都合上しかたないのである。内容の正確性よりもネタになることを優先した瞬間であった。

「High Precision」とか付いてるが、あまり意味はなくて、普通にタイマだと思えばよいなー。(PCはレガシな機能がたくさんあるので、AdvancedとかHigh Precisionとか付けて区別しないとやってられないのだ)

普通にタイマなので、ワンショットと周期モードがあるなー。ただ、PCのレガシなPITである8254とはちょっと違って、ワンショットでも正確に時間を計測できるというのがある。


まず、HPETには、ひとつのカウンタと、みっつの比較器があるなー。みっつの比較器はそれぞれ独立して動いてて、カウンタと値が一致したときに、それぞれ割り込みを発生できるのよ。
ほんで、割り込みがかかってもカウンタは止まらない仕組みになってて、比較器の値を更新していくことで、ワンショットで動かしても正確に時間がとれるようになっておる。割り込みが発生しただけてカウンタが止まっちゃう8254と違うんです。

例えば10tickごとに割り込みをかけたければ、
1. カウンタを0に。コンパレータを10に
2. カウンタが10になる。コンパレータと一致するのでここで割り込み
3. カウンタが多分増えてる。コンパレータを20に
4. カウンタが20になる。割り込み
つう感じだぞ。


こんな感じに、カウンタは増えていくだけというようになっているので、周期タイマの挙動も8254とは違うなー。何が違うかというと、コンパレータの横に増加量レジスタとAdder(あだ〜)が付いてて割り込みごとにかってにコンパレータの値が加算されるようになっておるな。
実に、昔は巨大であった64bit Adderが今はタイマの片隅に付いておるという時代である。半導体集積技術勝利の瞬間であった。


設定の仕方であるが、面倒になってきたので適当に書いておくと、適当にメモリマップされてるレジスタに値を書くとなんかうまく動くのだった。


さて、それでは割り込みを入れよう。
HPETの割り込みにはふたつのモードがあって、Legacy Replacement的なアレがあるなー。Legacy Replacementにすると、PC/ATのPITと、RTCの割り込みが発生しなくなって、そこで使われてたIRQ2とIRQ8が使えるようになるというものである。ちなみに通常は、PCIの割り込みと共有してて、16-23番が使える(真面目に調べるならINT_ROUTE_CAPのビットを見る)。
もちろん、我々はレガシィなシステムに興味は無いので、8254PITなんて死んでしまえといわんばかりに当然のごとくLegacy Replacement Modeを洗濯するのだった。


で、とりあえずTimer 0を使おう。IO APIC使ってて、Legacy Replacement時は、IRQ 2番だ。IO APICのリダイレクトテーブル2番を設定する。そして、CPUのIDTを設定する、そして、HPETの割り込みを有効にする、そして、IOAPICのテーブルエントリのマスクを外す、そして、HPETのコンパレータを設定、そして、HPETのカウンタを設定、そして、HPETを起動するっ…なんて長いみちのりだぜ…。


上の説明でわかる人がいるとは思えないのだった。
一応こんな感じ的的な図を。


あと、以上を確認するプログラムなー
hpetintコマンドを撃つと、指定した秒数後にHPETから割り込みが入るんです。