From patchwork Mon Dec 11 15:12:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 752680 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="HztDremx" Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::224]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B19FDEB; Mon, 11 Dec 2023 07:12:43 -0800 (PST) Received: by mail.gandi.net (Postfix) with ESMTPSA id 86060E0002; Mon, 11 Dec 2023 15:12:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1702307562; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AK2BzAx/YAHFbh+6Z+0veROK6HPT0/oAubfaxj7UV2s=; b=HztDremxIKWyhs4ljQkUaZkbUnkwWn3NamgaEJlyhZLnq7OoF09J2svtlEqppxF+2rFsmS FNMGnqBM38azQmBQUAq/NYb8lNmqBEBsaWeCZFspt0Xg3hYHkLJpZMgjMOBI153sp/nTF1 l7oUhk9myflVEYuLZh8u25rzeHSv/7yyonWorOnDUgCWHI0rwIxfjBaI3/y0y/dfcLGlYd pXUqTfZ4d1pRpLAlaVvuVyf5bl2c4/bOnlI/IOwEXBbuE3gEYCubQegSqP/ILiyd2gUqCY tSXqIC+Qbr/PG+7ES0+Tt0GkMzm3ENvr/yj/MKDjwUL4Is0B36EMq1qiWd7L4A== From: Romain Gantois To: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jens Axboe , Davidlohr Bueso , Ard Biesheuvel Cc: Romain Gantois , Thomas Petazzoni , Herve Codina , linux-mtd@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-block@vger.kernel.org, linux-efi@vger.kernel.org Subject: [RFC PATCH 1/6] block: partitions: efi: Move efi.h header to include/linux/gpt.h Date: Mon, 11 Dec 2023 16:12:37 +0100 Message-ID: <20231211151244.289349-2-romain.gantois@bootlin.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231211151244.289349-1-romain.gantois@bootlin.com> References: <20231211151244.289349-1-romain.gantois@bootlin.com> Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-GND-Sasl: romain.gantois@bootlin.com The GPT parser located in the block layer defines all of the GPT-specific data structures and logic that are necessary to parse this kind of partition table. However, it is also specifically tailored for block devices. Assuming that different GPT parsers will be implemented in other kernel subsystems, it is desirable to create a common set of GPT struct definitions, macros and helpers, so as to limit code reuse between parsers. As a first step towards this common codebase, this commit moves the efi.h header, that contains GPT-specific definitions, to include/linux/gpt.h. There is no functional change. Signed-off-by: Romain Gantois --- MAINTAINERS | 3 ++- block/partitions/efi.c | 2 +- block/partitions/msdos.c | 2 +- block/partitions/efi.h => include/linux/gpt.h | 0 4 files changed, 4 insertions(+), 3 deletions(-) rename block/partitions/efi.h => include/linux/gpt.h (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 97f51d5ec1cf..22e37e2ea1ae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9233,7 +9233,8 @@ GUID PARTITION TABLE (GPT) M: Davidlohr Bueso L: linux-efi@vger.kernel.org S: Maintained -F: block/partitions/efi.* +F: block/partitions/efi.c +F: include/linux/gpt.h HABANALABS PCI DRIVER M: Oded Gabbay diff --git a/block/partitions/efi.c b/block/partitions/efi.c index 5e9be13a56a8..db50c3f2bab3 100644 --- a/block/partitions/efi.c +++ b/block/partitions/efi.c @@ -87,8 +87,8 @@ #include #include #include +#include #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 diff --git a/block/partitions/msdos.c b/block/partitions/msdos.c index b5d5c229cc3b..d0376cf27448 100644 --- a/block/partitions/msdos.c +++ b/block/partitions/msdos.c @@ -27,9 +27,9 @@ */ #include #include +#include #include "check.h" -#include "efi.h" /* * Many architectures don't like unaligned accesses, while diff --git a/block/partitions/efi.h b/include/linux/gpt.h similarity index 100% rename from block/partitions/efi.h rename to include/linux/gpt.h From patchwork Mon Dec 11 15:12:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 752679 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="O8hGPUiQ" Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D9E91EA; Mon, 11 Dec 2023 07:12:47 -0800 (PST) Received: by mail.gandi.net (Postfix) with ESMTPSA id B2AA1E0017; Mon, 11 Dec 2023 15:12:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1702307566; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=p2JHxD84DRVgTb0d1ZhOrLjE36g3BfuxDbe2pYLA6Gw=; b=O8hGPUiQDtfQu17Xw+CO87hUBhs8vvpqaR19J7YEWHftLusXRdr98DReDmIaE6a1feZL3D r4h7xjTEyVnMQSJSqmgsBCbHcfDq9N9/sCal6XcPS9Jb7J5XbMdu6kwpVD8UGqq0HQdv7L 6Ygp9g2rgB3AAVElpfESQWu4F8Gi9FM229Iio34gaSBVkNQ9afagrnv6sdV+KNKRL3RCPg B6v4KEqydcEaqjECUveDGirAyWwssPBSqPAFA93xUirsxo/nLXha/NB2a5ezO1s/OwarZe PMbCZeZ/ZPairuEuC5IlYJJc1F/VmLIn6dEUzHEuKmZ6218XEGEjhJ/+kHdwNg== From: Romain Gantois To: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jens Axboe , Davidlohr Bueso , Ard Biesheuvel Cc: Romain Gantois , Thomas Petazzoni , Herve Codina , linux-mtd@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-block@vger.kernel.org, linux-efi@vger.kernel.org Subject: [RFC PATCH 3/6] block: partitions: efi: Separate out GPT-specific code Date: Mon, 11 Dec 2023 16:12:39 +0100 Message-ID: <20231211151244.289349-4-romain.gantois@bootlin.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231211151244.289349-1-romain.gantois@bootlin.com> References: <20231211151244.289349-1-romain.gantois@bootlin.com> Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-GND-Sasl: romain.gantois@bootlin.com The current GPT parser implemented in the block layer mixes blockdev-specific code with GPT concepts. Separate out device-agnostic GPT functions from the rest of the parser, in preparation for the creation of a new generic purpose GPT parser library. This mostly implies renaming functions and changing argument types. The only significant change is the new gpt_validate_header function, which has been separated out from the is_gpt_valid function. Signed-off-by: Romain Gantois --- block/partitions/efi.c | 199 ++++++++++++++++++++++------------------- include/linux/gpt.h | 37 +++++++- 2 files changed, 143 insertions(+), 93 deletions(-) diff --git a/block/partitions/efi.c b/block/partitions/efi.c index bac514a62d61..3630ebf4b997 100644 --- a/block/partitions/efi.c +++ b/block/partitions/efi.c @@ -151,7 +151,7 @@ static inline int pmbr_part_valid(gpt_mbr_record *part) } /** - * is_pmbr_valid(): test Protective MBR for validity + * gpt_is_pmbr_valid(): test Protective MBR for validity * @mbr: pointer to a legacy mbr structure * @total_sectors: amount of sectors in the device * @@ -168,7 +168,7 @@ static inline int pmbr_part_valid(gpt_mbr_record *part) * Returns 0 upon invalid MBR, or GPT_MBR_PROTECTIVE or * GPT_MBR_HYBRID depending on the device layout. */ -static int is_pmbr_valid(legacy_mbr *mbr, sector_t total_sectors) +int gpt_is_pmbr_valid(legacy_mbr *mbr, sector_t total_sectors) { uint32_t sz = 0; int i, part = 0, ret = 0; /* invalid by default */ @@ -324,166 +324,183 @@ static gpt_header *alloc_read_gpt_header(struct parsed_partitions *state, } /** - * is_gpt_valid() - tests one GPT header and PTEs for validity - * @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. + * gpt_validate_header() - tests one GPT header for validity + * @gpt: header to check + * @lba: logical block address of the GPT header to test + * @lba_size: logical block size of the partitioned device + * @lastlba: last logical block on the partitioned device * - * Description: returns 1 if valid, 0 on error. - * If valid, returns pointers to newly allocated GPT header and PTEs. + * Returns 0 if validation was successful. */ -static int is_gpt_valid(struct parsed_partitions *state, u64 lba, - gpt_header **gpt, gpt_entry **ptes) +int gpt_validate_header(gpt_header *gpt, u64 lba, unsigned int lba_size, + u64 lastlba) { u32 crc, origcrc; - u64 lastlba, pt_size; - - if (!ptes) - return 0; - if (!(*gpt = alloc_read_gpt_header(state, lba))) - return 0; + u64 pt_size; /* Check the GUID Partition Table signature */ - if (le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) { - pr_debug("GUID Partition Table Header signature is wrong:" - "%lld != %lld\n", - (unsigned long long)le64_to_cpu((*gpt)->signature), + if (le64_to_cpu(gpt->signature) != GPT_HEADER_SIGNATURE) { + 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); - goto fail; + return -EINVAL; } /* Check the GUID Partition Table header size is too big */ - if (le32_to_cpu((*gpt)->header_size) > - queue_logical_block_size(state->disk->queue)) { + if (le32_to_cpu(gpt->header_size) > lba_size) { pr_debug("GUID Partition Table Header size is too large: %u > %u\n", - le32_to_cpu((*gpt)->header_size), - queue_logical_block_size(state->disk->queue)); - goto fail; + le32_to_cpu(gpt->header_size), lba_size); + return -EINVAL; } /* Check the GUID Partition Table header size is too small */ - if (le32_to_cpu((*gpt)->header_size) < sizeof(gpt_header)) { + 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; + le32_to_cpu(gpt->header_size), + sizeof(gpt_header)); + return -EINVAL; } /* 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)); + 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) { pr_debug("GUID Partition Table Header CRC is wrong: %x != %x\n", crc, origcrc); - goto fail; + return -EINVAL; } - (*gpt)->header_crc32 = cpu_to_le32(origcrc); + 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) { + * the GUID Partition Table + */ + if (le64_to_cpu(gpt->my_lba) != lba) { pr_debug("GPT my_lba incorrect: %lld != %lld\n", - (unsigned long long)le64_to_cpu((*gpt)->my_lba), + (unsigned long long)le64_to_cpu(gpt->my_lba), (unsigned long long)lba); - goto fail; + return -EINVAL; } /* Check the first_usable_lba and last_usable_lba are * within the disk. */ - lastlba = last_lba(state->disk); - if (le64_to_cpu((*gpt)->first_usable_lba) > lastlba) { + if (le64_to_cpu(gpt->first_usable_lba) > lastlba) { pr_debug("GPT: first_usable_lba incorrect: %lld > %lld\n", - (unsigned long long)le64_to_cpu((*gpt)->first_usable_lba), + (unsigned long long)le64_to_cpu(gpt->first_usable_lba), (unsigned long long)lastlba); - goto fail; + return -EINVAL; } - if (le64_to_cpu((*gpt)->last_usable_lba) > lastlba) { + if (le64_to_cpu(gpt->last_usable_lba) > lastlba) { 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->last_usable_lba), (unsigned long long)lastlba); - goto fail; + return -EINVAL; } - if (le64_to_cpu((*gpt)->last_usable_lba) < le64_to_cpu((*gpt)->first_usable_lba)) { + 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; + (unsigned long long)le64_to_cpu(gpt->last_usable_lba), + (unsigned long long)le64_to_cpu(gpt->first_usable_lba)); + return -EINVAL; } + /* Check that sizeof_partition_entry has the correct value */ - if (le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) { + if (le32_to_cpu(gpt->sizeof_partition_entry) != sizeof(gpt_entry)) { pr_debug("GUID Partition Entry Size check failed.\n"); - goto fail; + return -EINVAL; } /* Sanity check partition table size */ - pt_size = (u64)le32_to_cpu((*gpt)->num_partition_entries) * - le32_to_cpu((*gpt)->sizeof_partition_entry); + pt_size = (u64)get_pt_size(gpt); 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; + return -EINVAL; } - if (!(*ptes = alloc_read_gpt_entries(state, *gpt))) - goto fail; + return 0; +} - /* Check the GUID Partition Entry Array CRC */ - crc = efi_crc32((const unsigned char *) (*ptes), pt_size); +/* Check the GUID Partition Entry Array CRC */ +int gpt_check_pte_array_crc(gpt_header *gpt, gpt_entry *ptes) +{ + u32 crc; - if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) { + crc = efi_crc32((const unsigned char *)ptes, get_pt_size(gpt)); + if (crc != le32_to_cpu(gpt->partition_entry_array_crc32)) { pr_debug("GUID Partition Entry Array CRC check failed.\n"); - goto fail_ptes; + return -EINVAL; } - /* 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:pte to check - * @lastlba: last lba of the disk + * is_gpt_valid() - tests one GPT header and PTEs for validity + * @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. * * Description: returns 1 if valid, 0 on error. + * If valid, returns pointers to newly allocated GPT header and PTEs. */ -static inline int -is_pte_valid(const gpt_entry *pte, const u64 lastlba) +static int is_gpt_valid(struct parsed_partitions *state, u64 lba, + gpt_header **gpt, gpt_entry **ptes) { - if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) || - le64_to_cpu(pte->starting_lba) > lastlba || - le64_to_cpu(pte->ending_lba) > lastlba) + u64 lastlba; + + if (!ptes) return 0; + + *gpt = alloc_read_gpt_header(state, lba); + if (!(*gpt)) + return 0; + + lastlba = last_lba(state->disk); + if (gpt_validate_header(*gpt, lba, + queue_logical_block_size(state->disk->queue), + lastlba)) + goto fail; + + *ptes = alloc_read_gpt_entries(state, *gpt); + if (!(*ptes)) + goto fail; + + if (gpt_check_pte_array_crc(*gpt, *ptes)) + goto fail_ptes; + + /* We're done, all's well */ return 1; + +fail_ptes: + kfree(*ptes); + *ptes = NULL; +fail: + kfree(*gpt); + *gpt = NULL; + return 0; } /** - * compare_gpts() - Search disk for valid GPT headers and PTEs + * gpt_compare_alt() - Compares the Primary and Alternate GPT headers * @pgpt: primary GPT header * @agpt: alternate GPT header * @lastlba: last LBA number * - * Description: Returns nothing. Sanity checks pgpt and agpt fields - * and prints warnings on discrepancies. - * + * Description: Sanity checks pgpt and agpt fields and prints warnings + * on discrepancies. Returns error count. GPT parsers can choose to + * ignore this or not. + * */ -static void -compare_gpts(gpt_header *pgpt, gpt_header *agpt, u64 lastlba) +int gpt_compare_alt(gpt_header *pgpt, gpt_header *agpt, u64 lastlba) { int error_found = 0; + if (!pgpt || !agpt) - return; + return -EINVAL; + if (le64_to_cpu(pgpt->my_lba) != le64_to_cpu(agpt->alternate_lba)) { pr_warn("GPT:Primary header LBA != Alt. header alternate_lba\n"); pr_warn("GPT:%lld != %lld\n", @@ -557,7 +574,7 @@ compare_gpts(gpt_header *pgpt, gpt_header *agpt, u64 lastlba) if (error_found) pr_warn("GPT: Use GNU Parted to correct GPT errors.\n"); - return; + return error_found; } /** @@ -601,7 +618,7 @@ static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt, goto fail; read_lba(state, 0, (u8 *)legacymbr, sizeof(*legacymbr)); - good_pmbr = is_pmbr_valid(legacymbr, total_sectors); + good_pmbr = gpt_is_pmbr_valid(legacymbr, total_sectors); kfree(legacymbr); if (!good_pmbr) @@ -635,7 +652,7 @@ static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt, if (!good_pgpt && !good_agpt) goto fail; - compare_gpts(pgpt, agpt, lastlba); + gpt_compare_alt(pgpt, agpt, lastlba); /* The good cases */ if (good_pgpt) { @@ -674,7 +691,7 @@ static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt, * 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) +void utf16_le_to_7bit(const __le16 *in, unsigned int size, u8 *out) { unsigned int i = 0; @@ -731,7 +748,7 @@ int efi_partition(struct parsed_partitions *state) u64 size = le64_to_cpu(ptes[i].ending_lba) - le64_to_cpu(ptes[i].starting_lba) + 1ULL; - if (!is_pte_valid(&ptes[i], last_lba(state->disk))) + if (!gpt_is_pte_valid(&ptes[i], last_lba(state->disk))) continue; put_partition(state, i + 1, start * ssz, size * ssz); diff --git a/include/linux/gpt.h b/include/linux/gpt.h index 633be6bc826c..f7f5892fe256 100644 --- a/include/linux/gpt.h +++ b/include/linux/gpt.h @@ -8,8 +8,8 @@ * Copyright 2000,2001 Dell Inc. ************************************************************/ -#ifndef FS_PART_EFI_H_INCLUDED -#define FS_PART_EFI_H_INCLUDED +#ifndef _GPT_H +#define _GPT_H #include #include @@ -111,4 +111,37 @@ typedef struct _legacy_mbr { __le16 signature; } __packed legacy_mbr; +// Helpers for validating GPT metadata +int gpt_is_pmbr_valid(legacy_mbr *mbr, sector_t total_sectors); +int gpt_validate_header(gpt_header *gpt, u64 lba, unsigned int lba_size, + u64 lastlba); +int gpt_check_pte_array_crc(gpt_header *gpt, gpt_entry *ptes); +int gpt_compare_alt(gpt_header *pgpt, gpt_header *agpt, u64 lastlba); + +/** + * is_pte_valid() - tests one PTE for validity + * @pte:pte to check + * @lastlba: last lba of the disk + * + * returns 1 if valid, 0 on error. + */ + static inline bool +gpt_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; +} + +// Returns size in bytes of PTE array +static inline int get_pt_size(gpt_header *gpt) +{ + return le32_to_cpu(gpt->num_partition_entries) + * le32_to_cpu(gpt->sizeof_partition_entry); +} + +void utf16_le_to_7bit(const __le16 *in, unsigned int size, u8 *out); + #endif From patchwork Mon Dec 11 15:12:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 752678 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="E5t7jfdU" Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E52E6F3; Mon, 11 Dec 2023 07:12:49 -0800 (PST) Received: by mail.gandi.net (Postfix) with ESMTPSA id C7B6AE0006; Mon, 11 Dec 2023 15:12:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1702307568; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=27exMqHpZla3nbyFfGXB40l8DvvyaMkXegra0K6TRzY=; b=E5t7jfdU0yZKAmOSFgdr3XJOL59pux6eGaoszNC8A0npWR2u+foG+ZchJ3XAfaYZ8BzyPx UTVQWoWy+FlGpqULjm4GBagvMpPIv72wahJCI9sNjoFQdPDS7vNLauHVKnLOVvetKcuCDK MZV6RyLjrgzC7nZCKyRtWeJjL7BIvrlrTD4uzzc7ddTx7sZD8qoB6t+NAKDO8mZYXHkZ8V D2jGIyzZyXTfTHHTWCiWdzow0n4TrvizEwEqCsJkpdsgGW7mrmtJ1qREUsKmkq/UMbSDZe fhuvP5km3b8DSK5SMcrtKaHBbxu5PEB5yImOt5oZYde4NYo4obchrvIT1PsppQ== From: Romain Gantois To: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jens Axboe , Davidlohr Bueso , Ard Biesheuvel Cc: Romain Gantois , Thomas Petazzoni , Herve Codina , linux-mtd@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-block@vger.kernel.org, linux-efi@vger.kernel.org Subject: [RFC PATCH 4/6] block: partitions: efi: Move GPT-specific code to a new library Date: Mon, 11 Dec 2023 16:12:40 +0100 Message-ID: <20231211151244.289349-5-romain.gantois@bootlin.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231211151244.289349-1-romain.gantois@bootlin.com> References: <20231211151244.289349-1-romain.gantois@bootlin.com> Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-GND-Sasl: romain.gantois@bootlin.com The GPT parser in the block layer contains much logic that is not specific to block devices. Move all of this logic to a new generic GPT library so that future GPT parsers can make use of it. lib/gpt.c is designed as a stateless library. It mainly contains helpers that validate GPT metadata. The efi_crc32 function is moved out of the block layer GPT parser and into the generic efi header. There is no functional change. Signed-off-by: Romain Gantois --- MAINTAINERS | 1 + block/partitions/Kconfig | 2 +- block/partitions/efi.c | 336 -------------------------------------- include/linux/efi.h | 18 +++ lib/Kconfig | 3 + lib/Makefile | 3 + lib/gpt.c | 342 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 368 insertions(+), 337 deletions(-) create mode 100644 lib/gpt.c diff --git a/MAINTAINERS b/MAINTAINERS index 22e37e2ea1ae..c01abee48b75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9235,6 +9235,7 @@ L: linux-efi@vger.kernel.org S: Maintained F: block/partitions/efi.c F: include/linux/gpt.h +F: lib/gpt.c HABANALABS PCI DRIVER M: Oded Gabbay diff --git a/block/partitions/Kconfig b/block/partitions/Kconfig index 7aff4eb81c60..c2b2618213ba 100644 --- a/block/partitions/Kconfig +++ b/block/partitions/Kconfig @@ -250,7 +250,7 @@ config KARMA_PARTITION config EFI_PARTITION bool "EFI GUID Partition support" if PARTITION_ADVANCED default y - select CRC32 + select GENERIC_LIB_GPT help Say Y here if you would like to use hard disks under Linux which were partitioned using EFI GPT. diff --git a/block/partitions/efi.c b/block/partitions/efi.c index 3630ebf4b997..58bcd2cbcdf8 100644 --- a/block/partitions/efi.c +++ b/block/partitions/efi.c @@ -83,7 +83,6 @@ * ************************************************************/ #include -#include #include #include #include @@ -103,24 +102,6 @@ force_gpt_fn(char *str) } __setup("gpt", force_gpt_fn); -/** - * 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(~0L, buf, len) ^ ~0L); -} - /** * last_lba(): return number of last logical block of device * @disk: block device @@ -136,91 +117,6 @@ static u64 last_lba(struct gendisk *disk) queue_logical_block_size(disk->queue)) - 1ULL; } -static inline int pmbr_part_valid(gpt_mbr_record *part) -{ - 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; - - return GPT_MBR_PROTECTIVE; -invalid: - return 0; -} - -/** - * gpt_is_pmbr_valid(): test Protective MBR for validity - * @mbr: pointer to a legacy mbr structure - * @total_sectors: amount of sectors in the device - * - * Description: Checks for a valid protective or hybrid - * master boot record (MBR). The validity of a pMBR depends - * on all of the following properties: - * 1) MSDOS signature is in the last two bytes of the MBR - * 2) One partition of type 0xEE is found - * - * 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. - */ -int gpt_is_pmbr_valid(legacy_mbr *mbr, sector_t total_sectors) -{ - uint32_t sz = 0; - int i, part = 0, ret = 0; /* invalid by default */ - - if (!mbr || le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE) - goto done; - - for (i = 0; i < 4; i++) { - ret = pmbr_part_valid(&mbr->partition_record[i]); - if (ret == GPT_MBR_PROTECTIVE) { - part = i; - /* - * 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: - for (i = 0; i < 4; i++) - if (mbr->partition_record[i].os_type != - EFI_PMBR_OSTYPE_EFI_GPT && - mbr->partition_record[i].os_type != 0x00) - ret = GPT_MBR_HYBRID; - - /* - * Protective MBRs take up the lesser of the whole disk - * or 2 TiB (32bit LBA), ignoring the rest of the disk. - * Some partitioning programs, nonetheless, choose to set - * the size to the maximum 32-bit limitation, disregarding - * the disk size. - * - * Hybrid MBRs do not necessarily comply with this. - * - * Consider a bad value here to be a warning to support dd'ing - * an image from a smaller disk to a larger disk. - */ - if (ret == GPT_MBR_PROTECTIVE) { - sz = le32_to_cpu(mbr->partition_record[part].size_in_lba); - if (sz != (uint32_t)total_sectors - 1 && sz != 0xFFFFFFFF) - pr_debug("GPT: mbr size in lba (%u) different than whole disk (%u).\n", - sz, min_t(uint32_t, - total_sectors - 1, 0xFFFFFFFF)); - } -done: - return ret; -} - /** * read_lba(): Read bytes from disk, starting at given LBA * @state: disk parsed partitions @@ -323,119 +219,6 @@ static gpt_header *alloc_read_gpt_header(struct parsed_partitions *state, return gpt; } -/** - * gpt_validate_header() - tests one GPT header for validity - * @gpt: header to check - * @lba: logical block address of the GPT header to test - * @lba_size: logical block size of the partitioned device - * @lastlba: last logical block on the partitioned device - * - * Returns 0 if validation was successful. - */ -int gpt_validate_header(gpt_header *gpt, u64 lba, unsigned int lba_size, - u64 lastlba) -{ - u32 crc, origcrc; - u64 pt_size; - - /* Check the GUID Partition Table signature */ - if (le64_to_cpu(gpt->signature) != GPT_HEADER_SIGNATURE) { - 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); - return -EINVAL; - } - - /* Check the GUID Partition Table header size is too big */ - if (le32_to_cpu(gpt->header_size) > lba_size) { - pr_debug("GUID Partition Table Header size is too large: %u > %u\n", - le32_to_cpu(gpt->header_size), lba_size); - return -EINVAL; - } - - /* 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)); - return -EINVAL; - } - - /* 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) { - pr_debug("GUID Partition Table Header CRC is wrong: %x != %x\n", - crc, origcrc); - return -EINVAL; - } - 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) { - pr_debug("GPT my_lba incorrect: %lld != %lld\n", - (unsigned long long)le64_to_cpu(gpt->my_lba), - (unsigned long long)lba); - return -EINVAL; - } - - /* Check the first_usable_lba and last_usable_lba are - * within the disk. - */ - if (le64_to_cpu(gpt->first_usable_lba) > lastlba) { - pr_debug("GPT: first_usable_lba incorrect: %lld > %lld\n", - (unsigned long long)le64_to_cpu(gpt->first_usable_lba), - (unsigned long long)lastlba); - return -EINVAL; - } - if (le64_to_cpu(gpt->last_usable_lba) > lastlba) { - pr_debug("GPT: last_usable_lba incorrect: %lld > %lld\n", - (unsigned long long)le64_to_cpu(gpt->last_usable_lba), - (unsigned long long)lastlba); - return -EINVAL; - } - 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)); - return -EINVAL; - } - - /* Check that sizeof_partition_entry has the correct value */ - if (le32_to_cpu(gpt->sizeof_partition_entry) != sizeof(gpt_entry)) { - pr_debug("GUID Partition Entry Size check failed.\n"); - return -EINVAL; - } - - /* Sanity check partition table size */ - pt_size = (u64)get_pt_size(gpt); - 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); - return -EINVAL; - } - - return 0; -} - -/* Check the GUID Partition Entry Array CRC */ -int gpt_check_pte_array_crc(gpt_header *gpt, gpt_entry *ptes) -{ - u32 crc; - - crc = efi_crc32((const unsigned char *)ptes, get_pt_size(gpt)); - if (crc != le32_to_cpu(gpt->partition_entry_array_crc32)) { - pr_debug("GUID Partition Entry Array CRC check failed.\n"); - return -EINVAL; - } - - return 0; -} - /** * is_gpt_valid() - tests one GPT header and PTEs for validity * @state: disk parsed partitions @@ -483,100 +266,6 @@ static int is_gpt_valid(struct parsed_partitions *state, u64 lba, return 0; } -/** - * gpt_compare_alt() - Compares the Primary and Alternate GPT headers - * @pgpt: primary GPT header - * @agpt: alternate GPT header - * @lastlba: last LBA number - * - * Description: Sanity checks pgpt and agpt fields and prints warnings - * on discrepancies. Returns error count. GPT parsers can choose to - * ignore this or not. - * - */ -int gpt_compare_alt(gpt_header *pgpt, gpt_header *agpt, u64 lastlba) -{ - int error_found = 0; - - if (!pgpt || !agpt) - return -EINVAL; - - if (le64_to_cpu(pgpt->my_lba) != le64_to_cpu(agpt->alternate_lba)) { - pr_warn("GPT:Primary header LBA != Alt. header alternate_lba\n"); - pr_warn("GPT:%lld != %lld\n", - (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)) { - pr_warn("GPT:Primary header alternate_lba != Alt. header my_lba\n"); - pr_warn("GPT:%lld != %lld\n", - (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)) { - pr_warn("GPT:first_usable_lbas don't match.\n"); - pr_warn("GPT:%lld != %lld\n", - (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)) { - pr_warn("GPT:last_usable_lbas don't match.\n"); - pr_warn("GPT:%lld != %lld\n", - (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)) { - pr_warn("GPT:disk_guids don't match.\n"); - error_found++; - } - if (le32_to_cpu(pgpt->num_partition_entries) != - le32_to_cpu(agpt->num_partition_entries)) { - pr_warn("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)) { - pr_warn("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)) { - pr_warn("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) { - pr_warn("GPT:Primary header thinks Alt. header is not at the end of the disk.\n"); - pr_warn("GPT:%lld != %lld\n", - (unsigned long long)le64_to_cpu(pgpt->alternate_lba), - (unsigned long long)lastlba); - error_found++; - } - - if (le64_to_cpu(agpt->my_lba) != lastlba) { - pr_warn("GPT:Alternate GPT header not at the end of the disk.\n"); - pr_warn("GPT:%lld != %lld\n", - (unsigned long long)le64_to_cpu(agpt->my_lba), - (unsigned long long)lastlba); - error_found++; - } - - if (error_found) - pr_warn("GPT: Use GNU Parted to correct GPT errors.\n"); - return error_found; -} - /** * find_valid_gpt() - Search disk for valid GPT headers and PTEs * @state: disk parsed partitions @@ -682,31 +371,6 @@ static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt, return 0; } -/** - * 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. - */ -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++; - } -} - /** * efi_partition - scan for GPT partitions * @state: disk parsed partitions diff --git a/include/linux/efi.h b/include/linux/efi.h index 9cc5bf32f6f2..c4b818f77de7 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -1348,4 +1349,21 @@ bool efi_config_table_is_usable(const efi_guid_t *guid, unsigned long table) umode_t efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n); +/** + * 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(~0L, buf, len) ^ ~0L); +} + #endif /* _LINUX_EFI_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 3ea1c830efab..de776911944e 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -748,6 +748,9 @@ config GENERIC_LIB_ASHLDI3 config GENERIC_LIB_ASHRDI3 bool +config GENERIC_LIB_GPT + bool + config GENERIC_LIB_LSHRDI3 bool diff --git a/lib/Makefile b/lib/Makefile index 6b09731d8e61..fba8fae70efa 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -444,3 +444,6 @@ $(obj)/$(TEST_FORTIFY_LOG): $(addprefix $(obj)/, $(TEST_FORTIFY_LOGS)) FORCE ifeq ($(CONFIG_FORTIFY_SOURCE),y) $(obj)/string.o: $(obj)/$(TEST_FORTIFY_LOG) endif + +# GPT parsing routines +obj-$(CONFIG_GENERIC_LIB_GPT) += gpt.o diff --git a/lib/gpt.c b/lib/gpt.c new file mode 100644 index 000000000000..16303634f4fb --- /dev/null +++ b/lib/gpt.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* EFI GUID Partition Table handling + * + * http://www.uefi.org/specs/ + * http://www.intel.com/technology/efi/ + * + * efi.[ch] by Matt Domsch + * Copyright 2000,2001,2002,2004 Dell Inc. + * + * This code was previously in block/partitions/efi.c + * and was moved in /lib so that other kernel subsystems + * could use it as a common GPT parsing library. + * + * This library should be stateless and not make any + * assumptions about the type of device the GPT data + * came from. + * + */ + +#include +#include + +static inline int pmbr_part_valid(gpt_mbr_record *part) +{ + 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; + + return GPT_MBR_PROTECTIVE; +invalid: + return 0; +} + +/** + * gpt_is_pmbr_valid(): test Protective MBR for validity + * @mbr: pointer to a legacy mbr structure + * @total_sectors: amount of sectors in the device + * + * Description: Checks for a valid protective or hybrid + * master boot record (MBR). The validity of a pMBR depends + * on all of the following properties: + * 1) MSDOS signature is in the last two bytes of the MBR + * 2) One partition of type 0xEE is found + * + * 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. + */ +int gpt_is_pmbr_valid(legacy_mbr *mbr, sector_t total_sectors) +{ + int i, part = 0, ret = 0; /* invalid by default */ + uint32_t sz = 0; + + if (!mbr || le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE) + goto done; + + for (i = 0; i < 4; i++) { + ret = pmbr_part_valid(&mbr->partition_record[i]); + if (ret == GPT_MBR_PROTECTIVE) { + part = i; + /* + * 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: + for (i = 0; i < 4; i++) + if (mbr->partition_record[i].os_type != EFI_PMBR_OSTYPE_EFI_GPT && + mbr->partition_record[i].os_type != 0x00) + ret = GPT_MBR_HYBRID; + + /* + * Protective MBRs take up the lesser of the whole disk + * or 2 TiB (32bit LBA), ignoring the rest of the disk. + * Some partitioning programs, nonetheless, choose to set + * the size to the maximum 32-bit limitation, disregarding + * the disk size. + * + * Hybrid MBRs do not necessarily comply with this. + * + * Consider a bad value here to be a warning to support dd'ing + * an image from a smaller disk to a larger disk. + */ + if (ret == GPT_MBR_PROTECTIVE) { + sz = le32_to_cpu(mbr->partition_record[part].size_in_lba); + if (sz != (uint32_t)total_sectors - 1 && sz != 0xFFFFFFFF) + pr_debug("GPT: mbr size in lba (%u) different than whole disk (%u).\n", + sz, min_t(uint32_t, + total_sectors - 1, 0xFFFFFFFF)); + } +done: + return ret; +} +EXPORT_SYMBOL_GPL(gpt_is_pmbr_valid); + +/** + * gpt_validate_header() - tests one GPT header for validity + * @gpt: header to check + * @lba: logical block address of the GPT header to test + * @lba_size: logical block size of the partitioned device + * @lastlba: last logical block on the partitioned device + * + * Returns 0 if validation was successful. + */ +int gpt_validate_header(gpt_header *gpt, u64 lba, unsigned int lba_size, + u64 lastlba) +{ + u32 crc, origcrc; + u64 pt_size; + + /* Check the GUID Partition Table signature */ + if (le64_to_cpu(gpt->signature) != GPT_HEADER_SIGNATURE) { + 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); + return -EINVAL; + } + + /* Check the GUID Partition Table header size is too big */ + if (le32_to_cpu(gpt->header_size) > lba_size) { + pr_debug("GUID Partition Table Header size is too large: %u > %u\n", + le32_to_cpu(gpt->header_size), lba_size); + return -EINVAL; + } + + /* 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)); + return -EINVAL; + } + + /* 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) { + pr_debug("GUID Partition Table Header CRC is wrong: %x != %x\n", + crc, origcrc); + return -EINVAL; + } + 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) { + pr_debug("GPT my_lba incorrect: %lld != %lld\n", + (unsigned long long)le64_to_cpu(gpt->my_lba), + (unsigned long long)lba); + return -EINVAL; + } + + /* Check the first_usable_lba and last_usable_lba are + * within the disk. + */ + if (le64_to_cpu(gpt->first_usable_lba) > lastlba) { + pr_debug("GPT: first_usable_lba incorrect: %lld > %lld\n", + (unsigned long long)le64_to_cpu(gpt->first_usable_lba), + (unsigned long long)lastlba); + return -EINVAL; + } + if (le64_to_cpu(gpt->last_usable_lba) > lastlba) { + pr_debug("GPT: last_usable_lba incorrect: %lld > %lld\n", + (unsigned long long)le64_to_cpu(gpt->last_usable_lba), + (unsigned long long)lastlba); + return -EINVAL; + } + 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)); + return -EINVAL; + } + + /* Check that sizeof_partition_entry has the correct value */ + if (le32_to_cpu(gpt->sizeof_partition_entry) != sizeof(gpt_entry)) { + pr_debug("GUID Partition Entry Size check failed.\n"); + return -EINVAL; + } + + /* Sanity check partition table size */ + pt_size = (u64)get_pt_size(gpt); + 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); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(gpt_validate_header); + +/* Check the GUID Partition Entry Array CRC */ +int gpt_check_pte_array_crc(gpt_header *gpt, gpt_entry *ptes) +{ + u32 crc; + + crc = efi_crc32((const unsigned char *)ptes, get_pt_size(gpt)); + if (crc != le32_to_cpu(gpt->partition_entry_array_crc32)) { + pr_debug("GUID Partition Entry Array CRC check failed.\n"); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(gpt_check_pte_array_crc); + +/** + * gpt_compare_alt() - Compares the Primary and Alternate GPT headers + * @pgpt: primary GPT header + * @agpt: alternate GPT header + * @lastlba: last LBA number + * + * Description: Sanity checks pgpt and agpt fields and prints warnings + * on discrepancies. Returns error count. GPT parsers can choose to + * ignore this or not. + * + */ +int gpt_compare_alt(gpt_header *pgpt, gpt_header *agpt, u64 lastlba) +{ + int error_found = 0; + + if (!pgpt || !agpt) + return -EINVAL; + + if (le64_to_cpu(pgpt->my_lba) != le64_to_cpu(agpt->alternate_lba)) { + pr_warn("GPT:Primary header LBA != Alt. header alternate_lba\n"); + pr_warn("GPT:%lld != %lld\n", + (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)) { + pr_warn("GPT:Primary header alternate_lba != Alt. header my_lba\n"); + pr_warn("GPT:%lld != %lld\n", + (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)) { + pr_warn("GPT:first_usable_lbas don't match.\n"); + pr_warn("GPT:%lld != %lld\n", + (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)) { + pr_warn("GPT:last_usable_lbas don't match.\n"); + pr_warn("GPT:%lld != %lld\n", + (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)) { + pr_warn("GPT:disk_guids don't match.\n"); + error_found++; + } + if (le32_to_cpu(pgpt->num_partition_entries) != + le32_to_cpu(agpt->num_partition_entries)) { + pr_warn("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)) { + pr_warn("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)) { + pr_warn("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) { + pr_warn("GPT:Primary header thinks Alt. header is not at the end of the disk.\n"); + pr_warn("GPT:%lld != %lld\n", + (unsigned long long)le64_to_cpu(pgpt->alternate_lba), + (unsigned long long)lastlba); + error_found++; + } + + if (le64_to_cpu(agpt->my_lba) != lastlba) { + pr_warn("GPT:Alternate GPT header not at the end of the disk.\n"); + pr_warn("GPT:%lld != %lld\n", + (unsigned long long)le64_to_cpu(agpt->my_lba), + (unsigned long long)lastlba); + error_found++; + } + + if (error_found) + pr_warn("GPT: Use GNU Parted to correct GPT errors.\n"); + return error_found; +} +EXPORT_SYMBOL_GPL(gpt_compare_alt); + +/** + * 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. + */ +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++; + } +} +EXPORT_SYMBOL_GPL(utf16_le_to_7bit);