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