add firmware generation tool for ZyXEL boards
[openwrt.git] / tools / firmware-utils / src / mkcsysimg.c
1 /*
2  *  $Id$
3  *
4  *  Copyright (C) 2007 Gabor Juhos <juhosg@freemail.hu>
5  *
6  *  This program was based on the code found in various Linux
7  *  source tarballs released by Edimax for it's devices.
8  *  Original author: David Hsu <davidhsu@realtek.com.tw>
9  *
10  *  This program is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU General Public License
12  *  as published by the Free Software Foundation; either version 2
13  *  of the License, or (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the
22  *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  *  Boston, MA  02110-1301, USA.
24  */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdint.h>
29 #include <string.h>
30 #include <unistd.h>     /* for unlink() */
31 #include <libgen.h>
32 #include <getopt.h>     /* for getopt() */
33 #include <stdarg.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36 #include <endian.h>     /* for __BYTE_ORDER */
37 #if defined(__CYGWIN__)
38 #  include <byteswap.h>
39 #endif
40
41 #include "csysimg.h"
42
43 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
44 #  define HOST_TO_LE16(x)       (x)
45 #  define HOST_TO_LE32(x)       (x)
46 #  define LE16_TO_HOST(x)       (x)
47 #  define LE32_TO_HOST(x)       (x)
48 #else
49 #  define HOST_TO_LE16(x)       bswap_16(x)
50 #  define HOST_TO_LE32(x)       bswap_32(x)
51 #  define LE16_TO_HOST(x)       bswap_16(x)
52 #  define LE32_TO_HOST(x)       bswap_32(x)
53 #endif
54
55 #define ALIGN(x,y)      ((x)+((y)-1)) & ~((y)-1)
56
57 #define MAX_NUM_BLOCKS  8
58 #define MAX_ARG_COUNT   32
59 #define MAX_ARG_LEN     1024
60 #define FILE_BUF_LEN    (16*1024)
61 #define CSYS_PADC       0xFF
62
63 #define BLOCK_TYPE_BOOT 0
64 #define BLOCK_TYPE_CONF 1
65 #define BLOCK_TYPE_WEBP 2
66 #define BLOCK_TYPE_CODE 3
67 #define BLOCK_TYPE_XTRA 4
68
69
70 struct csum_state{
71         int     size;
72         uint16_t val;
73         uint16_t tmp;
74         int     odd;
75 };
76
77
78 struct csys_block {
79         int             type;   /* type of the block */
80
81         int             need_file;
82         char            *file_name;     /* name of the file */
83         uint32_t        file_size;      /* length of the file */
84
85         uint32_t        size;
86         int             size_set;
87         uint8_t         padc;
88
89         uint32_t        size_hdr;
90         uint32_t        size_csum;
91         uint32_t        size_avail;
92
93         unsigned char   sig[SIG_LEN];
94         uint32_t        addr;
95         int             addr_set;
96         struct csum_state *css;
97 };
98
99
100 struct board_info {
101         char *model;
102         char *name;
103         uint32_t flash_size;
104
105         char sig_boot[SIG_LEN];
106         char sig_conf[SIG_LEN];
107         char sig_webp[SIG_LEN];
108
109         uint32_t boot_size;
110         uint32_t conf_size;
111         uint32_t webp_size;
112         uint32_t webp_size_max;
113         uint32_t code_size;
114
115         uint32_t addr_code;
116         uint32_t addr_webp;
117 };
118
119 #define BOARD(m, n, f, sigb, sigw, bs, cs, ws, ac, aw) {\
120         .model = m, .name = n, .flash_size = f<<20, \
121         .sig_boot = sigb, .sig_conf = SIG_CONF, .sig_webp = sigw, \
122         .boot_size = bs, .conf_size = cs, \
123         .webp_size = ws, .webp_size_max = 3*0x10000, \
124         .addr_code = ac, .addr_webp = aw \
125         }
126
127 #define BOARD_ADM(m,n,f, sigw) BOARD(m,n,f, ADM_BOOT_SIG, sigw, \
128         ADM_BOOT_SIZE, ADM_CONF_SIZE, ADM_WEBP_SIZE, \
129         ADM_CODE_ADDR, ADM_WEBP_ADDR)
130
131
132 /*
133  * Globals
134  */
135 char *progname;
136 char *ofname = NULL;
137 int verblevel = 0;
138 int invalid_causes_error = 1;
139 int keep_invalid_images = 0;
140
141 struct board_info *board = NULL;
142
143 struct csys_block *boot_block = NULL;
144 struct csys_block *conf_block = NULL;
145 struct csys_block *webp_block = NULL;
146 struct csys_block *code_block = NULL;
147
148 struct csys_block blocks[MAX_NUM_BLOCKS];
149 int num_blocks = 0;
150
151 static struct board_info boards[] = {
152         /* The original Edimax products */
153         BOARD_ADM("BR-6104K", "Edimax BR-6104K", 2, SIG_BR6104K),
154         BOARD_ADM("BR-6104KP", "Edimax BR-6104KP", 2, SIG_BR6104KP),
155         BOARD_ADM("BR-6114WG", "Edimax BR-6114WG", 2, SIG_BR6114WG),
156         BOARD_ADM("BR-6524K", "Edimax BR-6524K", 2, SIG_BR6524K),
157         BOARD_ADM("BR-6524KP", "Edimax BR-6524KP", 2, SIG_BR6524KP),
158         BOARD_ADM("BR-6524WG", "Edimax BR-6524WG", 4, SIG_BR6524WG),
159         BOARD_ADM("BR-6524WP", "Edimax BR-6524WP", 4, SIG_BR6524WP),
160         BOARD_ADM("BR-6541K", "Edimax BR-6541K", 2, SIG_BR6541K),
161         BOARD_ADM("BR-6541KP", "Edimax BR-6541K", 2, SIG_BR6541KP),
162         BOARD_ADM("BR-6541WP", "Edimax BR-6541WP", 4, SIG_BR6541WP),
163         BOARD_ADM("EW-7207APg", "Edimax EW-7207APg", 2, SIG_EW7207APg),
164         BOARD_ADM("PS-1205UWg", "Edimax PS-1205UWg", 2, SIG_PS1205UWg),
165         BOARD_ADM("PS-3205U", "Edimax PS-3205U", 2, SIG_PS3205U),
166         BOARD_ADM("PS-3205UWg", "Edimax PS-3205UWg", 2, SIG_PS3205UWg),
167
168         /* Hawking products */
169         BOARD_ADM("H2BR4", "Hawking H2BR4", 2, SIG_H2BR4),
170         BOARD_ADM("H2WR54G", "Hawking H2WR54G", 4, SIG_H2WR54G),
171
172         /* Planet products */
173         BOARD_ADM("XRT-401D", "Planet XRT-401D", 2, SIG_XRT401D),
174         BOARD_ADM("XRT-402D", "Planet XRT-402D", 2, SIG_XRT402D),
175
176         {.model = NULL}
177 };
178
179 /*
180  * Message macros
181  */
182 #define ERR(fmt, ...) do { \
183         fflush(0); \
184         fprintf(stderr, "[%s] *** error: " fmt "\n", progname, ## __VA_ARGS__ ); \
185 } while (0)
186
187 #define ERRS(fmt, ...) do { \
188         int save = errno; \
189         fflush(0); \
190         fprintf(stderr, "[%s] *** error: " fmt "\n", progname, ## __VA_ARGS__ \
191                 , strerror(save)); \
192 } while (0)
193
194 #define WARN(fmt, ...) do { \
195         fprintf(stderr, "[%s] *** warning: " fmt "\n", progname, ## __VA_ARGS__ ); \
196 } while (0)
197
198 #define DBG(lev, fmt, ...) do { \
199         if (verblevel < lev) \
200                 break;\
201         fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \
202 } while (0)
203
204 #define ERR_FATAL               -1
205 #define ERR_INVALID_IMAGE       -2
206
207 void
208 usage(int status)
209 {
210         FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
211         struct board_info *board;
212
213         fprintf(stream, "Usage: %s [OPTIONS...] <file>\n", progname);
214         fprintf(stream,
215 "\n"
216 "Options:\n"
217 "  -B <board>      create image for the board specified with <board>.\n"
218 "                  valid <board> values:\n"
219         );
220         for (board = boards; board->model != NULL; board++){
221                 fprintf(stream,
222 "                  %-12s: %s\n",
223                  board->model, board->name);
224         };
225         fprintf(stream,
226 "  -d              don't throw error on invalid images\n"
227 "  -k              keep invalid images\n"
228 "  -b <file>[:<len>[:<padc>]]\n"
229 "                  add boot code to the image\n"
230 "  -c <file>[:<len>[:<padc>]]\n"
231 "                  add configuration settings to the image\n"
232 "  -r <file>:[<addr>][:<len>[:<padc>]]\n"
233 "                  add runtime code to the image\n"
234 "  -w [<file>:[<addr>][:<len>[:<padc>]]]\n"
235 "                  add webpages to the image\n"
236 "  -x <file>[:<len>[:<padc>]]\n"
237 "                  add extra data at the end of the image\n"
238 "  -h              show this screen\n"
239 "Parameters:\n"
240 "  <file>          write output to the file <file>\n"
241         );
242
243         exit(status);
244 }
245
246
247 /*
248  * argument parsing
249  */
250 int
251 str2u32(char *arg, uint32_t *val)
252 {
253         char *err = NULL;
254         uint32_t t;
255
256         errno=0;
257         t = strtoul(arg, &err, 0);
258         if (errno || (err==arg) || ((err != NULL) && *err)) {
259                 return -1;
260         }
261
262         *val = t;
263         return 0;
264 }
265
266
267 int
268 str2u16(char *arg, uint16_t *val)
269 {
270         char *err = NULL;
271         uint32_t t;
272
273         errno=0;
274         t = strtoul(arg, &err, 0);
275         if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x10000)) {
276                 return -1;
277         }
278
279         *val = t & 0xFFFF;
280         return 0;
281 }
282
283 int
284 str2u8(char *arg, uint8_t *val)
285 {
286         char *err = NULL;
287         uint32_t t;
288
289         errno=0;
290         t = strtoul(arg, &err, 0);
291         if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x100)) {
292                 return -1;
293         }
294
295         *val = t & 0xFF;
296         return 0;
297 }
298
299 int
300 str2sig(char *arg, uint32_t *sig)
301 {
302         if (strlen(arg) != 4)
303                 return -1;
304
305         *sig = arg[0] | (arg[1] << 8) | (arg[2] << 16) | (arg[3] << 24);
306
307         return 0;
308 }
309
310
311 int
312 parse_arg(char *arg, char *buf, char *argv[])
313 {
314         int res = 0;
315         size_t argl;
316         char *tok;
317         char **ap = &buf;
318         int i;
319
320         memset(argv, 0, MAX_ARG_COUNT * sizeof(void *));
321
322         if ((arg == NULL)) {
323                 /* no arguments */
324                 return 0;
325         }
326
327         argl = strlen(arg);
328         if (argl == 0) {
329                 /* no arguments */
330                 return 0;
331         }
332
333         if (argl >= MAX_ARG_LEN) {
334                 /* argument is too long */
335                 argl = MAX_ARG_LEN-1;
336         }
337
338         memcpy(buf, arg, argl);
339         buf[argl] = '\0';
340
341         for (i = 0; i < MAX_ARG_COUNT; i++) {
342                 tok = strsep(ap, ":");
343                 if (tok == NULL) {
344                         break;
345                 }
346 #if 0
347                 else if (tok[0] == '\0') {
348                         break;
349                 }
350 #endif
351                 argv[i] = tok;
352                 res++;
353         }
354
355         return res;
356 }
357
358
359 int
360 required_arg(char c, char *arg)
361 {
362         if (arg == NULL || *arg != '-')
363                 return 0;
364
365         ERR("option -%c requires an argument\n", c);
366         return ERR_FATAL;
367 }
368
369
370 int
371 is_empty_arg(char *arg)
372 {
373         int ret = 1;
374         if (arg != NULL) {
375                 if (*arg) ret = 0;
376         };
377         return ret;
378 }
379
380
381 void
382 csum8_update(uint8_t *p, uint32_t len, struct csum_state *css)
383 {
384         for ( ; len > 0; len --) {
385                 css->val += *p++;
386         }
387 }
388
389
390 uint16_t
391 csum8_get(struct csum_state *css)
392 {
393         uint8_t t;
394
395         t = css->val;
396         return ~t + 1;
397 }
398
399
400 void
401 csum16_update(uint8_t *p, uint32_t len, struct csum_state *css)
402 {
403         uint16_t t;
404
405         if (css->odd) {
406                 t = css->tmp + (p[0]<<8);
407                 css->val += LE16_TO_HOST(t);
408                 css->odd = 0;
409                 len--;
410                 p++;
411         }
412
413         for ( ; len > 1; len -= 2, p +=2 ) {
414                 t = p[0] + (p[1] << 8);
415                 css->val += LE16_TO_HOST(t);
416         }
417
418         if (len == 1) {
419                 css->tmp = p[0];
420                 css->odd = 1;
421         }
422 }
423
424
425 uint16_t
426 csum16_get(struct csum_state *css)
427 {
428         char pad = 0;
429
430         csum16_update(&pad, 1, css);
431         return ~css->val + 1;
432 }
433
434
435 void
436 csum_init(struct csum_state *css, int size)
437 {
438         css->val = 0;
439         css->tmp = 0;
440         css->odd = 0;
441         css->size = size;
442 }
443
444
445 void
446 csum_update(uint8_t *p, uint32_t len, struct csum_state *css)
447 {
448         switch (css->size) {
449         case 1:
450                 csum8_update(p,len,css);
451                 break;
452         case 2:
453                 csum16_update(p,len,css);
454                 break;
455         }
456 }
457
458
459 uint16_t
460 csum_get(struct csum_state *css)
461 {
462         uint16_t ret;
463
464         switch (css->size) {
465         case 1:
466                 ret = csum8_get(css);
467                 break;
468         case 2:
469                 ret = csum16_get(css);
470                 break;
471         }
472
473         return ret;
474 }
475
476
477 /*
478  * routines to write data to the output file
479  */
480 int
481 write_out_data(FILE *outfile, uint8_t *data, size_t len,
482                 struct csum_state *css)
483 {
484         errno = 0;
485
486         fwrite(data, len, 1, outfile);
487         if (errno) {
488                 ERRS("unable to write output file");
489                 return ERR_FATAL;
490         }
491
492         if (css) {
493                 csum_update(data, len, css);
494         }
495
496         return 0;
497 }
498
499
500 int
501 write_out_padding(FILE *outfile, size_t len, uint8_t padc,
502                  struct csum_state *css)
503 {
504         uint8_t buf[512];
505         size_t buflen = sizeof(buf);
506         int err;
507
508         memset(buf, padc, buflen);
509         while (len > 0) {
510                 if (len < buflen)
511                         buflen = len;
512
513                 err = write_out_data(outfile, buf, buflen, css);
514                 if (err)
515                         return err;
516
517                 len -= buflen;
518         }
519
520         return 0;
521 }
522
523
524 int
525 block_stat_file(struct csys_block *block)
526 {
527         struct stat st;
528         int err;
529
530         if (block->file_name == NULL)
531                 return 0;
532
533         err = stat(block->file_name, &st);
534         if (err){
535                 ERRS("stat failed on %s", block->file_name);
536                 return ERR_FATAL;
537         }
538
539         block->file_size = st.st_size;
540         return 0;
541 }
542
543
544 int
545 block_writeout_hdr(FILE *outfile, struct csys_block *block)
546 {
547         struct csys_header hdr;
548         int res;
549
550         if (block->size_hdr == 0)
551                 return 0;
552
553         /* setup header fields */
554         memcpy(hdr.sig, block->sig, 4);
555         hdr.addr = HOST_TO_LE32(block->addr);
556         hdr.size = HOST_TO_LE32(block->size-block->size_hdr);
557
558         DBG(1,"writing header for block");
559         res = write_out_data(outfile, (uint8_t *)&hdr, sizeof(hdr),NULL);
560         return res;
561
562 }
563
564
565 int
566 block_writeout_file(FILE *outfile, struct csys_block *block)
567 {
568         char buf[FILE_BUF_LEN];
569         size_t buflen = sizeof(buf);
570         FILE *f;
571         size_t len;
572         int res;
573
574         if (block->file_name == NULL)
575                 return 0;
576
577         if (block->file_size == 0)
578                 return 0;
579
580         errno = 0;
581         f = fopen(block->file_name,"r");
582         if (errno) {
583                 ERRS("unable to open file: %s", block->file_name);
584                 return ERR_FATAL;
585         }
586
587         len = block->file_size;
588         while (len > 0) {
589                 if (len < buflen)
590                         buflen = len;
591
592                 /* read data from source file */
593                 errno = 0;
594                 fread(buf, buflen, 1, f);
595                 if (errno != 0) {
596                         ERRS("unable to read from file: %s", block->file_name);
597                         res = ERR_FATAL;
598                         break;
599                 }
600
601                 res = write_out_data(outfile, buf, buflen, block->css);
602                 if (res)
603                         break;
604
605                 len -= buflen;
606         }
607
608         fclose(f);
609         return res;
610 }
611
612
613 int
614 block_writeout_data(FILE *outfile, struct csys_block *block)
615 {
616         int res;
617         size_t padlen;
618
619         res = block_writeout_file(outfile, block);
620         if (res)
621                 return res;
622
623         /* write padding data if neccesary */
624         padlen = block->size_avail - block->file_size;
625         DBG(1,"padding block, length=%d", padlen);
626         res = write_out_padding(outfile, padlen, block->padc, block->css);
627
628         return res;
629 }
630
631
632 int
633 block_writeout_csum(FILE *outfile, struct csys_block *block)
634 {
635         uint16_t csum;
636         int res;
637
638         if (block->size_csum == 0)
639                 return 0;
640
641         DBG(1,"writing checksum for block");
642         csum = HOST_TO_LE16(csum_get(block->css));
643         res = write_out_data(outfile, (uint8_t *)&csum, block->size_csum, NULL);
644
645         return res;
646 }
647
648
649 int
650 block_writeout(FILE *outfile, struct csys_block *block)
651 {
652         int res;
653         struct csum_state css;
654
655         res = 0;
656
657         if (block == NULL)
658                 return res;
659
660         block->css = NULL;
661
662         DBG(2, "writing block, file=%s, file_size=%d, space=%d",
663                 block->file_name, block->file_size, block->size_avail);
664         res = block_writeout_hdr(outfile, block);
665         if (res)
666                 return res;
667
668         if (block->size_csum != 0) {
669                 block->css = &css;
670                 csum_init(&css, block->size_csum);
671         }
672
673         res = block_writeout_data(outfile, block);
674         if (res)
675                 return res;
676
677         res = block_writeout_csum(outfile, block);
678         if (res)
679                 return res;
680
681         return res;
682 }
683
684
685 int
686 write_out_blocks(FILE *outfile)
687 {
688         struct csys_block *block;
689         int i, res;
690
691         res = block_writeout(outfile, boot_block);
692         if (res)
693                 return res;
694
695         res = block_writeout(outfile, conf_block);
696         if (res)
697                 return res;
698
699         res = block_writeout(outfile, webp_block);
700         if (res)
701                 return res;
702
703         res = block_writeout(outfile, code_block);
704         if (res)
705                 return res;
706
707         res = 0;
708         for (i=0; i < num_blocks; i++) {
709                 block = &blocks[i];
710
711                 if (block->type != BLOCK_TYPE_XTRA)
712                         continue;
713
714                 res = block_writeout(outfile, block);
715                 if (res)
716                         break;
717         }
718
719         return res;
720 }
721
722
723 struct board_info *
724 find_board(char *model)
725 {
726         struct board_info *ret;
727         struct board_info *board;
728
729         ret = NULL;
730         for (board = boards; board->model != NULL; board++){
731                 if (strcmp(model, board->model) == 0) {
732                         ret = board;
733                         break;
734                 }
735         };
736
737         return ret;
738 }
739
740
741 int
742 parse_opt_board(char ch, char *arg)
743 {
744
745         DBG(1,"parsing board option: -%c %s", ch, arg);
746
747         if (board != NULL) {
748                 ERR("only one board option allowed");
749                 return ERR_FATAL;
750         }
751
752         if (required_arg(ch, arg))
753                 return ERR_FATAL;
754
755         board = find_board(arg);
756         if (board == NULL){
757                 ERR("invalid/unknown board specified: %s", arg);
758                 return ERR_FATAL;
759         }
760
761         return 0;
762 }
763
764
765 int
766 parse_opt_block(char ch, char *arg)
767 {
768         char buf[MAX_ARG_LEN];
769         char *argv[MAX_ARG_COUNT];
770         int argc;
771         char *p;
772         struct csys_block *block;
773         int i;
774
775         if ( num_blocks > MAX_NUM_BLOCKS ) {
776                 ERR("too many blocks specified");
777                 return ERR_FATAL;
778         }
779
780         block = &blocks[num_blocks];
781
782         /* setup default field values */
783         block->need_file = 1;
784         block->padc = 0xFF;
785
786         switch (ch) {
787         case 'b':
788                 if (boot_block) {
789                         WARN("only one boot block allowed");
790                         break;
791                 }
792                 block->type = BLOCK_TYPE_BOOT;
793                 boot_block = block;
794                 break;
795         case 'c':
796                 if (conf_block) {
797                         WARN("only one config block allowed");
798                         break;
799                 }
800                 block->type = BLOCK_TYPE_CONF;
801                 conf_block = block;
802                 break;
803         case 'w':
804                 if (webp_block) {
805                         WARN("only one web block allowed");
806                         break;
807                 }
808                 block->type = BLOCK_TYPE_WEBP;
809                 block->size_hdr = sizeof(struct csys_header);
810                 block->size_csum = 1;
811                 block->need_file = 0;
812                 webp_block = block;
813                 break;
814         case 'r':
815                 if (code_block) {
816                         WARN("only one runtime block allowed");
817                         break;
818                 }
819                 block->type = BLOCK_TYPE_CODE;
820                 block->size_hdr = sizeof(struct csys_header);
821                 block->size_csum = 2;
822                 code_block = block;
823                 break;
824         case 'x':
825                 block->type = BLOCK_TYPE_XTRA;
826                 break;
827         default:
828                 ERR("unknown block type \"%c\"", ch);
829                 return ERR_FATAL;
830         }
831
832         argc = parse_arg(arg, buf, argv);
833
834         i = 0;
835         p = argv[i++];
836         if (!is_empty_arg(p)) {
837                 block->file_name = strdup(p);
838                 if (block->file_name == NULL) {
839                         ERR("not enough memory");
840                         return ERR_FATAL;
841                 }
842         } else if (block->need_file){
843                 ERR("no file specified in %s", arg);
844                 return ERR_FATAL;
845         }
846
847         if (block->size_hdr) {
848                 p = argv[i++];
849                 if (!is_empty_arg(p)) {
850                         if (str2u32(p, &block->addr) != 0) {
851                                 ERR("invalid start address in %s", arg);
852                                 return ERR_FATAL;
853                         }
854                         block->addr_set = 1;
855                 }
856         }
857
858         p = argv[i++];
859         if (!is_empty_arg(p)) {
860                 if (str2u32(p, &block->size) != 0) {
861                         ERR("invalid block size in %s", arg);
862                         return ERR_FATAL;
863                 }
864                 block->size_set = 1;
865         }
866
867         p = argv[i++];
868         if (!is_empty_arg(p) && (str2u8(p, &block->padc) != 0)) {
869                 ERR("invalid paddig character in %s", arg);
870                 return ERR_FATAL;
871         }
872
873         num_blocks++;
874
875         return 0;
876 }
877
878
879 int
880 process_blocks(void)
881 {
882         struct csys_block *block;
883         uint32_t size_avail;
884         int i;
885         int res;
886
887         res = 0;
888         /* collecting stats */
889         for (i = 0; i < num_blocks; i++) {
890                 block = &blocks[i];
891                 res = block_stat_file(block);
892                 if (res)
893                         return res;
894         }
895
896         size_avail = board->flash_size;
897
898         /* bootloader */
899         block = boot_block;
900         if (block) {
901                 if (block->size_set) {
902                         board->boot_size= block->size;
903                 } else {
904                         block->size = board->boot_size;
905                 }
906                 if (block->size > size_avail) {
907                         WARN("boot block is too big");
908                         res = ERR_INVALID_IMAGE;
909                 }
910         }
911         size_avail -= board->boot_size;
912
913         /* configuration data */
914         block = conf_block;
915         if (block) {
916                 if (block->size_set) {
917                         board->conf_size = block->size;
918                 } else {
919                         block->size = board->conf_size;
920                 }
921                 if (block->size > size_avail) {
922                         WARN("config block is too big");
923                         res = ERR_INVALID_IMAGE;
924                 }
925
926         }
927         size_avail -= board->conf_size;
928
929         /* webpages */
930         block = webp_block;
931         if (block) {
932                 if (block->size_set == 0)
933                         block->size = board->webp_size;
934                 board->webp_size = block->size;
935                 if (block->size > board->webp_size_max) {
936                         WARN("webpages block is too big");
937                         res = ERR_INVALID_IMAGE;
938                 }
939                 memcpy(block->sig, board->sig_webp, 4);
940                 if (block->addr_set == 0)
941                         block->addr = board->addr_webp;
942         }
943         size_avail -= board->webp_size;
944
945         /* runtime code */
946         block = code_block;
947         if (block) {
948                 if (block->size_set == 0) {
949                         block->size =ALIGN(block->file_size+ block->size_hdr +
950                                 block->size_csum, 0x10000);
951                 }
952                 board->code_size = block->size;
953                 if (board->code_size > size_avail) {
954                         WARN("code block is too big");
955                         res = ERR_INVALID_IMAGE;
956                 }
957                 memcpy(code_block->sig, SIG_CSYS, 4);
958                 if (code_block->addr_set == 0)
959                         code_block->addr = board->addr_code;
960         }
961         size_avail -= board->code_size;
962
963         for (i = 0; i < num_blocks; i++) {
964                 block = &blocks[i];
965
966                 if (block->type != BLOCK_TYPE_XTRA)
967                         continue;
968
969                 if (block->size_set == 0)
970                         block->size = ALIGN(block->file_size, 0x10000);
971
972                 if (block->size > size_avail) {
973                         WARN("file %s is too big, size=%d, avail=%d",
974                                 block->file_name, block->file_size,
975                                 size_avail);
976                         res = ERR_INVALID_IMAGE;
977                 }
978
979                 size_avail -= block->size;
980         }
981
982         for (i = 0; i < num_blocks; i++) {
983                 block = &blocks[i];
984
985                 block->size_avail = block->size - block->size_hdr -
986                         block->size_csum;
987
988                 if (block->size_avail < block->file_size) {
989                         WARN("file %s is too big, size=%d, avail=%d",
990                                 block->file_name, block->file_size,
991                                 block->size_avail);
992                         res = ERR_INVALID_IMAGE;
993                 }
994         }
995
996         return res;
997 }
998
999
1000 int
1001 main(int argc, char *argv[])
1002 {
1003         int optinvalid = 0;   /* flag for invalid option */
1004         int c;
1005         int res = ERR_FATAL;
1006
1007         FILE *outfile;
1008
1009         progname=basename(argv[0]);
1010
1011         opterr = 0;  /* could not print standard getopt error messages */
1012         while ( 1 ) {
1013                 optinvalid = 0;
1014
1015                 c = getopt(argc, argv, "b:B:c:dhkr:vw:x:");
1016                 if (c == -1)
1017                         break;
1018
1019                 switch (c) {
1020                 case 'b':
1021                 case 'c':
1022                 case 'r':
1023                 case 'x':
1024                         optinvalid = parse_opt_block(c,optarg);
1025                         break;
1026                 case 'w':
1027                         if (optarg != NULL && *optarg == '-') {
1028                                 /* rollback */
1029                                 optind--;
1030                                 optarg = NULL;
1031                         }
1032                         optinvalid = parse_opt_block(c,optarg);
1033                         break;
1034                 case 'd':
1035                         invalid_causes_error = 0;
1036                         break;
1037                 case 'k':
1038                         keep_invalid_images = 1;
1039                         break;
1040                 case 'B':
1041                         optinvalid = parse_opt_board(c,optarg);
1042                         break;
1043                 case 'v':
1044                         verblevel++;
1045                         break;
1046                 case 'h':
1047                         usage(EXIT_SUCCESS);
1048                         break;
1049                 default:
1050                         optinvalid = 1;
1051                         break;
1052                 }
1053                 if (optinvalid != 0 ){
1054                         ERR("invalid option: -%c", optopt);
1055                         goto out;
1056                 }
1057         }
1058
1059         if (board == NULL) {
1060                 ERR("no board specified");
1061                 goto out;
1062         }
1063
1064         if (optind == argc) {
1065                 ERR("no output file specified");
1066                 goto out;
1067         }
1068
1069         ofname = argv[optind++];
1070
1071         if (optind < argc) {
1072                 ERR("invalid option: %s", argv[optind]);
1073                 goto out;
1074         }
1075
1076         res = process_blocks();
1077         if (res == ERR_FATAL)
1078                 goto out;
1079
1080         if (res == ERR_INVALID_IMAGE) {
1081                 if (invalid_causes_error)
1082                         res = ERR_FATAL;
1083
1084                 if (keep_invalid_images == 0) {
1085                         WARN("generation of invalid images disabled", ofname);
1086                         goto out;
1087                 }
1088
1089                 WARN("generating invalid image", ofname);
1090         }
1091
1092         outfile = fopen(ofname, "w");
1093         if (outfile == NULL) {
1094                 ERRS("could not open \"%s\" for writing", ofname);
1095                 res = ERR_FATAL;
1096                 goto out;
1097         }
1098
1099         if (write_out_blocks(outfile) != 0) {
1100                 res = ERR_FATAL;
1101                 goto out_flush;
1102         }
1103
1104         DBG(1,"Image file %s completed.", ofname);
1105
1106 out_flush:
1107         fflush(outfile);
1108         fclose(outfile);
1109         if (res == ERR_FATAL) {
1110                 unlink(ofname);
1111         }
1112 out:
1113         if (res == ERR_FATAL)
1114                 return EXIT_FAILURE;
1115
1116         return EXIT_SUCCESS;
1117 }