Root/tools/firmware-utils/src/mkfwimage.c

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., 675 Mass Ave, Cambridge, MA 02139, USA.
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 "fw.h"
33
34typedef struct fw_layout_data {
35    char name[PATH_MAX];
36    u_int32_t kern_start;
37    u_int32_t kern_entry;
38    u_int32_t firmware_max_length;
39} fw_layout_t;
40
41fw_layout_t fw_layout_data[] = {
42    {
43        .name = "XS2",
44        .kern_start = 0xbfc30000,
45        .kern_entry = 0x80041000,
46        .firmware_max_length= 0x00390000,
47    },
48    {
49        .name = "XS5",
50        .kern_start = 0xbe030000,
51        .kern_entry = 0x80041000,
52        .firmware_max_length= 0x00390000,
53    },
54    {
55        .name = "RS",
56        .kern_start = 0xbf030000,
57        .kern_entry = 0x80060000,
58        .firmware_max_length= 0x00B00000,
59    },
60    {
61        .name = "RSPRO",
62        .kern_start = 0xbf030000,
63        .kern_entry = 0x80060000,
64        .firmware_max_length= 0x00B00000,
65    },
66    {
67        .name = "LS-SR71",
68        .kern_start = 0xbf030000,
69        .kern_entry = 0x80060000,
70        .firmware_max_length= 0x00640000,
71    },
72    {
73        .name = "XS2-8",
74        .kern_start = 0xa8030000,
75        .kern_entry = 0x80041000,
76        .firmware_max_length= 0x006C0000,
77    },
78    {
79        .name = "XM",
80        .kern_start = 0x9f050000,
81        .kern_entry = 0x80002000,
82        .firmware_max_length= 0x006A0000,
83    },
84    { .name = "",
85    },
86};
87
88typedef struct part_data {
89    char partition_name[64];
90    int partition_index;
91    u_int32_t partition_baseaddr;
92    u_int32_t partition_startaddr;
93    u_int32_t partition_memaddr;
94    u_int32_t partition_entryaddr;
95    u_int32_t partition_length;
96
97    char filename[PATH_MAX];
98    struct stat stats;
99} part_data_t;
100
101#define MAX_SECTIONS 8
102#define DEFAULT_OUTPUT_FILE "firmware-image.bin"
103#define DEFAULT_VERSION "UNKNOWN"
104
105#define OPTIONS "B:hv:m:o:r:k:"
106
107static int debug = 0;
108
109typedef struct image_info {
110    char magic[16];
111    char version[256];
112    char outputfile[PATH_MAX];
113    u_int32_t part_count;
114    part_data_t parts[MAX_SECTIONS];
115} image_info_t;
116
117static void write_header(void* mem, const char *magic, const char* version)
118{
119    header_t* header = mem;
120    memset(header, 0, sizeof(header_t));
121
122    memcpy(header->magic, magic, MAGIC_LENGTH);
123    strncpy(header->version, version, sizeof(header->version));
124    header->crc = htonl(crc32(0L, (unsigned char *)header,
125                sizeof(header_t) - 2 * sizeof(u_int32_t)));
126    header->pad = 0L;
127}
128
129
130static void write_signature(void* mem, u_int32_t sig_offset)
131{
132    /* write signature */
133    signature_t* sign = (signature_t*)(mem + sig_offset);
134    memset(sign, 0, sizeof(signature_t));
135
136    memcpy(sign->magic, MAGIC_END, MAGIC_LENGTH);
137    sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset));
138    sign->pad = 0L;
139}
140
141static int write_part(void* mem, part_data_t* d)
142{
143    char* addr;
144    int fd;
145    part_t* p = mem;
146    part_crc_t* crc = mem + sizeof(part_t) + d->stats.st_size;
147
148    fd = open(d->filename, O_RDONLY);
149    if (fd < 0)
150    {
151        ERROR("Failed opening file '%s'\n", d->filename);
152        return -1;
153    }
154
155    if ((addr=(char*)mmap(0, d->stats.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED)
156    {
157        ERROR("Failed mmaping memory for file '%s'\n", d->filename);
158        close(fd);
159        return -2;
160    }
161
162    memcpy(mem + sizeof(part_t), addr, d->stats.st_size);
163    munmap(addr, d->stats.st_size);
164
165    memset(p->name, 0, sizeof(p->name));
166    strncpy(p->magic, MAGIC_PART, MAGIC_LENGTH);
167    strncpy(p->name, d->partition_name, sizeof(p->name));
168    p->index = htonl(d->partition_index);
169    p->data_size = htonl(d->stats.st_size);
170    p->part_size = htonl(d->partition_length);
171    p->baseaddr = htonl(d->partition_baseaddr);
172    p->memaddr = htonl(d->partition_memaddr);
173    p->entryaddr = htonl(d->partition_entryaddr);
174
175    crc->crc = htonl(crc32(0L, mem, d->stats.st_size + sizeof(part_t)));
176    crc->pad = 0L;
177
178    return 0;
179}
180
181static void usage(const char* progname)
182{
183    INFO("Version %s\n"
184             "Usage: %s [options]\n"
185         "\t-v <version string>\t - firmware version information, default: %s\n"
186         "\t-o <output file>\t - firmware output file, default: %s\n"
187         "\t-m <magic>\t - firmware magic, default: %s\n"
188         "\t-k <kernel file>\t\t - kernel file\n"
189         "\t-r <rootfs file>\t\t - rootfs file\n"
190         "\t-B <board name>\t\t - choose firmware layout for specified board (XS2, XS5, RS, XM)\n"
191         "\t-h\t\t\t - this help\n", VERSION,
192         progname, DEFAULT_VERSION, DEFAULT_OUTPUT_FILE, MAGIC_HEADER);
193}
194
195static void print_image_info(const image_info_t* im)
196{
197    int i = 0;
198    INFO("Firmware version: '%s'\n"
199         "Output file: '%s'\n"
200         "Part count: %u\n",
201         im->version, im->outputfile,
202         im->part_count);
203
204    for (i = 0; i < im->part_count; ++i)
205    {
206        const part_data_t* d = &im->parts[i];
207        INFO(" %10s: %8ld bytes (free: %8ld)\n",
208             d->partition_name,
209             d->stats.st_size,
210             d->partition_length - d->stats.st_size);
211    }
212}
213
214
215
216static u_int32_t filelength(const char* file)
217{
218    FILE *p;
219    int ret = -1;
220
221    if ( (p = fopen(file, "rb") ) == NULL) return (-1);
222
223    fseek(p, 0, SEEK_END);
224    ret = ftell(p);
225
226    fclose (p);
227
228    return (ret);
229}
230
231static int create_image_layout(const char* kernelfile, const char* rootfsfile, char* board_name, image_info_t* im)
232{
233    part_data_t* kernel = &im->parts[0];
234    part_data_t* rootfs = &im->parts[1];
235
236    fw_layout_t* p;
237
238    p = &fw_layout_data[0];
239    while ((strlen(p->name) != 0) && (strncmp(p->name, board_name, sizeof(board_name)) != 0))
240        p++;
241    if (p->name == NULL) {
242        printf("BUG! Unable to find default fw layout!\n");
243        exit(-1);
244    }
245
246    printf("board = %s\n", p->name);
247    strcpy(kernel->partition_name, "kernel");
248    kernel->partition_index = 1;
249    kernel->partition_baseaddr = p->kern_start;
250    if ( (kernel->partition_length = filelength(kernelfile)) < 0) return (-1);
251    kernel->partition_memaddr = p->kern_entry;
252    kernel->partition_entryaddr = p->kern_entry;
253    strncpy(kernel->filename, kernelfile, sizeof(kernel->filename));
254
255    if (filelength(rootfsfile) + kernel->partition_length > p->firmware_max_length)
256        return (-2);
257
258    strcpy(rootfs->partition_name, "rootfs");
259    rootfs->partition_index = 2;
260    rootfs->partition_baseaddr = kernel->partition_baseaddr + kernel->partition_length;
261    rootfs->partition_length = p->firmware_max_length - kernel->partition_length;
262    rootfs->partition_memaddr = 0x00000000;
263    rootfs->partition_entryaddr = 0x00000000;
264    strncpy(rootfs->filename, rootfsfile, sizeof(rootfs->filename));
265
266printf("kernel: %d 0x%08x\n", kernel->partition_length, kernel->partition_baseaddr);
267printf("root: %d 0x%08x\n", rootfs->partition_length, rootfs->partition_baseaddr);
268    im->part_count = 2;
269
270    return 0;
271}
272
273/**
274 * Checks the availability and validity of all image components.
275 * Fills in stats member of the part_data structure.
276 */
277static int validate_image_layout(image_info_t* im)
278{
279    int i;
280
281    if (im->part_count == 0 || im->part_count > MAX_SECTIONS)
282    {
283        ERROR("Invalid part count '%d'\n", im->part_count);
284        return -1;
285    }
286
287    for (i = 0; i < im->part_count; ++i)
288    {
289        part_data_t* d = &im->parts[i];
290        int len = strlen(d->partition_name);
291        if (len == 0 || len > 16)
292        {
293            ERROR("Invalid partition name '%s' of the part %d\n",
294                    d->partition_name, i);
295            return -1;
296        }
297        if (stat(d->filename, &d->stats) < 0)
298        {
299            ERROR("Couldn't stat file '%s' from part '%s'\n",
300                           d->filename, d->partition_name);
301            return -2;
302        }
303        if (d->stats.st_size == 0)
304        {
305            ERROR("File '%s' from part '%s' is empty!\n",
306                           d->filename, d->partition_name);
307            return -3;
308        }
309        if (d->stats.st_size > d->partition_length) {
310            ERROR("File '%s' too big (%d) - max size: 0x%08X (exceeds %lu bytes)\n",
311                           d->filename, i, d->partition_length,
312                    d->stats.st_size - d->partition_length);
313            return -4;
314        }
315    }
316
317    return 0;
318}
319
320static int build_image(image_info_t* im)
321{
322    char* mem;
323    char* ptr;
324    u_int32_t mem_size;
325    FILE* f;
326    int i;
327
328    // build in-memory buffer
329    mem_size = sizeof(header_t) + sizeof(signature_t);
330    for (i = 0; i < im->part_count; ++i)
331    {
332        part_data_t* d = &im->parts[i];
333        mem_size += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t);
334    }
335
336    mem = (char*)calloc(mem_size, 1);
337    if (mem == NULL)
338    {
339        ERROR("Cannot allocate memory chunk of size '%u'\n", mem_size);
340        return -1;
341    }
342
343    // write header
344    write_header(mem, im->magic, im->version);
345    ptr = mem + sizeof(header_t);
346    // write all parts
347    for (i = 0; i < im->part_count; ++i)
348    {
349        part_data_t* d = &im->parts[i];
350        int rc;
351        if ((rc = write_part(ptr, d)) != 0)
352        {
353            ERROR("ERROR: failed writing part %u '%s'\n", i, d->partition_name);
354        }
355        ptr += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t);
356    }
357    // write signature
358    write_signature(mem, mem_size - sizeof(signature_t));
359
360    // write in-memory buffer into file
361    if ((f = fopen(im->outputfile, "w")) == NULL)
362    {
363        ERROR("Can not create output file: '%s'\n", im->outputfile);
364        return -10;
365    }
366
367    if (fwrite(mem, mem_size, 1, f) != 1)
368    {
369        ERROR("Could not write %d bytes into file: '%s'\n",
370                mem_size, im->outputfile);
371        return -11;
372    }
373
374    free(mem);
375    fclose(f);
376    return 0;
377}
378
379
380int main(int argc, char* argv[])
381{
382    char kernelfile[PATH_MAX];
383    char rootfsfile[PATH_MAX];
384    char board_name[PATH_MAX];
385    int o, rc;
386    image_info_t im;
387
388    memset(&im, 0, sizeof(im));
389    memset(kernelfile, 0, sizeof(kernelfile));
390    memset(rootfsfile, 0, sizeof(rootfsfile));
391    memset(board_name, 0, sizeof(board_name));
392
393    strcpy(im.outputfile, DEFAULT_OUTPUT_FILE);
394    strcpy(im.version, DEFAULT_VERSION);
395    strncpy(im.magic, MAGIC_HEADER, sizeof(im.magic));
396
397    while ((o = getopt(argc, argv, OPTIONS)) != -1)
398    {
399        switch (o) {
400        case 'v':
401            if (optarg)
402                strncpy(im.version, optarg, sizeof(im.version));
403            break;
404        case 'o':
405            if (optarg)
406                strncpy(im.outputfile, optarg, sizeof(im.outputfile));
407            break;
408        case 'm':
409            if (optarg)
410                strncpy(im.magic, optarg, sizeof(im.magic));
411            break;
412        case 'h':
413            usage(argv[0]);
414            return -1;
415        case 'k':
416            if (optarg)
417                strncpy(kernelfile, optarg, sizeof(kernelfile));
418            break;
419        case 'r':
420            if (optarg)
421                strncpy(rootfsfile, optarg, sizeof(rootfsfile));
422            break;
423        case 'B':
424            if (optarg)
425                strncpy(board_name, optarg, sizeof(board_name));
426            break;
427        }
428    }
429    if (strlen(board_name) == 0)
430        strcpy(board_name, "XS2"); /* default to XS2 */
431
432    if (strlen(kernelfile) == 0)
433    {
434        ERROR("Kernel file is not specified, cannot continue\n");
435        usage(argv[0]);
436        return -2;
437    }
438
439    if (strlen(rootfsfile) == 0)
440    {
441        ERROR("Root FS file is not specified, cannot continue\n");
442        usage(argv[0]);
443        return -2;
444    }
445
446    if ((rc = create_image_layout(kernelfile, rootfsfile, board_name, &im)) != 0)
447    {
448        ERROR("Failed creating firmware layout description - error code: %d\n", rc);
449        return -3;
450    }
451
452    if ((rc = validate_image_layout(&im)) != 0)
453    {
454        ERROR("Failed validating firmware layout - error code: %d\n", rc);
455        return -4;
456    }
457
458    print_image_info(&im);
459
460    if ((rc = build_image(&im)) != 0)
461    {
462        ERROR("Failed building image file '%s' - error code: %d\n", im.outputfile, rc);
463        return -5;
464    }
465
466    return 0;
467}
468

Archive Download this file



interactive