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