source: trunk/tools/wrt350nv2-builder/src/wrt350nv2-builder.c

Last change on this file was 31199, checked in by florian, 4 years ago

[orion] update wrt350nv2-builder to v2.4 and use new functionality for target orion_generic

Signed-off-by: Matthias Bücher <mail@…>

File size: 32.7 KB
Line 
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}
Note: See TracBrowser for help on using the repository browser.