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

Archive Download this file



interactive