リアルモードからプロテクトモードへ
real_to_protのコード
ここを参照しながら読んでみてください。
GRUB後半コード
ソースコード
前回プロテクトモード用にセグメントレジスタにデータをロードしたところからの続き。
movl (%esp), %eax movl %eax, GRUB_MEMORY_MACHINE_REAL_STACK movl protstack, %eax movl %eax, %esp movl %eax, %ebp movl GRUB_MEMORY_MACHINE_REAL_STACK, %eax movl %eax, (%esp) xor eax,eax sidt LOCAL(realidt) lidt protidt ret
Real_to_protを呼び出したプログラムに戻るリターンアドレスを退避しておきます。
これをなくすと元に処理に復帰できなくなります。
bpとspを書き換えてプロテクトモード用のスタックを作ります。
対比しておいたリターンアドレスをプロテクトモード用のスタックにおきます。
これはret命令がスタックの一番上をリターンアドレスとして利用するためです。
これでスタックの切り替えが終わありました。
sidtは指定したアドレスにidtレジスタの値を保存します。
realidtには保存の為の領域を確保しています。
lidt命令は指定したアドレスからidtをロードする逆の命令です。
この処理によってリアルモードとプロテクトモードのidtの切り替えができました。
しかし
ret命令で元のアドレスに復帰します。
protid: .word 0 .long 0
は大きさ0となっているのでプロテクトモードではidtは使用しません。
プロテクトモードで割り込みは行わないだけで、割り込み処理はリアルモードに切り替えて割り込み処理を行う。
スタックにprot_to_real、real_to_protのアドレスを積んでいるから使い必要に応じてジャンプしてリアルモードとプロテクトモードを切り替えられます。
prot_to_realのコード
次はprot_to_realを見てみます。
基本的にreal_to_protと逆の処理をするだけです。
prot_to_real: lgdt gdtdesc sidt protidt lidt LOCAL(realidt) movl %esp, %eax movl %eax, protstack movl (%esp), %eax movl %eax, GRUB_MEMORY_MACHINE_REAL_STACK movl $GRUB_MEMORY_MACHINE_REAL_STACK, %eax movl %eax, %esp movl %eax, %ebp movw $GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss ljmp $GRUB_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $tmpcseg
lgdtでをgdtdescにgdtをレジスタに読み込み、sidtでprtoidtにidtを保存。
lidtでリアルモードのidtをメモリからレジスタにセットします。
eaxの内容をプロテクトモード用のスタックポインタをprotstackに保存。
このルーチンを抜けて帰還するリターンアドレスをGRUB_MEMORY_MACHINE_REAL_STACKへ退避しておきます。
リターンアドレスを退避したGRUB_MEMORY_MACHINE_REAL_STACKのアドレスをspとbpにセットしてスタックを作成。
セグメントをセットしてtmpcsegにジャンプ。
16ビットモード用に切り替えます。
tmpcseg: .code16 movl %cr0, %eax andl $(~GRUB_MEMORY_CPU_CR0_PE_ON), %eax movl %eax, %cr0 DATA32 ljmp $0, $realcseg realcseg: xorl %eax, %eax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss sti DATA32 ret
プロテクトモードから移ってきたので16ビットで動かすことをcpuに伝えます。
cr0レジスタの最下位に0をセットして16ビットへ移行します。
リアルモードからプロテクトモードへ移行する時にもプリフェッチキューをフラッシュして初期化しました。
逆の場合でもljmp命令を使いプリフェッチキューをクリアしcsレジスタに0をセットします。
これは決まり事です。
その後realcsegルーチンでコードセグメントやデータセグメントなどのセグメントレジスタを0でクリアしリアルモード用のセグメントを設定します。
その後プロテクトモードでは停止させていた割り込み機能をstiで復活させて、リアルモードからプロテクトモードへの移行を完了。
DATA32 ret命令でリアルモードを呼び出した処理へ復帰します。
コメント