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