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