44622
現在、「作りながら学ぶOSカーネル 保護モードプログラミングの基本と実践」という本でOSについて学んでいます。

そこで、x86について、リアルモードから保護モードへ移行する際にCR0の最下位ビット（PEビット）を1にすることでCPUが保護モードとして動作するようになるという記載がありました。  
しかし、その後の32ビットコード部分へのfarジャンプ命令では、prefix（0x66, 0x67）をつけているようです。

CPUはすでに保護モード（32ビット）として動いているはずなので、オペランドを32ビットと解釈させるためのprefixは不要と思えるのですが、なぜ必要となるのでしょうか？  
確かに、prefixを外して実行するとうまくいきませんでした。

実際に本で説明されていた移行部分のコードは下記となります。

```

    mov eax, cr0
    or eax, 0x00000001
    mov cr0, eax
    
    jmp $+2
    nop
    nop
    
    db 0x66    # <- ここ
    db 0x67    # <- ここ
    db 0xEA
    dd PM_Start
    dw SysCodeSelector
    
```

また、PEビットを1にした時点で保護モードとしてCPUが動作するのであれば、  
その直後の命令ポインタが指すアドレスがくるってしまう気がするのですが、そんなことはないのでしょうか。

というのも、保護モードではCSレジスタの値からGDTを参照して対象のセグメントを探し出し、  
EIPレジスタに入っているオフセットを足して次の命令をフェッチしていると思うのですが、  
PEビットを1にした直後はCSレジスタにはリアルモードで使用していたセグメント情報が入っており、  
それを元にGDTを参照しようとするとわけのわからないところを参照してしまいそうな気がしました。
CC BY-SA 4.0
2018-06-08T09:02:35.317
44618
2018-06-11T23:14:42.390
28835
question
アセンブリ言語 x86
x86アーキテクチャにおけるリアルモードからプロテクトモードへの移行について
[IA-32 インテル® アーキテクチャ
ソフトウェア・デベロッパーズ・マニュアル](https://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/IA32_Arh_Dev_Man_Vol3_i.pdf)を参照されることをお勧めします。質問の内容であれば
第9章 プロセッサの管理と初期化 で説明されています。

>
> CPUはすでに保護モード（32ビット）として動いているはずなので、オペランドを32ビットと解釈させるためのprefixは不要と思えるのですが、なぜ必要となるのでしょうか？
>
>
> また、PEビットを1にした時点で保護モードとしてCPUが動作するのであれば、その直後の命令ポインタが指すアドレスがくるってしまう気がするのですが、そんなことはないのでしょうか。

そのため、次のfar JMPまたはfar
CALLが行われるまでは16bitリアルモードが維持されます。これらの命令で`CS`レジスタが設定された段階で保護モードが有効になります。

具体的には9.9.1. 保護モードへの切り替え辺りで説明されているので引用します。

>   3. 制御レジスタ CR0 の PE フラグ（またオプションで PG フラグ）を設定する MOV CR0 命令を実行する。
>   4. MOV CR0 命令のすぐ後で、far JMP 命令または far CALL 命令を実行する。（この操作は通常、命令ストリーム内の次の命令への
> far ジャンプまたは far コールになる。）
>   5. MOV CR0 命令の直後に JMP 命令または CALL 命令を実行すると、実行の流れが変化し、プロセッサが順次処理される。
>   6. ページングがイネーブルな場合、MOV CR0 命令のコードと、JMP 命令または CALL
> 命令のコードは、アイデンティティ・マッピングされたページから得たものでなければならない（つまり、ジャンプ前のリニアアドレスが、ページングと保護モードがイネーブルになった後の物理アドレスと同一でなければならない）。JMP
> 命令または CALL 命令のターゲット命令は、アイデンティティ・マッピングされる必要はない。
>

* * *

> CSレジスタが設定された段階で32bitデフォルトになるという明確な記述はなかったような気がします

質問文はPEフラグを立てた後の命令についてでしたので、保護モード移行後については言及しませんでした。この点については 第17章 16 ビット・コードと32
ビット・コードの混在 で説明されています。  
具体的には 17.1. 16 ビット・プログラム・モジュールと 32 ビット・プログラム・モジュールの定義 から引用します。

>
> コード・セグメント・ディスクリプタのDフラグは、コード・セグメントの命令に対するデフォルトのオペランド・サイズとアドレスサイズを決定する。（実アドレスモードと仮想8086モードでは、セグメント・ディスクリプタは使用されず、デフォルトは16
> ビットである）。D フラグがセットされているコード・セグメントは 32
> ビット・セグメントであり、Dフラグがクリアされているコード・セグメントは16ビット・セグメントである。

とあります。質問文の手前でGDTを初期化していると思いますが、その中の該当コード・セグメント・ディスクリプタのDフラグが 1
となっているのではないでしょうか？
CC BY-SA 4.0
2018-06-08T12:55:10.200
44622
2018-06-11T23:14:42.390
2020-06-17T08:14:45.997
-1
4236
44618
answer