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