1/*
2 * Copyright (C) 2014  Claudio Leite <leitec@staticky.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19/*
20 * Builds a proper flash image for routers using some Gemtek
21 * OEM boards. These include the Airlink101 AR725W, the
22 * Asante SmartHub 600 (AWRT-600N), and Linksys WRT100/110.
23 *
24 * The resulting image is compatible with the factory firmware
25 * web upgrade and TFTP interface.
26 *
27 * To build:
28 *  gcc -O2 -o mkheader_gemtek mkheader_gemtek.c -lz
29 *
30 * Claudio Leite <leitec@staticky.com>
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <stdint.h>
36#include <string.h>
37
38#include <zlib.h>		/* for crc32() */
39
40/*
41 * The header is in little-endian format. In case
42 * we are on a BE host, we need to swap binary
43 * values.
44 */
45#ifdef __APPLE__
46# include <libkern/OSByteOrder.h>
47# define le32 OSSwapHostToLittleInt32
48#else
49# if defined(__linux__)
50#  include <endian.h>
51#  if __BYTE_ORDER == __BIG_ENDIAN
52#   define CPU_BIG_ENDIAN
53#  endif
54# else
55#  include <sys/endian.h>		/* BSD's should have this */
56#  if _BYTE_ORDER == _BIG_ENDIAN
57#   define CPU_BIG_ENDIAN
58#  endif
59# endif
60# ifdef CPU_BIG_ENDIAN
61#  define le32(x) (((x & 0xff000000) >> 24) | \
62                   ((x & 0x00ff0000) >> 8)  | \
63                   ((x & 0x0000ff00) << 8)  | \
64                   ((x & 0x000000ff) << 24))
65# else
66#  define le32(x) (x)
67# endif
68#endif
69
70struct gemtek_header {
71	uint8_t 	magic[4];
72	uint8_t 	version[4];
73	uint32_t 	product_id;
74	uint32_t 	imagesz;
75	uint32_t 	checksum;
76	uint32_t 	fast_checksum;
77	uint8_t 	build[4];
78	uint8_t 	lang[4];
79};
80
81#define HDRLEN	sizeof(struct gemtek_header)
82
83struct machines {
84	char           *desc;
85	char           *id;
86	uint32_t 	maxsize;
87	struct gemtek_header header;
88};
89
90struct machines mach_def[] = {
91	{"Airlink101 AR725W", "ar725w", 0x340000,
92		{"GMTK", "1003", le32(0x03000001), 0, 0,
93		  0, "01\0\0", "EN\0\0"}},
94	{"Asante AWRT-600N", "awrt600n", 0x340000,
95		{"A600", "1005", le32(0x03000001), 0, 0,
96		  0, "01\0\0", "EN\0\0"}},
97	{"Linksys WRT100", "wrt100", 0x320000,
98		{"GMTK", "1007", le32(0x03040001), 0, 0,
99		  0, "2\0\0\0", "EN\0\0"}},
100	{"Linksys WRT110", "wrt110", 0x320000,
101		{"GMTK", "1007", le32(0x03040001), 0, 0,
102		  0, "2\0\0\0", "EN\0\0"}},
103	{0}
104};
105
106int
107main(int argc, char *argv[])
108{
109	unsigned long 	res, flen;
110	struct gemtek_header my_hdr;
111	FILE           *f, *f_out;
112	int 		image_type = -1, index;
113	uint8_t        *buf;
114	uint32_t 	crc;
115
116	if (argc < 3) {
117		fprintf(stderr, "mkheader_gemtek <uImage> <webflash image> [machine ID]\n");
118		fprintf(stderr, "  where [machine ID] is one of:\n");
119		for (index = 0; mach_def[index].desc != 0; index++) {
120			fprintf(stderr, "    %-10s  %s", mach_def[index].id, mach_def[index].desc);
121			if (index == 0)
122				fprintf(stderr, " (default)\n");
123			else
124				fprintf(stderr, "\n");
125		}
126
127		exit(-1);
128	}
129
130	if (argc == 4) {
131		for(index = 0; mach_def[index].id != 0; index++) {
132			if(strcmp(mach_def[index].id, argv[3]) == 0) {
133				image_type = index;
134				break;
135			}
136		}
137
138		if(image_type == -1) {
139			fprintf(stderr, "\nERROR: invalid machine type\n");
140			exit(-1);
141		}
142	} else
143		image_type = 0;
144
145	printf("Opening %s...\n", argv[1]);
146
147	f = fopen(argv[1], "r");
148	if(!f) {
149		fprintf(stderr, "\nERROR: couldn't open input image\n");
150		exit(-1);
151	}
152
153	fseek(f, 0, SEEK_END);
154	flen = (unsigned long) ftell(f);
155
156	printf("  %lu (0x%lX) bytes long\n", flen, flen);
157
158	if (flen > mach_def[image_type].maxsize) {
159		fprintf(stderr, "\nERROR: image exceeds maximum compatible size\n");
160		goto f_error;
161	}
162
163	buf = malloc(flen + HDRLEN);
164	if (!buf) {
165		fprintf(stderr, "\nERROR: couldn't allocate buffer\n");
166		goto f_error;
167	}
168	rewind(f);
169	res = fread(buf + HDRLEN, 1, flen, f);
170	if (res != flen) {
171		perror("Couldn't read entire file: fread()");
172		goto f_error;
173	}
174	fclose(f);
175
176	printf("\nCreating %s...\n", argv[2]);
177
178	memcpy(&my_hdr, &mach_def[image_type].header, HDRLEN);
179
180	printf("  Using %s magic\n", mach_def[image_type].desc);
181
182	my_hdr.imagesz = le32(flen + HDRLEN);
183	memcpy(my_hdr.lang, "EN", 2);
184
185	memcpy(buf, &my_hdr, HDRLEN);
186
187	crc = crc32(0, buf, flen + HDRLEN);
188	printf("  CRC32: %08X\n", crc);
189
190	my_hdr.checksum = le32(crc);
191	memcpy(buf, &my_hdr, HDRLEN);
192
193	printf("  Writing...\n");
194
195	f_out = fopen(argv[2], "w");
196	if(!f_out) {
197		fprintf(stderr, "\nERROR: couldn't open output image\n");
198		exit(-1);
199	}
200
201	fwrite(buf, 1, flen + HDRLEN, f_out);
202
203	fclose(f_out);
204
205	free(buf);
206	return 0;
207
208f_error:
209	fclose(f);
210	exit(-1);
211}
212