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