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