File:  [ELWIX - Embedded LightWeight unIX -] / elwix / tools / ubnt-mkfwimage / mkfwimage.c
Revision 1.1.2.1: download - view: text, annotated - select for diffs - revision graph
Tue Jan 14 22:25:58 2014 UTC (10 years, 5 months ago) by misho
Branches: elwix2_2
moved

    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>