remove empty directories
[openwrt.git] / target / linux / generic / files / drivers / mtd / 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 static int __mtdsplit_parse_uimage(struct mtd_info *master,
75                                    struct mtd_partition **pparts,
76                                    struct mtd_part_parser_data *data,
77                                    bool (*verify)(struct uimage_header *hdr))
78 {
79         struct mtd_partition *parts;
80         struct uimage_header *header;
81         int nr_parts;
82         size_t offset;
83         size_t uimage_offset;
84         size_t uimage_size = 0;
85         size_t rootfs_offset;
86         size_t rootfs_size = 0;
87         int uimage_part, rf_part;
88         int ret;
89
90         nr_parts = 2;
91         parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
92         if (!parts)
93                 return -ENOMEM;
94
95         header = vmalloc(sizeof(*header));
96         if (!header) {
97                 ret = -ENOMEM;
98                 goto err_free_parts;
99         }
100
101         /* find uImage on erase block boundaries */
102         for (offset = 0; offset < master->size; offset += master->erasesize) {
103                 uimage_size = 0;
104
105                 ret = read_uimage_header(master, offset, header);
106                 if (ret)
107                         continue;
108
109                 if (!verify(header)) {
110                         pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
111                                  master->name, (unsigned long long) offset);
112                         continue;
113                 }
114
115                 uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size);
116                 if ((offset + uimage_size) > master->size) {
117                         pr_debug("uImage exceeds MTD device \"%s\"\n",
118                                  master->name);
119                         continue;
120                 }
121                 break;
122         }
123
124         if (uimage_size == 0) {
125                 pr_debug("no uImage found in \"%s\"\n", master->name);
126                 ret = -ENODEV;
127                 goto err_free_header;
128         }
129
130         uimage_offset = offset;
131
132         if (uimage_offset == 0) {
133                 uimage_part = 0;
134                 rf_part = 1;
135
136                 /* find the roots after the uImage */
137                 ret = mtd_find_rootfs_from(master,
138                                            uimage_offset + uimage_size,
139                                            master->size,
140                                            &rootfs_offset);
141                 if (ret) {
142                         pr_debug("no rootfs after uImage in \"%s\"\n",
143                                  master->name);
144                         goto err_free_header;
145                 }
146
147                 rootfs_size = master->size - rootfs_offset;
148                 uimage_size = rootfs_offset - uimage_offset;
149         } else {
150                 rf_part = 0;
151                 uimage_part = 1;
152
153                 /* check rootfs presence at offset 0 */
154                 ret = mtd_check_rootfs_magic(master, 0);
155                 if (ret) {
156                         pr_debug("no rootfs before uImage in \"%s\"\n",
157                                  master->name);
158                         goto err_free_header;
159                 }
160
161                 rootfs_offset = 0;
162                 rootfs_size = uimage_offset;
163         }
164
165         if (rootfs_size == 0) {
166                 pr_debug("no rootfs found in \"%s\"\n", master->name);
167                 ret = -ENODEV;
168                 goto err_free_header;
169         }
170
171         parts[uimage_part].name = KERNEL_PART_NAME;
172         parts[uimage_part].offset = uimage_offset;
173         parts[uimage_part].size = uimage_size;
174
175         parts[rf_part].name = ROOTFS_PART_NAME;
176         parts[rf_part].offset = rootfs_offset;
177         parts[rf_part].size = rootfs_size;
178
179         vfree(header);
180
181         *pparts = parts;
182         return nr_parts;
183
184 err_free_header:
185         vfree(header);
186
187 err_free_parts:
188         kfree(parts);
189         return ret;
190 }
191
192 static bool uimage_verify_default(struct uimage_header *header)
193 {
194         /* default sanity checks */
195         if (be32_to_cpu(header->ih_magic) != IH_MAGIC) {
196                 pr_debug("invalid uImage magic: %08x\n",
197                          be32_to_cpu(header->ih_magic));
198                 return false;
199         }
200
201         if (header->ih_os != IH_OS_LINUX) {
202                 pr_debug("invalid uImage OS: %08x\n",
203                          be32_to_cpu(header->ih_os));
204                 return false;
205         }
206
207         if (header->ih_type != IH_TYPE_KERNEL) {
208                 pr_debug("invalid uImage type: %08x\n",
209                          be32_to_cpu(header->ih_type));
210                 return false;
211         }
212
213         return true;
214 }
215
216 static int
217 mtdsplit_uimage_parse_generic(struct mtd_info *master,
218                               struct mtd_partition **pparts,
219                               struct mtd_part_parser_data *data)
220 {
221         return __mtdsplit_parse_uimage(master, pparts, data,
222                                       uimage_verify_default);
223 }
224
225 static struct mtd_part_parser uimage_generic_parser = {
226         .owner = THIS_MODULE,
227         .name = "uimage-fw",
228         .parse_fn = mtdsplit_uimage_parse_generic,
229         .type = MTD_PARSER_TYPE_FIRMWARE,
230 };
231
232 #define FW_MAGIC_WNR2000V3      0x32303033
233 #define FW_MAGIC_WNR2000V4      0x32303034
234 #define FW_MAGIC_WNR2200        0x32323030
235 #define FW_MAGIC_WNR612V2       0x32303631
236 #define FW_MAGIC_WNDR3700       0x33373030
237 #define FW_MAGIC_WNDR3700V2     0x33373031
238
239 static bool uimage_verify_wndr3700(struct uimage_header *header)
240 {
241         uint8_t expected_type = IH_TYPE_FILESYSTEM;
242         switch be32_to_cpu(header->ih_magic) {
243         case FW_MAGIC_WNR612V2:
244         case FW_MAGIC_WNR2000V3:
245         case FW_MAGIC_WNR2200:
246         case FW_MAGIC_WNDR3700:
247         case FW_MAGIC_WNDR3700V2:
248                 break;
249         case FW_MAGIC_WNR2000V4:
250                 expected_type = IH_TYPE_KERNEL;
251                 break;
252         default:
253                 return false;
254         }
255
256         if (header->ih_os != IH_OS_LINUX ||
257             header->ih_type != expected_type)
258                 return false;
259
260         return true;
261 }
262
263 static int
264 mtdsplit_uimage_parse_netgear(struct mtd_info *master,
265                               struct mtd_partition **pparts,
266                               struct mtd_part_parser_data *data)
267 {
268         return __mtdsplit_parse_uimage(master, pparts, data,
269                                       uimage_verify_wndr3700);
270 }
271
272 static struct mtd_part_parser uimage_netgear_parser = {
273         .owner = THIS_MODULE,
274         .name = "netgear-fw",
275         .parse_fn = mtdsplit_uimage_parse_netgear,
276         .type = MTD_PARSER_TYPE_FIRMWARE,
277 };
278
279 static int __init mtdsplit_uimage_init(void)
280 {
281         register_mtd_parser(&uimage_generic_parser);
282         register_mtd_parser(&uimage_netgear_parser);
283
284         return 0;
285 }
286
287 module_init(mtdsplit_uimage_init);