linux/drivers/mtd/mtdpart.c
<<
>>
Prefs
   1/*
   2 * Simple MTD partitioning layer
   3 *
   4 * (C) 2000 Nicolas Pitre <nico@fluxnic.net>
   5 *
   6 * This code is GPL
   7 *
   8 *      02-21-2002      Thomas Gleixner <gleixner@autronix.de>
   9 *                      added support for read_oob, write_oob
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/types.h>
  14#include <linux/kernel.h>
  15#include <linux/slab.h>
  16#include <linux/list.h>
  17#include <linux/kmod.h>
  18#include <linux/mtd/mtd.h>
  19#include <linux/mtd/partitions.h>
  20#include <linux/mtd/compatmac.h>
  21
  22/* Our partition linked list */
  23static LIST_HEAD(mtd_partitions);
  24
  25/* Our partition node structure */
  26struct mtd_part {
  27        struct mtd_info mtd;
  28        struct mtd_info *master;
  29        uint64_t offset;
  30        struct list_head list;
  31};
  32
  33/*
  34 * Given a pointer to the MTD object in the mtd_part structure, we can retrieve
  35 * the pointer to that structure with this macro.
  36 */
  37#define PART(x)  ((struct mtd_part *)(x))
  38
  39
  40/*
  41 * MTD methods which simply translate the effective address and pass through
  42 * to the _real_ device.
  43 */
  44
  45static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
  46                size_t *retlen, u_char *buf)
  47{
  48        struct mtd_part *part = PART(mtd);
  49        struct mtd_ecc_stats stats;
  50        int res;
  51
  52        stats = part->master->ecc_stats;
  53
  54        if (from >= mtd->size)
  55                len = 0;
  56        else if (from + len > mtd->size)
  57                len = mtd->size - from;
  58        res = part->master->read(part->master, from + part->offset,
  59                                   len, retlen, buf);
  60        if (unlikely(res)) {
  61                if (res == -EUCLEAN)
  62                        mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
  63                if (res == -EBADMSG)
  64                        mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;
  65        }
  66        return res;
  67}
  68
  69static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
  70                size_t *retlen, void **virt, resource_size_t *phys)
  71{
  72        struct mtd_part *part = PART(mtd);
  73        if (from >= mtd->size)
  74                len = 0;
  75        else if (from + len > mtd->size)
  76                len = mtd->size - from;
  77        return part->master->point (part->master, from + part->offset,
  78                                    len, retlen, virt, phys);
  79}
  80
  81static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
  82{
  83        struct mtd_part *part = PART(mtd);
  84
  85        part->master->unpoint(part->master, from + part->offset, len);
  86}
  87
  88static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
  89                                            unsigned long len,
  90                                            unsigned long offset,
  91                                            unsigned long flags)
  92{
  93        struct mtd_part *part = PART(mtd);
  94
  95        offset += part->offset;
  96        return part->master->get_unmapped_area(part->master, len, offset,
  97                                               flags);
  98}
  99
 100static int part_read_oob(struct mtd_info *mtd, loff_t from,
 101                struct mtd_oob_ops *ops)
 102{
 103        struct mtd_part *part = PART(mtd);
 104        int res;
 105
 106        if (from >= mtd->size)
 107                return -EINVAL;
 108        if (ops->datbuf && from + ops->len > mtd->size)
 109                return -EINVAL;
 110        res = part->master->read_oob(part->master, from + part->offset, ops);
 111
 112        if (unlikely(res)) {
 113                if (res == -EUCLEAN)
 114                        mtd->ecc_stats.corrected++;
 115                if (res == -EBADMSG)
 116                        mtd->ecc_stats.failed++;
 117        }
 118        return res;
 119}
 120
 121static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
 122                size_t len, size_t *retlen, u_char *buf)
 123{
 124        struct mtd_part *part = PART(mtd);
 125        return part->master->read_user_prot_reg(part->master, from,
 126                                        len, retlen, buf);
 127}
 128
 129static int part_get_user_prot_info(struct mtd_info *mtd,
 130                struct otp_info *buf, size_t len)
 131{
 132        struct mtd_part *part = PART(mtd);
 133        return part->master->get_user_prot_info(part->master, buf, len);
 134}
 135
 136static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
 137                size_t len, size_t *retlen, u_char *buf)
 138{
 139        struct mtd_part *part = PART(mtd);
 140        return part->master->read_fact_prot_reg(part->master, from,
 141                                        len, retlen, buf);
 142}
 143
 144static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
 145                size_t len)
 146{
 147        struct mtd_part *part = PART(mtd);
 148        return part->master->get_fact_prot_info(part->master, buf, len);
 149}
 150
 151static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
 152                size_t *retlen, const u_char *buf)
 153{
 154        struct mtd_part *part = PART(mtd);
 155        if (!(mtd->flags & MTD_WRITEABLE))
 156                return -EROFS;
 157        if (to >= mtd->size)
 158                len = 0;
 159        else if (to + len > mtd->size)
 160                len = mtd->size - to;
 161        return part->master->write(part->master, to + part->offset,
 162                                    len, retlen, buf);
 163}
 164
 165static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
 166                size_t *retlen, const u_char *buf)
 167{
 168        struct mtd_part *part = PART(mtd);
 169        if (!(mtd->flags & MTD_WRITEABLE))
 170                return -EROFS;
 171        if (to >= mtd->size)
 172                len = 0;
 173        else if (to + len > mtd->size)
 174                len = mtd->size - to;
 175        return part->master->panic_write(part->master, to + part->offset,
 176                                    len, retlen, buf);
 177}
 178
 179static int part_write_oob(struct mtd_info *mtd, loff_t to,
 180                struct mtd_oob_ops *ops)
 181{
 182        struct mtd_part *part = PART(mtd);
 183
 184        if (!(mtd->flags & MTD_WRITEABLE))
 185                return -EROFS;
 186
 187        if (to >= mtd->size)
 188                return -EINVAL;
 189        if (ops->datbuf && to + ops->len > mtd->size)
 190                return -EINVAL;
 191        return part->master->write_oob(part->master, to + part->offset, ops);
 192}
 193
 194static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
 195                size_t len, size_t *retlen, u_char *buf)
 196{
 197        struct mtd_part *part = PART(mtd);
 198        return part->master->write_user_prot_reg(part->master, from,
 199                                        len, retlen, buf);
 200}
 201
 202static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 203                size_t len)
 204{
 205        struct mtd_part *part = PART(mtd);
 206        return part->master->lock_user_prot_reg(part->master, from, len);
 207}
 208
 209static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
 210                unsigned long count, loff_t to, size_t *retlen)
 211{
 212        struct mtd_part *part = PART(mtd);
 213        if (!(mtd->flags & MTD_WRITEABLE))
 214                return -EROFS;
 215        return part->master->writev(part->master, vecs, count,
 216                                        to + part->offset, retlen);
 217}
 218
 219static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
 220{
 221        struct mtd_part *part = PART(mtd);
 222        int ret;
 223        if (!(mtd->flags & MTD_WRITEABLE))
 224                return -EROFS;
 225        if (instr->addr >= mtd->size)
 226                return -EINVAL;
 227        instr->addr += part->offset;
 228        ret = part->master->erase(part->master, instr);
 229        if (ret) {
 230                if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
 231                        instr->fail_addr -= part->offset;
 232                instr->addr -= part->offset;
 233        }
 234        return ret;
 235}
 236
 237void mtd_erase_callback(struct erase_info *instr)
 238{
 239        if (instr->mtd->erase == part_erase) {
 240                struct mtd_part *part = PART(instr->mtd);
 241
 242                if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
 243                        instr->fail_addr -= part->offset;
 244                instr->addr -= part->offset;
 245        }
 246        if (instr->callback)
 247                instr->callback(instr);
 248}
 249EXPORT_SYMBOL_GPL(mtd_erase_callback);
 250
 251static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 252{
 253        struct mtd_part *part = PART(mtd);
 254        if ((len + ofs) > mtd->size)
 255                return -EINVAL;
 256        return part->master->lock(part->master, ofs + part->offset, len);
 257}
 258
 259static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 260{
 261        struct mtd_part *part = PART(mtd);
 262        if ((len + ofs) > mtd->size)
 263                return -EINVAL;
 264        return part->master->unlock(part->master, ofs + part->offset, len);
 265}
 266
 267static void part_sync(struct mtd_info *mtd)
 268{
 269        struct mtd_part *part = PART(mtd);
 270        part->master->sync(part->master);
 271}
 272
 273static int part_suspend(struct mtd_info *mtd)
 274{
 275        struct mtd_part *part = PART(mtd);
 276        return part->master->suspend(part->master);
 277}
 278
 279static void part_resume(struct mtd_info *mtd)
 280{
 281        struct mtd_part *part = PART(mtd);
 282        part->master->resume(part->master);
 283}
 284
 285static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
 286{
 287        struct mtd_part *part = PART(mtd);
 288        if (ofs >= mtd->size)
 289                return -EINVAL;
 290        ofs += part->offset;
 291        return part->master->block_isbad(part->master, ofs);
 292}
 293
 294static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
 295{
 296        struct mtd_part *part = PART(mtd);
 297        int res;
 298
 299        if (!(mtd->flags & MTD_WRITEABLE))
 300                return -EROFS;
 301        if (ofs >= mtd->size)
 302                return -EINVAL;
 303        ofs += part->offset;
 304        res = part->master->block_markbad(part->master, ofs);
 305        if (!res)
 306                mtd->ecc_stats.badblocks++;
 307        return res;
 308}
 309
 310/*
 311 * This function unregisters and destroy all slave MTD objects which are
 312 * attached to the given master MTD object.
 313 */
 314
 315int del_mtd_partitions(struct mtd_info *master)
 316{
 317        struct mtd_part *slave, *next;
 318
 319        list_for_each_entry_safe(slave, next, &mtd_partitions, list)
 320                if (slave->master == master) {
 321                        list_del(&slave->list);
 322                        del_mtd_device(&slave->mtd);
 323                        kfree(slave);
 324                }
 325
 326        return 0;
 327}
 328EXPORT_SYMBOL(del_mtd_partitions);
 329
 330static struct mtd_part *add_one_partition(struct mtd_info *master,
 331                const struct mtd_partition *part, int partno,
 332                uint64_t cur_offset)
 333{
 334        struct mtd_part *slave;
 335
 336        /* allocate the partition structure */
 337        slave = kzalloc(sizeof(*slave), GFP_KERNEL);
 338        if (!slave) {
 339                printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
 340                        master->name);
 341                del_mtd_partitions(master);
 342                return NULL;
 343        }
 344        list_add(&slave->list, &mtd_partitions);
 345
 346        /* set up the MTD object for this partition */
 347        slave->mtd.type = master->type;
 348        slave->mtd.flags = master->flags & ~part->mask_flags;
 349        slave->mtd.size = part->size;
 350        slave->mtd.writesize = master->writesize;
 351        slave->mtd.oobsize = master->oobsize;
 352        slave->mtd.oobavail = master->oobavail;
 353        slave->mtd.subpage_sft = master->subpage_sft;
 354
 355        slave->mtd.name = part->name;
 356        slave->mtd.owner = master->owner;
 357        slave->mtd.backing_dev_info = master->backing_dev_info;
 358
 359        /* NOTE:  we don't arrange MTDs as a tree; it'd be error-prone
 360         * to have the same data be in two different partitions.
 361         */
 362        slave->mtd.dev.parent = master->dev.parent;
 363
 364        slave->mtd.read = part_read;
 365        slave->mtd.write = part_write;
 366
 367        if (master->panic_write)
 368                slave->mtd.panic_write = part_panic_write;
 369
 370        if (master->point && master->unpoint) {
 371                slave->mtd.point = part_point;
 372                slave->mtd.unpoint = part_unpoint;
 373        }
 374
 375        if (master->get_unmapped_area)
 376                slave->mtd.get_unmapped_area = part_get_unmapped_area;
 377        if (master->read_oob)
 378                slave->mtd.read_oob = part_read_oob;
 379        if (master->write_oob)
 380                slave->mtd.write_oob = part_write_oob;
 381        if (master->read_user_prot_reg)
 382                slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
 383        if (master->read_fact_prot_reg)
 384                slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
 385        if (master->write_user_prot_reg)
 386                slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
 387        if (master->lock_user_prot_reg)
 388                slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
 389        if (master->get_user_prot_info)
 390                slave->mtd.get_user_prot_info = part_get_user_prot_info;
 391        if (master->get_fact_prot_info)
 392                slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
 393        if (master->sync)
 394                slave->mtd.sync = part_sync;
 395        if (!partno && !master->dev.class && master->suspend && master->resume) {
 396                        slave->mtd.suspend = part_suspend;
 397                        slave->mtd.resume = part_resume;
 398        }
 399        if (master->writev)
 400                slave->mtd.writev = part_writev;
 401        if (master->lock)
 402                slave->mtd.lock = part_lock;
 403        if (master->unlock)
 404                slave->mtd.unlock = part_unlock;
 405        if (master->block_isbad)
 406                slave->mtd.block_isbad = part_block_isbad;
 407        if (master->block_markbad)
 408                slave->mtd.block_markbad = part_block_markbad;
 409        slave->mtd.erase = part_erase;
 410        slave->master = master;
 411        slave->offset = part->offset;
 412
 413        if (slave->offset == MTDPART_OFS_APPEND)
 414                slave->offset = cur_offset;
 415        if (slave->offset == MTDPART_OFS_NXTBLK) {
 416                slave->offset = cur_offset;
 417                if (mtd_mod_by_eb(cur_offset, master) != 0) {
 418                        /* Round up to next erasesize */
 419                        slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
 420                        printk(KERN_NOTICE "Moving partition %d: "
 421                               "0x%012llx -> 0x%012llx\n", partno,
 422                               (unsigned long long)cur_offset, (unsigned long long)slave->offset);
 423                }
 424        }
 425        if (slave->mtd.size == MTDPART_SIZ_FULL)
 426                slave->mtd.size = master->size - slave->offset;
 427
 428        printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
 429                (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
 430
 431        /* let's do some sanity checks */
 432        if (slave->offset >= master->size) {
 433                /* let's register it anyway to preserve ordering */
 434                slave->offset = 0;
 435                slave->mtd.size = 0;
 436                printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
 437                        part->name);
 438                goto out_register;
 439        }
 440        if (slave->offset + slave->mtd.size > master->size) {
 441                slave->mtd.size = master->size - slave->offset;
 442                printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
 443                        part->name, master->name, (unsigned long long)slave->mtd.size);
 444        }
 445        if (master->numeraseregions > 1) {
 446                /* Deal with variable erase size stuff */
 447                int i, max = master->numeraseregions;
 448                u64 end = slave->offset + slave->mtd.size;
 449                struct mtd_erase_region_info *regions = master->eraseregions;
 450
 451                /* Find the first erase regions which is part of this
 452                 * partition. */
 453                for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
 454                        ;
 455                /* The loop searched for the region _behind_ the first one */
 456                if (i > 0)
 457                        i--;
 458
 459                /* Pick biggest erasesize */
 460                for (; i < max && regions[i].offset < end; i++) {
 461                        if (slave->mtd.erasesize < regions[i].erasesize) {
 462                                slave->mtd.erasesize = regions[i].erasesize;
 463                        }
 464                }
 465                BUG_ON(slave->mtd.erasesize == 0);
 466        } else {
 467                /* Single erase size */
 468                slave->mtd.erasesize = master->erasesize;
 469        }
 470
 471        if ((slave->mtd.flags & MTD_WRITEABLE) &&
 472            mtd_mod_by_eb(slave->offset, &slave->mtd)) {
 473                /* Doesn't start on a boundary of major erase size */
 474                /* FIXME: Let it be writable if it is on a boundary of
 475                 * _minor_ erase size though */
 476                slave->mtd.flags &= ~MTD_WRITEABLE;
 477                printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
 478                        part->name);
 479        }
 480        if ((slave->mtd.flags & MTD_WRITEABLE) &&
 481            mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
 482                slave->mtd.flags &= ~MTD_WRITEABLE;
 483                printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
 484                        part->name);
 485        }
 486
 487        slave->mtd.ecclayout = master->ecclayout;
 488        if (master->block_isbad) {
 489                uint64_t offs = 0;
 490
 491                while (offs < slave->mtd.size) {
 492                        if (master->block_isbad(master,
 493                                                offs + slave->offset))
 494                                slave->mtd.ecc_stats.badblocks++;
 495                        offs += slave->mtd.erasesize;
 496                }
 497        }
 498
 499out_register:
 500        /* register our partition */
 501        add_mtd_device(&slave->mtd);
 502
 503        return slave;
 504}
 505
 506/*
 507 * This function, given a master MTD object and a partition table, creates
 508 * and registers slave MTD objects which are bound to the master according to
 509 * the partition definitions.
 510 *
 511 * We don't register the master, or expect the caller to have done so,
 512 * for reasons of data integrity.
 513 */
 514
 515int add_mtd_partitions(struct mtd_info *master,
 516                       const struct mtd_partition *parts,
 517                       int nbparts)
 518{
 519        struct mtd_part *slave;
 520        uint64_t cur_offset = 0;
 521        int i;
 522
 523        printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
 524
 525        for (i = 0; i < nbparts; i++) {
 526                slave = add_one_partition(master, parts + i, i, cur_offset);
 527                if (!slave)
 528                        return -ENOMEM;
 529                cur_offset = slave->offset + slave->mtd.size;
 530        }
 531
 532        return 0;
 533}
 534EXPORT_SYMBOL(add_mtd_partitions);
 535
 536static DEFINE_SPINLOCK(part_parser_lock);
 537static LIST_HEAD(part_parsers);
 538
 539static struct mtd_part_parser *get_partition_parser(const char *name)
 540{
 541        struct mtd_part_parser *p, *ret = NULL;
 542
 543        spin_lock(&part_parser_lock);
 544
 545        list_for_each_entry(p, &part_parsers, list)
 546                if (!strcmp(p->name, name) && try_module_get(p->owner)) {
 547                        ret = p;
 548                        break;
 549                }
 550
 551        spin_unlock(&part_parser_lock);
 552
 553        return ret;
 554}
 555
 556int register_mtd_parser(struct mtd_part_parser *p)
 557{
 558        spin_lock(&part_parser_lock);
 559        list_add(&p->list, &part_parsers);
 560        spin_unlock(&part_parser_lock);
 561
 562        return 0;
 563}
 564EXPORT_SYMBOL_GPL(register_mtd_parser);
 565
 566int deregister_mtd_parser(struct mtd_part_parser *p)
 567{
 568        spin_lock(&part_parser_lock);
 569        list_del(&p->list);
 570        spin_unlock(&part_parser_lock);
 571        return 0;
 572}
 573EXPORT_SYMBOL_GPL(deregister_mtd_parser);
 574
 575int parse_mtd_partitions(struct mtd_info *master, const char **types,
 576                         struct mtd_partition **pparts, unsigned long origin)
 577{
 578        struct mtd_part_parser *parser;
 579        int ret = 0;
 580
 581        for ( ; ret <= 0 && *types; types++) {
 582                parser = get_partition_parser(*types);
 583                if (!parser && !request_module("%s", *types))
 584                                parser = get_partition_parser(*types);
 585                if (!parser) {
 586                        printk(KERN_NOTICE "%s partition parsing not available\n",
 587                               *types);
 588                        continue;
 589                }
 590                ret = (*parser->parse_fn)(master, pparts, origin);
 591                if (ret > 0) {
 592                        printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
 593                               ret, parser->name, master->name);
 594                }
 595                put_partition_parser(parser);
 596        }
 597        return ret;
 598}
 599EXPORT_SYMBOL_GPL(parse_mtd_partitions);
 600