kernel: mtdsplit_uimage: use separated buffer for reading data
[openwrt/svn-archive/archive.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);