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