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