GPTパーティーション(2)
Rev.3を表示中。最新版はこちら。
GPTパーティーションでは、efi_partition()からfind_valid_gpt()をコールする事で、GPTパーティションのチェックを行っていました。find_valid_gpt()を追ってみます。is_gpt_valid()でLBA位置GPT_PRIMARY_PARTITION_TABLE_LBAから、GPTヘッダー/エントリーを取得します。OKならpgpt->alternate_lba(第二ヘッダー)位置からもGPTヘッダー/エントリーを取得します。メインヘッダー/第二ヘッダーとも取得できなければエラーです。なお、第二ヘッダー位置取得は、GPTヘッダー内に、この位置を保持する項目があるのですが、まだメインヘッダーの正当性が保障されていないので、lastlba = last_lba(bdev);でブロックデバイスのファイルサイズから計算していて、最終セクタを取得しています。なお、le64_to_cpuマクロはエンディアン処理です。
0セクター目のMBRの正当性をチェックします。good_pmbr=1ならOK
GPTメインヘッダー/第二ヘッダーのどちらかがOKでも、MBRがNGならエラーです。ただしforce_gpt=1の時、MBRは考慮されずに動作します。(補足参照)
if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) {のチェックは、動作的には問題ないですが、単にメッセージを表示するにすぎません。
compare_gpts()で、GPTメインヘッダー/第二ヘッダーの整合性(対応する項目が一致しているか?)をチェックします。もし整合性に問題があったら、その旨の表示するだけで、動作に影響はしません。
GPTメインヘッダーがOKなら、それをgpt_header **gpt, gpt_entry **ptesに設定し、第二ヘッダーとして取得したメモリを解放します。また逆に、第二ヘッダーがOKなら、それをgpt_header **gpt, gpt_entry **ptesに設定し、メインヘッダーとして取得したメモリを解放します。
#define GPT_PRIMARY_PARTITION_TABLE_LBA 1 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); } if (!good_pgpt && !good_agpt) { goto fail; } 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; } 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; } 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); 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; }is_gpt_valid()でGPTテーブルの正当性をチェックし、OKならgpt_header **gpt, gpt_entry **ptesに設定します。u64 lbaは、チェックするセクタ位置になります。
まず、ヘッダーのシグニチャをチェックし、CRCのチェックをしています。(*gpt)->header_crc32 = 0としているのは、生成多項式に(*gpt)->header_crc32も含まれるためだと思います。なおチェックした後元に戻しています。
if (le64_to_cpu((*gpt)->my_lba) != lba)は、読み込む先頭セクタから、GPTテーブルを設定しなければならない。と言うことです。
最後に、GPTエントリを取得し、CRCチェック後、その内容を引数の**ptesに設定します。
#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL static int is_gpt_valid(struct block_device *bdev, u64 lba, gpt_header **gpt, gpt_entry **ptes) { u32 crc, origcrc; if (!bdev || !gpt || !ptes) return 0; if (!(*gpt = alloc_read_gpt_header(bdev, lba))) return 0; if (le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) { Dprintk("GUID Partition Table Header signature is wrong: %" PRIx64 " != %" PRIx64 "\n", le64_to_cpu((*gpt)->signature), GPT_HEADER_SIGNATURE); kfree(*gpt); *gpt = NULL; return 0; } origcrc = le32_to_cpu((*gpt)->header_crc32); (*gpt)->header_crc32 = 0; crc = efi_crc32((const unsigned char *) (*gpt), le32_to_cpu((*gpt)->header_size)); if (crc != origcrc) { Dprintk ("GUID Partition Table Header CRC is wrong: %x != %x\n", crc, origcrc); kfree(*gpt); *gpt = NULL; return 0; } (*gpt)->header_crc32 = cpu_to_le32(origcrc); if (le64_to_cpu((*gpt)->my_lba) != lba) { Dprintk("GPT my_lba incorrect: %" PRIx64 " != %" PRIx64 "\n", le64_to_cpu((*gpt)->my_lba), lba); kfree(*gpt); *gpt = NULL; return 0; } if (!(*ptes = alloc_read_gpt_entries(bdev, *gpt))) { kfree(*gpt); *gpt = NULL; return 0; } crc = efi_crc32((const unsigned char *) (*ptes), le32_to_cpu((*gpt)->num_partition_entries) * le32_to_cpu((*gpt)->sizeof_partition_entry)); if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) { Dprintk("GUID Partitition Entry Array CRC check failed.\n"); kfree(*gpt); *gpt = NULL; kfree(*ptes); *ptes = NULL; return 0; } return 1; }is_pmbr_valid()は、0番目のMBRのチェックです。最終2バイトがブートシグニチャが設定されていて、パーティションのどれかに、パーティションタイプの0xEE(GPT)が設定されているか。
#define MSDOS_MBR_SIGNATURE 0xaa55 #define EFI_PMBR_OSTYPE_EFI_GPT 0xEE static int is_pmbr_valid(legacy_mbr *mbr) { int i, found = 0, signature = 0; if (!mbr) return 0; signature = (le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE); for (i = 0; signature && i < 4; i++) { if (mbr->partition_record[i].sys_ind == EFI_PMBR_OSTYPE_EFI_GPT) { found = 1; break; } } return (signature && found); }compare_gpts()は、2つのGPTテーブル間の正当性チェックを行います。なお整合性がなくても、その旨のメッセージを表示するだけで、動作に影響ありません。
メインヘッダーテーブル内のメインヘッダー位置と第二ヘッダーテーブル内の第二ヘッダー位置が同じ。
メインヘッダーテーブル内の第二ヘッダー位置と第二ヘッダーテーブル内のメインヘッダー位置が同じ。
使用可能LBA領域が、メインヘッダーテーブル/第二ヘッダーテーブルともに同じ。
ディスクGUIDが、メインヘッダーテーブル/第二ヘッダーテーブルともに同じ。
パーティションエントリ数/サイズ/CRCが、メインヘッダーテーブル/第二ヘッダーテーブルともに同じ。
第二ヘッダーがディスクの最終セクタか?
static void compare_gpts(gpt_header *pgpt, gpt_header *agpt, u64 lastlba) { int error_found = 0; if (!pgpt || !agpt) return; if (le64_to_cpu(pgpt->my_lba) != le64_to_cpu(agpt->alternate_lba)) { printk(KERN_WARNING "GPT:Primary header LBA != Alt. header alternate_lba\n"); printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n", le64_to_cpu(pgpt->my_lba), le64_to_cpu(agpt->alternate_lba)); error_found++; } if (le64_to_cpu(pgpt->alternate_lba) != le64_to_cpu(agpt->my_lba)) { printk(KERN_WARNING "GPT:Primary header alternate_lba != Alt. header my_lba\n"); printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n", le64_to_cpu(pgpt->alternate_lba), le64_to_cpu(agpt->my_lba)); error_found++; } if (le64_to_cpu(pgpt->first_usable_lba) != le64_to_cpu(agpt->first_usable_lba)) { printk(KERN_WARNING "GPT:first_usable_lbas don't match.\n"); printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n", le64_to_cpu(pgpt->first_usable_lba), le64_to_cpu(agpt->first_usable_lba)); error_found++; } if (le64_to_cpu(pgpt->last_usable_lba) != le64_to_cpu(agpt->last_usable_lba)) { printk(KERN_WARNING "GPT:last_usable_lbas don't match.\n"); printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n", le64_to_cpu(pgpt->last_usable_lba), le64_to_cpu(agpt->last_usable_lba)); error_found++; } if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) { printk(KERN_WARNING "GPT:disk_guids don't match.\n"); error_found++; } if (le32_to_cpu(pgpt->num_partition_entries) != le32_to_cpu(agpt->num_partition_entries)) { printk(KERN_WARNING "GPT:num_partition_entries don't match: " "0x%x != 0x%x\n", le32_to_cpu(pgpt->num_partition_entries), le32_to_cpu(agpt->num_partition_entries)); error_found++; } if (le32_to_cpu(pgpt->sizeof_partition_entry) != le32_to_cpu(agpt->sizeof_partition_entry)) { printk(KERN_WARNING "GPT:sizeof_partition_entry values don't match: " "0x%x != 0x%x\n", le32_to_cpu(pgpt->sizeof_partition_entry), le32_to_cpu(agpt->sizeof_partition_entry)); error_found++; } if (le32_to_cpu(pgpt->partition_entry_array_crc32) != le32_to_cpu(agpt->partition_entry_array_crc32)) { printk(KERN_WARNING "GPT:partition_entry_array_crc32 values don't match: " "0x%x != 0x%x\n", le32_to_cpu(pgpt->partition_entry_array_crc32), le32_to_cpu(agpt->partition_entry_array_crc32)); error_found++; } if (le64_to_cpu(pgpt->alternate_lba) != lastlba) { printk(KERN_WARNING "GPT:Primary header thinks Alt. header is not at the end of the disk.\n"); printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n", le64_to_cpu(pgpt->alternate_lba), lastlba); error_found++; } if (le64_to_cpu(agpt->my_lba) != lastlba) { printk(KERN_WARNING "GPT:Alternate GPT header not at the end of the disk.\n"); printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n", le64_to_cpu(agpt->my_lba), lastlba); error_found++; } if (error_found) printk(KERN_WARNING "GPT: Use GNU Parted to correct GPT errors.\n"); return; }
補足
force_gptは以下の様に定義されていて、カーネルパラメータ=gptで、force_gpt = 1となり、MBRのマジックNOに関係なく、GPTテーブルからブートする事ができるようになっています。通常の起動では、force_gpt=0で、MBRのマジックNOは必須となります。static int force_gpt; static int __init force_gpt_fn(char *str) { force_gpt = 1; return 1; } __setup("gpt", force_gpt_fn);