読者です 読者をやめる 読者になる 読者になる

割り込み実験

自作OS

相変わらず割り込みがうまくいかない。

そこで、実験の足跡を残しておきたいと思う。きっともっと先に見返せばわかるかも?


#include "def.h"
#include "prot.h"

#define ADR_BOOTINFO 0x0ff0

BOOTINFO *binfo = (BOOTINFO *)ADR_BOOTINFO;

struct IDTR {
short limit;
int base;
}__attribute*1;

void Main() {
init_gdtidt(); // GDT/IDTの初期化
init_pic(); // PICの初期化
io_sti(); // 割り込み許可

init_palete(); // パレットの初期化
init_screen(binfo->scrnx, binfo->scrny);

draw_mouse();

io_out8(PIC0_IMR, 0xf8); // PIC1とキーボードを許可
io_out8(PIC1_IMR, 0xef); // マウスを許可

char s[30];

struct IDTR idtr = {0};
_sidt(&idtr);
lsprintf(s, "idtr.limit = %x", idtr.limit);
vprint(s, 0, 0, COL_FFFFFF);
lsprintf(s, "idtr.base = %x", idtr.base);
vprint(s, 0, 16, COL_FFFFFF);

while(1) {
io_hlt();
}
}

今のところ、boot.cがこんな感じ。

IDTR構造体や28〜33行目はあまり気にしないでほしい。%idtrの値が正しいか確かめていたんだけど、

LDTR命令で正しく設定されていることはわかったので、単にここで文字が表示されるのを確認する

だけになっている(笑)



注目するのは23行目。PIC0(マスター)に対して、0xf8(11111000)をマスクしている。

つまり、IRQ0〜IRQ2に割り込み許可をしている。

ここで、IRQ0はタイマ、IRQ1はキーボード、IRQ2はスレーブPICとのカスケード接続。

自作本ではマスクは0xf9(11111001)になっている。

今回タイマの割り込み許可をしたのは、タイマは定期的に絶対割り込んでくれることがわかっているから。

キーボードはカチカチ押してもQEMUの設定で割り込めてないのかよくわからないけど、

タイマは絶対割り込んでくれるので、割り込みは少なくとも起こる。



で、タイマ用にも割り込みハンドラを用意してあげる。






// IDTの設定
set_gatedesc(idt + 0x21, (int)inthandler21, 2 * 8, AR_INTGATE32);
set_gatedesc(idt + 0x2c, (int)inthandler2c, 2 * 8, AR_INTGATE32);
set_gatedesc(idt + 0x20, (int)inthandler21, 2 * 8, AR_INTGATE32);
面倒なので、タイマ用のハンドラはキーボード用に用意したものを流用。

さて、これでタイマが割り込めば…



Screenshot-QEMU-3.png



こうなるはず!

さあタイマよ、割り込め!







Screenshot-QEMU-1.png



Screenshot-QEMU-2.png



はい失敗(笑)



さて考察。



前者の方は単純にタイマから割り込みが来てないだけ。

割り込みが何も来ずにIDTRの情報が表示された。



後者は?



まず、boot.cでは23行目でタイマの割り込みが有効化されている。

つまり、これ以降、どのタイミングでタイマの割り込みが来るかは予期できない。

前者ではたまたま最後までタイマから割り込みが来なかっただけだ。



とすると、後者では恐らく、割り込みを有効化してからIDTRの情報が表示されるまでの間に

タイマから割り込みがきて、なんらかの原因で割り込み処理から戻ってこれなかったということでは

なかろうか。







じゃあ、アセンブラで書いた、Cの関数を呼び出す割り込みハンドラを見てみよう。






inthandler21:
pushw %es
pushw %ds
pusha
movl %esp, %eax
pushl %eax
movw %ss,%ax
movw %ax, %ds
movw %ax, %es
call hInt21
popl %eax
popa
popw %ds
popw %es
iret
まず、この処理は16行目にはたどり着いていないということは確実。

なぜなら、16行目でhInt21をcallすると、一番最初のイメージ画像のように、

割り込みメッセージが表示されるからだ。



ではどこで処理が止まっているのか。

callするまではひたすらpushしたり、レジスタを弄っているだけなので、

処理が止まるような箇所は見当たらない。



ということは、そもそもこの割り込みハンドラが呼ばれていないのでは?



つまり、CPUは割り込みハンドラを呼んだつもりだけど、アドレスを間違えちゃった、みたいな。







ここから先はさらに原因究明していくけど、これで少なくとも割り込みは正常に起こってることが

わかったので、よい収穫だったかな。

*1:__packed__