You can import this changeset into BK by piping this whole message to '| bk receive [path to repository]' or apply the patch as usual. =================================================================== ChangeSet@1.584, 2002-07-02 09:31:18-05:00, Matt_Domsch@dell.com Initial check-in of the EFI GUID Partition Table (GPT) support copied from the ia64 port patch 020622 Documentation/Configure.help | 11 arch/ia64/defconfig | 1 fs/partitions/Config.in | 1 fs/partitions/Makefile | 2 fs/partitions/check.c | 4 fs/partitions/efi.c | 804 +++++++++++++++++++++++++++++++++++++++++++ fs/partitions/efi.h | 119 ++++++ fs/partitions/msdos.c | 11 8 files changed, 943 insertions, 10 deletions diff -Nru a/Documentation/Configure.help b/Documentation/Configure.help --- a/Documentation/Configure.help Tue Jul 2 09:39:01 2002 +++ b/Documentation/Configure.help Tue Jul 2 09:39:01 2002 @@ -15322,14 +15322,9 @@ Intel EFI GUID partition support CONFIG_EFI_PARTITION Say Y here if you would like to use hard disks under Linux which - were partitioned using EFI GPT. Presently only useful on the - IA-64 platform. - -/dev/guid support (EXPERIMENTAL) -CONFIG_DEVFS_GUID - Say Y here if you would like to access disks and partitions by - their Globally Unique Identifiers (GUIDs) which will appear as - symbolic links in /dev/guid. + were partitioned using EFI GPT. This is the default partition + scheme on IA64, and can be used on other platforms when + large block device (64-bit block address) support is desired. Ultrix partition table support CONFIG_ULTRIX_PARTITION diff -Nru a/arch/ia64/defconfig b/arch/ia64/defconfig --- a/arch/ia64/defconfig Tue Jul 2 09:39:00 2002 +++ b/arch/ia64/defconfig Tue Jul 2 09:39:00 2002 @@ -672,7 +672,6 @@ # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set CONFIG_EFI_PARTITION=y -# CONFIG_DEVFS_GUID is not set # CONFIG_LDM_PARTITION is not set # CONFIG_SGI_PARTITION is not set # CONFIG_ULTRIX_PARTITION is not set diff -Nru a/fs/partitions/Config.in b/fs/partitions/Config.in --- a/fs/partitions/Config.in Tue Jul 2 09:39:00 2002 +++ b/fs/partitions/Config.in Tue Jul 2 09:39:00 2002 @@ -32,6 +32,7 @@ bool ' SGI partition support' CONFIG_SGI_PARTITION bool ' Ultrix partition table support' CONFIG_ULTRIX_PARTITION bool ' Sun partition tables support' CONFIG_SUN_PARTITION + bool ' EFI GUID Partition support' CONFIG_EFI_PARTITION else if [ "$ARCH" = "alpha" ]; then define_bool CONFIG_OSF_PARTITION y diff -Nru a/fs/partitions/Makefile b/fs/partitions/Makefile --- a/fs/partitions/Makefile Tue Jul 2 09:39:00 2002 +++ b/fs/partitions/Makefile Tue Jul 2 09:39:00 2002 @@ -24,6 +24,6 @@ obj-$(CONFIG_SUN_PARTITION) += sun.o obj-$(CONFIG_ULTRIX_PARTITION) += ultrix.o obj-$(CONFIG_IBM_PARTITION) += ibm.o +obj-$(CONFIG_EFI_PARTITION) += efi.o include $(TOPDIR)/Rules.make - diff -Nru a/fs/partitions/check.c b/fs/partitions/check.c --- a/fs/partitions/check.c Tue Jul 2 09:39:00 2002 +++ b/fs/partitions/check.c Tue Jul 2 09:39:00 2002 @@ -33,6 +33,7 @@ #include "sun.h" #include "ibm.h" #include "ultrix.h" +#include "efi.h" extern int *blk_size[]; @@ -41,6 +42,9 @@ static int (*check_part[])(struct gendisk *hd, struct block_device *bdev, unsigned long first_sect, int first_minor) = { #ifdef CONFIG_ACORN_PARTITION acorn_partition, +#endif +#ifdef CONFIG_EFI_PARTITION + efi_partition, /* this must come before msdos */ #endif #ifdef CONFIG_LDM_PARTITION ldm_partition, /* this must come before msdos */ diff -Nru a/fs/partitions/efi.c b/fs/partitions/efi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/partitions/efi.c Tue Jul 2 09:39:01 2002 @@ -0,0 +1,804 @@ +/************************************************************ + * EFI GUID Partition Table handling + * Per Intel EFI Specification v1.02 + * http://developer.intel.com/technology/efi/efi.htm + * efi.[ch] by Matt Domsch + * Copyright 2000,2001,2002 Dell Computer Corporation + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * TODO: + * + * Changelog: + * Wed Mar 27 2002 Matt Domsch + * - Ported to 2.5.7-pre1 and 2.4.18 + * - 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. + * - 2.4.18 patch needs own crc32() function - there's no official + * lib/crc32.c in 2.4.x. + * + * Mon Feb 04 2002 Matt Domsch + * - Removed __PRIPTR_PREFIX - not being used + * + * Mon Jan 14 2002 Matt Domsch + * - Ported to 2.5.2-pre11 + library crc32 patch Linus applied + * + * Thu Dec 6 2001 Matt Domsch + * - 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 + * - Removed CONFIG_DEVFS_VOLUMES_UUID code entirely per Martin Wilck + * + * Wed Oct 10 2001 Matt Domsch + * - Changed function comments to DocBook style per Andreas Dilger suggestion. + * + * Mon Oct 08 2001 Matt Domsch + * - 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 + * - 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 + * - added devfs volume UUID support (/dev/volumes/uuids) for + * mounting file systems by the partition GUID. + * + * Tue Dec 5 2000 Matt Domsch + * - Moved crc32() to linux/lib, added efi_crc32(). + * + * Thu Nov 30 2000 Matt Domsch + * - Replaced Intel's CRC32 function with an equivalent + * non-license-restricted version. + * + * Wed Oct 25 2000 Matt Domsch + * - Fixed the last_lba() call to return the proper last block + * + * Thu Oct 12 2000 Matt Domsch + * - Thanks to Andries Brouwer for his debugging assistance. + * - Code works, detects all the partitions. + * + ************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "check.h" +#include "efi.h" + +#if CONFIG_BLK_DEV_MD +extern void md_autodetect_dev(kdev_t dev); +#endif + +/* Handle printing of 64-bit values */ +/* Borrowed from /usr/include/inttypes.h */ +# if BITS_PER_LONG == 64 +# define __PRI64_PREFIX "l" +# else +# define __PRI64_PREFIX "ll" +# endif +# define PRIx64 __PRI64_PREFIX "x" + + +#undef EFI_DEBUG +#ifdef EFI_DEBUG +#define Dprintk(x...) printk(KERN_DEBUG x) +#else +#define Dprintk(x...) +#endif + +/* 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); + + +/* + * There are multiple 16-bit CRC polynomials in common use, but this is + * *the* standard CRC-32 polynomial, first popularized by Ethernet. + * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 + */ +#define CRCPOLY_LE 0xedb88320 +/* How many bits at a time to use. Requires a table of 4<>= 1) { + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + for (j = 0; j < 1 << CRC_LE_BITS; j += 2 * i) + crc32table_le[i + j] = crc ^ crc32table_le[j]; + } + return 0; +} + +/** + * crc32cleanup_le(): free LE table data + */ +static void __exit crc32cleanup_le(void) +{ + if (crc32table_le) kfree(crc32table_le); + crc32table_le = NULL; +} + +__initcall(crc32init_le); +__exitcall(crc32cleanup_le); + +/** + * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 + * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for + * other uses, or the previous crc32 value if computing incrementally. + * @p - pointer to buffer over which CRC is run + * @len - length of buffer @p + * + */ +static u32 crc32_le(u32 crc, unsigned char const *p, size_t len) +{ + while (len--) { + crc = (crc >> 8) ^ crc32table_le[(crc ^ *p++) & 255]; + } + return crc; +} + + +/** + * efi_crc32() - EFI version of crc32 function + * @buf: buffer to calculate crc32 of + * @len - length of buf + * + * 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_le(~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 + * @hd: gendisk with partition list + * @bdev: block device + * + * 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. + */ +static u64 +last_lba(struct gendisk *hd, struct block_device *bdev) +{ + if (!hd || !hd->part || !bdev) + return 0; + return hd->part[MINOR(to_kdev_t(bdev->bd_dev))].nr_sects - 1; +} + +/** + * read_lba(): Read bytes from disk, starting at given LBA + * @hd + * @bdev + * @lba + * @buffer + * @size_t + * + * Description: Reads @count bytes from @bdev into @buffer. + * Returns number of bytes read on success, 0 on error. + */ +static size_t +read_lba(struct gendisk *hd, struct block_device *bdev, u64 lba, + u8 * buffer, size_t count) +{ + + size_t totalreadcount = 0, bytesread = 0; + unsigned long blocksize; + int i; + Sector sect; + unsigned char *data = NULL; + + if (!hd || !bdev || !buffer || !count) + return 0; + + blocksize = get_hardsect_size(to_kdev_t(bdev->bd_dev)); + if (!blocksize) + blocksize = 512; + + for (i = 0; count > 0; i++) { + data = read_dev_sector(bdev, lba, §); + if (!data) + return totalreadcount; + + bytesread = + PAGE_CACHE_SIZE - (data - + (unsigned char *) page_address(sect.v)); + bytesread = min(bytesread, count); + memcpy(buffer, data, bytesread); + put_dev_sector(sect); + + buffer += bytesread; + totalreadcount += bytesread; + count -= bytesread; + lba += (bytesread / blocksize); + } + return totalreadcount; +} + + +/** + * alloc_read_gpt_entries(): reads partition entries from disk + * @hd + * @bdev + * @gpt - GPT header + * + * 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! + */ +static gpt_entry * +alloc_read_gpt_entries(struct gendisk *hd, + struct block_device *bdev, gpt_header *gpt) +{ + size_t count; + gpt_entry *pte; + if (!hd || !bdev || !gpt) + return NULL; + + count = le32_to_cpu(gpt->num_partition_entries) * + le32_to_cpu(gpt->sizeof_partition_entry); + if (!count) + return NULL; + pte = kmalloc(count, GFP_KERNEL); + if (!pte) + return NULL; + memset(pte, 0, count); + + if (read_lba(hd, bdev, le64_to_cpu(gpt->partition_entry_lba), + (u8 *) pte, + count) < count) { + kfree(pte); + pte=NULL; + return NULL; + } + return pte; +} + +/** + * alloc_read_gpt_header(): Allocates GPT header, reads into it from disk + * @hd + * @bdev + * @lba is the Logical Block Address of the partition table + * + * Description: returns GPT header on success, NULL on error. Allocates + * and fills a GPT header starting at @ from @bdev. + * Note: remember to free gpt when finished with it. + */ +static gpt_header * +alloc_read_gpt_header(struct gendisk *hd, struct block_device *bdev, u64 lba) +{ + gpt_header *gpt; + if (!hd || !bdev) + return NULL; + + gpt = kmalloc(sizeof (gpt_header), GFP_KERNEL); + if (!gpt) + return NULL; + memset(gpt, 0, sizeof (gpt_header)); + + if (read_lba(hd, bdev, lba, (u8 *) gpt, + sizeof (gpt_header)) < sizeof (gpt_header)) { + kfree(gpt); + gpt=NULL; + return NULL; + } + + return gpt; +} + +/** + * is_gpt_valid() - tests one GPT header and PTEs for validity + * @hd + * @bdev + * @lba is the logical block address of the GPT header to test + * @gpt is a GPT header ptr, filled on return. + * @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. + */ +static int +is_gpt_valid(struct gendisk *hd, struct block_device *bdev, u64 lba, + gpt_header **gpt, gpt_entry **ptes) +{ + u32 crc, origcrc; + + if (!hd || !bdev || !gpt || !ptes) + return 0; + if (!(*gpt = alloc_read_gpt_header(hd, bdev, lba))) + return 0; + + /* Check the GUID Partition Table signature */ + 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; + } + + /* 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) { + 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); + + /* Check that the my_lba entry points to the LBA that contains + * the GUID Partition Table */ + 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(hd, bdev, *gpt))) { + kfree(*gpt); + *gpt = NULL; + return 0; + } + + /* Check the GUID Partition Entry Array CRC */ + 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; + } + + /* We're done, all's well */ + return 1; +} + +/** + * compare_gpts() - Search disk for valid GPT headers and PTEs + * @pgpt is the primary GPT header + * @agpt is the alternate GPT header + * @lastlba is the last LBA number + * 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)) { + 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; +} + +/** + * find_valid_gpt() - Search disk for valid GPT headers and PTEs + * @hd + * @bdev + * @gpt is a GPT header ptr, filled on return. + * @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. + */ +static int +find_valid_gpt(struct gendisk *hd, 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 (!hd || !bdev || !gpt || !ptes) + return 0; + + lastlba = last_lba(hd, bdev); + good_pgpt = is_gpt_valid(hd, bdev, GPT_PRIMARY_PARTITION_TABLE_LBA, + &pgpt, &pptes); + if (good_pgpt) { + good_agpt = is_gpt_valid(hd, bdev, + le64_to_cpu(pgpt->alternate_lba), + &agpt, &aptes); + if (!good_agpt) { + good_agpt = is_gpt_valid(hd, bdev, lastlba, + &agpt, &aptes); + } + } + else { + good_agpt = is_gpt_valid(hd, 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(hd, 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; +} + +/** + * add_gpt_partitions(struct gendisk *hd, struct block_device *bdev, + * @hd + * @bdev + * + * Description: Create devices for each entry in the GUID Partition Table + * Entries. + * + * 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 + * + */ +static int +add_gpt_partitions(struct gendisk *hd, struct block_device *bdev, int nextminor) +{ + gpt_header *gpt = NULL; + gpt_entry *ptes = NULL; + u32 i; + int max_p; + + if (!hd || !bdev) + return -1; + + if (!find_valid_gpt(hd, bdev, &gpt, &ptes) || !gpt || !ptes) { + if (gpt) { + kfree(gpt); + gpt = NULL; + } + if (ptes) { + kfree(ptes); + ptes = NULL; + } + return 0; + } + + Dprintk("GUID Partition Table is valid! Yea!\n"); + + max_p = (1 << hd->minor_shift) - 1; + for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < max_p; i++) { + if (!efi_guidcmp(ptes[i].partition_type_guid, NULL_GUID)) + continue; + + add_gd_partition(hd, nextminor+i, + 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 CONFIG_BLK_DEV_MD + if (!efi_guidcmp(ptes[i].partition_type_guid, + PARTITION_LINUX_RAID_GUID)) { + md_autodetect_dev(MKDEV + (MAJOR(to_kdev_t(bdev->bd_dev)), + nextminor)); + } +#endif + } + kfree(ptes); + ptes=NULL; + kfree(gpt); + gpt=NULL; + printk("\n"); + return 1; +} + +/** + * efi_partition(): EFI GPT partition handling entry function + * @hd + * @bdev + * @first_sector: unused + * @first_part_minor: minor number assigned to first GPT partition found + * + * 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. + * + * set_blocksize() calls are necessary to be able to read + * a disk with an odd number of 512-byte sectors, as the + * default BLOCK_SIZE of 1024 bytes won't let that last + * sector be read otherwise. + * + * Returns: + * -1 if unable to read the partition table + * 0 if this isn't our partitoin table + * 1 if successful + */ +int +efi_partition(struct gendisk *hd, struct block_device *bdev, + unsigned long first_sector, int first_part_minor) +{ + + kdev_t dev = to_kdev_t(bdev->bd_dev); + int hardblocksize = get_hardsect_size(dev); + int orig_blksize_size = BLOCK_SIZE; + int rc = 0; + + /* Need to change the block size that the block layer uses */ + if (blksize_size[MAJOR(dev)]) { + orig_blksize_size = blksize_size[MAJOR(dev)][MINOR(dev)]; + } + + if (orig_blksize_size != hardblocksize) + set_blocksize(dev, hardblocksize); + + rc = add_gpt_partitions(hd, bdev, first_part_minor); + + /* change back */ + if (orig_blksize_size != hardblocksize) + set_blocksize(dev, orig_blksize_size); + + return rc; +} diff -Nru a/fs/partitions/efi.h b/fs/partitions/efi.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/fs/partitions/efi.h Tue Jul 2 09:39:01 2002 @@ -0,0 +1,119 @@ +/************************************************************ + * EFI GUID Partition Table + * Per Intel EFI Specification v1.02 + * http://developer.intel.com/technology/efi/efi.htm + * + * By Matt Domsch Fri Sep 22 22:15:56 CDT 2000 + * Copyright 2000,2001 Dell Computer Corporation + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ************************************************************/ + +#ifndef FS_PART_EFI_H_INCLUDED +#define FS_PART_EFI_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +/* + * Yes, specifying asm-ia64 is ugly, but this lets it build on + * other platforms too, until efi.h moves to include/linux. + */ +#include + +#define MSDOS_MBR_SIGNATURE 0xaa55 +#define EFI_PMBR_OSTYPE_EFI 0xEF +#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE + +#define GPT_BLOCK_SIZE 512 +#define GPT_HEADER_SIGNATURE 0x5452415020494645L +#define GPT_HEADER_REVISION_V1 0x00010000 +#define GPT_PRIMARY_PARTITION_TABLE_LBA 1 + +#define PARTITION_SYSTEM_GUID \ + EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \ + 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) +#define LEGACY_MBR_PARTITION_GUID \ + EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \ + 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F) +#define PARTITION_MSFT_RESERVED_GUID \ + EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \ + 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE) +#define PARTITION_BASIC_DATA_GUID \ + EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \ + 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7) +#define PARTITION_LINUX_RAID_GUID \ + EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \ + 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e) +#define PARTITION_LINUX_SWAP_GUID \ + EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \ + 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f) +#define PARTITION_LINUX_LVM_GUID \ + EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \ + 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28) + +typedef struct _gpt_header { + u64 signature; + u32 revision; + u32 header_size; + u32 header_crc32; + u32 reserved1; + u64 my_lba; + u64 alternate_lba; + u64 first_usable_lba; + u64 last_usable_lba; + efi_guid_t disk_guid; + u64 partition_entry_lba; + u32 num_partition_entries; + u32 sizeof_partition_entry; + u32 partition_entry_array_crc32; + u8 reserved2[GPT_BLOCK_SIZE - 92]; +} __attribute__ ((packed)) gpt_header; + +typedef struct _gpt_entry_attributes { + u64 required_to_function:1; + u64 reserved:47; + u64 type_guid_specific:16; +} __attribute__ ((packed)) gpt_entry_attributes; + +typedef struct _gpt_entry { + efi_guid_t partition_type_guid; + efi_guid_t unique_partition_guid; + u64 starting_lba; + u64 ending_lba; + gpt_entry_attributes attributes; + efi_char16_t partition_name[72 / sizeof (efi_char16_t)]; +} __attribute__ ((packed)) gpt_entry; + +typedef struct _legacy_mbr { + u8 boot_code[440]; + u32 unique_mbr_signature; + u16 unknown; + struct partition partition_record[4]; + u16 signature; +} __attribute__ ((packed)) legacy_mbr; + +/* Functions */ +extern int + efi_partition(struct gendisk *hd, struct block_device *bdev, + unsigned long first_sector, int first_part_minor); + +#endif diff -Nru a/fs/partitions/msdos.c b/fs/partitions/msdos.c --- a/fs/partitions/msdos.c Tue Jul 2 09:39:00 2002 +++ b/fs/partitions/msdos.c Tue Jul 2 09:39:00 2002 @@ -41,6 +41,7 @@ #include "check.h" #include "msdos.h" +#include "efi.h" #if CONFIG_BLK_DEV_MD extern void md_autodetect_dev(kdev_t dev); @@ -573,6 +574,16 @@ return 0; } p = (struct partition *) (data + 0x1be); +#ifdef CONFIG_EFI_PARTITION + for (i=1 ; i<=4 ; i++,p++) { + /* If this is an EFI GPT disk, msdos should ignore it. */ + if (SYS_IND(p) == EFI_PMBR_OSTYPE_EFI_GPT) { + put_dev_sector(sect); + return 0; + } + } + p = (struct partition *) (data + 0x1be); +#endif /* * Look for partitions in two passes: =================================================================== This BitKeeper patch contains the following changesets: 1.584 ## Wrapped with gzip_uu ## begin 664 bkpatch13710 M'XL(``6[(3T``[1::5?;5K?^C'_%:;)6:Q,/DCQ@0^!E)C1,BZ%]:@9]59M.(E+ M6'=AQ?9(W,DP6E_1Z\U\))Y.Y/K*Y<'1S.0%?CT.+3\:R]BBXQ_SI8^&IAGXKZVO-;5VYU'O:*VU1UMW=-UJ MZ=+1C%:WTRH5[K*=W6&1R)IFZ*VF9AB/;=WH::5]H=?;W9;0C(:VUM`,H?76 MF_JZWJUI[75-$\MHBG==4=-*N^+_E_V]DBV.?3=V+4_8(VG?UEQ?!`,1CZ0X M.#P61S?'^Q!K&&-)X(MKJ^])43ZZN*Z(*)E,@C`6=C!QI2,&(9BD;:[5:0F> MF;`ZP$3',$H?1;N[9I0N9LHHU5[YKU32+*VT]0T16"'LA9AH.')@!_[`'1:$ MT=*:^F-SK=/5'V6O9\DUQ]`[_6:[.["6ROUYBT>]UO\C6( M&I-,D%%C'#E!5+>+:NHU>X]:M]GK/G::W7Y':W>MYJ!K]`9KRSE[@2#SAI_6 M(RAWC%?R=FK=RH'KR2?,M8Q.][&OV[;>Z:VMK1DM2Y/V]S`W3U%QA_L]:D:[ MU0)WWT$"^XNWZ[:,=ONQU3'6M$?+AC$;AB-[A@-Z/T*MW6W#.^&<:Z^4%3O- M$CUVNEWML=_I]3N#7KO7L2VCU9??P]D<0?!FZ$TRV4ZKK7_;QO8#.QE+/[:( M5F./K34)97TDO:;':.UI=PM'Z_W=2_1XX+)%.;,[J/S7:OK7V_S8T6;:X- ML-`>6ZU69ZW5TMI6>ZUG&,^`Q\ODVKUF6Q'B*+7$0BE>GM"PXZ4)OKK>,]7;[Q>"D_1/!R1:[;OQ1RHD,60+BY9R@L4P^ M'P5?$G%GV>RE6+S\OX7VT#7T'XA-_X`6]%=JH6LT_PD]'`H=K#<0\N^D%\#^@"/80=)KQ-(>^8$7#*=T7^74\9@VT<<_ M[-$7T9^R@H12D'B_3%M;M$.(O6`R#=WA*!;0A5;%+YU^&6(?ZS`[GB0Q&-X+ M0J14S"3V\=;KD1N)21@,0VLL\'$02BFB8!#?6Z'<$-,@$;;EBU`Z;A2';A]T MA!L+2*(1A&(<..Y@RH0PF/@.#J'L#8>-HRP#/#J[$4?2ER&RPXND[[FV.'%M MZ4=26#B;1J(1NY`IPG`2R4'B M59D&5HO?CZ\_G-]B;-#>$.A!_$57$?NC"<.'BJ7=X_TW`57F77JZ+=$]<2 MDI+BPK-LZ/4J(0K-IE85NZCU:.7IC@"TZ;I>0W:S)L3-U8ZZ%M&\/M\_7T\_ M*ZB"L]&`^!VW@V>%PE@3["3?YV4U<8%R!'MQ"Z/>KJ_5)J'4R1/PV*HCK^)% M.Q.E7E6V8*UU%[@H;*S$B\F<+`\VZ,,0Q4A:Y"]%5*D)VY.6C^W)!,'*=TP` ML.N85*OR]+E?@WG>"EAH8L=(GOA\UA1HCR5T/"4;7K6\>VL:K8J3`RC@'CL5 M4$3W5A^$)5"=G,^7TI&.(AUB,PQ@$@*DZ$!3\5>NJ.G`]Z9D\M@=1K$86P_F M1.2H+9#6A:Z,JG3C6P1;5O0M;@J$'+L^S,]/QGVIG).9GNU5,Y&((;>Z.DU) M-!4B<0DTN?>%'=I-HUP1@\2W>6N-S@GE+[A*`%L%!*,&56=X;K_!ZY$`039$ M\2$S^U-L%8>R+[36ZTS@DH7D"-.\N#R^N+[$'^#_OS$#,P%$.090&:4-^3 M*9(AD/$HK6`!1,102F82Y=$6"O8)>F_3\Y3/.@),%*S=M\8R8O.!K2NC"0B+ M(T)5.!//$,Z=<$[GN;>2Z65^?V['0E][G5`RG>^=GQT>'YG[![\=7IF_G9_< MG!Y4*-B!(\D#W%#"22C1/"7C]L7OKF??IBK)C]=>=WPFB-SF,455%%\= M)=5N$!`>3`&5=/*.[X02KKWO>D,\1LEP**.X$._()HD/K?LC?``F+,?T^A;4 MCO,)$$C!$PM3MF6/4B8\\9L;!G#+^R#,-,K((I).B^,#ED%6`0&B)Z@]`GAG M'4=J]JC&7[V>/BA?377>B*9<:`FTS24\>`/!@0`"X3 M=P39(C+9:O[`=US+]V4$(W0<-\VD9N@92]H*M(ND'<`6%]&^2JCKQHQ7,8LJ M0NI8G:$B'"8!?1LGP,_KE)UD^':/F(SL>Q0P+I`>&FE8A=0]*XI3B(<8P0`" M-W`G<)Q:Y/X/29+"!K?7H#,Z%E$5Y,_@LT'@12KZ(TE)$;HO)65]$$Y$N1(/`J5,4SKQ"Z#A%Q(%!!^&+A0WH\&4ZP7/ M^S4!$N=H.'-.LA!ZJO/3]F'RU8VCI';E2OA85+`6Q3)2^4$D[@(O&4O!OI\U M,,N4YC?43-1(`)!1A:ZFQ#E&@A,3\G$%&DVC6")!1H:OG">+A`2$=9%A>"(9 MPT6;L_KOMNM31JLL5$(^7-^BRNUG@B<(3^H!;2B!^X`.=.8.LP>>1/-@4\!9R\PPSC5>* MX)#!(_.+%*YL0AKV',",RL45#O$BT?>"'*M)&HS5QNO.O09&WC(P$PPC'Q*[ M89#<4^PX`V)$NPH@B%AN7;,HMX%$4(+Y%%(4V'2T4,CG,FDF7N_\&_ M1NFMZ]M>@M/>*]-0#>GZ:.O)S"!:-CJ4_LA9-J&\==G,V/H:A,LF2/7^TK/[ MWNTSPY.EZR//ZB\='T],TNVR.==WX_EQ*QHWE'\^'>]/8QF$@/&YJ3>JV3IZ M4QCB>OY-Z3.&!EGBL'ORD9('\W2_)!\H+`BN#,:.:25QH#1N`D3*M_AEQH0U ME8W26XF0,P"EQJKX0*5"FJ*3"0%0.ZU:'Q`+UTI@;=`MENT&81C<9V]3&DD4 M-E+&\#>F%UO0*JU]2_79[O'UE7EQ<&F>G)\=B>/AFD)Z*/Q>6*,6,>-OLT58\M!IK:S,+Q9O'DA,$!1U#P:4#D)(NS=')+F% M@910%O\?ZO5Z1:0/'P\NS]0Z\5"!T)C!91OF!,JE/WPLN,>?5\4://;'U&.B.#R9`!6%-:!>!1&@ M`$U>WRBI3(0R;3K2Y@Q]HSBJ3BSED^;`+]LCE+JK<+-*Z7]+*_F4V!3Z1FDE M!4-\_*MDFA&>)N4WF'Y3%44J,,3/+#*%CI(*3_R,4=2Z5*3K'39#@+^8!-[4 M#\:HPR*JNTB:N%U"O1GJDG#^X'*ZM0H!K%)ZA=H_=&ASC8J:?'\UK34GP23Q MK)#3&(3,`RKY?,GA73S\V33>/?QI=.A7DW[1HTZ/.G_2Z9>&7UW\K.&GC9\6 M+:0)_&@LVLQ6P,3%^ MV$*LU"50/).QIC$0F[/RC3ZFC2"Z@G`W1,!MN+3J9UM`9N.ICEF?<\;`SQI3 M`\N.5O7](HUWAJW/.7(@1ENTX@MR$U3MRJ M^`J[)KD0VVSEGTLKQ4EHIN-+\:7]H=%6-V2"D*+O$%9U8+JJT)O3*!HG6 M%5M;6%`1N-:*ND.9_FQMT>"?HLQ//]/#OXKVNBXT8DZ=\96/%%_%>[%X.1I] MMRE@-\(EOA>X=<4[D2E'*:4P^?4+3O@KAPV-8&/>W-)ZBBUN7;63%ZTKURK' M/].4#YE:"YMSS9*PYV4M;HGLPN"BU'&!LYN3$\6@,AU*`,M%\\$F=?IL9L9! M9=&1,B?:LSP[H58R0<&]"S?UW#CV9$UR<9CCD]BYN3[?/SX3Q\2H*B+N1PEU9_ MA.W`@S#ABG<;J3QVX/<0"3[`(=VP/4G;+8TB0N7R2Q^`;IF_N0^X`N*Q]'TU=%\%BK+7.(;N6)B9:5W:Y.WKVKP$N,=GO>9C&MC"+7;J$< MHA+_\#A_O8![*2EF]0Q?'5==S^X+4=FY/:BUJI^_3$`ILN[+R`Y=3BS6$3:( M*^Z#U5071U50,^/85CN%"KW47\N**]*[JG+8`,6B`VEZ!MQ*Y6VS%=H M?VM5C@D/04A-&S5$48Z6@BQ;"7(:Q*7L2SUS[[RJZJ57%>D+!2Y01'1*R1-5,0/Q(IXS*0M;#"1DLJ#13 MF#(GB!&W-_\M(,SDBRVXU#>:E3$N5],36D>50,RHH,#?DE(KVL6PG-I7F#JVB9=>Q6_ M%)(CB+M5M9O"4[7`&P=(%5AY^4HAQ*P4EX&RWDE[SV4LK6WELQ6J7OC2)JYB M7AT?G>U4&S(`R^"XHS;7-T-IH^I#CE)' M88A(XM`7*L22?U2KD.K,\ZOK3Q<')CWSM]R(\$IV;TH05E;ZH;1NZ=-?17`J MS_'&.^;-KU"2F)U>H%0SBEI_H9-(HZTK4Y$=L>.>MB2"@1W2HG MG^G8OI[MG69^V5SSS9W4D!"Y2BQ*;V)9NM+U`NH?#/LM:D,://=1+=(JJT;\]4*0%.)8HL^YEB97H(50PK!S@N8S5PJ"QA/!ZL7.T8&YM[/WX0"8_E\'<(0RGUPC M2BLI]I87!%SA=U*FY3BHR:,R\5%7=R^>0.^FR_ES-34&6C268WLR+6?&0@<6 MC("7()TN7CN]*M]!*0Y55KZ#-BS8U>*T&JTMC$*.M'+&I6C,S*TRE]HNBG,N MR^52UV1=44\G+=U5'"%W?O(N?X8D2R&#>D@U?LFK7FV]&"4FG&847)TM?SX^ M[*3-B4B]G&*XO[@^B&;]!-?G1@DSJ:(I\AEB)$]/([J->C5,Z1>7GS@;)0S` M;QHDO]!78`)?_E3$ETP<4V#=,U):`CFEW/1>P)[9]RC$*CYSL"CB#M17.!V< M;CP#!;PY]YL<-C*`\B3RVC0IPM+:%K#6G&4PZ34J!.8+_Y[L5'V/A<3[%%=EIS3"]P M2\LKU:4Y&H"BR^B`PS(5J@.1"*8?"-M4CX'8W7A"!Z.;*>L+-YGY(NORKV?= M+_MRS7K![&>^5$U]DD.I&W_#$PDATD3H)$W^=CE]VU'@E^7W"[WFI0X;I@X[ MXV7.;>>]%J+(N><[PB.I#4@E8(%`,0W9+F0*N=NKKP=RONK& M]47/S=QKT753"?]8LL".NN"\2UQSF4NJWGKF"ME+*`*GYDXT,G-?1@?&OW1XY@K$X5-7P.BSKO`Y=P:6WGQQ3:>DM35] MBPO%-04(6;0;,B<.`$_*[!<\8+[\L>8]H$"N7MVY;/.Q35^6MLO5DG'^4RV.FTG\&ME7]Y[T[R-[RR3UN*K MHM*_JJM+\ M]YM^S,[,[NRN9!!W7,65"&EW'CW=/3T]/=T]O4ZWIYL8<*+(@\9-:U_?LS34 M[3'7EV+WT':^V015EDY(G4WHY)M-1_Y\,]DT5[PB6#"U[UCA>I3-WV]I`M^Y MPRC-S=J&G++U2`-+,*"+J9M3/R1T1".RTT&_MN>\EZ)&E%6/S7H%C9Z:V*GJ M#I5B)16A98%-AM2@$DO#:F*QT5N2Z0.2Y@-0)-,=-#Y?%=ME.!'C@N'!,+=4 MTR9MV#9[@;H.&TA04LREFS489>C4;3I9)./)7$SB;\NI6C4!J!?D?OS79'8A M@QB,\60XO;P4^Z"VSL`6?@9^K>B()O3JO,LR`P5T06A(75Y)#6S66.0^QR3I M(2TZEY?)QVRJ+,_A*`D9/T4N+U7K2\J7*/.%"6*KF]>I(5[A(\_G"GEGQ0&Z MR#BC9"P6R3W!`$O0EM\B/O`U4[6")*]2N;_;`>+#20;$OP`!=#\'=:QG.#J# M2V@*GJCL*RF5#FV1G6>K+"D"K#C0L=OX`CRSS0WQDT0KHIQ!XA]*KS_-G1L^_4WAVD5_LGIAV/`5`O6N^< M+_8RY4IO5_*OI6%JKU;V97OODL&5;%V=^A$#7!JB'SR@A<:.2K.N':,,T2U1 M:?201RHUO49LFN-;`:ERT"MA$YV5^E=S.H@FA&Y4]U.H4HIF0D6^_!R$W7V( MQED,S_;6B,D"H"L@LSC(E="*QS(K835?HP:IN>)?#*=Y,%=`:6&$%1B5\3K# MBQEW#,L;/MIQ$O-!':JR@G8DE=)4*1>S*NMD@;*J7E)1KP9H:R4=_K:SJ4FQ MQ@=2\^'?/`F7&T=)E73H?+X_14CY?5?`<)7N6X_F>LVY'M<5;:P1X5605V*].=JM9O/>D&RT MI+$;MK;OIM/3_HSL%XT=^IF8/\$WCK>]^0TT&S5V:/.L3!SZR;!F_(!RIBU$ M=[:C[]09O]8VXQ7'RR5V>#'_I'GBL?(^DS8TF#SZX(VS!F5H`].TD`M/.\]? M]S&-Q8NC'Y_U7W3VCWM]L=O>(4>.>V1-N#=CFX]TXU&=Y-X90Y M&9^;G=^1`6$RP0L%R[(U7;H\[V'4-J7^H1@M]'S>TP'2YV#^(%A-T/Q!L#X< M5<@"/Q\+9V6,PV&M?0OQ+:?$C>R,6#583LWENM$EGLTO&*H5:Y'%MZI$]H[/ MG'7RR>\06B5H"P=LIU<8GS5(."@\QS-*J(#LTU@FXR`<`OS*8N%L]+`I1V48 MW'2<5V1[;3M=6(W(85ZI$NIX<+.T#?#D-^(-28U9`JX*L#KS^=4%)>=@%UM, M)".4PV]99\%GR>0C)-CX+%V"B@;8Y\!+CK74HBSWG`HLE"MXFNJ:KU4R]^5W M.$3`9$A0),\_%,"(K(#X$*@XFU[?GJM69:K/P%,P@'*,?G:>NA6)5NL"\"P9 M)WF?3G:(31="TD&7";HRY7K2*6X<;9`"0@N4VI84UB6@(JY"\SSQC55H2U%; M<(*BMHW<>/@EI.4,764*;UG?0\W(KGD0CSF_LQ3%GX_$.UVEU%9\HRJNQ*HJ M+H+.3(*R/NW,:@BZTCJF;^QTBA$]C*/. M$HK8\"^F&DC>MC&IJC#Q.#^8NKE@K5"%,'L/%5.F4,$XV,YS@/G4&E'E+"1J-W"/M^#(W@7+0!8AEVW$*YU#Y9^- M_]6P\<.I0B` MH"TD6G]^-AXMMBDFRXB3@7C"9;WQ.?R0R6>$(-XUCNW$6"'\4#4!X9A\A`<8 MZ,.8MBF8?PJY8:Y2"D1!7CU5?2.E,]9[,%YBWVZ81Q@0Z=&MV4=RIGPNF*)! MD*S5NTO8"&H[88+B?.\(\A,^;AVG`7X`5V<8CH- M:T:>%;%>/Q1EYCH^>O;S/_L`#I.I0E,J)@-Z^H.`<`4KT-;3SC\JX@E7,B@I M&;5-D:J<.P<.;ZR35"U_&U;!\$YYB\O9:%C@<_Y90`W%NMMM2A8I%`XEN&7V M5!DBJL?IYPWPY(A`T5IML6YPHD[Y`AKMXW#;1LI2S-2%7GJP=<2,-"8,,ARZ ML*9"6@F9AHDS\N_0,D3'*YEG)C0(M=62(M:/=#&'-+#%N"Q>C#G9%G=,Q*4X)CT6$KBU[XG,OX7;H@/%)^';P#0L>[/#7`^/PD%P._XURG]T4C MU`"T"3-10B4&T<^"U3@'&Z6+R7(<<@9/?1%'I<=14IYR31F9.Y+R)@Y3C4O6( M^S5I'M-QM>;Q<`/T#7,6W.XPPXQ6U2<":2/Y&<#QL"H3F5C<2D0*ZR`0>EH= MCJH5!J=FP1=8ML\5%+VX$+K,9H[\SU(V(%`:4LQ%A%R.U3-_:'IVGGSDM"B9 M6[/>VR\D(P&@MS9I;`.OK#[';^-W_<"RV,;=QR:28+DVYP=J8&89'#YBPJ)V M*K6M0#^)-D879NB5J+@U:(6*#![);IE7A9*$_<@F/-I*]"Z2X1P2AB*AKB&X M$[*N4>YB6+:3P0"D.&9`07E#-5`^B8T$<#1.H?GB:C2RG%9C7DWT>17+)X2( M@J3Y2+NG7Z_F5%X,:8%>KUDF`9GF1NP^]C@3_`64ODPO$DC6G?6#>Q35%:5L M_'Q_T-SQ%.*6?DLNQY@)#@7-<%8M76>#-+S0F&I1I[NPLX'LP5E90I% MS/X0"`9;T'6^>R'TF;8S&6,6&O%D]WI\NCAK.RW:.IZV4=3=\N:+Z(:NE("; M+CSCIHNPU0Z]NOM&W/CKO@T++QM:RW4:W58C=`1Z[)?*G-5=*G.VTJ4R2]Z/ M\_]TJ\P9D`]&::78V<9SIS#Z]5\KLPH=5KU7QO77[ M5T:`OKY[9>#=VJZ3@7K[RUPDXSB'EV/G))TYGB?^:[MA.XR<@^X+RH#,]T=8 M+IKY\XZ9ZCMF'"L._KQDYJN^9`:(^DDIN#$S-&8\/CQ!ERA,%O9]_^C9P?'/ MW5XWR_I:\OI-(94UYW4NYKC^G\[N39N%UY"_L:K?("WJ!X&H1>(U<]K!'$0!>&QK<+SWLNC$[!&OA3+]@&@)Y98!:%Z?_+ZY$7O*5HU'3HDQT&)GUNBDP/7ZS1]K[4COA^VW$/XUW5/ MO1WG36['W?BPWX&WP?X.`@>?'?P\B.'3[VG?][>=#)3CWG>=@]=(&`55"3A" M7>CV>H&+C?B])H/CV\")N_`VBA4X#1S&`=9J81N'/GS&A]L6Q#P].7PA4'[2 M>_ZRURT#J.<'F]\,#Q$!WOV4#B+IL(EB'")9'WPDX#P<3(MYZ-H#V M.R='!_UNYT6G%)K];J/3Z&!+^W$/VPH"WXJ>%J+A`/N.$#'[.(I]?.[A]S@F MA-F@R5G$+=`D;GS::C5&.+IP-$1H3OV!#9J$<("]-@,D+]7#SQ8^B1%_;EH. MS)89)0AXBF@NB/Z+(?F M^&7IM$HC`8G?Q*9'8:-)A!I:IU6"Y/01?1Z"T<*NO02?XX!&Q$ZM;3'%80V! M-8D-BEJN#3B8`C_BS)6(CR`AV3!H.OQ3R\)@/L%(D*S./+T4R[_+OLD4"<`_ M#-];?I:/(-1\FLVGZFXJ)PM5X\*6#%$,CO5PCM_9@XGX947,"Y1H90/U?LE) M]5TG]MX^VOC#Z?>%+DYZ8+_O;&W-DN'[%,+P%.8?E="%^Y35YY)"EY2''N_G MDL0=0&2R6JPP`5COS0]YV'FZ:H7W*XG&C-( M$0^:D_[OR!V#Z731AYN4?A$[Z[?,4SQH<)@UIIH;B5?O)]-KF&G7M6&L:QU MV?G3O'R6-R^39=W7U6_` MC7^7T^OYD^MD,1<=C`<72S8=N9[G"\K$-VXK\F(RC#9BC5."=L-KAU$EI_C. M;NNK9I18:,AY/JE"WJT8)O2]4,SJ(_%%K)2^T%^NX2*BC!GAVEST/F4W$2ER M.:V.=`S(RHL&!!F$\`%/B:-.%%!P(=@BR?*&D99YXP)DI10USX6\2XTTZS-MV["I9/F]:S(VZB*P=E`%,:M/'=:!GP;IHR:H>-:SLB( MW??$C*LE:4E%'0VQ']\T_%B(KL0;CDX;@U2,:3`(?9?)>JN&Q;]QX-\("KL1 MTC4RQ5:C[;6J3W36<9[S)<66'\9UIT89VFYS%M=RT'BGQI;C)"X.@FIDDTF[#2UXD M>&DZ^'7WKULV5MF&)/&@C$TWNEYLE4GL@K@JYW"U`N-$K990I:)X$(WB,(Z& MB1<,TGK&,9I3?!-&8EDG]4EGG*CM^6TWKF2<8#T'_%^*;:(@]&J4;,;9K400 MK$^%BTF/`E\H4+RGYILVF:N.NT\U`73'V/#OH+OU(MO'"L0*'2@=P:$?NHTN MN],RF$#LM3Q7;+/B&T(%,D'38`*_T?;=_X[T$$N`!3$/;7/P(5'3+2@;GXN: MW0"$P!%^FE2K6S:0.JO/?:Y6F/LM/V[=1'YK$#7"5N*/6EX\:M:HF!4M!^#" MY7F!>Q,V@LCC792Y?KAM-ZCF@*]>'0E=,?9*UF&LW480B!EO$P0A*+"-2F[B M^);'KO/(&?_M<>!@K,K.3,:KR`@,CKR89&[Z=`L1"08^_Q^_FX"P&"\P80)% M79R\/ND?/>MNS?`"K^I+M.QWKMPQPGWX2BT,VRF82B')+@:6/8`#H4&JKEC> =^(N@(]"#TJA>73Q.Q;8V&34&&_\!'JP.9'24```` ` end