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