GPTパーティーション


パーティーションは、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 00
16バイトの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  |................|
*
GPTヘッダテーブルから以下の事が分かります。

パーティーションエントリ数 :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)となり、上記のパーティーションエントリー1/2/3のアドレスと一致します。

補足

fdiskコマンドは、パーティションタイプとしてGPTを設定できますが、テーブル参照としてのGPTをサポートしていません。GPTテーブルを参照する場合、gdiskコマンドを使用する必要があります。

今回の内容は、驟雨のカーネル探検隊を参考にさせてもらいました。

最終更新 2013/06/02 06:47:41 - north
(2013/05/25 18:44:05 作成)


検索

アクセス数
3698050
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。