[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#281905: please enable CONFIG_EFI_PARTITION; it's needed for > 2TiB



* Christoph Hellwig

 > Including it with that patch sounds reasonable.  If you attach a
 > backport patch to this bug we can make sure to include it.  Else
 > I'm not sure it's going to happen in-time for sarge (or rather at
 > all for 2.6.8 as 2.6.10 has the change already)

  Excellent!  I doubt there's need for any backport, the patch applies
 cleanly to 2.6.8, with the exception of the one-line change to
 fs/partitions/Kconfig, which already seems to be applied to my
 kernel-source 2.6.8-12 tree (it only removes EFI_PARTITIONs dependency
 on IA64).  It's also the only change that's been done to efi.c since
 the version in 2.6.8-12;  if I apply it with "patch -R" to the vanilla
 2.6.10 version, the files become identical (same MD5 checksums, even).

  The patch as applied to Linus' 2.6.10 is available from
 <http://linux.bkbits.net:8080/linux-2.6/cset@419169e8IKUWkmh-tjnN_7LIYR9wBg>.
 As requested I am attaching a copy of it (excluding the already-merged
 changes to Kconfig) to this mail, too.

  I have not yet had a change to test that the kernel actually builds,
 and are able to understand GPTs, with it applied.  I seriously doubt
 that there'll be a problem, though - but I'll try to find time to test
 it tomorrow at work.  I will however not be able to verify that the USB
 problems are gone, as I lack necessary equipment to test it.  Linus
 seems to think it's okay, though - and I assume you'll weigh his opinion
 on the matter over mine anyway.  :-)

  Thank you for your excellent work on the kernel packages!  :-)

Regards,
-- 
Tore Anderson
# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/11/09 17:07:52-08:00 Matt_Domsch@dell.com 
#   [PATCH] EFI GPT: reduce alternate header probing
#   
#   EFI partitioning scheme was reading the last reported sector of the
#   block device to look for the alternate GPT header, before it had
#   confirmed that it should.  This causes problems for devices with the
#   following problems:  a) those who misreport their size (typically
#   off-by-one), and b) those who fail when asked to read a block
#   outside their range.
#   
#   This patch moves the test for the Protective Master Boot Record (PMBR)
#   ahead of the tests for the Primary and Alternate GPT headers.  If the
#   PMBR is not valid, the disk is assumed to not be a GPT disk.  This can
#   be overridden with the 'gpt' kernel command line option.  If the
#   Primary GPT header is not valid, the Alternate GPT header is not
#   probed automatically unless the 'gpt' kernel command line option is
#   passed.  If the both the PMBR and Primary GPT header are valid, then
#   the Alternate GPT header at the end of the disk is probed.
#   
#   Also re-enables CONFIG_EFI_PARTITION for all architectures.
#   
#   Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
#   Signed-off-by: Linus Torvalds <torvalds@osdl.org>
# 
# fs/partitions/efi.c
#   2004/11/09 12:43:34-08:00 Matt_Domsch@dell.com +129 -129
#   EFI GPT: reduce alternate header probing
# 
diff -Nru a/fs/partitions/efi.c b/fs/partitions/efi.c
--- a/fs/partitions/efi.c	2005-01-13 14:43:37 -08:00
+++ b/fs/partitions/efi.c	2005-01-13 14:43:37 -08:00
@@ -3,7 +3,7 @@
  * 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
@@ -23,6 +23,11 @@
  * 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
@@ -131,32 +136,6 @@
 }
 
 /**
- * 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
  * 
@@ -168,7 +147,40 @@
 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;
 }
 
 /**
@@ -186,8 +198,8 @@
 {
 	size_t totalreadcount = 0;
 
-	if (!bdev || !buffer)
-		return 0;
+	if (!bdev || !buffer || lba > last_lba(bdev))
+                return 0;
 
 	while (count) {
 		int copied = 512;
@@ -206,7 +218,6 @@
 	return totalreadcount;
 }
 
-
 /**
  * alloc_read_gpt_entries(): reads partition entries from disk
  * @bdev
@@ -289,6 +300,7 @@
 	     gpt_header **gpt, gpt_entry **ptes)
 {
 	u32 crc, origcrc;
+	u64 lastlba;
 
 	if (!bdev || !gpt || !ptes)
 		return 0;
@@ -301,9 +313,7 @@
 			"%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 */
@@ -315,9 +325,7 @@
 		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);
 
@@ -327,16 +335,28 @@
 		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),
@@ -345,15 +365,36 @@
 
 	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;
 }
 
 /**
@@ -464,8 +505,13 @@
  * @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)
@@ -479,70 +525,43 @@
 		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, "
@@ -550,21 +569,21 @@
                 }
                 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;
@@ -606,15 +625,15 @@
 	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;
@@ -624,22 +643,3 @@
 	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:
- */

Reply to: