■
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を置くセクションを分けるのが駄目だったのだろうか?よくわからない。。