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