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

Archive Download this file



interactive