kernel: mtdsplit_uimage: add parser for Edimax devices
[15.05/openwrt.git] / target / linux / generic / files / drivers / mtd / mtdsplit / mtdsplit_uimage.c
1 /*
2  *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
3  *
4  *  This program is free software; you can redistribute it and/or modify it
5  *  under the terms of the GNU General Public License version 2 as published
6  *  by the Free Software Foundation.
7  *
8  */
9
10 #define pr_fmt(fmt)     KBUILD_MODNAME ": " fmt
11
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/slab.h>
16 #include <linux/vmalloc.h>
17 #include <linux/mtd/mtd.h>
18 #include <linux/mtd/partitions.h>
19 #include <linux/byteorder/generic.h>
20
21 #include "mtdsplit.h"
22
23 #define IH_MAGIC        0x27051956      /* Image Magic Number           */
24 #define IH_NMLEN                32      /* Image Name Length            */
25
26 #define IH_OS_LINUX             5       /* Linux        */
27
28 #define IH_TYPE_KERNEL          2       /* OS Kernel Image              */
29 #define IH_TYPE_FILESYSTEM      7       /* Filesystem Image             */
30
31 /*
32  * Legacy format image header,
33  * all data in network byte order (aka natural aka bigendian).
34  */
35 struct uimage_header {
36         uint32_t        ih_magic;       /* Image Header Magic Number    */
37         uint32_t        ih_hcrc;        /* Image Header CRC Checksum    */
38         uint32_t        ih_time;        /* Image Creation Timestamp     */
39         uint32_t        ih_size;        /* Image Data Size              */
40         uint32_t        ih_load;        /* Data  Load  Address          */
41         uint32_t        ih_ep;          /* Entry Point Address          */
42         uint32_t        ih_dcrc;        /* Image Data CRC Checksum      */
43         uint8_t         ih_os;          /* Operating System             */
44         uint8_t         ih_arch;        /* CPU architecture             */
45         uint8_t         ih_type;        /* Image Type                   */
46         uint8_t         ih_comp;        /* Compression Type             */
47         uint8_t         ih_name[IH_NMLEN];      /* Image Name           */
48 };
49
50 static int
51 read_uimage_header(struct mtd_info *mtd, size_t offset,
52                    struct uimage_header *header)
53 {
54         size_t header_len;
55         size_t retlen;
56         int ret;
57
58         header_len = sizeof(*header);
59         ret = mtd_read(mtd, offset, header_len, &retlen,
60                        (unsigned char *) header);
61         if (ret) {
62                 pr_debug("read error in \"%s\"\n", mtd->name);
63                 return ret;
64         }
65
66         if (retlen != header_len) {
67                 pr_debug("short read in \"%s\"\n", mtd->name);
68                 return -EIO;
69         }
70
71         return 0;
72 }
73
74 /**
75  * __mtdsplit_parse_uimage - scan partition and create kernel + rootfs parts
76  *
77  * @find_header: function to call for a block of data that will return offset
78  *      of a valid uImage header if found
79  */
80 static int __mtdsplit_parse_uimage(struct mtd_info *master,
81                                    struct mtd_partition **pparts,
82                                    struct mtd_part_parser_data *data,
83                                    ssize_t (*find_header)(u_char *buf, size_t len))
84 {
85         struct mtd_partition *parts;
86         struct uimage_header *header;
87         int nr_parts;
88         size_t offset;
89         size_t uimage_offset;
90         size_t uimage_size = 0;
91         size_t rootfs_offset;
92         size_t rootfs_size = 0;
93         int uimage_part, rf_part;
94         int ret;
95
96         nr_parts = 2;
97         parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
98         if (!parts)
99                 return -ENOMEM;
100
101         header = vmalloc(sizeof(*header));
102         if (!header) {
103                 ret = -ENOMEM;
104                 goto err_free_parts;
105         }
106
107         /* find uImage on erase block boundaries */
108         for (offset = 0; offset < master->size; offset += master->erasesize) {
109                 uimage_size = 0;
110
111                 ret = read_uimage_header(master, offset, header);
112                 if (ret)
113                         continue;
114
115                 ret = find_header((u_char *)header, sizeof(*header));
116                 if (ret < 0) {
117                         pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
118                                  master->name, (unsigned long long) offset);
119                         continue;
120                 }
121                 if (ret > 0) {
122                         pr_warn("extra header offsets are not supported yet\n");
123                         continue;
124                 }
125
126                 uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size);
127                 if ((offset + uimage_size) > master->size) {
128                         pr_debug("uImage exceeds MTD device \"%s\"\n",
129                                  master->name);
130                         continue;
131                 }
132                 break;
133         }
134
135         if (uimage_size == 0) {
136                 pr_debug("no uImage found in \"%s\"\n", master->name);
137                 ret = -ENODEV;
138                 goto err_free_header;
139         }
140
141         uimage_offset = offset;
142
143         if (uimage_offset == 0) {
144                 uimage_part = 0;
145                 rf_part = 1;
146
147                 /* find the roots after the uImage */
148                 ret = mtd_find_rootfs_from(master,
149                                            uimage_offset + uimage_size,
150                                            master->size,
151                                            &rootfs_offset);
152                 if (ret) {
153                         pr_debug("no rootfs after uImage in \"%s\"\n",
154                                  master->name);
155                         goto err_free_header;
156                 }
157
158                 rootfs_size = master->size - rootfs_offset;
159                 uimage_size = rootfs_offset - uimage_offset;
160         } else {
161                 rf_part = 0;
162                 uimage_part = 1;
163
164                 /* check rootfs presence at offset 0 */
165                 ret = mtd_check_rootfs_magic(master, 0);
166                 if (ret) {
167                         pr_debug("no rootfs before uImage in \"%s\"\n",
168                                  master->name);
169                         goto err_free_header;
170                 }
171
172                 rootfs_offset = 0;
173                 rootfs_size = uimage_offset;
174         }
175
176         if (rootfs_size == 0) {
177                 pr_debug("no rootfs found in \"%s\"\n", master->name);
178                 ret = -ENODEV;
179                 goto err_free_header;
180         }
181
182         parts[uimage_part].name = KERNEL_PART_NAME;
183         parts[uimage_part].offset = uimage_offset;
184         parts[uimage_part].size = uimage_size;
185
186         parts[rf_part].name = ROOTFS_PART_NAME;
187         parts[rf_part].offset = rootfs_offset;
188         parts[rf_part].size = rootfs_size;
189
190         vfree(header);
191
192         *pparts = parts;
193         return nr_parts;
194
195 err_free_header:
196         vfree(header);
197
198 err_free_parts:
199         kfree(parts);
200         return ret;
201 }
202
203 static ssize_t uimage_verify_default(u_char *buf, size_t len)
204 {
205         struct uimage_header *header = (struct uimage_header *)buf;
206
207         /* default sanity checks */
208         if (be32_to_cpu(header->ih_magic) != IH_MAGIC) {
209                 pr_debug("invalid uImage magic: %08x\n",
210                          be32_to_cpu(header->ih_magic));
211                 return -EINVAL;
212         }
213
214         if (header->ih_os != IH_OS_LINUX) {
215                 pr_debug("invalid uImage OS: %08x\n",
216                          be32_to_cpu(header->ih_os));
217                 return -EINVAL;
218         }
219
220         if (header->ih_type != IH_TYPE_KERNEL) {
221                 pr_debug("invalid uImage type: %08x\n",
222                          be32_to_cpu(header->ih_type));
223                 return -EINVAL;
224         }
225
226         return 0;
227 }
228
229 static int
230 mtdsplit_uimage_parse_generic(struct mtd_info *master,
231                               struct mtd_partition **pparts,
232                               struct mtd_part_parser_data *data)
233 {
234         return __mtdsplit_parse_uimage(master, pparts, data,
235                                       uimage_verify_default);
236 }
237
238 static struct mtd_part_parser uimage_generic_parser = {
239         .owner = THIS_MODULE,
240         .name = "uimage-fw",
241         .parse_fn = mtdsplit_uimage_parse_generic,
242         .type = MTD_PARSER_TYPE_FIRMWARE,
243 };
244
245 #define FW_MAGIC_WNR2000V3      0x32303033
246 #define FW_MAGIC_WNR2000V4      0x32303034
247 #define FW_MAGIC_WNR2200        0x32323030
248 #define FW_MAGIC_WNR612V2       0x32303631
249 #define FW_MAGIC_WNR1000V2      0x31303031
250 #define FW_MAGIC_WNR1000V2_VC   0x31303030
251 #define FW_MAGIC_WNDR3700       0x33373030
252 #define FW_MAGIC_WNDR3700V2     0x33373031
253
254 static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len)
255 {
256         struct uimage_header *header = (struct uimage_header *)buf;
257         uint8_t expected_type = IH_TYPE_FILESYSTEM;
258
259         switch be32_to_cpu(header->ih_magic) {
260         case FW_MAGIC_WNR612V2:
261         case FW_MAGIC_WNR1000V2:
262         case FW_MAGIC_WNR1000V2_VC:
263         case FW_MAGIC_WNR2000V3:
264         case FW_MAGIC_WNR2200:
265         case FW_MAGIC_WNDR3700:
266         case FW_MAGIC_WNDR3700V2:
267                 break;
268         case FW_MAGIC_WNR2000V4:
269                 expected_type = IH_TYPE_KERNEL;
270                 break;
271         default:
272                 return -EINVAL;
273         }
274
275         if (header->ih_os != IH_OS_LINUX ||
276             header->ih_type != expected_type)
277                 return -EINVAL;
278
279         return 0;
280 }
281
282 static int
283 mtdsplit_uimage_parse_netgear(struct mtd_info *master,
284                               struct mtd_partition **pparts,
285                               struct mtd_part_parser_data *data)
286 {
287         return __mtdsplit_parse_uimage(master, pparts, data,
288                                       uimage_verify_wndr3700);
289 }
290
291 static struct mtd_part_parser uimage_netgear_parser = {
292         .owner = THIS_MODULE,
293         .name = "netgear-fw",
294         .parse_fn = mtdsplit_uimage_parse_netgear,
295         .type = MTD_PARSER_TYPE_FIRMWARE,
296 };
297
298 /**************************************************
299  * Edimax
300  **************************************************/
301
302 #define FW_EDIMAX_OFFSET        20
303 #define FW_MAGIC_EDIMAX         0x43535953
304
305 static ssize_t uimage_find_edimax(u_char *buf, size_t len)
306 {
307         struct uimage_header *header;
308
309         if (len < FW_EDIMAX_OFFSET + sizeof(*header)) {
310                 pr_err("Buffer too small for checking Edimax header\n");
311                 return -ENOSPC;
312         }
313
314         header = (struct uimage_header *)(buf + FW_EDIMAX_OFFSET);
315
316         switch be32_to_cpu(header->ih_magic) {
317         case FW_MAGIC_EDIMAX:
318                 break;
319         default:
320                 return -EINVAL;
321         }
322
323         if (header->ih_os != IH_OS_LINUX ||
324             header->ih_type != IH_TYPE_FILESYSTEM)
325                 return -EINVAL;
326
327         return FW_EDIMAX_OFFSET;
328 }
329
330 static int
331 mtdsplit_uimage_parse_edimax(struct mtd_info *master,
332                               struct mtd_partition **pparts,
333                               struct mtd_part_parser_data *data)
334 {
335         return __mtdsplit_parse_uimage(master, pparts, data,
336                                        uimage_find_edimax);
337 }
338
339 static struct mtd_part_parser uimage_edimax_parser = {
340         .owner = THIS_MODULE,
341         .name = "edimax-fw",
342         .parse_fn = mtdsplit_uimage_parse_edimax,
343         .type = MTD_PARSER_TYPE_FIRMWARE,
344 };
345
346 /**************************************************
347  * Init
348  **************************************************/
349
350 static int __init mtdsplit_uimage_init(void)
351 {
352         register_mtd_parser(&uimage_generic_parser);
353         register_mtd_parser(&uimage_netgear_parser);
354         register_mtd_parser(&uimage_edimax_parser);
355
356         return 0;
357 }
358
359 module_init(mtdsplit_uimage_init);