From patchwork Mon Dec 11 15:12:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 1874564 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=k5oRsPyL; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.a=rsa-sha256 header.s=gm1 header.b=VrzXpEEb; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=patchwork.ozlabs.org) Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Splcr2FnXz20Gc for ; Tue, 12 Dec 2023 02:13:32 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=rVI5IQN8WcC0gbejEHT3oO95U1/s2l1Sf4pQlvF1Pos=; b=k5oRsPyLUpBXzL gUhET6IMzu5eu/q5joLXBmIEF5Q/agDNXi1xMWREqviKLD08rwbzsh1B8Sqnx7GpQ5n0zl4IsS0cf AV9lcq3OY1E3C0JPRnxJCRq+4oiH2H853tBGVGyqflXSxbYTsI4p/7buILmpOyNHcWL2Bp/fAdW2u 91J1Jkm68ia5TKqD8oSu6J8qZFftllJ7k/c+AJtulJSFmrXLPf2qOWX8StlJefzMYU+LVbYL8+kkC GnLHn5vDggafzPLdSVbLzuJQfAVR1si0Ro/Pd+Dm1UnsQtg/x/cufJM1p7Bsdw9RGlc0ZETYPscOW hvfe7eqSJDwsST/1JFiA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1rChxx-005KUa-1Z; Mon, 11 Dec 2023 15:13:05 +0000 Received: from relay4-d.mail.gandi.net ([2001:4b98:dc4:8::224]) by bombadil.infradead.org with esmtps (Exim 4.96 #2 (Red Hat Linux)) id 1rChxk-005KHH-1J for linux-mtd@lists.infradead.org; Mon, 11 Dec 2023 15:13:00 +0000 Received: by mail.gandi.net (Postfix) with ESMTPSA id 8502AE000E; Mon, 11 Dec 2023 15:12:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1702307570; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=wZL+Ok+DrGFVb0w/xL+QP9Tdye38sT6TcArWDXMdC3U=; b=VrzXpEEbYkvP/2A4q27Kd2PttMT6x1PwMMvCQt0aKPHdixEI1DtEy0lxzK9ADTPXYkihBh 6NhoCAEZhnnYnUQjCdL7IOKSJyQFQZqxbIt7DpWRWs4AfdGtAJ2KTKs1LPYtGyKKioiG/L n9ffeTZIxeMwyZtllXsm7F+hibLap2VbD3sWlzq0WZleFDFbZO3xw2y6WHQS5PtSl6eYGF GbEwrVOMDXggPEI3XQy48Wb3d+xRimJHq7C9LukbJW1ACJWz7dX+m7KJ6xA0IAsUIIktUy HCwM+hv3s3lH6hoAW/xzZnTY1ZC8UB0BNAjI1ZDvcTUUzH5i/gpymCk/0/DBrQ== 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 5/6] drivers: mtd: introduce GPT parser for NOR flash devices Date: Mon, 11 Dec 2023 16:12:41 +0100 Message-ID: <20231211151244.289349-6-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> MIME-Version: 1.0 X-GND-Sasl: romain.gantois@bootlin.com X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20231211_071252_747946_A8203395 X-CRM114-Status: GOOD ( 30.90 ) X-Spam-Score: -0.9 (/) X-Spam-Report: Spam detection software, running on the system "bombadil.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: MTD devices can be partitioned using a fixed partitioning scheme defined in the relevant device tree node. Dynamic partitioning is also possible, using MTD parsers. They are capable of parsing partiti [...] Content analysis details: (-0.9 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at https://www.dnswl.org/, low trust [2001:4b98:dc4:8:0:0:0:224 listed in] [list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org MTD devices can be partitioned using a fixed partitioning scheme defined in the relevant device tree node. Dynamic partitioning is also possible, using MTD parsers. They are capable of parsing partition tables on the device at boot time. Add support for GPT parsing at the MTD layer. The parser logic makes use of a common set of GPT helpers and typedefs that have previously been extracted from the block layer parser. The main issue with GPT partitioning are the userspace tools which are already capable of writing a partition table. Not using them would require re-inventing the wheel, but on the other side they are block-oriented tools with absolutely no knowledge of the specificities of MTD flashes. Tools such as parted or fdisk act upon block devices and not mtd devices, so we need a block abstraction for writing the table. Thus, the only possible solution so far is the use of mtdblock in-between, which raises two main concerns: the lack of bad-block handling and wear-leveling. In order to avoid risky situations, the parser will only parse NORs. Example usage: > echo " device: /dev/mtdblock0 unit: sectors first-lba: 40 last-lba: 32734 sector-size: 512 start=40, size=8 start=48, size=80 start=128, size=32600 " | sfdisk /dev/mtdblock0 After rebooting: > lsmtd DEVICE MAJ:MIN NAME TYPE SIZE mtd0 90:0 spi0.0 nor 16M mtd1 90:2 nor 4K mtd2 90:4 nor 40K mtd3 90:6 nor 15.9M Signed-off-by: Romain Gantois --- drivers/mtd/parsers/Kconfig | 10 ++ drivers/mtd/parsers/Makefile | 1 + drivers/mtd/parsers/gpt.c | 222 +++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+) create mode 100644 drivers/mtd/parsers/gpt.c diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig index da03ab6efe04..3f34220c6fc3 100644 --- a/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig @@ -118,6 +118,16 @@ config MTD_AFS_PARTS for your particular device. It won't happen automatically. The 'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example. +config MTD_GPT_PARTS + tristate "GPT partition table parsing" + select GENERIC_LIB_GPT + help + This allow specifying MTD partitions on a NOR Flash device using + a fixed-location GPT partition table. No wear-leveling is done by + this parser, and it cannot recover from bad blocks in critical GPT + metadata locations. For these reasons, NAND Flash devices cannot + make use of this partition parser. + config MTD_PARSER_TPLINK_SAFELOADER tristate "TP-Link Safeloader partitions parser" depends on MTD && (ARCH_BCM_5301X || ATH79 || SOC_MT7620 || SOC_MT7621 || COMPILE_TEST) diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile index 9b00c62b837a..eb31585f2dd1 100644 --- a/drivers/mtd/parsers/Makefile +++ b/drivers/mtd/parsers/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o +obj-$(CONFIG_MTD_GPT_PARTS) += gpt.o diff --git a/drivers/mtd/parsers/gpt.c b/drivers/mtd/parsers/gpt.c new file mode 100644 index 000000000000..c2fa9545b4b5 --- /dev/null +++ b/drivers/mtd/parsers/gpt.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MTD parser for GPT partition tables. + * + * This parser supports GPT partition tables located at fixed standard sectors. + * Partitioning a raw flash device in this manner prevents wear-leveling and bad + * block handling at the whole-device level. Note that bad blocks on critical + * GPT sectors will completely break your partition table! Because of this, use + * of this parser is restricted to NOR flash devices, which are less susceptible + * to bad blocks than NAND flash devices. + * + * http://www.uefi.org/specs/ + * + * Acronyms: + * PTE: Partition Table Entry + * LBA: Logical Block Address + * + * Copyright © 2023 Bootlin + * + * Author: Romain Gantois + * + */ + +#include +#include +#include +#include +#include + +/* + * We assume that the GPT partition was written through an mtdblock device. This + * would make the LBA size 512. + */ +#define MTD_GPT_LBA_SIZE 512 + +/* + * This value is pretty much arbitrary, it's in the range of typical MTD parser + * caps. Creating too many partitions on a raw Flash device is a bad idea + * anyway. + */ +#define MTD_GPT_MAX_PARTS 32 + +#define mtd_gpt_lba_to_offset(x) ((x) * MTD_GPT_LBA_SIZE) +#define mtd_gpt_lba_to_size(x) ((size_t)(x) * MTD_GPT_LBA_SIZE) + +#define MTD_GPT_PARTNAME_SIZE sizeof(((gpt_entry *)0)->partition_name) + +static int mtd_gpt_read_header(struct mtd_info *mtd, gpt_header *gpt, int lba, u64 last_lba) +{ + loff_t read_offset = mtd_gpt_lba_to_offset(lba); + size_t retlen; + int ret; + + dev_dbg(&mtd->dev, "reading GPT header at offset %lli\n", read_offset); + + ret = mtd_read(mtd, read_offset, MTD_GPT_LBA_SIZE, &retlen, (u_char *)gpt); + if (ret) { + dev_err(&mtd->dev, "failed to read GPT header\n"); + return -EIO; + } + + if (retlen < MTD_GPT_LBA_SIZE) { + dev_err(&mtd->dev, "truncated read 0x%zx expected 0x%x\n", + retlen, MTD_GPT_LBA_SIZE); + return -EIO; + } + + return gpt_validate_header(gpt, lba, MTD_GPT_LBA_SIZE, last_lba); +} + +static int mtd_gpt_parse_partitions(struct mtd_info *mtd, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + int ret = 0, valid_parts = 0, part_no; + u64 last_lba = div_u64(mtd->size, MTD_GPT_LBA_SIZE) - 1ULL; + struct mtd_partition *parts, *part; + size_t pte_read_size, retlen; + char *name_buf, *cur_name; + loff_t pte_read_offset; + gpt_header *gpt, *agpt; + gpt_entry *ptes, *pte; + unsigned int nr_parts; + + if (mtd_type_is_nand(mtd)) { + /* NAND flash devices are too susceptible to bad blocks */ + dev_err(&mtd->dev, "GPT parsing is forbidden on NAND devices!"); + return -EPERM; + } + + gpt = kzalloc(MTD_GPT_LBA_SIZE, GFP_KERNEL); + if (!gpt) + return -ENOMEM; + + agpt = kzalloc(MTD_GPT_LBA_SIZE, GFP_KERNEL); + if (!agpt) + return -ENOMEM; + + ret = mtd_gpt_read_header(mtd, gpt, GPT_PRIMARY_PARTITION_TABLE_LBA, last_lba); + if (ret) { + dev_warn(&mtd->dev, + "invalid primary GPT header: error %d, trying alternate", ret); + ret = mtd_gpt_read_header(mtd, gpt, le64_to_cpu(gpt->alternate_lba), last_lba); + if (ret) { + dev_err(&mtd->dev, "invalid alternate GPT header: error %d\n", ret); + goto free_gpt; + } + } else { + /* Check alternate header and warn if it doesn't match primary header */ + gpt_compare_alt(gpt, agpt, last_lba); + } + + /* This should contain the PTE array */ + pte_read_offset = mtd_gpt_lba_to_offset(le64_to_cpu(gpt->partition_entry_lba)); + pte_read_size = get_pt_size(gpt); + ptes = kzalloc(pte_read_size, GFP_KERNEL); + if (!ptes) { + ret = -ENOMEM; + goto free_gpt; + } + + dev_dbg(&mtd->dev, "reading PTE array offset %lli size 0x%zx\n", + pte_read_offset, pte_read_size); + + ret = mtd_read(mtd, pte_read_offset, pte_read_size, &retlen, (u_char *)ptes); + if (ret) + goto free_ptes; + + if (retlen < pte_read_size) { + ret = -EIO; + goto free_ptes; + } + + ret = gpt_check_pte_array_crc(gpt, ptes); + if (ret) { + dev_err(&mtd->dev, + "CRC check failed for GPT Partition Table Entry array! error %d\n", ret); + goto free_ptes; + } + + nr_parts = le32_to_cpu(gpt->num_partition_entries); + parts = kcalloc(min(nr_parts, MTD_GPT_MAX_PARTS), + sizeof(*parts), + GFP_KERNEL); + if (!parts) { + ret = -ENOMEM; + goto free_ptes; + } + + name_buf = kcalloc(min(nr_parts, MTD_GPT_MAX_PARTS), (MTD_GPT_PARTNAME_SIZE + 1), + GFP_KERNEL); + if (!name_buf) { + ret = -ENOMEM; + goto free_parts; + } + + for (part_no = 0; part_no < nr_parts && valid_parts < MTD_GPT_MAX_PARTS; part_no++) { + pte = &ptes[part_no]; + part = &parts[valid_parts]; + cur_name = &name_buf[valid_parts * (MTD_GPT_PARTNAME_SIZE + 1)]; + + if (!gpt_is_pte_valid(pte, last_lba)) { + dev_warn(&mtd->dev, "skipping invalid partition entry %d!\n", part_no); + continue; + } + + part->offset = mtd_gpt_lba_to_offset(le64_to_cpu(pte->starting_lba)); + part->size = mtd_gpt_lba_to_size(le64_to_cpu(pte->ending_lba) - + le64_to_cpu(pte->starting_lba) + 1ULL); + part->name = cur_name; + + /* part->name is const so we can't pass it directly */ + utf16_le_to_7bit(pte->partition_name, + MTD_GPT_PARTNAME_SIZE / sizeof(__le16), + cur_name); + valid_parts++; + } + + if (valid_parts == MTD_GPT_MAX_PARTS) + dev_warn(&mtd->dev, + "reached maximum allowed number of MTD partitions %d\n", + MTD_GPT_MAX_PARTS); + + *pparts = parts; + kfree(ptes); + kfree(gpt); + return valid_parts; + +free_parts: + kfree(parts); +free_ptes: + kfree(ptes); +free_gpt: + kfree(agpt); + kfree(gpt); + + return ret; +} + +static void mtd_gpt_cleanup_partitions(const struct mtd_partition *pparts, int nr_parts) +{ + kfree(pparts->name); + kfree(pparts); +} + +static const struct of_device_id mtd_gpt_of_match_table[] = { + { .compatible = "gpt" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtd_gpt_of_match_table); + +static struct mtd_part_parser mtd_gpt_parser = { + .parse_fn = mtd_gpt_parse_partitions, + .cleanup = mtd_gpt_cleanup_partitions, + .name = "GPT", + .of_match_table = mtd_gpt_of_match_table, +}; +module_mtd_part_parser(mtd_gpt_parser); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Romain Gantois "); +MODULE_DESCRIPTION("MTD parser for GPT partition tables");