Blame view

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