* Per Intel EFI Specification v1.02
* http://developer.intel.com/technology/efi/efi.htm
* efi.[ch] by Matt Domsch <Matt_Domsch@dell.com>
- * Copyright 2000,2001,2002 Dell Inc.
+ * Copyright 2000,2001,2002,2004 Dell Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* TODO:
*
* Changelog:
+ * Mon Nov 09 2004 Matt Domsch <Matt_Domsch@dell.com>
+ * - test for valid PMBR and valid PGPT before ever reading
+ * AGPT, allow override with 'gpt' kernel command line option.
+ * - check for first/last_usable_lba outside of size of disk
+ *
* Tue Mar 26 2002 Matt Domsch <Matt_Domsch@dell.com>
* - Ported to 2.5.7-pre1 and 2.5.7-dj2
* - Applied patch to avoid fault in alternate header handling
return (crc32(~0L, buf, len) ^ ~0L);
}
-/**
- * is_pmbr_valid(): test Protective MBR for validity
- * @mbr: pointer to a legacy mbr structure
- *
- * Description: Returns 1 if PMBR is valid, 0 otherwise.
- * Validity depends on two things:
- * 1) MSDOS signature is in the last two bytes of the MBR
- * 2) One partition of type 0xEE is found
- */
-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);
-}
-
/**
* last_lba(): return number of last logical block of device
* @bdev: block device
static u64
last_lba(struct block_device *bdev)
{
- return (bdev->bd_inode->i_size >> 9) - 1;
+ if (!bdev || !bdev->bd_inode)
+ return 0;
+ return (bdev->bd_inode->i_size >> 9) - 1ULL;
+}
+
+static inline int
+pmbr_part_valid(struct partition *part, u64 lastlba)
+{
+ if (part->sys_ind == EFI_PMBR_OSTYPE_EFI_GPT &&
+ le32_to_cpu(part->start_sect) == 1UL)
+ return 1;
+ return 0;
+}
+
+/**
+ * is_pmbr_valid(): test Protective MBR for validity
+ * @mbr: pointer to a legacy mbr structure
+ * @lastlba: last_lba for the whole device
+ *
+ * Description: Returns 1 if PMBR is valid, 0 otherwise.
+ * Validity depends on two things:
+ * 1) MSDOS signature is in the last two bytes of the MBR
+ * 2) One partition of type 0xEE is found
+ */
+static int
+is_pmbr_valid(legacy_mbr *mbr, u64 lastlba)
+{
+ int i;
+ if (!mbr || le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE)
+ return 0;
+ for (i = 0; i < 4; i++)
+ if (pmbr_part_valid(&mbr->partition_record[i], lastlba))
+ return 1;
+ return 0;
}
/**
{
size_t totalreadcount = 0;
- if (!bdev || !buffer)
- return 0;
+ if (!bdev || !buffer || lba > last_lba(bdev))
+ return 0;
while (count) {
int copied = 512;
return totalreadcount;
}
-
/**
* alloc_read_gpt_entries(): reads partition entries from disk
* @bdev
gpt_header **gpt, gpt_entry **ptes)
{
u32 crc, origcrc;
+ u64 lastlba;
if (!bdev || !gpt || !ptes)
return 0;
"%lld != %lld\n",
(unsigned long long)le64_to_cpu((*gpt)->signature),
(unsigned long long)GPT_HEADER_SIGNATURE);
- kfree(*gpt);
- *gpt = NULL;
- return 0;
+ goto fail;
}
/* Check the GUID Partition Table CRC */
Dprintk
("GUID Partition Table Header CRC is wrong: %x != %x\n",
crc, origcrc);
- kfree(*gpt);
- *gpt = NULL;
- return 0;
+ goto fail;
}
(*gpt)->header_crc32 = cpu_to_le32(origcrc);
Dprintk("GPT my_lba incorrect: %lld != %lld\n",
(unsigned long long)le64_to_cpu((*gpt)->my_lba),
(unsigned long long)lba);
- kfree(*gpt);
- *gpt = NULL;
- return 0;
+ goto fail;
}
- if (!(*ptes = alloc_read_gpt_entries(bdev, *gpt))) {
- kfree(*gpt);
- *gpt = NULL;
- return 0;
+ /* Check the first_usable_lba and last_usable_lba are
+ * within the disk.
+ */
+ lastlba = last_lba(bdev);
+ if (le64_to_cpu((*gpt)->first_usable_lba) > lastlba) {
+ Dprintk("GPT: first_usable_lba incorrect: %lld > %lld\n",
+ (unsigned long long)le64_to_cpu((*gpt)->first_usable_lba),
+ (unsigned long long)lastlba);
+ goto fail;
}
+ if (le64_to_cpu((*gpt)->last_usable_lba) > lastlba) {
+ Dprintk("GPT: last_usable_lba incorrect: %lld > %lld\n",
+ (unsigned long long)le64_to_cpu((*gpt)->last_usable_lba),
+ (unsigned long long)lastlba);
+ goto fail;
+ }
+
+ if (!(*ptes = alloc_read_gpt_entries(bdev, *gpt)))
+ goto fail;
/* Check the GUID Partition Entry Array CRC */
crc = efi_crc32((const unsigned char *) (*ptes),
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;
+ goto fail_ptes;
}
/* We're done, all's well */
return 1;
+
+ fail_ptes:
+ kfree(*ptes);
+ *ptes = NULL;
+ fail:
+ kfree(*gpt);
+ *gpt = NULL;
+ return 0;
+}
+
+/**
+ * is_pte_valid() - tests one PTE for validity
+ * @pte is the pte to check
+ * @lastlba is last lba of the disk
+ *
+ * Description: returns 1 if valid, 0 on error.
+ */
+static inline int
+is_pte_valid(const gpt_entry *pte, const u64 lastlba)
+{
+ if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) ||
+ le64_to_cpu(pte->starting_lba) > lastlba ||
+ le64_to_cpu(pte->ending_lba) > lastlba)
+ return 0;
+ return 1;
}
/**
* @ptes is a PTEs ptr, filled on return.
* Description: Returns 1 if valid, 0 on error.
* If valid, returns pointers to newly allocated GPT header and PTEs.
- * Validity depends on finding either the Primary GPT header and PTEs valid,
- * or the Alternate GPT header and PTEs valid, and the PMBR valid.
+ * Validity depends on PMBR being valid (or being overridden by the
+ * 'gpt' kernel command line option) and finding either the Primary
+ * GPT header and PTEs valid, or the Alternate GPT header and PTEs
+ * valid. If the Primary GPT header is not valid, the Alternate GPT header
+ * is not checked unless the 'gpt' kernel command line option is passed.
+ * This protects against devices which misreport their size, and forces
+ * the user to decide to use the Alternate GPT.
*/
static int
find_valid_gpt(struct block_device *bdev, gpt_header **gpt, gpt_entry **ptes)
return 0;
lastlba = last_lba(bdev);
+ if (!force_gpt) {
+ /* 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, lastlba);
+ kfree(legacymbr);
+ legacymbr=NULL;
+ }
+ if (!good_pmbr)
+ goto fail;
+ }
+
good_pgpt = is_gpt_valid(bdev, GPT_PRIMARY_PARTITION_TABLE_LBA,
&pgpt, &pptes);
- if (good_pgpt) {
+ if (good_pgpt)
good_agpt = is_gpt_valid(bdev,
- le64_to_cpu(pgpt->alternate_lba),
+ le64_to_cpu(pgpt->alternate_lba),
&agpt, &aptes);
- if (!good_agpt) {
- good_agpt = is_gpt_valid(bdev, lastlba,
- &agpt, &aptes);
- }
- }
- else {
+ if (!good_agpt && force_gpt)
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");
+ if (!good_pgpt && !good_agpt)
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)) {
+ if (good_pgpt) {
*gpt = pgpt;
*ptes = pptes;
- if (agpt) { kfree(agpt); agpt = NULL; }
- if (aptes) { kfree(aptes); aptes = NULL; }
+ kfree(agpt);
+ kfree(aptes);
if (!good_agpt) {
printk(KERN_WARNING
"Alternate GPT is invalid, "
}
return 1;
}
- else if (good_agpt && (good_pmbr || force_gpt)) {
+ else if (good_agpt) {
*gpt = agpt;
*ptes = aptes;
- if (pgpt) { kfree(pgpt); pgpt = NULL; }
- if (pptes) { kfree(pptes); pptes = NULL; }
+ kfree(pgpt);
+ kfree(pptes);
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; }
+ kfree(pgpt);
+ kfree(agpt);
+ kfree(pptes);
+ kfree(aptes);
*gpt = NULL;
*ptes = NULL;
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))
+ if (!is_pte_valid(&ptes[i], last_lba(bdev)))
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));
+ 1ULL));
- /* If there's this is a RAID volume, tell md */
+ /* If 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;
printk("\n");
return 1;
}
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */