GPTとMBRはどのように違うのか?
UEFI環境下では、BIOSでサポートされているMBRに代わりGPTと呼ばれる新しいパーティーションテーブルを用います。
2TB以上の大容量なHDDをサポート出来る、と説明される事が多いのですが、具体的にどのような違いがあるのか比較してみます。
MBR
MBRはLBA 0に置かれる512byteのパーティーションテーブルで、以下の様なレイアウトになっています:
オフセット | サイズ | 内容 |
0x000 | 446byte | ブートストラップローダ |
0x1be | 64byte | パーティーションテーブル(4エントリ) |
0x1fe | 2byte | ブートシグニチャ(0xaa55) |
ブートストラップローダはBIOSからロード・実行されるブートプログラムで、パーティーションの情報はパーティーションテーブルに存在します。
なお、最大パーティーション数は4個と決められており、4つのパーティーションを「物理パーティーション」と呼び、拡張パーティションと呼ばれる物理パーティーション内に論理的なパーティーションを入れ子式に作成する事により、パーティーション数の制限を回避しています(論理パーティション)。
パーティーションテーブルのレイアウトは以下のようになっています。
オフセット | サイズ | 内容 |
0x00 | 1byte | ブートフラグ |
0x01 | 3byte | 開始セクタ(CHS) |
0x04 | 1byte | パーティーションタイプ |
0x05 | 3byte | 終了セクタ(CHS) |
0x08 | 4byte | 開始セクタ(LBA) |
0x0c | 4byte | 総セクタ数 |
ブートフラグはOSの置かれているパーティーションの指定に使われます。
パーティーションタイプはパーティーションの種類の表現に使われます。
開始セクタ・終了セクタのエントリがCHSとLBAで二つありますが、これは大容量のHDDをサポートするため導入されたBIOSのLBA拡張により、MBRへ後から変更が加えられたものと思われます(参考)。
開始セクタ(LBA)に値がセットされた場合、開始セクタ(CHS)/終了セクタ(CHS)は無視されLBAの値が用いられます。
MBRからのブート
BIOSはMBR上のブートストラップローダをロード・実行し、これに制御を譲ります。
この為、現在のOSではブートストラップローダへブートローダの一段目を書き込み、独自の手順で起動しているものが少なくありません。
しかし、元々のブートストラップローダはパーティーションテーブルからブートフラグがセットされているパーティーションを見つけ、先頭セクタをロード・実行するというプログラムであるべき、のようです(参考)。
この時、パーテーションの先頭セクタには以下のようなPartition Boot Record(PBR)が存在する事になっているようです。
オフセット | サイズ | 内容 |
0x000 | 3byte | ジャンプ命令 |
0x003 | 8byte | OEM名 |
0x00B | 25byte | BIOSパラメータブロック |
0x01C | 26byte | 拡張BIOSパラメータブロック |
0x03E | 448byte | ブートストラップコード |
0x1FE | 2byte | シグニチャ |
ブートストラップローダは1セクタを読み込んで先頭アドレスを実行するだけだと思われますが、先頭のジャンプ命令によりブートストラップコードが呼び出され、BIOSパラメータブロック・拡張BIOSパラメータブロックはパーテーションの情報をブートローダ/OSへ与えるデータ領域として機能するようです(参考)。
一方、本来のブートストラップローダの仕組みを取っていない例としてGRUBをMBRへインストールした場合の動作を紹介します(参考)。
GRUBがMBRへ書き込む一段目のブートローダ(stage1)はLBA 1以降に配置されている二段目のブートローダ(stage1_5)をロード・実行する為に使われます。
stage1_5はLinuxのファイルシステムを読み込む機能を持ち、ファイルシステムから三段目のブートローダ(stage2)を見つけてロードします。
他のOSのブートローダでは、また異なる方法でブートしている場合もあると思われますが、MBR・PBRのブートコードサイズの制約を逃れるため、これに似た手法で多段ブートを行なっている場合が殆どです。
MBRがBIOSからロード・実行される時は当然リアルモードで実行されますが、多くのブートローダでプロテクトモードへの切り替えが行われており、これによってメモリ空間サイズの制約を受けてOSがロードできないといった事を避けたり、アセンブリではなくCで実装しより大きく高機能なブートローダを実現したりしているようです。
LinuxからみたMBR
fdiskからMBRを覗いてみましょう。
$ sudo fdisk /dev/sda Command (m for help): p Disk /dev/sda: 160.0 GB, 160041885696 bytes 255 heads, 63 sectors/track, 19457 cylinders, total 312581808 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x000f2acc Device Boot Start End Blocks Id System /dev/sda1 * 2048 262266879 131132416 83 Linux /dev/sda2 262268926 312580095 25155585 5 Extended /dev/sda5 262268928 312580095 25155584 82 Linux swap / Solaris
Bootがブートフラグ、Startが開始セクタ(LBA)、Endが開始セクタ(LBA)+総セクタ数、Blocksが総セクタ数、Id・Systemがパーティーションタイプに相当します。
GPT
GPTはLBA0-33とHDDの最終33セクタに置かれるパーティーションテーブルで、以下の様なレイアウトになっています。
MBRは古いソフトウェアとの互換性のために存在しています。
GPTヘッダーやパーティーションエントリが2つあるのは、第一エントリが破損した場合に第二エントリを使ってリカバリを試みるためです。
GPTヘッダーにはユーザが使用可能なディスクの領域やパーティーション数などの値が書き込まれます。
GPTヘッダーのレイアウトは以下のようになっています。
オフセット | サイズ | 内容 |
0 | 8byte | シグニチャ |
8 | 4byte | GPTのバージョン |
12 | 4byte | ヘッダサイズ |
16 | 4byte | ヘッダのCRC32 |
20 | 4byte | reserved |
24 | 8byte | 第一GPTヘッダのLBAアドレス |
32 | 8byte | 第二GPTヘッダのLBAアドレス |
40 | 8byte | 使用可能領域の開始LBAアドレス |
48 | 8byte | 使用可能領域の終了LBAアドレス |
56 | 16byte | ディスクのGUID |
72 | 8byte | パーティーションエントリのLBAアドレス |
80 | 4byte | パーティーションエントリ数 |
84 | 4byte | パーティーションエントリのサイズ |
88 | 4byte | パーティーションエントリのCRC32 |
92 | reserved |
各LBAアドレスは64bitへ拡張されています。
GPTヘッダやパーティーションエントリの破損を検出する為のCRC32の値や、第一GPTヘッダのリカバリに使う第二GPTヘッダのアドレスが存在します。
第二パーティーションエントリのLBAアドレスが見当たりませんが、恐らく第二GPTヘッダに書かれているのではないでしょうか。
また、ディスクのGUIDが設定できるようになっています。
MBRにはユーザが使用可能なHDD領域の範囲を設定する項目はありませんでしたが、ここでは任意の値がセットできるようです。
パーティションエントリの数とサイズが指定できるようになっていますが、少なくとも64bit Windows環境では128個・128byteと設定されるようです。
このため、作成できるパーティション数は128となります。
次に、パーティションエントリのレイアウトを見てみます。
オフセット | サイズ | 内容 |
0 | 16byte | パーティションタイプGUID |
16 | 16byte | パーティーションユニークGUID |
32 | 8byte | 開始LBAアドレス |
40 | 8byte | 終了LBAアドレス |
48 | 8byte | 属性フラグ |
56 | 72byte | パーティション名 |
パーティションエントリのレイアウトはMBRのものと似ていますが、幾つか違いがあります。
まず、パーティションタイプは小さな数値ではなく、予め定義されたファイルシステムのGUIDとなっています。
Linux filesystem dataなら0FC63DAF-8483-4772-8E79-3D69D8477DE4、Windows Basic data partitionならEBD0A0A2-B9E5-4433-87C0-68B6B72699C7といった値を指定します(GUIDのリスト)。
MBRのブートフラグに似たものとして属性フラグがありますが、ここには読み込み専用・隠しパーティションなどの属性は存在しているものの「ブートパーティション」というフラグは存在しません(表「Partition attributes」を参照)。理由は後述します。
MBRには存在していないものとして、パーティションの識別子としてOSから使われるパーティーションユニークGUIDや、パーティション名を文字列で設定するフィールドがあります。
GPTからのブート
UEFIはブートストラップローダとパーティションのブートフラグを用いるBIOSのブート方式を踏襲していません。
このため、これらのフィールドはGPT上に存在しません。
UEFIでは、ブート対象のHDDのGPTを参照し、EFI System partition(GUID:C12A7328-F81F-11D2-BA4B-00A0C93EC93B)を探して \EFI\BOOT\BOOTX64.EFI(32bit環境ならBOOTIA32.EFI)というファイル名で保存されているブートローダを実行します(ファイル名はUEFIの設定により変更できます。これについては後日改めて記事にします)。
EFI System partitionは専用のパーティションタイプGUIDを用いますが、中身は普通のFATファイルシステムで、OSからも読み書きが可能です。
BOOTX64.EFIはWindowsの.exeと同じPEバイナリで、32bitまたは64bitで動作しMBR上のブートストラップローダで見られたようなサイズ制限はありません。
BIOSで動くブートローダがBIOSコールを用いてディスクにアクセスしたり画面表示を行うように、BOOTX64.EFIではUEFIが定めた方式でUEFIのAPIを呼び出してディスクにアクセスしたり画面表示を行います。
それらの操作は完全にC言語で記述でき、BIOSで動くブートローダのようにプロテクトモードへの切り替えとそれに伴うアセンブリコードの実装などを必要としません。
このBOOTX64.EFIのようなプログラムの事を「UEFI Application」と呼びます。
GRUB2はUEFI Applicationとして動作します。
LinuxカーネルそのものをUEFI Applicationとしてビルドする事も可能なようです。
LinuxからみたGPT
fdiskに似たgdiskでGPTを覗いてみましょう。
test@uefitest:~$ sudo gdisk /dev/sda GPT fdisk (gdisk) version 0.8.5 Partition table scan: MBR: protective BSD: not present APM: not present GPT: present Found valid GPT with protective MBR; using GPT. Command (? for help): p Disk /dev/sda: 16777216 sectors, 8.0 GiB Logical sector size: 512 bytes Disk identifier (GUID): A2E69954-099E-4905-9D0A-7615E2043BB0 Partition table holds up to 128 entries First usable sector is 34, last usable sector is 16777182 Partitions will be aligned on 2048-sector boundaries Total free space is 4029 sectors (2.0 MiB) Number Start (sector) End (sector) Size Code Name 1 2048 194559 94.0 MiB EF00 2 194560 15730687 7.4 GiB 0700 3 15730688 16775167 510.0 MiB 8200
NumberはGPTパーティション番号、Startは開始LBAアドレス、Endは終了LBAアドレス、Sizeは終了LBAアドレス - 開始LBAアドレス、CodeはパーティションタイプGUIDが長ったらしいのでgdiskの独自のパーティションコードに置き換えて表示中、Nameはパーティション名に相当するようです。
GUIDなどが表示されなかったので、各パーティーション毎に更に細かい情報を表示してみます。
Command (? for help): i Partition number (1-3): 1 Partition GUID code: C12A7328-F81F-11D2-BA4B-00A0C93EC93B (EFI System) Partition unique GUID: 78F5DBF5-6275-4776-B6B7-382043216865 First sector: 2048 (at 1024.0 KiB) Last sector: 194559 (at 95.0 MiB) Partition size: 192512 sectors (94.0 MiB) Attribute flags: 0000000000000000 Partition name: ''
パーティーション1はEFI System partitionでした。
ユニークGUIDは78F5DBF5-6275-4776-B6B7-382043216865、属性フラグは無し、パーティーション名も無しのようです。
Command (? for help): i Partition number (1-3): 2 Partition GUID code: EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 (Microsoft basic data) Partition unique GUID: 30FF5CB5-2B05-4876-B65D-1C5CC1CA6F08 First sector: 194560 (at 95.0 MiB) Last sector: 15730687 (at 7.5 GiB) Partition size: 15536128 sectors (7.4 GiB) Attribute flags: 0000000000000000 Partition name: ''
これがrootパーティーションとしてマウントしているsda2ですが、GUIDにWindows Basic data partitionが指定されているようです。
Ubuntu 12.10のデフォルト設定でセットアップしたのにおかしいな、と思ったのですが、LinuxではデータパーティションにWindows Basic data partitionのGUIDを使っていて、最近Linux用のGUIDが割り当てられたという事のようです(詳細)。
こちらのユニークGUIDは30FF5CB5-2B05-4876-B65D-1C5CC1CA6F08、属性フラグは無し、パーティーション名も無しのようです。
Command (? for help): i Partition number (1-3): 3 Partition GUID code: 0657FD6D-A4AB-43C4-84E5-0933C84B4F4F (Linux swap) Partition unique GUID: D0A434B9-5378-4684-A13D-480FE40C5957 First sector: 15730688 (at 7.5 GiB) Last sector: 16775167 (at 8.0 GiB) Partition size: 1044480 sectors (510.0 MiB) Attribute flags: 0000000000000000 Partition name: ''
パーティーション3はLinux swap partitionでした。
ユニークGUIDはD0A434B9-5378-4684-A13D-480FE40C5957、属性フラグは無し、パーティーション名も無しのようです。
test@uefitest:~$ ls -l /dev/disk/by-partuuid/ total 0 lrwxrwxrwx 1 root root 10 Jan 3 07:10 30ff5cb5-2b05-4876-b65d-1c5cc1ca6f08 -> ../../sda2 lrwxrwxrwx 1 root root 10 Jan 3 07:12 78f5dbf5-6275-4776-b6b7-382043216865 -> ../../sda1 lrwxrwxrwx 1 root root 10 Jan 3 07:10 d0a434b9-5378-4684-a13d-480fe40c5957 -> ../../sda3
ユニークGUIDでパーティーションへアクセス出来る事を確認してみました。
test@uefitest:~$ mount|grep sda1 /dev/sda1 on /boot/efi type vfat (rw) test@uefitest:~$ ls /boot/efi/ EFI test@uefitest:~$ ls /boot/efi/EFI/ ubuntu test@uefitest:~$ ls /boot/efi/EFI/ubuntu/ grubx64.efi
EFI System partitionがどこにマウントされているか調べてみたところ、/boot/efiへマウントされていました。
中身を覗いてみると\EFI\ubuntu\grubx64.efiというバイナリが置いてあるようです。
ファイル名が\EFI\BOOT\BOOTX64.EFIではないわけですが、この点については次回説明することとしましょう。