9f543365a572fa68578e4ea03cd52d40879d55fa
[openwrt/openwrt.git] /
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 || ARCH_MEDIATEK || RALINK || 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_SERCOMM_PARTS) += scpart.o
76 obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.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");