d97e54e0f6b4924cb1d7098c21da8632fc5d0ce8
[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 uint8_t buf[1024];
571 const char *type;
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 (!strcmp(type, "cferom")) {
604 offset = info.cferom_offset;
605 length = info.bootfs_offset - offset;
606 if (!length) {
607 err = -ENOENT;
608 fprintf(stderr, "This BCM4908 image doesn't contain cferom\n");
609 goto err_close;
610 }
611 } else if (!strcmp(type, "bootfs")) {
612 offset = info.bootfs_offset;
613 length = (info.padding_offset ? info.padding_offset : info.rootfs_offset) - offset;
614 } else if (!strcmp(type, "rootfs")) {
615 offset = info.rootfs_offset;
616 length = info.file_size - offset - sizeof(struct bcm4908img_tail);
617 } else if (!strcmp(type, "firmware")) {
618 offset = info.bootfs_offset;
619 length = info.file_size - offset - sizeof(struct bcm4908img_tail);
620 } else {
621 err = -EINVAL;
622 fprintf(stderr, "Unsupported extract type: %s\n", type);
623 goto err_close;
624 }
625
626 if (!length) {
627 err = -EINVAL;
628 fprintf(stderr, "No data to extract specified\n");
629 goto err_close;
630 }
631
632 fseek(fp, offset, SEEK_SET);
633 while (length && (bytes = fread(buf, 1, bcm4908img_min(sizeof(buf), length), fp)) > 0) {
634 fwrite(buf, bytes, 1, stdout);
635 length -= bytes;
636 }
637 if (length) {
638 err = -EIO;
639 fprintf(stderr, "Failed to read last %zd B of data\n", length);
640 goto err_close;
641 }
642
643 err_close:
644 bcm4908img_close(fp);
645 err_out:
646 return err;
647 }
648
649 /**************************************************
650 * bootfs
651 **************************************************/
652
653 #define JFFS2_MAGIC_BITMASK 0x1985
654
655 #define JFFS2_COMPR_NONE 0x00
656 #define JFFS2_COMPR_ZERO 0x01
657 #define JFFS2_COMPR_RTIME 0x02
658 #define JFFS2_COMPR_RUBINMIPS 0x03
659 #define JFFS2_COMPR_COPY 0x04
660 #define JFFS2_COMPR_DYNRUBIN 0x05
661 #define JFFS2_COMPR_ZLIB 0x06
662 #define JFFS2_COMPR_LZO 0x07
663 /* Compatibility flags. */
664 #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
665 #define JFFS2_NODE_ACCURATE 0x2000
666 /* INCOMPAT: Fail to mount the filesystem */
667 #define JFFS2_FEATURE_INCOMPAT 0xc000
668 /* ROCOMPAT: Mount read-only */
669 #define JFFS2_FEATURE_ROCOMPAT 0x8000
670 /* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
671 #define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
672 /* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
673 #define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
674
675 #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
676
677 typedef struct {
678 uint32_t v32;
679 } __attribute__((packed)) jint32_t;
680
681 typedef struct {
682 uint16_t v16;
683 } __attribute__((packed)) jint16_t;
684
685 struct jffs2_unknown_node
686 {
687 /* All start like this */
688 jint16_t magic;
689 jint16_t nodetype;
690 jint32_t totlen; /* So we can skip over nodes we don't grok */
691 jint32_t hdr_crc;
692 };
693
694 struct jffs2_raw_dirent
695 {
696 jint16_t magic;
697 jint16_t nodetype; /* == JFFS2_NODETYPE_DIRENT */
698 jint32_t totlen;
699 jint32_t hdr_crc;
700 jint32_t pino;
701 jint32_t version;
702 jint32_t ino; /* == zero for unlink */
703 jint32_t mctime;
704 uint8_t nsize;
705 uint8_t type;
706 uint8_t unused[2];
707 jint32_t node_crc;
708 jint32_t name_crc;
709 uint8_t name[0];
710 };
711
712 #define je16_to_cpu(x) ((x).v16)
713 #define je32_to_cpu(x) ((x).v32)
714
715 static int bcm4908img_bootfs_ls(FILE *fp, struct bcm4908img_info *info) {
716 struct jffs2_unknown_node node;
717 struct jffs2_raw_dirent dirent;
718 size_t offset;
719 size_t bytes;
720 int err = 0;
721
722 for (offset = info->bootfs_offset; ; offset += (je32_to_cpu(node.totlen) + 0x03) & ~0x03) {
723 char name[FILENAME_MAX + 1];
724
725 if (fseek(fp, offset, SEEK_SET)) {
726 err = -errno;
727 fprintf(stderr, "Failed to fseek: %d\n", err);
728 return err;
729 }
730
731 bytes = fread(&node, 1, sizeof(node), fp);
732 if (bytes != sizeof(node)) {
733 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
734 return -EIO;
735 }
736
737 if (je16_to_cpu(node.magic) != JFFS2_MAGIC_BITMASK) {
738 break;
739 }
740
741 if (je16_to_cpu(node.nodetype) != JFFS2_NODETYPE_DIRENT) {
742 continue;
743 }
744
745 memcpy(&dirent, &node, sizeof(node));
746 bytes += fread((uint8_t *)&dirent + sizeof(node), 1, sizeof(dirent) - sizeof(node), fp);
747 if (bytes != sizeof(dirent)) {
748 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
749 return -EIO;
750 }
751
752 if (dirent.nsize + 1 > sizeof(name)) {
753 /* Keep reading & printing BUT exit with error code */
754 fprintf(stderr, "Too long filename\n");
755 err = -ENOMEM;
756 continue;
757 }
758
759 bytes = fread(name, 1, dirent.nsize, fp);
760 if (bytes != dirent.nsize) {
761 fprintf(stderr, "Failed to read filename\n");
762 return -EIO;
763 }
764 name[bytes] = '\0';
765
766 printf("%s\n", name);
767 }
768
769 return err;
770 }
771
772 static int bcm4908img_bootfs_mv(FILE *fp, struct bcm4908img_info *info, int argc, char **argv) {
773 struct jffs2_unknown_node node;
774 struct jffs2_raw_dirent dirent;
775 const char *oldname;
776 const char *newname;
777 size_t offset;
778 size_t bytes;
779 int err = -ENOENT;
780
781 if (argc - optind < 2) {
782 fprintf(stderr, "No enough arguments passed\n");
783 return -EINVAL;
784 }
785 oldname = argv[optind++];
786 newname = argv[optind++];
787
788 if (strlen(newname) != strlen(oldname)) {
789 fprintf(stderr, "New filename must have the same length as the old one\n");
790 return -EINVAL;
791 }
792
793 for (offset = info->bootfs_offset; ; offset += (je32_to_cpu(node.totlen) + 0x03) & ~0x03) {
794 char name[FILENAME_MAX];
795 uint32_t crc32;
796
797 if (fseek(fp, offset, SEEK_SET)) {
798 err = -errno;
799 fprintf(stderr, "Failed to fseek: %d\n", err);
800 return err;
801 }
802
803 bytes = fread(&node, 1, sizeof(node), fp);
804 if (bytes != sizeof(node)) {
805 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
806 return -EIO;
807 }
808
809 if (je16_to_cpu(node.magic) != JFFS2_MAGIC_BITMASK) {
810 break;
811 }
812
813 if (je16_to_cpu(node.nodetype) != JFFS2_NODETYPE_DIRENT) {
814 continue;
815 }
816
817 bytes += fread((uint8_t *)&dirent + sizeof(node), 1, sizeof(dirent) - sizeof(node), fp);
818 if (bytes != sizeof(dirent)) {
819 fprintf(stderr, "Failed to read %zu bytes\n", sizeof(node));
820 return -EIO;
821 }
822
823 if (dirent.nsize + 1 > sizeof(name)) {
824 fprintf(stderr, "Too long filename\n");
825 err = -ENOMEM;
826 continue;
827 }
828
829 bytes = fread(name, 1, dirent.nsize, fp);
830 if (bytes != dirent.nsize) {
831 fprintf(stderr, "Failed to read filename\n");
832 return -EIO;
833 }
834 name[bytes] = '\0';
835
836 if (debug)
837 printf("offset:%08zx name_crc:%04x filename:%s\n", offset, je32_to_cpu(dirent.name_crc), name);
838
839 if (strcmp(name, oldname)) {
840 continue;
841 }
842
843 if (fseek(fp, offset + offsetof(struct jffs2_raw_dirent, name_crc), SEEK_SET)) {
844 err = -errno;
845 fprintf(stderr, "Failed to fseek: %d\n", err);
846 return err;
847 }
848 crc32 = bcm4908img_crc32(0, newname, dirent.nsize);
849 bytes = fwrite(&crc32, 1, sizeof(crc32), fp);
850 if (bytes != sizeof(crc32)) {
851 fprintf(stderr, "Failed to write new CRC32\n");
852 return -EIO;
853 }
854
855 if (fseek(fp, offset + offsetof(struct jffs2_raw_dirent, name), SEEK_SET)) {
856 err = -errno;
857 fprintf(stderr, "Failed to fseek: %d\n", err);
858 return err;
859 }
860 bytes = fwrite(newname, 1, dirent.nsize, fp);
861 if (bytes != dirent.nsize) {
862 fprintf(stderr, "Failed to write new filename\n");
863 return -EIO;
864 }
865
866 /* Calculate new BCM4908 image checksum */
867
868 err = bcm4908img_calc_crc32(fp, info);
869 if (err) {
870 fprintf(stderr, "Failed to write new filename\n");
871 return err;
872 }
873
874 info->tail.crc32 = cpu_to_le32(info->crc32);
875 if (fseek(fp, -sizeof(struct bcm4908img_tail), SEEK_END)) {
876 err = -errno;
877 fprintf(stderr, "Failed to write new filename\n");
878 return err;
879 }
880
881 if (fwrite(&info->tail, 1, sizeof(struct bcm4908img_tail), fp) != sizeof(struct bcm4908img_tail)) {
882 fprintf(stderr, "Failed to write updated tail\n");
883 return -EIO;
884 }
885
886 printf("Successfully renamed %s to the %s\n", oldname, newname);
887
888 return 0;
889 }
890
891 fprintf(stderr, "Failed to find %s\n", oldname);
892
893 return -ENOENT;
894 }
895
896 static int bcm4908img_bootfs(int argc, char **argv) {
897 struct bcm4908img_info info;
898 const char *pathname = NULL;
899 const char *mode;
900 const char *cmd;
901 FILE *fp;
902 int c;
903 int err = 0;
904
905 while ((c = getopt(argc, argv, "i:")) != -1) {
906 switch (c) {
907 case 'i':
908 pathname = optarg;
909 break;
910 }
911 }
912
913 if (argc - optind < 1) {
914 fprintf(stderr, "No bootfs command specified\n");
915 err = -EINVAL;
916 goto out;
917 }
918 cmd = argv[optind++];
919
920 mode = strcmp(cmd, "mv") ? "r" : "r+";
921 fp = bcm4908img_open(pathname, mode);
922 if (!fp) {
923 fprintf(stderr, "Failed to open BCM4908 image\n");
924 err = -EACCES;
925 goto out;
926 }
927
928 err = bcm4908img_parse(fp, &info);
929 if (err) {
930 fprintf(stderr, "Failed to parse BCM4908 image\n");
931 goto err_close;
932 }
933
934 if (!strcmp(cmd, "ls")) {
935 err = bcm4908img_bootfs_ls(fp, &info);
936 } else if (!strcmp(cmd, "mv")) {
937 err = bcm4908img_bootfs_mv(fp, &info, argc, argv);
938 } else {
939 err = -EINVAL;
940 fprintf(stderr, "Unsupported bootfs command: %s\n", cmd);
941 }
942
943 err_close:
944 bcm4908img_close(fp);
945 out:
946 return err;
947 }
948
949 /**************************************************
950 * Start
951 **************************************************/
952
953 static void usage() {
954 printf("Usage:\n");
955 printf("\n");
956 printf("Info about a BCM4908 image:\n");
957 printf("\tbcm4908img info <options>\n");
958 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
959 printf("\n");
960 printf("Creating a new BCM4908 image:\n");
961 printf("\tbcm4908img create <file> [options]\n");
962 printf("\t-f file\t\t\t\tadd data from specified file\n");
963 printf("\t-a alignment\t\t\tpad image with zeros to specified alignment\n");
964 printf("\t-A offset\t\t\t\tappend zeros until reaching specified offset\n");
965 printf("\n");
966 printf("Extracting from a BCM4908 image:\n");
967 printf("\tbcm4908img extract <options>\n");
968 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
969 printf("\t-t <type>\t\t\t\tone of: cferom, bootfs, rootfs, firmware\n");
970 printf("\n");
971 printf("Access bootfs in a BCM4908 image:\n");
972 printf("\tbcm4908img bootfs <options> <command> <arguments>\n");
973 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
974 printf("\tls\t\t\t\t\tlist bootfs files\n");
975 printf("\tmv <source> <dest>\t\t\trename bootfs file\n");
976 }
977
978 int main(int argc, char **argv) {
979 if (argc > 1) {
980 optind++;
981 if (!strcmp(argv[1], "info"))
982 return bcm4908img_info(argc, argv);
983 else if (!strcmp(argv[1], "create"))
984 return bcm4908img_create(argc, argv);
985 else if (!strcmp(argv[1], "extract"))
986 return bcm4908img_extract(argc, argv);
987 else if (!strcmp(argv[1], "bootfs"))
988 return bcm4908img_bootfs(argc, argv);
989 }
990
991 usage();
992 return 0;
993 }