リアルモードからプロテクトモードへの変更

## はじめに
この記事では、リアルモードで起動したOSがプロテクトモードに切り替わる処理を見ていきます。
また、セグメントやセグメントを取り扱うセグメントディスクリプタテーブル、GDTRなどの処理もコードを交えて解説します。

## リアルモードとは
16bitのレジスタを使い、仮想メモリをサポートせず物理アドレスのみで動作するモードです。
リアルモードで使えるレジスタは、
　　ax, bx, cx, dx, ip, sp, bp, si, di, flags
があります。
また、**セグメント**レジスタとして
　　cs, ds, ss, es, gs, fs
があります。
リアルモードの**セグメント**とは、セグメントレジスタの値とオフセットから計算された物理アドレスです。
セグメントの意味はリアルモードとプロテクトモードで異なります。これは後ほど解説します。

## リアルモードのアドレッシング
リアルモードのアドレッシングは、セグメントレジスタとオフセットを加算して行います。
図を用いて解説します。
![001_リアルモード_オフセット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1508794/3f9bc103-f06f-242a-a829-cbf704026d6f.png)
レジスタ値:オフセット値 の形式でセグメントを指定します。例えば、2345:0100のように指定します。
セグメントベースには、セグメントレジスタが使われます。
セグメントレジスタ(cs, ds, ss, es, gs, fs)のうち、cs, ds, ssは用途が決まっています。
cs : Code Segment はCPUが実行するプログラムコードのセグメントを
ds : Data Segment は読み書きするデータのセグメントを
ss : Stack Segment はスタックのセグメントを指定します。
　
物理アドレスの計算式は、(セグメントベース * **16** + オフセット)です。
なぜセグメントベースを16倍するのかは、次の画像を参照してください。
![002_8086_pin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1508794/7bdad4ae-6de5-0f5a-0147-0c0c8959b23e.png)
8086のピンです。A0~A19までのピンがあるのがわかります。
メモリアクセスには、この20個のアドレスバスを使用できる事ができます。つまり、20bitでアドレス指定ができるということです。
そのため、セグメントベースを16倍して、20bitでのアクセスを行います。

## プロテクトモードとは
プロテクトモードでは、32bitのアドレスバスを使用できます。
それだけでなく、プロテクションリングや仮想メモリ、セグメンテーションの改善が行われました。
プロテクトモードでも、セグメントレジスタ(cs, ds, ss, es, gs, fs)を使用します。
セグメントレジスタは32bitに拡張されている・・・と思いきや、16bitのままです。
しかし、セグメンテーションが改善されており、アドレス計算方法が全く別のものになっています。

## プロテクトモードのアドレッシング
プロテクトモードのアドレッシングは、
1, セグメントレジスタが**セグメントディスクリプタテーブル**を参照
2, **セグメントディスクリプタテーブル**がセグメントを指定する
という二段階に分かれています。
![003_プロテクトモード_アドレッシング.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1508794/71feba10-d074-c785-572e-341b8ab6f1f8.gif)
セグメントディスクリプタテーブルには、LDTと**GDT**があり、ほぼすべてのOSで**GDT**が採用されています。
**GDTR**(グローバルディスクリプタテーブルレジスタ)にGDTのセグメントディスクリプタテーブルのアドレスが格納されます
　
GDTのセグメントディスクリプタの構成は、以下の図です
![004_GDT_セグメントディスクリプタ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1508794/9002bc20-19e0-f9f4-8c3b-570a267fd28c.jpeg)
重要なのは、BaseとType, DPLです
Baseはセグメントのベースアドレスを表し、Typeがセグメントのタイプ(読み書き実行の可or不可)を、DPLがセグメントのアクセスに必要なCPUの特権レベルを表します。

### リアルモードからプロテクトモードへの移行
プロセッサがプロテクトモードに入るには、
1, メモリ上にGDTのセグメントディスクリプタを作成する
2, GDTRをセットアップする
3, レジスタcr0を1にする
4, ファージャンプを行う
の処理を行います。
　
実際に、GRUB2のコードを見ていきます。ソースコードはこちらにあります。
https://chromium.googlesource.com/chromiumos/third_party/grub2/+/11508780425a8cd9a8d40370e2d2d4f458917a73/grub-core/kern/i386/realmode.S
CPUをリアルモードからプロテクトモードに移行する処理は、real_to_protで行われています。
　
まずは、1, メモリ上にGDTのセグメントディスクリプタを作成する 処理を見ていきましょう。
81~97行目にGDTが定義されています。

```asm
gdt:
	.word	0, 0
	.byte	0, 0, 0, 0

	/* -- code segment --
	 * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present
	 * type = 32bit code execute/read, DPL = 0
	 */
	.word	0xFFFF, 0
	.byte	0, 0x9A, 0xCF, 0

	/* -- data segment --
	 * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present
	 * type = 32 bit data read/write, DPL = 0
	 */
	.word	0xFFFF, 0
	.byte	0, 0x92, 0xCF, 0
```
コードセグメントは、00 CF 9A 00 00 00 FF FF
データセグメントは、00 CF 92 00 00 00 FF FF
であり、どちらもベースアドレスが0, セグメントの大きさは最大4GB, DPLは0です。
異なる点はタイプで、コードセグメントは実行と読みが許可、データセグメントは読み書きが許可されています。
　
133行~の、real_to_protを見ていきます。

```
real_to_prot:
	.code16
	cli
	/* load the GDT register */
	xorw	%ax, %ax
	movw	%ax, %ds
	DATA32	ADDR32	lgdt	gdtdesc
	/* turn on protected mode */
	movl	%cr0, %eax
	orl	$GRUB_MEMORY_CPU_CR0_PE_ON, %eax
	movl	%eax, %cr0
	/* jump to relocation, flush prefetch queue, and reload %cs */
	DATA32	ljmp	$GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg
	.code32
```

```
DATA32	ADDR32	lgdt	gdtdesc
```

のlgdt命令で、2, GDTRをセットアップする を行い、
次に、

```
movl	%cr0, %eax
orl	$GRUB_MEMORY_CPU_CR0_PE_ON, %eax
movl	%eax, %cr0
```

で 3, レジスタcr0を1にする を行います。
最後に

```
DATA32	ljmp	$GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg
```

で 4, ファージャンプを行う を完了します。
これにより、CPUがリアルモードからプロテクトモードになります。

## 参考書籍
低レベルプログラミング
新装改訂版 Linuxのブートプロセスをみる