kernel: mtdsplit_uimage: use separated buffer for reading data
[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                    u_char *buf)
53 {
54         struct uimage_header *header;
55         size_t header_len;
56         size_t retlen;
57         int ret;
58
59         header_len = sizeof(*header);
60         ret = mtd_read(mtd, offset, header_len, &retlen, buf);
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         u_char *buf;
87         struct uimage_header *header;
88         int nr_parts;
89         size_t offset;
90         size_t uimage_offset;
91         size_t uimage_size = 0;
92         size_t rootfs_offset;
93         size_t rootfs_size = 0;
94         int uimage_part, rf_part;
95         int ret;
96
97         nr_parts = 2;
98         parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
99         if (!parts)
100                 return -ENOMEM;
101
102         buf = vmalloc(sizeof(*header));
103         if (!buf) {
104                 ret = -ENOMEM;
105                 goto err_free_parts;
106         }
107
108         /* find uImage on erase block boundaries */
109         for (offset = 0; offset < master->size; offset += master->erasesize) {
110                 uimage_size = 0;
111
112                 ret = read_uimage_header(master, offset, buf);
113                 if (ret)
114                         continue;
115
116                 ret = find_header(buf, sizeof(*buf));
117                 if (ret < 0) {
118                         pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
119                                  master->name, (unsigned long long) offset);
120                         continue;
121                 }
122                 header = (struct uimage_header *)(buf + ret);
123
124                 uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size);
125                 if ((offset + uimage_size) > master->size) {
126                         pr_debug("uImage exceeds MTD device \"%s\"\n",
127                                  master->name);
128                         continue;
129                 }
130                 break;
131         }
132
133         if (uimage_size == 0) {
134                 pr_debug("no uImage found in \"%s\"\n", master->name);
135                 ret = -ENODEV;
136                 goto err_free_buf;
137         }
138
139         uimage_offset = offset;
140
141         if (uimage_offset == 0) {
142                 uimage_part = 0;
143                 rf_part = 1;
144
145                 /* find the roots after the uImage */
146                 ret = mtd_find_rootfs_from(master,
147                                            uimage_offset + uimage_size,
148                                            master->size,
149                                            &rootfs_offset);
150                 if (ret) {
151                         pr_debug("no rootfs after uImage in \"%s\"\n",
152                                  master->name);
153                         goto err_free_buf;
154                 }
155
156                 rootfs_size = master->size - rootfs_offset;
157                 uimage_size = rootfs_offset - uimage_offset;
158         } else {
159                 rf_part = 0;
160                 uimage_part = 1;
161
162                 /* check rootfs presence at offset 0 */
163                 ret = mtd_check_rootfs_magic(master, 0);
164                 if (ret) {
165                         pr_debug("no rootfs before uImage in \"%s\"\n",
166                                  master->name);
167                         goto err_free_buf;
168                 }
169
170                 rootfs_offset = 0;
171                 rootfs_size = uimage_offset;
172         }
173
174         if (rootfs_size == 0) {
175                 pr_debug("no rootfs found in \"%s\"\n", master->name);
176                 ret = -ENODEV;
177                 goto err_free_buf;
178         }
179
180         parts[uimage_part].name = KERNEL_PART_NAME;
181         parts[uimage_part].offset = uimage_offset;
182         parts[uimage_part].size = uimage_size;
183
184         parts[rf_part].name = ROOTFS_PART_NAME;
185         parts[rf_part].offset = rootfs_offset;
186         parts[rf_part].size = rootfs_size;
187
188         vfree(buf);
189
190         *pparts = parts;
191         return nr_parts;
192
193 err_free_buf:
194         vfree(buf);
195
196 err_free_parts:
197         kfree(parts);
198         return ret;
199 }
200
201 static ssize_t uimage_verify_default(u_char *buf, size_t len)
202 {
203         struct uimage_header *header = (struct uimage_header *)buf;
204
205         /* default sanity checks */
206         if (be32_to_cpu(header->ih_magic) != IH_MAGIC) {
207                 pr_debug("invalid uImage magic: %08x\n",
208                          be32_to_cpu(header->ih_magic));
209                 return -EINVAL;
210         }
211
212         if (header->ih_os != IH_OS_LINUX) {
213                 pr_debug("invalid uImage OS: %08x\n",
214                          be32_to_cpu(header->ih_os));
215                 return -EINVAL;
216         }
217
218         if (header->ih_type != IH_TYPE_KERNEL) {
219                 pr_debug("invalid uImage type: %08x\n",
220                          be32_to_cpu(header->ih_type));
221                 return -EINVAL;
222         }
223
224         return 0;
225 }
226
227 static int
228 mtdsplit_uimage_parse_generic(struct mtd_info *master,
229                               struct mtd_partition **pparts,
230                               struct mtd_part_parser_data *data)
231 {
232         return __mtdsplit_parse_uimage(master, pparts, data,
233                                       uimage_verify_default);
234 }
235
236 static struct mtd_part_parser uimage_generic_parser = {
237         .owner = THIS_MODULE,
238         .name = "uimage-fw",
239         .parse_fn = mtdsplit_uimage_parse_generic,
240         .type = MTD_PARSER_TYPE_FIRMWARE,
241 };
242
243 #define FW_MAGIC_WNR2000V3      0x32303033
244 #define FW_MAGIC_WNR2000V4      0x32303034
245 #define FW_MAGIC_WNR2200        0x32323030
246 #define FW_MAGIC_WNR612V2       0x32303631
247 #define FW_MAGIC_WNR1000V2      0x31303031
248 #define FW_MAGIC_WNR1000V2_VC   0x31303030
249 #define FW_MAGIC_WNDR3700       0x33373030
250 #define FW_MAGIC_WNDR3700V2     0x33373031
251
252 static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len)
253 {
254         struct uimage_header *header = (struct uimage_header *)buf;
255         uint8_t expected_type = IH_TYPE_FILESYSTEM;
256
257         switch be32_to_cpu(header->ih_magic) {
258         case FW_MAGIC_WNR612V2:
259         case FW_MAGIC_WNR1000V2:
260         case FW_MAGIC_WNR1000V2_VC:
261         case FW_MAGIC_WNR2000V3:
262         case FW_MAGIC_WNR2200:
263         case FW_MAGIC_WNDR3700:
264         case FW_MAGIC_WNDR3700V2:
265                 break;
266         case FW_MAGIC_WNR2000V4:
267                 expected_type = IH_TYPE_KERNEL;
268                 break;
269         default:
270                 return -EINVAL;
271         }
272
273         if (header->ih_os != IH_OS_LINUX ||
274             header->ih_type != expected_type)
275                 return -EINVAL;
276
277         return 0;
278 }
279
280 static int
281 mtdsplit_uimage_parse_netgear(struct mtd_info *master,
282                               struct mtd_partition **pparts,
283                               struct mtd_part_parser_data *data)
284 {
285         return __mtdsplit_parse_uimage(master, pparts, data,
286                                       uimage_verify_wndr3700);
287 }
288
289 static struct mtd_part_parser uimage_netgear_parser = {
290         .owner = THIS_MODULE,
291         .name = "netgear-fw",
292         .parse_fn = mtdsplit_uimage_parse_netgear,
293         .type = MTD_PARSER_TYPE_FIRMWARE,
294 };
295
296 /**************************************************
297  * Edimax
298  **************************************************/
299
300 #define FW_EDIMAX_OFFSET        20
301 #define FW_MAGIC_EDIMAX         0x43535953
302
303 static ssize_t uimage_find_edimax(u_char *buf, size_t len)
304 {
305         struct uimage_header *header;
306
307         if (len < FW_EDIMAX_OFFSET + sizeof(*header)) {
308                 pr_err("Buffer too small for checking Edimax header\n");
309                 return -ENOSPC;
310         }
311
312         header = (struct uimage_header *)(buf + FW_EDIMAX_OFFSET);
313
314         switch be32_to_cpu(header->ih_magic) {
315         case FW_MAGIC_EDIMAX:
316                 break;
317         default:
318                 return -EINVAL;
319         }
320
321         if (header->ih_os != IH_OS_LINUX ||
322             header->ih_type != IH_TYPE_FILESYSTEM)
323                 return -EINVAL;
324
325         return FW_EDIMAX_OFFSET;
326 }
327
328 static int
329 mtdsplit_uimage_parse_edimax(struct mtd_info *master,
330                               struct mtd_partition **pparts,
331                               struct mtd_part_parser_data *data)
332 {
333         return __mtdsplit_parse_uimage(master, pparts, data,
334                                        uimage_find_edimax);
335 }
336
337 static struct mtd_part_parser uimage_edimax_parser = {
338         .owner = THIS_MODULE,
339         .name = "edimax-fw",
340         .parse_fn = mtdsplit_uimage_parse_edimax,
341         .type = MTD_PARSER_TYPE_FIRMWARE,
342 };
343
344 /**************************************************
345  * Init
346  **************************************************/
347
348 static int __init mtdsplit_uimage_init(void)
349 {
350         register_mtd_parser(&uimage_generic_parser);
351         register_mtd_parser(&uimage_netgear_parser);
352         register_mtd_parser(&uimage_edimax_parser);
353
354         return 0;
355 }
356
357 module_init(mtdsplit_uimage_init);