Blame view

drivers/mtd/redboot.c 8.21 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,
b9adf469f   Brian Norris   mtd: partitions: ...
59
  				    const struct mtd_partition **pparts,
c79753301   Dmitry Eremin-Solenikov   mtd: abstract las...
60
  				    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;
050c0c1bb   Artem Bityutskiy   mtd: redboot: rem...
79
  		while (mtd_block_isbad(master, offset)) {
3c441baa0   David Woodhouse   [MTD] Skip bad bl...
80
81
82
83
84
85
86
87
88
89
  			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;
050c0c1bb   Artem Bityutskiy   mtd: redboot: rem...
90
  		while (mtd_block_isbad(master, offset)) {
3c441baa0   David Woodhouse   [MTD] Skip bad bl...
91
92
93
94
95
  			offset += master->erasesize;
  			if (offset == master->size)
  				goto nogood;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
  	buf = vmalloc(master->erasesize);
  
  	if (!buf)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
  	printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx
  ",
  	       master->name, offset);
329ad399a   Artem Bityutskiy   mtd: introduce mt...
103
104
  	ret = mtd_read(master, offset, master->erasesize, &retlen,
  		       (void *)buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
107
108
109
110
111
112
113
114
115
  
  	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...
116
117
118
  		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/...
119
  			 * one erase block; if the buf[i].size field is
9cff3372b   John Bowler   [PATCH] drivers/m...
120
121
  			 * swab32(erasesize) then we know we are looking at
  			 * a byte swapped FIS directory - swap all the entries!
77a331355   John Bowler   [PATCH] "drivers/...
122
  			 * (NOTE: this is 'size' not 'data_length'; size is
9cff3372b   John Bowler   [PATCH] drivers/m...
123
124
  			 * the full size of the entry.)
  			 */
7ca353a42   David Woodhouse   [MTD] Improve heu...
125
126
127
128
129
130
131
132
133
134
  
  			/* 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...
135
  				int j;
11192146e   Rod Whitby   [MTD] Fix RedBoot...
136
137
  				/* Update numslots based on actual FIS directory size */
  				numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc);
f33665d93   Rod Whitby   [MTD] Support com...
138
139
140
141
142
143
144
145
146
147
148
149
  				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...
150
151
152
  					/* The unsigned long fields were written with the
  					 * wrong byte sex, name and pad have no byte sex.
  					 */
77a331355   John Bowler   [PATCH] "drivers/...
153
154
155
156
157
158
159
  					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...
160
  				}
7ca353a42   David Woodhouse   [MTD] Improve heu...
161
  			} else if (buf[i].size < master->erasesize) {
11192146e   Rod Whitby   [MTD] Fix RedBoot...
162
163
  				/* Update numslots based on actual FIS directory size */
  				numslots = buf[i].size / sizeof(struct fis_image_desc);
9cff3372b   John Bowler   [PATCH] drivers/m...
164
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  			break;
9cff3372b   John Bowler   [PATCH] drivers/m...
166
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
169
170
171
172
173
174
175
176
177
178
  	}
  	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...
179
180
181
182
183
184
185
  		if (buf[i].name[0] == 0xff) {
  			if (buf[i].name[1] == 0xff) {
  				break;
  			} else {
  				continue;
  			}
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
189
190
191
192
193
194
195
  		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...
196
197
198
199
  		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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  
  		/* 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...
225
  	parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
229
230
  
  	if (!parts) {
  		ret = -ENOMEM;
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
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
  	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 = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
  	.parse_fn = parse_redboot_partitions,
  	.name = "RedBoot",
  };
b8f70badb   Brian Norris   mtd: kill off MTD...
292
  module_mtd_part_parser(redboot_parser);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293

d5de1907d   Andres Salomon   mtd: provide an a...
294
295
  /* mtd parsers will request the module by parser name */
  MODULE_ALIAS("RedBoot");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
  MODULE_LICENSE("GPL");
44d1b980c   David Woodhouse   Fix various old e...
297
  MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
  MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables");