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