File: /usr/src/linux/drivers/mtd/redboot.c
1 /*
2 * $Id: redboot.c,v 1.4 2001/05/31 20:43:18 dwmw2 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
11 #include <linux/mtd/mtd.h>
12 #include <linux/mtd/partitions.h>
13
14 struct fis_image_desc {
15 unsigned char name[16]; // Null terminated name
16 unsigned long flash_base; // Address within FLASH of image
17 unsigned long mem_base; // Address in memory where it executes
18 unsigned long size; // Length of image
19 unsigned long entry_point; // Execution entry point
20 unsigned long data_length; // Length of actual data
21 unsigned char _pad[256-(16+7*sizeof(unsigned long))];
22 unsigned long desc_cksum; // Checksum over image descriptor
23 unsigned long file_cksum; // Checksum over image data
24 };
25
26 struct fis_list {
27 struct fis_image_desc *img;
28 struct fis_list *next;
29 };
30
31 static inline int redboot_checksum(struct fis_image_desc *img)
32 {
33 /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */
34 return 1;
35 }
36
37 int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts)
38 {
39 int nrparts = 0;
40 struct fis_image_desc *buf;
41 struct mtd_partition *parts;
42 struct fis_list *fl = NULL, *tmp_fl;
43 int ret, i;
44 size_t retlen;
45 char *names;
46 int namelen = 0;
47
48 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
49
50 if (!buf)
51 return -ENOMEM;
52
53 /* Read the start of the last erase block */
54 ret = master->read(master, master->size - master->erasesize,
55 PAGE_SIZE, &retlen, (void *)buf);
56
57 if (ret)
58 goto out;
59
60 if (retlen != PAGE_SIZE) {
61 ret = -EIO;
62 goto out;
63 }
64
65 if (memcmp(buf, "RedBoot", 8)) {
66 ret = 0;
67 goto out;
68 }
69
70 for (i = 0; i < PAGE_SIZE / sizeof(struct fis_image_desc); i++) {
71 struct fis_list *new_fl, **prev;
72
73 if (buf[i].name[0] == 0xff)
74 break;
75 if (!redboot_checksum(&buf[i]))
76 break;
77
78 new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL);
79 namelen += strlen(buf[i].name)+1;
80 if (!new_fl) {
81 ret = -ENOMEM;
82 goto out;
83 }
84 new_fl->img = &buf[i];
85 buf[i].flash_base &= master->size-1;
86
87 /* I'm sure the JFFS2 code has done me permanent damage.
88 * I now think the following is _normal_
89 */
90 prev = &fl;
91 while(*prev && (*prev)->img->flash_base < new_fl->img->flash_base)
92 prev = &(*prev)->next;
93 new_fl->next = *prev;
94 *prev = new_fl;
95
96 nrparts++;
97 }
98 if (fl->img->flash_base)
99 nrparts++;
100
101 for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) {
102 if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize < tmp_fl->next->img->flash_base)
103 nrparts++;
104 }
105 parts = kmalloc(sizeof(*parts)*nrparts + namelen, GFP_KERNEL);
106
107 if (!parts) {
108 ret = -ENOMEM;
109 goto out;
110 }
111 names = (char *)&parts[nrparts];
112 memset(parts, 0, sizeof(*parts)*nrparts + namelen);
113 i=0;
114
115 if (fl->img->flash_base) {
116 parts[0].name = "unallocated space";
117 parts[0].size = fl->img->flash_base;
118 parts[0].offset = 0;
119 }
120 for ( ; i<nrparts; i++) {
121 parts[i].size = fl->img->size;
122 parts[i].offset = fl->img->flash_base;
123 parts[i].name = names;
124
125 strcpy(names, fl->img->name);
126 names += strlen(names)+1;
127
128 if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) {
129 i++;
130 parts[i].offset = parts[i-1].size + parts[i-1].offset;
131 parts[i].size = fl->next->img->flash_base - parts[i].offset;
132 parts[i].name = "unallocated space";
133 }
134 tmp_fl = fl;
135 fl = fl->next;
136 kfree(tmp_fl);
137 }
138 ret = nrparts;
139 *pparts = parts;
140 out:
141 while (fl) {
142 struct fis_list *old = fl;
143 fl = fl->next;
144 kfree(old);
145 }
146 kfree(buf);
147 return ret;
148 }
149
150 EXPORT_SYMBOL(parse_redboot_partitions);
151