Annotation of elwix/tools/ubnt-mkfwimage/mkfwimage.c, revision 1.2
1.2 ! misho 1: /*
! 2: * Copyright (C) 2007 Ubiquiti Networks, Inc.
! 3: * Copyright (C) 2008 Lukas Kuna <ValXdater@seznam.cz>
! 4: *
! 5: * This program is free software; you can redistribute it and/or
! 6: * modify it under the terms of the GNU General Public License as
! 7: * published by the Free Software Foundation; either version 2 of the
! 8: * License, or (at your option) any later version.
! 9: *
! 10: * This program is distributed in the hope that it will be useful, but
! 11: * WITHOUT ANY WARRANTY; without even the implied warranty of
! 12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
! 13: * General Public License for more details.
! 14: *
! 15: * You should have received a copy of the GNU General Public License
! 16: * along with this program; if not, write to the Free Software
! 17: * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110.
! 18: */
! 19:
! 20: #include <sys/types.h>
! 21: #include <sys/stat.h>
! 22: #include <fcntl.h>
! 23: #include <unistd.h>
! 24: #include <string.h>
! 25: #include <errno.h>
! 26: #include <zlib.h>
! 27: #include <sys/mman.h>
! 28: #include <netinet/in.h>
! 29: #include <stdio.h>
! 30: #include <stdlib.h>
! 31: #include <limits.h>
! 32: #include <zlib.h>
! 33: #include "fw.h"
! 34:
! 35: typedef struct fw_layout_data {
! 36: char name[PATH_MAX];
! 37: u_int32_t kern_start;
! 38: u_int32_t kern_entry;
! 39: u_int32_t firmware_max_length;
! 40: } fw_layout_t;
! 41:
! 42: fw_layout_t fw_layout_data[] = {
! 43: {
! 44: .name = "XS2",
! 45: .kern_start = 0x00040000,
! 46: .kern_entry = 0x80041000,
! 47: .firmware_max_length= 0x006A0000,
! 48: },
! 49: {
! 50: .name = "XS5",
! 51: .kern_start = 0xbe030000,
! 52: .kern_entry = 0x80041000,
! 53: .firmware_max_length= 0x00390000,
! 54: },
! 55: {
! 56: .name = "RS",
! 57: .kern_start = 0xbf030000,
! 58: .kern_entry = 0x80060000,
! 59: .firmware_max_length= 0x00B00000,
! 60: },
! 61: {
! 62: .name = "RSPRO",
! 63: .kern_start = 0xbf030000,
! 64: .kern_entry = 0x80050100,
! 65: .firmware_max_length= 0x00B00000,
! 66: },
! 67: {
! 68: .name = "LS-SR71",
! 69: .kern_start = 0xbf030000,
! 70: .kern_entry = 0x80060000,
! 71: .firmware_max_length= 0x00640000,
! 72: },
! 73: {
! 74: .name = "XS2-8",
! 75: .kern_start = 0xa8030000,
! 76: .kern_entry = 0x80041000,
! 77: .firmware_max_length= 0x006C0000,
! 78: },
! 79: {
! 80: .name = "XM",
! 81: .kern_start = 0x9f050000,
! 82: .kern_entry = 0x80002000,
! 83: .firmware_max_length= 0x006A0000,
! 84: },
! 85: {
! 86: .name = "PB42",
! 87: .kern_start = 0xbf030000,
! 88: .kern_entry = 0x80060000,
! 89: .firmware_max_length= 0x00B00000,
! 90: },
! 91: { .name = "",
! 92: },
! 93: };
! 94:
! 95: typedef struct part_data {
! 96: char partition_name[64];
! 97: int partition_index;
! 98: u_int32_t partition_baseaddr;
! 99: u_int32_t partition_startaddr;
! 100: u_int32_t partition_memaddr;
! 101: u_int32_t partition_entryaddr;
! 102: u_int32_t partition_length;
! 103:
! 104: char filename[PATH_MAX];
! 105: struct stat stats;
! 106: } part_data_t;
! 107:
! 108: #define MAX_SECTIONS 8
! 109: #define DEFAULT_OUTPUT_FILE "firmware-image.bin"
! 110: #define DEFAULT_VERSION "UNKNOWN"
! 111:
! 112: #define OPTIONS "B:C:c:hv:o:r:k:"
! 113:
! 114: static int debug = 1;
! 115:
! 116: typedef struct image_info {
! 117: char version[256];
! 118: char outputfile[PATH_MAX];
! 119: u_int32_t part_count;
! 120: part_data_t parts[MAX_SECTIONS];
! 121: struct {
! 122: int enable; /* enable cfgfs? */
! 123: size_t size; /* size of config partition */
! 124: } cfg;
! 125: } image_info_t;
! 126:
! 127: static void write_header(void* mem, const char* version)
! 128: {
! 129: header_t* header = mem;
! 130: memset(header, 0, sizeof(header_t));
! 131:
! 132: memcpy(header->magic, MAGIC_HEADER, MAGIC_LENGTH);
! 133: strncpy(header->version, version, sizeof(header->version));
! 134: header->crc = htonl(crc32(0L, (unsigned char *)header,
! 135: sizeof(header_t) - 2 * sizeof(u_int32_t)));
! 136: header->pad = 0L;
! 137: }
! 138:
! 139:
! 140: static void write_signature(void* mem, u_int32_t sig_offset)
! 141: {
! 142: /* write signature */
! 143: signature_t* sign = (signature_t*)(mem + sig_offset);
! 144: memset(sign, 0, sizeof(signature_t));
! 145:
! 146: memcpy(sign->magic, MAGIC_END, MAGIC_LENGTH);
! 147: sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset));
! 148: sign->pad = 0L;
! 149: }
! 150:
! 151: static int write_part(void* mem, part_data_t* d)
! 152: {
! 153: char* addr;
! 154: int fd;
! 155: part_t* p = mem;
! 156: part_crc_t* crc = mem + sizeof(part_t) + d->stats.st_size;
! 157:
! 158: fd = open(d->filename, O_RDONLY);
! 159: if (fd < 0)
! 160: {
! 161: ERROR("Failed opening file '%s'\n", d->filename);
! 162: return -1;
! 163: }
! 164:
! 165: if ((addr=(char*)mmap(0, d->stats.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED)
! 166: {
! 167: ERROR("Failed mmaping memory for file '%s'\n", d->filename);
! 168: close(fd);
! 169: return -2;
! 170: }
! 171:
! 172: memcpy(mem + sizeof(part_t), addr, d->stats.st_size);
! 173: munmap(addr, d->stats.st_size);
! 174:
! 175: memset(p->name, 0, sizeof(p->name));
! 176: strncpy(p->magic, MAGIC_PART, MAGIC_LENGTH);
! 177: strncpy(p->name, d->partition_name, sizeof(p->name));
! 178: p->index = htonl(d->partition_index);
! 179: p->data_size = htonl(d->stats.st_size);
! 180: p->part_size = htonl(d->partition_length);
! 181: p->baseaddr = htonl(d->partition_baseaddr);
! 182: p->memaddr = htonl(d->partition_memaddr);
! 183: p->entryaddr = htonl(d->partition_entryaddr);
! 184:
! 185: crc->crc = htonl(crc32(0L, mem, d->stats.st_size + sizeof(part_t)));
! 186: crc->pad = 0L;
! 187:
! 188: return 0;
! 189: }
! 190:
! 191: static void usage(const char* progname)
! 192: {
! 193: INFO("Version %s\n"
! 194: "Usage: %s [options]\n"
! 195: "\t-v <version string>\t - firmware version information, default: %s\n"
! 196: "\t-o <output file>\t - firmware output file, default: %s\n"
! 197: "\t-k <kernel file>\t\t - kernel file\n"
! 198: "\t-r <rootfs file>\t\t - rootfs file\n"
! 199: "\t-C <cfgfs size>\t\t - enable 'cfg' partition; size in bytes\n"
! 200: "\t-c <cfgfs file>\t\t - configfs file\n"
! 201: "\t-B <board name>\t\t - choose firmware layout for specified board (XS2, XS5, RS, XM)\n"
! 202: "\t-h\t\t\t - this help\n", VERSION,
! 203: progname, DEFAULT_VERSION, DEFAULT_OUTPUT_FILE);
! 204: }
! 205:
! 206: static void print_image_info(const image_info_t* im)
! 207: {
! 208: int i = 0;
! 209: INFO("Firmware version: '%s'\n"
! 210: "Output file: '%s'\n"
! 211: "Part count: %u\n",
! 212: im->version, im->outputfile,
! 213: im->part_count);
! 214:
! 215: for (i = 0; i < im->part_count; ++i)
! 216: {
! 217: const part_data_t* d = &im->parts[i];
! 218: INFO(" %10s: %8lld bytes (free: %8lld)\n",
! 219: d->partition_name,
! 220: d->stats.st_size,
! 221: d->partition_length - d->stats.st_size);
! 222: }
! 223: }
! 224:
! 225:
! 226:
! 227: static u_int32_t filelength(const char* file)
! 228: {
! 229: FILE *p;
! 230: int ret = -1;
! 231:
! 232: if ( (p = fopen(file, "rb") ) == NULL) return (-1);
! 233:
! 234: fseek(p, 0, SEEK_END);
! 235: ret = ftell(p);
! 236:
! 237: fclose (p);
! 238:
! 239: return (ret);
! 240: }
! 241:
! 242: static int create_image_layout(const char* kernelfile, const char* rootfsfile,
! 243: const char *cfgfsfile, char* board_name, image_info_t* im)
! 244: {
! 245: part_data_t* kernel = &im->parts[0];
! 246: part_data_t* rootfs = &im->parts[1];
! 247: part_data_t* cfgfs = &im->parts[2];
! 248:
! 249: fw_layout_t* p;
! 250: im->part_count = 0;
! 251:
! 252: p = &fw_layout_data[0];
! 253: while ((strlen(p->name) != 0) && (strcmp(p->name, board_name) != 0))
! 254: p++;
! 255: if (p->name == NULL) {
! 256: printf("BUG! Unable to find default fw layout!\n");
! 257: exit(-1);
! 258: }
! 259:
! 260: printf("board = %s\n", p->name);
! 261: strcpy(kernel->partition_name, "kernel");
! 262: kernel->partition_index = 1;
! 263: kernel->partition_baseaddr = p->kern_start;
! 264: if ( (kernel->partition_length = filelength(kernelfile)) == (u_int) -1) return (-1);
! 265: kernel->partition_memaddr = p->kern_entry;
! 266: kernel->partition_entryaddr = p->kern_entry;
! 267: strncpy(kernel->filename, kernelfile, sizeof(kernel->filename));
! 268: im->part_count++;
! 269:
! 270: printf("kernel: %d bytes (base 0x%08x)\n", kernel->partition_length,
! 271: kernel->partition_baseaddr);
! 272: printf("rootfs: %d bytes\n", filelength(rootfsfile));
! 273: if (cfgfs)
! 274: printf("cfgfs: %d bytes\n", im->cfg.size);
! 275:
! 276: printf("total: (%d bytes)\n",
! 277: kernel->partition_length +
! 278: filelength(rootfsfile) +
! 279: (cfgfs != NULL ? im->cfg.size : 0));
! 280:
! 281: /*
! 282: * This is dirty - cfgfs isn't calculated here, it's subtracted from
! 283: * rootfs. I'll fix that later. :)
! 284: */
! 285: strcpy(rootfs->partition_name, "rootfs");
! 286: if (filelength(rootfsfile) + kernel->partition_length > p->firmware_max_length)
! 287: return (-2);
! 288:
! 289: rootfs->partition_index = 2;
! 290: rootfs->partition_baseaddr = kernel->partition_baseaddr + kernel->partition_length;
! 291: rootfs->partition_length = p->firmware_max_length - kernel->partition_length;
! 292: rootfs->partition_memaddr = 0x00000000;
! 293: rootfs->partition_entryaddr = 0x00000000;
! 294: strncpy(rootfs->filename, rootfsfile, sizeof(rootfs->filename));
! 295: im->part_count++;
! 296:
! 297:
! 298: /*
! 299: * If cfg is enabled, subtract the cfg size from the
! 300: * rootfs entry.
! 301: */
! 302: if (im->cfg.enable) {
! 303: rootfs->partition_length -= im->cfg.size;
! 304: strcpy(cfgfs->partition_name, "cfg");
! 305: cfgfs->partition_index = 3;
! 306: cfgfs->partition_baseaddr = kernel->partition_baseaddr +
! 307: kernel->partition_length + rootfs->partition_length;
! 308: cfgfs->partition_length = im->cfg.size;
! 309: cfgfs->partition_memaddr = 0x00000000;
! 310: cfgfs->partition_entryaddr = 0x00000000;
! 311: strncpy(cfgfs->filename, cfgfsfile, sizeof(rootfs->filename));
! 312: im->part_count++;
! 313: }
! 314:
! 315: printf("root: %d 0x%08x\n", rootfs->partition_length, rootfs->partition_baseaddr);
! 316:
! 317: if (im->cfg.enable)
! 318: printf("cfg: %d 0x%08x\n", cfgfs->partition_length,
! 319: cfgfs->partition_baseaddr);
! 320:
! 321: return 0;
! 322: }
! 323:
! 324: /**
! 325: * Checks the availability and validity of all image components.
! 326: * Fills in stats member of the part_data structure.
! 327: */
! 328: static int validate_image_layout(image_info_t* im)
! 329: {
! 330: int i;
! 331:
! 332: if (im->part_count == 0 || im->part_count > MAX_SECTIONS)
! 333: {
! 334: ERROR("Invalid part count '%d'\n", im->part_count);
! 335: return -1;
! 336: }
! 337:
! 338: for (i = 0; i < im->part_count; ++i)
! 339: {
! 340: part_data_t* d = &im->parts[i];
! 341: int len = strlen(d->partition_name);
! 342: if (len == 0 || len > 16)
! 343: {
! 344: ERROR("Invalid partition name '%s' of the part %d\n",
! 345: d->partition_name, i);
! 346: return -1;
! 347: }
! 348: if (stat(d->filename, &d->stats) < 0)
! 349: {
! 350: ERROR("Couldn't stat file '%s' from part '%s'\n",
! 351: d->filename, d->partition_name);
! 352: return -2;
! 353: }
! 354: if (d->stats.st_size == 0)
! 355: {
! 356: ERROR("File '%s' from part '%s' is empty!\n",
! 357: d->filename, d->partition_name);
! 358: return -3;
! 359: }
! 360: if (d->stats.st_size > d->partition_length) {
! 361: ERROR("File '%s' too big (%d) - max size: 0x%08X (exceeds %llu bytes)\n",
! 362: d->filename, i, d->partition_length,
! 363: d->stats.st_size - d->partition_length);
! 364: return -4;
! 365: }
! 366: }
! 367:
! 368: return 0;
! 369: }
! 370:
! 371: static int build_image(image_info_t* im)
! 372: {
! 373: char* mem;
! 374: char* ptr;
! 375: u_int32_t mem_size;
! 376: FILE* f;
! 377: int i;
! 378:
! 379: // build in-memory buffer
! 380: mem_size = sizeof(header_t) + sizeof(signature_t);
! 381: for (i = 0; i < im->part_count; ++i)
! 382: {
! 383: part_data_t* d = &im->parts[i];
! 384: mem_size += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t);
! 385: }
! 386:
! 387: mem = (char*)calloc(mem_size, 1);
! 388: if (mem == NULL)
! 389: {
! 390: ERROR("Cannot allocate memory chunk of size '%u'\n", mem_size);
! 391: return -1;
! 392: }
! 393:
! 394: // write header
! 395: write_header(mem, im->version);
! 396: ptr = mem + sizeof(header_t);
! 397: // write all parts
! 398: for (i = 0; i < im->part_count; ++i)
! 399: {
! 400: part_data_t* d = &im->parts[i];
! 401: int rc;
! 402: if ((rc = write_part(ptr, d)) != 0)
! 403: {
! 404: ERROR("ERROR: failed writing part %u '%s'\n", i, d->partition_name);
! 405: }
! 406: ptr += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t);
! 407: }
! 408: // write signature
! 409: write_signature(mem, mem_size - sizeof(signature_t));
! 410:
! 411: // write in-memory buffer into file
! 412: if ((f = fopen(im->outputfile, "w")) == NULL)
! 413: {
! 414: ERROR("Can not create output file: '%s'\n", im->outputfile);
! 415: return -10;
! 416: }
! 417:
! 418: if (fwrite(mem, mem_size, 1, f) != 1)
! 419: {
! 420: ERROR("Could not write %d bytes into file: '%s'\n",
! 421: mem_size, im->outputfile);
! 422: return -11;
! 423: }
! 424:
! 425: free(mem);
! 426: fclose(f);
! 427: return 0;
! 428: }
! 429:
! 430:
! 431: int main(int argc, char* argv[])
! 432: {
! 433: char kernelfile[PATH_MAX];
! 434: char rootfsfile[PATH_MAX];
! 435: char cfgfsfile[PATH_MAX];
! 436: char board_name[PATH_MAX];
! 437: int o, rc;
! 438: image_info_t im;
! 439:
! 440: memset(&im, 0, sizeof(im));
! 441: memset(kernelfile, 0, sizeof(kernelfile));
! 442: memset(rootfsfile, 0, sizeof(rootfsfile));
! 443: memset(board_name, 0, sizeof(board_name));
! 444:
! 445: strcpy(im.outputfile, DEFAULT_OUTPUT_FILE);
! 446: strcpy(im.version, DEFAULT_VERSION);
! 447:
! 448: while ((o = getopt(argc, argv, OPTIONS)) != -1)
! 449: {
! 450: switch (o) {
! 451: case 'c':
! 452: if (optarg)
! 453: strncpy(cfgfsfile, optarg, sizeof(cfgfsfile));
! 454: break;
! 455: case 'C':
! 456: if (optarg) {
! 457: im.cfg.enable = 1;
! 458: im.cfg.size = atoi(optarg);
! 459: } else {
! 460: usage(argv[0]);
! 461: return -1;
! 462: }
! 463: break;
! 464: case 'v':
! 465: if (optarg)
! 466: strncpy(im.version, optarg, sizeof(im.version));
! 467: break;
! 468: case 'o':
! 469: if (optarg)
! 470: strncpy(im.outputfile, optarg, sizeof(im.outputfile));
! 471: break;
! 472: case 'h':
! 473: usage(argv[0]);
! 474: return -1;
! 475: case 'k':
! 476: if (optarg)
! 477: strncpy(kernelfile, optarg, sizeof(kernelfile));
! 478: break;
! 479: case 'r':
! 480: if (optarg)
! 481: strncpy(rootfsfile, optarg, sizeof(rootfsfile));
! 482: break;
! 483: case 'B':
! 484: if (optarg)
! 485: strncpy(board_name, optarg, sizeof(board_name));
! 486: break;
! 487: }
! 488: }
! 489: if (strlen(board_name) == 0)
! 490: strcpy(board_name, "XS2"); /* default to XS2 */
! 491:
! 492: if (strlen(kernelfile) == 0)
! 493: {
! 494: ERROR("Kernel file is not specified, cannot continue\n");
! 495: usage(argv[0]);
! 496: return -2;
! 497: }
! 498:
! 499: if (strlen(rootfsfile) == 0)
! 500: {
! 501: ERROR("Root FS file is not specified, cannot continue\n");
! 502: usage(argv[0]);
! 503: return -2;
! 504: }
! 505:
! 506: if ((rc = create_image_layout(kernelfile, rootfsfile, cfgfsfile,
! 507: board_name, &im)) != 0)
! 508: {
! 509: ERROR("Failed creating firmware layout description - error code: %d\n", rc);
! 510: return -3;
! 511: }
! 512:
! 513: if ((rc = validate_image_layout(&im)) != 0)
! 514: {
! 515: ERROR("Failed validating firmware layout - error code: %d\n", rc);
! 516: return -4;
! 517: }
! 518:
! 519: print_image_info(&im);
! 520:
! 521: if ((rc = build_image(&im)) != 0)
! 522: {
! 523: ERROR("Failed building image file '%s' - error code: %d\n", im.outputfile, rc);
! 524: return -5;
! 525: }
! 526:
! 527: return 0;
! 528: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>