1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2007 Ubiquiti Networks, Inc. 4 * Copyright (C) 2008 Lukas Kuna <ValXdater@seznam.cz> 5 */ 6 7 #include <sys/types.h> 8 #include <sys/stat.h> 9 #include <inttypes.h> 10 #include <fcntl.h> 11 #include <unistd.h> 12 #include <string.h> 13 #include <errno.h> 14 #include <zlib.h> 15 #include <sys/mman.h> 16 #include <netinet/in.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <limits.h> 20 #include <stdbool.h> 21 #include "fw.h" 22 #include "utils.h" 23 24 typedef struct fw_layout_data { 25 u_int32_t kern_start; 26 u_int32_t kern_entry; 27 u_int32_t firmware_max_length; 28 } fw_layout_t; 29 30 struct fw_info { 31 char name[PATH_MAX]; 32 struct fw_layout_data fw_layout; 33 bool sign; 34 }; 35 36 struct fw_info fw_info[] = { 37 { 38 .name = "XS2", 39 .fw_layout = { 40 .kern_start = 0xbfc30000, 41 .kern_entry = 0x80041000, 42 .firmware_max_length= 0x00390000, 43 }, 44 .sign = false, 45 }, 46 { 47 .name = "XS5", 48 .fw_layout = { 49 .kern_start = 0xbe030000, 50 .kern_entry = 0x80041000, 51 .firmware_max_length= 0x00390000, 52 }, 53 .sign = false, 54 }, 55 { 56 .name = "RS", 57 .fw_layout = { 58 .kern_start = 0xbf030000, 59 .kern_entry = 0x80060000, 60 .firmware_max_length= 0x00B00000, 61 }, 62 .sign = false, 63 }, 64 { 65 .name = "RSPRO", 66 .fw_layout = { 67 .kern_start = 0xbf030000, 68 .kern_entry = 0x80060000, 69 .firmware_max_length= 0x00F00000, 70 }, 71 .sign = false, 72 }, 73 { 74 .name = "LS-SR71", 75 .fw_layout = { 76 .kern_start = 0xbf030000, 77 .kern_entry = 0x80060000, 78 .firmware_max_length= 0x00640000, 79 }, 80 .sign = false, 81 }, 82 { 83 .name = "XS2-8", 84 .fw_layout = { 85 .kern_start = 0xa8030000, 86 .kern_entry = 0x80041000, 87 .firmware_max_length= 0x006C0000, 88 }, 89 .sign = false, 90 91 }, 92 { 93 .name = "XM", 94 .fw_layout = { 95 .kern_start = 0x9f050000, 96 .kern_entry = 0x80002000, 97 .firmware_max_length= 0x00760000, 98 }, 99 .sign = false, 100 }, 101 { 102 .name = "SW", 103 .fw_layout = { 104 .kern_start = 0x9f050000, 105 .kern_entry = 0x80002000, 106 .firmware_max_length= 0x00760000, 107 }, 108 .sign = false, 109 }, 110 { 111 .name = "UBDEV01", 112 .fw_layout = { 113 .kern_start = 0x9f050000, 114 .kern_entry = 0x80002000, 115 .firmware_max_length= 0x006A0000, 116 }, 117 .sign = false, 118 }, 119 { 120 .name = "WA", 121 .fw_layout = { 122 .kern_start = 0x9f050000, 123 .kern_entry = 0x80002000, 124 .firmware_max_length= 0x00F60000, 125 }, 126 .sign = true, 127 }, 128 { 129 .name = "XC", 130 .fw_layout = { 131 .kern_start = 0x9f050000, 132 .kern_entry = 0x80002000, 133 .firmware_max_length= 0x00F60000, 134 }, 135 .sign = true, 136 }, 137 { 138 .name = "ACB", 139 .fw_layout = { 140 .kern_start = 0x9f050000, 141 .kern_entry = 0x80002000, 142 .firmware_max_length= 0x00F60000, 143 }, 144 .sign = true, 145 }, 146 { 147 .name = "", 148 }, 149 }; 150 151 typedef struct part_data { 152 char partition_name[64]; 153 int partition_index; 154 u_int32_t partition_baseaddr; 155 u_int32_t partition_startaddr; 156 u_int32_t partition_memaddr; 157 u_int32_t partition_entryaddr; 158 u_int32_t partition_length; 159 160 char filename[PATH_MAX]; 161 struct stat stats; 162 } part_data_t; 163 164 #define MAX_SECTIONS 8 165 #define DEFAULT_OUTPUT_FILE "firmware-image.bin" 166 #define DEFAULT_VERSION "UNKNOWN" 167 168 #define OPTIONS "B:hv:m:o:r:k:" 169 170 typedef struct image_info { 171 char magic[16]; 172 char version[256]; 173 char outputfile[PATH_MAX]; 174 u_int32_t part_count; 175 part_data_t parts[MAX_SECTIONS]; 176 struct fw_info* fwinfo; 177 } image_info_t; 178 179 static struct fw_info* get_fwinfo(char* board_name) { 180 struct fw_info *fwinfo = fw_info; 181 while(strlen(fwinfo->name)) { 182 if(strcmp(fwinfo->name, board_name) == 0) { 183 return fwinfo; 184 } 185 fwinfo++; 186 } 187 return NULL; 188 } 189 190 static void write_header(void* mem, const char *magic, const char* version) 191 { 192 header_t* header = mem; 193 memset(header, 0, sizeof(header_t)); 194 195 FW_MEMCPY_STR(header->magic, magic); 196 FW_MEMCPY_STR(header->version, version); 197 header->crc = htonl(crc32(0L, (uint8_t*) header, 198 sizeof(header_t) - 2 * sizeof(u_int32_t))); 199 header->pad = 0L; 200 } 201 202 static void write_signature(void* mem, u_int32_t sig_offset) 203 { 204 /* write signature */ 205 signature_t* sign = (signature_t*)(mem + sig_offset); 206 memset(sign, 0, sizeof(signature_t)); 207 208 FW_MEMCPY_STR(sign->magic, MAGIC_END); 209 sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset)); 210 sign->pad = 0L; 211 } 212 213 static void write_signature_rsa(void* mem, u_int32_t sig_offset) 214 { 215 /* write signature */ 216 signature_rsa_t* sign = (signature_rsa_t*)(mem + sig_offset); 217 memset(sign, 0, sizeof(signature_rsa_t)); 218 219 FW_MEMCPY_STR(sign->magic, MAGIC_ENDS); 220 // sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset)); 221 sign->pad = 0L; 222 } 223 224 static int write_part(void* mem, part_data_t* d) 225 { 226 char* addr; 227 int fd; 228 part_t* p = mem; 229 part_crc_t* crc = mem + sizeof(part_t) + d->stats.st_size; 230 231 fd = open(d->filename, O_RDONLY); 232 if (fd < 0) 233 { 234 ERROR("Failed opening file '%s'\n", d->filename); 235 return -1; 236 } 237 238 if ((addr=(char*)mmap(0, d->stats.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) 239 { 240 ERROR("Failed mmaping memory for file '%s'\n", d->filename); 241 close(fd); 242 return -2; 243 } 244 245 memcpy(mem + sizeof(part_t), addr, d->stats.st_size); 246 munmap(addr, d->stats.st_size); 247 248 memset(p->name, 0, PART_NAME_LENGTH); 249 FW_MEMCPY_STR(p->magic, MAGIC_PART); 250 FW_MEMCPY_STR(p->name, d->partition_name); 251 252 p->index = htonl(d->partition_index); 253 p->data_size = htonl(d->stats.st_size); 254 p->part_size = htonl(d->partition_length); 255 p->baseaddr = htonl(d->partition_baseaddr); 256 p->memaddr = htonl(d->partition_memaddr); 257 p->entryaddr = htonl(d->partition_entryaddr); 258 259 crc->crc = htonl(crc32(0L, mem, d->stats.st_size + sizeof(part_t))); 260 crc->pad = 0L; 261 262 return 0; 263 } 264 265 static void usage(const char* progname) 266 { 267 INFO("Version %s\n" 268 "Usage: %s [options]\n" 269 "\t-v <version string>\t - firmware version information, default: %s\n" 270 "\t-o <output file>\t - firmware output file, default: %s\n" 271 "\t-m <magic>\t - firmware magic, default: %s\n" 272 "\t-k <kernel file>\t\t - kernel file\n" 273 "\t-r <rootfs file>\t\t - rootfs file\n" 274 "\t-B <board name>\t\t - choose firmware layout for specified board (XS2, XS5, RS, XM)\n" 275 "\t-h\t\t\t - this help\n", VERSION, 276 progname, DEFAULT_VERSION, DEFAULT_OUTPUT_FILE, MAGIC_HEADER); 277 } 278 279 static void print_image_info(const image_info_t* im) 280 { 281 unsigned int i = 0; 282 283 INFO("Firmware version: '%s'\n" 284 "Output file: '%s'\n" 285 "Part count: %u\n", 286 im->version, im->outputfile, 287 im->part_count); 288 289 for (i = 0; i < im->part_count; ++i) 290 { 291 const part_data_t* d = &im->parts[i]; 292 INFO(" %10s: %8" PRId64 " bytes (free: %8" PRId64 ")\n", 293 d->partition_name, 294 d->stats.st_size, 295 d->partition_length - d->stats.st_size); 296 } 297 } 298 299 static u_int32_t filelength(const char* file) 300 { 301 FILE *p; 302 int ret = -1; 303 304 if ( (p = fopen(file, "rb") ) == NULL) return (-1); 305 306 fseek(p, 0, SEEK_END); 307 ret = ftell(p); 308 309 fclose (p); 310 311 return (ret); 312 } 313 314 static int create_image_layout(const char* kernelfile, const char* rootfsfile, image_info_t* im) 315 { 316 uint32_t rootfs_len = 0; 317 part_data_t* kernel = &im->parts[0]; 318 part_data_t* rootfs = &im->parts[1]; 319 320 fw_layout_t* p = &im->fwinfo->fw_layout; 321 322 printf("board = %s\n", im->fwinfo->name); 323 strcpy(kernel->partition_name, "kernel"); 324 kernel->partition_index = 1; 325 kernel->partition_baseaddr = p->kern_start; 326 if ( (kernel->partition_length = filelength(kernelfile)) == (u_int32_t)-1) return (-1); 327 kernel->partition_memaddr = p->kern_entry; 328 kernel->partition_entryaddr = p->kern_entry; 329 strncpy(kernel->filename, kernelfile, sizeof(kernel->filename)); 330 331 rootfs_len = filelength(rootfsfile); 332 if (rootfs_len + kernel->partition_length > p->firmware_max_length) { 333 ERROR("File '%s' too big (0x%08X) - max size: 0x%08X (exceeds %u bytes)\n", 334 rootfsfile, rootfs_len, p->firmware_max_length, 335 (rootfs_len + kernel->partition_length) - p->firmware_max_length); 336 return (-2); 337 } 338 339 strcpy(rootfs->partition_name, "rootfs"); 340 rootfs->partition_index = 2; 341 rootfs->partition_baseaddr = kernel->partition_baseaddr + kernel->partition_length; 342 rootfs->partition_length = p->firmware_max_length - kernel->partition_length; 343 rootfs->partition_memaddr = 0x00000000; 344 rootfs->partition_entryaddr = 0x00000000; 345 strncpy(rootfs->filename, rootfsfile, sizeof(rootfs->filename)); 346 347 printf("kernel: %d 0x%08x\n", kernel->partition_length, kernel->partition_baseaddr); 348 printf("root: %d 0x%08x\n", rootfs->partition_length, rootfs->partition_baseaddr); 349 im->part_count = 2; 350 351 return 0; 352 } 353 354 /** 355 * Checks the availability and validity of all image components. 356 * Fills in stats member of the part_data structure. 357 */ 358 static int validate_image_layout(image_info_t* im) 359 { 360 unsigned int i; 361 362 if (im->part_count == 0 || im->part_count > MAX_SECTIONS) 363 { 364 ERROR("Invalid part count '%d'\n", im->part_count); 365 return -1; 366 } 367 368 for (i = 0; i < im->part_count; ++i) 369 { 370 part_data_t* d = &im->parts[i]; 371 int len = strlen(d->partition_name); 372 if (len == 0 || len > 16) 373 { 374 ERROR("Invalid partition name '%s' of the part %d\n", 375 d->partition_name, i); 376 return -1; 377 } 378 if (stat(d->filename, &d->stats) < 0) 379 { 380 ERROR("Couldn't stat file '%s' from part '%s'\n", 381 d->filename, d->partition_name); 382 return -2; 383 } 384 if (d->stats.st_size == 0) 385 { 386 ERROR("File '%s' from part '%s' is empty!\n", 387 d->filename, d->partition_name); 388 return -3; 389 } 390 if (d->stats.st_size > d->partition_length) { 391 ERROR("File '%s' too big (%d) - max size: 0x%08X (exceeds %" PRId64 " bytes)\n", 392 d->filename, i, d->partition_length, 393 d->stats.st_size - d->partition_length); 394 return -4; 395 } 396 } 397 398 return 0; 399 } 400 401 static int build_image(image_info_t* im) 402 { 403 char* mem; 404 char* ptr; 405 u_int32_t mem_size; 406 FILE* f; 407 unsigned int i; 408 409 // build in-memory buffer 410 mem_size = sizeof(header_t); 411 if(im->fwinfo->sign) { 412 mem_size += sizeof(signature_rsa_t); 413 } else { 414 mem_size += sizeof(signature_t); 415 } 416 for (i = 0; i < im->part_count; ++i) 417 { 418 part_data_t* d = &im->parts[i]; 419 mem_size += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t); 420 } 421 422 mem = (char*)calloc(mem_size, 1); 423 if (mem == NULL) 424 { 425 ERROR("Cannot allocate memory chunk of size '%u'\n", mem_size); 426 return -1; 427 } 428 429 // write header 430 write_header(mem, im->magic, im->version); 431 ptr = mem + sizeof(header_t); 432 // write all parts 433 for (i = 0; i < im->part_count; ++i) 434 { 435 part_data_t* d = &im->parts[i]; 436 int rc; 437 if ((rc = write_part(ptr, d)) != 0) 438 { 439 ERROR("ERROR: failed writing part %u '%s'\n", i, d->partition_name); 440 } 441 ptr += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t); 442 } 443 // write signature 444 if(im->fwinfo->sign) { 445 write_signature_rsa(mem, mem_size - sizeof(signature_rsa_t)); 446 } else { 447 write_signature(mem, mem_size - sizeof(signature_t)); 448 } 449 450 // write in-memory buffer into file 451 if ((f = fopen(im->outputfile, "w")) == NULL) 452 { 453 ERROR("Can not create output file: '%s'\n", im->outputfile); 454 free(mem); 455 return -10; 456 } 457 458 if (fwrite(mem, mem_size, 1, f) != 1) 459 { 460 ERROR("Could not write %d bytes into file: '%s'\n", 461 mem_size, im->outputfile); 462 free(mem); 463 fclose(f); 464 return -11; 465 } 466 467 free(mem); 468 fclose(f); 469 return 0; 470 } 471 472 473 int main(int argc, char* argv[]) 474 { 475 char kernelfile[PATH_MAX]; 476 char rootfsfile[PATH_MAX]; 477 char board_name[PATH_MAX]; 478 int o, rc; 479 image_info_t im; 480 struct fw_info *fwinfo; 481 482 memset(&im, 0, sizeof(im)); 483 memset(kernelfile, 0, sizeof(kernelfile)); 484 memset(rootfsfile, 0, sizeof(rootfsfile)); 485 memset(board_name, 0, sizeof(board_name)); 486 487 strcpy(im.outputfile, DEFAULT_OUTPUT_FILE); 488 strcpy(im.version, DEFAULT_VERSION); 489 strncpy(im.magic, MAGIC_HEADER, sizeof(im.magic)); 490 491 while ((o = getopt(argc, argv, OPTIONS)) != -1) 492 { 493 switch (o) { 494 case 'v': 495 if (optarg) 496 strncpy(im.version, optarg, sizeof(im.version) - 1); 497 break; 498 case 'o': 499 if (optarg) 500 strncpy(im.outputfile, optarg, sizeof(im.outputfile) - 1); 501 break; 502 case 'm': 503 if (optarg) 504 strncpy(im.magic, optarg, sizeof(im.magic) - 1); 505 break; 506 case 'h': 507 usage(argv[0]); 508 return -1; 509 case 'k': 510 if (optarg) 511 strncpy(kernelfile, optarg, sizeof(kernelfile) - 1); 512 break; 513 case 'r': 514 if (optarg) 515 strncpy(rootfsfile, optarg, sizeof(rootfsfile) - 1); 516 break; 517 case 'B': 518 if (optarg) 519 strncpy(board_name, optarg, sizeof(board_name) - 1); 520 break; 521 } 522 } 523 if (strlen(board_name) == 0) 524 strcpy(board_name, "XS2"); /* default to XS2 */ 525 526 if (strlen(kernelfile) == 0) 527 { 528 ERROR("Kernel file is not specified, cannot continue\n"); 529 usage(argv[0]); 530 return -2; 531 } 532 533 if (strlen(rootfsfile) == 0) 534 { 535 ERROR("Root FS file is not specified, cannot continue\n"); 536 usage(argv[0]); 537 return -2; 538 } 539 540 if ((fwinfo = get_fwinfo(board_name)) == NULL) { 541 ERROR("Invalid baord name '%s'\n", board_name); 542 usage(argv[0]); 543 return -2; 544 } 545 546 im.fwinfo = fwinfo; 547 548 if ((rc = create_image_layout(kernelfile, rootfsfile, &im)) != 0) 549 { 550 ERROR("Failed creating firmware layout description - error code: %d\n", rc); 551 return -3; 552 } 553 554 if ((rc = validate_image_layout(&im)) != 0) 555 { 556 ERROR("Failed validating firmware layout - error code: %d\n", rc); 557 return -4; 558 } 559 560 print_image_info(&im); 561 562 if ((rc = build_image(&im)) != 0) 563 { 564 ERROR("Failed building image file '%s' - error code: %d\n", im.outputfile, rc); 565 return -5; 566 } 567 568 return 0; 569 } 570
This page was automatically generated by LXR 0.3.1. • OpenWrt