GPTパーティーション
Rev.14を表示中。最新版はこちら。
パーティーションは、MBR内に存在する4つの固定のパーティーションテーブルて定義されていて、4つ以上の物理パーティーションを定義できません。また、LBA形式として、総セクタ数は4byteで、セクタサイズを512とすると、定義できるHDの容量は、512×0xFFFFFFFF=2Tバイトとなり、それを超えてのHDのエリアを定義できません。GPTパーティーションは、上記の問題を対応します。GPTパーティーションのパーティーションエントリ数は4byte、LBAで指定するアドレスは8byteとなっています。
カーネルのブロック読み出しのパーティーションの扱いは、そのパーティーションのblock_deviceの開始アドレスをオフセットとすることで読み出しているにすぎません。GPTについても同じです。ブロックデバイス登録は、add_disk()からregister_disk()をコールする事で行い、check_partition()をコールして、かかるデバイスのパーティーションを取得し、そのパーティーション数(state->limit) add_partition()で、struct gendisk *diskに追加していきます。
なお、カーネルとしてサポートするパーティーション数の最大は、MAX_PART = 256のようです。
void add_disk(struct gendisk *disk) { disk->flags |= GENHD_FL_UP; blk_register_region(MKDEV(disk->major, disk->first_minor), disk->minors, NULL, exact_match, exact_lock, disk); register_disk(disk); blk_register_queue(disk); } enum { MAX_PART = 256 }; struct parsed_partitions { char name[BDEVNAME_SIZE]; struct { sector_t from; sector_t size; int flags; } parts[MAX_PART]; int next; int limit; }; void register_disk(struct gendisk *disk) { struct parsed_partitions *state; struct block_device *bdev; : : state = check_partition(disk, bdev); if (state) { for (j = 1; j < state->limit; j++) { sector_t size = state->parts[j].size; sector_t from = state->parts[j].from; if (!size) continue; add_partition(disk, j, from, size); #ifdef CONFIG_BLK_DEV_MD if (!state->parts[j].flags) continue; md_autodetect_dev(bdev->bd_dev+j); #endif } kfree(state); } blkdev_put(bdev, BDEV_RAW); }check_partition()は、(*check_part[])()で定義された、各パーティーションタイプのテーブルをチェックするコールバック関数群が定義されていて、正しいテーブルがstruct parsed_partitions *stateに取得できるまで、順次コールしていきます。
static struct parsed_partitions * check_partition(struct gendisk *hd, struct block_device *bdev) { struct parsed_partitions *state; int i, res; state = kmalloc(sizeof(struct parsed_partitions), GFP_KERNEL); if (!state) return NULL; #ifdef CONFIG_DEVFS_FS if (hd->devfs_name[0] != '\0') { printk(KERN_INFO " /dev/%s:", hd->devfs_name); sprintf(state->name, "p"); } #endif else { disk_name(hd, 0, state->name); printk(KERN_INFO " %s:", state->name); if (isdigit(state->name[strlen(state->name)-1])) sprintf(state->name, "p"); } state->limit = hd->minors; i = res = 0; while (!res && check_part[i]) { memset(&state->parts, 0, sizeof(state->parts)); res = check_part[i++](state, bdev); } if (res > 0) return state; if (!res) printk(" unknown partition table\n"); else if (warn_no_part) printk(" unable to read partition table\n"); kfree(state); return NULL; } static int (*check_part[])(struct parsed_partitions *, struct block_device *) = { : : #ifdef CONFIG_ACORN_PARTITION_EESOX adfspart_check_EESOX, #endif #ifdef CONFIG_ACORN_PARTITION_CUMANA adfspart_check_CUMANA, #endif #ifdef CONFIG_ACORN_PARTITION_ADFS adfspart_check_ADFS, #endif #ifdef CONFIG_EFI_PARTITION efi_partition, /* this must come before msdos */ #endif : : NULL };
以下はGPTの処理で、find_valid_gpt()をコールする事で、GPTパーティーションテーブルを取得します。
find_valid_gpt()でGPTヘッダー/エントリを取得します。シグニチャ/CRCのチェックまた、第一ヘッダーが破損していた場合、第二ヘッダから取得したりとの処理等があってぐちゃぐちゃしていますが、要は gpt_header **gpt, gpt_entry **ptesに、GPT情報を取得することにあります。
int efi_partition(struct parsed_partitions *state, struct block_device *bdev) { gpt_header *gpt = NULL; gpt_entry *ptes = NULL; u32 i; if (!find_valid_gpt(bdev, &gpt, &ptes) || !gpt || !ptes) { kfree(gpt); kfree(ptes); return 0; } Dprintk("GUID Partition Table is valid! Yea!\n"); for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < state->limit-1; i++) { if (!efi_guidcmp(ptes[i].partition_type_guid, NULL_GUID)) continue; put_partition(state, i+1, le64_to_cpu(ptes[i].starting_lba), (le64_to_cpu(ptes[i].ending_lba) - le64_to_cpu(ptes[i].starting_lba) + 1)); /* If there's this is a RAID volume, tell md */ if (!efi_guidcmp(ptes[i].partition_type_guid, PARTITION_LINUX_RAID_GUID)) state->parts[i+1].flags = 1; } kfree(ptes); kfree(gpt); printk("\n"); return 1; } typedef struct { u8 b[16]; } efi_guid_t; typedef struct _gpt_header { オフセット 0 u64 signature; 8 u32 revision; 12 u32 header_size; 16 u32 header_crc32; 20 u32 reserved1; 24 u64 my_lba; 32 u64 alternate_lba; 40 u64 first_usable_lba; 48 u64 last_usable_lba; 56 efi_guid_t disk_guid; 72 u64 partition_entry_lba; 80 u32 num_partition_entries; 84 u32 sizeof_partition_entry; 88 u32 partition_entry_array_crc32; 92 u8 reserved2[GPT_BLOCK_SIZE - 92]; } __attribute__ ((packed)) gpt_header; typedef struct _gpt_entry { オフセット 0 efi_guid_t partition_type_guid; 16 efi_guid_t unique_partition_guid; 32 u64 starting_lba; 40 u64 ending_lba; 48 gpt_entry_attributes attributes; 56 efi_char16_t partition_name[72 / sizeof (efi_char16_t)]; } __attribute__ ((packed)) gpt_entry; static int find_valid_gpt(struct block_device *bdev, gpt_header **gpt, gpt_entry **ptes) { int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; gpt_header *pgpt = NULL, *agpt = NULL; gpt_entry *pptes = NULL, *aptes = NULL; legacy_mbr *legacymbr = NULL; u64 lastlba; if (!bdev || !gpt || !ptes) return 0; lastlba = last_lba(bdev); good_pgpt = is_gpt_valid(bdev, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes); if (good_pgpt) { good_agpt = is_gpt_valid(bdev, le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes); if (!good_agpt) { good_agpt = is_gpt_valid(bdev, lastlba, &agpt, &aptes); } } else { good_agpt = is_gpt_valid(bdev, lastlba, &agpt, &aptes); } /* The obviously unsuccessful case */ if (!good_pgpt && !good_agpt) { goto fail; } /* This will be added to the EFI Spec. per Intel after v1.02. */ legacymbr = kmalloc(sizeof (*legacymbr), GFP_KERNEL); if (legacymbr) { memset(legacymbr, 0, sizeof (*legacymbr)); read_lba(bdev, 0, (u8 *) legacymbr, sizeof (*legacymbr)); good_pmbr = is_pmbr_valid(legacymbr); kfree(legacymbr); legacymbr=NULL; } /* Failure due to bad PMBR */ if ((good_pgpt || good_agpt) && !good_pmbr && !force_gpt) { printk(KERN_WARNING " Warning: Disk has a valid GPT signature " "but invalid PMBR.\n"); printk(KERN_WARNING " Assuming this disk is *not* a GPT disk anymore.\n"); printk(KERN_WARNING " Use gpt kernel option to override. " "Use GNU Parted to correct disk.\n"); goto fail; } /* Would fail due to bad PMBR, but force GPT anyhow */ if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) { printk(KERN_WARNING " Warning: Disk has a valid GPT signature but " "invalid PMBR.\n"); printk(KERN_WARNING " Use GNU Parted to correct disk.\n"); printk(KERN_WARNING " gpt option taken, disk treated as GPT.\n"); } compare_gpts(pgpt, agpt, lastlba); /* The good cases */ if (good_pgpt && (good_pmbr || force_gpt)) { *gpt = pgpt; *ptes = pptes; if (agpt) { kfree(agpt); agpt = NULL; } if (aptes) { kfree(aptes); aptes = NULL; } if (!good_agpt) { printk(KERN_WARNING "Alternate GPT is invalid, " "using primary GPT.\n"); } return 1; } else if (good_agpt && (good_pmbr || force_gpt)) { *gpt = agpt; *ptes = aptes; if (pgpt) { kfree(pgpt); pgpt = NULL; } if (pptes) { kfree(pptes); pptes = NULL; } printk(KERN_WARNING "Primary GPT is invalid, using alternate GPT.\n"); return 1; } fail: if (pgpt) { kfree(pgpt); pgpt=NULL; } if (agpt) { kfree(agpt); agpt=NULL; } if (pptes) { kfree(pptes); pptes=NULL; } if (aptes) { kfree(aptes); aptes=NULL; } *gpt = NULL; *ptes = NULL; return 0; }上記内容を踏まえて、私の環境下での、セクタ0以降の内容です。システムはsda1/2/3のパーティションを有しています。
[root@localhost kitamura]# gdisk -l /dev/sda : : Number Start (sector) End (sector) Size Code Name 1 2048 4095 1024.0 KiB EF02 2 4096 1028095 500.0 MiB EF00 ext4 3 1028096 16775167 7.5 GiB 8E00 [root@localhost kitamura]# ls /dev/sda* /dev/sda /dev/sda1 /dev/sda2 /dev/sda3
[root@localhost north]# hexdump -C sda.imgブートローダおよびBPB
00000000 eb 63 90 00 00 00 00 00 00 00 00 00 00 00 00 00 |.c..............| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000050 00 00 00 00 00 00 00 00 00 00 00 80 00 08 00 00 |................| 00000060 00 00 00 00 ff fa 90 90 f6 c2 80 74 05 f6 c2 70 |...........t...p| 00000070 74 02 b2 80 ea 79 7c 00 00 31 c0 8e d8 8e d0 bc |t....y|..1......| 00000080 00 20 fb a0 64 7c 3c ff 74 02 88 c2 52 be 80 7d |. ..d|<.t...R..}| 00000090 e8 17 01 be 05 7c b4 41 bb aa 55 cd 13 5a 52 72 |.....|.A..U..ZRr| 000000a0 3d 81 fb 55 aa 75 37 83 e1 01 74 32 31 c0 89 44 |=..U.u7...t21..D| 000000b0 04 40 88 44 ff 89 44 02 c7 04 10 00 66 8b 1e 5c |.@.D..D.....f..\| 000000c0 7c 66 89 5c 08 66 8b 1e 60 7c 66 89 5c 0c c7 44 ||f.\.f..`|f.\..D| 000000d0 06 00 70 b4 42 cd 13 72 05 bb 00 70 eb 76 b4 08 |..p.B..r...p.v..| 000000e0 cd 13 73 0d f6 c2 80 0f 84 d8 00 be 8b 7d e9 82 |..s..........}..| 000000f0 00 66 0f b6 c6 88 64 ff 40 66 89 44 04 0f b6 d1 |.f....d.@f.D....| 00000100 c1 e2 02 88 e8 88 f4 40 89 44 08 0f b6 c2 c0 e8 |.......@.D......| 00000110 02 66 89 04 66 a1 60 7c 66 09 c0 75 4e 66 a1 5c |.f..f.`|f..uNf.\| 00000120 7c 66 31 d2 66 f7 34 88 d1 31 d2 66 f7 74 04 3b ||f1.f.4..1.f.t.;| 00000130 44 08 7d 37 fe c1 88 c5 30 c0 c1 e8 02 08 c1 88 |D.}7....0.......| 00000140 d0 5a 88 c6 bb 00 70 8e c3 31 db b8 01 02 cd 13 |.Z....p..1......| 00000150 72 1e 8c c3 60 1e b9 00 01 8e db 31 f6 bf 00 80 |r...`......1....| 00000160 8e c6 fc f3 a5 1f 61 ff 26 5a 7c be 86 7d eb 03 |......a.&Z|..}..| 00000170 be 95 7d e8 34 00 be 9a 7d e8 2e 00 cd 18 eb fe |..}.4...}.......| 00000180 47 52 55 42 20 00 47 65 6f 6d 00 48 61 72 64 20 |GRUB .Geom.Hard | 00000190 44 69 73 6b 00 52 65 61 64 00 20 45 72 72 6f 72 |Disk.Read. Error| 000001a0 0d 0a 00 bb 01 00 b4 0e cd 10 ac 3c 00 75 f4 c3 |...........<.u..| 000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0016バイトの4パーティションテーブル(パーティション1のタイプはeeでGPT)
ただし、パーティション2/3/4は無し。終端の55/aaはマスターブートレコードとしての正当を示すマジッククナンバー
00 |................| 000001c0 01 00 ee fe ff ff 01 00 00 00 ff ff ff 00 00 00 |................| 000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|GPTヘッダテーブル(struct _gpt_header)
00000200 45 46 49 20 50 41 52 54 00 00 01 00 5c 00 00 00 |EFI PART....\...| 00000210 de 0d 7b 16 00 00 00 00 01 00 00 00 00 00 00 00 |..{.............| 00000220 ff ff ff 00 00 00 00 00 22 00 00 00 00 00 00 00 |........".......| 00000230 de ff ff 00 00 00 00 00 ff 17 6b 75 f9 51 79 40 |..........ku.Qy@| 00000240 bf 74 59 cc d8 7c 2d 6f 02 00 00 00 00 00 00 00 |.tY..|-o........| 00000250 80 00 00 00 80 00 00 00 5c 83 7e e9 00 00 00 00 |........\.~.....| 00000260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| *パーティーション1(struct _gpt_entry)
パーティーションタイプGUIに、Hah!IdontNeedEFIとは、洒落ていますね〜。Linuxブートローダは多段階で動作するため、EFIでなくても、従来のBIOSで、GPTパーティーションからでも、問題なくカーネルを起動できるだよ〜ってことですね。たぶん。
00000400 48 61 68 21 49 64 6f 6e 74 4e 65 65 64 45 46 49 |Hah!IdontNeedEFI| 00000410 8f ab 1d 0b 83 b9 eb 4a bf e0 5a 62 fc a5 1a 0d |.......J..Zb....| 00000420 00 08 00 00 00 00 00 00 ff 0f 00 00 00 00 00 00 |................| 00000430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| *パーティーション2(struct _gpt_entry)
00000480 28 73 2a c1 1f f8 d2 11 ba 4b 00 a0 c9 3e c9 3b |(s*......K...>.;| 00000490 26 9a 11 c6 13 62 5d 4e 8a 3d 6f 77 c4 63 bc 6e |&....b]N.=ow.c.n| 000004a0 00 10 00 00 00 00 00 00 ff af 0f 00 00 00 00 00 |................| 000004b0 00 00 00 00 00 00 00 00 65 00 78 00 74 00 34 00 |........e.x.t.4.| 000004c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| *パーティーション3(struct _gpt_entry)
なお以降00のブロックが、必要とするエントリ数分、続いています。
00000500 79 d3 d6 e6 07 f5 c2 44 a2 3c 23 8f 2a 3d f9 28 |y......D.<#.*=.(| 00000510 dd a9 79 93 3a b3 10 40 8f f5 10 14 0c 1f 46 08 |..y.:..@......F.| 00000520 00 b0 0f 00 00 00 00 00 ff f7 ff 00 00 00 00 00 |................| 00000530 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| *
パーティーションエントリ数 :80 00 00 00(0x80)
パーティーションエントリのサイズ :80 00 00 00(0x80)
パーティーションエントリのLBAアドレス:02 00 00 00 00 00 00 00(512*2=1024(0x400)
パーティーションエントリ数は0x80となっていますが、efi_partition()のパーティションエントリ数は0x80ですが、エントリを追加する時、以下の様に
if (!efi_guidcmp(ptes[i].partition_type_guid, NULL_GUID)) continue;とし、partition_type_guidが設定されていないエントリはスキップしています。
パーティーションエントリのLBAアドレスが0x400で、サイズが0x80ですから、パーティーションエントリは、0x400/0x400+0x80(0x480)/0x400+0x80+0x80(0x500)となります。
補足
fdiskコマンドは、パーティションタイプとしてGPTを設定できますが、テーブル参照としてのGPTをサポートしていません。GPTテーブルを参照する場合、gdiskコマンドを使用する必要があります。今回の内容は、驟雨のカーネル探検隊を参考にさせてもらいました。