ptgen: add Chromium OS kernel partition support
[project/firmware-utils.git] / src / xiaomifw.c
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 }