忍者ブログ

Cyber Bird

   

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

OS自作入門 onLinux 6日目

Screenshot-QEMU.png

割り込みの仕組みについて

割り込み実験

ここ2カ月程、OS自作入門がずっと6日目で止まっていました。
割り込み処理がうまくできなかったからです。

ところが、ついに割り込みハンドラを呼ぶのに成功しました!
ようやく努力が報われました。すごく嬉しいです(´∀`)ノ

それでは続きから。


原因は簡単です。セグメントの設定がいけなかったのです。

前回の「割り込み実験」で、最後にこう結論付けました。

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

これ、ビンゴでした。



今回のソースはこちら

さて、重要なのはdsctbl.cだけです。


void init_gdtidt() {
	SEGMENT_DESCRIPTOR	*gdt = (SEGMENT_DESCRIPTOR *)ADR_GDT;
	GATE_DESCRIPTOR	  	*idt = (GATE_DESCRIPTOR *)ADR_IDT;
	int i;

	// GDTの初期化
	for( i = 0; i <= LIMIT_GDT / 8; i++ ) {
		set_segmdesc(gdt + i, 0, 0, 0);
	}
	set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, AR_DATA32_RW);
	set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER);
	set_segmdesc(gdt + 3, 0xffffffff, 0x00000000, AR_CODE32_ER);
	load_gdtr(LIMIT_GDT, ADR_GDT);

	// IDTの初期化
	for( i = 0; i <= LIMIT_IDT / 8; i++ ) {
		set_gatedesc(idt + i, 0, 0, 0);
	}
	load_idtr(LIMIT_IDT, ADR_IDT);

	// IDTの設定
	set_gatedesc(idt + 0x21, (int)inthandler21, 3 * 8, AR_INTGATE32);
	set_gatedesc(idt + 0x2c, (int)inthandler2c, 3 * 8, AR_INTGATE32);
}
18行目に注目。新しいセグメントを勝手に定義してます。
それでもって、その3番セグメントを割り込みゲートのセレクタ値に指定しています(28,29行目)。
これで正常動作しました。



はっきりとは理由はわからないんですが、おそらく、5日目あたりで強引にエントリアドレスを
設定したあたりから2番セグメントで動作しないんじゃないかと思います。たぶん。

以下適当な推測。

この時点で%ds=(1<<8)なんですよね。head.sで設定したままなので。

すると、set_gatedesc関数でセレクタ値を設定するときに、inthandler21のアドレスを
もちろんメモリオペランドとして読み込みます。
dsはセグメント1番を指しているので、セグメント1番から読み込みます。
セグメント1番ってメモリ全体じゃないですか。つまり、inthandler21の物理アドレスですよね。

inthandler21の物理アドレスを今、0xHANDLERとしておきます。

そして、割り込みが入って割り込みハンドラをCPUが呼ぶとき…
%cs = (2 << 8)
%eip = 0xHANDLER
になります。

そしたら、セグメント2番の0xHANDLER番地に割り込みハンドラがあると思って実行するじゃないですか。
セグメント2番ってbootpack全体のセグメントですよ。ベース番地は0x280000です。
つまり、物理アドレス0x280000+0xHANDLERを実行することになっちゃうでしょ。

実行して欲しいのは物理アドレス0xHANDLER。
じゃあ、もうひとつメモリ全体を指す実行可能なセグメントを作って、それを設定しとけーってことです。
セグメント1番は読み込み用セグメントで、実行できないので。


って考えたんですが、書き終わってもう一度考えると、inthandler21のアドレスの部分ですが、
メモリオペランドとして読み込まれているのではなくて、リンカが再配置して即値として
バイナリに埋め込まれているんですよね。結局原因分からずじまい。


※追記
よく考えましたが、上記でだいたいあってると思います。
5日目にリンカスクリプトでC言語部分のエントリアドレスを0x280000にしています。
ということは、リンカは再配置時にすべて0x280000を基準に再配置するので、
inthandler21も実際のアドレスに0x280000が足されて、結果的にos.sys内における物理アドレスになったみたいです。




一応、割り込みハンドラについても、GASに書き直しているので軽く解説を。

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
諸事情により、割り込みハンドラを記述しているソースファイル名がint.sからgasint.sになっています。

さて、基本的には自作本通りで解説するところはないのですが、
まあGASだと変わっちゃうところだけ触れておきます。

PUSHAD->PUSHA
POPAD->POPA
IRETD->IRET

命令名が違うだけですが^^;




画像だけだと、割り込みが起こったのかさっぱりわからないので、
スクリーンキャストを投下。





いやー、2か月もかけてここまでたどり着くと、感慨深いですね…。
これでひとまずは順調にすすみそうでよかったです。

それでは続きをお楽しみにー。
PR

COMMENT

NAME
TITLE
MAIL(非公開)
URL
EMOJI
Vodafone絵文字 i-mode絵文字 Ezweb絵文字
COMMENT
PASS(コメント編集に必須です)
SECRET
管理人のみ閲覧できます

TRACKBACK

Trackback URL:

ブログ内検索

プロフィール

HN:
levelfour
性別:
男性
自己紹介:
ぼちぼち更新を再開する予定です。

twitter

最新コメント

[09/27 菜々氏]
[06/17 NONAME]
[04/30 mithril]
[04/29 Liva]
[01/30 NONAME]

最新トラックバック

コガネモチ

Copyright ©  -- Cyber Bird --  All Rights Reserved
Design by CriCri / Photo by Geralt / powered by NINJA TOOLS / 忍者ブログ / [PR]