kernel: mtdsplit_uimage: fix Edimax parser
[openwrt/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 /*
24 * uimage_header itself is only 64B, but it may be prepended with another data.
25 * Currently the biggest size is for Edimax devices: 20B + 64B
26 */
27 #define MAX_HEADER_LEN 84
28
29 #define IH_MAGIC 0x27051956 /* Image Magic Number */
30 #define IH_NMLEN 32 /* Image Name Length */
31
32 #define IH_OS_LINUX 5 /* Linux */
33
34 #define IH_TYPE_KERNEL 2 /* OS Kernel Image */
35 #define IH_TYPE_FILESYSTEM 7 /* Filesystem Image */
36
37 /*
38 * Legacy format image header,
39 * all data in network byte order (aka natural aka bigendian).
40 */
41 struct uimage_header {
42 uint32_t ih_magic; /* Image Header Magic Number */
43 uint32_t ih_hcrc; /* Image Header CRC Checksum */
44 uint32_t ih_time; /* Image Creation Timestamp */
45 uint32_t ih_size; /* Image Data Size */
46 uint32_t ih_load; /* Data Load Address */
47 uint32_t ih_ep; /* Entry Point Address */
48 uint32_t ih_dcrc; /* Image Data CRC Checksum */
49 uint8_t ih_os; /* Operating System */
50 uint8_t ih_arch; /* CPU architecture */
51 uint8_t ih_type; /* Image Type */
52 uint8_t ih_comp; /* Compression Type */
53 uint8_t ih_name[IH_NMLEN]; /* Image Name */
54 };
55
56 static int
57 read_uimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
58 size_t header_len)
59 {
60 size_t retlen;
61 int ret;
62
63 ret = mtd_read(mtd, offset, header_len, &retlen, buf);
64 if (ret) {
65 pr_debug("read error in \"%s\"\n", mtd->name);
66 return ret;
67 }
68
69 if (retlen != header_len) {
70 pr_debug("short read in \"%s\"\n", mtd->name);
71 return -EIO;
72 }
73
74 return 0;
75 }
76
77 /**
78 * __mtdsplit_parse_uimage - scan partition and create kernel + rootfs parts
79 *
80 * @find_header: function to call for a block of data that will return offset
81 * of a valid uImage header if found
82 */
83 static int __mtdsplit_parse_uimage(struct mtd_info *master,
84 struct mtd_partition **pparts,
85 struct mtd_part_parser_data *data,
86 ssize_t (*find_header)(u_char *buf, size_t len))
87 {
88 struct mtd_partition *parts;
89 u_char *buf;
90 int nr_parts;
91 size_t offset;
92 size_t uimage_offset;
93 size_t uimage_size = 0;
94 size_t rootfs_offset;
95 size_t rootfs_size = 0;
96 int uimage_part, rf_part;
97 int ret;
98 enum mtdsplit_part_type type;
99
100 nr_parts = 2;
101 parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
102 if (!parts)
103 return -ENOMEM;
104
105 buf = vmalloc(MAX_HEADER_LEN);
106 if (!buf) {
107 ret = -ENOMEM;
108 goto err_free_parts;
109 }
110
111 /* find uImage on erase block boundaries */
112 for (offset = 0; offset < master->size; offset += master->erasesize) {
113 struct uimage_header *header;
114
115 uimage_size = 0;
116
117 ret = read_uimage_header(master, offset, buf, MAX_HEADER_LEN);
118 if (ret)
119 continue;
120
121 ret = find_header(buf, MAX_HEADER_LEN);
122 if (ret < 0) {
123 pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
124 master->name, (unsigned long long) offset);
125 continue;
126 }
127 header = (struct uimage_header *)(buf + ret);
128
129 uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size) + ret;
130 if ((offset + uimage_size) > master->size) {
131 pr_debug("uImage exceeds MTD device \"%s\"\n",
132 master->name);
133 continue;
134 }
135 break;
136 }
137
138 if (uimage_size == 0) {
139 pr_debug("no uImage found in \"%s\"\n", master->name);
140 ret = -ENODEV;
141 goto err_free_buf;
142 }
143
144 uimage_offset = offset;
145
146 if (uimage_offset == 0) {
147 uimage_part = 0;
148 rf_part = 1;
149
150 /* find the roots after the uImage */
151 ret = mtd_find_rootfs_from(master, uimage_offset + uimage_size,
152 master->size, &rootfs_offset, &type);
153 if (ret) {
154 pr_debug("no rootfs after uImage in \"%s\"\n",
155 master->name);
156 goto err_free_buf;
157 }
158
159 rootfs_size = master->size - rootfs_offset;
160 uimage_size = rootfs_offset - uimage_offset;
161 } else {
162 rf_part = 0;
163 uimage_part = 1;
164
165 /* check rootfs presence at offset 0 */
166 ret = mtd_check_rootfs_magic(master, 0, &type);
167 if (ret) {
168 pr_debug("no rootfs before uImage in \"%s\"\n",
169 master->name);
170 goto err_free_buf;
171 }
172
173 rootfs_offset = 0;
174 rootfs_size = uimage_offset;
175 }
176
177 if (rootfs_size == 0) {
178 pr_debug("no rootfs found in \"%s\"\n", master->name);
179 ret = -ENODEV;
180 goto err_free_buf;
181 }
182
183 parts[uimage_part].name = KERNEL_PART_NAME;
184 parts[uimage_part].offset = uimage_offset;
185 parts[uimage_part].size = uimage_size;
186
187 if (type == MTDSPLIT_PART_TYPE_UBI)
188 parts[rf_part].name = UBI_PART_NAME;
189 else
190 parts[rf_part].name = ROOTFS_PART_NAME;
191 parts[rf_part].offset = rootfs_offset;
192 parts[rf_part].size = rootfs_size;
193
194 vfree(buf);
195
196 *pparts = parts;
197 return nr_parts;
198
199 err_free_buf:
200 vfree(buf);
201
202 err_free_parts:
203 kfree(parts);
204 return ret;
205 }
206
207 static ssize_t uimage_verify_default(u_char *buf, size_t len)
208 {
209 struct uimage_header *header = (struct uimage_header *)buf;
210
211 /* default sanity checks */
212 if (be32_to_cpu(header->ih_magic) != IH_MAGIC) {
213 pr_debug("invalid uImage magic: %08x\n",
214 be32_to_cpu(header->ih_magic));
215 return -EINVAL;
216 }
217
218 if (header->ih_os != IH_OS_LINUX) {
219 pr_debug("invalid uImage OS: %08x\n",
220 be32_to_cpu(header->ih_os));
221 return -EINVAL;
222 }
223
224 if (header->ih_type != IH_TYPE_KERNEL) {
225 pr_debug("invalid uImage type: %08x\n",
226 be32_to_cpu(header->ih_type));
227 return -EINVAL;
228 }
229
230 return 0;
231 }
232
233 static int
234 mtdsplit_uimage_parse_generic(struct mtd_info *master,
235 struct mtd_partition **pparts,
236 struct mtd_part_parser_data *data)
237 {
238 return __mtdsplit_parse_uimage(master, pparts, data,
239 uimage_verify_default);
240 }
241
242 static struct mtd_part_parser uimage_generic_parser = {
243 .owner = THIS_MODULE,
244 .name = "uimage-fw",
245 .parse_fn = mtdsplit_uimage_parse_generic,
246 .type = MTD_PARSER_TYPE_FIRMWARE,
247 };
248
249 #define FW_MAGIC_WNR2000V3 0x32303033
250 #define FW_MAGIC_WNR2000V4 0x32303034
251 #define FW_MAGIC_WNR2200 0x32323030
252 #define FW_MAGIC_WNR612V2 0x32303631
253 #define FW_MAGIC_WNR1000V2 0x31303031
254 #define FW_MAGIC_WNR1000V2_VC 0x31303030
255 #define FW_MAGIC_WNDR3700 0x33373030
256 #define FW_MAGIC_WNDR3700V2 0x33373031
257 #define FW_MAGIC_WPN824N 0x31313030
258
259 static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len)
260 {
261 struct uimage_header *header = (struct uimage_header *)buf;
262 uint8_t expected_type = IH_TYPE_FILESYSTEM;
263
264 switch be32_to_cpu(header->ih_magic) {
265 case FW_MAGIC_WNR612V2:
266 case FW_MAGIC_WNR1000V2:
267 case FW_MAGIC_WNR1000V2_VC:
268 case FW_MAGIC_WNR2000V3:
269 case FW_MAGIC_WNR2200:
270 case FW_MAGIC_WNDR3700:
271 case FW_MAGIC_WNDR3700V2:
272 case FW_MAGIC_WPN824N:
273 break;
274 case FW_MAGIC_WNR2000V4:
275 expected_type = IH_TYPE_KERNEL;
276 break;
277 default:
278 return -EINVAL;
279 }
280
281 if (header->ih_os != IH_OS_LINUX ||
282 header->ih_type != expected_type)
283 return -EINVAL;
284
285 return 0;
286 }
287
288 static int
289 mtdsplit_uimage_parse_netgear(struct mtd_info *master,
290 struct mtd_partition **pparts,
291 struct mtd_part_parser_data *data)
292 {
293 return __mtdsplit_parse_uimage(master, pparts, data,
294 uimage_verify_wndr3700);
295 }
296
297 static struct mtd_part_parser uimage_netgear_parser = {
298 .owner = THIS_MODULE,
299 .name = "netgear-fw",
300 .parse_fn = mtdsplit_uimage_parse_netgear,
301 .type = MTD_PARSER_TYPE_FIRMWARE,
302 };
303
304 /**************************************************
305 * Edimax
306 **************************************************/
307
308 #define FW_EDIMAX_OFFSET 20
309 #define FW_MAGIC_EDIMAX 0x43535953
310
311 static ssize_t uimage_find_edimax(u_char *buf, size_t len)
312 {
313 u32 *magic;
314
315 if (len < FW_EDIMAX_OFFSET + sizeof(struct uimage_header)) {
316 pr_err("Buffer too small for checking Edimax header\n");
317 return -ENOSPC;
318 }
319
320 magic = (u32 *)buf;
321 if (be32_to_cpu(*magic) != FW_MAGIC_EDIMAX)
322 return -EINVAL;
323
324 if (!uimage_verify_default(buf + FW_EDIMAX_OFFSET, len))
325 return FW_EDIMAX_OFFSET;
326
327 return -EINVAL;
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);