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