1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl> 4 */ 5 6 /* 7 * Standard Xiaomi firmware image consists of: 8 * 1. Xiaomi header 9 * 2. Blobs 10 * 3. RSA signature 11 * 12 * Each blob section consists of: 13 * 1. Header 14 * 2. Content 15 * 16 * Signature consists of: 17 * 1. Header 18 * 2. Content 19 */ 20 21 #include <byteswap.h> 22 #include <endian.h> 23 #include <errno.h> 24 #include <stdbool.h> 25 #include <stddef.h> 26 #include <stdint.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <sys/stat.h> 31 #include <unistd.h> 32 33 #if !defined(__BYTE_ORDER) 34 #error "Unknown byte order" 35 #endif 36 37 #if __BYTE_ORDER == __BIG_ENDIAN 38 #define cpu_to_le32(x) bswap_32(x) 39 #define le32_to_cpu(x) bswap_32(x) 40 #define cpu_to_be32(x) (x) 41 #define be32_to_cpu(x) (x) 42 #define cpu_to_le16(x) bswap_16(x) 43 #define le16_to_cpu(x) bswap_16(x) 44 #define cpu_to_be16(x) (x) 45 #define be16_to_cpu(x) (x) 46 #elif __BYTE_ORDER == __LITTLE_ENDIAN 47 #define cpu_to_le32(x) (x) 48 #define le32_to_cpu(x) (x) 49 #define cpu_to_be32(x) bswap_32(x) 50 #define be32_to_cpu(x) bswap_32(x) 51 #define cpu_to_le16(x) (x) 52 #define le16_to_cpu(x) (x) 53 #define cpu_to_be16(x) bswap_16(x) 54 #define be16_to_cpu(x) bswap_16(x) 55 #else 56 #error "Unsupported endianness" 57 #endif 58 59 #define DEVICE_ID_MIWIFI_R1CM 0x0003 60 #define DEVICE_ID_MIWIFI_R2D 0x0004 61 #define DEVICE_ID_MIWIFI_R1CL 0x0005 62 #define DEVICE_ID_MIWIFI_R3 0x0007 63 #define DEVICE_ID_MIWIFI_R3D 0x0008 64 #define DEVICE_ID_MIWIFI_R3G 0x000d 65 #define DEVICE_ID_MIWIFI_R4CM 0x0012 66 #define DEVICE_ID_MIWIFI_R2100 0x0016 67 #define DEVICE_ID_MIWIFI_RA70 0x0025 68 69 #define BLOB_ALIGNMENT 0x4 70 71 #define BLOB_TYPE_UBOOT 0x0001 72 #define BLOB_TYPE_FW_UIMAGE 0x0004 /* Found in r1cl, r1cm */ 73 #define BLOB_TYPE_FW_OS2 0x0006 74 #define BLOB_TYPE_FW_UIMAGE2 0x0007 /* Found in r4cm */ 75 76 /* Raw data */ 77 78 struct xiaomi_header { 79 char magic[4]; 80 uint32_t signature_offset; 81 uint32_t crc32; 82 uint16_t unused; 83 uint16_t device_id; 84 uint32_t blob_offsets[8]; 85 }; 86 87 struct xiaomi_blob_header { 88 uint32_t magic; 89 uint32_t flash_offset; 90 uint32_t size; 91 uint16_t type; 92 uint16_t unused; 93 char name[32]; 94 }; 95 96 struct xiaomi_signature_header { 97 uint32_t size; 98 uint32_t padding[3]; 99 uint8_t content[0x100]; 100 }; 101 102 /* Parsed info */ 103 104 struct xiaomifw_blob_info { 105 struct xiaomi_blob_header header; 106 size_t offset; 107 size_t size; 108 }; 109 110 struct xiaomifw_info { 111 struct xiaomi_header header; 112 size_t file_size; 113 struct xiaomifw_blob_info blobs[8]; 114 size_t signature_offset; 115 uint32_t crc32; 116 }; 117 118 static inline size_t xiaomifw_min(size_t x, size_t y) { 119 return x < y ? x : y; 120 } 121 122 struct device_map { 123 int device_id; 124 const char *device_name; 125 }; 126 127 static const struct device_map device_names[] = { 128 { DEVICE_ID_MIWIFI_R1CM, "r1cm" }, 129 { DEVICE_ID_MIWIFI_R2D, "r2d" }, 130 { DEVICE_ID_MIWIFI_R1CL, "r1cl" }, 131 { DEVICE_ID_MIWIFI_R3, "r3" }, 132 { DEVICE_ID_MIWIFI_R3D, "r3d" }, 133 { DEVICE_ID_MIWIFI_R3G, "r3g" }, 134 { DEVICE_ID_MIWIFI_R4CM, "r4cm" }, 135 { DEVICE_ID_MIWIFI_R2100, "r2100" }, 136 { DEVICE_ID_MIWIFI_RA70, "ra70" }, 137 }; 138 139 const char *xiaomifw_device_name(int device_id) { 140 int i; 141 142 for (i = 0; i < sizeof(device_names); i++) { 143 if (device_names[i].device_id == device_id) { 144 return device_names[i].device_name; 145 } 146 } 147 148 return "unknown"; 149 } 150 151 const int xiaomifw_device_id(const char *device_name) { 152 int i; 153 154 for (i = 0; i < sizeof(device_names); i++) { 155 if (!strcmp(device_names[i].device_name, device_name)) { 156 return device_names[i].device_id; 157 } 158 } 159 160 return -ENOENT; 161 } 162 163 /************************************************** 164 * CRC32 165 **************************************************/ 166 167 static const uint32_t crc32_tbl[] = { 168 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 169 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 170 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 171 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 172 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 173 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 174 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 175 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 176 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 177 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 178 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 179 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 180 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 181 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 182 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 183 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 184 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 185 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 186 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 187 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 188 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 189 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 190 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 191 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 192 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 193 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 194 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 195 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 196 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 197 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 198 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 199 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 200 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 201 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 202 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 203 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 204 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 205 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 206 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 207 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 208 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 209 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 210 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 211 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 212 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 213 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 214 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 215 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 216 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 217 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 218 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 219 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 220 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 221 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 222 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 223 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 224 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 225 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 226 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 227 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 228 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 229 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 230 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 231 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, 232 }; 233 234 uint32_t xiaomifw_crc32(uint32_t crc, const void *buf, size_t len) { 235 const uint8_t *in = buf; 236 237 while (len) { 238 crc = crc32_tbl[(crc ^ *in) & 0xff] ^ (crc >> 8); 239 in++; 240 len--; 241 } 242 243 return crc; 244 } 245 246 /************************************************** 247 * Helpers 248 **************************************************/ 249 250 static FILE *xiaomifw_open(const char *pathname, const char *mode) { 251 struct stat st; 252 253 if (pathname) 254 return fopen(pathname, mode); 255 256 if (isatty(fileno(stdin))) { 257 fprintf(stderr, "Reading from TTY stdin is unsupported\n"); 258 return NULL; 259 } 260 261 if (fstat(fileno(stdin), &st)) { 262 fprintf(stderr, "Failed to fstat stdin: %d\n", -errno); 263 return NULL; 264 } 265 266 if (S_ISFIFO(st.st_mode)) { 267 fprintf(stderr, "Reading from pipe stdin is unsupported\n"); 268 return NULL; 269 } 270 271 return stdin; 272 } 273 274 static void xiaomifw_close(FILE *fp) { 275 if (fp != stdin) 276 fclose(fp); 277 } 278 279 /************************************************** 280 * Existing firmware parser 281 **************************************************/ 282 283 static int xiaomifw_parse(FILE *fp, struct xiaomifw_info *info) { 284 struct xiaomi_header *header = &info->header; 285 struct stat st; 286 uint8_t buf[1024]; 287 size_t length; 288 size_t bytes; 289 int i; 290 int err = 0; 291 292 memset(info, 0, sizeof(*info)); 293 294 /* File size */ 295 296 if (fstat(fileno(fp), &st)) { 297 err = -errno; 298 fprintf(stderr, "Failed to fstat: %d\n", err); 299 return err; 300 } 301 info->file_size = st.st_size; 302 303 /* Header */ 304 305 if (fread(header, 1, sizeof(*header), fp) != sizeof(*header)) { 306 fprintf(stderr, "Failed to read Xiaomi header\n"); 307 return -EIO; 308 } 309 310 if (strncmp(header->magic, "HDR1", 4)) { 311 fprintf(stderr, "Invalid Xiaomi header magic\n"); 312 return -EPROTO; 313 } 314 info->signature_offset = le32_to_cpu(header->signature_offset); 315 316 /* CRC32 */ 317 318 fseek(fp, 12, SEEK_SET); 319 320 info->crc32 = 0xffffffff; 321 length = info->file_size - 12; 322 while (length && (bytes = fread(buf, 1, xiaomifw_min(sizeof(buf), length), fp)) > 0) { 323 info->crc32 = xiaomifw_crc32(info->crc32, buf, bytes); 324 length -= bytes; 325 } 326 if (length) { 327 fprintf(stderr, "Failed to read last %zd B of data\n", length); 328 return -EIO; 329 } 330 331 if (info->crc32 != le32_to_cpu(header->crc32)) { 332 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", info->crc32, le32_to_cpu(header->crc32)); 333 return -EPROTO; 334 } 335 336 /* Blobs */ 337 338 for (i = 0; i < sizeof(info->blobs); i++) { 339 size_t offset = le32_to_cpu(info->header.blob_offsets[i]); 340 struct xiaomifw_blob_info *file_info = &info->blobs[i]; 341 342 if (!offset) { 343 break; 344 } 345 346 fseek(fp, offset, SEEK_SET); 347 348 if (fread(&file_info->header, 1, sizeof(file_info->header), fp) != sizeof(file_info->header)) { 349 fprintf(stderr, "Failed to read file Xiaomi header\n"); 350 return -EIO; 351 } 352 353 file_info->offset = offset; 354 file_info->size = le32_to_cpu(file_info->header.size); 355 356 offset += sizeof(file_info->header) + file_info->size; 357 offset = (offset + 4) & ~(0x4 - 1); 358 } 359 360 return 0; 361 } 362 363 /************************************************** 364 * Info 365 **************************************************/ 366 367 static int xiaomifw_info(int argc, char **argv) { 368 struct xiaomifw_info info; 369 const char *pathname = NULL; 370 uint16_t device_id; 371 FILE *fp; 372 int i; 373 int c; 374 int err = 0; 375 376 while ((c = getopt(argc, argv, "i:")) != -1) { 377 switch (c) { 378 case 'i': 379 pathname = optarg; 380 break; 381 } 382 } 383 384 fp = xiaomifw_open(pathname, "r"); 385 if (!fp) { 386 fprintf(stderr, "Failed to open Xiaomi firmware image\n"); 387 err = -EACCES; 388 goto out; 389 } 390 391 err = xiaomifw_parse(fp, &info); 392 if (err) { 393 fprintf(stderr, "Failed to parse Xiaomi firmware image\n"); 394 goto err_close; 395 } 396 397 device_id = le16_to_cpu(info.header.device_id); 398 399 printf("Device ID: 0x%04x (%s)\n", device_id, xiaomifw_device_name(device_id)); 400 printf("CRC32: 0x%08x\n", info.crc32); 401 printf("Signature offset: 0x%08zx\n", info.signature_offset); 402 for (i = 0; i < sizeof(info.blobs) && info.blobs[i].offset; i++) { 403 struct xiaomifw_blob_info *file_info = &info.blobs[i]; 404 405 printf("[Blob %d] offset:0x%08zx flash_offset:0x%08x size:0x%08zx type:0x%04x name:%s\n", i, file_info->offset, file_info->header.flash_offset, file_info->size, file_info->header.type, file_info->header.name); 406 } 407 408 err_close: 409 xiaomifw_close(fp); 410 out: 411 return err; 412 } 413 414 /************************************************** 415 * Create 416 **************************************************/ 417 418 static ssize_t xiaomifw_create_append_zeros(FILE *fp, size_t length) { 419 uint8_t *buf; 420 421 buf = malloc(length); 422 if (!buf) 423 return -ENOMEM; 424 memset(buf, 0, length); 425 426 if (fwrite(buf, 1, length, fp) != length) { 427 fprintf(stderr, "Failed to write %zu B of zeros\n", length); 428 free(buf); 429 return -EIO; 430 } 431 432 free(buf); 433 434 return length; 435 } 436 437 static ssize_t xiaomifw_create_append_file(FILE *fp, char *blob) { 438 struct xiaomi_blob_header header = { 439 .magic = le32_to_cpu(0x0000babe), 440 .flash_offset = ~0, 441 .type = ~0, 442 }; 443 struct stat st; 444 char *in_path = NULL; 445 ssize_t length = 0; 446 char *type = NULL; 447 char *resptr; 448 char *tok; 449 char *p; 450 uint8_t buf[1024]; 451 size_t bytes; 452 FILE *in; 453 int err; 454 int i = 0; 455 456 /* sscanf and strtok can't handle optional fields (e.g. "::firmware.bin:/tmp/foo.bin") */ 457 resptr = blob; 458 do { 459 p = resptr; 460 if ((tok = strchr(resptr, ':'))) { 461 *tok = '\0'; 462 resptr = tok + 1; 463 } else { 464 resptr = NULL; 465 } 466 467 switch (i++) { 468 case 0: 469 if (*p) { 470 header.flash_offset = cpu_to_le32(strtoul(p, NULL, 0)); 471 } 472 break; 473 case 1: 474 type = p; 475 break; 476 case 2: 477 strncpy(header.name, p, sizeof(header.name)); 478 break; 479 case 3: 480 in_path = p; 481 break; 482 } 483 } while (resptr); 484 485 if (i < 4) { 486 fprintf(stderr, "Failed to parse blob info\n"); 487 return -EPROTO; 488 } 489 490 in = fopen(in_path, "r"); 491 if (!in) { 492 fprintf(stderr, "Failed to open %s\n", in_path); 493 return -EACCES; 494 } 495 496 if (fstat(fileno(in), &st)) { 497 err = -errno; 498 fprintf(stderr, "Failed to fstat: %d\n", err); 499 return err; 500 } 501 header.size = cpu_to_le32(st.st_size); 502 503 if (*type) { 504 if (!strcmp(type, "uimage")) { 505 header.type = cpu_to_le32(BLOB_TYPE_FW_UIMAGE); 506 } else if (!strcmp(type, "uimage2")) { 507 header.type = cpu_to_le32(BLOB_TYPE_FW_UIMAGE2); 508 } else { 509 fprintf(stderr, "Unsupported blob type: %s\n", type); 510 return -ENOENT; 511 } 512 } 513 514 bytes = fwrite(&header, 1, sizeof(header), fp); 515 if (bytes != sizeof(header)) { 516 fprintf(stderr, "Failed to write blob header\n"); 517 return -EIO; 518 } 519 length += bytes; 520 521 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) { 522 if (fwrite(buf, 1, bytes, fp) != bytes) { 523 fprintf(stderr, "Failed to write %zu B of blob\n", bytes); 524 return -EIO; 525 } 526 length += bytes; 527 } 528 529 fclose(in); 530 531 if (length & (BLOB_ALIGNMENT - 1)) { 532 size_t padding = BLOB_ALIGNMENT - (length % BLOB_ALIGNMENT); 533 534 bytes = xiaomifw_create_append_zeros(fp, padding); 535 if (bytes != padding) { 536 fprintf(stderr, "Failed to align blob\n"); 537 return -EIO; 538 } 539 length += bytes; 540 } 541 542 return length; 543 } 544 545 static ssize_t xiaomifw_create_write_signature(FILE *fp) { 546 struct xiaomi_signature_header header = { 547 }; 548 size_t bytes; 549 550 bytes = fwrite(&header, 1, sizeof(header), fp); 551 if (bytes != sizeof(header)) { 552 fprintf(stderr, "Failed to write blob header\n"); 553 return -EIO; 554 } 555 556 return bytes; 557 } 558 559 static int xiaomifw_create(int argc, char **argv) { 560 struct xiaomi_header header = { 561 .magic = { 'H', 'D', 'R', '1' }, 562 }; 563 uint32_t crc32 = 0xffffffff; 564 uint8_t buf[1024]; 565 int blob_idx = 0; 566 ssize_t length; 567 ssize_t offset; 568 ssize_t bytes; 569 int device_id; 570 FILE *fp; 571 int c; 572 int err = 0; 573 574 if (argc < 3) { 575 fprintf(stderr, "No Xiaomi firmware image pathname passed\n"); 576 err = -EINVAL; 577 goto out; 578 } 579 580 optind = 3; 581 while ((c = getopt(argc, argv, "m:b:")) != -1) { 582 switch (c) { 583 case 'm': 584 device_id = xiaomifw_device_id(optarg); 585 if (device_id < 0) { 586 err = device_id; 587 fprintf(stderr, "Failed to find device %s\n", optarg); 588 goto out; 589 } 590 header.device_id = device_id; 591 break; 592 case 'b': 593 break; 594 } 595 if (err) 596 goto out; 597 } 598 599 fp = fopen(argv[2], "w+"); 600 if (!fp) { 601 fprintf(stderr, "Failed to open %s\n", argv[2]); 602 err = -EACCES; 603 goto out; 604 } 605 606 offset = sizeof(header); 607 fseek(fp, offset, SEEK_SET); 608 609 optind = 3; 610 while ((c = getopt(argc, argv, "m:b:")) != -1) { 611 switch (c) { 612 case 'm': 613 break; 614 case 'b': 615 if (blob_idx >= sizeof(header.blob_offsets)) { 616 err = -ENOENT; 617 fprintf(stderr, "Too many blobs specified\n"); 618 goto err_close; 619 } 620 bytes = xiaomifw_create_append_file(fp, optarg); 621 if (bytes < 0) { 622 err = bytes; 623 fprintf(stderr, "Failed to append blob: %d\n", err); 624 goto err_close; 625 } 626 header.blob_offsets[blob_idx++] = cpu_to_le32(offset); 627 offset += bytes; 628 break; 629 } 630 if (err) 631 goto err_close; 632 } 633 634 bytes = xiaomifw_create_write_signature(fp); 635 if (bytes < 0) { 636 err = bytes; 637 fprintf(stderr, "Failed to write signature: %d\n", err); 638 goto err_close; 639 } 640 header.signature_offset = cpu_to_le32(offset); 641 offset += bytes; 642 643 crc32 = xiaomifw_crc32(crc32, (uint8_t *)&header + 12, sizeof(header) - 12); 644 fseek(fp, sizeof(header), SEEK_SET); 645 length = offset - sizeof(header); 646 while (length && (bytes = fread(buf, 1, xiaomifw_min(sizeof(buf), length), fp)) > 0) { 647 crc32 = xiaomifw_crc32(crc32, buf, bytes); 648 length -= bytes; 649 } 650 if (length) { 651 err = -EIO; 652 fprintf(stderr, "Failed to calculate CRC32 over the last %zd B of data\n", length); 653 goto err_close; 654 } 655 656 header.crc32 = cpu_to_le32(crc32); 657 658 rewind(fp); 659 660 bytes = fwrite(&header, 1, sizeof(header), fp); 661 if (bytes != sizeof(header)) { 662 fprintf(stderr, "Failed to write header\n"); 663 return -EIO; 664 } 665 666 err_close: 667 fclose(fp); 668 out: 669 return err; 670 } 671 672 /************************************************** 673 * Extract 674 **************************************************/ 675 676 static int xiaomifw_extract(int argc, char **argv) { 677 struct xiaomifw_info info; 678 const char *pathname = NULL; 679 const char *name = NULL; 680 uint8_t buf[1024]; 681 size_t offset = 0; 682 size_t size = 0; 683 size_t bytes; 684 FILE *fp; 685 int i; 686 int c; 687 int err = 0; 688 689 while ((c = getopt(argc, argv, "i:n:")) != -1) { 690 switch (c) { 691 case 'i': 692 pathname = optarg; 693 break; 694 case 'n': 695 name = optarg; 696 break; 697 } 698 } 699 700 if (!name) { 701 err = -EINVAL; 702 fprintf(stderr, "No data to extract specified\n"); 703 goto err_out; 704 } 705 706 fp = xiaomifw_open(pathname, "r"); 707 if (!fp) { 708 fprintf(stderr, "Failed to open Xiaomi firmware image\n"); 709 err = -EACCES; 710 goto err_out; 711 } 712 713 err = xiaomifw_parse(fp, &info); 714 if (err) { 715 fprintf(stderr, "Failed to parse Xiaomi firmware image\n"); 716 goto err_close; 717 } 718 719 for (i = 0; i < sizeof(info.blobs) && info.blobs[i].offset; i++) { 720 struct xiaomifw_blob_info *file_info = &info.blobs[i]; 721 722 if (!strcmp(file_info->header.name, name)) { 723 offset = file_info->offset; 724 size = file_info->size; 725 } 726 } 727 728 if (!offset || !size) { 729 err = -EINVAL; 730 fprintf(stderr, "Failed to find requested data in input image\n"); 731 goto err_close; 732 } 733 734 fseek(fp, offset + sizeof(struct xiaomi_blob_header), SEEK_SET); 735 while (size && (bytes = fread(buf, 1, xiaomifw_min(sizeof(buf), size), fp)) > 0) { 736 fwrite(buf, bytes, 1, stdout); 737 size -= bytes; 738 } 739 if (size) { 740 err = -EIO; 741 fprintf(stderr, "Failed to read last %zd B of data\n", size); 742 goto err_close; 743 } 744 745 err_close: 746 xiaomifw_close(fp); 747 err_out: 748 return err; 749 } 750 751 /************************************************** 752 * Start 753 **************************************************/ 754 755 static void usage() { 756 printf("Usage:\n"); 757 printf("\n"); 758 printf("Info about a Xiaomi firmware image:\n"); 759 printf("\txiaomifw info <options>\n"); 760 printf("\t-i <file>\t\t\t\t\tinput Xiaomi firmware image\n"); 761 printf("\n"); 762 printf("Creating a new Xiaomi firmware image:\n"); 763 printf("\txiaomifw create <file> [options]\n"); 764 printf("\t-m <model>\t\t\t\t\tmodel name (e.g. \"r4cm\")\n"); 765 printf("\t-b <flash_offset>:<type>:<name>:<path>\t\tblob to include\n"); 766 printf("\n"); 767 printf("Extracting from a Xiaomi firmware image:\n"); 768 printf("\txiaomifw extract <options>\n"); 769 printf("\t-i <file>\t\t\t\t\tinput Xiaomi firmware image\n"); 770 printf("\t-n <type>\t\t\t\t\tname of blob to extract (e.g. \"firmware.bin\")\n"); 771 printf("\n"); 772 printf("Examples:\n"); 773 printf("\txiaomifw info -i miwifi_r4cm_firmware_c6fa8_3.0.23_INT.bin\n"); 774 printf("\txiaomifw extract -i miwifi_r4cm_firmware_c6fa8_3.0.23_INT.bin -n firmware.bin\n"); 775 printf("\txiaomifw create \\\n"); 776 printf("\t\t-m r1cm \\\n"); 777 printf("\t\t-b ::xiaoqiang_version:/tmp/xiaoqiang_version \\\n"); 778 printf("\t\t-b 0x160000:uimage2:firmware.bin:/tmp/custom.bin\n"); 779 } 780 781 int main(int argc, char **argv) { 782 if (argc > 1) { 783 optind++; 784 if (!strcmp(argv[1], "info")) 785 return xiaomifw_info(argc, argv); 786 else if (!strcmp(argv[1], "create")) 787 return xiaomifw_create(argc, argv); 788 else if (!strcmp(argv[1], "extract")) 789 return xiaomifw_extract(argc, argv); 790 } 791 792 usage(); 793 return 0; 794 } 795
This page was automatically generated by LXR 0.3.1. • OpenWrt