firmware-utils/mkplinkfw: add TL-WR1043ND v2 support
[openwrt.git] / tools / firmware-utils / src / mktplinkfw.c
1 /*
2  * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
3  *
4  * This tool was based on:
5  *   TP-Link WR941 V2 firmware checksum fixing tool.
6  *   Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License version 2 as published
10  * by the Free Software Foundation.
11  *
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdint.h>
17 #include <string.h>
18 #include <unistd.h>     /* for unlink() */
19 #include <libgen.h>
20 #include <getopt.h>     /* for getopt() */
21 #include <stdarg.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24
25 #include <arpa/inet.h>
26 #include <netinet/in.h>
27
28 #include "md5.h"
29
30 #define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
31
32 #define HEADER_VERSION_V1       0x01000000
33 #define HWID_TL_MR10U_V1        0x00100101
34 #define HWID_TL_MR13U_V1        0x00130101
35 #define HWID_TL_MR3020_V1       0x30200001
36 #define HWID_TL_MR3220_V1       0x32200001
37 #define HWID_TL_MR3220_V2       0x32200002
38 #define HWID_TL_MR3420_V1       0x34200001
39 #define HWID_TL_MR3420_V2       0x34200002
40 #define HWID_TL_WA701N_V1       0x07010001
41 #define HWID_TL_WA7510N_V1      0x75100001
42 #define HWID_TL_WA801ND_V1      0x08010001
43 #define HWID_TL_WA830RE_V1      0x08300010
44 #define HWID_TL_WA830RE_V2      0x08300002
45 #define HWID_TL_WA901ND_V1      0x09010001
46 #define HWID_TL_WA901ND_V2      0x09010002
47 #define HWID_TL_WDR4900_V1      0x49000001
48 #define HWID_TL_WR703N_V1       0x07030101
49 #define HWID_TL_WR720N_V3       0x07200103
50 #define HWID_TL_WR741ND_V1      0x07410001
51 #define HWID_TL_WR741ND_V4      0x07410004
52 #define HWID_TL_WR740N_V1       0x07400001
53 #define HWID_TL_WR740N_V3       0x07400003
54 #define HWID_TL_WR743ND_V1      0x07430001
55 #define HWID_TL_WR743ND_V2      0x07430002
56 #define HWID_TL_WR841N_V1_5     0x08410002
57 #define HWID_TL_WR841ND_V3      0x08410003
58 #define HWID_TL_WR841ND_V5      0x08410005
59 #define HWID_TL_WR841ND_V7      0x08410007
60 #define HWID_TL_WR941ND_V2      0x09410002
61 #define HWID_TL_WR941ND_V4      0x09410004
62 #define HWID_TL_WR1043ND_V1     0x10430001
63 #define HWID_TL_WR1043ND_V2     0x10430002
64 #define HWID_TL_WR1041N_V2      0x10410002
65 #define HWID_TL_WR2543N_V1      0x25430001
66
67 #define MD5SUM_LEN      16
68
69 struct file_info {
70         char            *file_name;     /* name of the file */
71         uint32_t        file_size;      /* length of the file */
72 };
73
74 struct fw_header {
75         uint32_t        version;        /* header version */
76         char            vendor_name[24];
77         char            fw_version[36];
78         uint32_t        hw_id;          /* hardware id */
79         uint32_t        hw_rev;         /* hardware revision */
80         uint32_t        unk1;
81         uint8_t         md5sum1[MD5SUM_LEN];
82         uint32_t        unk2;
83         uint8_t         md5sum2[MD5SUM_LEN];
84         uint32_t        unk3;
85         uint32_t        kernel_la;      /* kernel load address */
86         uint32_t        kernel_ep;      /* kernel entry point */
87         uint32_t        fw_length;      /* total length of the firmware */
88         uint32_t        kernel_ofs;     /* kernel data offset */
89         uint32_t        kernel_len;     /* kernel data length */
90         uint32_t        rootfs_ofs;     /* rootfs data offset */
91         uint32_t        rootfs_len;     /* rootfs data length */
92         uint32_t        boot_ofs;       /* bootloader data offset */
93         uint32_t        boot_len;       /* bootloader data length */
94         uint16_t        ver_hi;
95         uint16_t        ver_mid;
96         uint16_t        ver_lo;
97         uint8_t         pad[354];
98 } __attribute__ ((packed));
99
100 struct flash_layout {
101         char            *id;
102         uint32_t        fw_max_len;
103         uint32_t        kernel_la;
104         uint32_t        kernel_ep;
105         uint32_t        rootfs_ofs;
106 };
107
108 struct board_info {
109         char            *id;
110         uint32_t        hw_id;
111         uint32_t        hw_rev;
112         char            *layout_id;
113 };
114
115 /*
116  * Globals
117  */
118 static char *ofname;
119 static char *progname;
120 static char *vendor = "TP-LINK Technologies";
121 static char *version = "ver. 1.0";
122 static char *fw_ver = "0.0.0";
123
124 static char *board_id;
125 static struct board_info *board;
126 static char *layout_id;
127 static struct flash_layout *layout;
128 static char *opt_hw_id;
129 static uint32_t hw_id;
130 static char *opt_hw_rev;
131 static uint32_t hw_rev;
132 static int fw_ver_lo;
133 static int fw_ver_mid;
134 static int fw_ver_hi;
135 static struct file_info kernel_info;
136 static uint32_t kernel_la = 0;
137 static uint32_t kernel_ep = 0;
138 static uint32_t kernel_len = 0;
139 static struct file_info rootfs_info;
140 static uint32_t rootfs_ofs = 0;
141 static uint32_t rootfs_align;
142 static struct file_info boot_info;
143 static int combined;
144 static int strip_padding;
145 static int add_jffs2_eof;
146 static unsigned char jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
147
148 static struct file_info inspect_info;
149 static int extract = 0;
150
151 char md5salt_normal[MD5SUM_LEN] = {
152         0xdc, 0xd7, 0x3a, 0xa5, 0xc3, 0x95, 0x98, 0xfb,
153         0xdd, 0xf9, 0xe7, 0xf4, 0x0e, 0xae, 0x47, 0x38,
154 };
155
156 char md5salt_boot[MD5SUM_LEN] = {
157         0x8c, 0xef, 0x33, 0x5b, 0xd5, 0xc5, 0xce, 0xfa,
158         0xa7, 0x9c, 0x28, 0xda, 0xb2, 0xe9, 0x0f, 0x42,
159 };
160
161 static struct flash_layout layouts[] = {
162         {
163                 .id             = "4M",
164                 .fw_max_len     = 0x3c0000,
165                 .kernel_la      = 0x80060000,
166                 .kernel_ep      = 0x80060000,
167                 .rootfs_ofs     = 0x140000,
168         }, {
169                 .id             = "4Mlzma",
170                 .fw_max_len     = 0x3c0000,
171                 .kernel_la      = 0x80060000,
172                 .kernel_ep      = 0x80060000,
173                 .rootfs_ofs     = 0x100000,
174         }, {
175                 .id             = "8M",
176                 .fw_max_len     = 0x7c0000,
177                 .kernel_la      = 0x80060000,
178                 .kernel_ep      = 0x80060000,
179                 .rootfs_ofs     = 0x140000,
180         }, {
181                 .id             = "8Mlzma",
182                 .fw_max_len     = 0x7c0000,
183                 .kernel_la      = 0x80060000,
184                 .kernel_ep      = 0x80060000,
185                 .rootfs_ofs     = 0x100000,
186         }, {
187                 .id             = "16Mppc",
188                 .fw_max_len     = 0xf80000,
189                 .kernel_la      = 0x00000000,
190                 .kernel_ep      = 0xc0000000,
191                 .rootfs_ofs     = 0x2a0000,
192         }, {
193                 /* terminating entry */
194         }
195 };
196
197 static struct board_info boards[] = {
198         {
199                 .id             = "TL-MR10Uv1",
200                 .hw_id          = HWID_TL_MR10U_V1,
201                 .hw_rev         = 1,
202                 .layout_id      = "4Mlzma",
203         }, {
204                 .id             = "TL-MR13Uv1",
205                 .hw_id          = HWID_TL_MR13U_V1,
206                 .hw_rev         = 1,
207                 .layout_id      = "4Mlzma",
208         }, {
209                 .id             = "TL-MR3020v1",
210                 .hw_id          = HWID_TL_MR3020_V1,
211                 .hw_rev         = 1,
212                 .layout_id      = "4Mlzma",
213         }, {
214                 .id             = "TL-MR3220v1",
215                 .hw_id          = HWID_TL_MR3220_V1,
216                 .hw_rev         = 1,
217                 .layout_id      = "4M",
218         }, {
219                 .id             = "TL-MR3220v2",
220                 .hw_id          = HWID_TL_MR3220_V2,
221                 .hw_rev         = 1,
222                 .layout_id      = "4Mlzma",
223         }, {
224                 .id             = "TL-MR3420v1",
225                 .hw_id          = HWID_TL_MR3420_V1,
226                 .hw_rev         = 1,
227                 .layout_id      = "4M",
228         }, {
229                 .id             = "TL-MR3420v2",
230                 .hw_id          = HWID_TL_MR3420_V2,
231                 .hw_rev         = 1,
232                 .layout_id      = "4Mlzma",
233         }, {
234                 .id             = "TL-WA701Nv1",
235                 .hw_id          = HWID_TL_WA701N_V1,
236                 .hw_rev         = 1,
237                 .layout_id      = "4M",
238         }, {
239                 .id             = "TL-WA7510N",
240                 .hw_id          = HWID_TL_WA7510N_V1,
241                 .hw_rev         = 1,
242                 .layout_id      = "4M",
243         }, {
244                 .id             = "TL-WA801NDv1",
245                 .hw_id          = HWID_TL_WA801ND_V1,
246                 .hw_rev         = 1,
247                 .layout_id      = "4M",
248         }, {
249                 .id             = "TL-WA830REv1",
250                 .hw_id          = HWID_TL_WA830RE_V1,
251                 .hw_rev         = 1,
252                 .layout_id      = "4M",
253         }, {
254                 .id             = "TL-WA830REv2",
255                 .hw_id          = HWID_TL_WA830RE_V2,
256                 .hw_rev         = 1,
257                 .layout_id      = "4M",
258         }, {
259                 .id             = "TL-WA901NDv1",
260                 .hw_id          = HWID_TL_WA901ND_V1,
261                 .hw_rev         = 1,
262                 .layout_id      = "4M",
263         }, {
264                 .id             = "TL-WA901NDv2",
265                 .hw_id          = HWID_TL_WA901ND_V2,
266                 .hw_rev         = 1,
267                 .layout_id      = "4M",
268         }, {
269                 .id             = "TL-WDR4900v1",
270                 .hw_id          = HWID_TL_WDR4900_V1,
271                 .hw_rev         = 1,
272                 .layout_id      = "16Mppc",
273         }, {
274                 .id             = "TL-WR741NDv1",
275                 .hw_id          = HWID_TL_WR741ND_V1,
276                 .hw_rev         = 1,
277                 .layout_id      = "4M",
278         }, {
279                 .id             = "TL-WR741NDv4",
280                 .hw_id          = HWID_TL_WR741ND_V4,
281                 .hw_rev         = 1,
282                 .layout_id      = "4Mlzma",
283         }, {
284                 .id             = "TL-WR740Nv1",
285                 .hw_id          = HWID_TL_WR740N_V1,
286                 .hw_rev         = 1,
287                 .layout_id      = "4M",
288         }, {
289                 .id             = "TL-WR740Nv3",
290                 .hw_id          = HWID_TL_WR740N_V3,
291                 .hw_rev         = 1,
292                 .layout_id      = "4M",
293         }, {
294                 .id             = "TL-WR743NDv1",
295                 .hw_id          = HWID_TL_WR743ND_V1,
296                 .hw_rev         = 1,
297                 .layout_id      = "4M",
298         }, {
299                 .id             = "TL-WR743NDv2",
300                 .hw_id          = HWID_TL_WR743ND_V2,
301                 .hw_rev         = 1,
302                 .layout_id      = "4Mlzma",
303         }, {
304                 .id             = "TL-WR841Nv1.5",
305                 .hw_id          = HWID_TL_WR841N_V1_5,
306                 .hw_rev         = 2,
307                 .layout_id      = "4M",
308         }, {
309                 .id             = "TL-WR841NDv3",
310                 .hw_id          = HWID_TL_WR841ND_V3,
311                 .hw_rev         = 3,
312                 .layout_id      = "4M",
313         }, {
314                 .id             = "TL-WR841NDv5",
315                 .hw_id          = HWID_TL_WR841ND_V5,
316                 .hw_rev         = 1,
317                 .layout_id      = "4M",
318         }, {
319                 .id             = "TL-WR841NDv7",
320                 .hw_id          = HWID_TL_WR841ND_V7,
321                 .hw_rev         = 1,
322                 .layout_id      = "4M",
323         }, {
324                 .id             = "TL-WR941NDv2",
325                 .hw_id          = HWID_TL_WR941ND_V2,
326                 .hw_rev         = 2,
327                 .layout_id      = "4M",
328         }, {
329                 .id             = "TL-WR941NDv4",
330                 .hw_id          = HWID_TL_WR941ND_V4,
331                 .hw_rev         = 1,
332                 .layout_id      = "4M",
333         }, {
334                 .id             = "TL-WR1041Nv2",
335                 .hw_id          = HWID_TL_WR1041N_V2,
336                 .hw_rev         = 1,
337                 .layout_id      = "4Mlzma",
338         }, {
339                 .id             = "TL-WR1043NDv1",
340                 .hw_id          = HWID_TL_WR1043ND_V1,
341                 .hw_rev         = 1,
342                 .layout_id      = "8M",
343         }, {
344                 .id             = "TL-WR1043NDv2",
345                 .hw_id          = HWID_TL_WR1043ND_V2,
346                 .hw_rev         = 1,
347                 .layout_id      = "8Mlzma",
348         }, {
349                 .id             = "TL-WR2543Nv1",
350                 .hw_id          = HWID_TL_WR2543N_V1,
351                 .hw_rev         = 1,
352                 .layout_id      = "8Mlzma",
353         }, {
354                 .id             = "TL-WR703Nv1",
355                 .hw_id          = HWID_TL_WR703N_V1,
356                 .hw_rev         = 1,
357                 .layout_id      = "4Mlzma",
358         }, {
359                 .id             = "TL-WR720Nv3",
360                 .hw_id          = HWID_TL_WR720N_V3,
361                 .hw_rev         = 1,
362                 .layout_id      = "4Mlzma",
363         }, {
364                 /* terminating entry */
365         }
366 };
367
368 /*
369  * Message macros
370  */
371 #define ERR(fmt, ...) do { \
372         fflush(0); \
373         fprintf(stderr, "[%s] *** error: " fmt "\n", \
374                         progname, ## __VA_ARGS__ ); \
375 } while (0)
376
377 #define ERRS(fmt, ...) do { \
378         int save = errno; \
379         fflush(0); \
380         fprintf(stderr, "[%s] *** error: " fmt "\n", \
381                         progname, ## __VA_ARGS__, strerror(save)); \
382 } while (0)
383
384 #define DBG(fmt, ...) do { \
385         fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \
386 } while (0)
387
388 static struct board_info *find_board(char *id)
389 {
390         struct board_info *ret;
391         struct board_info *board;
392
393         ret = NULL;
394         for (board = boards; board->id != NULL; board++){
395                 if (strcasecmp(id, board->id) == 0) {
396                         ret = board;
397                         break;
398                 }
399         };
400
401         return ret;
402 }
403
404 static struct board_info *find_board_by_hwid(uint32_t hw_id)
405 {
406         struct board_info *board;
407
408         for (board = boards; board->id != NULL; board++) {
409                 if (hw_id == board->hw_id)
410                         return board;
411         };
412
413         return NULL;
414 }
415
416 static struct flash_layout *find_layout(char *id)
417 {
418         struct flash_layout *ret;
419         struct flash_layout *l;
420
421         ret = NULL;
422         for (l = layouts; l->id != NULL; l++){
423                 if (strcasecmp(id, l->id) == 0) {
424                         ret = l;
425                         break;
426                 }
427         };
428
429         return ret;
430 }
431
432 static void usage(int status)
433 {
434         FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
435         struct board_info *board;
436
437         fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
438         fprintf(stream,
439 "\n"
440 "Options:\n"
441 "  -B <board>      create image for the board specified with <board>\n"
442 "  -c              use combined kernel image\n"
443 "  -E <ep>         overwrite kernel entry point with <ep> (hexval prefixed with 0x)\n"
444 "  -L <la>         overwrite kernel load address with <la> (hexval prefixed with 0x)\n"
445 "  -H <hwid>       use hardware id specified with <hwid>\n"
446 "  -W <hwrev>      use hardware revision specified with <hwrev>\n"
447 "  -F <id>         use flash layout specified with <id>\n"
448 "  -k <file>       read kernel image from the file <file>\n"
449 "  -r <file>       read rootfs image from the file <file>\n"
450 "  -a <align>      align the rootfs start on an <align> bytes boundary\n"
451 "  -R <offset>     overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n"
452 "  -o <file>       write output to the file <file>\n"
453 "  -s              strip padding from the end of the image\n"
454 "  -j              add jffs2 end-of-filesystem markers\n"
455 "  -N <vendor>     set image vendor to <vendor>\n"
456 "  -V <version>    set image version to <version>\n"
457 "  -v <version>    set firmware version to <version>\n"
458 "  -i <file>       inspect given firmware file <file>\n"
459 "  -x              extract kernel and rootfs while inspecting (requires -i)\n"
460 "  -h              show this screen\n"
461         );
462
463         exit(status);
464 }
465
466 static int get_md5(char *data, int size, char *md5)
467 {
468         MD5_CTX ctx;
469
470         MD5_Init(&ctx);
471         MD5_Update(&ctx, data, size);
472         MD5_Final(md5, &ctx);
473 }
474
475 static int get_file_stat(struct file_info *fdata)
476 {
477         struct stat st;
478         int res;
479
480         if (fdata->file_name == NULL)
481                 return 0;
482
483         res = stat(fdata->file_name, &st);
484         if (res){
485                 ERRS("stat failed on %s", fdata->file_name);
486                 return res;
487         }
488
489         fdata->file_size = st.st_size;
490         return 0;
491 }
492
493 static int read_to_buf(struct file_info *fdata, char *buf)
494 {
495         FILE *f;
496         int ret = EXIT_FAILURE;
497
498         f = fopen(fdata->file_name, "r");
499         if (f == NULL) {
500                 ERRS("could not open \"%s\" for reading", fdata->file_name);
501                 goto out;
502         }
503
504         errno = 0;
505         fread(buf, fdata->file_size, 1, f);
506         if (errno != 0) {
507                 ERRS("unable to read from file \"%s\"", fdata->file_name);
508                 goto out_close;
509         }
510
511         ret = EXIT_SUCCESS;
512
513  out_close:
514         fclose(f);
515  out:
516         return ret;
517 }
518
519 static int check_options(void)
520 {
521         int ret;
522
523         if (inspect_info.file_name) {
524                 ret = get_file_stat(&inspect_info);
525                 if (ret)
526                         return ret;
527
528                 return 0;
529         } else if (extract) {
530                 ERR("no firmware for inspection specified");
531                 return -1;
532         }
533
534         if (board_id == NULL && opt_hw_id == NULL) {
535                 ERR("either board or hardware id must be specified");
536                 return -1;
537         }
538
539         if (board_id) {
540                 board = find_board(board_id);
541                 if (board == NULL) {
542                         ERR("unknown/unsupported board id \"%s\"", board_id);
543                         return -1;
544                 }
545                 if (layout_id == NULL)
546                         layout_id = board->layout_id;
547
548                 hw_id = board->hw_id;
549                 hw_rev = board->hw_rev;
550         } else {
551                 if (layout_id == NULL) {
552                         ERR("flash layout is not specified");
553                         return -1;
554                 }
555                 hw_id = strtoul(opt_hw_id, NULL, 0);
556
557                 if (opt_hw_rev)
558                         hw_rev = strtoul(opt_hw_rev, NULL, 0);
559                 else
560                         hw_rev = 1;
561         }
562
563         layout = find_layout(layout_id);
564         if (layout == NULL) {
565                 ERR("unknown flash layout \"%s\"", layout_id);
566                 return -1;
567         }
568
569         if (!kernel_la)
570                 kernel_la = layout->kernel_la;
571         if (!kernel_ep)
572                 kernel_ep = layout->kernel_ep;
573         if (!rootfs_ofs)
574                 rootfs_ofs = layout->rootfs_ofs;
575
576         if (kernel_info.file_name == NULL) {
577                 ERR("no kernel image specified");
578                 return -1;
579         }
580
581         ret = get_file_stat(&kernel_info);
582         if (ret)
583                 return ret;
584
585         kernel_len = kernel_info.file_size;
586
587         if (combined) {
588                 if (kernel_info.file_size >
589                     layout->fw_max_len - sizeof(struct fw_header)) {
590                         ERR("kernel image is too big");
591                         return -1;
592                 }
593         } else {
594                 if (rootfs_info.file_name == NULL) {
595                         ERR("no rootfs image specified");
596                         return -1;
597                 }
598
599                 ret = get_file_stat(&rootfs_info);
600                 if (ret)
601                         return ret;
602
603                 if (rootfs_align) {
604                         kernel_len += sizeof(struct fw_header);
605                         kernel_len = ALIGN(kernel_len, rootfs_align);
606                         kernel_len -= sizeof(struct fw_header);
607
608                         DBG("kernel length aligned to %u", kernel_len);
609
610                         if (kernel_len + rootfs_info.file_size >
611                             layout->fw_max_len - sizeof(struct fw_header)) {
612                                 ERR("images are too big");
613                                 return -1;
614                         }
615                 } else {
616                         if (kernel_info.file_size >
617                             rootfs_ofs - sizeof(struct fw_header)) {
618                                 ERR("kernel image is too big");
619                                 return -1;
620                         }
621
622                         if (rootfs_info.file_size >
623                             (layout->fw_max_len - rootfs_ofs)) {
624                                 ERR("rootfs image is too big");
625                                 return -1;
626                         }
627                 }
628         }
629
630         if (ofname == NULL) {
631                 ERR("no output file specified");
632                 return -1;
633         }
634
635         ret = sscanf(fw_ver, "%d.%d.%d", &fw_ver_hi, &fw_ver_mid, &fw_ver_lo);
636         if (ret != 3) {
637                 ERR("invalid firmware version '%s'", fw_ver);
638                 return -1;
639         }
640
641         return 0;
642 }
643
644 static void fill_header(char *buf, int len)
645 {
646         struct fw_header *hdr = (struct fw_header *)buf;
647
648         memset(hdr, 0, sizeof(struct fw_header));
649
650         hdr->version = htonl(HEADER_VERSION_V1);
651         strncpy(hdr->vendor_name, vendor, sizeof(hdr->vendor_name));
652         strncpy(hdr->fw_version, version, sizeof(hdr->fw_version));
653         hdr->hw_id = htonl(hw_id);
654         hdr->hw_rev = htonl(hw_rev);
655
656         if (boot_info.file_size == 0)
657                 memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1));
658         else
659                 memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1));
660
661         hdr->kernel_la = htonl(kernel_la);
662         hdr->kernel_ep = htonl(kernel_ep);
663         hdr->fw_length = htonl(layout->fw_max_len);
664         hdr->kernel_ofs = htonl(sizeof(struct fw_header));
665         hdr->kernel_len = htonl(kernel_len);
666         if (!combined) {
667                 hdr->rootfs_ofs = htonl(rootfs_ofs);
668                 hdr->rootfs_len = htonl(rootfs_info.file_size);
669         }
670
671         hdr->ver_hi = htons(fw_ver_hi);
672         hdr->ver_mid = htons(fw_ver_mid);
673         hdr->ver_lo = htons(fw_ver_lo);
674
675         get_md5(buf, len, hdr->md5sum1);
676 }
677
678 static int pad_jffs2(char *buf, int currlen)
679 {
680         int len;
681         uint32_t pad_mask;
682
683         len = currlen;
684         pad_mask = (64 * 1024);
685         while ((len < layout->fw_max_len) && (pad_mask != 0)) {
686                 uint32_t mask;
687                 int i;
688
689                 for (i = 10; i < 32; i++) {
690                         mask = 1 << i;
691                         if (pad_mask & mask)
692                                 break;
693                 }
694
695                 len = ALIGN(len, mask);
696
697                 for (i = 10; i < 32; i++) {
698                         mask = 1 << i;
699                         if ((len & (mask - 1)) == 0)
700                                 pad_mask &= ~mask;
701                 }
702
703                 for (i = 0; i < sizeof(jffs2_eof_mark); i++)
704                         buf[len + i] = jffs2_eof_mark[i];
705
706                 len += sizeof(jffs2_eof_mark);
707         }
708
709         return len;
710 }
711
712 static int write_fw(char *data, int len)
713 {
714         FILE *f;
715         int ret = EXIT_FAILURE;
716
717         f = fopen(ofname, "w");
718         if (f == NULL) {
719                 ERRS("could not open \"%s\" for writing", ofname);
720                 goto out;
721         }
722
723         errno = 0;
724         fwrite(data, len, 1, f);
725         if (errno) {
726                 ERRS("unable to write output file");
727                 goto out_flush;
728         }
729
730         DBG("firmware file \"%s\" completed", ofname);
731
732         ret = EXIT_SUCCESS;
733
734  out_flush:
735         fflush(f);
736         fclose(f);
737         if (ret != EXIT_SUCCESS) {
738                 unlink(ofname);
739         }
740  out:
741         return ret;
742 }
743
744 static int build_fw(void)
745 {
746         int buflen;
747         char *buf;
748         char *p;
749         int ret = EXIT_FAILURE;
750         int writelen = 0;
751
752         buflen = layout->fw_max_len;
753
754         buf = malloc(buflen);
755         if (!buf) {
756                 ERR("no memory for buffer\n");
757                 goto out;
758         }
759
760         memset(buf, 0xff, buflen);
761         p = buf + sizeof(struct fw_header);
762         ret = read_to_buf(&kernel_info, p);
763         if (ret)
764                 goto out_free_buf;
765
766         writelen = sizeof(struct fw_header) + kernel_len;
767
768         if (!combined) {
769                 if (rootfs_align)
770                         p = buf + writelen;
771                 else
772                         p = buf + rootfs_ofs;
773
774                 ret = read_to_buf(&rootfs_info, p);
775                 if (ret)
776                         goto out_free_buf;
777
778                 if (rootfs_align)
779                         writelen += rootfs_info.file_size;
780                 else
781                         writelen = rootfs_ofs + rootfs_info.file_size;
782
783                 if (add_jffs2_eof)
784                         writelen = pad_jffs2(buf, writelen);
785         }
786
787         if (!strip_padding)
788                 writelen = buflen;
789
790         fill_header(buf, writelen);
791         ret = write_fw(buf, writelen);
792         if (ret)
793                 goto out_free_buf;
794
795         ret = EXIT_SUCCESS;
796
797  out_free_buf:
798         free(buf);
799  out:
800         return ret;
801 }
802
803 /* Helper functions to inspect_fw() representing different output formats */
804 static inline void inspect_fw_pstr(char *label, char *str)
805 {
806         printf("%-23s: %s\n", label, str);
807 }
808
809 static inline void inspect_fw_phex(char *label, uint32_t val)
810 {
811         printf("%-23s: 0x%08x\n", label, val);
812 }
813
814 static inline void inspect_fw_phexpost(char *label,
815                                        uint32_t val, char *post)
816 {
817         printf("%-23s: 0x%08x (%s)\n", label, val, post);
818 }
819
820 static inline void inspect_fw_phexdef(char *label,
821                                       uint32_t val, uint32_t defval)
822 {
823         printf("%-23s: 0x%08x                  ", label, val);
824
825         if (val == defval)
826                 printf("(== OpenWrt default)\n");
827         else
828                 printf("(OpenWrt default: 0x%08x)\n", defval);
829 }
830
831 static inline void inspect_fw_phexexp(char *label,
832                                       uint32_t val, uint32_t expval)
833 {
834         printf("%-23s: 0x%08x ", label, val);
835
836         if (val == expval)
837                 printf("(ok)\n");
838         else
839                 printf("(expected: 0x%08x)\n", expval);
840 }
841
842 static inline void inspect_fw_phexdec(char *label, uint32_t val)
843 {
844         printf("%-23s: 0x%08x / %8u bytes\n", label, val, val);
845 }
846
847 static inline void inspect_fw_phexdecdef(char *label,
848                                          uint32_t val, uint32_t defval)
849 {
850         printf("%-23s: 0x%08x / %8u bytes ", label, val, val);
851
852         if (val == defval)
853                 printf("(== OpenWrt default)\n");
854         else
855                 printf("(OpenWrt default: 0x%08x)\n", defval);
856 }
857
858 static inline void inspect_fw_pmd5sum(char *label, uint8_t *val, char *text)
859 {
860         int i;
861
862         printf("%-23s:", label);
863         for (i=0; i<MD5SUM_LEN; i++)
864                 printf(" %02x", val[i]);
865         printf(" %s\n", text);
866 }
867
868 static int inspect_fw(void)
869 {
870         char *buf;
871         struct fw_header *hdr;
872         uint8_t md5sum[MD5SUM_LEN];
873         struct board_info *board;
874         int ret = EXIT_FAILURE;
875
876         buf = malloc(inspect_info.file_size);
877         if (!buf) {
878                 ERR("no memory for buffer!\n");
879                 goto out;
880         }
881
882         ret = read_to_buf(&inspect_info, buf);
883         if (ret)
884                 goto out_free_buf;
885         hdr = (struct fw_header *)buf;
886
887         inspect_fw_pstr("File name", inspect_info.file_name);
888         inspect_fw_phexdec("File size", inspect_info.file_size);
889
890         if (ntohl(hdr->version) != HEADER_VERSION_V1) {
891                 ERR("file does not seem to have V1 header!\n");
892                 goto out_free_buf;
893         }
894
895         inspect_fw_phexdec("Version 1 Header size", sizeof(struct fw_header));
896
897         if (ntohl(hdr->unk1) != 0)
898                 inspect_fw_phexdec("Unknown value 1", hdr->unk1);
899
900         memcpy(md5sum, hdr->md5sum1, sizeof(md5sum));
901         if (ntohl(hdr->boot_len) == 0)
902                 memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum));
903         else
904                 memcpy(hdr->md5sum1, md5salt_boot, sizeof(md5sum));
905         get_md5(buf, inspect_info.file_size, hdr->md5sum1);
906
907         if (memcmp(md5sum, hdr->md5sum1, sizeof(md5sum))) {
908                 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(*ERROR*)");
909                 inspect_fw_pmd5sum("          --> expected", hdr->md5sum1, "");
910         } else {
911                 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(ok)");
912         }
913         if (ntohl(hdr->unk2) != 0)
914                 inspect_fw_phexdec("Unknown value 2", hdr->unk2);
915         inspect_fw_pmd5sum("Header MD5Sum2", hdr->md5sum2,
916                            "(purpose yet unknown, unchecked here)");
917         if (ntohl(hdr->unk3) != 0)
918                 inspect_fw_phexdec("Unknown value 3", hdr->unk3);
919
920         printf("\n");
921
922         inspect_fw_pstr("Vendor name", hdr->vendor_name);
923         inspect_fw_pstr("Firmware version", hdr->fw_version);
924         board = find_board_by_hwid(ntohl(hdr->hw_id));
925         if (board) {
926                 layout = find_layout(board->layout_id);
927                 inspect_fw_phexpost("Hardware ID",
928                                     ntohl(hdr->hw_id), board->id);
929                 inspect_fw_phexexp("Hardware Revision",
930                                    ntohl(hdr->hw_rev), board->hw_rev);
931         } else {
932                 inspect_fw_phexpost("Hardware ID",
933                                     ntohl(hdr->hw_id), "unknown");
934                 inspect_fw_phex("Hardware Revision",
935                                 ntohl(hdr->hw_rev));
936         }
937
938         printf("\n");
939
940         inspect_fw_phexdec("Kernel data offset",
941                            ntohl(hdr->kernel_ofs));
942         inspect_fw_phexdec("Kernel data length",
943                            ntohl(hdr->kernel_len));
944         if (board) {
945                 inspect_fw_phexdef("Kernel load address",
946                                    ntohl(hdr->kernel_la),
947                                    layout ? layout->kernel_la : 0xffffffff);
948                 inspect_fw_phexdef("Kernel entry point",
949                                    ntohl(hdr->kernel_ep),
950                                    layout ? layout->kernel_ep : 0xffffffff);
951                 inspect_fw_phexdecdef("Rootfs data offset",
952                                       ntohl(hdr->rootfs_ofs),
953                                       layout ? layout->rootfs_ofs : 0xffffffff);
954         } else {
955                 inspect_fw_phex("Kernel load address",
956                                 ntohl(hdr->kernel_la));
957                 inspect_fw_phex("Kernel entry point",
958                                 ntohl(hdr->kernel_ep));
959                 inspect_fw_phexdec("Rootfs data offset",
960                                    ntohl(hdr->rootfs_ofs));
961         }
962         inspect_fw_phexdec("Rootfs data length",
963                            ntohl(hdr->rootfs_len));
964         inspect_fw_phexdec("Boot loader data offset",
965                            ntohl(hdr->boot_ofs));
966         inspect_fw_phexdec("Boot loader data length",
967                            ntohl(hdr->boot_len));
968         inspect_fw_phexdec("Total firmware length",
969                            ntohl(hdr->fw_length));
970
971         if (extract) {
972                 FILE *fp;
973                 char *filename;
974
975                 printf("\n");
976
977                 filename = malloc(strlen(inspect_info.file_name) + 8);
978                 sprintf(filename, "%s-kernel", inspect_info.file_name);
979                 printf("Extracting kernel to \"%s\"...\n", filename);
980                 fp = fopen(filename, "w");
981                 if (fp) {
982                         if (!fwrite(buf + ntohl(hdr->kernel_ofs),
983                                     ntohl(hdr->kernel_len), 1, fp)) {
984                                 ERR("error in fwrite(): %s", strerror(errno));
985                         }
986                         fclose(fp);
987                 } else {
988                         ERR("error in fopen(): %s", strerror(errno));
989                 }
990                 free(filename);
991
992                 filename = malloc(strlen(inspect_info.file_name) + 8);
993                 sprintf(filename, "%s-rootfs", inspect_info.file_name);
994                 printf("Extracting rootfs to \"%s\"...\n", filename);
995                 fp = fopen(filename, "w");
996                 if (fp) {
997                         if (!fwrite(buf + ntohl(hdr->rootfs_ofs),
998                                     ntohl(hdr->rootfs_len), 1, fp)) {
999                                 ERR("error in fwrite(): %s", strerror(errno));
1000                         }
1001                         fclose(fp);
1002                 } else {
1003                         ERR("error in fopen(): %s", strerror(errno));
1004                 }
1005                 free(filename);
1006         }
1007
1008  out_free_buf:
1009         free(buf);
1010  out:
1011         return ret;
1012 }
1013
1014 int main(int argc, char *argv[])
1015 {
1016         int ret = EXIT_FAILURE;
1017         int err;
1018
1019         FILE *outfile;
1020
1021         progname = basename(argv[0]);
1022
1023         while ( 1 ) {
1024                 int c;
1025
1026                 c = getopt(argc, argv, "a:B:H:E:F:L:V:N:W:ci:k:r:R:o:xhsjv:");
1027                 if (c == -1)
1028                         break;
1029
1030                 switch (c) {
1031                 case 'a':
1032                         sscanf(optarg, "0x%x", &rootfs_align);
1033                         break;
1034                 case 'B':
1035                         board_id = optarg;
1036                         break;
1037                 case 'H':
1038                         opt_hw_id = optarg;
1039                         break;
1040                 case 'E':
1041                         sscanf(optarg, "0x%x", &kernel_ep);
1042                         break;
1043                 case 'F':
1044                         layout_id = optarg;
1045                         break;
1046                 case 'W':
1047                         opt_hw_rev = optarg;
1048                         break;
1049                 case 'L':
1050                         sscanf(optarg, "0x%x", &kernel_la);
1051                         break;
1052                 case 'V':
1053                         version = optarg;
1054                         break;
1055                 case 'v':
1056                         fw_ver = optarg;
1057                         break;
1058                 case 'N':
1059                         vendor = optarg;
1060                         break;
1061                 case 'c':
1062                         combined++;
1063                         break;
1064                 case 'k':
1065                         kernel_info.file_name = optarg;
1066                         break;
1067                 case 'r':
1068                         rootfs_info.file_name = optarg;
1069                         break;
1070                 case 'R':
1071                         sscanf(optarg, "0x%x", &rootfs_ofs);
1072                         break;
1073                 case 'o':
1074                         ofname = optarg;
1075                         break;
1076                 case 's':
1077                         strip_padding = 1;
1078                         break;
1079                 case 'i':
1080                         inspect_info.file_name = optarg;
1081                         break;
1082                 case 'j':
1083                         add_jffs2_eof = 1;
1084                         break;
1085                 case 'x':
1086                         extract = 1;
1087                         break;
1088                 case 'h':
1089                         usage(EXIT_SUCCESS);
1090                         break;
1091                 default:
1092                         usage(EXIT_FAILURE);
1093                         break;
1094                 }
1095         }
1096
1097         ret = check_options();
1098         if (ret)
1099                 goto out;
1100
1101         if (!inspect_info.file_name)
1102                 ret = build_fw();
1103         else
1104                 ret = inspect_fw();
1105
1106  out:
1107         return ret;
1108 }
1109