| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From aec4d5f5ffd0f0092bd9dc21ea90e0bc237d4b74 Mon Sep 17 00:00:00 2001 |
| 2 | From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
| 3 | Date: Sat, 15 Oct 2022 11:29:50 +0200 |
| 4 | Subject: [PATCH] mtd: parsers: add TP-Link SafeLoader partitions table parser |
| 5 | MIME-Version: 1.0 |
| 6 | Content-Type: text/plain; charset=UTF-8 |
| 7 | Content-Transfer-Encoding: 8bit |
| 8 | |
| 9 | This parser deals with most TP-Link home routers. It reads info about |
| 10 | partitions and registers them in the MTD subsystem. |
| 11 | |
| 12 | Example from TP-Link Archer C5 V2: |
| 13 | |
| 14 | spi-nor spi0.0: s25fl128s1 (16384 Kbytes) |
| 15 | 15 tplink-safeloader partitions found on MTD device spi0.0 |
| 16 | Creating 15 MTD partitions on "spi0.0": |
| 17 | 0x000000000000-0x000000040000 : "fs-uboot" |
| 18 | 0x000000040000-0x000000440000 : "os-image" |
| 19 | 0x000000440000-0x000000e40000 : "rootfs" |
| 20 | 0x000000e40000-0x000000e40200 : "default-mac" |
| 21 | 0x000000e40200-0x000000e40400 : "pin" |
| 22 | 0x000000e40400-0x000000e40600 : "product-info" |
| 23 | 0x000000e50000-0x000000e60000 : "partition-table" |
| 24 | 0x000000e60000-0x000000e60200 : "soft-version" |
| 25 | 0x000000e61000-0x000000e70000 : "support-list" |
| 26 | 0x000000e70000-0x000000e80000 : "profile" |
| 27 | 0x000000e80000-0x000000e90000 : "default-config" |
| 28 | 0x000000e90000-0x000000ee0000 : "user-config" |
| 29 | 0x000000ee0000-0x000000fe0000 : "log" |
| 30 | 0x000000fe0000-0x000000ff0000 : "radio_bk" |
| 31 | 0x000000ff0000-0x000001000000 : "radio" |
| 32 | |
| 33 | Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
| 34 | Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> |
| 35 | Link: https://lore.kernel.org/linux-mtd/20221015092950.27467-2-zajec5@gmail.com |
| 36 | --- |
| 37 | drivers/mtd/parsers/Kconfig | 15 +++ |
| 38 | drivers/mtd/parsers/Makefile | 1 + |
| 39 | drivers/mtd/parsers/tplink_safeloader.c | 150 ++++++++++++++++++++++++ |
| 40 | 3 files changed, 166 insertions(+) |
| 41 | create mode 100644 drivers/mtd/parsers/tplink_safeloader.c |
| 42 | |
| 43 | --- a/drivers/mtd/parsers/Kconfig |
| 44 | +++ b/drivers/mtd/parsers/Kconfig |
| 45 | @@ -113,6 +113,21 @@ config MTD_AFS_PARTS |
| 46 | for your particular device. It won't happen automatically. The |
| 47 | 'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example. |
| 48 | |
| 49 | +config MTD_PARSER_TPLINK_SAFELOADER |
| 50 | + tristate "TP-Link Safeloader partitions parser" |
| 51 | + depends on MTD && (ARCH_BCM_5301X || ATH79 || SOC_MT7620 || SOC_MT7621 || COMPILE_TEST) |
| 52 | + help |
| 53 | + TP-Link home routers use flash partitions to store various data. Info |
| 54 | + about flash space layout is stored in a partitions table using a |
| 55 | + custom ASCII-based format. |
| 56 | + |
| 57 | + That format was first found in devices with SafeLoader bootloader and |
| 58 | + was named after it. Later it was adapted to CFE and U-Boot |
| 59 | + bootloaders. |
| 60 | + |
| 61 | + This driver reads partitions table, parses it and creates MTD |
| 62 | + partitions. |
| 63 | + |
| 64 | config MTD_PARSER_TRX |
| 65 | tristate "Parser for TRX format partitions" |
| 66 | depends on MTD && (BCM47XX || ARCH_BCM_5301X || COMPILE_TEST) |
| 67 | --- a/drivers/mtd/parsers/Makefile |
| 68 | +++ b/drivers/mtd/parsers/Makefile |
| 69 | @@ -9,6 +9,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += |
| 70 | ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o |
| 71 | obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o |
| 72 | obj-$(CONFIG_MTD_AFS_PARTS) += afs.o |
| 73 | +obj-$(CONFIG_MTD_PARSER_TPLINK_SAFELOADER) += tplink_safeloader.o |
| 74 | obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o |
| 75 | obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o |
| 76 | obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o |
| 77 | --- /dev/null |
| 78 | +++ b/drivers/mtd/parsers/tplink_safeloader.c |
| 79 | @@ -0,0 +1,150 @@ |
| 80 | +// SPDX-License-Identifier: GPL-2.0-only |
| 81 | +/* |
| 82 | + * Copyright © 2022 Rafał Miłecki <rafal@milecki.pl> |
| 83 | + */ |
| 84 | + |
| 85 | +#include <linux/kernel.h> |
| 86 | +#include <linux/module.h> |
| 87 | +#include <linux/mtd/mtd.h> |
| 88 | +#include <linux/mtd/partitions.h> |
| 89 | +#include <linux/of.h> |
| 90 | +#include <linux/slab.h> |
| 91 | + |
| 92 | +#define TPLINK_SAFELOADER_DATA_OFFSET 4 |
| 93 | +#define TPLINK_SAFELOADER_MAX_PARTS 32 |
| 94 | + |
| 95 | +struct safeloader_cmn_header { |
| 96 | + __be32 size; |
| 97 | + uint32_t unused; |
| 98 | +} __packed; |
| 99 | + |
| 100 | +static void *mtd_parser_tplink_safeloader_read_table(struct mtd_info *mtd) |
| 101 | +{ |
| 102 | + struct safeloader_cmn_header hdr; |
| 103 | + struct device_node *np; |
| 104 | + size_t bytes_read; |
| 105 | + size_t offset; |
| 106 | + size_t size; |
| 107 | + char *buf; |
| 108 | + int err; |
| 109 | + |
| 110 | + np = mtd_get_of_node(mtd); |
| 111 | + if (mtd_is_partition(mtd)) |
| 112 | + of_node_get(np); |
| 113 | + else |
| 114 | + np = of_get_child_by_name(np, "partitions"); |
| 115 | + |
| 116 | + if (of_property_read_u32(np, "partitions-table-offset", (u32 *)&offset)) { |
| 117 | + pr_err("Failed to get partitions table offset\n"); |
| 118 | + goto err_put; |
| 119 | + } |
| 120 | + |
| 121 | + err = mtd_read(mtd, offset, sizeof(hdr), &bytes_read, (uint8_t *)&hdr); |
| 122 | + if (err && !mtd_is_bitflip(err)) { |
| 123 | + pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset); |
| 124 | + goto err_put; |
| 125 | + } |
| 126 | + |
| 127 | + size = be32_to_cpu(hdr.size); |
| 128 | + |
| 129 | + buf = kmalloc(size + 1, GFP_KERNEL); |
| 130 | + if (!buf) |
| 131 | + goto err_put; |
| 132 | + |
| 133 | + err = mtd_read(mtd, offset + sizeof(hdr), size, &bytes_read, buf); |
| 134 | + if (err && !mtd_is_bitflip(err)) { |
| 135 | + pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset + sizeof(hdr)); |
| 136 | + goto err_kfree; |
| 137 | + } |
| 138 | + |
| 139 | + buf[size] = '\0'; |
| 140 | + |
| 141 | + of_node_put(np); |
| 142 | + |
| 143 | + return buf; |
| 144 | + |
| 145 | +err_kfree: |
| 146 | + kfree(buf); |
| 147 | +err_put: |
| 148 | + of_node_put(np); |
| 149 | + return NULL; |
| 150 | +} |
| 151 | + |
| 152 | +static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd, |
| 153 | + const struct mtd_partition **pparts, |
| 154 | + struct mtd_part_parser_data *data) |
| 155 | +{ |
| 156 | + struct mtd_partition *parts; |
| 157 | + char name[65]; |
| 158 | + size_t offset; |
| 159 | + size_t bytes; |
| 160 | + char *buf; |
| 161 | + int idx; |
| 162 | + int err; |
| 163 | + |
| 164 | + parts = kcalloc(TPLINK_SAFELOADER_MAX_PARTS, sizeof(*parts), GFP_KERNEL); |
| 165 | + if (!parts) { |
| 166 | + err = -ENOMEM; |
| 167 | + goto err_out; |
| 168 | + } |
| 169 | + |
| 170 | + buf = mtd_parser_tplink_safeloader_read_table(mtd); |
| 171 | + if (!buf) { |
| 172 | + err = -ENOENT; |
| 173 | + goto err_out; |
| 174 | + } |
| 175 | + |
| 176 | + for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET; |
| 177 | + idx < TPLINK_SAFELOADER_MAX_PARTS && |
| 178 | + sscanf(buf + offset, "partition %64s base 0x%llx size 0x%llx%zn\n", |
| 179 | + name, &parts[idx].offset, &parts[idx].size, &bytes) == 3; |
| 180 | + idx++, offset += bytes + 1) { |
| 181 | + parts[idx].name = kstrdup(name, GFP_KERNEL); |
| 182 | + if (!parts[idx].name) { |
| 183 | + err = -ENOMEM; |
| 184 | + goto err_free; |
| 185 | + } |
| 186 | + } |
| 187 | + |
| 188 | + if (idx == TPLINK_SAFELOADER_MAX_PARTS) |
| 189 | + pr_warn("Reached maximum number of partitions!\n"); |
| 190 | + |
| 191 | + kfree(buf); |
| 192 | + |
| 193 | + *pparts = parts; |
| 194 | + |
| 195 | + return idx; |
| 196 | + |
| 197 | +err_free: |
| 198 | + for (idx -= 1; idx >= 0; idx--) |
| 199 | + kfree(parts[idx].name); |
| 200 | +err_out: |
| 201 | + return err; |
| 202 | +}; |
| 203 | + |
| 204 | +static void mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition *pparts, |
| 205 | + int nr_parts) |
| 206 | +{ |
| 207 | + int i; |
| 208 | + |
| 209 | + for (i = 0; i < nr_parts; i++) |
| 210 | + kfree(pparts[i].name); |
| 211 | + |
| 212 | + kfree(pparts); |
| 213 | +} |
| 214 | + |
| 215 | +static const struct of_device_id mtd_parser_tplink_safeloader_of_match_table[] = { |
| 216 | + { .compatible = "tplink,safeloader-partitions" }, |
| 217 | + {}, |
| 218 | +}; |
| 219 | +MODULE_DEVICE_TABLE(of, mtd_parser_tplink_safeloader_of_match_table); |
| 220 | + |
| 221 | +static struct mtd_part_parser mtd_parser_tplink_safeloader = { |
| 222 | + .parse_fn = mtd_parser_tplink_safeloader_parse, |
| 223 | + .cleanup = mtd_parser_tplink_safeloader_cleanup, |
| 224 | + .name = "tplink-safeloader", |
| 225 | + .of_match_table = mtd_parser_tplink_safeloader_of_match_table, |
| 226 | +}; |
| 227 | +module_mtd_part_parser(mtd_parser_tplink_safeloader); |
| 228 | + |
| 229 | +MODULE_LICENSE("GPL"); |