ウォンツテック

そでやまのーと

CPUのプロテクトモードへの移行において、GDT(グローバルディスクリプタテーブル)部分を読み直した。
IA-32 インテル アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル下巻で今回必要な部分を詳しく見てみる

全部で8バイト
ディスクリプタテーブルに必要な値としては
1. リミット値 - 20bit
2.セグメントベース - 32bit
3.属性 - 12bit
でありこれを以下のようにメモリ上に配置する。

0byte - リミット値 下位8bit
1byte - リミット値 中位8bit
2byte - セグメントベース 1byte目
3byte - セグメントベース 2byte目
4byte - セグメントベース 3byte目
5byte - 属性値 下位8bit
6byte - (上位4bit)⇒属性値 上位4bit (下位4bit)⇒リミット値上位4bit
7byte - セグメントベース 4byte目

今回は仮設定なのでコード用、データ用、スタック用3つのGDTをフラットに作成する。
フラットとは特にセグメントを分けずに同じメモリ空間を共有するようにす
る事で、具体的に上記設定の「セグメントベース」と「リミット値」を同じ
値である「0x00000000」と「0xfffff」にする。stackはセグメントベース
「0x0009c000」辺りにしてリミット値は「0x00000」にする。(stackの
場合、リミット値はセグメントベースからマイナスして行き、そのリミット
値までスタックを減算出来るという意味になる)

属性値の意味を詳しく見てみると

■上位4bit |G|D/B|0|AVL|
・G - G(グラニュラティ)フラグ
   このフラグを立てるとセグメントのサイズが4Kバイト単位となる。 
 立てなかった場合は1バイト単位なので20bitのリミット値は最大で1MB
 であるが、このフラグを立てると1MBx4K=4GBとなる。(通常は立てる)
・D/B - デフォルトオペレーションサイズ/デフォルトスタック・ポインタサイズ
  コードやスタックの動作において、デフォルトで16bitモードか
 32bitモードのどちらで動作させるかを指定する。ビットを立てると32bit
  モードとなる。
  ※16bitと32bitのコードでは指定するオペランドが16bitか32bitかの違い
 があるためマシン語が変わってくる。マシン語プレフィックス0x66を
 つけると16bit⇔32bitの反転が出来る。
・0 - システムで予約されている 常に0
・AVL - システム・ソフトウェアで使用可

■下位8bit  |P|DPL|S|TYPE|A| 
・P - セグメント存在フラグ 1bit
  セグメントがメモリ内に存在している事を示す
 DPL - ディスクリプタ特権レベル 2bit
  0〜3の4段階で特権レベルを決める、通常OSが0でアプリが3
・S - ディスクリプタ・タイプ フラグ 1bit
  セグメント・ディスクリプタがシステムセグメント用(フラグを立てる)
  かコード/データセグメント用かを指定する。
  ※GDTの場合はビットを立てる。このテーブルをゲートディスクリプタなど
 に使う場合はビットを立てない。
・TYPE - セグメント・ディスクリプタのタイプ 3bit
  例.
   実行および読み出し可能コードセグメント  5
   読み書き可能データセグメント 1
   読み書き可能スタックセグメント 3
・A - アクセス状態フラグ 1bit
  セグメントがアクセスされたかの状態を示す。
  ※ページアウト処理などに使う

以上よりCPUのプロテクトモード移行部分のみを抜き出して書く

_KERNEL_CS = 0x08

		cli

		xorl	%eax, %eax
		movw	%ds, %ax
		shll	$4, %eax
		addl	$gdt, %eax
		movl	%eax, (gdtr+2)     #set the address of gdt base

		lgdt	gdtr
		lidt	idtr

		movw	$1, %ax
		lmsw	%ax
		jmp	flush_pipe_queue
		
flush_pipe_queue:
		.byte	0x66, 0xea
		.long	first_code32 + 0x90000
		.word	_KERNEL_CS

.code32
.text
first_code32:
		movl	$0x10, %eax
		movw	%ax, %ds
		movw	%ax, %es
		movl	$0x18, %eax
		movw	%ax, %ss
		movl	$0x9c000, %esp
		jmp	hang

hang:	
		jmp	hang

gdtr:	.word	gdt_end - gdt - 1
		.long	0		# filled in code
gdt:
gdt00h:					#It's dummy for cpu
		.word	0		#selector 00h
		.word	0
		.byte	0
		.byte	0
		.byte	0
		.byte	0
gdt08h:					#code segment
		.word	0xFFFF	#selector 08h
		.word	0
		.byte	0
		.byte	0x9A	# P=1, DPL=0, S=1, TYPE=5, A=0
		.byte	0xCF	# G=1, D=1
		.byte	0
gdt10h:					#data segment
		.word	0xFFFF	#selector 10h
		.word	0
		.byte	0
		.byte	0x92	# P=1, DPL=0, S=1, TYPE=1, A=0
		.byte	0xCF	# G=1, D=1
		.byte	0
gdt18h:					#stack segment
		.word	0		#selector 18h 
		.word	0x0
		.byte	0x0
		.byte	0x96	# P=1, DPL=0, S=1, TYPE=3, A=0
		.byte	0xC0	# G=1, D=1
		.byte	0						
gdt_end:				

idtr:	        .word	0
		.word	0, 0

最初gdtを.dataセクションで書いたのでうまく動かなかった。lgdtを書くセクションとgdtを置くセクションを分けるのが駄目だったのだろうか?よくわからない。。