--- zzzz-none-000/linux-3.10.107/drivers/mtd/mtdpart.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/mtd/mtdpart.c 2021-02-04 17:41:59.000000000 +0000 @@ -29,9 +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); @@ -45,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. @@ -150,11 +158,12 @@ retlen, buf); } -static int part_get_user_prot_info(struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int part_get_user_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) { struct mtd_part *part = PART(mtd); - return part->master->_get_user_prot_info(part->master, buf, len); + return part->master->_get_user_prot_info(part->master, len, retlen, + buf); } static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, @@ -165,11 +174,12 @@ retlen, buf); } -static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, - size_t len) +static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) { struct mtd_part *part = PART(mtd); - return part->master->_get_fact_prot_info(part->master, buf, len); + return part->master->_get_fact_prot_info(part->master, len, retlen, + buf); } static int part_write(struct mtd_info *mtd, loff_t to, size_t len, @@ -228,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; } @@ -242,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; @@ -261,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) @@ -288,6 +371,13 @@ part->master->_resume(part->master); } +static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_part *part = PART(mtd); + ofs += part->offset; + return part->master->_block_isreserved(part->master, ofs); +} + static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_part *part = PART(mtd); @@ -369,12 +459,18 @@ slave->mtd.name = name; slave->mtd.owner = master->owner; - slave->mtd.backing_dev_info = master->backing_dev_info; - /* NOTE: we don't arrange MTDs as a tree; it'd be error-prone - * to have the same data be in two different partitions. + /* NOTE: Historically, we didn't arrange MTDs as a tree out of + * concern for showing the same data in multiple partitions. + * However, it is very useful to have the master node present, + * so the MTD_PARTITIONED_MASTER option allows that. The master + * will have device nodes etc only if this is set, so make the + * parent conditional on that option. Note, this is a way to + * distinguish between the master and the partition in sysfs. */ - slave->mtd.dev.parent = master->dev.parent; + slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ? + &master->dev : + master->dev.parent; slave->mtd._read = part_read; slave->mtd._write = part_write; @@ -420,6 +516,8 @@ slave->mtd._unlock = part_unlock; if (master->_is_locked) slave->mtd._is_locked = part_is_locked; + if (master->_block_isreserved) + slave->mtd._block_isreserved = part_block_isreserved; if (master->_block_isbad) slave->mtd._block_isbad = part_block_isbad; if (master->_block_markbad) @@ -431,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; @@ -502,20 +598,24 @@ 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; + slave->mtd.ecc_step_size = master->ecc_step_size; slave->mtd.ecc_strength = master->ecc_strength; slave->mtd.bitflip_threshold = master->bitflip_threshold; @@ -523,7 +623,9 @@ uint64_t offs = 0; while (offs < slave->mtd.size) { - if (mtd_block_isbad(master, offs + slave->offset)) + if (mtd_block_isreserved(master, offs + slave->offset)) + slave->mtd.ecc_stats.bbtblocks++; + else if (mtd_block_isbad(master, offs + slave->offset)) slave->mtd.ecc_stats.badblocks++; offs += slave->mtd.erasesize; } @@ -533,12 +635,35 @@ return slave; } -int mtd_add_partition(struct mtd_info *master, char *name, +static ssize_t mtd_partition_offset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + struct mtd_part *part = PART(mtd); + return snprintf(buf, PAGE_SIZE, "%lld\n", part->offset); +} + +static DEVICE_ATTR(offset, S_IRUGO, mtd_partition_offset_show, NULL); + +static const struct attribute *mtd_partition_attrs[] = { + &dev_attr_offset.attr, + NULL +}; + +static int mtd_add_partition_attrs(struct mtd_part *new) +{ + int ret = sysfs_create_files(&new->mtd.dev.kobj, mtd_partition_attrs); + if (ret) + printk(KERN_WARNING + "mtd: failed to create partition attrs, err=%d\n", ret); + return ret; +} + +int mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length) { struct mtd_partition part; - struct mtd_part *p, *new; - uint64_t start, end; + struct mtd_part *new; int ret = 0; /* the direct offset is expected */ @@ -562,31 +687,16 @@ if (IS_ERR(new)) return PTR_ERR(new); - start = offset; - end = offset + length; - mutex_lock(&mtd_partitions_mutex); - list_for_each_entry(p, &mtd_partitions, list) - if (p->master == master) { - if ((start >= p->offset) && - (start < (p->offset + p->mtd.size))) - goto err_inv; - - if ((end >= p->offset) && - (end < (p->offset + p->mtd.size))) - goto err_inv; - } - list_add(&new->list, &mtd_partitions); mutex_unlock(&mtd_partitions_mutex); add_mtd_device(&new->mtd); + mtd_partition_split(master, new); + + mtd_add_partition_attrs(new); return ret; -err_inv: - mutex_unlock(&mtd_partitions_mutex); - free_partition(new); - return -EINVAL; } EXPORT_SYMBOL_GPL(mtd_add_partition); @@ -599,6 +709,8 @@ list_for_each_entry_safe(slave, next, &mtd_partitions, list) if ((slave->master == master) && (slave->mtd.index == partno)) { + sysfs_remove_files(&slave->mtd.dev.kobj, + mtd_partition_attrs); ret = del_mtd_device(&slave->mtd); if (ret < 0) break; @@ -613,42 +725,120 @@ } 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 * the partition definitions. * - * We don't register the master, or expect the caller to have done so, - * for reasons of data integrity. + * For historical reasons, this function's caller only registers the master + * if the MTD_PARTITIONED_MASTER config option is set. */ int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts, int nbparts) { - struct mtd_part *slave; - uint64_t cur_offset = 0; - int i; + 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); - 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); - } + add_mtd_device(&slave->mtd); + mtd_partition_split(master, slave); - mutex_lock(&mtd_partitions_mutex); - list_add(&slave->list, &mtd_partitions); - mutex_unlock(&mtd_partitions_mutex); + cur_offset = slave->offset + slave->mtd.size; + } - add_mtd_device(&slave->mtd); - - cur_offset = slave->offset + slave->mtd.size; - } - - return 0; + return 0; } static DEFINE_SPINLOCK(part_parser_lock); @@ -673,30 +863,88 @@ #define put_partition_parser(p) do { module_put((p)->owner); } while (0) -int register_mtd_parser(struct mtd_part_parser *p) +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); - list_add(&p->list, &part_parsers); + + 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 0; + return ret; +} + +void register_mtd_parser(struct mtd_part_parser *p) +{ + spin_lock(&part_parser_lock); + list_add(&p->list, &part_parsers); + spin_unlock(&part_parser_lock); } EXPORT_SYMBOL_GPL(register_mtd_parser); -int deregister_mtd_parser(struct mtd_part_parser *p) +void deregister_mtd_parser(struct mtd_part_parser *p) { spin_lock(&part_parser_lock); list_del(&p->list); spin_unlock(&part_parser_lock); - return 0; } 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 @@ -727,27 +975,78 @@ struct mtd_part_parser_data *data) { struct mtd_part_parser *parser; - int ret = 0; + 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; - for ( ; ret <= 0 && *types; types++) { + for ( ; *types; types++) { + pr_debug("%s: parsing partitions %s\n", master->name, *types); parser = get_partition_parser(*types); if (!parser && !request_module("%s", *types)) parser = get_partition_parser(*types); + pr_debug("%s: got parser %s\n", master->name, + parser ? parser->name : NULL); if (!parser) continue; ret = (*parser->parse_fn)(master, pparts, data); + pr_debug("%s: parser %s: %i\n", + master->name, parser->name, ret); put_partition_parser(parser); if (ret > 0) { printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", ret, parser->name, master->name); + return ret; + } + /* + * Stash the first error we see; only report it if no parser + * succeeds + */ + 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) { @@ -766,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) {