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