314 lines
7.3 KiB
Diff
314 lines
7.3 KiB
Diff
|
--- a/drivers/mtd/Kconfig
|
||
|
+++ b/drivers/mtd/Kconfig
|
||
|
@@ -12,6 +12,25 @@ menuconfig MTD
|
||
|
|
||
|
if MTD
|
||
|
|
||
|
+menu "OpenWrt specific MTD options"
|
||
|
+
|
||
|
+config MTD_ROOTFS_ROOT_DEV
|
||
|
+ bool "Automatically set 'rootfs' partition to be root filesystem"
|
||
|
+ default y
|
||
|
+
|
||
|
+config MTD_SPLIT_FIRMWARE
|
||
|
+ bool "Automatically split firmware partition for kernel+rootfs"
|
||
|
+ default y
|
||
|
+
|
||
|
+config MTD_SPLIT_FIRMWARE_NAME
|
||
|
+ string "Firmware partition name"
|
||
|
+ depends on MTD_SPLIT_FIRMWARE
|
||
|
+ default "firmware"
|
||
|
+
|
||
|
+source "drivers/mtd/mtdsplit/Kconfig"
|
||
|
+
|
||
|
+endmenu
|
||
|
+
|
||
|
config MTD_TESTS
|
||
|
tristate "MTD tests support (DANGEROUS)"
|
||
|
depends on m
|
||
|
--- a/drivers/mtd/mtdpart.c
|
||
|
+++ b/drivers/mtd/mtdpart.c
|
||
|
@@ -15,10 +15,12 @@
|
||
|
#include <linux/kmod.h>
|
||
|
#include <linux/mtd/mtd.h>
|
||
|
#include <linux/mtd/partitions.h>
|
||
|
+#include <linux/magic.h>
|
||
|
#include <linux/err.h>
|
||
|
#include <linux/of.h>
|
||
|
|
||
|
#include "mtdcore.h"
|
||
|
+#include "mtdsplit/mtdsplit.h"
|
||
|
|
||
|
/*
|
||
|
* MTD methods which simply translate the effective address and pass through
|
||
|
@@ -236,6 +238,146 @@ static int mtd_add_partition_attrs(struc
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
+static DEFINE_SPINLOCK(part_parser_lock);
|
||
|
+static LIST_HEAD(part_parsers);
|
||
|
+
|
||
|
+static struct mtd_part_parser *mtd_part_parser_get(const char *name)
|
||
|
+{
|
||
|
+ struct mtd_part_parser *p, *ret = NULL;
|
||
|
+
|
||
|
+ spin_lock(&part_parser_lock);
|
||
|
+
|
||
|
+ list_for_each_entry(p, &part_parsers, list)
|
||
|
+ if (!strcmp(p->name, name) && try_module_get(p->owner)) {
|
||
|
+ ret = p;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ spin_unlock(&part_parser_lock);
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
|
||
|
+{
|
||
|
+ module_put(p->owner);
|
||
|
+}
|
||
|
+
|
||
|
+static struct mtd_part_parser *
|
||
|
+get_partition_parser_by_type(enum mtd_parser_type type,
|
||
|
+ struct mtd_part_parser *start)
|
||
|
+{
|
||
|
+ struct mtd_part_parser *p, *ret = NULL;
|
||
|
+
|
||
|
+ spin_lock(&part_parser_lock);
|
||
|
+
|
||
|
+ p = list_prepare_entry(start, &part_parsers, list);
|
||
|
+ if (start)
|
||
|
+ mtd_part_parser_put(start);
|
||
|
+
|
||
|
+ list_for_each_entry_continue(p, &part_parsers, list) {
|
||
|
+ if (p->type == type && try_module_get(p->owner)) {
|
||
|
+ ret = p;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ spin_unlock(&part_parser_lock);
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static int parse_mtd_partitions_by_type(struct mtd_info *master,
|
||
|
+ enum mtd_parser_type type,
|
||
|
+ const struct mtd_partition **pparts,
|
||
|
+ struct mtd_part_parser_data *data)
|
||
|
+{
|
||
|
+ struct mtd_part_parser *prev = NULL;
|
||
|
+ int ret = 0;
|
||
|
+
|
||
|
+ while (1) {
|
||
|
+ struct mtd_part_parser *parser;
|
||
|
+
|
||
|
+ parser = get_partition_parser_by_type(type, prev);
|
||
|
+ if (!parser)
|
||
|
+ break;
|
||
|
+
|
||
|
+ ret = (*parser->parse_fn)(master, pparts, data);
|
||
|
+
|
||
|
+ if (ret > 0) {
|
||
|
+ mtd_part_parser_put(parser);
|
||
|
+ printk(KERN_NOTICE
|
||
|
+ "%d %s partitions found on MTD device %s\n",
|
||
|
+ ret, parser->name, master->name);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ prev = parser;
|
||
|
+ }
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static int
|
||
|
+run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
|
||
|
+{
|
||
|
+ struct mtd_partition *parts;
|
||
|
+ int nr_parts;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
|
||
|
+ NULL);
|
||
|
+ if (nr_parts <= 0)
|
||
|
+ return nr_parts;
|
||
|
+
|
||
|
+ if (WARN_ON(!parts))
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ for (i = 0; i < nr_parts; i++) {
|
||
|
+ /* adjust partition offsets */
|
||
|
+ parts[i].offset += child->part.offset;
|
||
|
+
|
||
|
+ mtd_add_partition(child->parent,
|
||
|
+ parts[i].name,
|
||
|
+ parts[i].offset,
|
||
|
+ parts[i].size);
|
||
|
+ }
|
||
|
+
|
||
|
+ kfree(parts);
|
||
|
+
|
||
|
+ return nr_parts;
|
||
|
+}
|
||
|
+
|
||
|
+#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
|
||
|
+#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME
|
||
|
+#else
|
||
|
+#define SPLIT_FIRMWARE_NAME "unused"
|
||
|
+#endif
|
||
|
+
|
||
|
+static void split_firmware(struct mtd_info *master, struct mtd_info *part)
|
||
|
+{
|
||
|
+ run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
|
||
|
+}
|
||
|
+
|
||
|
+static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
|
||
|
+{
|
||
|
+ static int rootfs_found = 0;
|
||
|
+
|
||
|
+ if (rootfs_found)
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (!strcmp(part->name, "rootfs")) {
|
||
|
+ run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
|
||
|
+
|
||
|
+ rootfs_found = 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
|
||
|
+ !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
|
||
|
+ !of_find_property(mtd_get_of_node(part), "compatible", NULL))
|
||
|
+ split_firmware(master, part);
|
||
|
+}
|
||
|
+
|
||
|
int mtd_add_partition(struct mtd_info *parent, const char *name,
|
||
|
long long offset, long long length)
|
||
|
{
|
||
|
@@ -274,6 +416,7 @@ int mtd_add_partition(struct mtd_info *p
|
||
|
if (ret)
|
||
|
goto err_remove_part;
|
||
|
|
||
|
+ mtd_partition_split(parent, child);
|
||
|
mtd_add_partition_attrs(child);
|
||
|
|
||
|
return 0;
|
||
|
@@ -422,6 +565,7 @@ int add_mtd_partitions(struct mtd_info *
|
||
|
goto err_del_partitions;
|
||
|
}
|
||
|
|
||
|
+ mtd_partition_split(master, child);
|
||
|
mtd_add_partition_attrs(child);
|
||
|
|
||
|
/* Look for subpartitions */
|
||
|
@@ -438,31 +582,6 @@ err_del_partitions:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
-static DEFINE_SPINLOCK(part_parser_lock);
|
||
|
-static LIST_HEAD(part_parsers);
|
||
|
-
|
||
|
-static struct mtd_part_parser *mtd_part_parser_get(const char *name)
|
||
|
-{
|
||
|
- struct mtd_part_parser *p, *ret = NULL;
|
||
|
-
|
||
|
- spin_lock(&part_parser_lock);
|
||
|
-
|
||
|
- list_for_each_entry(p, &part_parsers, list)
|
||
|
- if (!strcmp(p->name, name) && try_module_get(p->owner)) {
|
||
|
- ret = p;
|
||
|
- break;
|
||
|
- }
|
||
|
-
|
||
|
- spin_unlock(&part_parser_lock);
|
||
|
-
|
||
|
- return ret;
|
||
|
-}
|
||
|
-
|
||
|
-static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
|
||
|
-{
|
||
|
- module_put(p->owner);
|
||
|
-}
|
||
|
-
|
||
|
/*
|
||
|
* Many partition parsers just expected the core to kfree() all their data in
|
||
|
* one chunk. Do that by default.
|
||
|
--- a/include/linux/mtd/partitions.h
|
||
|
+++ b/include/linux/mtd/partitions.h
|
||
|
@@ -75,6 +75,12 @@ struct mtd_part_parser_data {
|
||
|
* Functions dealing with the various ways of partitioning the space
|
||
|
*/
|
||
|
|
||
|
+enum mtd_parser_type {
|
||
|
+ MTD_PARSER_TYPE_DEVICE = 0,
|
||
|
+ MTD_PARSER_TYPE_ROOTFS,
|
||
|
+ MTD_PARSER_TYPE_FIRMWARE,
|
||
|
+};
|
||
|
+
|
||
|
struct mtd_part_parser {
|
||
|
struct list_head list;
|
||
|
struct module *owner;
|
||
|
@@ -83,6 +89,7 @@ struct mtd_part_parser {
|
||
|
int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
|
||
|
struct mtd_part_parser_data *);
|
||
|
void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
|
||
|
+ enum mtd_parser_type type;
|
||
|
};
|
||
|
|
||
|
/* Container for passing around a set of parsed partitions */
|
||
|
--- a/drivers/mtd/Makefile
|
||
|
+++ b/drivers/mtd/Makefile
|
||
|
@@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconc
|
||
|
|
||
|
obj-y += parsers/
|
||
|
|
||
|
+obj-$(CONFIG_MTD_SPLIT) += mtdsplit/
|
||
|
+
|
||
|
# 'Users' - code which presents functionality to userspace.
|
||
|
obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
|
||
|
obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
|
||
|
--- a/include/linux/mtd/mtd.h
|
||
|
+++ b/include/linux/mtd/mtd.h
|
||
|
@@ -608,6 +608,24 @@ static inline void mtd_align_erase_req(s
|
||
|
req->len += mtd->erasesize - mod;
|
||
|
}
|
||
|
|
||
|
+static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
|
||
|
+{
|
||
|
+ if (mtd_mod_by_eb(sz, mtd) == 0)
|
||
|
+ return sz;
|
||
|
+
|
||
|
+ /* Round up to next erase block */
|
||
|
+ return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
|
||
|
+}
|
||
|
+
|
||
|
+static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
|
||
|
+{
|
||
|
+ if (mtd_mod_by_eb(sz, mtd) == 0)
|
||
|
+ return sz;
|
||
|
+
|
||
|
+ /* Round down to the start of the current erase block */
|
||
|
+ return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
|
||
|
+}
|
||
|
+
|
||
|
static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
|
||
|
{
|
||
|
if (mtd->writesize_shift)
|
||
|
@@ -680,6 +698,13 @@ extern void __put_mtd_device(struct mtd_
|
||
|
extern struct mtd_info *get_mtd_device_nm(const char *name);
|
||
|
extern void put_mtd_device(struct mtd_info *mtd);
|
||
|
|
||
|
+static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
|
||
|
+{
|
||
|
+ if (!mtd_is_partition(mtd))
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ return mtd->part.offset;
|
||
|
+}
|
||
|
|
||
|
struct mtd_notifier {
|
||
|
void (*add)(struct mtd_info *mtd);
|