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