firmware-utils: bcm4908img: fix uninitialized var usage
[openwrt/openwrt.git] / tools / firmware-utils / src / bcm4908img.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
4 */
5
6 #include <byteswap.h>
7 #include <endian.h>
8 #include <errno.h>
9 #include <stdbool.h>
10 #include <stddef.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17
18 #if !defined(__BYTE_ORDER)
19 #error "Unknown byte order"
20 #endif
21
22 #if __BYTE_ORDER == __BIG_ENDIAN
23 #define cpu_to_le32(x) bswap_32(x)
24 #define le32_to_cpu(x) bswap_32(x)
25 #define cpu_to_be32(x) (x)
26 #define be32_to_cpu(x) (x)
27 #define cpu_to_le16(x) bswap_16(x)
28 #define le16_to_cpu(x) bswap_16(x)
29 #define cpu_to_be16(x) (x)
30 #define be16_to_cpu(x) (x)
31 #elif __BYTE_ORDER == __LITTLE_ENDIAN
32 #define cpu_to_le32(x) (x)
33 #define le32_to_cpu(x) (x)
34 #define cpu_to_be32(x) bswap_32(x)
35 #define be32_to_cpu(x) bswap_32(x)
36 #define cpu_to_le16(x) (x)
37 #define le16_to_cpu(x) (x)
38 #define cpu_to_be16(x) bswap_16(x)
39 #define be16_to_cpu(x) bswap_16(x)
40 #else
41 #error "Unsupported endianness"
42 #endif
43
44 #define WFI_VERSION 0x00005732
45 #define WFI_VERSION_NAND_1MB_DATA 0x00005731
46
47 #define WFI_NOR_FLASH 1
48 #define WFI_NAND16_FLASH 2
49 #define WFI_NAND128_FLASH 3
50 #define WFI_NAND256_FLASH 4
51 #define WFI_NAND512_FLASH 5
52 #define WFI_NAND1024_FLASH 6
53 #define WFI_NAND2048_FLASH 7
54
55 #define WFI_FLAG_HAS_PMC 0x1
56 #define WFI_FLAG_SUPPORTS_BTRM 0x2
57
58 #define UBI_EC_HDR_MAGIC 0x55424923
59
60 static int debug;
61
62 struct bcm4908img_tail {
63 uint32_t crc32;
64 uint32_t version;
65 uint32_t chip_id;
66 uint32_t flash_type;
67 uint32_t flags;
68 };
69
70 /**
71 * struct bcm4908img_info - info about BCM4908 image
72 *
73 * Standard BCM4908 image consists of:
74 * 1. (Optional) vedor header
75 * 2. (Optional) cferom
76 * 3. bootfs ─┐
77 * 4. padding ├─ firmware
78 * 5. rootfs ─┘
79 * 6. BCM4908 tail
80 */
81 struct bcm4908img_info {
82 size_t file_size;
83 size_t cferom_offset;
84 size_t bootfs_offset;
85 size_t padding_offset;
86 size_t rootfs_offset;
87 uint32_t crc32; /* Calculated checksum */
88 struct bcm4908img_tail tail;
89 };
90
91 char *pathname;
92
93 static inline size_t bcm4908img_min(size_t x, size_t y) {
94 return x < y ? x : y;
95 }
96
97 /**************************************************
98 * CRC32
99 **************************************************/
100
101 static const uint32_t crc32_tbl[] = {
102 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
103 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
104 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
105 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
106 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
107 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
108 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
109 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
110 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
111 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
112 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
113 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
114 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
115 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
116 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
117 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
118 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
119 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
120 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
121 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
122 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
123 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
124 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
125 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
126 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
127 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
128 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
129 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
130 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
131 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
132 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
133 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
134 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
135 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
136 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
137 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
138 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
139 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
140 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
141 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
142 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
143 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
144 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
145 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
146 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
147 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
148 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
149 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
150 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
151 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
152 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
153 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
154 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
155 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
156 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
157 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
158 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
159 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
160 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
161 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
162 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
163 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
164 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
165 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
166 };
167
168 uint32_t bcm4908img_crc32(uint32_t crc, const void *buf, size_t len) {
169 const uint8_t *in = buf;
170
171 while (len) {
172 crc = crc32_tbl[(crc ^ *in) & 0xff] ^ (crc >> 8);
173 in++;
174 len--;
175 }
176
177 return crc;
178 }
179
180 /**************************************************
181 * Helpers
182 **************************************************/
183
184 static FILE *bcm4908img_open(const char *pathname, const char *mode) {
185 struct stat st;
186
187 if (pathname)
188 return fopen(pathname, mode);
189
190 if (isatty(fileno(stdin))) {
191 fprintf(stderr, "Reading from TTY stdin is unsupported\n");
192 return NULL;
193 }
194
195 if (fstat(fileno(stdin), &st)) {
196 fprintf(stderr, "Failed to fstat stdin: %d\n", -errno);
197 return NULL;
198 }
199
200 if (S_ISFIFO(st.st_mode)) {
201 fprintf(stderr, "Reading from pipe stdin is unsupported\n");
202 return NULL;
203 }
204
205 return stdin;
206 }
207
208 static void bcm4908img_close(FILE *fp) {
209 if (fp != stdin)
210 fclose(fp);
211 }
212
213 static int bcm4908img_calc_crc32(FILE *fp, struct bcm4908img_info *info) {
214 uint8_t buf[1024];
215 size_t length;
216 size_t bytes;
217
218 /* Start with cferom (or bootfs) - skip vendor header */
219 fseek(fp, info->cferom_offset, SEEK_SET);
220
221 info->crc32 = 0xffffffff;
222 length = info->file_size - info->cferom_offset - sizeof(struct bcm4908img_tail);
223 while (length && (bytes = fread(buf, 1, bcm4908img_min(sizeof(buf), length), fp)) > 0) {
224 info->crc32 = bcm4908img_crc32(info->crc32, buf, bytes);
225 length -= bytes;
226 }
227 if (length) {
228 fprintf(stderr, "Failed to read last %zd B of data\n", length);
229 return -EIO;
230 }
231
232 return 0;
233 }
234
235 /**************************************************
236 * Existing firmware parser
237 **************************************************/
238
239 struct chk_header {
240 uint32_t magic;
241 uint32_t header_len;
242 uint8_t reserved[8];
243 uint32_t kernel_chksum;
244 uint32_t rootfs_chksum;
245 uint32_t kernel_len;
246 uint32_t rootfs_len;
247 uint32_t image_chksum;
248 uint32_t header_chksum;
249 char board_id[0];
250 };
251
252 static bool bcm4908img_is_all_ff(const void *buf, size_t length)
253 {
254 const uint8_t *in = buf;
255 int i;
256
257 for (i = 0; i < length; i++) {
258 if (in[i] != 0xff)
259 return false;
260 }
261
262 return true;
263 }
264
265 static int bcm4908img_parse(FILE *fp, struct bcm4908img_info *info) {
266 struct bcm4908img_tail *tail = &info->tail;
267 struct chk_header *chk;
268 struct stat st;
269 uint8_t buf[1024];
270 uint16_t tmp16;
271 size_t length;
272 size_t bytes;
273 int err = 0;
274
275 memset(info, 0, sizeof(*info));
276
277 /* File size */
278
279 if (fstat(fileno(fp), &st)) {
280 err = -errno;
281 fprintf(stderr, "Failed to fstat: %d\n", err);
282 return err;
283 }
284 info->file_size = st.st_size;
285
286 /* Vendor formats */
287
288 rewind(fp);
289 if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) {
290 fprintf(stderr, "Failed to read file header\n");
291 return -EIO;
292 }
293 chk = (void *)buf;
294 if (be32_to_cpu(chk->magic) == 0x2a23245e)
295 info->cferom_offset = be32_to_cpu(chk->header_len);
296
297 /* Offsets */
298
299 for (info->bootfs_offset = info->cferom_offset;
300 info->bootfs_offset < info->file_size;
301 info->bootfs_offset += 0x20000) {
302 if (fseek(fp, info->bootfs_offset, SEEK_SET)) {
303 err = -errno;
304 fprintf(stderr, "Failed to fseek to the 0x%zx\n", info->bootfs_offset);
305 return err;
306 }
307 if (fread(&tmp16, 1, sizeof(tmp16), fp) != sizeof(tmp16)) {
308 fprintf(stderr, "Failed to read while looking for JFFS2\n");
309 return -EIO;
310 }
311 if (be16_to_cpu(tmp16) == 0x8519)
312 break;
313 }
314 if (info->bootfs_offset >= info->file_size) {
315 fprintf(stderr, "Failed to find bootfs offset\n");
316 return -EPROTO;
317 }
318
319 for (info->rootfs_offset = info->bootfs_offset;
320 info->rootfs_offset < info->file_size;
321 info->rootfs_offset += 0x20000) {
322 uint32_t *magic = (uint32_t *)&buf[0];
323
324 if (fseek(fp, info->rootfs_offset, SEEK_SET)) {
325 err = -errno;
326 fprintf(stderr, "Failed to fseek: %d\n", err);
327 return err;
328 }
329
330 length = info->padding_offset ? sizeof(*magic) : 256;
331 bytes = fread(buf, 1, length, fp);
332 if (bytes != length) {
333 fprintf(stderr, "Failed to read %zu bytes\n", length);
334 return -EIO;
335 }
336
337 if (!info->padding_offset && bcm4908img_is_all_ff(buf, length))
338 info->padding_offset = info->rootfs_offset;
339
340 if (be32_to_cpu(*magic) == UBI_EC_HDR_MAGIC)
341 break;
342 }
343 if (info->rootfs_offset >= info->file_size) {
344 fprintf(stderr, "Failed to find rootfs offset\n");
345 return -EPROTO;
346 }
347
348 /* CRC32 */
349
350 /* Start with cferom (or bootfs) - skip vendor header */
351 fseek(fp, info->cferom_offset, SEEK_SET);
352
353 info->crc32 = 0xffffffff;
354 length = info->file_size - info->cferom_offset - sizeof(*tail);
355 while (length && (bytes = fread(buf, 1, bcm4908img_min(sizeof(buf), length), fp)) > 0) {
356 info->crc32 = bcm4908img_crc32(info->crc32, buf, bytes);
357 length -= bytes;
358 }
359 if (length) {
360 fprintf(stderr, "Failed to read last %zd B of data\n", length);
361 return -EIO;
362 }
363
364 /* Tail */
365
366 if (fread(tail, 1, sizeof(*tail), fp) != sizeof(*tail)) {
367 fprintf(stderr, "Failed to read BCM4908 image tail\n");
368 return -EIO;
369 }
370
371 /* Standard validation */
372
373 if (info->crc32 != le32_to_cpu(tail->crc32)) {
374 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", info->crc32, le32_to_cpu(tail->crc32));
375 return -EPROTO;
376 }
377
378 return 0;
379 }
380
381 /**************************************************
382 * Info
383 **************************************************/
384
385 static int bcm4908img_info(int argc, char **argv) {
386 struct bcm4908img_info info;
387 const char *pathname = NULL;
388 FILE *fp;
389 int c;
390 int err = 0;
391
392 while ((c = getopt(argc, argv, "i:")) != -1) {
393 switch (c) {
394 case 'i':
395 pathname = optarg;
396 break;
397 }
398 }
399
400 fp = bcm4908img_open(pathname, "r");
401 if (!fp) {
402 fprintf(stderr, "Failed to open BCM4908 image\n");
403 err = -EACCES;
404 goto out;
405 }
406
407 err = bcm4908img_parse(fp, &info);
408 if (err) {
409 fprintf(stderr, "Failed to parse BCM4908 image\n");
410 goto err_close;
411 }
412
413 if (info.bootfs_offset != info.cferom_offset)
414 printf("cferom offset:\t%zu\n", info.cferom_offset);
415 printf("bootfs offset:\t0x%zx\n", info.bootfs_offset);
416 if (info.padding_offset)
417 printf("padding offset:\t0x%zx\n", info.padding_offset);
418 printf("rootfs offset:\t0x%zx\n", info.rootfs_offset);
419 printf("Checksum:\t0x%08x\n", info.crc32);
420
421 err_close:
422 bcm4908img_close(fp);
423 out:
424 return err;
425 }
426
427 /**************************************************
428 * Create
429 **************************************************/
430
431 static ssize_t bcm4908img_create_append_file(FILE *trx, const char *in_path, uint32_t *crc32) {
432 FILE *in;
433 size_t bytes;
434 ssize_t length = 0;
435 uint8_t buf[1024];
436
437 in = fopen(in_path, "r");
438 if (!in) {
439 fprintf(stderr, "Failed to open %s\n", in_path);
440 return -EACCES;
441 }
442
443 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
444 if (fwrite(buf, 1, bytes, trx) != bytes) {
445 fprintf(stderr, "Failed to write %zu B to %s\n", bytes, pathname);
446 length = -EIO;
447 break;
448 }
449 *crc32 = bcm4908img_crc32(*crc32, buf, bytes);
450 length += bytes;
451 }
452
453 fclose(in);
454
455 return length;
456 }
457
458 static ssize_t bcm4908img_create_append_zeros(FILE *trx, size_t length) {
459 uint8_t *buf;
460
461 buf = malloc(length);
462 if (!buf)
463 return -ENOMEM;
464 memset(buf, 0, length);
465
466 if (fwrite(buf, 1, length, trx) != length) {
467 fprintf(stderr, "Failed to write %zu B to %s\n", length, pathname);
468 free(buf);
469 return -EIO;
470 }
471
472 free(buf);
473
474 return length;
475 }
476
477 static ssize_t bcm4908img_create_align(FILE *trx, size_t cur_offset, size_t alignment) {
478 if (cur_offset & (alignment - 1)) {
479 size_t length = alignment - (cur_offset % alignment);
480 return bcm4908img_create_append_zeros(trx, length);
481 }
482
483 return 0;
484 }
485
486 static int bcm4908img_create(int argc, char **argv) {
487 struct bcm4908img_tail tail = {
488 .version = cpu_to_le32(WFI_VERSION),
489 .chip_id = cpu_to_le32(0x4908),
490 .flash_type = cpu_to_le32(WFI_NAND128_FLASH),
491 .flags = cpu_to_le32(WFI_FLAG_SUPPORTS_BTRM),
492 };
493 uint32_t crc32 = 0xffffffff;
494 size_t cur_offset = 0;
495 ssize_t bytes;
496 FILE *fp;
497 int c;
498 int err = 0;
499
500 if (argc < 3) {
501 fprintf(stderr, "No BCM4908 image pathname passed\n");
502 err = -EINVAL;
503 goto out;
504 }
505 pathname = argv[2];
506
507 fp = fopen(pathname, "w+");
508 if (!fp) {
509 fprintf(stderr, "Failed to open %s\n", pathname);
510 err = -EACCES;
511 goto out;
512 }
513
514 optind = 3;
515 while ((c = getopt(argc, argv, "f:a:A:")) != -1) {
516 switch (c) {
517 case 'f':
518 bytes = bcm4908img_create_append_file(fp, optarg, &crc32);
519 if (bytes < 0) {
520 fprintf(stderr, "Failed to append file %s\n", optarg);
521 } else {
522 cur_offset += bytes;
523 }
524 break;
525 case 'a':
526 bytes = bcm4908img_create_align(fp, cur_offset, strtol(optarg, NULL, 0));
527 if (bytes < 0)
528 fprintf(stderr, "Failed to append zeros\n");
529 else
530 cur_offset += bytes;
531 break;
532 case 'A':
533 bytes = strtol(optarg, NULL, 0) - cur_offset;
534 if (bytes < 0) {
535 fprintf(stderr, "Current BCM4908 image length is 0x%zx, can't pad it with zeros to 0x%lx\n", cur_offset, strtol(optarg, NULL, 0));
536 } else {
537 bytes = bcm4908img_create_append_zeros(fp, bytes);
538 if (bytes < 0)
539 fprintf(stderr, "Failed to append zeros\n");
540 else
541 cur_offset += bytes;
542 }
543 break;
544 }
545 if (err)
546 goto err_close;
547 }
548
549 tail.crc32 = cpu_to_le32(crc32);
550
551 bytes = fwrite(&tail, 1, sizeof(tail), fp);
552 if (bytes != sizeof(tail)) {
553 fprintf(stderr, "Failed to write BCM4908 image tail to %s\n", pathname);
554 return -EIO;
555 }
556
557 err_close:
558 fclose(fp);
559 out:
560 return err;
561 }
562
563 /**************************************************
564 * Extract
565 **************************************************/
566
567 static int bcm4908img_extract(int argc, char **argv) {
568 struct bcm4908img_info info;
569 const char *pathname = NULL;
570 const char *type = NULL;
571 uint8_t buf[1024];
572 size_t offset;
573 size_t length;
574 size_t bytes;
575 FILE *fp;
576 int c;
577 int err = 0;
578
579 while ((c = getopt(argc, argv, "i:t:")) != -1) {
580 switch (c) {
581 case 'i':
582 pathname = optarg;
583 break;
584 case 't':
585 type = optarg;
586 break;
587 }
588 }
589
590 fp = bcm4908img_open(pathname, "r");
591 if (!fp) {
592 fprintf(stderr, "Failed to open BCM4908 image\n");
593 err = -EACCES;
594 goto err_out;
595 }
596
597 err = bcm4908img_parse(fp, &info);
598 if (err) {
599 fprintf(stderr, "Failed to parse BCM4908 image\n");
600 goto err_close;
601 }
602
603 if (!type) {
604 err = -EINVAL;
605 fprintf(stderr, "No data to extract specified\n");
606 goto err_close;
607 } else if (!strcmp(type, "cferom")) {
608 offset = info.cferom_offset;
609 length = info.bootfs_offset - offset;
610 if (!length) {
611 err = -ENOENT;
612 fprintf(stderr, "This BCM4908 image doesn't contain cferom\n");
613 goto err_close;
614 }
615 } else if (!strcmp(type, "bootfs")) {
616 offset = info.bootfs_offset;
617 length = (info.padding_offset ? info.padding_offset : info.rootfs_offset) - offset;
618 } else if (!strcmp(type, "rootfs")) {
619 offset = info.rootfs_offset;
620 length = info.file_size - offset - sizeof(struct bcm4908img_tail);
621 } else if (!strcmp(type, "firmware")) {
622 offset = info.bootfs_offset;
623 length = info.file_size - offset - sizeof(struct bcm4908img_tail);
624 } else {
625 err = -EINVAL;
626 fprintf(stderr, "Unsupported extract type: %s\n", type);
627 goto err_close;
628 }
629
630 if (!length) {
631 err = -EINVAL;
632 fprintf(stderr, "Failed to find requested data in input image\n");
633 goto err_close;
634 }
635
636 fseek(fp, offset, SEEK_SET);
637 while (length && (bytes = fread(buf, 1, bcm4908img_min(sizeof(buf), length), fp)) > 0) {
638 fwrite(buf, bytes, 1, stdout);
639 length -= bytes;
640 }
641 if (length) {
642 err = -EIO;
643 fprintf(stderr, "Failed to read last %zd B of data\n", length);
644 goto err_close;
645 }
646
647 err_close:
648 bcm4908img_close(fp);
649 err_out:
650 return err;
651 }
652
653 /**************************************************
654 * bootfs
655 **************************************************/
656
657 #define JFFS2_MAGIC_BITMASK 0x1985
658
659 #define JFFS2_COMPR_NONE 0x00
660 #define JFFS2_COMPR_ZERO 0x01
661 #define JFFS2_COMPR_RTIME 0x02
662 #define JFFS2_COMPR_RUBINMIPS 0x03
663 #define JFFS2_COMPR_COPY 0x04
664 #define JFFS2_COMPR_DYNRUBIN 0x05
665 #define JFFS2_COMPR_ZLIB 0x06
666 #define JFFS2_COMPR_LZO 0x07
667 /* Compatibility flags. */
668 #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
669 #define JFFS2_NODE_ACCURATE 0x2000
670 /* INCOMPAT: Fail to mount the filesystem */
671 #define JFFS2_FEATURE_INCOMPAT 0xc000
672 /* ROCOMPAT: Mount read-only */
673 #define JFFS2_FEATURE_ROCOMPAT 0x8000
674 /* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
675 #define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
676 /* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
677 #define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
678
679 #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
680
681 typedef struct {
682 uint32_t v32;
683 } __attribute__((packed)) jint32_t;
684
685 typedef struct {
686 uint16_t v16;
687 } __attribute__((packed)) jint16_t;
688
689 struct jffs2_unknown_node
690 {
691 /* All start like this */
692 jint16_t magic;
693 jint16_t nodetype;
694 jint32_t totlen; /* So we can skip over nodes we don't grok */
695 jint32_t hdr_crc;
696 };
697
698 struct jffs2_raw_dirent
699 {
700 jint16_t magic;
701 jint16_t nodetype; /* == JFFS2_NODETYPE_DIRENT */
702 jint32_t totlen;
703 jint32_t hdr_crc;
704 jint32_t pino;
705 jint32_t version;
706 jint32_t ino; /* == zero for unlink */
707 jint32_t mctime;
708 uint8_t nsize;
709 uint8_t type;
710 uint8_t unused[2];
711 jint32_t node_crc;
712 jint32_t name_crc;
713 uint8_t name[0];
714 };
715
716 #define je16_to_cpu(x) ((x).v16)
717 #define je32_to_cpu(x) ((x).v32)
718
719 static int bcm4908img_bootfs_ls(FILE *fp, struct bcm4908img_info *info) {
720 struct jffs2_unknown_node node;
721 struct jffs2_raw_dirent dirent;
722 size_t offset;
723 size_t bytes;
724 int err = 0;
725
726 for (offset = info->bootfs_offset; ; offset += (je32_to_cpu(node.totlen) + 0x03) & ~0x03) {
727 char name[FILENAME_MAX + 1];
728
729 if (fseek(fp, offset, SEEK_SET)) {
730 err = -errno;
731 fprintf(stderr, "Failed to fseek: %d\n", err);
732 return err;
733 }
734
735 bytes = fread(&node, 1, sizeof(node), fp);
736 if (bytes != sizeof(node)) {
737 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
738 return -EIO;
739 }
740
741 if (je16_to_cpu(node.magic) != JFFS2_MAGIC_BITMASK) {
742 break;
743 }
744
745 if (je16_to_cpu(node.nodetype) != JFFS2_NODETYPE_DIRENT) {
746 continue;
747 }
748
749 memcpy(&dirent, &node, sizeof(node));
750 bytes += fread((uint8_t *)&dirent + sizeof(node), 1, sizeof(dirent) - sizeof(node), fp);
751 if (bytes != sizeof(dirent)) {
752 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
753 return -EIO;
754 }
755
756 if (dirent.nsize + 1 > sizeof(name)) {
757 /* Keep reading & printing BUT exit with error code */
758 fprintf(stderr, "Too long filename\n");
759 err = -ENOMEM;
760 continue;
761 }
762
763 bytes = fread(name, 1, dirent.nsize, fp);
764 if (bytes != dirent.nsize) {
765 fprintf(stderr, "Failed to read filename\n");
766 return -EIO;
767 }
768 name[bytes] = '\0';
769
770 printf("%s\n", name);
771 }
772
773 return err;
774 }
775
776 static int bcm4908img_bootfs_mv(FILE *fp, struct bcm4908img_info *info, int argc, char **argv) {
777 struct jffs2_unknown_node node;
778 struct jffs2_raw_dirent dirent;
779 const char *oldname;
780 const char *newname;
781 size_t offset;
782 size_t bytes;
783 int err = -ENOENT;
784
785 if (argc - optind < 2) {
786 fprintf(stderr, "No enough arguments passed\n");
787 return -EINVAL;
788 }
789 oldname = argv[optind++];
790 newname = argv[optind++];
791
792 if (strlen(newname) != strlen(oldname)) {
793 fprintf(stderr, "New filename must have the same length as the old one\n");
794 return -EINVAL;
795 }
796
797 for (offset = info->bootfs_offset; ; offset += (je32_to_cpu(node.totlen) + 0x03) & ~0x03) {
798 char name[FILENAME_MAX];
799 uint32_t crc32;
800
801 if (fseek(fp, offset, SEEK_SET)) {
802 err = -errno;
803 fprintf(stderr, "Failed to fseek: %d\n", err);
804 return err;
805 }
806
807 bytes = fread(&node, 1, sizeof(node), fp);
808 if (bytes != sizeof(node)) {
809 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
810 return -EIO;
811 }
812
813 if (je16_to_cpu(node.magic) != JFFS2_MAGIC_BITMASK) {
814 break;
815 }
816
817 if (je16_to_cpu(node.nodetype) != JFFS2_NODETYPE_DIRENT) {
818 continue;
819 }
820
821 bytes += fread((uint8_t *)&dirent + sizeof(node), 1, sizeof(dirent) - sizeof(node), fp);
822 if (bytes != sizeof(dirent)) {
823 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
824 return -EIO;
825 }
826
827 if (dirent.nsize + 1 > sizeof(name)) {
828 fprintf(stderr, "Too long filename\n");
829 err = -ENOMEM;
830 continue;
831 }
832
833 bytes = fread(name, 1, dirent.nsize, fp);
834 if (bytes != dirent.nsize) {
835 fprintf(stderr, "Failed to read filename\n");
836 return -EIO;
837 }
838 name[bytes] = '\0';
839
840 if (debug)
841 printf("offset:%08zx name_crc:%04x filename:%s\n", offset, je32_to_cpu(dirent.name_crc), name);
842
843 if (strcmp(name, oldname)) {
844 continue;
845 }
846
847 if (fseek(fp, offset + offsetof(struct jffs2_raw_dirent, name_crc), SEEK_SET)) {
848 err = -errno;
849 fprintf(stderr, "Failed to fseek: %d\n", err);
850 return err;
851 }
852 crc32 = bcm4908img_crc32(0, newname, dirent.nsize);
853 bytes = fwrite(&crc32, 1, sizeof(crc32), fp);
854 if (bytes != sizeof(crc32)) {
855 fprintf(stderr, "Failed to write new CRC32\n");
856 return -EIO;
857 }
858
859 if (fseek(fp, offset + offsetof(struct jffs2_raw_dirent, name), SEEK_SET)) {
860 err = -errno;
861 fprintf(stderr, "Failed to fseek: %d\n", err);
862 return err;
863 }
864 bytes = fwrite(newname, 1, dirent.nsize, fp);
865 if (bytes != dirent.nsize) {
866 fprintf(stderr, "Failed to write new filename\n");
867 return -EIO;
868 }
869
870 /* Calculate new BCM4908 image checksum */
871
872 err = bcm4908img_calc_crc32(fp, info);
873 if (err) {
874 fprintf(stderr, "Failed to write new filename\n");
875 return err;
876 }
877
878 info->tail.crc32 = cpu_to_le32(info->crc32);
879 if (fseek(fp, -sizeof(struct bcm4908img_tail), SEEK_END)) {
880 err = -errno;
881 fprintf(stderr, "Failed to write new filename\n");
882 return err;
883 }
884
885 if (fwrite(&info->tail, 1, sizeof(struct bcm4908img_tail), fp) != sizeof(struct bcm4908img_tail)) {
886 fprintf(stderr, "Failed to write updated tail\n");
887 return -EIO;
888 }
889
890 printf("Successfully renamed %s to the %s\n", oldname, newname);
891
892 return 0;
893 }
894
895 fprintf(stderr, "Failed to find %s\n", oldname);
896
897 return -ENOENT;
898 }
899
900 static int bcm4908img_bootfs(int argc, char **argv) {
901 struct bcm4908img_info info;
902 const char *pathname = NULL;
903 const char *mode;
904 const char *cmd;
905 FILE *fp;
906 int c;
907 int err = 0;
908
909 while ((c = getopt(argc, argv, "i:")) != -1) {
910 switch (c) {
911 case 'i':
912 pathname = optarg;
913 break;
914 }
915 }
916
917 if (argc - optind < 1) {
918 fprintf(stderr, "No bootfs command specified\n");
919 err = -EINVAL;
920 goto out;
921 }
922 cmd = argv[optind++];
923
924 mode = strcmp(cmd, "mv") ? "r" : "r+";
925 fp = bcm4908img_open(pathname, mode);
926 if (!fp) {
927 fprintf(stderr, "Failed to open BCM4908 image\n");
928 err = -EACCES;
929 goto out;
930 }
931
932 err = bcm4908img_parse(fp, &info);
933 if (err) {
934 fprintf(stderr, "Failed to parse BCM4908 image\n");
935 goto err_close;
936 }
937
938 if (!strcmp(cmd, "ls")) {
939 err = bcm4908img_bootfs_ls(fp, &info);
940 } else if (!strcmp(cmd, "mv")) {
941 err = bcm4908img_bootfs_mv(fp, &info, argc, argv);
942 } else {
943 err = -EINVAL;
944 fprintf(stderr, "Unsupported bootfs command: %s\n", cmd);
945 }
946
947 err_close:
948 bcm4908img_close(fp);
949 out:
950 return err;
951 }
952
953 /**************************************************
954 * Start
955 **************************************************/
956
957 static void usage() {
958 printf("Usage:\n");
959 printf("\n");
960 printf("Info about a BCM4908 image:\n");
961 printf("\tbcm4908img info <options>\n");
962 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
963 printf("\n");
964 printf("Creating a new BCM4908 image:\n");
965 printf("\tbcm4908img create <file> [options]\n");
966 printf("\t-f file\t\t\t\tadd data from specified file\n");
967 printf("\t-a alignment\t\t\tpad image with zeros to specified alignment\n");
968 printf("\t-A offset\t\t\t\tappend zeros until reaching specified offset\n");
969 printf("\n");
970 printf("Extracting from a BCM4908 image:\n");
971 printf("\tbcm4908img extract <options>\n");
972 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
973 printf("\t-t <type>\t\t\t\tone of: cferom, bootfs, rootfs, firmware\n");
974 printf("\n");
975 printf("Access bootfs in a BCM4908 image:\n");
976 printf("\tbcm4908img bootfs <options> <command> <arguments>\n");
977 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
978 printf("\tls\t\t\t\t\tlist bootfs files\n");
979 printf("\tmv <source> <dest>\t\t\trename bootfs file\n");
980 }
981
982 int main(int argc, char **argv) {
983 if (argc > 1) {
984 optind++;
985 if (!strcmp(argv[1], "info"))
986 return bcm4908img_info(argc, argv);
987 else if (!strcmp(argv[1], "create"))
988 return bcm4908img_create(argc, argv);
989 else if (!strcmp(argv[1], "extract"))
990 return bcm4908img_extract(argc, argv);
991 else if (!strcmp(argv[1], "bootfs"))
992 return bcm4908img_bootfs(argc, argv);
993 }
994
995 usage();
996 return 0;
997 }