kirkwood: move led triggers to dts
[oweals/openwrt.git] / tools / wrt350nv2-builder / src / wrt350nv2-builder.c
1 /*
2
3         WRT350Nv2-Builder 2.4 (previously called buildimg)
4         Copyright (C) 2008-2009 Dirk Teurlings <info@upexia.nl>
5         Copyright (C) 2009-2011 Matthias Buecher (http://www.maddes.net/)
6
7         This program is free software; you can redistribute it and/or modify
8         it under the terms of the GNU General Public License as published by
9         the Free Software Foundation; either version 2 of the License, or
10         (at your option) any later version.
11
12         This program is distributed in the hope that it will be useful,
13         but WITHOUT ANY WARRANTY; without even the implied warranty of
14         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15         GNU General Public License for more details.
16
17         You should have received a copy of the GNU General Public License
18         along with this program; if not, write to the Free Software
19         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21         A lot of thanks to Kaloz and juhosg from OpenWRT and Lennert Buytenhek from
22         marvell for helping me figure this one out. This code is based on bash
23         scripts wrote by Peter van Valderen so the real credit should go to him.
24
25         This program reads the provided parameter file and creates an image which can
26         be used to flash a Linksys WRT350N v2 from stock firmware.
27         The trick is to fill unused space in the bin file with random, so that the
28         resulting zip file passes the size check of the stock firmware.
29
30         The parameter file layout for an original Linksys firmware:
31                 :kernel 0x001A0000      /path/to/uImage
32                 :rootfs 0       /path/to/root.squashfs
33                 :u-boot 0       /path/to/u-boot.bin
34                 #version        0x2020
35
36         Additionally since v2.4 an already complete image can be used:
37                 :image          0       /path/to/openwrt-wrt350nv2-[squashfs|jffs2-64k].img
38
39         args:
40                 1       wrt350nv2.par           parameter file describing the image layout
41                 2       wrt350nv2.img           output file for linksys style image
42
43         A u-boot image inside the bin file is not necessary.
44         The version is not important.
45         The name of the bin file is not important, but still "wrt350n.bin" is used to
46         keep as close as possible to the stock firmware.
47
48         Linksys assumes that no mtd will be used to its maximum, so the last 16 bytes
49         of the mtd are abused to define the length of the next mtd content (4 bytes for
50         size + 12 pad bytes).
51
52         At the end of "rootfs" additional 16 bytes are abused for some data and a
53         highly important eRcOmM identifier, so the last 32 bytes of "rootfs" are abused.
54
55         At the end of "u-boot" 128 bytes are abused for some data, a checksum and a
56         highly important sErCoMm identifier.
57
58
59         This program uses a special GNU scanf modifier to allocate
60         sufficient memory for a strings with unknown length.
61         See http://www.kernel.org/doc/man-pages/online/pages/man3/scanf.3.html#NOTES
62
63
64         To extract everything from a Linksys style firmware image see
65         https://forum.openwrt.org/viewtopic.php?pid=92928#p92928
66
67         Changelog:
68         v2.4 - added ":image" definition for parameter file, this allows
69                to use a complete sysupgrade image without any kernel size check
70         v2.3 - allow jffs by adding its magic number (0x8519)
71                added parameter option -i to ignore unknown magic numbers
72         v2.2 - fixed checksum byte calculation for other versions than 0x2019
73                fixed rare problem with padsize
74                updated info to stock firmware 2.00.20
75                fixed typos
76         v2.1 - used "wrt350n.bin" for the created image (closer to stock)
77                 added option to create the image in two separate steps (-b / -z)
78         v2.0 - complete re-write
79
80 */
81
82 // includes
83 #define _GNU_SOURCE     // for GNU's basename()
84 #include <assert.h>
85 #include <errno.h>      // errno
86 #include <stdarg.h>
87 #include <stdio.h>      // fopen(), fread(), fclose(), etc.
88 #include <stdlib.h>     // system(), etc.
89 #include <string.h>     // basename(), strerror(), strdup(), etc.
90 #include <unistd.h>     // optopt(), access(), etc.
91 #include <libgen.h>
92 #include <sys/wait.h>   // WEXITSTATUS, etc.
93
94 // custom includes
95 #include "md5.h"        // MD5 routines
96 #include "upgrade.h"    // Linksys definitions from firmware 2.0.19 (unchanged up to 2.0.20)
97
98
99 // version info
100 #define VERSION "2.4"
101 char 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
106 int verbosity = 0;
107
108 // mtd info
109 typedef 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
118 mtd_info mtd_kernel = { "kernel", 0, 0, NULL, 0L, { 0, 0 } };
119 mtd_info mtd_rootfs = { "rootfs", 0, 0, NULL, 0L, { 0, 0 } };
120 mtd_info mtd_image = { "image", 0, 0, NULL, 0L, { 0, 0 } };
121 mtd_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"
130 unsigned char product_id[] = { 0x00, 0x03 };    // seems to be a fixed value
131 unsigned char protocol_id[] = { 0x00, 0x00 };   // seems to be a fixed value
132 unsigned char fw_version[] = { 0x20, 0x20 };
133 unsigned char rootfs_unknown[] = { 0x90, 0xF7 };        // seems to be a fixed value
134 unsigned 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?
142 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
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)
151 unsigned 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
186 unsigned char img_eof[] = {     0xFF };
187
188
189 void 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
199 int 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
384 int 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         free(buffer);
560         return exitcode;
561 }
562
563
564 int create_zip_file(char *zip_filename, char *bin_filename) {
565         int exitcode = 0;
566
567         char *buffer;
568         size_t buffer_size;
569         int count;
570
571         buffer_size = 1000;
572         buffer = NULL;
573         do {
574                 // allocate memory for command line
575                 if (!buffer) {
576                         buffer = malloc(buffer_size);
577                 }
578                 if (!buffer) {
579                         exitcode = 1;
580                         printf("create_zip_file: can not allocate %i bytes\n", (int) buffer_size);
581                         break;
582                 }
583
584                 // if buffer was not completely filled, then line fit in completely
585                 count = snprintf(buffer, buffer_size, "zip \"%s\" \"%s\"", zip_filename, bin_filename);
586                 if ((count > -1) && (count < buffer_size)) {
587                         break;
588                 }
589
590                 // otherwise try again with more space
591                 if (count > -1) {       // glibc 2.1
592                         buffer_size = count + 1;        // precisely what is needed
593                 } else {        // glibc 2.0
594                         buffer_size *= 2;       // twice the old size
595                 }
596                 free(buffer);
597                 buffer = NULL;
598                 lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
599         } while (1);
600
601         if (!exitcode) {
602                 // zipping binfile
603                 lprintf(DEBUG, "%s\n", buffer);
604                 count = system(buffer);
605                 if ((count < 0) || (WEXITSTATUS(count))) {
606                         exitcode = 1;
607                         printf("create_zip_file: can not execute %s bytes\n", buffer);
608                 }
609         }
610
611         return exitcode;
612 }
613
614
615 int create_img_file(FILE *f_out, char *out_filename, char *zip_filename) {
616         int exitcode = 0;
617
618         md5_state_t state;
619         md5_byte_t digest[16];
620
621         int i;
622         int size;
623
624         FILE *f_in;
625         unsigned char buffer[1];
626
627         // copy firmware version
628         memcpy(&img_hdr[50], fw_version, 2);
629
630         // clear md5 checksum
631         memset(&img_hdr[480], 0, 16);
632
633         // prepare md5 checksum calculation
634         md5_init(&state);
635
636         // add img header
637         lprintf(DEBUG_LVL2, " adding img header\n");
638         for (i = 0; i < 512; i++) {
639                 size = fputc(img_hdr[i], f_out);
640                 if (size == EOF) {
641                         exitcode = ferror(f_out);
642                         printf("output file %s: %s\n", out_filename, strerror(exitcode));
643                         break;
644                 }
645                 md5_append(&state, (const md5_byte_t *)&img_hdr[i], 1);
646         }
647
648         // adding zip file
649         if (!exitcode) {
650                 lprintf(DEBUG_LVL2, " adding zip file\n");
651                 f_in = fopen(zip_filename, "rb");
652                 if (!f_in) {
653                         exitcode = errno;
654                         printf("input file %s: %s\n", zip_filename, strerror(exitcode));
655                 } else {
656                         while ((size = fgetc(f_in)) != EOF) {
657                                 buffer[0] = size;
658
659                                 size = fputc(buffer[0], f_out);
660                                 if (size == EOF) {
661                                         exitcode = ferror(f_out);
662                                         printf("output file %s: %s\n", out_filename, strerror(exitcode));
663                                         break;
664                                 }
665                                 md5_append(&state, (const md5_byte_t *)buffer, 1);
666                         }
667                         if (ferror(f_in)) {
668                                 exitcode = ferror(f_in);
669                                 printf("input file %s: %s\n", zip_filename, strerror(exitcode));
670                         }
671                 }
672
673         }
674
675         // add end byte
676         if (!exitcode) {
677                 lprintf(DEBUG_LVL2, " adding img eof byte\n");
678                 size = fputc(img_eof[0], f_out);
679                 if (size == EOF) {
680                         exitcode = ferror(f_out);
681                         printf("output file %s: %s\n", out_filename, strerror(exitcode));
682                 }
683                 md5_append(&state, (const md5_byte_t *)img_eof, 1);
684         }
685
686         // append salt to md5 checksum
687         md5_append(&state, (const md5_byte_t *)"A^gU*<>?RFY@#DR&Z", 17);
688
689         // finish md5 checksum calculation
690         md5_finish(&state, digest);
691
692         // write md5 checksum into img header
693         if (!exitcode) {
694                 lprintf(DEBUG_LVL2, " writing md5 checksum into img header of file\n");
695
696                 size = fseek(f_out, 480, SEEK_SET);
697                 if (size == -1) {
698                         exitcode = errno;
699                         printf("output file %s: %s\n", out_filename, strerror(exitcode));
700                 } else {
701                         size = fwrite(digest, 16, 1, f_out);
702                         if (size < 1) {
703                                 if (ferror(f_out)) {
704                                         exitcode = ferror(f_out);
705                                         printf("output file %s: %s\n", out_filename, strerror(exitcode));
706                                 } else {
707                                         exitcode = 1;
708                                         printf("output file %s: unspecified write error\n", out_filename);
709                                 }
710                         }
711                 }
712
713                 fclose(f_in);
714         }
715
716         return exitcode;
717 }
718
719
720 int main(int argc, char *argv[]) {
721         int exitcode = 0;
722
723         int help;
724         int onlybin;
725         int havezip;
726         int ignoremagic;
727         char option;
728         char *par_filename = NULL;
729         char *img_filename = NULL;
730         char *base_filename = NULL;
731         char *bin_filename = NULL;
732         char *zip_filename = NULL;
733
734         FILE *f_par = NULL;
735         FILE *f_img = NULL;
736
737         int i;
738         mtd_info *mtd;
739         int noupdate;
740         int sizecheck;
741         int magiccheck;
742         int magicerror;
743
744
745 // display program header
746         printf(program_info, VERSION);
747
748
749 // command line processing
750         // options
751         help = 0;
752         onlybin = 0;
753         havezip = 0;
754         ignoremagic = 0;
755         while ((option = getopt(argc, argv, "hbzif:v")) != -1) {
756                 switch(option) {
757                         case 'h':
758                                 help = 1;
759                                 break;
760                         case 'b':
761                                 onlybin = 1;
762                                 break;
763                         case 'z':
764                                 havezip = 1;
765                                 break;
766                         case 'i':
767                                 ignoremagic = 1;
768                                 break;
769                         case 'f':
770                                 sizecheck = sscanf(optarg, "%i", &i);
771                                 if (sizecheck != 1) {
772                                         printf("Firmware version of -f option not a valid integer\n");
773                                         exitcode = 1;
774                                 } else {
775                                         fw_version[0] = 0x000000FF & ( i >> 8 );
776                                         fw_version[1] = 0x000000FF &   i;
777                                 }
778                                 break;
779                         case 'v':
780                                 verbosity++;
781                                 break;
782                         case ':':       // option with missing operand
783                                 printf("Option -%c requires an operand\n", optopt);
784                                 exitcode = 1;
785                                 break;
786                         case '?':
787                                 printf("Unrecognized option: -%c\n", optopt);
788                                 exitcode = 1;
789                                 break;
790                 }
791         }
792
793         // files
794         for ( ; optind < argc; optind++) {
795                 if (!par_filename) {
796                         par_filename = argv[optind];
797
798                         if (access(par_filename, R_OK)) {
799                                 if (havezip) {
800                                         printf("No read access to zip file %s\n", par_filename);
801                                 } else {
802                                         printf("No read access to parameter or zip file %s\n", par_filename);
803                                 }
804                                 exitcode = 1;
805                         }
806
807                         continue;
808                 }
809
810                 if ((!onlybin) && (!img_filename)) {
811                         img_filename = argv[optind];
812
813                         if (!access(img_filename, F_OK)) {      // if file already exists then check write access
814                                 if (access(img_filename, W_OK)) {
815                                         printf("No write access to image file %s\n", img_filename);
816                                         exitcode = 1;
817                                 }
818                         }
819
820                         continue;
821                 }
822
823                 printf("Too many files stated\n");
824                 exitcode = 1;
825                 break;
826         }
827
828         // file name checks
829         if (!par_filename) {
830                 if (havezip) {
831                         printf("Zip file not stated\n");
832                 } else {
833                         printf("Parameter file not stated\n");
834                 }
835                 exitcode = 1;
836         } else {
837                 base_filename = basename(par_filename);
838                 if (!base_filename) {
839                         if (havezip) {
840                                 printf("Zip file is a directory\n");
841                         } else {
842                                 printf("Parameter file is a directory\n");
843                         }
844                         exitcode = 1;
845                 }
846         }
847
848         if (!onlybin) {
849                 if (!img_filename) {
850                         printf("Image file not stated\n");
851                         exitcode = 1;
852                 } else {
853                         base_filename = basename(img_filename);
854                         if (!base_filename) {
855                                 printf("Image file is a directory\n");
856                                 exitcode = 1;
857                         }
858                 }
859         }
860
861         // check for mutually exclusive options
862         if ((onlybin) && (havezip)) {
863                 printf("Option -b and -z are mutually exclusive\n");
864                 exitcode = 1;
865         }
866
867         // react on option problems or help request, then exit
868         if ((exitcode) || (help)) {
869                 if (help) {
870                         printf("This program creates Linksys style images for the WRT350Nv2 router.\n");
871                 }
872                 printf("  Usage:\n\
873   %s [-h] [-b] [-z] [-i] [-f <version>] [-v] <parameter or zip file> [<image file>]\n\n\
874   Options:\n\
875   -h            -  Show this help\n\
876   -b            -  Create only bin file, no img or zip file is created\n\
877   -z            -  Have zip file, the img file will be directly created from it\n\
878   -i            -  Ignore unknown magic numbers\n\
879   -f <version>  -  Wanted firmware version to use with -z\n\
880                    Default firmware version is 0x2020 = 2.00.20.\n\
881                    Note: version from parameter file will supersede this\n\
882   -v            -  Increase debug verbosity level\n\n\
883   Example:\n\
884   %s wrt350nv2.par wrt350nv2.img\n\n", argv[0], argv[0]);
885                 return exitcode;
886         }
887
888         // handle special case when zipfile is stated
889         if (havezip) {
890                 zip_filename = par_filename;
891                 par_filename = NULL;
892         }
893
894         lprintf(DEBUG_LVL2, " Verbosity: %i\n", verbosity);
895         lprintf(DEBUG_LVL2, " Program: %s\n", argv[0]);
896
897         if (par_filename) {
898                 lprintf(DEBUG, "Parameter file: %s\n", par_filename);
899         }
900         if (zip_filename) {
901                 lprintf(DEBUG, "Zip file: %s\n", zip_filename);
902         }
903         if (img_filename) {
904                 lprintf(DEBUG, "Image file: %s\n", img_filename);
905         }
906
907
908 // open files from command line
909         // parameter/zip file
910         if (par_filename) {
911                 f_par = fopen(par_filename, "rt");
912                 if (!f_par) {
913                         exitcode = errno;
914                         printf("Input file %s: %s\n", par_filename, strerror(exitcode));
915                 }
916         }
917
918         // image file
919         if (img_filename) {
920                 f_img = fopen(img_filename, "wb");
921                 if (!f_img) {
922                         exitcode = errno;
923                         printf("Output file %s: %s\n", img_filename, strerror(exitcode));
924                 }
925         }
926
927         if (exitcode) {
928                 return exitcode;
929         }
930
931
932 // parameter file processing
933         if ((!exitcode) && (f_par)) {
934                 lprintf(DEBUG, "parsing parameter file...\n");
935
936                 exitcode = parse_par_file(f_par);
937
938                 lprintf(DEBUG, "...done parsing file\n");
939         }
940         if (f_par) {
941                 fclose(f_par);
942         }
943
944
945 // check all input data
946         if ((!exitcode) && (par_filename)) {
947                 lprintf(DEBUG, "checking mtd data...\n");
948
949                 for (i = 1; i <= 4; i++) {
950                         noupdate = 0;
951                         sizecheck = 0;
952                         magiccheck = 0;
953
954                         switch (i) {
955                                 case 1:
956                                         mtd = &mtd_image;
957                                         sizecheck = ROOTFS_END_OFFSET;
958                                         magiccheck = 1;
959                                         break;
960                                 case 2:
961                                         mtd = &mtd_kernel;
962                                         sizecheck = mtd_kernel.size - 16;
963                                         magiccheck = 1;
964                                         break;
965                                 case 3:
966                                         mtd = &mtd_rootfs;
967                                         mtd->offset = mtd_kernel.size;
968                                         mtd->size = ROOTFS_END_OFFSET - mtd_kernel.size;
969                                         sizecheck = PRODUCT_ID_OFFSET - mtd_kernel.size;
970                                         magiccheck = 1;
971                                         break;
972                                 case 4:
973                                         mtd = &mtd_uboot;
974                                         mtd->offset = BOOT_ADDR_BASE_OFF;
975                                         noupdate = 1;
976                                         sizecheck = SN_OFF - BOOT_ADDR_BASE_OFF;
977                                         break;
978                                 default:
979                                         mtd = NULL;
980                                         exitcode = 1;
981                                         printf("unknown mtd check %i\n", i);
982                                         break;
983                         }
984                         if (!mtd) {
985                                 break;
986                         }
987
988                         lprintf(DEBUG_LVL2, " checking mtd %s\n", mtd->name);
989
990                         // general checks
991
992                         // no further checks if no file data present
993                         if (!mtd->filename) {
994                                 continue;
995                         }
996
997                         // not updated by stock firmware
998                         if (noupdate) {
999                                 printf("mtd %s is specified, but will not be updated as of Linksys firmware 2.0.19\n", mtd->name);
1000                         }
1001
1002                         // general magic number check
1003                         magicerror = 0;
1004                         if (magiccheck) {
1005                                 switch (i) {
1006                                         case 1: // image
1007                                         case 2: // kernel
1008                                                 if (!( 
1009                                                        ((mtd->magic[0] == 0x27) && (mtd->magic[1] == 0x05))     // uImage
1010                                                 )) {
1011                                                         magicerror = 1;
1012                                                 }
1013                                                 break;
1014                                         case 3: // rootfs
1015                                                 if (!( 
1016                                                        ((mtd->magic[0] == 0x68) && (mtd->magic[1] == 0x73))     // squashfs
1017                                                     || ((mtd->magic[0] == 0x85) && (mtd->magic[1] == 0x19))     // jffs
1018                                                 )) {
1019                                                         magicerror = 1;
1020                                                 }
1021                                                 break;
1022                                         default:
1023                                                 magicerror = 1;
1024                                                 break;
1025                                 }
1026                                 if (magicerror) {
1027                                         printf("mtd %s input file %s has unknown magic number (0x%02X%02X)", mtd->name, mtd->filename, mtd->magic[0], mtd->magic[1]);
1028                                         if (ignoremagic) {
1029                                                 printf("...ignoring");
1030                                         } else {
1031                                                 exitcode = 1;
1032                                         }
1033                                         printf("\n");
1034                                 }
1035                         }
1036
1037                         // mtd specific size check
1038                         if (mtd == &mtd_image) {
1039                                 if (mtd->filesize < 0x00200000) {
1040                                         exitcode = 1;
1041                                         printf("mtd %s input file %s too unrealistic small (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1042                                 }
1043                         }
1044
1045                         if (mtd == &mtd_kernel) {
1046                                 if (mtd->filesize < 0x00080000) {
1047                                         exitcode = 1;
1048                                         printf("mtd %s input file %s too unrealistic small (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1049                                 }
1050                         }
1051
1052                         // general size check
1053                         if (sizecheck) {
1054                                 if (sizecheck <= 0) {
1055                                         exitcode = 1;
1056                                         printf("mtd %s bad file size check (%i) due to input data\n", mtd->name, sizecheck);
1057                                 } else {
1058                                         if (mtd->filesize > sizecheck) {
1059                                                 exitcode = 1;
1060                                                 printf("mtd %s input file %s too big (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1061                                         }
1062                                 }
1063                         }
1064                 }
1065
1066                 // Check for mandatory parts
1067                 if ((!mtd_image.filename) && (!mtd_kernel.filename || !mtd_rootfs.filename)) {
1068                         exitcode = 1;
1069                         if (mtd_kernel.filename && !mtd_rootfs.filename) {
1070                                 printf("Kernel without rootfs, either incorrectly specified or not at all in parameter file\n");
1071                         } else if (!mtd_kernel.filename && mtd_rootfs.filename) {
1072                                 printf("Rootfs without kernel, either incorrectly specified or not at all in parameter file\n");
1073                         } else {
1074                                 printf("Neither an image nor kernel with rootfs was/were correctly specified or at all in parameter file\n");
1075                         }
1076                 }
1077
1078                 // Check for duplicate parts
1079                 if ((mtd_image.filename) && (mtd_kernel.filename || mtd_rootfs.filename)) {
1080                         exitcode = 1;
1081                         printf("Image and kernel/rootfs specified in parameter file\n");
1082                 }
1083
1084                 lprintf(DEBUG, "...done checking mtd data\n");
1085         }
1086
1087
1088 // bin creation in memory
1089         if ((!exitcode) && (par_filename)) {
1090                 bin_filename = "wrt350n.bin";
1091
1092                 lprintf(DEBUG, "creating bin file %s...\n", bin_filename);
1093
1094                 exitcode = create_bin_file(bin_filename);
1095
1096                 lprintf(DEBUG, "...done creating bin file\n");
1097         }
1098
1099 // zip file creation
1100         if ((!exitcode) && (!onlybin) && (!zip_filename)) {
1101                 zip_filename = "wrt350n.zip";
1102
1103                 lprintf(DEBUG, "creating zip file %s...\n", zip_filename);
1104
1105                 exitcode = create_zip_file(zip_filename, bin_filename);
1106
1107                 lprintf(DEBUG, "...done creating zip file\n");
1108         }
1109
1110
1111 // img file creation
1112         if ((!exitcode) && (f_img)) {
1113                 lprintf(DEBUG, "creating img file...\n");
1114
1115                 exitcode = create_img_file(f_img, img_filename, zip_filename);
1116
1117                 lprintf(DEBUG, "...done creating img file\n");
1118         }
1119
1120 // clean up
1121         if (f_img) {
1122                 fclose(f_img);
1123         }
1124
1125 // end program
1126         return exitcode;
1127 }