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