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