removed some unnecessary tools; added swap support to kernel
[openwrt_grafs.git] / tools / wrt350nv2-builder / src / wrt350nv2-builder.c
blob2ce57234c5f7acfe6a99941adf9b4110bb30db45
1 /*
3 WRT350Nv2-Builder 2.3 (previously called buildimg)
4 Copyright (C) 2008-2009 Dirk Teurlings <info@upexia.nl>
5 Copyright (C) 2009-2010 Matthias Buecher (http://www.maddes.net/)
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.
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.
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
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.
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.
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
36 args:
37 1 wrt350nv2.par parameter file describing the image layout
38 2 wrt350nv2.img output file for linksys style image
40 A u-boot image inside the bin file is not necessary.
41 The version is not important.
42 The name of the bin file is not important, but still "wrt350n.bin" is used to
43 keep as close as possible to the stock firmware.
45 Linksys assumes that no mtd will be used to its maximum, so the last 16 bytes
46 of the mtd are abused to define the length of the next mtd content (4 bytes for
47 size + 12 pad bytes).
49 At the end of "rootfs" additional 16 bytes are abused for some data and a
50 highly important eRcOmM identifier, so the last 32 bytes of "rootfs" are abused.
52 At the end of "u-boot" 128 bytes are abused for some data, a checksum and a
53 highly important sErCoMm identifier.
56 This program uses a special GNU scanf modifier to allocate
57 sufficient memory for a strings with unknown length.
58 See http://www.kernel.org/doc/man-pages/online/pages/man3/scanf.3.html#NOTES
61 To extract everything from a Linksys style firmware image see
62 https://forum.openwrt.org/viewtopic.php?pid=92928#p92928
64 Changelog:
65 v2.3 - allow jffs by adding its magic number (0x8519)
66 added parameter option -i to ignore unknown magic numbers
67 v2.2 - fixed checksum byte calculation for other versions than 0x2019
68 fixed rare problem with padsize
69 updated info to stock firmware 2.00.20
70 fixed typos
71 v2.1 - used "wrt350n.bin" for the created image (closer to stock)
72 added option to create the image in two separate steps (-b / -z)
73 v2.0 - complete re-write
77 // includes
78 #define _GNU_SOURCE // for GNU's basename()
79 #include <assert.h>
80 #include <errno.h> // errno
81 #include <stdarg.h>
82 #include <stdio.h> // fopen(), fread(), fclose(), etc.
83 #include <stdlib.h> // system(), etc.
84 #include <string.h> // basename(), strerror(), strdup(), etc.
85 #include <unistd.h> // optopt(), access(), etc.
86 #include <libgen.h>
87 #include <sys/wait.h> // WEXITSTATUS, etc.
89 // custom includes
90 #include "md5.h" // MD5 routines
91 #include "upgrade.h" // Linksys definitions from firmware 2.0.19 (unchanged up to 2.0.20)
94 // version info
95 #define VERSION "2.3"
96 char program_info[] = "WRT350Nv2-Builder v%s by Dirk Teurlings <info@upexia.nl> and Matthias Buecher (http://www.maddes.net/)\n";
98 // verbosity
99 #define DEBUG 1
100 #define DEBUG_LVL2 2
101 int verbosity = 0;
103 // mtd info
104 typedef struct {
105 char *name;
106 int offset;
107 int size;
108 char *filename;
109 long int filesize;
110 unsigned char magic[2];
111 } mtd_info;
113 mtd_info mtd_kernel = { "kernel", 0, 0, NULL, 0L, { 0, 0 } };
114 mtd_info mtd_rootfs = { "rootfs", 0, 0, NULL, 0L, { 0, 0 } };
115 mtd_info mtd_uboot = { "u-boot", 0, 0, NULL, 0L, { 0, 0 } };
117 #define ROOTFS_END_OFFSET 0x00760000
118 #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
119 // 2.0.17: filled up to 0x00640000
120 // 2.0.19: filled up to 0x00670000
121 // 2.0.20: filled up to 0x00670000
123 // rootfs statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x0075FFE0 -n 16 "wrt350n.bin" ; echo -en "\n"
124 unsigned char product_id[] = { 0x00, 0x03 }; // seems to be a fixed value
125 unsigned char protocol_id[] = { 0x00, 0x00 }; // seems to be a fixed value
126 unsigned char fw_version[] = { 0x20, 0x20 };
127 unsigned char rootfs_unknown[] = { 0x90, 0xF7 }; // seems to be a fixed value
128 unsigned char sign[] = { 0x65, 0x52, 0x63, 0x4F, 0x6D, 0x4D, 0x00, 0x00 }; // eRcOmM
130 // u-boot statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x007FFF80 -n 128 "wrt350n.bin" ; echo -en "\n"
131 //unsigned char sn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (12) seems to be an unused value
132 //unsigned char pin[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (8) seems to be an unused value
133 //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
134 // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
135 //unsigned char checksum[] = { 0xE9 }; // (1) is calculated, does it belong to node?
136 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
137 0x10, 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, 0x03, 0x00, // protocol id?
139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // protocol id?
140 0x12, 0x34, // firmware version, same as in rootfs
141 0x00, 0x00, 0x00, 0x04,
142 0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D }; // sErCoMm
144 // 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)
145 unsigned char img_hdr[] = { 0x00, 0x01, 0x00, 0x00, 0x59, 0x42, 0x50, 0x00, 0x01, 0x10, 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, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
148 0x00, 0x00,
149 0x12, 0x34, // firmware version, same as in rootfs
150 0x00, 0x00, 0x00, 0x04, 0x61, 0x44, 0x6D, 0x42, 0x6C, 0x4B, 0x3D, 0x00,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 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 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // md5 checksum
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
180 unsigned char img_eof[] = { 0xFF };
183 void lprintf(int outputlevel, char *fmt, ...) {
184 va_list argp;
185 if (outputlevel <= verbosity) {
186 va_start(argp, fmt);
187 vprintf(fmt, argp);
188 va_end(argp);
193 int parse_par_file(FILE *f_par) {
194 int exitcode = 0;
196 char *buffer;
197 size_t buffer_size;
198 char *line;
200 int lineno;
201 int count;
203 char string1[256];
204 char string2[256];
205 int value;
207 mtd_info *mtd;
208 FILE *f_in;
209 int f_exitcode = 0;
211 // read all lines
212 buffer_size = 1000;
213 buffer = NULL;
214 lineno = 0;
215 while (!feof(f_par)) {
216 // read next line into memory
217 do {
218 // allocate memory for input line
219 if (!buffer) {
220 buffer = malloc(buffer_size);
222 if (!buffer) {
223 exitcode = 1;
224 printf("parse_par_file: can not allocate %i bytes\n", (int) buffer_size);
225 break;
228 line = fgets(buffer, buffer_size, f_par);
229 if (!line) {
230 exitcode = ferror(f_par);
231 if (exitcode) {
232 printf("parse_par_file: %s\n", strerror(exitcode));
234 break;
237 // if buffer was not completely filled, then assume that line is complete
238 count = strlen(buffer) + 1;
239 if (count-- < buffer_size) {
240 break;
243 // otherwise....
245 // reset file position to line start
246 value = fseek(f_par, -count, SEEK_CUR);
247 if (value == -1) {
248 exitcode = errno;
249 printf("parse_par_file: %s\n", strerror(exitcode));
250 break;
253 // double buffer size
254 free(buffer);
255 buffer = NULL;
256 buffer_size *= 2;
257 lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
258 } while (1);
259 if ((!line) || (exitcode)) {
260 break;
263 lineno++; // increase line number
265 lprintf(DEBUG_LVL2, " line %i (%i) %s", lineno, count, line);
267 value = 0;
268 mtd = NULL;
270 // split line if starting with a colon
271 switch (line[0]) {
272 case ':':
273 count = sscanf(line, ":%255s %i %255s", string1, &value, string2);
274 if (count != 3) {
275 printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno);
276 } else {
277 // populate mtd_info if supported mtd names
278 if (!strcmp(string1, mtd_kernel.name)) {
279 mtd = &mtd_kernel;
280 } else if (!strcmp(string1, mtd_rootfs.name)) {
281 mtd = &mtd_rootfs;
282 } else if (!strcmp(string1, mtd_uboot.name)) {
283 mtd = &mtd_uboot;
286 if (!mtd) {
287 printf("unknown mtd %s in line %i\n", string1, lineno);
288 } else if (mtd->filename) {
289 f_exitcode = 1;
290 printf("mtd %s in line %i multiple definitions\n", string1, lineno);
291 } else {
292 mtd->size = value;
293 mtd->filename = strdup(string2);
295 // Get file size
296 f_in = fopen(mtd->filename, "rb");
297 if (!f_in) {
298 f_exitcode = errno;
299 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
300 } else {
301 value = fread(&mtd->magic, 1, 2, f_in);
302 if (value < 2) {
303 if (ferror(f_in)) {
304 f_exitcode = ferror(f_in);
305 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
306 } else {
307 f_exitcode = 1;
308 printf("input file %s: smaller than two bytes, no magic code\n", mtd->filename);
312 value = fseek(f_in, 0, SEEK_END);
313 if (value == -1) {
314 f_exitcode = errno;
315 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
316 } else {
317 mtd->filesize = ftell(f_in);
318 if (mtd->filesize == -1) {
319 f_exitcode = errno;
320 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
324 fclose(f_in);
327 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);
330 break;
331 case '#': // integer values
332 count = sscanf(line, "#%255s %i", string1, &value);
333 if (count != 2) {
334 printf("line %i does not meet defined format (#<variable name> <integer>\n", lineno);
335 } else {
336 if (!strcmp(string1, "version")) {
337 // changing version
338 fw_version[0] = 0x000000FF & ( value >> 8 );
339 fw_version[1] = 0x000000FF & value;
340 } else {
341 printf("unknown integer variable %s in line %i\n", string1, lineno);
344 lprintf(DEBUG, "integer variable %s in line %i: 0x%08X\n", string1, lineno, value);
346 break;
347 case '$': // strings
348 count = sscanf(line, "$%255s %255s", string1, string2);
349 if (count != 2) {
350 printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno);
351 } else {
353 if (!strcmp(string1, "something")) {
354 something = strdup(string2);
355 } else {
357 printf("unknown string variable %s in line %i\n", string1, lineno);
358 // }
359 lprintf(DEBUG, "string variable %s in line %i: %s\n", string1, lineno, string2);
361 break;
362 default:
363 break;
366 free(buffer);
368 if (!exitcode) {
369 exitcode = f_exitcode;
372 return exitcode;
376 int create_bin_file(char *bin_filename) {
377 int exitcode = 0;
379 unsigned char *buffer;
381 int i;
382 mtd_info *mtd;
383 int addsize;
384 int padsize;
386 char *rand_filename = "/dev/urandom";
387 FILE *f_in;
388 int size;
390 unsigned long int csum;
391 unsigned char checksum;
393 FILE *f_out;
395 // allocate memory for bin file
396 buffer = malloc(KERNEL_CODE_OFFSET + FLASH_SIZE);
397 if (!buffer) {
398 exitcode = 1;
399 printf("create_bin_file: can not allocate %i bytes\n", FLASH_SIZE);
400 } else {
401 // initialize with zero
402 memset(buffer, 0, KERNEL_CODE_OFFSET + FLASH_SIZE);
405 // add files
406 if (!exitcode) {
407 for (i = 1; i <= 3; i++) {
408 addsize = 0;
409 padsize = 0;
411 switch (i) {
412 case 1:
413 mtd = &mtd_kernel;
414 break;
415 case 2:
416 mtd = &mtd_rootfs;
417 addsize = mtd->filesize;
418 padsize = ROOTFS_MIN_OFFSET - mtd_kernel.size - mtd->filesize;
419 break;
420 case 3:
421 mtd = &mtd_uboot;
422 addsize = mtd->filesize;
423 break;
424 default:
425 mtd = NULL;
426 exitcode = 1;
427 printf("create_bin_file: unknown mtd %i\n", i);
428 break;
430 if (!mtd) {
431 break;
433 if (!mtd->filename) {
434 continue;
437 lprintf(DEBUG, "adding mtd %s file %s\n", mtd->name, mtd->filename);
439 // adding file size
440 if (addsize) {
441 buffer[KERNEL_CODE_OFFSET + mtd->offset - 16] = 0x000000FFL & ( addsize >> 24 );
442 buffer[KERNEL_CODE_OFFSET + mtd->offset - 15] = 0x000000FFL & ( addsize >> 16 );
443 buffer[KERNEL_CODE_OFFSET + mtd->offset - 14] = 0x000000FFL & ( addsize >> 8 );
444 buffer[KERNEL_CODE_OFFSET + mtd->offset - 13] = 0x000000FFL & addsize;
447 // adding file content
448 f_in = fopen(mtd->filename, "rb");
449 if (!f_in) {
450 exitcode = errno;
451 printf("input file %s: %s\n", mtd->filename, strerror(exitcode));
452 } else {
453 size = fread(&buffer[KERNEL_CODE_OFFSET + mtd->offset], mtd->filesize, 1, f_in);
454 if (size < 1) {
455 if (ferror(f_in)) {
456 exitcode = ferror(f_in);
457 printf("input file %s: %s\n", mtd->filename, strerror(exitcode));
458 } else {
459 exitcode = 1;
460 printf("input file %s: smaller than before *doh*\n", mtd->filename);
463 fclose(f_in);
466 // padding
467 if (padsize > 0) {
468 addsize = padsize & 0x0000FFFF; // start on next 64KB border
469 padsize -= addsize;
471 if (padsize > 0) {
472 printf("mtd %s input file %s is too small (0x%08lX), adding 0x%08X random bytes\n", mtd->name, mtd->filename, mtd->filesize, padsize);
474 addsize += KERNEL_CODE_OFFSET + mtd->offset + mtd->filesize; // get offset
475 lprintf(DEBUG, " padding offset 0x%08X length 0x%08X\n", addsize, padsize);
477 f_in = fopen(rand_filename, "rb");
478 if (!f_in) {
479 exitcode = errno;
480 printf("input file %s: %s\n", rand_filename, strerror(exitcode));
481 } else {
482 size = fread(&buffer[addsize], padsize, 1, f_in);
483 if (size < 1) {
484 if (ferror(f_in)) {
485 exitcode = ferror(f_in);
486 printf("input file %s: %s\n", rand_filename, strerror(exitcode));
487 } else {
488 exitcode = 1;
489 printf("input file %s: smaller than before *doh*\n", rand_filename);
493 fclose(f_in);
498 // add special contents
499 if (!exitcode) {
500 lprintf(DEBUG, "adding rootfs special data\n");
501 memcpy(&buffer[KERNEL_CODE_OFFSET + PRODUCT_ID_OFFSET], product_id, 2);
502 memcpy(&buffer[KERNEL_CODE_OFFSET + PROTOCOL_ID_OFFSET], protocol_id, 2);
503 memcpy(&buffer[KERNEL_CODE_OFFSET + FW_VERSION_OFFSET], fw_version, 2);
504 memcpy(&buffer[KERNEL_CODE_OFFSET + FW_VERSION_OFFSET + 2], rootfs_unknown, 2);
505 memcpy(&buffer[KERNEL_CODE_OFFSET + SIGN_OFFSET], sign, 8); // eRcOmM
507 lprintf(DEBUG, "adding u-boot special data\n");
508 // memcpy(&buffer[KERNEL_CODE_OFFSET + SN_OFF], sn, 12); // ToDo: currently zero, find out what's this for?
509 // memcpy(&buffer[KERNEL_CODE_OFFSET + PIN_OFF], pin, 8); // ToDo: currently zero, find out what's this for?
510 // memcpy(&buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF], node, 25); // ToDo: currently zero, find out what's this for?
511 memcpy(&buffer[KERNEL_CODE_OFFSET + BOOT_ADDR_BASE_OFF + PID_OFFSET], pid, 70); // sErCoMm
512 memcpy(&buffer[KERNEL_CODE_OFFSET + BOOT_ADDR_BASE_OFF + PID_OFFSET + 57], fw_version, 2);
514 lprintf(DEBUG, "adding checksum byte\n");
515 csum = 0;
516 for (i = 0; i < KERNEL_CODE_OFFSET + FLASH_SIZE; i++) {
517 csum += buffer[i];
519 lprintf(DEBUG_LVL2, " checksum 0x%016lX (%li)\n", csum, csum);
521 buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF + 25] = ~csum + 1;
522 lprintf(DEBUG, " byte 0x%02X\n", buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF + 25]);
525 // write bin file
526 if (!exitcode) {
527 lprintf(DEBUG, "writing file %s\n", bin_filename);
528 f_out = fopen(bin_filename, "wb");
529 if (!f_out) {
530 exitcode = errno;
531 printf("output file %s: %s\n", bin_filename, strerror(exitcode));
532 } else {
533 size = fwrite(buffer, KERNEL_CODE_OFFSET + FLASH_SIZE, 1, f_out);
534 if (size < 1) {
535 if (ferror(f_out)) {
536 exitcode = ferror(f_out);
537 printf("output file %s: %s\n", bin_filename, strerror(exitcode));
538 } else {
539 exitcode = 1;
540 printf("output file %s: unspecified write error\n", bin_filename);
543 fclose(f_out);
547 return exitcode;
551 int create_zip_file(char *zip_filename, char *bin_filename) {
552 int exitcode = 0;
554 char *buffer;
555 size_t buffer_size;
556 int count;
558 buffer_size = 1000;
559 buffer = NULL;
560 do {
561 // allocate memory for command line
562 if (!buffer) {
563 buffer = malloc(buffer_size);
565 if (!buffer) {
566 exitcode = 1;
567 printf("create_zip_file: can not allocate %i bytes\n", (int) buffer_size);
568 break;
571 // if buffer was not completely filled, then line fit in completely
572 count = snprintf(buffer, buffer_size, "zip \"%s\" \"%s\"", zip_filename, bin_filename);
573 if ((count > -1) && (count < buffer_size)) {
574 break;
577 // otherwise try again with more space
578 if (count > -1) { // glibc 2.1
579 buffer_size = count + 1; // precisely what is needed
580 } else { // glibc 2.0
581 buffer_size *= 2; // twice the old size
583 free(buffer);
584 buffer = NULL;
585 lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
586 } while (1);
588 if (!exitcode) {
589 // zipping binfile
590 lprintf(DEBUG, "%s\n", buffer);
591 count = system(buffer);
592 if ((count < 0) || (WEXITSTATUS(count))) {
593 exitcode = 1;
594 printf("create_zip_file: can not execute %s bytes\n", buffer);
598 return exitcode;
602 int create_img_file(FILE *f_out, char *out_filename, char *zip_filename) {
603 int exitcode = 0;
605 md5_state_t state;
606 md5_byte_t digest[16];
608 int i;
609 int size;
611 FILE *f_in;
612 unsigned char buffer[1];
614 // copy firmware version
615 memcpy(&img_hdr[50], fw_version, 2);
617 // clear md5 checksum
618 memset(&img_hdr[480], 0, 16);
620 // prepare md5 checksum calculation
621 md5_init(&state);
623 // add img header
624 lprintf(DEBUG_LVL2, " adding img header\n");
625 for (i = 0; i < 512; i++) {
626 size = fputc(img_hdr[i], f_out);
627 if (size == EOF) {
628 exitcode = ferror(f_out);
629 printf("output file %s: %s\n", out_filename, strerror(exitcode));
630 break;
632 md5_append(&state, (const md5_byte_t *)&img_hdr[i], 1);
635 // adding zip file
636 if (!exitcode) {
637 lprintf(DEBUG_LVL2, " adding zip file\n");
638 f_in = fopen(zip_filename, "rb");
639 if (!f_in) {
640 exitcode = errno;
641 printf("input file %s: %s\n", zip_filename, strerror(exitcode));
642 } else {
643 while ((size = fgetc(f_in)) != EOF) {
644 buffer[0] = size;
646 size = fputc(buffer[0], f_out);
647 if (size == EOF) {
648 exitcode = ferror(f_out);
649 printf("output file %s: %s\n", out_filename, strerror(exitcode));
650 break;
652 md5_append(&state, (const md5_byte_t *)buffer, 1);
654 if (ferror(f_in)) {
655 exitcode = ferror(f_in);
656 printf("input file %s: %s\n", zip_filename, strerror(exitcode));
662 // add end byte
663 if (!exitcode) {
664 lprintf(DEBUG_LVL2, " adding img eof byte\n");
665 size = fputc(img_eof[0], f_out);
666 if (size == EOF) {
667 exitcode = ferror(f_out);
668 printf("output file %s: %s\n", out_filename, strerror(exitcode));
670 md5_append(&state, (const md5_byte_t *)img_eof, 1);
673 // append salt to md5 checksum
674 md5_append(&state, (const md5_byte_t *)"A^gU*<>?RFY@#DR&Z", 17);
676 // finish md5 checksum calculation
677 md5_finish(&state, digest);
679 // write md5 checksum into img header
680 if (!exitcode) {
681 lprintf(DEBUG_LVL2, " writing md5 checksum into img header of file\n");
683 size = fseek(f_out, 480, SEEK_SET);
684 if (size == -1) {
685 exitcode = errno;
686 printf("output file %s: %s\n", out_filename, strerror(exitcode));
687 } else {
688 size = fwrite(digest, 16, 1, f_out);
689 if (size < 1) {
690 if (ferror(f_out)) {
691 exitcode = ferror(f_out);
692 printf("output file %s: %s\n", out_filename, strerror(exitcode));
693 } else {
694 exitcode = 1;
695 printf("output file %s: unspecified write error\n", out_filename);
700 fclose(f_in);
703 return exitcode;
707 int main(int argc, char *argv[]) {
708 int exitcode = 0;
710 int help;
711 int onlybin;
712 int havezip;
713 int ignoremagic;
714 char option;
715 char *par_filename = NULL;
716 char *img_filename = NULL;
717 char *base_filename = NULL;
718 char *bin_filename = NULL;
719 char *zip_filename = NULL;
721 FILE *f_par = NULL;
722 FILE *f_img = NULL;
724 int i;
725 mtd_info *mtd;
726 int mandatory;
727 int noupdate;
728 int sizecheck;
729 int magiccheck;
730 int magicerror;
733 // display program header
734 printf(program_info, VERSION);
737 // command line processing
738 // options
739 help = 0;
740 onlybin = 0;
741 havezip = 0;
742 ignoremagic = 0;
743 while ((option = getopt(argc, argv, "hbzif:v")) != -1) {
744 switch(option) {
745 case 'h':
746 help = 1;
747 break;
748 case 'b':
749 onlybin = 1;
750 break;
751 case 'z':
752 havezip = 1;
753 break;
754 case 'i':
755 ignoremagic = 1;
756 break;
757 case 'f':
758 sizecheck = sscanf(optarg, "%i", &i);
759 if (sizecheck != 1) {
760 printf("Firmware version of -f option not a valid integer\n");
761 exitcode = 1;
762 } else {
763 fw_version[0] = 0x000000FF & ( i >> 8 );
764 fw_version[1] = 0x000000FF & i;
766 break;
767 case 'v':
768 verbosity++;
769 break;
770 case ':': // option with missing operand
771 printf("Option -%c requires an operand\n", optopt);
772 exitcode = 1;
773 break;
774 case '?':
775 printf("Unrecognized option: -%c\n", optopt);
776 exitcode = 1;
777 break;
781 // files
782 for ( ; optind < argc; optind++) {
783 if (!par_filename) {
784 par_filename = argv[optind];
786 if (access(par_filename, R_OK)) {
787 if (havezip) {
788 printf("No read access to zip file %s\n", par_filename);
789 } else {
790 printf("No read access to parameter or zip file %s\n", par_filename);
792 exitcode = 1;
795 continue;
798 if ((!onlybin) && (!img_filename)) {
799 img_filename = argv[optind];
801 if (!access(img_filename, F_OK)) { // if file already exists then check write access
802 if (access(img_filename, W_OK)) {
803 printf("No write access to image file %s\n", img_filename);
804 exitcode = 1;
808 continue;
811 printf("Too many files stated\n");
812 exitcode = 1;
813 break;
816 // file name checks
817 if (!par_filename) {
818 if (havezip) {
819 printf("Zip file not stated\n");
820 } else {
821 printf("Parameter file not stated\n");
823 exitcode = 1;
824 } else {
825 base_filename = basename(par_filename);
826 if (!base_filename) {
827 if (havezip) {
828 printf("Zip file is a directory\n");
829 } else {
830 printf("Parameter file is a directory\n");
832 exitcode = 1;
836 if (!onlybin) {
837 if (!img_filename) {
838 printf("Image file not stated\n");
839 exitcode = 1;
840 } else {
841 base_filename = basename(img_filename);
842 if (!base_filename) {
843 printf("Image file is a directory\n");
844 exitcode = 1;
849 // check for mutually exclusive options
850 if ((onlybin) && (havezip)) {
851 printf("Option -b and -z are mutually exclusive\n");
852 exitcode = 1;
855 // react on option problems or help request, then exit
856 if ((exitcode) || (help)) {
857 if (help) {
858 printf("This program creates Linksys style images for the WRT350Nv2 router.\n");
860 printf(" Usage:\n\
861 %s [-h] [-b] [-z] [-i] [-f <version>] [-v] <parameter or zip file> [<image file>]\n\n\
862 Options:\n\
863 -h - Show this help\n\
864 -b - Create only bin file, no img or zip file is created\n\
865 -z - Have zip file, the img file will be directly created from it\n\
866 -i - Ignore unknown magic numbers\n\
867 -f <version> - Wanted firmware version to use with -z\n\
868 Default firmware version is 0x2020 = 2.00.20.\n\
869 Note: version from parameter file will supersede this\n\
870 -v - Increase debug verbosity level\n\n\
871 Example:\n\
872 %s wrt350nv2.par wrt350nv2.img\n\n", argv[0], argv[0]);
873 return exitcode;
876 // handle special case when zipfile is stated
877 if (havezip) {
878 zip_filename = par_filename;
879 par_filename = NULL;
882 lprintf(DEBUG_LVL2, " Verbosity: %i\n", verbosity);
883 lprintf(DEBUG_LVL2, " Program: %s\n", argv[0]);
885 if (par_filename) {
886 lprintf(DEBUG, "Parameter file: %s\n", par_filename);
888 if (zip_filename) {
889 lprintf(DEBUG, "Zip file: %s\n", zip_filename);
891 if (img_filename) {
892 lprintf(DEBUG, "Image file: %s\n", img_filename);
896 // open files from command line
897 // parameter/zip file
898 if (par_filename) {
899 f_par = fopen(par_filename, "rt");
900 if (!f_par) {
901 exitcode = errno;
902 printf("Input file %s: %s\n", par_filename, strerror(exitcode));
906 // image file
907 if (img_filename) {
908 f_img = fopen(img_filename, "wb");
909 if (!f_img) {
910 exitcode = errno;
911 printf("Output file %s: %s\n", img_filename, strerror(exitcode));
915 if (exitcode) {
916 return exitcode;
920 // parameter file processing
921 if ((!exitcode) && (f_par)) {
922 lprintf(DEBUG, "parsing parameter file...\n");
924 exitcode = parse_par_file(f_par);
926 lprintf(DEBUG, "...done parsing file\n");
928 if (f_par) {
929 fclose(f_par);
933 // check all input data
934 if ((!exitcode) && (par_filename)) {
935 lprintf(DEBUG, "checking mtd data...\n");
937 for (i = 1; i <= 3; i++) {
938 mandatory = 0;
939 noupdate = 0;
940 sizecheck = 0;
941 magiccheck = 0;
943 switch (i) {
944 case 1:
945 mtd = &mtd_kernel;
946 mandatory = 1;
947 sizecheck = mtd_kernel.size - 16;
948 magiccheck = 1;
949 break;
950 case 2:
951 mtd = &mtd_rootfs;
952 mtd->offset = mtd_kernel.size;
953 mtd->size = ROOTFS_END_OFFSET - mtd_kernel.size;
954 mandatory = 1;
955 sizecheck = PRODUCT_ID_OFFSET - mtd_kernel.size;
956 magiccheck = 1;
957 break;
958 case 3:
959 mtd = &mtd_uboot;
960 mtd->offset = BOOT_ADDR_BASE_OFF;
961 noupdate = 1;
962 sizecheck = SN_OFF - BOOT_ADDR_BASE_OFF;
963 break;
964 default:
965 mtd = NULL;
966 exitcode = 1;
967 printf("unknown mtd check %i\n", i);
968 break;
970 if (!mtd) {
971 break;
974 lprintf(DEBUG_LVL2, " checking mtd %s\n", mtd->name);
976 // general checks
977 if ((mandatory) && (!mtd->filename)) {
978 exitcode = 1;
979 printf("mtd %s not specified correctly or at all in parameter file\n", mtd->name);
982 // no further checks if no file data present
983 if (!mtd->filename) {
984 continue;
987 // not updated by stock firmware
988 if (noupdate) {
989 printf("mtd %s is specified, but will not be updated as of Linksys firmware 2.0.19\n", mtd->name);
992 // general magic number check
993 magicerror = 0;
994 if (magiccheck) {
995 switch (i) {
996 case 1: // kernel
997 if (!(
998 ((mtd->magic[0] == 0x27) && (mtd->magic[1] == 0x05)) // uImage
999 )) {
1000 magicerror = 1;
1002 break;
1003 case 2: // rootfs
1004 if (!(
1005 ((mtd->magic[0] == 0x68) && (mtd->magic[1] == 0x73)) // squashfs
1006 || ((mtd->magic[0] == 0x85) && (mtd->magic[1] == 0x19)) // jffs
1007 )) {
1008 magicerror = 1;
1010 break;
1011 default:
1012 magicerror = 1;
1013 break;
1015 if (magicerror) {
1016 printf("mtd %s input file %s has unknown magic number (0x%02X%02X)", mtd->name, mtd->filename, mtd->magic[0], mtd->magic[1]);
1017 if (ignoremagic) {
1018 printf("...ignoring");
1019 } else {
1020 exitcode = 1;
1022 printf("\n");
1026 // mtd specific size check
1027 if (mtd == &mtd_kernel) {
1028 if (mtd->filesize < 0x00050000) {
1029 exitcode = 1;
1030 printf("mtd %s input file %s too unrealistic small (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1034 // general size check
1035 if (sizecheck) {
1036 if (sizecheck <= 0) {
1037 exitcode = 1;
1038 printf("mtd %s bad file size check (%i) due to input data\n", mtd->name, sizecheck);
1039 } else {
1040 if (mtd->filesize > sizecheck) {
1041 exitcode = 1;
1042 printf("mtd %s input file %s too big (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1047 lprintf(DEBUG, "...done checking mtd data\n");
1051 // bin creation in memory
1052 if ((!exitcode) && (par_filename)) {
1053 bin_filename = "wrt350n.bin";
1055 lprintf(DEBUG, "creating bin file %s...\n", bin_filename);
1057 exitcode = create_bin_file(bin_filename);
1059 lprintf(DEBUG, "...done creating bin file\n");
1062 // zip file creation
1063 if ((!exitcode) && (!onlybin) && (!zip_filename)) {
1064 zip_filename = "wrt350n.zip";
1066 lprintf(DEBUG, "creating zip file %s...\n", zip_filename);
1068 exitcode = create_zip_file(zip_filename, bin_filename);
1070 lprintf(DEBUG, "...done creating zip file\n");
1074 // img file creation
1075 if ((!exitcode) && (f_img)) {
1076 lprintf(DEBUG, "creating img file...\n");
1078 exitcode = create_img_file(f_img, img_filename, zip_filename);
1080 lprintf(DEBUG, "...done creating img file\n");
1083 // clean up
1084 if (f_img) {
1085 fclose(f_img);
1088 // end program
1089 return exitcode;