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