1 From 39717277d5c87bdb183cf2f258957b44ba99b4df Mon Sep 17 00:00:00 2001
2 From: OpenWrt community <openwrt-devel@lists.openwrt.org>
3 Date: Wed, 13 Jul 2022 11:47:35 +0200
4 Subject: [PATCH] mtd: mtdsplit support
7 drivers/mtd/Kconfig | 19 ++++
8 drivers/mtd/Makefile | 2 +
9 drivers/mtd/mtdpart.c | 169 ++++++++++++++++++++++++++++-----
10 include/linux/mtd/mtd.h | 25 +++++
11 include/linux/mtd/partitions.h | 7 ++
12 5 files changed, 197 insertions(+), 25 deletions(-)
14 diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
15 index 796a2eccbef0..f9ed93c4cf0f 100644
16 --- a/drivers/mtd/Kconfig
17 +++ b/drivers/mtd/Kconfig
18 @@ -12,6 +12,25 @@ menuconfig MTD
22 +menu "OpenWrt specific MTD options"
24 +config MTD_ROOTFS_ROOT_DEV
25 + bool "Automatically set 'rootfs' partition to be root filesystem"
28 +config MTD_SPLIT_FIRMWARE
29 + bool "Automatically split firmware partition for kernel+rootfs"
32 +config MTD_SPLIT_FIRMWARE_NAME
33 + string "Firmware partition name"
34 + depends on MTD_SPLIT_FIRMWARE
37 +source "drivers/mtd/mtdsplit/Kconfig"
42 tristate "MTD tests support (DANGEROUS)"
44 diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
45 index 593d0593a038..b14b7fe0f597 100644
46 --- a/drivers/mtd/Makefile
47 +++ b/drivers/mtd/Makefile
48 @@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o
52 +obj-$(CONFIG_MTD_SPLIT) += mtdsplit/
54 # 'Users' - code which presents functionality to userspace.
55 obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
56 obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
57 diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
58 index d442fa94c872..f1ed12aae1fe 100644
59 --- a/drivers/mtd/mtdpart.c
60 +++ b/drivers/mtd/mtdpart.c
62 #include <linux/kmod.h>
63 #include <linux/mtd/mtd.h>
64 #include <linux/mtd/partitions.h>
65 +#include <linux/magic.h>
66 #include <linux/err.h>
68 #include <linux/of_platform.h>
71 +#include "mtdsplit/mtdsplit.h"
74 * MTD methods which simply translate the effective address and pass through
75 @@ -236,6 +238,146 @@ static int mtd_add_partition_attrs(struct mtd_info *new)
79 +static DEFINE_SPINLOCK(part_parser_lock);
80 +static LIST_HEAD(part_parsers);
82 +static struct mtd_part_parser *mtd_part_parser_get(const char *name)
84 + struct mtd_part_parser *p, *ret = NULL;
86 + spin_lock(&part_parser_lock);
88 + list_for_each_entry(p, &part_parsers, list)
89 + if (!strcmp(p->name, name) && try_module_get(p->owner)) {
94 + spin_unlock(&part_parser_lock);
99 +static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
101 + module_put(p->owner);
104 +static struct mtd_part_parser *
105 +get_partition_parser_by_type(enum mtd_parser_type type,
106 + struct mtd_part_parser *start)
108 + struct mtd_part_parser *p, *ret = NULL;
110 + spin_lock(&part_parser_lock);
112 + p = list_prepare_entry(start, &part_parsers, list);
114 + mtd_part_parser_put(start);
116 + list_for_each_entry_continue(p, &part_parsers, list) {
117 + if (p->type == type && try_module_get(p->owner)) {
123 + spin_unlock(&part_parser_lock);
128 +static int parse_mtd_partitions_by_type(struct mtd_info *master,
129 + enum mtd_parser_type type,
130 + const struct mtd_partition **pparts,
131 + struct mtd_part_parser_data *data)
133 + struct mtd_part_parser *prev = NULL;
137 + struct mtd_part_parser *parser;
139 + parser = get_partition_parser_by_type(type, prev);
143 + ret = (*parser->parse_fn)(master, pparts, data);
146 + mtd_part_parser_put(parser);
148 + "%d %s partitions found on MTD device %s\n",
149 + ret, parser->name, master->name);
160 +run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
162 + struct mtd_partition *parts;
166 + nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
171 + if (WARN_ON(!parts))
174 + for (i = 0; i < nr_parts; i++) {
175 + /* adjust partition offsets */
176 + parts[i].offset += child->part.offset;
178 + mtd_add_partition(child->parent,
189 +#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
190 +#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME
192 +#define SPLIT_FIRMWARE_NAME "unused"
195 +static void split_firmware(struct mtd_info *master, struct mtd_info *part)
197 + run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
200 +static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
202 + static int rootfs_found = 0;
207 + if (!strcmp(part->name, "rootfs")) {
208 + run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
213 + if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
214 + !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
215 + !of_find_property(mtd_get_of_node(part), "compatible", NULL))
216 + split_firmware(master, part);
219 int mtd_add_partition(struct mtd_info *parent, const char *name,
220 long long offset, long long length)
222 @@ -274,6 +416,7 @@ int mtd_add_partition(struct mtd_info *parent, const char *name,
224 goto err_remove_part;
226 + mtd_partition_split(parent, child);
227 mtd_add_partition_attrs(child);
230 @@ -422,6 +565,7 @@ int add_mtd_partitions(struct mtd_info *parent,
231 goto err_del_partitions;
234 + mtd_partition_split(master, child);
235 mtd_add_partition_attrs(child);
237 /* Look for subpartitions */
238 @@ -438,31 +582,6 @@ int add_mtd_partitions(struct mtd_info *parent,
242 -static DEFINE_SPINLOCK(part_parser_lock);
243 -static LIST_HEAD(part_parsers);
245 -static struct mtd_part_parser *mtd_part_parser_get(const char *name)
247 - struct mtd_part_parser *p, *ret = NULL;
249 - spin_lock(&part_parser_lock);
251 - list_for_each_entry(p, &part_parsers, list)
252 - if (!strcmp(p->name, name) && try_module_get(p->owner)) {
257 - spin_unlock(&part_parser_lock);
262 -static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
264 - module_put(p->owner);
268 * Many partition parsers just expected the core to kfree() all their data in
269 * one chunk. Do that by default.
270 diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
271 index 8a2c60235ebb..b092bf6ff97d 100644
272 --- a/include/linux/mtd/mtd.h
273 +++ b/include/linux/mtd/mtd.h
274 @@ -613,6 +613,24 @@ static inline void mtd_align_erase_req(struct mtd_info *mtd,
275 req->len += mtd->erasesize - mod;
278 +static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
280 + if (mtd_mod_by_eb(sz, mtd) == 0)
283 + /* Round up to next erase block */
284 + return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
287 +static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
289 + if (mtd_mod_by_eb(sz, mtd) == 0)
292 + /* Round down to the start of the current erase block */
293 + return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
296 static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
298 if (mtd->writesize_shift)
299 @@ -685,6 +703,13 @@ extern void __put_mtd_device(struct mtd_info *mtd);
300 extern struct mtd_info *get_mtd_device_nm(const char *name);
301 extern void put_mtd_device(struct mtd_info *mtd);
303 +static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
305 + if (!mtd_is_partition(mtd))
308 + return mtd->part.offset;
311 struct mtd_notifier {
312 void (*add)(struct mtd_info *mtd);
313 diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
314 index b74a539ec581..65ba0dbf961d 100644
315 --- a/include/linux/mtd/partitions.h
316 +++ b/include/linux/mtd/partitions.h
317 @@ -75,6 +75,12 @@ struct mtd_part_parser_data {
318 * Functions dealing with the various ways of partitioning the space
321 +enum mtd_parser_type {
322 + MTD_PARSER_TYPE_DEVICE = 0,
323 + MTD_PARSER_TYPE_ROOTFS,
324 + MTD_PARSER_TYPE_FIRMWARE,
327 struct mtd_part_parser {
328 struct list_head list;
329 struct module *owner;
330 @@ -83,6 +89,7 @@ struct mtd_part_parser {
331 int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
332 struct mtd_part_parser_data *);
333 void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
334 + enum mtd_parser_type type;
337 /* Container for passing around a set of parsed partitions */