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