1/*
2 *
3 *  Copyright (C) 2007-2008 OpenWrt.org
4 *  Copyright (C) 2007-2008 Gabor Juhos <juhosg at openwrt.org>
5 *
6 *  This code was based on the information of the ZyXEL's firmware
7 *  image format written by Kolja Waschk, can be found at:
8 *  http://www.ixo.de/info/zyxel_uclinux
9 *
10 *  This program is free software; you can redistribute it and/or modify it
11 *  under the terms of the GNU General Public License version 2 as published
12 *  by the Free Software Foundation.
13 *
14 */
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <stdint.h>
19#include <string.h>
20#include <unistd.h>	/* for unlink() */
21#include <libgen.h>
22#include <getopt.h>	/* for getopt() */
23#include <stdarg.h>
24#include <errno.h>
25#include <sys/stat.h>
26#include <endian.h>	/* for __BYTE_ORDER */
27#if defined(__CYGWIN__)
28#  include <byteswap.h>
29#endif
30#include <inttypes.h>
31
32#include "zynos.h"
33
34#if (__BYTE_ORDER == __LITTLE_ENDIAN)
35#  define HOST_TO_LE16(x)	(x)
36#  define HOST_TO_LE32(x)	(x)
37#  define LE16_TO_HOST(x)	(x)
38#  define LE32_TO_HOST(x)	(x)
39#  define HOST_TO_BE16(x)	bswap_16(x)
40#  define HOST_TO_BE32(x)	bswap_32(x)
41#  define BE16_TO_HOST(x)	bswap_16(x)
42#  define BE32_TO_HOST(x)	bswap_32(x)
43#else
44#  define HOST_TO_BE16(x)	(x)
45#  define HOST_TO_BE32(x)	(x)
46#  define BE16_TO_HOST(x)	(x)
47#  define BE32_TO_HOST(x)	(x)
48#  define HOST_TO_LE16(x)	bswap_16(x)
49#  define HOST_TO_LE32(x)	bswap_32(x)
50#  define LE16_TO_HOST(x)	bswap_16(x)
51#  define LE32_TO_HOST(x)	bswap_32(x)
52#endif
53
54#define ALIGN(x,y)	(((x)+((y)-1)) & ~((y)-1))
55
56#define MAX_NUM_BLOCKS	8
57#define MAX_ARG_COUNT	32
58#define MAX_ARG_LEN	1024
59#define FILE_BUF_LEN	(16*1024)
60
61
62struct csum_state{
63	int		odd;
64	uint32_t	sum;
65	uint32_t	tmp;
66};
67
68struct fw_block {
69	uint32_t	align;		/* alignment of this block */
70	char		*file_name;	/* name of the file */
71	uint32_t	file_size;	/* length of the file */
72	char		*mmap_name;	/* name in the MMAP table */
73	int		type;		/* block type */
74	uint32_t	padlen;
75	uint8_t		padc;
76};
77
78#define BLOCK_TYPE_BOOTEXT	0
79#define BLOCK_TYPE_RAW		1
80
81struct fw_mmap {
82	uint32_t	addr;
83	uint32_t	size;
84	uint32_t	user_addr;
85	uint32_t	user_size;
86};
87#define MMAP_DATA_SIZE	1024
88#define MMAP_ALIGN	16
89
90struct board_info {
91	char *name;		/* model name */
92	char *desc;		/* description */
93	uint16_t vendor;	/* vendor id */
94	uint16_t model;		/* model id */
95	uint32_t flash_base;	/* flash base address */
96	uint32_t flash_size;	/* board flash size */
97	uint32_t code_start;	/* code start address */
98	uint32_t romio_offs;	/* offset of the firmware within the flash */
99	uint32_t bootext_size;	/* maximum size of bootext block */
100};
101
102/*
103 * Globals
104 */
105char *progname;
106char *ofname = NULL;
107int verblevel = 0;
108
109struct board_info *board = NULL;
110
111struct fw_block blocks[MAX_NUM_BLOCKS];
112struct fw_block *bootext_block = NULL;
113int num_blocks = 0;
114
115#define ADM5120_FLASH_BASE	0xBFC00000
116#define ADM5120_CODE_START	0x80008000
117
118/* TODO: check values for AR7 */
119#define AR7_FLASH_BASE		0xB0000000
120#define AR7_CODE_START		0x94008000
121
122#define ATHEROS_FLASH_BASE	0xBFC00000
123#define ATHEROS_CODE_START	0x80e00000
124
125#define AR71XX_FLASH_BASE	0xBFC00000
126#define AR71XX_CODE_START	0x81E00000
127
128#define BOARD(n, d, v, m, fb, fs, cs, fo) {		\
129	.name = (n), .desc=(d),				\
130	.vendor = (v), .model = (m),			\
131	.flash_base = (fb), .flash_size = (fs)<<20,	\
132	.code_start = (cs), .romio_offs = (fo),		\
133	.bootext_size = BOOTEXT_DEF_SIZE		\
134	}
135
136#define ADMBOARD1(n, d, m, fs) BOARD(n, d, ZYNOS_VENDOR_ID_ZYXEL, m, \
137	ADM5120_FLASH_BASE, fs, ADM5120_CODE_START, 0x8000)
138
139#define ADMBOARD2(n, d, m, fs) BOARD(n, d, ZYNOS_VENDOR_ID_ZYXEL, m, \
140	ADM5120_FLASH_BASE, fs, ADM5120_CODE_START, 0x10000)
141
142#define AR7BOARD1(n, d, m, fs) BOARD(n, d, ZYNOS_VENDOR_ID_ZYXEL, m, \
143	AR7_FLASH_BASE, fs, AR7_CODE_START, 0x8000)
144
145#define ATHEROSBOARD1(n, d, m, fs) BOARD(n, d, ZYNOS_VENDOR_ID_ZYXEL, m, \
146	ATHEROS_FLASH_BASE, fs, ATHEROS_CODE_START, 0x30000)
147
148#define AR71XXBOARD1(n, d, m, fs) {		\
149	.name = (n), .desc=(d),				\
150	.vendor = (ZYNOS_VENDOR_ID_ZYXEL), .model = (m),			\
151	.flash_base = (AR71XX_FLASH_BASE), .flash_size = (fs)<<20,	\
152	.code_start = (AR71XX_CODE_START), .romio_offs = (0x40000),		\
153	.bootext_size = 0x30000		\
154	}
155
156
157static struct board_info boards[] = {
158	/*
159	 * Infineon/ADMtek ADM5120 based boards
160	 */
161	ADMBOARD2("ES-2024A",	"ZyXEL ES-2024A", ZYNOS_MODEL_ES_2024A, 4),
162	ADMBOARD2("ES-2024PWR",	"ZyXEL ES-2024PWR", ZYNOS_MODEL_ES_2024PWR, 4),
163	ADMBOARD2("ES-2108",	"ZyXEL ES-2108", ZYNOS_MODEL_ES_2108, 4),
164	ADMBOARD2("ES-2108-F",	"ZyXEL ES-2108-F", ZYNOS_MODEL_ES_2108_F, 4),
165	ADMBOARD2("ES-2108-G",	"ZyXEL ES-2108-G", ZYNOS_MODEL_ES_2108_G, 4),
166	ADMBOARD2("ES-2108-LC",	"ZyXEL ES-2108-LC", ZYNOS_MODEL_ES_2108_LC, 4),
167	ADMBOARD2("ES-2108PWR",	"ZyXEL ES-2108PWR", ZYNOS_MODEL_ES_2108PWR, 4),
168	ADMBOARD1("HS-100",	"ZyXEL HomeSafe 100", ZYNOS_MODEL_HS_100, 2),
169	ADMBOARD1("HS-100W",	"ZyXEL HomeSafe 100W", ZYNOS_MODEL_HS_100W, 2),
170	ADMBOARD1("P-334",	"ZyXEL Prestige 334", ZYNOS_MODEL_P_334, 2),
171	ADMBOARD1("P-334U",	"ZyXEL Prestige 334U", ZYNOS_MODEL_P_334U, 4),
172	ADMBOARD1("P-334W",	"ZyXEL Prestige 334W", ZYNOS_MODEL_P_334W, 2),
173	ADMBOARD1("P-334WH",	"ZyXEL Prestige 334WH", ZYNOS_MODEL_P_334WH, 4),
174	ADMBOARD1("P-334WHD",	"ZyXEL Prestige 334WHD", ZYNOS_MODEL_P_334WHD, 4),
175	ADMBOARD1("P-334WT",	"ZyXEL Prestige 334WT", ZYNOS_MODEL_P_334WT, 4),
176	ADMBOARD1("P-335",	"ZyXEL Prestige 335", ZYNOS_MODEL_P_335, 4),
177	ADMBOARD1("P-335Plus",	"ZyXEL Prestige 335Plus", ZYNOS_MODEL_P_335PLUS, 4),
178	ADMBOARD1("P-335U",	"ZyXEL Prestige 335U", ZYNOS_MODEL_P_335U, 4),
179	ADMBOARD1("P-335WT",	"ZyXEL Prestige 335WT", ZYNOS_MODEL_P_335WT, 4),
180
181	{
182		.name		= "P-2602HW-D1A",
183		.desc		= "ZyXEL P-2602HW-D1A",
184		.vendor		= ZYNOS_VENDOR_ID_ZYXEL,
185		.model		= ZYNOS_MODEL_P_2602HW_D1A,
186		.flash_base	= AR7_FLASH_BASE,
187		.flash_size	= 4*1024*1024,
188		.code_start	= 0x94008000,
189		.romio_offs	= 0x20000,
190		.bootext_size	= BOOTEXT_DEF_SIZE,
191	},
192
193#if 0
194	/*
195	 * Texas Instruments AR7 based boards
196	 */
197	AR7BOARD1("P-660H-61",  "ZyXEL P-660H-61", ZYNOS_MODEL_P_660H_61, 2),
198	AR7BOARD1("P-660H-63",  "ZyXEL P-660H-63", ZYNOS_MODEL_P_660H_63, 2),
199	AR7BOARD1("P-660H-D1",  "ZyXEL P-660H-D1", ZYNOS_MODEL_P_660H_D1, 2),
200	AR7BOARD1("P-660H-D3",  "ZyXEL P-660H-D3", ZYNOS_MODEL_P_660H_D3, 2),
201	AR7BOARD1("P-660HW-61", "ZyXEL P-660HW-61", ZYNOS_MODEL_P_660HW_61, 2),
202	AR7BOARD1("P-660HW-63", "ZyXEL P-660HW-63", ZYNOS_MODEL_P_660HW_63, 2),
203	AR7BOARD1("P-660HW-67", "ZyXEL P-660HW-67", ZYNOS_MODEL_P_660HW_67, 2),
204	AR7BOARD1("P-660HW-D1", "ZyXEL P-660HW-D1", ZYNOS_MODEL_P_660HW_D1, 2),
205	AR7BOARD1("P-660HW-D3", "ZyXEL P-660HW-D3", ZYNOS_MODEL_P_660HW_D3, 2),
206	AR7BOARD1("P-660R-61",  "ZyXEL P-660R-61", ZYNOS_MODEL_P_660R_61, 2),
207	AR7BOARD1("P-660R-61C", "ZyXEL P-660R-61C", ZYNOS_MODEL_P_660R_61C, 2),
208	AR7BOARD1("P-660R-63",  "ZyXEL P-660R-63", ZYNOS_MODEL_P_660R_63, 2),
209	AR7BOARD1("P-660R-63C", "ZyXEL P-660R-63C", ZYNOS_MODEL_P_660R_63C, 2),
210	AR7BOARD1("P-660R-67",  "ZyXEL P-660R-67", ZYNOS_MODEL_P_660R_67, 2),
211	AR7BOARD1("P-660R-D1",  "ZyXEL P-660R-D1", ZYNOS_MODEL_P_660R_D1, 2),
212	AR7BOARD1("P-660R-D3",  "ZyXEL P-660R-D3", ZYNOS_MODEL_P_660R_D3, 2),
213#endif
214	{
215		.name		= "O2SURF",
216		.desc		= "O2 DSL Surf & Phone",
217		.vendor		= ZYNOS_VENDOR_ID_O2,
218		.model		= ZYNOS_MODEL_O2SURF,
219		.flash_base	= AR7_FLASH_BASE,
220		.flash_size	= 8*1024*1024,
221		.code_start	= 0x94014000,
222		.romio_offs	= 0x40000,
223		.bootext_size	= BOOTEXT_DEF_SIZE,
224	},
225
226	/*
227:x
228	 */
229	ATHEROSBOARD1("NBG-318S", "ZyXEL NBG-318S", ZYNOS_MODEL_NBG_318S, 4),
230
231	/*
232	 * Atheros ar71xx based boards
233	 */
234	AR71XXBOARD1("NBG-460N", "ZyXEL NBG-460N", ZYNOS_MODEL_NBG_460N, 4),
235
236	{.name = NULL}
237};
238
239/*
240 * Message macros
241 */
242#define ERR(fmt, ...) do { \
243	fflush(0); \
244	fprintf(stderr, "[%s] *** error: " fmt "\n", \
245			progname, ## __VA_ARGS__ ); \
246} while (0)
247
248#define ERRS(fmt, ...) do { \
249	int save = errno; \
250	fflush(0); \
251	fprintf(stderr, "[%s] *** error: " fmt ", %s\n", \
252			progname, ## __VA_ARGS__, strerror(save)); \
253} while (0)
254
255#define WARN(fmt, ...) do { \
256	fprintf(stderr, "[%s] *** warning: " fmt "\n", \
257			progname, ## __VA_ARGS__ ); \
258} while (0)
259
260#define DBG(lev, fmt, ...) do { \
261	if (verblevel < lev) \
262		break;\
263	fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \
264} while (0)
265
266#define ERR_FATAL		-1
267#define ERR_INVALID_IMAGE	-2
268
269/*
270 * Helper routines
271 */
272void
273usage(int status)
274{
275	FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
276	struct board_info *board;
277
278	fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
279	fprintf(stream,
280"\n"
281"Options:\n"
282"  -B <board>      create image for the board specified with <board>.\n"
283"                  valid <board> values:\n"
284	);
285	for (board = boards; board->name != NULL; board++){
286		fprintf(stream,
287"                    %-12s= %s\n",
288		 board->name, board->desc);
289	};
290	fprintf(stream,
291"  -b <file>[:<align>]\n"
292"                  add boot extension block to the image\n"
293"  -r <file>[:<align>]\n"
294"                  add raw block to the image\n"
295"  -o <file>       write output to the file <file>\n"
296"  -h              show this screen\n"
297	);
298
299	exit(status);
300}
301
302
303/*
304 * argument parsing
305 */
306int
307str2u32(char *arg, uint32_t *val)
308{
309	char *err = NULL;
310	uint32_t t;
311
312	errno=0;
313	t = strtoul(arg, &err, 0);
314	if (errno || (err==arg) || ((err != NULL) && *err)) {
315		return -1;
316	}
317
318	*val = t;
319	return 0;
320}
321
322
323int
324str2u16(char *arg, uint16_t *val)
325{
326	char *err = NULL;
327	uint32_t t;
328
329	errno=0;
330	t = strtoul(arg, &err, 0);
331	if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x10000)) {
332		return -1;
333	}
334
335	*val = t & 0xFFFF;
336	return 0;
337}
338
339int
340str2u8(char *arg, uint8_t *val)
341{
342	char *err = NULL;
343	uint32_t t;
344
345	errno=0;
346	t = strtoul(arg, &err, 0);
347	if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x100)) {
348		return -1;
349	}
350
351	*val = t & 0xFF;
352	return 0;
353}
354
355int
356str2sig(char *arg, uint32_t *sig)
357{
358	if (strlen(arg) != 4)
359		return -1;
360
361	*sig = arg[0] | (arg[1] << 8) | (arg[2] << 16) | (arg[3] << 24);
362
363	return 0;
364}
365
366
367int
368parse_arg(char *arg, char *buf, char *argv[])
369{
370	int res = 0;
371	size_t argl;
372	char *tok;
373	char **ap = &buf;
374	int i;
375
376	memset(argv, 0, MAX_ARG_COUNT * sizeof(void *));
377
378	if ((arg == NULL)) {
379		/* no arguments */
380		return 0;
381	}
382
383	argl = strlen(arg);
384	if (argl == 0) {
385		/* no arguments */
386		return 0;
387	}
388
389	if (argl >= MAX_ARG_LEN) {
390		/* argument is too long */
391		argl = MAX_ARG_LEN-1;
392	}
393
394	memcpy(buf, arg, argl);
395	buf[argl] = '\0';
396
397	for (i = 0; i < MAX_ARG_COUNT; i++) {
398		tok = strsep(ap, ":");
399		if (tok == NULL) {
400			break;
401		}
402#if 0
403		else if (tok[0] == '\0') {
404			break;
405		}
406#endif
407		argv[i] = tok;
408		res++;
409	}
410
411	return res;
412}
413
414
415int
416required_arg(char c, char *arg)
417{
418	if (arg == NULL || *arg != '-')
419		return 0;
420
421	ERR("option -%c requires an argument\n", c);
422	return -1;
423}
424
425
426int
427is_empty_arg(char *arg)
428{
429	int ret = 1;
430	if (arg != NULL) {
431		if (*arg) ret = 0;
432	};
433	return ret;
434}
435
436
437void
438csum_init(struct csum_state *css)
439{
440	css->odd = 0;
441	css->sum = 0;
442	css->tmp = 0;
443}
444
445
446void
447csum_update(uint8_t *p, uint32_t len, struct csum_state *css)
448{
449	if (len == 0)
450		return;
451
452	if (css->odd) {
453		css->sum += (css->tmp << 8) + p[0];
454		if (css->sum > 0xFFFF) {
455			css->sum += 1;
456			css->sum &= 0xFFFF;
457		}
458		css->odd = 0;
459		len--;
460		p++;
461	}
462
463	for ( ; len > 1; len -= 2, p +=2 ) {
464		css->sum  += (p[0] << 8) + p[1];
465		if (css->sum > 0xFFFF) {
466			css->sum += 1;
467			css->sum &= 0xFFFF;
468		}
469	}
470
471	if (len == 1){
472		css->tmp = p[0];
473		css->odd = 1;
474	}
475}
476
477
478uint16_t
479csum_get(struct csum_state *css)
480{
481	char pad = 0;
482
483	csum_update(&pad, 1, css);
484	return css->sum;
485}
486
487uint16_t
488csum_buf(uint8_t *p, uint32_t len)
489{
490	struct csum_state css;
491
492	csum_init(&css);
493	csum_update(p, len, &css);
494	return csum_get(&css);
495
496}
497
498/*
499 * routines to write data to the output file
500 */
501int
502write_out_data(FILE *outfile, uint8_t *data, size_t len,
503		struct csum_state *css)
504{
505	errno = 0;
506
507	fwrite(data, len, 1, outfile);
508	if (errno) {
509		ERR("unable to write output file");
510		return -1;
511	}
512
513	if (css) {
514		csum_update(data, len, css);
515	}
516
517	return 0;
518}
519
520
521int
522write_out_padding(FILE *outfile, size_t len, uint8_t padc,
523		 struct csum_state *css)
524{
525	uint8_t buf[512];
526	size_t buflen = sizeof(buf);
527
528	memset(buf, padc, buflen);
529	while (len > 0) {
530		if (len < buflen)
531			buflen = len;
532
533		if (write_out_data(outfile, buf, buflen, css))
534			return -1;
535
536		len -= buflen;
537	}
538
539	return 0;
540}
541
542
543int
544write_out_data_align(FILE *outfile, uint8_t *data, size_t len, size_t align,
545		struct csum_state *css)
546{
547	size_t padlen;
548	int res;
549
550	res = write_out_data(outfile, data, len, css);
551	if (res)
552		return res;
553
554	padlen = ALIGN(len,align) - len;
555	res = write_out_padding(outfile, padlen, 0xFF, css);
556
557	return res;
558}
559
560
561int
562write_out_header(FILE *outfile, struct zyn_rombin_hdr *hdr)
563{
564	struct zyn_rombin_hdr t;
565
566	errno = 0;
567	if (fseek(outfile, 0, SEEK_SET) != 0) {
568		ERRS("fseek failed on output file");
569		return -1;
570	}
571
572	/* setup temporary header fields */
573	memset(&t, 0, sizeof(t));
574	t.addr = HOST_TO_BE32(hdr->addr);
575	memcpy(&t.sig, ROMBIN_SIGNATURE, ROMBIN_SIG_LEN);
576	t.type = hdr->type;
577	t.flags = hdr->flags;
578	t.osize = HOST_TO_BE32(hdr->osize);
579	t.csize = HOST_TO_BE32(hdr->csize);
580	t.ocsum = HOST_TO_BE16(hdr->ocsum);
581	t.ccsum = HOST_TO_BE16(hdr->ccsum);
582	t.mmap_addr = HOST_TO_BE32(hdr->mmap_addr);
583
584	DBG(2, "hdr.addr      = 0x%08x", hdr->addr);
585	DBG(2, "hdr.type      = 0x%02x", hdr->type);
586	DBG(2, "hdr.osize     = 0x%08x", hdr->osize);
587	DBG(2, "hdr.csize     = 0x%08x", hdr->csize);
588	DBG(2, "hdr.flags     = 0x%02x", hdr->flags);
589	DBG(2, "hdr.ocsum     = 0x%04x", hdr->ocsum);
590	DBG(2, "hdr.ccsum     = 0x%04x", hdr->ccsum);
591	DBG(2, "hdr.mmap_addr = 0x%08x", hdr->mmap_addr);
592
593	return write_out_data(outfile, (uint8_t *)&t, sizeof(t), NULL);
594}
595
596
597int
598write_out_mmap(FILE *outfile, struct fw_mmap *mmap, struct csum_state *css)
599{
600	struct zyn_mmt_hdr *mh;
601	uint8_t buf[MMAP_DATA_SIZE];
602	uint32_t user_size;
603	char *data;
604	int res;
605
606	memset(buf, 0, sizeof(buf));
607
608	mh = (struct zyn_mmt_hdr *)buf;
609
610	/* TODO: needs to recreate the memory map too? */
611	mh->count=0;
612
613	/* Build user data section */
614	data = buf+sizeof(*mh);
615	data += sprintf(data, "Vendor 1 %d", board->vendor);
616	*data++ = '\0';
617	data += sprintf(data, "Model 1 %d", BE16_TO_HOST(board->model));
618	*data++ = '\0';
619	/* TODO: make hardware version configurable? */
620	data += sprintf(data, "HwVerRange 2 %d %d", 0, 0);
621	*data++ = '\0';
622
623	user_size = (uint8_t *)data - buf;
624	mh->user_start= HOST_TO_BE32(mmap->addr+sizeof(*mh));
625	mh->user_end= HOST_TO_BE32(mmap->addr+user_size);
626	mh->csum = HOST_TO_BE16(csum_buf(buf+sizeof(*mh), user_size));
627
628	res = write_out_data(outfile, buf, sizeof(buf), css);
629
630	return res;
631}
632
633
634int
635block_stat_file(struct fw_block *block)
636{
637	struct stat st;
638	int res;
639
640	if (block->file_name == NULL)
641		return 0;
642
643	res = stat(block->file_name, &st);
644	if (res){
645		ERRS("stat failed on %s", block->file_name);
646		return res;
647	}
648
649	block->file_size = st.st_size;
650	return 0;
651}
652
653
654int
655read_magic(uint16_t *magic)
656{
657	FILE *f;
658	int res;
659
660	errno = 0;
661	f = fopen(bootext_block->file_name,"r");
662	if (errno) {
663		ERRS("unable to open file: %s", bootext_block->file_name);
664		return -1;
665	}
666
667	errno = 0;
668	fread(magic, 2, 1, f);
669	if (errno != 0) {
670		ERRS("unable to read from file: %s", bootext_block->file_name);
671		res = -1;
672		goto err;
673	}
674
675	res = 0;
676
677err:
678	fclose(f);
679	return res;
680}
681
682
683int
684write_out_file(FILE *outfile, char *name, size_t len, struct csum_state *css)
685{
686	char buf[FILE_BUF_LEN];
687	size_t buflen = sizeof(buf);
688	FILE *f;
689	int res;
690
691	DBG(2, "writing out file, name=%s, len=%zu",
692		name, len);
693
694	errno = 0;
695	f = fopen(name,"r");
696	if (errno) {
697		ERRS("unable to open file: %s", name);
698		return -1;
699	}
700
701	while (len > 0) {
702		if (len < buflen)
703			buflen = len;
704
705		/* read data from source file */
706		errno = 0;
707		fread(buf, buflen, 1, f);
708		if (errno != 0) {
709			ERRS("unable to read from file: %s",name);
710			res = -1;
711			break;
712		}
713
714		res = write_out_data(outfile, buf, buflen, css);
715		if (res)
716			break;
717
718		len -= buflen;
719	}
720
721	fclose(f);
722	return res;
723}
724
725
726int
727write_out_block(FILE *outfile, struct fw_block *block, struct csum_state *css)
728{
729	int res;
730
731	if (block == NULL)
732		return 0;
733
734	if (block->file_name == NULL)
735		return 0;
736
737	if (block->file_size == 0)
738		return 0;
739
740	res = write_out_file(outfile, block->file_name,
741			block->file_size, css);
742	return res;
743}
744
745
746int
747write_out_image(FILE *outfile)
748{
749	struct fw_block *block;
750	struct fw_mmap mmap;
751	struct zyn_rombin_hdr hdr;
752	struct csum_state css;
753	int i, res;
754	uint32_t offset;
755	uint32_t padlen;
756	uint16_t csum;
757	uint16_t t;
758
759	/* setup header fields */
760	memset(&hdr, 0, sizeof(hdr));
761	hdr.addr = board->code_start;
762	hdr.type = OBJECT_TYPE_BOOTEXT;
763	hdr.flags = ROMBIN_FLAG_OCSUM;
764
765	offset = board->romio_offs;
766
767	res = write_out_header(outfile, &hdr);
768	if (res)
769		return res;
770
771	offset += sizeof(hdr);
772
773	csum_init(&css);
774	res = write_out_block(outfile, bootext_block, &css);
775	if (res)
776		return res;
777
778	offset += bootext_block->file_size;
779	if (offset > (board->romio_offs + board->bootext_size)) {
780		ERR("bootext file '%s' is too big", bootext_block->file_name);
781		return -1;
782	}
783
784	padlen = ALIGN(offset, MMAP_ALIGN) - offset;
785	res = write_out_padding(outfile, padlen, 0xFF, &css);
786	if (res)
787		return res;
788
789	offset += padlen;
790
791	mmap.addr = board->flash_base + offset;
792	res = write_out_mmap(outfile, &mmap, &css);
793	if (res)
794		return res;
795
796	offset += MMAP_DATA_SIZE;
797
798	if ((offset - board->romio_offs) < board->bootext_size) {
799		padlen = board->romio_offs + board->bootext_size - offset;
800		res = write_out_padding(outfile, padlen, 0xFF, &css);
801		if (res)
802			return res;
803		offset += padlen;
804
805		DBG(2, "bootext end at %08x", offset);
806	}
807
808	for (i = 0; i < num_blocks; i++) {
809		block = &blocks[i];
810
811		if (block->type == BLOCK_TYPE_BOOTEXT)
812			continue;
813
814		padlen = ALIGN(offset, block->align) - offset;
815		res = write_out_padding(outfile, padlen, 0xFF, &css);
816		if (res)
817			return res;
818		offset += padlen;
819
820		res = write_out_block(outfile, block, &css);
821		if (res)
822			return res;
823		offset += block->file_size;
824	}
825
826	padlen = ALIGN(offset, 4) - offset;
827	res = write_out_padding(outfile, padlen, 0xFF, &css);
828	if (res)
829		return res;
830	offset += padlen;
831
832	csum = csum_get(&css);
833	hdr.mmap_addr = mmap.addr;
834	hdr.osize = 2;
835
836	res = read_magic(&hdr.ocsum);
837	if (res)
838		return res;
839	hdr.ocsum = BE16_TO_HOST(hdr.ocsum);
840
841	if (csum <= hdr.ocsum)
842		t = hdr.ocsum - csum;
843	else
844		t = hdr.ocsum - csum - 1;
845
846	DBG(2, "ocsum=%04x, csum=%04x, fix=%04x", hdr.ocsum, csum, t);
847
848	t = HOST_TO_BE16(t);
849	res = write_out_data(outfile, (uint8_t *)&t, 2, NULL);
850	if (res)
851		return res;
852
853
854	res = write_out_header(outfile, &hdr);
855
856	return res;
857}
858
859
860struct board_info *
861find_board(char *name)
862{
863	struct board_info *ret;
864	struct board_info *board;
865
866	ret = NULL;
867	for (board = boards; board->name != NULL; board++){
868		if (strcasecmp(name, board->name) == 0) {
869			ret = board;
870			break;
871		}
872	};
873
874	return ret;
875}
876
877
878int
879parse_opt_board(char ch, char *arg)
880{
881
882	DBG(1,"parsing board option: -%c %s", ch, arg);
883
884	if (board != NULL) {
885		ERR("only one board option allowed");
886		return -1;
887	}
888
889	if (required_arg(ch, arg))
890		return -1;
891
892	board = find_board(arg);
893	if (board == NULL){
894		ERR("invalid/unknown board specified: %s", arg);
895		return -1;
896	}
897
898	return 0;
899}
900
901
902int
903parse_opt_ofname(char ch, char *arg)
904{
905
906	if (ofname != NULL) {
907		ERR("only one output file allowed");
908		return -1;
909	}
910
911	if (required_arg(ch, arg))
912		return -1;
913
914	ofname = arg;
915
916	return 0;
917}
918
919
920int
921parse_opt_block(char ch, char *arg)
922{
923	char buf[MAX_ARG_LEN];
924	char *argv[MAX_ARG_COUNT];
925	int argc;
926	char *p;
927	struct fw_block *block;
928	int i;
929
930	if ( num_blocks >= MAX_NUM_BLOCKS ) {
931		ERR("too many blocks specified");
932		return -1;
933	}
934
935	block = &blocks[num_blocks++];
936
937	/* setup default field values */
938	block->padc = 0xFF;
939
940	switch(ch) {
941	case 'b':
942		if (bootext_block) {
943			ERR("only one boot extension block allowed");
944			break;
945		}
946		block->type = BLOCK_TYPE_BOOTEXT;
947		bootext_block = block;
948		break;
949	case 'r':
950		block->type = BLOCK_TYPE_RAW;
951		break;
952	}
953
954	argc = parse_arg(arg, buf, argv);
955
956	i = 0;
957	p = argv[i++];
958	if (is_empty_arg(p)) {
959		ERR("no file specified in %s", arg);
960		return -1;
961	} else {
962		block->file_name = strdup(p);
963		if (block->file_name == NULL) {
964			ERR("not enough memory");
965			return -1;
966		}
967	}
968
969	if (block->type == BLOCK_TYPE_BOOTEXT)
970		return 0;
971
972	p = argv[i++];
973	if (!is_empty_arg(p)) {
974		if (str2u32(p, &block->align) != 0) {
975			ERR("invalid block align in %s", arg);
976			return -1;
977		}
978	}
979
980	return 0;
981}
982
983
984int
985calc_block_offsets(int type, uint32_t *offset)
986{
987	struct fw_block *block;
988	uint32_t next_offs;
989	uint32_t avail;
990	int i, res;
991
992	DBG(1,"calculating block offsets, starting with %" PRIu32,
993		*offset);
994
995	res = 0;
996	for (i = 0; i < num_blocks; i++) {
997		block = &blocks[i];
998
999		if (block->type != type)
1000			continue;
1001
1002		next_offs = ALIGN(*offset, block->align);
1003		avail = board->flash_size - next_offs;
1004		if (block->file_size > avail) {
1005			ERR("file %s is too big, offset = %u, size=%u,"
1006				" avail = %u, align = %u", block->file_name,
1007				(unsigned)next_offs,
1008				(unsigned)block->file_size,
1009				(unsigned)avail,
1010				(unsigned)block->align);
1011			res = -1;
1012			break;
1013		}
1014
1015		block->padlen = next_offs - *offset;
1016		*offset += block->file_size;
1017	}
1018
1019	return res;
1020}
1021
1022int
1023process_blocks(void)
1024{
1025	struct fw_block *block;
1026	uint32_t offset;
1027	int i;
1028	int res;
1029
1030	/* collecting file stats */
1031	for (i = 0; i < num_blocks; i++) {
1032		block = &blocks[i];
1033		res = block_stat_file(block);
1034		if (res)
1035			return res;
1036	}
1037
1038	offset = board->romio_offs + bootext_block->file_size;
1039	res = calc_block_offsets(BLOCK_TYPE_RAW, &offset);
1040
1041	return res;
1042}
1043
1044
1045int
1046main(int argc, char *argv[])
1047{
1048	int optinvalid = 0;   /* flag for invalid option */
1049	int c;
1050	int res = EXIT_FAILURE;
1051
1052	FILE *outfile;
1053
1054	progname=basename(argv[0]);
1055
1056	opterr = 0;  /* could not print standard getopt error messages */
1057	while ( 1 ) {
1058		optinvalid = 0;
1059
1060		c = getopt(argc, argv, "b:B:ho:r:v");
1061		if (c == -1)
1062			break;
1063
1064		switch (c) {
1065		case 'b':
1066		case 'r':
1067			optinvalid = parse_opt_block(c,optarg);
1068			break;
1069		case 'B':
1070			optinvalid = parse_opt_board(c,optarg);
1071			break;
1072		case 'o':
1073			optinvalid = parse_opt_ofname(c,optarg);
1074			break;
1075		case 'v':
1076			verblevel++;
1077			break;
1078		case 'h':
1079			usage(EXIT_SUCCESS);
1080			break;
1081		default:
1082			optinvalid = 1;
1083			break;
1084		}
1085		if (optinvalid != 0 ) {
1086			ERR("invalid option: -%c", optopt);
1087			goto out;
1088		}
1089	}
1090
1091	if (board == NULL) {
1092		ERR("no board specified");
1093		goto out;
1094	}
1095
1096	if (ofname == NULL) {
1097		ERR("no output file specified");
1098		goto out;
1099	}
1100
1101	if (optind < argc) {
1102		ERR("invalid option: %s", argv[optind]);
1103		goto out;
1104	}
1105
1106	if (process_blocks() != 0) {
1107		goto out;
1108	}
1109
1110	outfile = fopen(ofname, "w");
1111	if (outfile == NULL) {
1112		ERRS("could not open \"%s\" for writing", ofname);
1113		goto out;
1114	}
1115
1116	if (write_out_image(outfile) != 0)
1117		goto out_flush;
1118
1119	DBG(1,"Image file %s completed.", ofname);
1120
1121	res = EXIT_SUCCESS;
1122
1123out_flush:
1124	fflush(outfile);
1125	fclose(outfile);
1126	if (res != EXIT_SUCCESS) {
1127		unlink(ofname);
1128	}
1129out:
1130	return res;
1131}
1132