1/*
2
3	fpkg - Package a firmware
4	Copyright (C) 2007 Jonathan Zarate
5
6	This program is free software; you can redistribute it and/or
7	modify it under the terms of the GNU General Public License
8	as published by the Free Software Foundation; either version 2
9	of the License, or (at your option) any later version.
10
11	This program is distributed in the hope that it will be useful,
12	but WITHOUT ANY WARRANTY; without even the implied warranty of
13	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14	GNU General Public License for more details.
15
16	You should have received a copy of the GNU General Public License
17	along with this program; if not, write to the Free Software
18	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
20*/
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <errno.h>
26#include <unistd.h>
27#include <getopt.h>
28#include <time.h>
29#include <stdint.h>
30#include <sys/stat.h>
31#include <arpa/inet.h>
32
33#define ROUNDUP(n, a)	((n + (a - 1)) & ~(a - 1))
34
35#define TRX_MAGIC		0x30524448
36#define TRX_MAX_OFFSET		4
37#define TRX_MAX_LEN		((32 * 1024 * 1024) - ((256 + 128) * 1024))		// 32MB - (256K cfe + 128K cfg)
38
39typedef struct {
40	uint32_t magic;
41	uint32_t length;
42	uint32_t crc32;
43	uint32_t flag_version;
44	uint32_t offsets[TRX_MAX_OFFSET];
45} trx_t;
46
47char names[TRX_MAX_OFFSET][80];
48
49char trx_version = 1;
50int trx_max_offset = 3;
51
52
53typedef struct {
54	uint32_t crc32;
55	uint32_t magic;
56} moto_t;
57
58typedef struct {
59	uint8_t magic[4];
60	uint8_t extra1[4];
61	uint8_t date[3];
62	uint8_t version[3];
63	uint8_t u2nd[4];
64	uint8_t hardware;
65	uint8_t serial;
66	uint16_t flags;
67	uint8_t extra2[10];
68} cytan_t;
69
70uint32_t *crc_table = NULL;
71trx_t *trx = NULL;
72int trx_count = 0;
73int trx_final = 0;
74int trx_padding;
75time_t max_time = 0;
76
77inline size_t trx_header_size(void)
78{
79	return sizeof(*trx) - sizeof(trx->offsets) + (trx_max_offset * sizeof(trx->offsets[0]));
80}
81
82int crc_init(void)
83{
84	uint32_t c;
85	int i, j;
86
87	if (crc_table == NULL) {
88		if ((crc_table = malloc(sizeof(uint32_t) * 256)) == NULL) return 0;
89		for (i = 255; i >= 0; --i) {
90			c = i;
91			for (j = 8; j > 0; --j) {
92				if (c & 1) c = (c >> 1) ^ 0xEDB88320L;
93					else c >>= 1;
94			}
95			crc_table[i] = c;
96		}
97	}
98	return 1;
99}
100
101void crc_done(void)
102{
103	free(crc_table);
104	crc_table = NULL;
105}
106
107uint32_t crc_calc(uint32_t crc, uint8_t *buf, int len)
108{
109	while (len-- > 0) {
110		crc = crc_table[(crc ^ *buf) & 0xFF] ^ (crc >> 8);
111		++buf;
112	}
113	return crc;
114}
115
116void help(void)
117{
118	fprintf(stderr,
119		"fpkg - Package a firmware\n"
120		"Copyright (C) 2007 Jonathan Zarate\n"
121		"\n"
122		"Usage: [-v <trx version>] -i <input> [-a <align>] [-i <input>] [-a <align>] {output}\n"
123		"Output:\n"
124		" TRX:            -t <output file>\n"
125		" Linksys/Cisco:  -l <id>,<output file>\n"
126		"   W54G WRT54G / WRT54GL\n"
127		"   W54U WRTSL54GS\n"
128		"   W54S WRTS54GS\n"
129		"   W54s WRT54GS v4\n"
130		"   N160 WRT160N v3\n"
131		"   EWCB WRT300N v1\n"
132		"   310N WRT310N v1/v2\n"
133		"   320N WRT320N\n"
134		"   610N WRT610N v2\n"
135		"   32XN E2000\n"
136		"   61XN E3000\n"
137		" Motorola:       -m <id>,<output file>\n"
138		"   0x10577000 WE800\n"
139		"   0x10577040 WA840\n"
140		"   0x10577050 WR850\n"
141		" ASUS: 	  -r <id>,<v1>,<v2>,<v3>,<v4>,<output file>\n"
142		"\n"
143	);
144	exit(1);
145}
146
147void load_image(const char *fname)
148{
149	struct stat st;
150	FILE *f;
151	long rsize;
152	char *p;
153
154	if (trx_final) {
155		fprintf(stderr, "Cannot load another image if an output has already been written.\n");
156		exit(1);
157	}
158	if (trx_count >= trx_max_offset) {
159		fprintf(stderr, "Too many input files.\n");
160		exit(1);
161	}
162
163	if (stat(fname, &st) != 0) {
164		perror(fname);
165		exit(1);
166	}
167	if (st.st_ctime > max_time) max_time = st.st_ctime;
168
169	rsize = ROUNDUP(st.st_size, 4);
170	if ((trx->length + rsize) > TRX_MAX_LEN) {
171		fprintf(stderr, "Total size %lu (%.1f KB) is too big. Maximum is %lu (%.1f KB).\n",
172			(trx->length + rsize), (trx->length + rsize) / 1024.0,
173			(long unsigned int) TRX_MAX_LEN, TRX_MAX_LEN / 1024.0);
174		exit(1);
175	}
176
177	p = (char *)trx + trx->length;
178	if ((f = fopen(fname, "r")) == NULL) {
179		perror(fname);
180		exit(1);
181	}
182	if (fread((char *)trx + trx->length, st.st_size, 1, f) != 1) {
183		perror(fname);
184		exit(1);
185	}
186	fclose(f);
187	strncpy(names[trx_count], fname, sizeof(names[0]) -1);
188	trx->offsets[trx_count++] = trx->length;
189	trx->length += rsize;
190}
191
192void align_trx(const char *align)
193{
194	uint32_t len;
195	size_t n;
196	char *e;
197
198	errno = 0;
199	n = strtoul(align, &e, 0);
200	if (errno || (e == align) || *e) {
201		fprintf(stderr, "Illegal numeric string\n");
202		help();
203	}
204
205	if (trx_final) {
206		fprintf(stderr, "Cannot align if an output has already been written.\n");
207		exit(1);
208	}
209
210	len = ROUNDUP(trx->length, n);
211	if (len > TRX_MAX_LEN) {
212		fprintf(stderr, "Total size %u (%.1f KB) is too big. Maximum is %lu (%.1f KB).\n",
213			len, len / 1024.0, (long unsigned int) TRX_MAX_LEN, TRX_MAX_LEN / 1024.0);
214		exit(1);
215	}
216	trx->length = len;
217}
218
219void set_trx_version(const char *ver)
220{
221	int n;
222	char *e;
223
224	errno = 0;
225	n = strtoul(ver, &e, 0);
226	if (errno || (e == ver) || *e) {
227		fprintf(stderr, "Illegal numeric string\n");
228		help();
229	}
230
231	if (trx_count > 0) {
232		fprintf(stderr, "Cannot change trx version after images have already been loaded.\n");
233		exit(1);
234	}
235
236	if (n < 1 || n > 2) {
237		fprintf(stderr, "TRX version %d is not supported.\n", n);
238		exit(1);
239	}
240
241	trx_version = (char)n;
242	switch (trx_version) {
243	case 2:
244		trx_max_offset = 4;
245		break;
246	default:
247		trx_max_offset = 3;
248		break;
249	}
250	trx->length = trx_header_size();
251}
252
253void finalize_trx(void)
254{
255	uint32_t len;
256
257	if (trx_count == 0) {
258		fprintf(stderr, "No image was loaded.\n");
259		exit(1);
260	}
261
262	if (trx_final) return;
263	trx_final = 1;
264
265	len = trx->length;
266
267	trx->length = ROUNDUP(len, 4096);
268	trx->magic = TRX_MAGIC;
269	trx->flag_version = trx_version << 16;
270	trx->crc32 = crc_calc(0xFFFFFFFF, (void *)&trx->flag_version,
271		trx->length - (sizeof(*trx) - (sizeof(trx->flag_version) + sizeof(trx->offsets))));
272
273	trx_padding = trx->length - len;
274}
275
276void create_trx(const char *fname)
277{
278	FILE *f;
279
280	finalize_trx();
281
282	printf("Creating TRX: %s\n", fname);
283
284	if (((f = fopen(fname, "w")) == NULL) ||
285		(fwrite(trx, trx->length, 1, f) != 1)) {
286		perror(fname);
287		exit(1);
288	}
289	fclose(f);
290}
291
292void create_cytan(const char *fname, const char *pattern)
293{
294	FILE *f;
295	cytan_t h;
296	char padding[1024 - sizeof(h)];
297	struct tm *tm;
298
299	if (strlen(pattern) != 4) {
300		fprintf(stderr, "Linksys signature must be 4 characters. \"%s\" is invalid.\n", pattern);
301		exit(1);
302	}
303
304	finalize_trx();
305
306	printf("Creating Linksys %s: %s\n", pattern, fname);
307
308	memset(&h, 0, sizeof(h));
309	memcpy(h.magic, pattern, 4);
310	memcpy(h.u2nd, "U2ND", 4);
311	h.version[0] = 4;		// 4.0.0	should be >= *_VERSION_FROM defined in code_pattern.h
312	h.flags = 0xFF;
313	tm = localtime(&max_time);
314	h.date[0] = tm->tm_year - 100;
315	h.date[1] = tm->tm_mon + 1;
316	h.date[2] = tm->tm_mday;
317
318	memset(padding, 0, sizeof(padding));
319
320	if (((f = fopen(fname, "w")) == NULL) ||
321		(fwrite(&h, sizeof(h), 1, f) != 1) ||
322		(fwrite(trx, trx->length, 1, f) != 1) ||
323		(fwrite(padding, sizeof(padding), 1, f) != 1)) {
324		perror(fname);
325		exit(1);
326	}
327	fclose(f);
328}
329
330void create_moto(const char *fname, const char *signature)
331{
332	FILE *f;
333	moto_t h;
334	char *p;
335
336	h.magic = strtoul(signature, &p, 0);
337	if (*p != 0) help();
338
339	finalize_trx();
340
341	printf("Creating Motorola 0x%08X: %s\n", h.magic, fname);
342
343	h.magic = htonl(h.magic);
344	h.crc32 = crc_calc(0xFFFFFFFF, (void *)&h.magic, sizeof(h.magic));
345	h.crc32 = htonl(crc_calc(h.crc32, (void *)trx, trx->length));
346
347	if (((f = fopen(fname, "w")) == NULL) ||
348		(fwrite(&h, sizeof(h), 1, f) != 1) ||
349		(fwrite(trx, trx->length, 1, f) != 1)) {
350		perror(fname);
351		exit(1);
352	}
353	fclose(f);
354}
355
356#define MAX_STRING 12
357#define MAX_VER 4
358
359typedef struct {
360	uint8_t major;
361	uint8_t minor;
362} version_t;
363
364typedef struct {
365	version_t kernel;
366	version_t fs;
367	char 	  productid[MAX_STRING];
368	version_t hw[MAX_VER*2];
369	char	  pad[32];
370} TAIL;
371
372/* usage:
373 * -r <productid>,<version>,<output file>
374 *
375 */
376int create_asus(const char *optarg)
377{
378	FILE *f;
379	char value[320];
380	char *next, *pid, *ver, *fname, *p;
381	TAIL asus_tail;
382	uint32_t len;
383	uint32_t v1, v2, v3, v4;
384
385	memset(&asus_tail, 0, sizeof(TAIL));
386
387	strncpy(value, optarg, sizeof(value));
388	next = value;
389	pid = strsep(&next, ",");
390	if(!pid) return 0;
391
392	strncpy(&asus_tail.productid[0], pid, MAX_STRING);
393
394	ver = strsep(&next, ",");
395	if(!ver) return 0;
396
397	sscanf(ver, "%d.%d.%d.%d", &v1, &v2, &v3, &v4);
398	asus_tail.kernel.major = (uint8_t)v1;
399	asus_tail.kernel.minor = (uint8_t)v2;
400	asus_tail.fs.major = (uint8_t)v3;
401	asus_tail.fs.minor = (uint8_t)v4;
402
403	fname = strsep(&next, ",");
404	if(!fname) return 0;
405
406	// append version information into the latest offset
407	trx->length += sizeof(TAIL);
408	len = trx->length;
409	trx->length = ROUNDUP(len, 4096);
410
411	p = (char *)trx+trx->length-sizeof(TAIL);
412	memcpy(p, &asus_tail, sizeof(TAIL));
413
414	finalize_trx();
415
416	printf("Creating ASUS %s firmware to %s\n", asus_tail.productid, fname);
417
418	if (((f = fopen(fname, "w")) == NULL) ||
419		(fwrite(trx, trx->length, 1, f) != 1)) {
420		perror(fname);
421		exit(1);
422	}
423	fclose(f);
424
425	return 1;
426}
427
428int main(int argc, char **argv)
429{
430	char s[256];
431	char *p;
432	int o;
433	unsigned l, j;
434
435	printf("\n");
436
437	if ((!crc_init()) || ((trx = calloc(1, TRX_MAX_LEN)) == NULL)) {
438		fprintf(stderr, "Not enough memory\n");
439		exit(1);
440	}
441	trx->length = trx_header_size();
442
443	while ((o = getopt(argc, argv, "v:i:a:t:l:m:r:")) != -1) {
444		switch (o) {
445		case 'v':
446			set_trx_version(optarg);
447			break;
448		case 'i':
449			load_image(optarg);
450			break;
451		case 'a':
452			align_trx(optarg);
453			break;
454		case 't':
455			create_trx(optarg);
456			break;
457		case 'l':
458		case 'm':
459			if (strlen(optarg) >= sizeof(s)) help();
460			strcpy(s, optarg);
461			if ((p = strchr(s, ',')) == NULL) help();
462			*p = 0;
463			++p;
464			if (o == 'l') create_cytan(p, s);
465				else create_moto(p, s);
466			break;
467		case 'r':
468			create_asus(optarg);
469			break;
470		default:
471			help();
472			return 1;
473		}
474	}
475
476	if (trx_count == 0) {
477		help();
478	}
479	else {
480		finalize_trx();
481		l = trx->length - trx_padding - trx_header_size();
482		printf("\nTRX Image:\n");
483		printf(" Total Size .... : %u (%.1f KB) (%.1f MB)\n", trx->length, trx->length / 1024.0, trx->length / 1024.0 / 1024.0);
484		printf("   Images ...... : %u (0x%08x)\n", l , l);
485		printf("   Padding ..... : %d\n", trx_padding);
486
487		printf(" Avail. for jffs :\n");
488
489		/* Reserved: 2 EBs for pmon, 1 EB for nvram. */
490		l = trx->length;
491		if (l < (4 * 1024 * 1024) - (3 * 64 * 1024))
492			j = (4 * 1024 * 1024) - (3 * 64 * 1024) - l;
493		else
494			j = 0;
495		printf("   4MB, 128K CFE : %d EBs + %d\n", j / (64*1024), j % (64*1024));
496
497		/* Reserved: 4 EBs for pmon, 1 EB for nvram. */
498		if (l < (4 * 1024 * 1024) - (5 * 64 * 1024))
499			j = (4 * 1024 * 1024) - (5 * 64 * 1024) - l;
500		else
501			j = 0;
502		printf("   4MB, 256K CFE : %d EBs + %d\n", j / (64*1024), j % (64*1024));
503
504		if (l < (8 * 1024 * 1024) - (5 * 64 * 1024))
505			j = (8 * 1024 * 1024) - (5 * 64 * 1024) - l;
506		else
507			j = 0;
508		printf("   8MB, 256K CFE : %d EBs + %d\n", j / (64*1024), j % (64*1024));
509
510		if (l < (32 * 1024 * 1024) - (5 * 64 * 1024))
511			j = (32 * 1024 * 1024) - (5 * 64 * 1024) - l;
512		else
513			j = 0;
514		printf("  32MB, 256K CFE : %d EBs + %d\n", j / (64*1024), j % (64*1024));
515
516		printf(" CRC-32 ........ : %8X\n", trx->crc32);
517		l = (ROUNDUP(trx->length, (128 * 1024)) / (128 * 1024));
518		printf(" 128K Blocks ... : %u (0x%08X)\n", l, l);
519		l = (ROUNDUP(trx->length, (64 * 1024)) / (64 * 1024));
520		printf("  64K Blocks ... : %u (0x%08X)\n", l, l);
521		printf(" Offsets:\n");
522		for (o = 0; o < trx_max_offset; ++o) {
523		   printf("   %d: 0x%08X  %s\n", o, trx->offsets[o], names[o]);
524		}
525	}
526	printf("\n");
527	return 0;
528}
529