Root/tools/wrt350nv2-builder/src/wrt350nv2-builder.c

1/*
2
3    WRT350Nv2-Builder 2.4 (previously called buildimg)
4    Copyright (C) 2008-2009 Dirk Teurlings <info@upexia.nl>
5    Copyright (C) 2009-2011 Matthias Buecher (http://www.maddes.net/)
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
21    A lot of thanks to Kaloz and juhosg from OpenWRT and Lennert Buytenhek from
22    marvell for helping me figure this one out. This code is based on bash
23    scripts wrote by Peter van Valderen so the real credit should go to him.
24
25    This program reads the provided parameter file and creates an image which can
26    be used to flash a Linksys WRT350N v2 from stock firmware.
27    The trick is to fill unused space in the bin file with random, so that the
28    resulting zip file passes the size check of the stock firmware.
29
30    The parameter file layout for an original Linksys firmware:
31        :kernel 0x001A0000 /path/to/uImage
32        :rootfs 0 /path/to/root.squashfs
33        :u-boot 0 /path/to/u-boot.bin
34        #version 0x2020
35
36    Additionally since v2.4 an already complete image can be used:
37        :image 0 /path/to/openwrt-wrt350nv2-[squashfs|jffs2-64k].img
38
39    args:
40        1 wrt350nv2.par parameter file describing the image layout
41        2 wrt350nv2.img output file for linksys style image
42
43    A u-boot image inside the bin file is not necessary.
44    The version is not important.
45    The name of the bin file is not important, but still "wrt350n.bin" is used to
46    keep as close as possible to the stock firmware.
47
48    Linksys assumes that no mtd will be used to its maximum, so the last 16 bytes
49    of the mtd are abused to define the length of the next mtd content (4 bytes for
50    size + 12 pad bytes).
51
52    At the end of "rootfs" additional 16 bytes are abused for some data and a
53    highly important eRcOmM identifier, so the last 32 bytes of "rootfs" are abused.
54
55    At the end of "u-boot" 128 bytes are abused for some data, a checksum and a
56    highly important sErCoMm identifier.
57
58
59    This program uses a special GNU scanf modifier to allocate
60    sufficient memory for a strings with unknown length.
61    See http://www.kernel.org/doc/man-pages/online/pages/man3/scanf.3.html#NOTES
62
63
64    To extract everything from a Linksys style firmware image see
65    https://forum.openwrt.org/viewtopic.php?pid=92928#p92928
66
67    Changelog:
68    v2.4 - added ":image" definition for parameter file, this allows
69           to use a complete sysupgrade image without any kernel size check
70    v2.3 - allow jffs by adding its magic number (0x8519)
71           added parameter option -i to ignore unknown magic numbers
72    v2.2 - fixed checksum byte calculation for other versions than 0x2019
73           fixed rare problem with padsize
74           updated info to stock firmware 2.00.20
75           fixed typos
76    v2.1 - used "wrt350n.bin" for the created image (closer to stock)
77        added option to create the image in two separate steps (-b / -z)
78    v2.0 - complete re-write
79
80*/
81
82// includes
83#define _GNU_SOURCE // for GNU's basename()
84#include <assert.h>
85#include <errno.h> // errno
86#include <stdarg.h>
87#include <stdio.h> // fopen(), fread(), fclose(), etc.
88#include <stdlib.h> // system(), etc.
89#include <string.h> // basename(), strerror(), strdup(), etc.
90#include <unistd.h> // optopt(), access(), etc.
91#include <libgen.h>
92#include <sys/wait.h> // WEXITSTATUS, etc.
93
94// custom includes
95#include "md5.h" // MD5 routines
96#include "upgrade.h" // Linksys definitions from firmware 2.0.19 (unchanged up to 2.0.20)
97
98
99// version info
100#define VERSION "2.4"
101char program_info[] = "WRT350Nv2-Builder v%s by Dirk Teurlings <info@upexia.nl> and Matthias Buecher (http://www.maddes.net/)\n";
102
103// verbosity
104#define DEBUG 1
105#define DEBUG_LVL2 2
106int verbosity = 0;
107
108// mtd info
109typedef struct {
110    char *name;
111    int offset;
112    int size;
113    char *filename;
114    long int filesize;
115    unsigned char magic[2];
116} mtd_info;
117
118mtd_info mtd_kernel = { "kernel", 0, 0, NULL, 0L, { 0, 0 } };
119mtd_info mtd_rootfs = { "rootfs", 0, 0, NULL, 0L, { 0, 0 } };
120mtd_info mtd_image = { "image", 0, 0, NULL, 0L, { 0, 0 } };
121mtd_info mtd_uboot = { "u-boot", 0, 0, NULL, 0L, { 0, 0 } };
122
123#define ROOTFS_END_OFFSET 0x00760000
124#define ROOTFS_MIN_OFFSET 0x00640000 // should be filled up to here, to make sure that the zip file is big enough to pass the size check of the stock firmware
125                        // 2.0.17: filled up to 0x00640000
126                        // 2.0.19: filled up to 0x00670000
127                        // 2.0.20: filled up to 0x00670000
128
129// rootfs statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x0075FFE0 -n 16 "wrt350n.bin" ; echo -en "\n"
130unsigned char product_id[] = { 0x00, 0x03 }; // seems to be a fixed value
131unsigned char protocol_id[] = { 0x00, 0x00 }; // seems to be a fixed value
132unsigned char fw_version[] = { 0x20, 0x20 };
133unsigned char rootfs_unknown[] = { 0x90, 0xF7 }; // seems to be a fixed value
134unsigned char sign[] = { 0x65, 0x52, 0x63, 0x4F, 0x6D, 0x4D, 0x00, 0x00 }; // eRcOmM
135
136// u-boot statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x007FFF80 -n 128 "wrt350n.bin" ; echo -en "\n"
137//unsigned char sn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (12) seems to be an unused value
138//unsigned char pin[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (8) seems to be an unused value
139//unsigned char node[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (25) seems to be an unused value
140// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
141//unsigned char checksum[] = { 0xE9 }; // (1) is calculated, does it belong to node?
142unsigned char pid[] = { 0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, 0x00, 0x01, 0x00, 0x00, 0x59, 0x42, 0x50, 0x00, 0x01, // (70) seems to be a fixed value, except for fw version
143                0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, // protocol id?
145                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // protocol id?
146                0x12, 0x34, // firmware version, same as in rootfs
147                0x00, 0x00, 0x00, 0x04,
148                0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D }; // sErCoMm
149
150// img statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0 -n 512 "WRT350N-EU-ETSI-2.00.19.img" ; echo -en "\n" (unchanged up to 2.0.20)
151unsigned char img_hdr[] = { 0x00, 0x01, 0x00, 0x00, 0x59, 0x42, 0x50, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
152                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
154                0x00, 0x00,
155                0x12, 0x34, // firmware version, same as in rootfs
156                0x00, 0x00, 0x00, 0x04, 0x61, 0x44, 0x6D, 0x42, 0x6C, 0x4B, 0x3D, 0x00,
157                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
182                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183                0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // md5 checksum
184                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
185
186unsigned char img_eof[] = { 0xFF };
187
188
189void lprintf(int outputlevel, char *fmt, ...) {
190    va_list argp;
191    if (outputlevel <= verbosity) {
192        va_start(argp, fmt);
193        vprintf(fmt, argp);
194        va_end(argp);
195    }
196}
197
198
199int parse_par_file(FILE *f_par) {
200    int exitcode = 0;
201
202    char *buffer;
203    size_t buffer_size;
204    char *line;
205
206    int lineno;
207    int count;
208
209    char string1[256];
210    char string2[256];
211    int value;
212
213    mtd_info *mtd;
214    FILE *f_in;
215    int f_exitcode = 0;
216
217    // read all lines
218    buffer_size = 1000;
219    buffer = NULL;
220    lineno = 0;
221    while (!feof(f_par)) {
222        // read next line into memory
223        do {
224            // allocate memory for input line
225            if (!buffer) {
226                buffer = malloc(buffer_size);
227            }
228            if (!buffer) {
229                exitcode = 1;
230                printf("parse_par_file: can not allocate %i bytes\n", (int) buffer_size);
231                break;
232            }
233
234            line = fgets(buffer, buffer_size, f_par);
235            if (!line) {
236                exitcode = ferror(f_par);
237                if (exitcode) {
238                    printf("parse_par_file: %s\n", strerror(exitcode));
239                }
240                break;
241            }
242
243            // if buffer was not completely filled, then assume that line is complete
244            count = strlen(buffer) + 1;
245            if (count-- < buffer_size) {
246                break;
247            }
248
249            // otherwise....
250
251            // reset file position to line start
252            value = fseek(f_par, -count, SEEK_CUR);
253            if (value == -1) {
254                exitcode = errno;
255                printf("parse_par_file: %s\n", strerror(exitcode));
256                break;
257            }
258
259            // double buffer size
260            free(buffer);
261            buffer = NULL;
262            buffer_size *= 2;
263            lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
264        } while (1);
265        if ((!line) || (exitcode)) {
266            break;
267        }
268
269        lineno++; // increase line number
270
271        lprintf(DEBUG_LVL2, " line %i (%i) %s", lineno, count, line);
272
273        value = 0;
274        mtd = NULL;
275
276        // split line if starting with a colon
277        switch (line[0]) {
278            case ':':
279                count = sscanf(line, ":%255s %i %255s", string1, &value, string2);
280                if (count != 3) {
281                    printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno);
282                } else {
283                    // populate mtd_info if supported mtd names
284                    if (!strcmp(string1, mtd_kernel.name)) {
285                        mtd = &mtd_kernel;
286                    } else if (!strcmp(string1, mtd_rootfs.name)) {
287                        mtd = &mtd_rootfs;
288                    } else if (!strcmp(string1, mtd_uboot.name)) {
289                        mtd = &mtd_uboot;
290                    } else if (!strcmp(string1, mtd_image.name)) {
291                        mtd = &mtd_image;
292                    }
293
294                    if (!mtd) {
295                        printf("unknown mtd %s in line %i\n", string1, lineno);
296                    } else if (mtd->filename) {
297                        f_exitcode = 1;
298                        printf("mtd %s in line %i multiple definitions\n", string1, lineno);
299                    } else {
300                        mtd->size = value;
301                        mtd->filename = strdup(string2);
302
303                        // Get file size
304                        f_in = fopen(mtd->filename, "rb");
305                        if (!f_in) {
306                            f_exitcode = errno;
307                            printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
308                        } else {
309                            value = fread(&mtd->magic, 1, 2, f_in);
310                            if (value < 2) {
311                                if (ferror(f_in)) {
312                                    f_exitcode = ferror(f_in);
313                                    printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
314                                } else {
315                                    f_exitcode = 1;
316                                    printf("input file %s: smaller than two bytes, no magic code\n", mtd->filename);
317                                }
318                            }
319
320                            value = fseek(f_in, 0, SEEK_END);
321                            if (value == -1) {
322                                f_exitcode = errno;
323                                printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
324                            } else {
325                                mtd->filesize = ftell(f_in);
326                                if (mtd->filesize == -1) {
327                                    f_exitcode = errno;
328                                    printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
329                                }
330                            }
331
332                            fclose(f_in);
333                        }
334
335                        lprintf(DEBUG, "mtd %s in line %i: size=0x%08X, filesize=0x%08lX, magic=0x%02X%02X, file=%s\n", mtd->name, lineno, mtd->size, mtd->filesize, mtd->magic[0], mtd->magic[1], mtd->filename);
336                    }
337                }
338                break;
339            case '#': // integer values
340                count = sscanf(line, "#%255s %i", string1, &value);
341                if (count != 2) {
342                    printf("line %i does not meet defined format (#<variable name> <integer>\n", lineno);
343                } else {
344                    if (!strcmp(string1, "version")) {
345                        // changing version
346                        fw_version[0] = 0x000000FF & ( value >> 8 );
347                        fw_version[1] = 0x000000FF & value;
348                    } else {
349                        printf("unknown integer variable %s in line %i\n", string1, lineno);
350                    }
351
352                    lprintf(DEBUG, "integer variable %s in line %i: 0x%08X\n", string1, lineno, value);
353                }
354                break;
355            case '$': // strings
356                count = sscanf(line, "$%255s %255s", string1, string2);
357                if (count != 2) {
358                    printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno);
359                } else {
360/*
361                    if (!strcmp(string1, "something")) {
362                        something = strdup(string2);
363                    } else {
364*/
365                        printf("unknown string variable %s in line %i\n", string1, lineno);
366// }
367                    lprintf(DEBUG, "string variable %s in line %i: %s\n", string1, lineno, string2);
368                }
369                break;
370            default:
371                break;
372        }
373    }
374    free(buffer);
375
376    if (!exitcode) {
377        exitcode = f_exitcode;
378    }
379
380    return exitcode;
381}
382
383
384int create_bin_file(char *bin_filename) {
385    int exitcode = 0;
386
387    unsigned char *buffer;
388
389    int i;
390    mtd_info *mtd;
391    int addsize;
392    int padsize;
393
394    char *rand_filename = "/dev/urandom";
395    FILE *f_in;
396    int size;
397
398    unsigned long int csum;
399    unsigned char checksum;
400
401    FILE *f_out;
402
403    // allocate memory for bin file
404    buffer = malloc(KERNEL_CODE_OFFSET + FLASH_SIZE);
405    if (!buffer) {
406        exitcode = 1;
407        printf("create_bin_file: can not allocate %i bytes\n", FLASH_SIZE);
408    } else {
409        // initialize with zero
410        memset(buffer, 0, KERNEL_CODE_OFFSET + FLASH_SIZE);
411    }
412
413    // add files
414    if (!exitcode) {
415        for (i = 1; i <= 4; i++) {
416            addsize = 0;
417            padsize = 0;
418
419            switch (i) {
420                case 1:
421                    mtd = &mtd_image;
422                    padsize = ROOTFS_MIN_OFFSET - mtd->filesize;
423                    break;
424                case 2:
425                    mtd = &mtd_kernel;
426                    break;
427                case 3:
428                    mtd = &mtd_rootfs;
429                    addsize = mtd->filesize;
430                    padsize = ROOTFS_MIN_OFFSET - mtd_kernel.size - mtd->filesize;
431                    break;
432                case 4:
433                    mtd = &mtd_uboot;
434                    addsize = mtd->filesize;
435                    break;
436                default:
437                    mtd = NULL;
438                    exitcode = 1;
439                    printf("create_bin_file: unknown mtd %i\n", i);
440                    break;
441            }
442            if (!mtd) {
443                break;
444            }
445            if (!mtd->filename) {
446                continue;
447            }
448
449            lprintf(DEBUG, "adding mtd %s file %s\n", mtd->name, mtd->filename);
450
451            // adding file size
452            if (addsize) {
453                buffer[KERNEL_CODE_OFFSET + mtd->offset - 16] = 0x000000FFL & ( addsize >> 24 );
454                buffer[KERNEL_CODE_OFFSET + mtd->offset - 15] = 0x000000FFL & ( addsize >> 16 );
455                buffer[KERNEL_CODE_OFFSET + mtd->offset - 14] = 0x000000FFL & ( addsize >> 8 );
456                buffer[KERNEL_CODE_OFFSET + mtd->offset - 13] = 0x000000FFL & addsize;
457            }
458
459            // adding file content
460            f_in = fopen(mtd->filename, "rb");
461            if (!f_in) {
462                exitcode = errno;
463                printf("input file %s: %s\n", mtd->filename, strerror(exitcode));
464            } else {
465                size = fread(&buffer[KERNEL_CODE_OFFSET + mtd->offset], mtd->filesize, 1, f_in);
466                if (size < 1) {
467                    if (ferror(f_in)) {
468                        exitcode = ferror(f_in);
469                        printf("input file %s: %s\n", mtd->filename, strerror(exitcode));
470                    } else {
471                        exitcode = 1;
472                        printf("input file %s: smaller than before *doh*\n", mtd->filename);
473                    }
474                }
475                fclose(f_in);
476            }
477
478            // padding
479            if (padsize > 0) {
480                addsize = padsize & 0x0000FFFF; // start on next 64KB border
481                padsize -= addsize;
482            }
483            if (padsize > 0) {
484                printf("mtd %s input file %s is too small (0x%08lX), adding 0x%08X random bytes\n", mtd->name, mtd->filename, mtd->filesize, padsize);
485
486                addsize += KERNEL_CODE_OFFSET + mtd->offset + mtd->filesize; // get offset
487                lprintf(DEBUG, " padding offset 0x%08X length 0x%08X\n", addsize, padsize);
488
489                f_in = fopen(rand_filename, "rb");
490                if (!f_in) {
491                    exitcode = errno;
492                    printf("input file %s: %s\n", rand_filename, strerror(exitcode));
493                } else {
494                    size = fread(&buffer[addsize], padsize, 1, f_in);
495                    if (size < 1) {
496                        if (ferror(f_in)) {
497                            exitcode = ferror(f_in);
498                            printf("input file %s: %s\n", rand_filename, strerror(exitcode));
499                        } else {
500                            exitcode = 1;
501                            printf("input file %s: smaller than before *doh*\n", rand_filename);
502                        }
503                    }
504                }
505                fclose(f_in);
506            }
507        }
508    }
509
510    // add special contents
511    if (!exitcode) {
512        lprintf(DEBUG, "adding rootfs special data\n");
513        memcpy(&buffer[KERNEL_CODE_OFFSET + PRODUCT_ID_OFFSET], product_id, 2);
514        memcpy(&buffer[KERNEL_CODE_OFFSET + PROTOCOL_ID_OFFSET], protocol_id, 2);
515        memcpy(&buffer[KERNEL_CODE_OFFSET + FW_VERSION_OFFSET], fw_version, 2);
516        memcpy(&buffer[KERNEL_CODE_OFFSET + FW_VERSION_OFFSET + 2], rootfs_unknown, 2);
517        memcpy(&buffer[KERNEL_CODE_OFFSET + SIGN_OFFSET], sign, 8); // eRcOmM
518
519        lprintf(DEBUG, "adding u-boot special data\n");
520// memcpy(&buffer[KERNEL_CODE_OFFSET + SN_OFF], sn, 12); // ToDo: currently zero, find out what's this for?
521// memcpy(&buffer[KERNEL_CODE_OFFSET + PIN_OFF], pin, 8); // ToDo: currently zero, find out what's this for?
522// memcpy(&buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF], node, 25); // ToDo: currently zero, find out what's this for?
523        memcpy(&buffer[KERNEL_CODE_OFFSET + BOOT_ADDR_BASE_OFF + PID_OFFSET], pid, 70); // sErCoMm
524        memcpy(&buffer[KERNEL_CODE_OFFSET + BOOT_ADDR_BASE_OFF + PID_OFFSET + 57], fw_version, 2);
525
526        lprintf(DEBUG, "adding checksum byte\n");
527        csum = 0;
528        for (i = 0; i < KERNEL_CODE_OFFSET + FLASH_SIZE; i++) {
529            csum += buffer[i];
530        }
531        lprintf(DEBUG_LVL2, " checksum 0x%016lX (%li)\n", csum, csum);
532
533        buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF + 25] = ~csum + 1;
534        lprintf(DEBUG, " byte 0x%02X\n", buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF + 25]);
535    }
536
537    // write bin file
538    if (!exitcode) {
539        lprintf(DEBUG, "writing file %s\n", bin_filename);
540        f_out = fopen(bin_filename, "wb");
541        if (!f_out) {
542            exitcode = errno;
543            printf("output file %s: %s\n", bin_filename, strerror(exitcode));
544        } else {
545            size = fwrite(buffer, KERNEL_CODE_OFFSET + FLASH_SIZE, 1, f_out);
546            if (size < 1) {
547                if (ferror(f_out)) {
548                    exitcode = ferror(f_out);
549                    printf("output file %s: %s\n", bin_filename, strerror(exitcode));
550                } else {
551                    exitcode = 1;
552                    printf("output file %s: unspecified write error\n", bin_filename);
553                }
554            }
555            fclose(f_out);
556        }
557    }
558
559    return exitcode;
560}
561
562
563int create_zip_file(char *zip_filename, char *bin_filename) {
564    int exitcode = 0;
565
566    char *buffer;
567    size_t buffer_size;
568    int count;
569
570    buffer_size = 1000;
571    buffer = NULL;
572    do {
573        // allocate memory for command line
574        if (!buffer) {
575            buffer = malloc(buffer_size);
576        }
577        if (!buffer) {
578            exitcode = 1;
579            printf("create_zip_file: can not allocate %i bytes\n", (int) buffer_size);
580            break;
581        }
582
583        // if buffer was not completely filled, then line fit in completely
584        count = snprintf(buffer, buffer_size, "zip \"%s\" \"%s\"", zip_filename, bin_filename);
585        if ((count > -1) && (count < buffer_size)) {
586            break;
587        }
588
589        // otherwise try again with more space
590        if (count > -1) { // glibc 2.1
591            buffer_size = count + 1; // precisely what is needed
592        } else { // glibc 2.0
593            buffer_size *= 2; // twice the old size
594        }
595        free(buffer);
596        buffer = NULL;
597        lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
598    } while (1);
599
600    if (!exitcode) {
601        // zipping binfile
602        lprintf(DEBUG, "%s\n", buffer);
603        count = system(buffer);
604        if ((count < 0) || (WEXITSTATUS(count))) {
605            exitcode = 1;
606            printf("create_zip_file: can not execute %s bytes\n", buffer);
607        }
608    }
609
610    return exitcode;
611}
612
613
614int create_img_file(FILE *f_out, char *out_filename, char *zip_filename) {
615    int exitcode = 0;
616
617    md5_state_t state;
618    md5_byte_t digest[16];
619
620    int i;
621    int size;
622
623    FILE *f_in;
624    unsigned char buffer[1];
625
626    // copy firmware version
627    memcpy(&img_hdr[50], fw_version, 2);
628
629    // clear md5 checksum
630    memset(&img_hdr[480], 0, 16);
631
632    // prepare md5 checksum calculation
633    md5_init(&state);
634
635    // add img header
636    lprintf(DEBUG_LVL2, " adding img header\n");
637    for (i = 0; i < 512; i++) {
638        size = fputc(img_hdr[i], f_out);
639        if (size == EOF) {
640            exitcode = ferror(f_out);
641            printf("output file %s: %s\n", out_filename, strerror(exitcode));
642            break;
643        }
644        md5_append(&state, (const md5_byte_t *)&img_hdr[i], 1);
645    }
646
647    // adding zip file
648    if (!exitcode) {
649        lprintf(DEBUG_LVL2, " adding zip file\n");
650        f_in = fopen(zip_filename, "rb");
651        if (!f_in) {
652            exitcode = errno;
653            printf("input file %s: %s\n", zip_filename, strerror(exitcode));
654        } else {
655            while ((size = fgetc(f_in)) != EOF) {
656                buffer[0] = size;
657
658                size = fputc(buffer[0], f_out);
659                if (size == EOF) {
660                    exitcode = ferror(f_out);
661                    printf("output file %s: %s\n", out_filename, strerror(exitcode));
662                    break;
663                }
664                md5_append(&state, (const md5_byte_t *)buffer, 1);
665            }
666            if (ferror(f_in)) {
667                exitcode = ferror(f_in);
668                printf("input file %s: %s\n", zip_filename, strerror(exitcode));
669            }
670        }
671
672    }
673
674    // add end byte
675    if (!exitcode) {
676        lprintf(DEBUG_LVL2, " adding img eof byte\n");
677        size = fputc(img_eof[0], f_out);
678        if (size == EOF) {
679            exitcode = ferror(f_out);
680            printf("output file %s: %s\n", out_filename, strerror(exitcode));
681        }
682        md5_append(&state, (const md5_byte_t *)img_eof, 1);
683    }
684
685    // append salt to md5 checksum
686    md5_append(&state, (const md5_byte_t *)"A^gU*<>?RFY@#DR&Z", 17);
687
688    // finish md5 checksum calculation
689    md5_finish(&state, digest);
690
691    // write md5 checksum into img header
692    if (!exitcode) {
693        lprintf(DEBUG_LVL2, " writing md5 checksum into img header of file\n");
694
695        size = fseek(f_out, 480, SEEK_SET);
696        if (size == -1) {
697            exitcode = errno;
698            printf("output file %s: %s\n", out_filename, strerror(exitcode));
699        } else {
700            size = fwrite(digest, 16, 1, f_out);
701            if (size < 1) {
702                if (ferror(f_out)) {
703                    exitcode = ferror(f_out);
704                    printf("output file %s: %s\n", out_filename, strerror(exitcode));
705                } else {
706                    exitcode = 1;
707                    printf("output file %s: unspecified write error\n", out_filename);
708                }
709            }
710        }
711
712        fclose(f_in);
713    }
714
715    return exitcode;
716}
717
718
719int main(int argc, char *argv[]) {
720    int exitcode = 0;
721
722    int help;
723    int onlybin;
724    int havezip;
725    int ignoremagic;
726    char option;
727    char *par_filename = NULL;
728    char *img_filename = NULL;
729    char *base_filename = NULL;
730    char *bin_filename = NULL;
731    char *zip_filename = NULL;
732
733    FILE *f_par = NULL;
734    FILE *f_img = NULL;
735
736    int i;
737    mtd_info *mtd;
738    int noupdate;
739    int sizecheck;
740    int magiccheck;
741    int magicerror;
742
743
744// display program header
745    printf(program_info, VERSION);
746
747
748// command line processing
749    // options
750    help = 0;
751    onlybin = 0;
752    havezip = 0;
753    ignoremagic = 0;
754    while ((option = getopt(argc, argv, "hbzif:v")) != -1) {
755        switch(option) {
756            case 'h':
757                help = 1;
758                break;
759            case 'b':
760                onlybin = 1;
761                break;
762            case 'z':
763                havezip = 1;
764                break;
765            case 'i':
766                ignoremagic = 1;
767                break;
768            case 'f':
769                sizecheck = sscanf(optarg, "%i", &i);
770                if (sizecheck != 1) {
771                    printf("Firmware version of -f option not a valid integer\n");
772                    exitcode = 1;
773                } else {
774                    fw_version[0] = 0x000000FF & ( i >> 8 );
775                    fw_version[1] = 0x000000FF & i;
776                }
777                break;
778            case 'v':
779                verbosity++;
780                break;
781            case ':': // option with missing operand
782                printf("Option -%c requires an operand\n", optopt);
783                exitcode = 1;
784                break;
785            case '?':
786                printf("Unrecognized option: -%c\n", optopt);
787                exitcode = 1;
788                break;
789        }
790    }
791
792    // files
793    for ( ; optind < argc; optind++) {
794        if (!par_filename) {
795            par_filename = argv[optind];
796
797            if (access(par_filename, R_OK)) {
798                if (havezip) {
799                    printf("No read access to zip file %s\n", par_filename);
800                } else {
801                    printf("No read access to parameter or zip file %s\n", par_filename);
802                }
803                exitcode = 1;
804            }
805
806            continue;
807        }
808
809        if ((!onlybin) && (!img_filename)) {
810            img_filename = argv[optind];
811
812            if (!access(img_filename, F_OK)) { // if file already exists then check write access
813                if (access(img_filename, W_OK)) {
814                    printf("No write access to image file %s\n", img_filename);
815                    exitcode = 1;
816                }
817            }
818
819            continue;
820        }
821
822        printf("Too many files stated\n");
823        exitcode = 1;
824        break;
825    }
826
827    // file name checks
828    if (!par_filename) {
829        if (havezip) {
830            printf("Zip file not stated\n");
831        } else {
832            printf("Parameter file not stated\n");
833        }
834        exitcode = 1;
835    } else {
836        base_filename = basename(par_filename);
837        if (!base_filename) {
838            if (havezip) {
839                printf("Zip file is a directory\n");
840            } else {
841                printf("Parameter file is a directory\n");
842            }
843            exitcode = 1;
844        }
845    }
846
847    if (!onlybin) {
848        if (!img_filename) {
849            printf("Image file not stated\n");
850            exitcode = 1;
851        } else {
852            base_filename = basename(img_filename);
853            if (!base_filename) {
854                printf("Image file is a directory\n");
855                exitcode = 1;
856            }
857        }
858    }
859
860    // check for mutually exclusive options
861    if ((onlybin) && (havezip)) {
862        printf("Option -b and -z are mutually exclusive\n");
863        exitcode = 1;
864    }
865
866    // react on option problems or help request, then exit
867    if ((exitcode) || (help)) {
868        if (help) {
869            printf("This program creates Linksys style images for the WRT350Nv2 router.\n");
870        }
871        printf(" Usage:\n\
872  %s [-h] [-b] [-z] [-i] [-f <version>] [-v] <parameter or zip file> [<image file>]\n\n\
873  Options:\n\
874  -h - Show this help\n\
875  -b - Create only bin file, no img or zip file is created\n\
876  -z - Have zip file, the img file will be directly created from it\n\
877  -i - Ignore unknown magic numbers\n\
878  -f <version> - Wanted firmware version to use with -z\n\
879                   Default firmware version is 0x2020 = 2.00.20.\n\
880                   Note: version from parameter file will supersede this\n\
881  -v - Increase debug verbosity level\n\n\
882  Example:\n\
883  %s wrt350nv2.par wrt350nv2.img\n\n", argv[0], argv[0]);
884        return exitcode;
885    }
886
887    // handle special case when zipfile is stated
888    if (havezip) {
889        zip_filename = par_filename;
890        par_filename = NULL;
891    }
892
893    lprintf(DEBUG_LVL2, " Verbosity: %i\n", verbosity);
894    lprintf(DEBUG_LVL2, " Program: %s\n", argv[0]);
895
896    if (par_filename) {
897        lprintf(DEBUG, "Parameter file: %s\n", par_filename);
898    }
899    if (zip_filename) {
900        lprintf(DEBUG, "Zip file: %s\n", zip_filename);
901    }
902    if (img_filename) {
903        lprintf(DEBUG, "Image file: %s\n", img_filename);
904    }
905
906
907// open files from command line
908    // parameter/zip file
909    if (par_filename) {
910        f_par = fopen(par_filename, "rt");
911        if (!f_par) {
912            exitcode = errno;
913            printf("Input file %s: %s\n", par_filename, strerror(exitcode));
914        }
915    }
916
917    // image file
918    if (img_filename) {
919        f_img = fopen(img_filename, "wb");
920        if (!f_img) {
921            exitcode = errno;
922            printf("Output file %s: %s\n", img_filename, strerror(exitcode));
923        }
924    }
925
926    if (exitcode) {
927        return exitcode;
928    }
929
930
931// parameter file processing
932    if ((!exitcode) && (f_par)) {
933        lprintf(DEBUG, "parsing parameter file...\n");
934
935        exitcode = parse_par_file(f_par);
936
937        lprintf(DEBUG, "...done parsing file\n");
938    }
939    if (f_par) {
940        fclose(f_par);
941    }
942
943
944// check all input data
945    if ((!exitcode) && (par_filename)) {
946        lprintf(DEBUG, "checking mtd data...\n");
947
948        for (i = 1; i <= 4; i++) {
949            noupdate = 0;
950            sizecheck = 0;
951            magiccheck = 0;
952
953            switch (i) {
954                case 1:
955                    mtd = &mtd_image;
956                    sizecheck = ROOTFS_END_OFFSET;
957                    magiccheck = 1;
958                    break;
959                case 2:
960                    mtd = &mtd_kernel;
961                    sizecheck = mtd_kernel.size - 16;
962                    magiccheck = 1;
963                    break;
964                case 3:
965                    mtd = &mtd_rootfs;
966                    mtd->offset = mtd_kernel.size;
967                    mtd->size = ROOTFS_END_OFFSET - mtd_kernel.size;
968                    sizecheck = PRODUCT_ID_OFFSET - mtd_kernel.size;
969                    magiccheck = 1;
970                    break;
971                case 4:
972                    mtd = &mtd_uboot;
973                    mtd->offset = BOOT_ADDR_BASE_OFF;
974                    noupdate = 1;
975                    sizecheck = SN_OFF - BOOT_ADDR_BASE_OFF;
976                    break;
977                default:
978                    mtd = NULL;
979                    exitcode = 1;
980                    printf("unknown mtd check %i\n", i);
981                    break;
982            }
983            if (!mtd) {
984                break;
985            }
986
987            lprintf(DEBUG_LVL2, " checking mtd %s\n", mtd->name);
988
989            // general checks
990
991            // no further checks if no file data present
992            if (!mtd->filename) {
993                continue;
994            }
995
996            // not updated by stock firmware
997            if (noupdate) {
998                printf("mtd %s is specified, but will not be updated as of Linksys firmware 2.0.19\n", mtd->name);
999            }
1000
1001            // general magic number check
1002            magicerror = 0;
1003            if (magiccheck) {
1004                switch (i) {
1005                    case 1: // image
1006                    case 2: // kernel
1007                        if (!(
1008                               ((mtd->magic[0] == 0x27) && (mtd->magic[1] == 0x05)) // uImage
1009                        )) {
1010                            magicerror = 1;
1011                        }
1012                        break;
1013                    case 3: // rootfs
1014                        if (!(
1015                               ((mtd->magic[0] == 0x68) && (mtd->magic[1] == 0x73)) // squashfs
1016                            || ((mtd->magic[0] == 0x85) && (mtd->magic[1] == 0x19)) // jffs
1017                        )) {
1018                            magicerror = 1;
1019                        }
1020                        break;
1021                    default:
1022                        magicerror = 1;
1023                        break;
1024                }
1025                if (magicerror) {
1026                    printf("mtd %s input file %s has unknown magic number (0x%02X%02X)", mtd->name, mtd->filename, mtd->magic[0], mtd->magic[1]);
1027                    if (ignoremagic) {
1028                        printf("...ignoring");
1029                    } else {
1030                        exitcode = 1;
1031                    }
1032                    printf("\n");
1033                }
1034            }
1035
1036            // mtd specific size check
1037            if (mtd == &mtd_image) {
1038                if (mtd->filesize < 0x00200000) {
1039                    exitcode = 1;
1040                    printf("mtd %s input file %s too unrealistic small (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1041                }
1042            }
1043
1044            if (mtd == &mtd_kernel) {
1045                if (mtd->filesize < 0x00080000) {
1046                    exitcode = 1;
1047                    printf("mtd %s input file %s too unrealistic small (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1048                }
1049            }
1050
1051            // general size check
1052            if (sizecheck) {
1053                if (sizecheck <= 0) {
1054                    exitcode = 1;
1055                    printf("mtd %s bad file size check (%i) due to input data\n", mtd->name, sizecheck);
1056                } else {
1057                    if (mtd->filesize > sizecheck) {
1058                        exitcode = 1;
1059                        printf("mtd %s input file %s too big (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1060                    }
1061                }
1062            }
1063        }
1064
1065        // Check for mandatory parts
1066        if ((!mtd_image.filename) && (!mtd_kernel.filename || !mtd_rootfs.filename)) {
1067            exitcode = 1;
1068            if (mtd_kernel.filename && !mtd_rootfs.filename) {
1069                printf("Kernel without rootfs, either incorrectly specified or not at all in parameter file\n");
1070            } else if (!mtd_kernel.filename && mtd_rootfs.filename) {
1071                printf("Rootfs without kernel, either incorrectly specified or not at all in parameter file\n");
1072            } else {
1073                printf("Neither an image nor kernel with rootfs was/were correctly specified or at all in parameter file\n");
1074            }
1075        }
1076
1077        // Check for duplicate parts
1078        if ((mtd_image.filename) && (mtd_kernel.filename || mtd_rootfs.filename)) {
1079            exitcode = 1;
1080            printf("Image and kernel/rootfs specified in parameter file\n");
1081        }
1082
1083        lprintf(DEBUG, "...done checking mtd data\n");
1084    }
1085
1086
1087// bin creation in memory
1088    if ((!exitcode) && (par_filename)) {
1089        bin_filename = "wrt350n.bin";
1090
1091        lprintf(DEBUG, "creating bin file %s...\n", bin_filename);
1092
1093        exitcode = create_bin_file(bin_filename);
1094
1095        lprintf(DEBUG, "...done creating bin file\n");
1096    }
1097
1098// zip file creation
1099    if ((!exitcode) && (!onlybin) && (!zip_filename)) {
1100        zip_filename = "wrt350n.zip";
1101
1102        lprintf(DEBUG, "creating zip file %s...\n", zip_filename);
1103
1104        exitcode = create_zip_file(zip_filename, bin_filename);
1105
1106        lprintf(DEBUG, "...done creating zip file\n");
1107    }
1108
1109
1110// img file creation
1111    if ((!exitcode) && (f_img)) {
1112        lprintf(DEBUG, "creating img file...\n");
1113
1114        exitcode = create_img_file(f_img, img_filename, zip_filename);
1115
1116        lprintf(DEBUG, "...done creating img file\n");
1117    }
1118
1119// clean up
1120    if (f_img) {
1121        fclose(f_img);
1122    }
1123
1124// end program
1125    return exitcode;
1126}
1127

Archive Download this file



interactive