Merge to Fedora kernel-2.6.7-1.494 and VServer 1.9.1.12. Fix some previous merge...
[linux-2.6.git] / drivers / mtd / redboot.c
1 /*
2  * $Id: redboot.c,v 1.13 2004/04/01 10:17:40 gthomas Exp $
3  *
4  * Parse RedBoot-style Flash Image System (FIS) tables and
5  * produce a Linux partition array to match.
6  */
7
8 #include <linux/kernel.h>
9 #include <linux/slab.h>
10 #include <linux/init.h>
11
12 #include <linux/mtd/mtd.h>
13 #include <linux/mtd/partitions.h>
14
15 struct fis_image_desc {
16     unsigned char name[16];      // Null terminated name
17     unsigned long flash_base;    // Address within FLASH of image
18     unsigned long mem_base;      // Address in memory where it executes
19     unsigned long size;          // Length of image
20     unsigned long entry_point;   // Execution entry point
21     unsigned long data_length;   // Length of actual data
22     unsigned char _pad[256-(16+7*sizeof(unsigned long))];
23     unsigned long desc_cksum;    // Checksum over image descriptor
24     unsigned long file_cksum;    // Checksum over image data
25 };
26
27 struct fis_list {
28         struct fis_image_desc *img;
29         struct fis_list *next;
30 };
31
32 static inline int redboot_checksum(struct fis_image_desc *img)
33 {
34         /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */
35         return 1;
36 }
37
38 static int parse_redboot_partitions(struct mtd_info *master, 
39                              struct mtd_partition **pparts,
40                              unsigned long fis_origin)
41 {
42         int nrparts = 0;
43         struct fis_image_desc *buf;
44         struct mtd_partition *parts;
45         struct fis_list *fl = NULL, *tmp_fl;
46         int ret, i;
47         size_t retlen;
48         char *names;
49         char *nullname;
50         int namelen = 0;
51         int nulllen = 0;
52 #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
53         static char nullstring[] = "unallocated";
54 #endif
55
56         buf = kmalloc(master->erasesize, GFP_KERNEL);
57
58         if (!buf)
59                 return -ENOMEM;
60
61         /* Read the start of the last erase block */
62         ret = master->read(master, master->size - master->erasesize,
63                            master->erasesize, &retlen, (void *)buf);
64
65         if (ret)
66                 goto out;
67
68         if (retlen != master->erasesize) {
69                 ret = -EIO;
70                 goto out;
71         }
72
73         /* RedBoot image could appear in any of the first three slots */
74         for (i = 0; i < 3; i++) {
75                 if (!memcmp(buf[i].name, "RedBoot", 8))
76                         break;
77         }
78         if (i == 3) {
79                 /* Didn't find it */
80                 printk(KERN_NOTICE "No RedBoot partition table detected in %s\n",
81                        master->name);
82                 ret = 0;
83                 goto out;
84         }
85
86         for (i = 0; i < master->erasesize / sizeof(struct fis_image_desc); i++) {
87                 struct fis_list *new_fl, **prev;
88
89                 if (buf[i].name[0] == 0xff)
90                         break;
91                 if (!redboot_checksum(&buf[i]))
92                         break;
93
94                 new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL);
95                 namelen += strlen(buf[i].name)+1;
96                 if (!new_fl) {
97                         ret = -ENOMEM;
98                         goto out;
99                 }
100                 new_fl->img = &buf[i];
101                 if (fis_origin) {
102                         buf[i].flash_base -= fis_origin;
103                 } else {
104                         buf[i].flash_base &= master->size-1;
105                 }
106
107                 /* I'm sure the JFFS2 code has done me permanent damage.
108                  * I now think the following is _normal_
109                  */
110                 prev = &fl;
111                 while(*prev && (*prev)->img->flash_base < new_fl->img->flash_base)
112                         prev = &(*prev)->next;
113                 new_fl->next = *prev;
114                 *prev = new_fl;
115
116                 nrparts++;
117         }
118 #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
119         if (fl->img->flash_base) {
120                 nrparts++;
121                 nulllen = sizeof(nullstring);
122         }
123
124         for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) {
125                 if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) {
126                         nrparts++;
127                         nulllen = sizeof(nullstring);
128                 }
129         }
130 #endif
131         parts = kmalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
132
133         if (!parts) {
134                 ret = -ENOMEM;
135                 goto out;
136         }
137
138         memset(parts, 0, sizeof(*parts)*nrparts + nulllen + namelen);
139
140         nullname = (char *)&parts[nrparts];
141 #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
142         if (nulllen > 0) {
143                 strcpy(nullname, nullstring);
144         }
145 #endif
146         names = nullname + nulllen;
147
148         i=0;
149
150 #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
151         if (fl->img->flash_base) {
152                parts[0].name = nullname;
153                parts[0].size = fl->img->flash_base;
154                parts[0].offset = 0;
155                 i++;
156         }
157 #endif
158         for ( ; i<nrparts; i++) {
159                 parts[i].size = fl->img->size;
160                 parts[i].offset = fl->img->flash_base;
161                 parts[i].name = names;
162
163                 strcpy(names, fl->img->name);
164 #ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY
165                 if (!memcmp(names, "RedBoot", 8) ||
166                                 !memcmp(names, "RedBoot config", 15) ||
167                                 !memcmp(names, "FIS directory", 14)) {
168                         parts[i].mask_flags = MTD_WRITEABLE;
169                 }
170 #endif
171                 names += strlen(names)+1;
172
173 #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
174                 if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
175                         i++;
176                         parts[i].offset = parts[i-1].size + parts[i-1].offset;
177                         parts[i].size = fl->next->img->flash_base - parts[i].offset;
178                         parts[i].name = nullname;
179                 }
180 #endif
181                 tmp_fl = fl;
182                 fl = fl->next;
183                 kfree(tmp_fl);
184         }
185         ret = nrparts;
186         *pparts = parts;
187  out:
188         while (fl) {
189                 struct fis_list *old = fl;
190                 fl = fl->next;
191                 kfree(old);
192         }
193         kfree(buf);
194         return ret;
195 }
196
197 static struct mtd_part_parser redboot_parser = {
198         .owner = THIS_MODULE,
199         .parse_fn = parse_redboot_partitions,
200         .name = "RedBoot",
201 };
202
203 static int __init redboot_parser_init(void)
204 {
205         return register_mtd_parser(&redboot_parser);
206 }
207
208 static void __exit redboot_parser_exit(void)
209 {
210         deregister_mtd_parser(&redboot_parser);
211 }
212
213 module_init(redboot_parser_init);
214 module_exit(redboot_parser_exit);
215
216 MODULE_LICENSE("GPL");
217 MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>");
218 MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables");