2019-05-01 02:42:40 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 06:20:36 +08:00
|
|
|
/************************************************************
|
|
|
|
* EFI GUID Partition Table handling
|
2009-11-23 16:29:13 +08:00
|
|
|
*
|
|
|
|
* http://www.uefi.org/specs/
|
|
|
|
* http://www.intel.com/technology/efi/
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* efi.[ch] by Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* Copyright 2000,2001,2002,2004 Dell Inc.
|
|
|
|
*
|
|
|
|
* TODO:
|
|
|
|
*
|
|
|
|
* Changelog:
|
2013-09-12 05:25:03 +08:00
|
|
|
* Mon August 5th, 2013 Davidlohr Bueso <davidlohr@hp.com>
|
|
|
|
* - detect hybrid MBRs, tighter pMBR checking & cleanups.
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* 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
|
|
|
|
* - cleaned up find_valid_gpt
|
|
|
|
* - On-disk structure and copy in memory is *always* LE now -
|
|
|
|
* swab fields as needed
|
|
|
|
* - remove print_gpt_header()
|
|
|
|
* - only use first max_p partition entries, to keep the kernel minor number
|
|
|
|
* and partition numbers tied.
|
|
|
|
*
|
|
|
|
* Mon Feb 04 2002 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Removed __PRIPTR_PREFIX - not being used
|
|
|
|
*
|
|
|
|
* Mon Jan 14 2002 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Ported to 2.5.2-pre11 + library crc32 patch Linus applied
|
|
|
|
*
|
|
|
|
* Thu Dec 6 2001 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Added compare_gpts().
|
|
|
|
* - moved le_efi_guid_to_cpus() back into this file. GPT is the only
|
|
|
|
* thing that keeps EFI GUIDs on disk.
|
|
|
|
* - Changed gpt structure names and members to be simpler and more Linux-like.
|
|
|
|
*
|
|
|
|
* Wed Oct 17 2001 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Removed CONFIG_DEVFS_VOLUMES_UUID code entirely per Martin Wilck
|
|
|
|
*
|
|
|
|
* Wed Oct 10 2001 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Changed function comments to DocBook style per Andreas Dilger suggestion.
|
|
|
|
*
|
|
|
|
* Mon Oct 08 2001 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Change read_lba() to use the page cache per Al Viro's work.
|
|
|
|
* - print u64s properly on all architectures
|
|
|
|
* - fixed debug_printk(), now Dprintk()
|
|
|
|
*
|
|
|
|
* Mon Oct 01 2001 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Style cleanups
|
|
|
|
* - made most functions static
|
|
|
|
* - Endianness addition
|
|
|
|
* - remove test for second alternate header, as it's not per spec,
|
|
|
|
* and is unnecessary. There's now a method to read/write the last
|
|
|
|
* sector of an odd-sized disk from user space. No tools have ever
|
|
|
|
* been released which used this code, so it's effectively dead.
|
|
|
|
* - Per Asit Mallick of Intel, added a test for a valid PMBR.
|
|
|
|
* - Added kernel command line option 'gpt' to override valid PMBR test.
|
|
|
|
*
|
|
|
|
* Wed Jun 6 2001 Martin Wilck <Martin.Wilck@Fujitsu-Siemens.com>
|
|
|
|
* - added devfs volume UUID support (/dev/volumes/uuids) for
|
|
|
|
* mounting file systems by the partition GUID.
|
|
|
|
*
|
|
|
|
* Tue Dec 5 2000 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Moved crc32() to linux/lib, added efi_crc32().
|
|
|
|
*
|
|
|
|
* Thu Nov 30 2000 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Replaced Intel's CRC32 function with an equivalent
|
|
|
|
* non-license-restricted version.
|
|
|
|
*
|
|
|
|
* Wed Oct 25 2000 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Fixed the last_lba() call to return the proper last block
|
|
|
|
*
|
|
|
|
* Thu Oct 12 2000 Matt Domsch <Matt_Domsch@dell.com>
|
|
|
|
* - Thanks to Andries Brouwer for his debugging assistance.
|
|
|
|
* - Code works, detects all the partitions.
|
|
|
|
*
|
|
|
|
************************************************************/
|
2013-11-22 06:32:05 +08:00
|
|
|
#include <linux/kernel.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/crc32.h>
|
2010-09-01 04:47:06 +08:00
|
|
|
#include <linux/ctype.h>
|
2009-11-23 16:29:13 +08:00
|
|
|
#include <linux/math64.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "check.h"
|
|
|
|
#include "efi.h"
|
|
|
|
|
|
|
|
/* This allows a kernel command line option 'gpt' to override
|
|
|
|
* the test for invalid PMBR. Not __initdata because reloading
|
|
|
|
* the partition tables happens after init too.
|
|
|
|
*/
|
|
|
|
static int force_gpt;
|
|
|
|
static int __init
|
|
|
|
force_gpt_fn(char *str)
|
|
|
|
{
|
|
|
|
force_gpt = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
__setup("gpt", force_gpt_fn);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* efi_crc32() - EFI version of crc32 function
|
|
|
|
* @buf: buffer to calculate crc32 of
|
2014-06-13 02:26:01 +08:00
|
|
|
* @len: length of buf
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Description: Returns EFI-style CRC32 value for @buf
|
|
|
|
*
|
|
|
|
* This function uses the little endian Ethernet polynomial
|
|
|
|
* but seeds the function with ~0, and xor's with ~0 at the end.
|
|
|
|
* Note, the EFI Specification, v1.02, has a reference to
|
|
|
|
* Dr. Dobbs Journal, May 1994 (actually it's in May 1992).
|
|
|
|
*/
|
|
|
|
static inline u32
|
|
|
|
efi_crc32(const void *buf, unsigned long len)
|
|
|
|
{
|
|
|
|
return (crc32(~0L, buf, len) ^ ~0L);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* last_lba(): return number of last logical block of device
|
2021-08-10 23:45:09 +08:00
|
|
|
* @disk: block device
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Description: Returns last LBA value on success, 0 on error.
|
|
|
|
* This is stored (by sd and ide-geometry) in
|
|
|
|
* the part[0] entry for this disk, and is the number of
|
|
|
|
* physical sectors available on the disk.
|
|
|
|
*/
|
2021-08-10 23:45:09 +08:00
|
|
|
static u64 last_lba(struct gendisk *disk)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2021-08-10 23:45:09 +08:00
|
|
|
return div_u64(disk->part0->bd_inode->i_size,
|
|
|
|
queue_logical_block_size(disk->queue)) - 1ULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-09-12 05:24:55 +08:00
|
|
|
static inline int pmbr_part_valid(gpt_mbr_record *part)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-09-12 05:24:56 +08:00
|
|
|
if (part->os_type != EFI_PMBR_OSTYPE_EFI_GPT)
|
|
|
|
goto invalid;
|
|
|
|
|
|
|
|
/* set to 0x00000001 (i.e., the LBA of the GPT Partition Header) */
|
|
|
|
if (le32_to_cpu(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA)
|
|
|
|
goto invalid;
|
|
|
|
|
partitions/efi: detect hybrid MBRs
One of the biggest problems with GPT is compatibility with older, non-GPT
systems. The problem is addressed by creating hybrid mbrs, an extension,
or variant, of the traditional protective mbr. This contains, apart from
the 0xEE partition, up three additional primary partitions that point to
the same space marked by up to three GPT partitions. The result is that
legacy OSs can see the three required MBR partitions and at the same time
ignore the GPT-aware partitions that protect the GPT structures.
While hybrid MBRs are hacks, workarounds and simply not part of the GPT
standard, they do exist and we have no way around them. For instance, by
default, OSX creates a hybrid scheme when using multi-OS booting.
In order for Linux to properly discover protective MBRs, it must be made
aware of devices that have hybrid MBRs. No functionality is changed by
this patch, just a debug message informing the user of the MBR scheme that
is being used.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
Reviewed-by: Karel Zak <kzak@redhat.com>
Acked-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-12 05:24:58 +08:00
|
|
|
return GPT_MBR_PROTECTIVE;
|
2013-09-12 05:24:56 +08:00
|
|
|
invalid:
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* is_pmbr_valid(): test Protective MBR for validity
|
|
|
|
* @mbr: pointer to a legacy mbr structure
|
2013-09-12 05:25:00 +08:00
|
|
|
* @total_sectors: amount of sectors in the device
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
partitions/efi: detect hybrid MBRs
One of the biggest problems with GPT is compatibility with older, non-GPT
systems. The problem is addressed by creating hybrid mbrs, an extension,
or variant, of the traditional protective mbr. This contains, apart from
the 0xEE partition, up three additional primary partitions that point to
the same space marked by up to three GPT partitions. The result is that
legacy OSs can see the three required MBR partitions and at the same time
ignore the GPT-aware partitions that protect the GPT structures.
While hybrid MBRs are hacks, workarounds and simply not part of the GPT
standard, they do exist and we have no way around them. For instance, by
default, OSX creates a hybrid scheme when using multi-OS booting.
In order for Linux to properly discover protective MBRs, it must be made
aware of devices that have hybrid MBRs. No functionality is changed by
this patch, just a debug message informing the user of the MBR scheme that
is being used.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
Reviewed-by: Karel Zak <kzak@redhat.com>
Acked-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-12 05:24:58 +08:00
|
|
|
* Description: Checks for a valid protective or hybrid
|
|
|
|
* master boot record (MBR). The validity of a pMBR depends
|
|
|
|
* on all of the following properties:
|
2005-04-17 06:20:36 +08:00
|
|
|
* 1) MSDOS signature is in the last two bytes of the MBR
|
|
|
|
* 2) One partition of type 0xEE is found
|
partitions/efi: detect hybrid MBRs
One of the biggest problems with GPT is compatibility with older, non-GPT
systems. The problem is addressed by creating hybrid mbrs, an extension,
or variant, of the traditional protective mbr. This contains, apart from
the 0xEE partition, up three additional primary partitions that point to
the same space marked by up to three GPT partitions. The result is that
legacy OSs can see the three required MBR partitions and at the same time
ignore the GPT-aware partitions that protect the GPT structures.
While hybrid MBRs are hacks, workarounds and simply not part of the GPT
standard, they do exist and we have no way around them. For instance, by
default, OSX creates a hybrid scheme when using multi-OS booting.
In order for Linux to properly discover protective MBRs, it must be made
aware of devices that have hybrid MBRs. No functionality is changed by
this patch, just a debug message informing the user of the MBR scheme that
is being used.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
Reviewed-by: Karel Zak <kzak@redhat.com>
Acked-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-12 05:24:58 +08:00
|
|
|
*
|
|
|
|
* In addition, a hybrid MBR will have up to three additional
|
|
|
|
* primary partitions, which point to the same space that's
|
|
|
|
* marked out by up to three GPT partitions.
|
|
|
|
*
|
|
|
|
* Returns 0 upon invalid MBR, or GPT_MBR_PROTECTIVE or
|
|
|
|
* GPT_MBR_HYBRID depending on the device layout.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2013-09-12 05:25:00 +08:00
|
|
|
static int is_pmbr_valid(legacy_mbr *mbr, sector_t total_sectors)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-09-14 06:02:22 +08:00
|
|
|
uint32_t sz = 0;
|
2013-09-12 05:25:00 +08:00
|
|
|
int i, part = 0, ret = 0; /* invalid by default */
|
partitions/efi: detect hybrid MBRs
One of the biggest problems with GPT is compatibility with older, non-GPT
systems. The problem is addressed by creating hybrid mbrs, an extension,
or variant, of the traditional protective mbr. This contains, apart from
the 0xEE partition, up three additional primary partitions that point to
the same space marked by up to three GPT partitions. The result is that
legacy OSs can see the three required MBR partitions and at the same time
ignore the GPT-aware partitions that protect the GPT structures.
While hybrid MBRs are hacks, workarounds and simply not part of the GPT
standard, they do exist and we have no way around them. For instance, by
default, OSX creates a hybrid scheme when using multi-OS booting.
In order for Linux to properly discover protective MBRs, it must be made
aware of devices that have hybrid MBRs. No functionality is changed by
this patch, just a debug message informing the user of the MBR scheme that
is being used.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
Reviewed-by: Karel Zak <kzak@redhat.com>
Acked-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-12 05:24:58 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!mbr || le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE)
|
partitions/efi: detect hybrid MBRs
One of the biggest problems with GPT is compatibility with older, non-GPT
systems. The problem is addressed by creating hybrid mbrs, an extension,
or variant, of the traditional protective mbr. This contains, apart from
the 0xEE partition, up three additional primary partitions that point to
the same space marked by up to three GPT partitions. The result is that
legacy OSs can see the three required MBR partitions and at the same time
ignore the GPT-aware partitions that protect the GPT structures.
While hybrid MBRs are hacks, workarounds and simply not part of the GPT
standard, they do exist and we have no way around them. For instance, by
default, OSX creates a hybrid scheme when using multi-OS booting.
In order for Linux to properly discover protective MBRs, it must be made
aware of devices that have hybrid MBRs. No functionality is changed by
this patch, just a debug message informing the user of the MBR scheme that
is being used.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
Reviewed-by: Karel Zak <kzak@redhat.com>
Acked-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-12 05:24:58 +08:00
|
|
|
goto done;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
ret = pmbr_part_valid(&mbr->partition_record[i]);
|
|
|
|
if (ret == GPT_MBR_PROTECTIVE) {
|
2013-09-12 05:25:00 +08:00
|
|
|
part = i;
|
partitions/efi: detect hybrid MBRs
One of the biggest problems with GPT is compatibility with older, non-GPT
systems. The problem is addressed by creating hybrid mbrs, an extension,
or variant, of the traditional protective mbr. This contains, apart from
the 0xEE partition, up three additional primary partitions that point to
the same space marked by up to three GPT partitions. The result is that
legacy OSs can see the three required MBR partitions and at the same time
ignore the GPT-aware partitions that protect the GPT structures.
While hybrid MBRs are hacks, workarounds and simply not part of the GPT
standard, they do exist and we have no way around them. For instance, by
default, OSX creates a hybrid scheme when using multi-OS booting.
In order for Linux to properly discover protective MBRs, it must be made
aware of devices that have hybrid MBRs. No functionality is changed by
this patch, just a debug message informing the user of the MBR scheme that
is being used.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
Reviewed-by: Karel Zak <kzak@redhat.com>
Acked-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-12 05:24:58 +08:00
|
|
|
/*
|
|
|
|
* Ok, we at least know that there's a protective MBR,
|
|
|
|
* now check if there are other partition types for
|
|
|
|
* hybrid MBR.
|
|
|
|
*/
|
|
|
|
goto check_hybrid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret != GPT_MBR_PROTECTIVE)
|
|
|
|
goto done;
|
|
|
|
check_hybrid:
|
2005-04-17 06:20:36 +08:00
|
|
|
for (i = 0; i < 4; i++)
|
partitions/efi: detect hybrid MBRs
One of the biggest problems with GPT is compatibility with older, non-GPT
systems. The problem is addressed by creating hybrid mbrs, an extension,
or variant, of the traditional protective mbr. This contains, apart from
the 0xEE partition, up three additional primary partitions that point to
the same space marked by up to three GPT partitions. The result is that
legacy OSs can see the three required MBR partitions and at the same time
ignore the GPT-aware partitions that protect the GPT structures.
While hybrid MBRs are hacks, workarounds and simply not part of the GPT
standard, they do exist and we have no way around them. For instance, by
default, OSX creates a hybrid scheme when using multi-OS booting.
In order for Linux to properly discover protective MBRs, it must be made
aware of devices that have hybrid MBRs. No functionality is changed by
this patch, just a debug message informing the user of the MBR scheme that
is being used.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
Reviewed-by: Karel Zak <kzak@redhat.com>
Acked-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-12 05:24:58 +08:00
|
|
|
if ((mbr->partition_record[i].os_type !=
|
|
|
|
EFI_PMBR_OSTYPE_EFI_GPT) &&
|
|
|
|
(mbr->partition_record[i].os_type != 0x00))
|
|
|
|
ret = GPT_MBR_HYBRID;
|
2013-09-12 05:25:00 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Protective MBRs take up the lesser of the whole disk
|
|
|
|
* or 2 TiB (32bit LBA), ignoring the rest of the disk.
|
2013-09-14 06:02:22 +08:00
|
|
|
* Some partitioning programs, nonetheless, choose to set
|
|
|
|
* the size to the maximum 32-bit limitation, disregarding
|
|
|
|
* the disk size.
|
2013-09-12 05:25:00 +08:00
|
|
|
*
|
|
|
|
* Hybrid MBRs do not necessarily comply with this.
|
2013-10-17 04:46:57 +08:00
|
|
|
*
|
|
|
|
* Consider a bad value here to be a warning to support dd'ing
|
|
|
|
* an image from a smaller disk to a larger disk.
|
2013-09-12 05:25:00 +08:00
|
|
|
*/
|
|
|
|
if (ret == GPT_MBR_PROTECTIVE) {
|
2013-09-14 06:02:22 +08:00
|
|
|
sz = le32_to_cpu(mbr->partition_record[part].size_in_lba);
|
|
|
|
if (sz != (uint32_t) total_sectors - 1 && sz != 0xFFFFFFFF)
|
2013-10-17 04:46:57 +08:00
|
|
|
pr_debug("GPT: mbr size in lba (%u) different than whole disk (%u).\n",
|
|
|
|
sz, min_t(uint32_t,
|
|
|
|
total_sectors - 1, 0xFFFFFFFF));
|
2013-09-12 05:25:00 +08:00
|
|
|
}
|
partitions/efi: detect hybrid MBRs
One of the biggest problems with GPT is compatibility with older, non-GPT
systems. The problem is addressed by creating hybrid mbrs, an extension,
or variant, of the traditional protective mbr. This contains, apart from
the 0xEE partition, up three additional primary partitions that point to
the same space marked by up to three GPT partitions. The result is that
legacy OSs can see the three required MBR partitions and at the same time
ignore the GPT-aware partitions that protect the GPT structures.
While hybrid MBRs are hacks, workarounds and simply not part of the GPT
standard, they do exist and we have no way around them. For instance, by
default, OSX creates a hybrid scheme when using multi-OS booting.
In order for Linux to properly discover protective MBRs, it must be made
aware of devices that have hybrid MBRs. No functionality is changed by
this patch, just a debug message informing the user of the MBR scheme that
is being used.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
Reviewed-by: Karel Zak <kzak@redhat.com>
Acked-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-12 05:24:58 +08:00
|
|
|
done:
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* read_lba(): Read bytes from disk, starting at given LBA
|
2014-06-13 02:26:01 +08:00
|
|
|
* @state: disk parsed partitions
|
|
|
|
* @lba: the Logical Block Address of the partition table
|
|
|
|
* @buffer: destination buffer
|
|
|
|
* @count: bytes to read
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2021-08-10 23:45:09 +08:00
|
|
|
* Description: Reads @count bytes from @state->disk into @buffer.
|
2005-04-17 06:20:36 +08:00
|
|
|
* Returns number of bytes read on success, 0 on error.
|
|
|
|
*/
|
2010-05-16 02:09:30 +08:00
|
|
|
static size_t read_lba(struct parsed_partitions *state,
|
|
|
|
u64 lba, u8 *buffer, size_t count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
size_t totalreadcount = 0;
|
2021-08-10 23:45:09 +08:00
|
|
|
sector_t n = lba *
|
|
|
|
(queue_logical_block_size(state->disk->queue) / 512);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-08-10 23:45:09 +08:00
|
|
|
if (!buffer || lba > last_lba(state->disk))
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (count) {
|
|
|
|
int copied = 512;
|
|
|
|
Sector sect;
|
2010-05-16 02:09:30 +08:00
|
|
|
unsigned char *data = read_part_sector(state, n++, §);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!data)
|
|
|
|
break;
|
|
|
|
if (copied > count)
|
|
|
|
copied = count;
|
|
|
|
memcpy(buffer, data, copied);
|
|
|
|
put_dev_sector(sect);
|
|
|
|
buffer += copied;
|
|
|
|
totalreadcount +=copied;
|
|
|
|
count -= copied;
|
|
|
|
}
|
|
|
|
return totalreadcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* alloc_read_gpt_entries(): reads partition entries from disk
|
2014-06-13 02:26:01 +08:00
|
|
|
* @state: disk parsed partitions
|
|
|
|
* @gpt: GPT header
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Description: Returns ptes on success, NULL on error.
|
|
|
|
* Allocates space for PTEs based on information found in @gpt.
|
|
|
|
* Notes: remember to free pte when you're done!
|
|
|
|
*/
|
2010-05-16 02:09:30 +08:00
|
|
|
static gpt_entry *alloc_read_gpt_entries(struct parsed_partitions *state,
|
|
|
|
gpt_header *gpt)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
size_t count;
|
|
|
|
gpt_entry *pte;
|
2010-05-16 02:09:30 +08:00
|
|
|
|
|
|
|
if (!gpt)
|
2005-04-17 06:20:36 +08:00
|
|
|
return NULL;
|
|
|
|
|
2017-01-16 06:31:56 +08:00
|
|
|
count = (size_t)le32_to_cpu(gpt->num_partition_entries) *
|
2005-04-17 06:20:36 +08:00
|
|
|
le32_to_cpu(gpt->sizeof_partition_entry);
|
|
|
|
if (!count)
|
|
|
|
return NULL;
|
2013-04-30 05:00:18 +08:00
|
|
|
pte = kmalloc(count, GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!pte)
|
|
|
|
return NULL;
|
|
|
|
|
2010-05-16 02:09:30 +08:00
|
|
|
if (read_lba(state, le64_to_cpu(gpt->partition_entry_lba),
|
2013-09-12 05:25:03 +08:00
|
|
|
(u8 *) pte, count) < count) {
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(pte);
|
|
|
|
pte=NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* alloc_read_gpt_header(): Allocates GPT header, reads into it from disk
|
2014-06-13 02:26:01 +08:00
|
|
|
* @state: disk parsed partitions
|
|
|
|
* @lba: the Logical Block Address of the partition table
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Description: returns GPT header on success, NULL on error. Allocates
|
2021-08-10 23:45:09 +08:00
|
|
|
* and fills a GPT header starting at @ from @state->disk.
|
2005-04-17 06:20:36 +08:00
|
|
|
* Note: remember to free gpt when finished with it.
|
|
|
|
*/
|
2010-05-16 02:09:30 +08:00
|
|
|
static gpt_header *alloc_read_gpt_header(struct parsed_partitions *state,
|
|
|
|
u64 lba)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
gpt_header *gpt;
|
2021-08-10 23:45:09 +08:00
|
|
|
unsigned ssz = queue_logical_block_size(state->disk->queue);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-04-30 05:00:18 +08:00
|
|
|
gpt = kmalloc(ssz, GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!gpt)
|
|
|
|
return NULL;
|
|
|
|
|
2010-05-16 02:09:30 +08:00
|
|
|
if (read_lba(state, lba, (u8 *) gpt, ssz) < ssz) {
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(gpt);
|
|
|
|
gpt=NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return gpt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* is_gpt_valid() - tests one GPT header and PTEs for validity
|
2014-06-13 02:26:01 +08:00
|
|
|
* @state: disk parsed partitions
|
|
|
|
* @lba: logical block address of the GPT header to test
|
|
|
|
* @gpt: GPT header ptr, filled on return.
|
|
|
|
* @ptes: PTEs ptr, filled on return.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Description: returns 1 if valid, 0 on error.
|
|
|
|
* If valid, returns pointers to newly allocated GPT header and PTEs.
|
|
|
|
*/
|
2010-05-16 02:09:30 +08:00
|
|
|
static int is_gpt_valid(struct parsed_partitions *state, u64 lba,
|
|
|
|
gpt_header **gpt, gpt_entry **ptes)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
u32 crc, origcrc;
|
2017-01-16 06:31:56 +08:00
|
|
|
u64 lastlba, pt_size;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-05-16 02:09:30 +08:00
|
|
|
if (!ptes)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2010-05-16 02:09:30 +08:00
|
|
|
if (!(*gpt = alloc_read_gpt_header(state, lba)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Check the GUID Partition Table signature */
|
|
|
|
if (le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) {
|
2008-07-25 16:48:26 +08:00
|
|
|
pr_debug("GUID Partition Table Header signature is wrong:"
|
|
|
|
"%lld != %lld\n",
|
|
|
|
(unsigned long long)le64_to_cpu((*gpt)->signature),
|
|
|
|
(unsigned long long)GPT_HEADER_SIGNATURE);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2013-02-28 09:05:17 +08:00
|
|
|
/* Check the GUID Partition Table header size is too big */
|
2011-05-27 07:25:57 +08:00
|
|
|
if (le32_to_cpu((*gpt)->header_size) >
|
2021-08-10 23:45:09 +08:00
|
|
|
queue_logical_block_size(state->disk->queue)) {
|
2013-02-28 09:05:17 +08:00
|
|
|
pr_debug("GUID Partition Table Header size is too large: %u > %u\n",
|
2011-05-27 07:25:57 +08:00
|
|
|
le32_to_cpu((*gpt)->header_size),
|
2021-08-10 23:45:09 +08:00
|
|
|
queue_logical_block_size(state->disk->queue));
|
2011-05-27 07:25:57 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2013-02-28 09:05:17 +08:00
|
|
|
/* Check the GUID Partition Table header size is too small */
|
|
|
|
if (le32_to_cpu((*gpt)->header_size) < sizeof(gpt_header)) {
|
|
|
|
pr_debug("GUID Partition Table Header size is too small: %u < %zu\n",
|
|
|
|
le32_to_cpu((*gpt)->header_size),
|
|
|
|
sizeof(gpt_header));
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Check the GUID Partition Table CRC */
|
|
|
|
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) {
|
2008-07-25 16:48:26 +08:00
|
|
|
pr_debug("GUID Partition Table Header CRC is wrong: %x != %x\n",
|
|
|
|
crc, origcrc);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
(*gpt)->header_crc32 = cpu_to_le32(origcrc);
|
|
|
|
|
|
|
|
/* Check that the my_lba entry points to the LBA that contains
|
|
|
|
* the GUID Partition Table */
|
|
|
|
if (le64_to_cpu((*gpt)->my_lba) != lba) {
|
2008-07-25 16:48:26 +08:00
|
|
|
pr_debug("GPT my_lba incorrect: %lld != %lld\n",
|
|
|
|
(unsigned long long)le64_to_cpu((*gpt)->my_lba),
|
|
|
|
(unsigned long long)lba);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check the first_usable_lba and last_usable_lba are
|
|
|
|
* within the disk.
|
|
|
|
*/
|
2021-08-10 23:45:09 +08:00
|
|
|
lastlba = last_lba(state->disk);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (le64_to_cpu((*gpt)->first_usable_lba) > lastlba) {
|
2008-07-25 16:48:26 +08:00
|
|
|
pr_debug("GPT: first_usable_lba incorrect: %lld > %lld\n",
|
|
|
|
(unsigned long long)le64_to_cpu((*gpt)->first_usable_lba),
|
|
|
|
(unsigned long long)lastlba);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (le64_to_cpu((*gpt)->last_usable_lba) > lastlba) {
|
2008-07-25 16:48:26 +08:00
|
|
|
pr_debug("GPT: last_usable_lba incorrect: %lld > %lld\n",
|
|
|
|
(unsigned long long)le64_to_cpu((*gpt)->last_usable_lba),
|
|
|
|
(unsigned long long)lastlba);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
2013-09-12 05:25:01 +08:00
|
|
|
if (le64_to_cpu((*gpt)->last_usable_lba) < le64_to_cpu((*gpt)->first_usable_lba)) {
|
|
|
|
pr_debug("GPT: last_usable_lba incorrect: %lld > %lld\n",
|
|
|
|
(unsigned long long)le64_to_cpu((*gpt)->last_usable_lba),
|
|
|
|
(unsigned long long)le64_to_cpu((*gpt)->first_usable_lba));
|
|
|
|
goto fail;
|
|
|
|
}
|
2011-05-06 19:47:35 +08:00
|
|
|
/* Check that sizeof_partition_entry has the correct value */
|
|
|
|
if (le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) {
|
2016-02-08 19:53:12 +08:00
|
|
|
pr_debug("GUID Partition Entry Size check failed.\n");
|
2011-05-06 19:47:35 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2017-01-16 06:31:56 +08:00
|
|
|
/* Sanity check partition table size */
|
|
|
|
pt_size = (u64)le32_to_cpu((*gpt)->num_partition_entries) *
|
|
|
|
le32_to_cpu((*gpt)->sizeof_partition_entry);
|
|
|
|
if (pt_size > KMALLOC_MAX_SIZE) {
|
|
|
|
pr_debug("GUID Partition Table is too large: %llu > %lu bytes\n",
|
|
|
|
(unsigned long long)pt_size, KMALLOC_MAX_SIZE);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2010-05-16 02:09:30 +08:00
|
|
|
if (!(*ptes = alloc_read_gpt_entries(state, *gpt)))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/* Check the GUID Partition Entry Array CRC */
|
2017-01-16 06:31:56 +08:00
|
|
|
crc = efi_crc32((const unsigned char *) (*ptes), pt_size);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
|
2016-02-08 19:53:12 +08:00
|
|
|
pr_debug("GUID Partition Entry Array CRC check failed.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
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
|
2014-06-13 02:26:01 +08:00
|
|
|
* @pte:pte to check
|
|
|
|
* @lastlba: last lba of the disk
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* compare_gpts() - Search disk for valid GPT headers and PTEs
|
2014-06-13 02:26:01 +08:00
|
|
|
* @pgpt: primary GPT header
|
|
|
|
* @agpt: alternate GPT header
|
|
|
|
* @lastlba: last LBA number
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Description: Returns nothing. Sanity checks pgpt and agpt fields
|
|
|
|
* and prints warnings on discrepancies.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
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)) {
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT:Primary header LBA != Alt. header alternate_lba\n");
|
|
|
|
pr_warn("GPT:%lld != %lld\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
(unsigned long long)le64_to_cpu(pgpt->my_lba),
|
|
|
|
(unsigned long long)le64_to_cpu(agpt->alternate_lba));
|
|
|
|
error_found++;
|
|
|
|
}
|
|
|
|
if (le64_to_cpu(pgpt->alternate_lba) != le64_to_cpu(agpt->my_lba)) {
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT:Primary header alternate_lba != Alt. header my_lba\n");
|
|
|
|
pr_warn("GPT:%lld != %lld\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
(unsigned long long)le64_to_cpu(pgpt->alternate_lba),
|
|
|
|
(unsigned long long)le64_to_cpu(agpt->my_lba));
|
|
|
|
error_found++;
|
|
|
|
}
|
|
|
|
if (le64_to_cpu(pgpt->first_usable_lba) !=
|
|
|
|
le64_to_cpu(agpt->first_usable_lba)) {
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT:first_usable_lbas don't match.\n");
|
|
|
|
pr_warn("GPT:%lld != %lld\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
(unsigned long long)le64_to_cpu(pgpt->first_usable_lba),
|
|
|
|
(unsigned long long)le64_to_cpu(agpt->first_usable_lba));
|
|
|
|
error_found++;
|
|
|
|
}
|
|
|
|
if (le64_to_cpu(pgpt->last_usable_lba) !=
|
|
|
|
le64_to_cpu(agpt->last_usable_lba)) {
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT:last_usable_lbas don't match.\n");
|
|
|
|
pr_warn("GPT:%lld != %lld\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
(unsigned long long)le64_to_cpu(pgpt->last_usable_lba),
|
|
|
|
(unsigned long long)le64_to_cpu(agpt->last_usable_lba));
|
|
|
|
error_found++;
|
|
|
|
}
|
|
|
|
if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) {
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT:disk_guids don't match.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
error_found++;
|
|
|
|
}
|
|
|
|
if (le32_to_cpu(pgpt->num_partition_entries) !=
|
|
|
|
le32_to_cpu(agpt->num_partition_entries)) {
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT:num_partition_entries don't match: "
|
2005-04-17 06:20:36 +08:00
|
|
|
"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)) {
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT:sizeof_partition_entry values don't match: "
|
2005-04-17 06:20:36 +08:00
|
|
|
"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)) {
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT:partition_entry_array_crc32 values don't match: "
|
2005-04-17 06:20:36 +08:00
|
|
|
"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) {
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT:Primary header thinks Alt. header is not at the end of the disk.\n");
|
|
|
|
pr_warn("GPT:%lld != %lld\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
(unsigned long long)le64_to_cpu(pgpt->alternate_lba),
|
|
|
|
(unsigned long long)lastlba);
|
|
|
|
error_found++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (le64_to_cpu(agpt->my_lba) != lastlba) {
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT:Alternate GPT header not at the end of the disk.\n");
|
|
|
|
pr_warn("GPT:%lld != %lld\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
(unsigned long long)le64_to_cpu(agpt->my_lba),
|
|
|
|
(unsigned long long)lastlba);
|
|
|
|
error_found++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error_found)
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("GPT: Use GNU Parted to correct GPT errors.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* find_valid_gpt() - Search disk for valid GPT headers and PTEs
|
2014-06-13 02:26:01 +08:00
|
|
|
* @state: disk parsed partitions
|
|
|
|
* @gpt: GPT header ptr, filled on return.
|
|
|
|
* @ptes: PTEs ptr, filled on return.
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Description: Returns 1 if valid, 0 on error.
|
|
|
|
* If valid, returns pointers to newly allocated GPT header and PTEs.
|
|
|
|
* 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.
|
|
|
|
*/
|
2010-05-16 02:09:30 +08:00
|
|
|
static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt,
|
|
|
|
gpt_entry **ptes)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
|
|
|
|
gpt_header *pgpt = NULL, *agpt = NULL;
|
|
|
|
gpt_entry *pptes = NULL, *aptes = NULL;
|
2007-05-11 13:22:42 +08:00
|
|
|
legacy_mbr *legacymbr;
|
2021-08-20 08:45:34 +08:00
|
|
|
struct gendisk *disk = state->disk;
|
|
|
|
const struct block_device_operations *fops = disk->fops;
|
2021-08-10 23:45:09 +08:00
|
|
|
sector_t total_sectors = get_capacity(state->disk);
|
2005-04-17 06:20:36 +08:00
|
|
|
u64 lastlba;
|
2010-05-16 02:09:30 +08:00
|
|
|
|
|
|
|
if (!ptes)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
2021-08-10 23:45:09 +08:00
|
|
|
lastlba = last_lba(state->disk);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!force_gpt) {
|
partitions/efi: detect hybrid MBRs
One of the biggest problems with GPT is compatibility with older, non-GPT
systems. The problem is addressed by creating hybrid mbrs, an extension,
or variant, of the traditional protective mbr. This contains, apart from
the 0xEE partition, up three additional primary partitions that point to
the same space marked by up to three GPT partitions. The result is that
legacy OSs can see the three required MBR partitions and at the same time
ignore the GPT-aware partitions that protect the GPT structures.
While hybrid MBRs are hacks, workarounds and simply not part of the GPT
standard, they do exist and we have no way around them. For instance, by
default, OSX creates a hybrid scheme when using multi-OS booting.
In order for Linux to properly discover protective MBRs, it must be made
aware of devices that have hybrid MBRs. No functionality is changed by
this patch, just a debug message informing the user of the MBR scheme that
is being used.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
Reviewed-by: Karel Zak <kzak@redhat.com>
Acked-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-12 05:24:58 +08:00
|
|
|
/* This will be added to the EFI Spec. per Intel after v1.02. */
|
|
|
|
legacymbr = kzalloc(sizeof(*legacymbr), GFP_KERNEL);
|
|
|
|
if (!legacymbr)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
read_lba(state, 0, (u8 *)legacymbr, sizeof(*legacymbr));
|
2013-09-12 05:25:00 +08:00
|
|
|
good_pmbr = is_pmbr_valid(legacymbr, total_sectors);
|
partitions/efi: detect hybrid MBRs
One of the biggest problems with GPT is compatibility with older, non-GPT
systems. The problem is addressed by creating hybrid mbrs, an extension,
or variant, of the traditional protective mbr. This contains, apart from
the 0xEE partition, up three additional primary partitions that point to
the same space marked by up to three GPT partitions. The result is that
legacy OSs can see the three required MBR partitions and at the same time
ignore the GPT-aware partitions that protect the GPT structures.
While hybrid MBRs are hacks, workarounds and simply not part of the GPT
standard, they do exist and we have no way around them. For instance, by
default, OSX creates a hybrid scheme when using multi-OS booting.
In order for Linux to properly discover protective MBRs, it must be made
aware of devices that have hybrid MBRs. No functionality is changed by
this patch, just a debug message informing the user of the MBR scheme that
is being used.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
Reviewed-by: Karel Zak <kzak@redhat.com>
Acked-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-12 05:24:58 +08:00
|
|
|
kfree(legacymbr);
|
|
|
|
|
|
|
|
if (!good_pmbr)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
pr_debug("Device has a %s MBR\n",
|
|
|
|
good_pmbr == GPT_MBR_PROTECTIVE ?
|
|
|
|
"protective" : "hybrid");
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-05-16 02:09:30 +08:00
|
|
|
good_pgpt = is_gpt_valid(state, GPT_PRIMARY_PARTITION_TABLE_LBA,
|
2005-04-17 06:20:36 +08:00
|
|
|
&pgpt, &pptes);
|
|
|
|
if (good_pgpt)
|
2010-05-16 02:09:30 +08:00
|
|
|
good_agpt = is_gpt_valid(state,
|
2005-04-17 06:20:36 +08:00
|
|
|
le64_to_cpu(pgpt->alternate_lba),
|
|
|
|
&agpt, &aptes);
|
|
|
|
if (!good_agpt && force_gpt)
|
2010-05-16 02:09:30 +08:00
|
|
|
good_agpt = is_gpt_valid(state, lastlba, &agpt, &aptes);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-08-20 08:45:34 +08:00
|
|
|
if (!good_agpt && force_gpt && fops->alternative_gpt_sector) {
|
|
|
|
sector_t agpt_sector;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = fops->alternative_gpt_sector(disk, &agpt_sector);
|
|
|
|
if (!err)
|
|
|
|
good_agpt = is_gpt_valid(state, agpt_sector,
|
|
|
|
&agpt, &aptes);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* The obviously unsuccessful case */
|
|
|
|
if (!good_pgpt && !good_agpt)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
compare_gpts(pgpt, agpt, lastlba);
|
|
|
|
|
|
|
|
/* The good cases */
|
|
|
|
if (good_pgpt) {
|
|
|
|
*gpt = pgpt;
|
|
|
|
*ptes = pptes;
|
|
|
|
kfree(agpt);
|
|
|
|
kfree(aptes);
|
2013-09-12 05:25:03 +08:00
|
|
|
if (!good_agpt)
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("Alternate GPT is invalid, using primary GPT.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (good_agpt) {
|
|
|
|
*gpt = agpt;
|
|
|
|
*ptes = aptes;
|
|
|
|
kfree(pgpt);
|
|
|
|
kfree(pptes);
|
2013-09-12 05:25:04 +08:00
|
|
|
pr_warn("Primary GPT is invalid, using alternate GPT.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fail:
|
|
|
|
kfree(pgpt);
|
|
|
|
kfree(agpt);
|
|
|
|
kfree(pptes);
|
|
|
|
kfree(aptes);
|
|
|
|
*gpt = NULL;
|
|
|
|
*ptes = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-08 16:08:59 +08:00
|
|
|
/**
|
|
|
|
* utf16_le_to_7bit(): Naively converts a UTF-16LE string to 7-bit ASCII characters
|
|
|
|
* @in: input UTF-16LE string
|
|
|
|
* @size: size of the input string
|
|
|
|
* @out: output string ptr, should be capable to store @size+1 characters
|
|
|
|
*
|
|
|
|
* Description: Converts @size UTF16-LE symbols from @in string to 7-bit
|
|
|
|
* ASCII characters and stores them to @out. Adds trailing zero to @out array.
|
|
|
|
*/
|
|
|
|
static void utf16_le_to_7bit(const __le16 *in, unsigned int size, u8 *out)
|
|
|
|
{
|
|
|
|
unsigned int i = 0;
|
|
|
|
|
|
|
|
out[size] = 0;
|
|
|
|
|
|
|
|
while (i < size) {
|
|
|
|
u8 c = le16_to_cpu(in[i]) & 0xff;
|
|
|
|
|
|
|
|
if (c && !isprint(c))
|
|
|
|
c = '!';
|
|
|
|
out[i] = c;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/**
|
2021-05-14 01:17:08 +08:00
|
|
|
* efi_partition - scan for GPT partitions
|
2014-06-13 02:26:01 +08:00
|
|
|
* @state: disk parsed partitions
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Description: called from check.c, if the disk contains GPT
|
|
|
|
* partitions, sets up partition entries in the kernel.
|
|
|
|
*
|
|
|
|
* If the first block on the disk is a legacy MBR,
|
|
|
|
* it will get handled by msdos_partition().
|
|
|
|
* If it's a Protective MBR, we'll handle it here.
|
|
|
|
*
|
|
|
|
* We do not create a Linux partition for GPT, but
|
|
|
|
* only for the actual data partitions.
|
|
|
|
* Returns:
|
|
|
|
* -1 if unable to read the partition table
|
|
|
|
* 0 if this isn't our partition table
|
|
|
|
* 1 if successful
|
|
|
|
*
|
|
|
|
*/
|
2010-05-16 02:09:30 +08:00
|
|
|
int efi_partition(struct parsed_partitions *state)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
gpt_header *gpt = NULL;
|
|
|
|
gpt_entry *ptes = NULL;
|
|
|
|
u32 i;
|
2021-08-10 23:45:09 +08:00
|
|
|
unsigned ssz = queue_logical_block_size(state->disk->queue) / 512;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-05-16 02:09:30 +08:00
|
|
|
if (!find_valid_gpt(state, &gpt, &ptes) || !gpt || !ptes) {
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(gpt);
|
|
|
|
kfree(ptes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-07-25 16:48:26 +08:00
|
|
|
pr_debug("GUID Partition Table is valid! Yea!\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < state->limit-1; i++) {
|
2010-09-01 04:47:06 +08:00
|
|
|
struct partition_meta_info *info;
|
|
|
|
unsigned label_max;
|
2009-11-23 16:29:13 +08:00
|
|
|
u64 start = le64_to_cpu(ptes[i].starting_lba);
|
|
|
|
u64 size = le64_to_cpu(ptes[i].ending_lba) -
|
|
|
|
le64_to_cpu(ptes[i].starting_lba) + 1ULL;
|
|
|
|
|
2021-08-10 23:45:09 +08:00
|
|
|
if (!is_pte_valid(&ptes[i], last_lba(state->disk)))
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
|
2009-11-23 16:29:13 +08:00
|
|
|
put_partition(state, i+1, start * ssz, size * ssz);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* If this is a RAID volume, tell md */
|
2013-09-12 05:25:03 +08:00
|
|
|
if (!efi_guidcmp(ptes[i].partition_type_guid, PARTITION_LINUX_RAID_GUID))
|
2010-04-18 06:28:09 +08:00
|
|
|
state->parts[i + 1].flags = ADDPART_FLAG_RAID;
|
2010-09-01 04:47:06 +08:00
|
|
|
|
|
|
|
info = &state->parts[i + 1].info;
|
2014-12-18 23:02:17 +08:00
|
|
|
efi_guid_to_str(&ptes[i].unique_partition_guid, info->uuid);
|
2010-09-01 04:47:06 +08:00
|
|
|
|
|
|
|
/* Naively convert UTF16-LE to 7 bits. */
|
2013-11-22 06:32:05 +08:00
|
|
|
label_max = min(ARRAY_SIZE(info->volname) - 1,
|
|
|
|
ARRAY_SIZE(ptes[i].partition_name));
|
2020-03-08 16:08:59 +08:00
|
|
|
utf16_le_to_7bit(ptes[i].partition_name, label_max, info->volname);
|
2010-09-01 04:47:06 +08:00
|
|
|
state->parts[i + 1].has_info = true;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
kfree(ptes);
|
|
|
|
kfree(gpt);
|
2010-08-11 09:03:14 +08:00
|
|
|
strlcat(state->pp_buf, "\n", PAGE_SIZE);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
|
|
|
}
|