--- zzzz-none-000/linux-4.4.60/drivers/mtd/mtdpart.c 2017-04-08 07:53:53.000000000 +0000 +++ wasp-540e-714/linux-4.4.60/drivers/mtd/mtdpart.c 2019-07-03 09:21:34.000000000 +0000 @@ -29,10 +29,15 @@ #include #include #include +#include +#include #include #include #include "mtdcore.h" +#include "mtdsplit/mtdsplit.h" + +#define MTD_ERASE_PARTIAL 0x8000 /* partition only covers parts of an erase block */ /* Our partition linked list */ static LIST_HEAD(mtd_partitions); @@ -46,6 +51,8 @@ struct list_head list; }; +static void mtd_partition_split(struct mtd_info *master, struct mtd_part *part); + /* * Given a pointer to the MTD object in the mtd_part structure, we can retrieve * the pointer to that structure with this macro. @@ -231,13 +238,61 @@ struct mtd_part *part = PART(mtd); int ret; + + instr->partial_start = false; + if (mtd->flags & MTD_ERASE_PARTIAL) { + size_t readlen = 0; + u64 mtd_ofs; + + instr->erase_buf = kmalloc(part->master->erasesize, GFP_ATOMIC); + if (!instr->erase_buf) + return -ENOMEM; + + mtd_ofs = part->offset + instr->addr; + instr->erase_buf_ofs = do_div(mtd_ofs, part->master->erasesize); + + if (instr->erase_buf_ofs > 0) { + instr->addr -= instr->erase_buf_ofs; + ret = mtd_read(part->master, + instr->addr + part->offset, + part->master->erasesize, + &readlen, instr->erase_buf); + + instr->len += instr->erase_buf_ofs; + instr->partial_start = true; + } else { + mtd_ofs = part->offset + part->mtd.size; + instr->erase_buf_ofs = part->master->erasesize - + do_div(mtd_ofs, part->master->erasesize); + + if (instr->erase_buf_ofs > 0) { + instr->len += instr->erase_buf_ofs; + ret = mtd_read(part->master, + part->offset + instr->addr + + instr->len - part->master->erasesize, + part->master->erasesize, &readlen, + instr->erase_buf); + } else { + ret = 0; + } + } + if (ret < 0) { + kfree(instr->erase_buf); + return ret; + } + + } + instr->addr += part->offset; ret = part->master->_erase(part->master, instr); if (ret) { if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) instr->fail_addr -= part->offset; instr->addr -= part->offset; + if (mtd->flags & MTD_ERASE_PARTIAL) + kfree(instr->erase_buf); } + return ret; } @@ -245,7 +300,25 @@ { if (instr->mtd->_erase == part_erase) { struct mtd_part *part = PART(instr->mtd); + size_t wrlen = 0; + if (instr->mtd->flags & MTD_ERASE_PARTIAL) { + if (instr->partial_start) { + part->master->_write(part->master, + instr->addr, instr->erase_buf_ofs, + &wrlen, instr->erase_buf); + instr->addr += instr->erase_buf_ofs; + } else { + instr->len -= instr->erase_buf_ofs; + part->master->_write(part->master, + instr->addr + instr->len, + instr->erase_buf_ofs, &wrlen, + instr->erase_buf + + part->master->erasesize - + instr->erase_buf_ofs); + } + kfree(instr->erase_buf); + } if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) instr->fail_addr -= part->offset; instr->addr -= part->offset; @@ -264,7 +337,14 @@ static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_part *part = PART(mtd); - return part->master->_unlock(part->master, ofs + part->offset, len); + + ofs += part->offset; + if (mtd->flags & MTD_ERASE_PARTIAL) { + /* round up len to next erasesize and round down offset to prev block */ + len = (mtd_div_by_eb(len, part->master) + 1) * part->master->erasesize; + ofs &= ~(part->master->erasesize - 1); + } + return part->master->_unlock(part->master, ofs, len); } static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -449,14 +529,12 @@ if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; if (slave->offset == MTDPART_OFS_NXTBLK) { - slave->offset = cur_offset; - if (mtd_mod_by_eb(cur_offset, master) != 0) { - /* Round up to next erasesize */ - slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize; + /* Round up to next erasesize */ + slave->offset = mtd_roundup_to_eb(cur_offset, master); + if (slave->offset != cur_offset) printk(KERN_NOTICE "Moving partition %d: " "0x%012llx -> 0x%012llx\n", partno, (unsigned long long)cur_offset, (unsigned long long)slave->offset); - } } if (slave->offset == MTDPART_OFS_RETAIN) { slave->offset = cur_offset; @@ -520,17 +598,20 @@ if ((slave->mtd.flags & MTD_WRITEABLE) && mtd_mod_by_eb(slave->offset, &slave->mtd)) { /* Doesn't start on a boundary of major erase size */ - /* FIXME: Let it be writable if it is on a boundary of - * _minor_ erase size though */ - slave->mtd.flags &= ~MTD_WRITEABLE; - printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", - part->name); + slave->mtd.flags |= MTD_ERASE_PARTIAL; + if (((u32) slave->mtd.size) > master->erasesize) + slave->mtd.flags &= ~MTD_WRITEABLE; + else + slave->mtd.erasesize = slave->mtd.size; } if ((slave->mtd.flags & MTD_WRITEABLE) && - mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { - slave->mtd.flags &= ~MTD_WRITEABLE; - printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", - part->name); + mtd_mod_by_eb(slave->offset + slave->mtd.size, &slave->mtd)) { + slave->mtd.flags |= MTD_ERASE_PARTIAL; + + if ((u32) slave->mtd.size > master->erasesize) + slave->mtd.flags &= ~MTD_WRITEABLE; + else + slave->mtd.erasesize = slave->mtd.size; } slave->mtd.ecclayout = master->ecclayout; @@ -611,6 +692,7 @@ mutex_unlock(&mtd_partitions_mutex); add_mtd_device(&new->mtd); + mtd_partition_split(master, new); mtd_add_partition_attrs(new); @@ -643,6 +725,83 @@ } EXPORT_SYMBOL_GPL(mtd_del_partition); +static int +run_parsers_by_type(struct mtd_part *slave, enum mtd_parser_type type) +{ + struct mtd_partition *parts; + int nr_parts; + int i; + + nr_parts = parse_mtd_partitions_by_type(&slave->mtd, type, &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 += slave->offset; + + mtd_add_partition(slave->master, + parts[i].name, + parts[i].offset, + parts[i].size); + } + + kfree(parts); + + return nr_parts; +} + +static inline unsigned long +mtd_pad_erasesize(struct mtd_info *mtd, int offset, int len) +{ + unsigned long mask = mtd->erasesize - 1; + + len += offset & mask; + len = (len + mask) & ~mask; + len -= offset & mask; + return len; +} + +#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_part *part) +{ + run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE); +} + +void __weak arch_split_mtd_part(struct mtd_info *master, const char *name, + int offset, int size) +{ +} + +static void mtd_partition_split(struct mtd_info *master, struct mtd_part *part) +{ + static int rootfs_found = 0; + + if (rootfs_found) + return; + + if (!strcmp(part->mtd.name, "rootfs")) { + run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS); + + rootfs_found = 1; + } + + if (!strcmp(part->mtd.name, SPLIT_FIRMWARE_NAME) && + config_enabled(CONFIG_MTD_SPLIT_FIRMWARE)) + split_firmware(master, part); + + arch_split_mtd_part(master, part->mtd.name, part->offset, + part->mtd.size); +} /* * This function, given a master MTD object and a partition table, creates * and registers slave MTD objects which are bound to the master according to @@ -656,30 +815,30 @@ const struct mtd_partition *parts, int nbparts) { - struct mtd_part *slave; - uint64_t cur_offset = 0; - int i; - - printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); - - for (i = 0; i < nbparts; i++) { - slave = allocate_partition(master, parts + i, i, cur_offset); - if (IS_ERR(slave)) { - del_mtd_partitions(master); - return PTR_ERR(slave); - } + struct mtd_part *slave; + uint64_t cur_offset = 0; + int i; + + printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + + for (i = 0; i < nbparts; i++) { + slave = allocate_partition(master, parts + i, i, cur_offset); + if (IS_ERR(slave)) { + del_mtd_partitions(master); + return PTR_ERR(slave); + } + + mutex_lock(&mtd_partitions_mutex); + list_add(&slave->list, &mtd_partitions); + mutex_unlock(&mtd_partitions_mutex); - mutex_lock(&mtd_partitions_mutex); - list_add(&slave->list, &mtd_partitions); - mutex_unlock(&mtd_partitions_mutex); + add_mtd_device(&slave->mtd); + mtd_partition_split(master, slave); - add_mtd_device(&slave->mtd); - mtd_add_partition_attrs(slave); + cur_offset = slave->offset + slave->mtd.size; + } - cur_offset = slave->offset + slave->mtd.size; - } - - return 0; + return 0; } static DEFINE_SPINLOCK(part_parser_lock); @@ -704,6 +863,30 @@ #define put_partition_parser(p) do { module_put((p)->owner); } while (0) +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) + put_partition_parser(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; +} + void register_mtd_parser(struct mtd_part_parser *p) { spin_lock(&part_parser_lock); @@ -721,10 +904,47 @@ EXPORT_SYMBOL_GPL(deregister_mtd_parser); /* + * Parses the linux,part-probe device tree property. + * When a non null value is returned it has to be freed with kfree() by + * the caller. + */ +static const char * const *of_get_probes(struct device_node *dp) +{ + const char *cp; + int cplen; + unsigned int l; + unsigned int count; + const char **res; + + cp = of_get_property(dp, "linux,part-probe", &cplen); + if (cp == NULL) + return NULL; + + count = 0; + for (l = 0; l != cplen; l++) + if (cp[l] == 0) + count++; + + res = kzalloc((count + 1) * sizeof(*res), GFP_KERNEL); + if (!res) + return NULL; + count = 0; + while (cplen > 0) { + res[count] = cp; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + count++; + } + return res; +} + +/* * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you * are changing this array! */ static const char * const default_mtd_part_types[] = { + "avmpart_of_nor_spi", "cmdlinepart", "ofpart", NULL @@ -756,6 +976,13 @@ { struct mtd_part_parser *parser; int ret, err = 0; + const char *const *types_of = NULL; + + if (data && data->of_node) { + types_of = of_get_probes(data->of_node); + if (types_of != NULL) + types = types_of; + } if (!types) types = default_mtd_part_types; @@ -785,9 +1012,42 @@ if (ret < 0 && !err) err = ret; } + kfree(types_of); return err; } +int parse_mtd_partitions_by_type(struct mtd_info *master, + enum mtd_parser_type type, + 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) { + put_partition_parser(parser); + printk(KERN_NOTICE + "%d %s partitions found on MTD device %s\n", + ret, parser->name, master->name); + break; + } + + prev = parser; + } + + return ret; +} +EXPORT_SYMBOL_GPL(parse_mtd_partitions_by_type); + int mtd_is_partition(const struct mtd_info *mtd) { struct mtd_part *part; @@ -805,6 +1065,24 @@ } EXPORT_SYMBOL_GPL(mtd_is_partition); +struct mtd_info *mtdpart_get_master(const struct mtd_info *mtd) +{ + if (!mtd_is_partition(mtd)) + return (struct mtd_info *)mtd; + + return PART(mtd)->master; +} +EXPORT_SYMBOL_GPL(mtdpart_get_master); + +uint64_t mtdpart_get_offset(const struct mtd_info *mtd) +{ + if (!mtd_is_partition(mtd)) + return 0; + + return PART(mtd)->offset; +} +EXPORT_SYMBOL_GPL(mtdpart_get_offset); + /* Returns the size of the entire flash chip */ uint64_t mtd_get_device_size(const struct mtd_info *mtd) {