1/* 2 3 fpkg - Package a firmware 4 Copyright (C) 2007 Jonathan Zarate 5 6 This program is free software; you can redistribute it and/or 7 modify it under the terms of the GNU General Public License 8 as published by the Free Software Foundation; either version 2 9 of the License, or (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 20*/ 21 22#include <stdio.h> 23#include <stdlib.h> 24#include <string.h> 25#include <errno.h> 26#include <unistd.h> 27#include <getopt.h> 28#include <time.h> 29#include <stdint.h> 30#include <sys/stat.h> 31#include <arpa/inet.h> 32 33#define ROUNDUP(n, a) ((n + (a - 1)) & ~(a - 1)) 34 35#define TRX_MAGIC 0x30524448 36#define TRX_MAX_OFFSET 4 37#define TRX_MAX_LEN ((32 * 1024 * 1024) - ((256 + 128) * 1024)) // 32MB - (256K cfe + 128K cfg) 38 39typedef struct { 40 uint32_t magic; 41 uint32_t length; 42 uint32_t crc32; 43 uint32_t flag_version; 44 uint32_t offsets[TRX_MAX_OFFSET]; 45} trx_t; 46 47char names[TRX_MAX_OFFSET][80]; 48 49char trx_version = 1; 50int trx_max_offset = 3; 51 52 53typedef struct { 54 uint32_t crc32; 55 uint32_t magic; 56} moto_t; 57 58typedef struct { 59 uint8_t magic[4]; 60 uint8_t extra1[4]; 61 uint8_t date[3]; 62 uint8_t version[3]; 63 uint8_t u2nd[4]; 64 uint8_t hardware; 65 uint8_t serial; 66 uint16_t flags; 67 uint8_t extra2[10]; 68} cytan_t; 69 70uint32_t *crc_table = NULL; 71trx_t *trx = NULL; 72int trx_count = 0; 73int trx_final = 0; 74int trx_padding; 75time_t max_time = 0; 76 77inline size_t trx_header_size(void) 78{ 79 return sizeof(*trx) - sizeof(trx->offsets) + (trx_max_offset * sizeof(trx->offsets[0])); 80} 81 82int crc_init(void) 83{ 84 uint32_t c; 85 int i, j; 86 87 if (crc_table == NULL) { 88 if ((crc_table = malloc(sizeof(uint32_t) * 256)) == NULL) return 0; 89 for (i = 255; i >= 0; --i) { 90 c = i; 91 for (j = 8; j > 0; --j) { 92 if (c & 1) c = (c >> 1) ^ 0xEDB88320L; 93 else c >>= 1; 94 } 95 crc_table[i] = c; 96 } 97 } 98 return 1; 99} 100 101void crc_done(void) 102{ 103 free(crc_table); 104 crc_table = NULL; 105} 106 107uint32_t crc_calc(uint32_t crc, uint8_t *buf, int len) 108{ 109 while (len-- > 0) { 110 crc = crc_table[(crc ^ *buf) & 0xFF] ^ (crc >> 8); 111 ++buf; 112 } 113 return crc; 114} 115 116void help(void) 117{ 118 fprintf(stderr, 119 "fpkg - Package a firmware\n" 120 "Copyright (C) 2007 Jonathan Zarate\n" 121 "\n" 122 "Usage: [-v <trx version>] -i <input> [-a <align>] [-i <input>] [-a <align>] {output}\n" 123 "Output:\n" 124 " TRX: -t <output file>\n" 125 " Linksys/Cisco: -l <id>,<output file>\n" 126 " W54G WRT54G / WRT54GL\n" 127 " W54U WRTSL54GS\n" 128 " W54S WRTS54GS\n" 129 " W54s WRT54GS v4\n" 130 " N160 WRT160N v3\n" 131 " EWCB WRT300N v1\n" 132 " 310N WRT310N v1/v2\n" 133 " 320N WRT320N\n" 134 " 610N WRT610N v2\n" 135 " 32XN E2000\n" 136 " 61XN E3000\n" 137 " Motorola: -m <id>,<output file>\n" 138 " 0x10577000 WE800\n" 139 " 0x10577040 WA840\n" 140 " 0x10577050 WR850\n" 141 " ASUS: -r <id>,<v1>,<v2>,<v3>,<v4>,<output file>\n" 142 "\n" 143 ); 144 exit(1); 145} 146 147void load_image(const char *fname) 148{ 149 struct stat st; 150 FILE *f; 151 long rsize; 152 char *p; 153 154 if (trx_final) { 155 fprintf(stderr, "Cannot load another image if an output has already been written.\n"); 156 exit(1); 157 } 158 if (trx_count >= trx_max_offset) { 159 fprintf(stderr, "Too many input files.\n"); 160 exit(1); 161 } 162 163 if (stat(fname, &st) != 0) { 164 perror(fname); 165 exit(1); 166 } 167 if (st.st_ctime > max_time) max_time = st.st_ctime; 168 169 rsize = ROUNDUP(st.st_size, 4); 170 if ((trx->length + rsize) > TRX_MAX_LEN) { 171 fprintf(stderr, "Total size %lu (%.1f KB) is too big. Maximum is %lu (%.1f KB).\n", 172 (trx->length + rsize), (trx->length + rsize) / 1024.0, 173 (long unsigned int) TRX_MAX_LEN, TRX_MAX_LEN / 1024.0); 174 exit(1); 175 } 176 177 p = (char *)trx + trx->length; 178 if ((f = fopen(fname, "r")) == NULL) { 179 perror(fname); 180 exit(1); 181 } 182 if (fread((char *)trx + trx->length, st.st_size, 1, f) != 1) { 183 perror(fname); 184 exit(1); 185 } 186 fclose(f); 187 strncpy(names[trx_count], fname, sizeof(names[0]) -1); 188 trx->offsets[trx_count++] = trx->length; 189 trx->length += rsize; 190} 191 192void align_trx(const char *align) 193{ 194 uint32_t len; 195 size_t n; 196 char *e; 197 198 errno = 0; 199 n = strtoul(align, &e, 0); 200 if (errno || (e == align) || *e) { 201 fprintf(stderr, "Illegal numeric string\n"); 202 help(); 203 } 204 205 if (trx_final) { 206 fprintf(stderr, "Cannot align if an output has already been written.\n"); 207 exit(1); 208 } 209 210 len = ROUNDUP(trx->length, n); 211 if (len > TRX_MAX_LEN) { 212 fprintf(stderr, "Total size %u (%.1f KB) is too big. Maximum is %lu (%.1f KB).\n", 213 len, len / 1024.0, (long unsigned int) TRX_MAX_LEN, TRX_MAX_LEN / 1024.0); 214 exit(1); 215 } 216 trx->length = len; 217} 218 219void set_trx_version(const char *ver) 220{ 221 int n; 222 char *e; 223 224 errno = 0; 225 n = strtoul(ver, &e, 0); 226 if (errno || (e == ver) || *e) { 227 fprintf(stderr, "Illegal numeric string\n"); 228 help(); 229 } 230 231 if (trx_count > 0) { 232 fprintf(stderr, "Cannot change trx version after images have already been loaded.\n"); 233 exit(1); 234 } 235 236 if (n < 1 || n > 2) { 237 fprintf(stderr, "TRX version %d is not supported.\n", n); 238 exit(1); 239 } 240 241 trx_version = (char)n; 242 switch (trx_version) { 243 case 2: 244 trx_max_offset = 4; 245 break; 246 default: 247 trx_max_offset = 3; 248 break; 249 } 250 trx->length = trx_header_size(); 251} 252 253void finalize_trx(void) 254{ 255 uint32_t len; 256 257 if (trx_count == 0) { 258 fprintf(stderr, "No image was loaded.\n"); 259 exit(1); 260 } 261 262 if (trx_final) return; 263 trx_final = 1; 264 265 len = trx->length; 266 267 trx->length = ROUNDUP(len, 4096); 268 trx->magic = TRX_MAGIC; 269 trx->flag_version = trx_version << 16; 270 trx->crc32 = crc_calc(0xFFFFFFFF, (void *)&trx->flag_version, 271 trx->length - (sizeof(*trx) - (sizeof(trx->flag_version) + sizeof(trx->offsets)))); 272 273 trx_padding = trx->length - len; 274} 275 276void create_trx(const char *fname) 277{ 278 FILE *f; 279 280 finalize_trx(); 281 282 printf("Creating TRX: %s\n", fname); 283 284 if (((f = fopen(fname, "w")) == NULL) || 285 (fwrite(trx, trx->length, 1, f) != 1)) { 286 perror(fname); 287 exit(1); 288 } 289 fclose(f); 290} 291 292void create_cytan(const char *fname, const char *pattern) 293{ 294 FILE *f; 295 cytan_t h; 296 char padding[1024 - sizeof(h)]; 297 struct tm *tm; 298 299 if (strlen(pattern) != 4) { 300 fprintf(stderr, "Linksys signature must be 4 characters. \"%s\" is invalid.\n", pattern); 301 exit(1); 302 } 303 304 finalize_trx(); 305 306 printf("Creating Linksys %s: %s\n", pattern, fname); 307 308 memset(&h, 0, sizeof(h)); 309 memcpy(h.magic, pattern, 4); 310 memcpy(h.u2nd, "U2ND", 4); 311 h.version[0] = 4; // 4.0.0 should be >= *_VERSION_FROM defined in code_pattern.h 312 h.flags = 0xFF; 313 tm = localtime(&max_time); 314 h.date[0] = tm->tm_year - 100; 315 h.date[1] = tm->tm_mon + 1; 316 h.date[2] = tm->tm_mday; 317 318 memset(padding, 0, sizeof(padding)); 319 320 if (((f = fopen(fname, "w")) == NULL) || 321 (fwrite(&h, sizeof(h), 1, f) != 1) || 322 (fwrite(trx, trx->length, 1, f) != 1) || 323 (fwrite(padding, sizeof(padding), 1, f) != 1)) { 324 perror(fname); 325 exit(1); 326 } 327 fclose(f); 328} 329 330void create_moto(const char *fname, const char *signature) 331{ 332 FILE *f; 333 moto_t h; 334 char *p; 335 336 h.magic = strtoul(signature, &p, 0); 337 if (*p != 0) help(); 338 339 finalize_trx(); 340 341 printf("Creating Motorola 0x%08X: %s\n", h.magic, fname); 342 343 h.magic = htonl(h.magic); 344 h.crc32 = crc_calc(0xFFFFFFFF, (void *)&h.magic, sizeof(h.magic)); 345 h.crc32 = htonl(crc_calc(h.crc32, (void *)trx, trx->length)); 346 347 if (((f = fopen(fname, "w")) == NULL) || 348 (fwrite(&h, sizeof(h), 1, f) != 1) || 349 (fwrite(trx, trx->length, 1, f) != 1)) { 350 perror(fname); 351 exit(1); 352 } 353 fclose(f); 354} 355 356#define MAX_STRING 12 357#define MAX_VER 4 358 359typedef struct { 360 uint8_t major; 361 uint8_t minor; 362} version_t; 363 364typedef struct { 365 version_t kernel; 366 version_t fs; 367 char productid[MAX_STRING]; 368 version_t hw[MAX_VER*2]; 369 char pad[32]; 370} TAIL; 371 372/* usage: 373 * -r <productid>,<version>,<output file> 374 * 375 */ 376int create_asus(const char *optarg) 377{ 378 FILE *f; 379 char value[320]; 380 char *next, *pid, *ver, *fname, *p; 381 TAIL asus_tail; 382 uint32_t len; 383 uint32_t v1, v2, v3, v4; 384 385 memset(&asus_tail, 0, sizeof(TAIL)); 386 387 strncpy(value, optarg, sizeof(value)); 388 next = value; 389 pid = strsep(&next, ","); 390 if(!pid) return 0; 391 392 strncpy(&asus_tail.productid[0], pid, MAX_STRING); 393 394 ver = strsep(&next, ","); 395 if(!ver) return 0; 396 397 sscanf(ver, "%d.%d.%d.%d", &v1, &v2, &v3, &v4); 398 asus_tail.kernel.major = (uint8_t)v1; 399 asus_tail.kernel.minor = (uint8_t)v2; 400 asus_tail.fs.major = (uint8_t)v3; 401 asus_tail.fs.minor = (uint8_t)v4; 402 403 fname = strsep(&next, ","); 404 if(!fname) return 0; 405 406 // append version information into the latest offset 407 trx->length += sizeof(TAIL); 408 len = trx->length; 409 trx->length = ROUNDUP(len, 4096); 410 411 p = (char *)trx+trx->length-sizeof(TAIL); 412 memcpy(p, &asus_tail, sizeof(TAIL)); 413 414 finalize_trx(); 415 416 printf("Creating ASUS %s firmware to %s\n", asus_tail.productid, fname); 417 418 if (((f = fopen(fname, "w")) == NULL) || 419 (fwrite(trx, trx->length, 1, f) != 1)) { 420 perror(fname); 421 exit(1); 422 } 423 fclose(f); 424 425 return 1; 426} 427 428int main(int argc, char **argv) 429{ 430 char s[256]; 431 char *p; 432 int o; 433 unsigned l, j; 434 435 printf("\n"); 436 437 if ((!crc_init()) || ((trx = calloc(1, TRX_MAX_LEN)) == NULL)) { 438 fprintf(stderr, "Not enough memory\n"); 439 exit(1); 440 } 441 trx->length = trx_header_size(); 442 443 while ((o = getopt(argc, argv, "v:i:a:t:l:m:r:")) != -1) { 444 switch (o) { 445 case 'v': 446 set_trx_version(optarg); 447 break; 448 case 'i': 449 load_image(optarg); 450 break; 451 case 'a': 452 align_trx(optarg); 453 break; 454 case 't': 455 create_trx(optarg); 456 break; 457 case 'l': 458 case 'm': 459 if (strlen(optarg) >= sizeof(s)) help(); 460 strcpy(s, optarg); 461 if ((p = strchr(s, ',')) == NULL) help(); 462 *p = 0; 463 ++p; 464 if (o == 'l') create_cytan(p, s); 465 else create_moto(p, s); 466 break; 467 case 'r': 468 create_asus(optarg); 469 break; 470 default: 471 help(); 472 return 1; 473 } 474 } 475 476 if (trx_count == 0) { 477 help(); 478 } 479 else { 480 finalize_trx(); 481 l = trx->length - trx_padding - trx_header_size(); 482 printf("\nTRX Image:\n"); 483 printf(" Total Size .... : %u (%.1f KB) (%.1f MB)\n", trx->length, trx->length / 1024.0, trx->length / 1024.0 / 1024.0); 484 printf(" Images ...... : %u (0x%08x)\n", l , l); 485 printf(" Padding ..... : %d\n", trx_padding); 486 487 printf(" Avail. for jffs :\n"); 488 489 /* Reserved: 2 EBs for pmon, 1 EB for nvram. */ 490 l = trx->length; 491 if (l < (4 * 1024 * 1024) - (3 * 64 * 1024)) 492 j = (4 * 1024 * 1024) - (3 * 64 * 1024) - l; 493 else 494 j = 0; 495 printf(" 4MB, 128K CFE : %d EBs + %d\n", j / (64*1024), j % (64*1024)); 496 497 /* Reserved: 4 EBs for pmon, 1 EB for nvram. */ 498 if (l < (4 * 1024 * 1024) - (5 * 64 * 1024)) 499 j = (4 * 1024 * 1024) - (5 * 64 * 1024) - l; 500 else 501 j = 0; 502 printf(" 4MB, 256K CFE : %d EBs + %d\n", j / (64*1024), j % (64*1024)); 503 504 if (l < (8 * 1024 * 1024) - (5 * 64 * 1024)) 505 j = (8 * 1024 * 1024) - (5 * 64 * 1024) - l; 506 else 507 j = 0; 508 printf(" 8MB, 256K CFE : %d EBs + %d\n", j / (64*1024), j % (64*1024)); 509 510 if (l < (32 * 1024 * 1024) - (5 * 64 * 1024)) 511 j = (32 * 1024 * 1024) - (5 * 64 * 1024) - l; 512 else 513 j = 0; 514 printf(" 32MB, 256K CFE : %d EBs + %d\n", j / (64*1024), j % (64*1024)); 515 516 printf(" CRC-32 ........ : %8X\n", trx->crc32); 517 l = (ROUNDUP(trx->length, (128 * 1024)) / (128 * 1024)); 518 printf(" 128K Blocks ... : %u (0x%08X)\n", l, l); 519 l = (ROUNDUP(trx->length, (64 * 1024)) / (64 * 1024)); 520 printf(" 64K Blocks ... : %u (0x%08X)\n", l, l); 521 printf(" Offsets:\n"); 522 for (o = 0; o < trx_max_offset; ++o) { 523 printf(" %d: 0x%08X %s\n", o, trx->offsets[o], names[o]); 524 } 525 } 526 printf("\n"); 527 return 0; 528} 529