File:
[ELWIX - Embedded LightWeight unIX -] /
elwix /
tools /
ubnt-mkfwimage /
mkfwimage.c
Revision
1.2:
download - view:
text,
annotated -
select for diffs -
revision graph
Mon Sep 15 19:06:55 2014 UTC (10 years, 1 month ago) by
misho
Branches:
MAIN
CVS tags:
elwix2_8,
elwix2_7,
elwix2_6,
elwix2_3,
HEAD,
ELWIX2_7,
ELWIX2_6,
ELWIX2_5,
ELWIX2_2p0
elwix 2.2
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>