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 62struct csum_state{ 63 int odd; 64 uint32_t sum; 65 uint32_t tmp; 66}; 67 68struct 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 81struct 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 90struct 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 */ 105char *progname; 106char *ofname = NULL; 107int verblevel = 0; 108 109struct board_info *board = NULL; 110 111struct fw_block blocks[MAX_NUM_BLOCKS]; 112struct fw_block *bootext_block = NULL; 113int 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 157static 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 */ 272void 273usage(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 */ 306int 307str2u32(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 323int 324str2u16(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 339int 340str2u8(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 355int 356str2sig(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 367int 368parse_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 415int 416required_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 426int 427is_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 437void 438csum_init(struct csum_state *css) 439{ 440 css->odd = 0; 441 css->sum = 0; 442 css->tmp = 0; 443} 444 445 446void 447csum_update(uint8_t *p, uint32_t len, struct csum_state *css) 448{ 449 if (len == 0) 450 return; 451 452 if (css->odd) { 453 css->sum += (css->tmp << 8) + p[0]; 454 if (css->sum > 0xFFFF) { 455 css->sum += 1; 456 css->sum &= 0xFFFF; 457 } 458 css->odd = 0; 459 len--; 460 p++; 461 } 462 463 for ( ; len > 1; len -= 2, p +=2 ) { 464 css->sum += (p[0] << 8) + p[1]; 465 if (css->sum > 0xFFFF) { 466 css->sum += 1; 467 css->sum &= 0xFFFF; 468 } 469 } 470 471 if (len == 1){ 472 css->tmp = p[0]; 473 css->odd = 1; 474 } 475} 476 477 478uint16_t 479csum_get(struct csum_state *css) 480{ 481 char pad = 0; 482 483 csum_update(&pad, 1, css); 484 return css->sum; 485} 486 487uint16_t 488csum_buf(uint8_t *p, uint32_t len) 489{ 490 struct csum_state css; 491 492 csum_init(&css); 493 csum_update(p, len, &css); 494 return csum_get(&css); 495 496} 497 498/* 499 * routines to write data to the output file 500 */ 501int 502write_out_data(FILE *outfile, uint8_t *data, size_t len, 503 struct csum_state *css) 504{ 505 errno = 0; 506 507 fwrite(data, len, 1, outfile); 508 if (errno) { 509 ERR("unable to write output file"); 510 return -1; 511 } 512 513 if (css) { 514 csum_update(data, len, css); 515 } 516 517 return 0; 518} 519 520 521int 522write_out_padding(FILE *outfile, size_t len, uint8_t padc, 523 struct csum_state *css) 524{ 525 uint8_t buf[512]; 526 size_t buflen = sizeof(buf); 527 528 memset(buf, padc, buflen); 529 while (len > 0) { 530 if (len < buflen) 531 buflen = len; 532 533 if (write_out_data(outfile, buf, buflen, css)) 534 return -1; 535 536 len -= buflen; 537 } 538 539 return 0; 540} 541 542 543int 544write_out_data_align(FILE *outfile, uint8_t *data, size_t len, size_t align, 545 struct csum_state *css) 546{ 547 size_t padlen; 548 int res; 549 550 res = write_out_data(outfile, data, len, css); 551 if (res) 552 return res; 553 554 padlen = ALIGN(len,align) - len; 555 res = write_out_padding(outfile, padlen, 0xFF, css); 556 557 return res; 558} 559 560 561int 562write_out_header(FILE *outfile, struct zyn_rombin_hdr *hdr) 563{ 564 struct zyn_rombin_hdr t; 565 566 errno = 0; 567 if (fseek(outfile, 0, SEEK_SET) != 0) { 568 ERRS("fseek failed on output file"); 569 return -1; 570 } 571 572 /* setup temporary header fields */ 573 memset(&t, 0, sizeof(t)); 574 t.addr = HOST_TO_BE32(hdr->addr); 575 memcpy(&t.sig, ROMBIN_SIGNATURE, ROMBIN_SIG_LEN); 576 t.type = hdr->type; 577 t.flags = hdr->flags; 578 t.osize = HOST_TO_BE32(hdr->osize); 579 t.csize = HOST_TO_BE32(hdr->csize); 580 t.ocsum = HOST_TO_BE16(hdr->ocsum); 581 t.ccsum = HOST_TO_BE16(hdr->ccsum); 582 t.mmap_addr = HOST_TO_BE32(hdr->mmap_addr); 583 584 DBG(2, "hdr.addr = 0x%08x", hdr->addr); 585 DBG(2, "hdr.type = 0x%02x", hdr->type); 586 DBG(2, "hdr.osize = 0x%08x", hdr->osize); 587 DBG(2, "hdr.csize = 0x%08x", hdr->csize); 588 DBG(2, "hdr.flags = 0x%02x", hdr->flags); 589 DBG(2, "hdr.ocsum = 0x%04x", hdr->ocsum); 590 DBG(2, "hdr.ccsum = 0x%04x", hdr->ccsum); 591 DBG(2, "hdr.mmap_addr = 0x%08x", hdr->mmap_addr); 592 593 return write_out_data(outfile, (uint8_t *)&t, sizeof(t), NULL); 594} 595 596 597int 598write_out_mmap(FILE *outfile, struct fw_mmap *mmap, struct csum_state *css) 599{ 600 struct zyn_mmt_hdr *mh; 601 uint8_t buf[MMAP_DATA_SIZE]; 602 uint32_t user_size; 603 char *data; 604 int res; 605 606 memset(buf, 0, sizeof(buf)); 607 608 mh = (struct zyn_mmt_hdr *)buf; 609 610 /* TODO: needs to recreate the memory map too? */ 611 mh->count=0; 612 613 /* Build user data section */ 614 data = buf+sizeof(*mh); 615 data += sprintf(data, "Vendor 1 %d", board->vendor); 616 *data++ = '\0'; 617 data += sprintf(data, "Model 1 %d", BE16_TO_HOST(board->model)); 618 *data++ = '\0'; 619 /* TODO: make hardware version configurable? */ 620 data += sprintf(data, "HwVerRange 2 %d %d", 0, 0); 621 *data++ = '\0'; 622 623 user_size = (uint8_t *)data - buf; 624 mh->user_start= HOST_TO_BE32(mmap->addr+sizeof(*mh)); 625 mh->user_end= HOST_TO_BE32(mmap->addr+user_size); 626 mh->csum = HOST_TO_BE16(csum_buf(buf+sizeof(*mh), user_size)); 627 628 res = write_out_data(outfile, buf, sizeof(buf), css); 629 630 return res; 631} 632 633 634int 635block_stat_file(struct fw_block *block) 636{ 637 struct stat st; 638 int res; 639 640 if (block->file_name == NULL) 641 return 0; 642 643 res = stat(block->file_name, &st); 644 if (res){ 645 ERRS("stat failed on %s", block->file_name); 646 return res; 647 } 648 649 block->file_size = st.st_size; 650 return 0; 651} 652 653 654int 655read_magic(uint16_t *magic) 656{ 657 FILE *f; 658 int res; 659 660 errno = 0; 661 f = fopen(bootext_block->file_name,"r"); 662 if (errno) { 663 ERRS("unable to open file: %s", bootext_block->file_name); 664 return -1; 665 } 666 667 errno = 0; 668 fread(magic, 2, 1, f); 669 if (errno != 0) { 670 ERRS("unable to read from file: %s", bootext_block->file_name); 671 res = -1; 672 goto err; 673 } 674 675 res = 0; 676 677err: 678 fclose(f); 679 return res; 680} 681 682 683int 684write_out_file(FILE *outfile, char *name, size_t len, struct csum_state *css) 685{ 686 char buf[FILE_BUF_LEN]; 687 size_t buflen = sizeof(buf); 688 FILE *f; 689 int res; 690 691 DBG(2, "writing out file, name=%s, len=%zu", 692 name, len); 693 694 errno = 0; 695 f = fopen(name,"r"); 696 if (errno) { 697 ERRS("unable to open file: %s", name); 698 return -1; 699 } 700 701 while (len > 0) { 702 if (len < buflen) 703 buflen = len; 704 705 /* read data from source file */ 706 errno = 0; 707 fread(buf, buflen, 1, f); 708 if (errno != 0) { 709 ERRS("unable to read from file: %s",name); 710 res = -1; 711 break; 712 } 713 714 res = write_out_data(outfile, buf, buflen, css); 715 if (res) 716 break; 717 718 len -= buflen; 719 } 720 721 fclose(f); 722 return res; 723} 724 725 726int 727write_out_block(FILE *outfile, struct fw_block *block, struct csum_state *css) 728{ 729 int res; 730 731 if (block == NULL) 732 return 0; 733 734 if (block->file_name == NULL) 735 return 0; 736 737 if (block->file_size == 0) 738 return 0; 739 740 res = write_out_file(outfile, block->file_name, 741 block->file_size, css); 742 return res; 743} 744 745 746int 747write_out_image(FILE *outfile) 748{ 749 struct fw_block *block; 750 struct fw_mmap mmap; 751 struct zyn_rombin_hdr hdr; 752 struct csum_state css; 753 int i, res; 754 uint32_t offset; 755 uint32_t padlen; 756 uint16_t csum; 757 uint16_t t; 758 759 /* setup header fields */ 760 memset(&hdr, 0, sizeof(hdr)); 761 hdr.addr = board->code_start; 762 hdr.type = OBJECT_TYPE_BOOTEXT; 763 hdr.flags = ROMBIN_FLAG_OCSUM; 764 765 offset = board->romio_offs; 766 767 res = write_out_header(outfile, &hdr); 768 if (res) 769 return res; 770 771 offset += sizeof(hdr); 772 773 csum_init(&css); 774 res = write_out_block(outfile, bootext_block, &css); 775 if (res) 776 return res; 777 778 offset += bootext_block->file_size; 779 if (offset > (board->romio_offs + board->bootext_size)) { 780 ERR("bootext file '%s' is too big", bootext_block->file_name); 781 return -1; 782 } 783 784 padlen = ALIGN(offset, MMAP_ALIGN) - offset; 785 res = write_out_padding(outfile, padlen, 0xFF, &css); 786 if (res) 787 return res; 788 789 offset += padlen; 790 791 mmap.addr = board->flash_base + offset; 792 res = write_out_mmap(outfile, &mmap, &css); 793 if (res) 794 return res; 795 796 offset += MMAP_DATA_SIZE; 797 798 if ((offset - board->romio_offs) < board->bootext_size) { 799 padlen = board->romio_offs + board->bootext_size - offset; 800 res = write_out_padding(outfile, padlen, 0xFF, &css); 801 if (res) 802 return res; 803 offset += padlen; 804 805 DBG(2, "bootext end at %08x", offset); 806 } 807 808 for (i = 0; i < num_blocks; i++) { 809 block = &blocks[i]; 810 811 if (block->type == BLOCK_TYPE_BOOTEXT) 812 continue; 813 814 padlen = ALIGN(offset, block->align) - offset; 815 res = write_out_padding(outfile, padlen, 0xFF, &css); 816 if (res) 817 return res; 818 offset += padlen; 819 820 res = write_out_block(outfile, block, &css); 821 if (res) 822 return res; 823 offset += block->file_size; 824 } 825 826 padlen = ALIGN(offset, 4) - offset; 827 res = write_out_padding(outfile, padlen, 0xFF, &css); 828 if (res) 829 return res; 830 offset += padlen; 831 832 csum = csum_get(&css); 833 hdr.mmap_addr = mmap.addr; 834 hdr.osize = 2; 835 836 res = read_magic(&hdr.ocsum); 837 if (res) 838 return res; 839 hdr.ocsum = BE16_TO_HOST(hdr.ocsum); 840 841 if (csum <= hdr.ocsum) 842 t = hdr.ocsum - csum; 843 else 844 t = hdr.ocsum - csum - 1; 845 846 DBG(2, "ocsum=%04x, csum=%04x, fix=%04x", hdr.ocsum, csum, t); 847 848 t = HOST_TO_BE16(t); 849 res = write_out_data(outfile, (uint8_t *)&t, 2, NULL); 850 if (res) 851 return res; 852 853 854 res = write_out_header(outfile, &hdr); 855 856 return res; 857} 858 859 860struct board_info * 861find_board(char *name) 862{ 863 struct board_info *ret; 864 struct board_info *board; 865 866 ret = NULL; 867 for (board = boards; board->name != NULL; board++){ 868 if (strcasecmp(name, board->name) == 0) { 869 ret = board; 870 break; 871 } 872 }; 873 874 return ret; 875} 876 877 878int 879parse_opt_board(char ch, char *arg) 880{ 881 882 DBG(1,"parsing board option: -%c %s", ch, arg); 883 884 if (board != NULL) { 885 ERR("only one board option allowed"); 886 return -1; 887 } 888 889 if (required_arg(ch, arg)) 890 return -1; 891 892 board = find_board(arg); 893 if (board == NULL){ 894 ERR("invalid/unknown board specified: %s", arg); 895 return -1; 896 } 897 898 return 0; 899} 900 901 902int 903parse_opt_ofname(char ch, char *arg) 904{ 905 906 if (ofname != NULL) { 907 ERR("only one output file allowed"); 908 return -1; 909 } 910 911 if (required_arg(ch, arg)) 912 return -1; 913 914 ofname = arg; 915 916 return 0; 917} 918 919 920int 921parse_opt_block(char ch, char *arg) 922{ 923 char buf[MAX_ARG_LEN]; 924 char *argv[MAX_ARG_COUNT]; 925 int argc; 926 char *p; 927 struct fw_block *block; 928 int i; 929 930 if ( num_blocks >= MAX_NUM_BLOCKS ) { 931 ERR("too many blocks specified"); 932 return -1; 933 } 934 935 block = &blocks[num_blocks++]; 936 937 /* setup default field values */ 938 block->padc = 0xFF; 939 940 switch(ch) { 941 case 'b': 942 if (bootext_block) { 943 ERR("only one boot extension block allowed"); 944 break; 945 } 946 block->type = BLOCK_TYPE_BOOTEXT; 947 bootext_block = block; 948 break; 949 case 'r': 950 block->type = BLOCK_TYPE_RAW; 951 break; 952 } 953 954 argc = parse_arg(arg, buf, argv); 955 956 i = 0; 957 p = argv[i++]; 958 if (is_empty_arg(p)) { 959 ERR("no file specified in %s", arg); 960 return -1; 961 } else { 962 block->file_name = strdup(p); 963 if (block->file_name == NULL) { 964 ERR("not enough memory"); 965 return -1; 966 } 967 } 968 969 if (block->type == BLOCK_TYPE_BOOTEXT) 970 return 0; 971 972 p = argv[i++]; 973 if (!is_empty_arg(p)) { 974 if (str2u32(p, &block->align) != 0) { 975 ERR("invalid block align in %s", arg); 976 return -1; 977 } 978 } 979 980 return 0; 981} 982 983 984int 985calc_block_offsets(int type, uint32_t *offset) 986{ 987 struct fw_block *block; 988 uint32_t next_offs; 989 uint32_t avail; 990 int i, res; 991 992 DBG(1,"calculating block offsets, starting with %" PRIu32, 993 *offset); 994 995 res = 0; 996 for (i = 0; i < num_blocks; i++) { 997 block = &blocks[i]; 998 999 if (block->type != type) 1000 continue; 1001 1002 next_offs = ALIGN(*offset, block->align); 1003 avail = board->flash_size - next_offs; 1004 if (block->file_size > avail) { 1005 ERR("file %s is too big, offset = %u, size=%u," 1006 " avail = %u, align = %u", block->file_name, 1007 (unsigned)next_offs, 1008 (unsigned)block->file_size, 1009 (unsigned)avail, 1010 (unsigned)block->align); 1011 res = -1; 1012 break; 1013 } 1014 1015 block->padlen = next_offs - *offset; 1016 *offset += block->file_size; 1017 } 1018 1019 return res; 1020} 1021 1022int 1023process_blocks(void) 1024{ 1025 struct fw_block *block; 1026 uint32_t offset; 1027 int i; 1028 int res; 1029 1030 /* collecting file stats */ 1031 for (i = 0; i < num_blocks; i++) { 1032 block = &blocks[i]; 1033 res = block_stat_file(block); 1034 if (res) 1035 return res; 1036 } 1037 1038 offset = board->romio_offs + bootext_block->file_size; 1039 res = calc_block_offsets(BLOCK_TYPE_RAW, &offset); 1040 1041 return res; 1042} 1043 1044 1045int 1046main(int argc, char *argv[]) 1047{ 1048 int optinvalid = 0; /* flag for invalid option */ 1049 int c; 1050 int res = EXIT_FAILURE; 1051 1052 FILE *outfile; 1053 1054 progname=basename(argv[0]); 1055 1056 opterr = 0; /* could not print standard getopt error messages */ 1057 while ( 1 ) { 1058 optinvalid = 0; 1059 1060 c = getopt(argc, argv, "b:B:ho:r:v"); 1061 if (c == -1) 1062 break; 1063 1064 switch (c) { 1065 case 'b': 1066 case 'r': 1067 optinvalid = parse_opt_block(c,optarg); 1068 break; 1069 case 'B': 1070 optinvalid = parse_opt_board(c,optarg); 1071 break; 1072 case 'o': 1073 optinvalid = parse_opt_ofname(c,optarg); 1074 break; 1075 case 'v': 1076 verblevel++; 1077 break; 1078 case 'h': 1079 usage(EXIT_SUCCESS); 1080 break; 1081 default: 1082 optinvalid = 1; 1083 break; 1084 } 1085 if (optinvalid != 0 ) { 1086 ERR("invalid option: -%c", optopt); 1087 goto out; 1088 } 1089 } 1090 1091 if (board == NULL) { 1092 ERR("no board specified"); 1093 goto out; 1094 } 1095 1096 if (ofname == NULL) { 1097 ERR("no output file specified"); 1098 goto out; 1099 } 1100 1101 if (optind < argc) { 1102 ERR("invalid option: %s", argv[optind]); 1103 goto out; 1104 } 1105 1106 if (process_blocks() != 0) { 1107 goto out; 1108 } 1109 1110 outfile = fopen(ofname, "w"); 1111 if (outfile == NULL) { 1112 ERRS("could not open \"%s\" for writing", ofname); 1113 goto out; 1114 } 1115 1116 if (write_out_image(outfile) != 0) 1117 goto out_flush; 1118 1119 DBG(1,"Image file %s completed.", ofname); 1120 1121 res = EXIT_SUCCESS; 1122 1123out_flush: 1124 fflush(outfile); 1125 fclose(outfile); 1126 if (res != EXIT_SUCCESS) { 1127 unlink(ofname); 1128 } 1129out: 1130 return res; 1131} 1132