source: trunk/tools/firmware-utils/src/mkfwimage.c

Last change on this file was 48829, checked in by nbd, 4 months ago

firmware-utils: mkfwimage: fix firmware_max_length for XM layout

The new u-boot version bundled with the 5.6.x firmwares from Ubiquiti gets
confused by the smaller rootfs partition size; this can lead to various
issues:

  1. We've gotten reports that flashing from the 5.6.x stock firmware to OpenWrt will brick devices; I wasn't able to reproduce this myself
  2. Flashing from 5.5.x stock firmware to OpenWrt and back to stock (via TFTP recovery), following by an update to 5.6.x via web interface can yield a bricked device with the following properties:
    • It can't be booted without entering commands over a serial console, as u-boot supplies the wrong MTD layout
    • The web interface won't accept any image with the original flash layout, so stock firmware upgrades are impossible
    • As the TFTP recovery doesn't update u-boot, returning to the old u-boot from firmware 5.5.x is impossible

To recover from 2., creating an OpenWrt image which doesn't set u-boot as
read-only and flashing a backup of the old u-boot from there is the only
way known to me. (Fixing the mtdparts variable in u-boot-env from OpenWrt
might also work; settings this from u-boot over serial didn't have
any permanent effect.)

Fix all of this by setting the correct flash layout also used by the stock
firmware. Flashing has been tested from both firmware 5.5.x and 5.6.x. The
fixed layout also matches the mtdparts defined by OpenWrt.

Signed-off-by: Matthias Schiffer <mschiffer@…>

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