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