linux/drivers/mtd/redboot.c
<<
>>
Prefs
   1/*
   2 * Parse RedBoot-style Flash Image System (FIS) tables and
   3 * produce a Linux partition array to match.
   4 */
   5
   6#include <linux/kernel.h>
   7#include <linux/slab.h>
   8#include <linux/init.h>
   9#include <linux/vmalloc.h>
  10
  11#include <linux/mtd/mtd.h>
  12#include <linux/mtd/partitions.h>
  13
  14struct fis_image_desc {
  15    unsigned char name[16];      // Null terminated name
  16    uint32_t      flash_base;    // Address within FLASH of image
  17    uint32_t      mem_base;      // Address in memory where it executes
  18    uint32_t      size;          // Length of image
  19    uint32_t      entry_point;   // Execution entry point
  20    uint32_t      data_length;   // Length of actual data
  21    unsigned char _pad[256-(16+7*sizeof(uint32_t))];
  22    uint32_t      desc_cksum;    // Checksum over image descriptor
  23    uint32_t      file_cksum;    // Checksum over image data
  24};
  25
  26struct fis_list {
  27        struct fis_image_desc *img;
  28        struct fis_list *next;
  29};
  30
  31static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK;
  32module_param(directory, int, 0);
  33
  34static inline int redboot_checksum(struct fis_image_desc *img)
  35{
  36        /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */
  37        return 1;
  38}
  39
  40static int parse_redboot_partitions(struct mtd_info *master,
  41                             struct mtd_partition **pparts,
  42                             unsigned long fis_origin)
  43{
  44        int nrparts = 0;
  45        struct fis_image_desc *buf;
  46        struct mtd_partition *parts;
  47        struct fis_list *fl = NULL, *tmp_fl;
  48        int ret, i;
  49        size_t retlen;
  50        char *names;
  51        char *nullname;
  52        int namelen = 0;
  53        int nulllen = 0;
  54        int numslots;
  55        unsigned long offset;
  56#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
  57        static char nullstring[] = "unallocated";
  58#endif
  59
  60        if ( directory < 0 ) {
  61                offset = master->size + directory * master->erasesize;
  62                while (master->block_isbad && 
  63                       master->block_isbad(master, offset)) {
  64                        if (!offset) {
  65                        nogood:
  66                                printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n");
  67                                return -EIO;
  68                        }
  69                        offset -= master->erasesize;
  70                }
  71        } else {
  72                offset = directory * master->erasesize;
  73                while (master->block_isbad && 
  74                       master->block_isbad(master, offset)) {
  75                        offset += master->erasesize;
  76                        if (offset == master->size)
  77                                goto nogood;
  78                }
  79        }
  80        buf = vmalloc(master->erasesize);
  81
  82        if (!buf)
  83                return -ENOMEM;
  84
  85        printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n",
  86               master->name, offset);
  87
  88        ret = master->read(master, offset,
  89                           master->erasesize, &retlen, (void *)buf);
  90
  91        if (ret)
  92                goto out;
  93
  94        if (retlen != master->erasesize) {
  95                ret = -EIO;
  96                goto out;
  97        }
  98
  99        numslots = (master->erasesize / sizeof(struct fis_image_desc));
 100        for (i = 0; i < numslots; i++) {
 101                if (!memcmp(buf[i].name, "FIS directory", 14)) {
 102                        /* This is apparently the FIS directory entry for the
 103                         * FIS directory itself.  The FIS directory size is
 104                         * one erase block; if the buf[i].size field is
 105                         * swab32(erasesize) then we know we are looking at
 106                         * a byte swapped FIS directory - swap all the entries!
 107                         * (NOTE: this is 'size' not 'data_length'; size is
 108                         * the full size of the entry.)
 109                         */
 110
 111                        /* RedBoot can combine the FIS directory and
 112                           config partitions into a single eraseblock;
 113                           we assume wrong-endian if either the swapped
 114                           'size' matches the eraseblock size precisely,
 115                           or if the swapped size actually fits in an
 116                           eraseblock while the unswapped size doesn't. */
 117                        if (swab32(buf[i].size) == master->erasesize ||
 118                            (buf[i].size > master->erasesize
 119                             && swab32(buf[i].size) < master->erasesize)) {
 120                                int j;
 121                                /* Update numslots based on actual FIS directory size */
 122                                numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc);
 123                                for (j = 0; j < numslots; ++j) {
 124
 125                                        /* A single 0xff denotes a deleted entry.
 126                                         * Two of them in a row is the end of the table.
 127                                         */
 128                                        if (buf[j].name[0] == 0xff) {
 129                                                if (buf[j].name[1] == 0xff) {
 130                                                        break;
 131                                                } else {
 132                                                        continue;
 133                                                }
 134                                        }
 135
 136                                        /* The unsigned long fields were written with the
 137                                         * wrong byte sex, name and pad have no byte sex.
 138                                         */
 139                                        swab32s(&buf[j].flash_base);
 140                                        swab32s(&buf[j].mem_base);
 141                                        swab32s(&buf[j].size);
 142                                        swab32s(&buf[j].entry_point);
 143                                        swab32s(&buf[j].data_length);
 144                                        swab32s(&buf[j].desc_cksum);
 145                                        swab32s(&buf[j].file_cksum);
 146                                }
 147                        } else if (buf[i].size < master->erasesize) {
 148                                /* Update numslots based on actual FIS directory size */
 149                                numslots = buf[i].size / sizeof(struct fis_image_desc);
 150                        }
 151                        break;
 152                }
 153        }
 154        if (i == numslots) {
 155                /* Didn't find it */
 156                printk(KERN_NOTICE "No RedBoot partition table detected in %s\n",
 157                       master->name);
 158                ret = 0;
 159                goto out;
 160        }
 161
 162        for (i = 0; i < numslots; i++) {
 163                struct fis_list *new_fl, **prev;
 164
 165                if (buf[i].name[0] == 0xff) {
 166                        if (buf[i].name[1] == 0xff) {
 167                                break;
 168                        } else {
 169                                continue;
 170                        }
 171                }
 172                if (!redboot_checksum(&buf[i]))
 173                        break;
 174
 175                new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL);
 176                namelen += strlen(buf[i].name)+1;
 177                if (!new_fl) {
 178                        ret = -ENOMEM;
 179                        goto out;
 180                }
 181                new_fl->img = &buf[i];
 182                if (fis_origin) {
 183                        buf[i].flash_base -= fis_origin;
 184                } else {
 185                        buf[i].flash_base &= master->size-1;
 186                }
 187
 188                /* I'm sure the JFFS2 code has done me permanent damage.
 189                 * I now think the following is _normal_
 190                 */
 191                prev = &fl;
 192                while(*prev && (*prev)->img->flash_base < new_fl->img->flash_base)
 193                        prev = &(*prev)->next;
 194                new_fl->next = *prev;
 195                *prev = new_fl;
 196
 197                nrparts++;
 198        }
 199#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
 200        if (fl->img->flash_base) {
 201                nrparts++;
 202                nulllen = sizeof(nullstring);
 203        }
 204
 205        for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) {
 206                if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) {
 207                        nrparts++;
 208                        nulllen = sizeof(nullstring);
 209                }
 210        }
 211#endif
 212        parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
 213
 214        if (!parts) {
 215                ret = -ENOMEM;
 216                goto out;
 217        }
 218
 219        nullname = (char *)&parts[nrparts];
 220#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
 221        if (nulllen > 0) {
 222                strcpy(nullname, nullstring);
 223        }
 224#endif
 225        names = nullname + nulllen;
 226
 227        i=0;
 228
 229#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
 230        if (fl->img->flash_base) {
 231               parts[0].name = nullname;
 232               parts[0].size = fl->img->flash_base;
 233               parts[0].offset = 0;
 234                i++;
 235        }
 236#endif
 237        for ( ; i<nrparts; i++) {
 238                parts[i].size = fl->img->size;
 239                parts[i].offset = fl->img->flash_base;
 240                parts[i].name = names;
 241
 242                strcpy(names, fl->img->name);
 243#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY
 244                if (!memcmp(names, "RedBoot", 8) ||
 245                                !memcmp(names, "RedBoot config", 15) ||
 246                                !memcmp(names, "FIS directory", 14)) {
 247                        parts[i].mask_flags = MTD_WRITEABLE;
 248                }
 249#endif
 250                names += strlen(names)+1;
 251
 252#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
 253                if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
 254                        i++;
 255                        parts[i].offset = parts[i-1].size + parts[i-1].offset;
 256                        parts[i].size = fl->next->img->flash_base - parts[i].offset;
 257                        parts[i].name = nullname;
 258                }
 259#endif
 260                tmp_fl = fl;
 261                fl = fl->next;
 262                kfree(tmp_fl);
 263        }
 264        ret = nrparts;
 265        *pparts = parts;
 266 out:
 267        while (fl) {
 268                struct fis_list *old = fl;
 269                fl = fl->next;
 270                kfree(old);
 271        }
 272        vfree(buf);
 273        return ret;
 274}
 275
 276static struct mtd_part_parser redboot_parser = {
 277        .owner = THIS_MODULE,
 278        .parse_fn = parse_redboot_partitions,
 279        .name = "RedBoot",
 280};
 281
 282static int __init redboot_parser_init(void)
 283{
 284        return register_mtd_parser(&redboot_parser);
 285}
 286
 287static void __exit redboot_parser_exit(void)
 288{
 289        deregister_mtd_parser(&redboot_parser);
 290}
 291
 292module_init(redboot_parser_init);
 293module_exit(redboot_parser_exit);
 294
 295MODULE_LICENSE("GPL");
 296MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 297MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables");
 298