b04916443b6b980bf91d39b573a61cc3c5d330a1
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
16 #if !defined(__BYTE_ORDER)
17 #error "Unknown byte order"
20 #if __BYTE_ORDER == __BIG_ENDIAN
21 #define cpu_to_le32(x) bswap_32(x)
22 #define le32_to_cpu(x) bswap_32(x)
23 #define cpu_to_be32(x) (x)
24 #define be32_to_cpu(x) (x)
25 #define cpu_to_le16(x) bswap_16(x)
26 #define le16_to_cpu(x) bswap_16(x)
27 #define cpu_to_be16(x) (x)
28 #define be16_to_cpu(x) (x)
29 #elif __BYTE_ORDER == __LITTLE_ENDIAN
30 #define cpu_to_le32(x) (x)
31 #define le32_to_cpu(x) (x)
32 #define cpu_to_be32(x) bswap_32(x)
33 #define be32_to_cpu(x) bswap_32(x)
34 #define cpu_to_le16(x) (x)
35 #define le16_to_cpu(x) (x)
36 #define cpu_to_be16(x) bswap_16(x)
37 #define be16_to_cpu(x) bswap_16(x)
39 #error "Unsupported endianness"
42 #define WFI_VERSION 0x00005732
43 #define WFI_VERSION_NAND_1MB_DATA 0x00005731
45 #define WFI_NOR_FLASH 1
46 #define WFI_NAND16_FLASH 2
47 #define WFI_NAND128_FLASH 3
48 #define WFI_NAND256_FLASH 4
49 #define WFI_NAND512_FLASH 5
50 #define WFI_NAND1024_FLASH 6
51 #define WFI_NAND2048_FLASH 7
53 #define WFI_FLAG_HAS_PMC 0x1
54 #define WFI_FLAG_SUPPORTS_BTRM 0x2
56 struct bcm4908img_tail
{
64 /* Info about BCM4908 image */
65 struct bcm4908img_info
{
67 size_t vendor_header_size
; /* Vendor header size */
69 uint32_t crc32
; /* Calculated checksum */
70 struct bcm4908img_tail tail
;
75 static inline size_t bcm4908img_min(size_t x
, size_t y
) {
79 /**************************************************
81 **************************************************/
83 static const uint32_t crc32_tbl
[] = {
84 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
85 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
86 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
87 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
88 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
89 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
90 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
91 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
92 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
93 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
94 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
95 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
96 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
97 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
98 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
99 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
100 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
101 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
102 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
103 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
104 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
105 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
106 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
107 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
108 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
109 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
110 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
111 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
112 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
113 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
114 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
115 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
116 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
117 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
118 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
119 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
120 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
121 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
122 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
123 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
124 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
125 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
126 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
127 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
128 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
129 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
130 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
131 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
132 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
133 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
134 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
135 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
136 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
137 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
138 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
139 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
140 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
141 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
142 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
143 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
144 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
145 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
146 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
147 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
150 uint32_t bcm4908img_crc32(uint32_t crc
, uint8_t *buf
, size_t len
) {
152 crc
= crc32_tbl
[(crc
^ *buf
) & 0xff] ^ (crc
>> 8);
160 /**************************************************
162 **************************************************/
164 static FILE *bcm4908img_open(const char *pathname
, const char *mode
) {
168 return fopen(pathname
, mode
);
170 if (isatty(fileno(stdin
))) {
171 fprintf(stderr
, "Reading from TTY stdin is unsupported\n");
175 if (fstat(fileno(stdin
), &st
)) {
176 fprintf(stderr
, "Failed to fstat stdin: %d\n", -errno
);
180 if (S_ISFIFO(st
.st_mode
)) {
181 fprintf(stderr
, "Reading from pipe stdin is unsupported\n");
188 static void bcm4908img_close(FILE *fp
) {
193 /**************************************************
194 * Existing firmware parser
195 **************************************************/
201 uint32_t kernel_chksum
;
202 uint32_t rootfs_chksum
;
205 uint32_t image_chksum
;
206 uint32_t header_chksum
;
210 static int bcm4908img_parse(FILE *fp
, struct bcm4908img_info
*info
) {
211 struct bcm4908img_tail
*tail
= &info
->tail
;
212 struct chk_header
*chk
;
220 memset(info
, 0, sizeof(*info
));
224 if (fstat(fileno(fp
), &st
)) {
226 fprintf(stderr
, "Failed to fstat: %d\n", err
);
229 info
->file_size
= st
.st_size
;
234 if (fread(buf
, 1, sizeof(buf
), fp
) != sizeof(buf
)) {
235 fprintf(stderr
, "Failed to read file header\n");
239 if (be32_to_cpu(chk
->magic
) == 0x2a23245e)
240 info
->vendor_header_size
= be32_to_cpu(chk
->header_len
);
244 for (; info
->vendor_header_size
+ info
->cferom_size
<= info
->file_size
; info
->cferom_size
+= 0x20000) {
245 if (fseek(fp
, info
->vendor_header_size
+ info
->cferom_size
, SEEK_SET
)) {
247 fprintf(stderr
, "Failed to fseek to the 0x%zx\n", info
->cferom_size
);
250 if (fread(&tmp16
, 1, sizeof(tmp16
), fp
) != sizeof(tmp16
)) {
251 fprintf(stderr
, "Failed to read while looking for JFFS2\n");
254 if (be16_to_cpu(tmp16
) == 0x8519)
257 if (info
->vendor_header_size
+ info
->cferom_size
>= info
->file_size
) {
258 fprintf(stderr
, "Failed to find cferom size (no bootfs found)\n");
264 fseek(fp
, info
->vendor_header_size
, SEEK_SET
);
266 info
->crc32
= 0xffffffff;
267 length
= info
->file_size
- info
->vendor_header_size
- sizeof(*tail
);
268 while (length
&& (bytes
= fread(buf
, 1, bcm4908img_min(sizeof(buf
), length
), fp
)) > 0) {
269 info
->crc32
= bcm4908img_crc32(info
->crc32
, buf
, bytes
);
273 fprintf(stderr
, "Failed to read last %zd B of data\n", length
);
279 if (fread(tail
, 1, sizeof(*tail
), fp
) != sizeof(*tail
)) {
280 fprintf(stderr
, "Failed to read BCM4908 image tail\n");
284 /* Standard validation */
286 if (info
->crc32
!= le32_to_cpu(tail
->crc32
)) {
287 fprintf(stderr
, "Invalid data crc32: 0x%08x instead of 0x%08x\n", info
->crc32
, le32_to_cpu(tail
->crc32
));
294 /**************************************************
296 **************************************************/
298 static int bcm4908img_info(int argc
, char **argv
) {
299 struct bcm4908img_info info
;
300 const char *pathname
= NULL
;
305 while ((c
= getopt(argc
, argv
, "i:")) != -1) {
313 fp
= bcm4908img_open(pathname
, "r");
315 fprintf(stderr
, "Failed to open BCM4908 image\n");
320 err
= bcm4908img_parse(fp
, &info
);
322 fprintf(stderr
, "Failed to parse BCM4908 image\n");
326 printf("Vendor header length:\t%zu\n", info
.vendor_header_size
);
327 printf("cferom size:\t0x%zx\n", info
.cferom_size
);
328 printf("Checksum:\t0x%08x\n", info
.crc32
);
331 bcm4908img_close(fp
);
336 /**************************************************
338 **************************************************/
340 static ssize_t
bcm4908img_create_append_file(FILE *trx
, const char *in_path
, uint32_t *crc32
) {
346 in
= fopen(in_path
, "r");
348 fprintf(stderr
, "Failed to open %s\n", in_path
);
352 while ((bytes
= fread(buf
, 1, sizeof(buf
), in
)) > 0) {
353 if (fwrite(buf
, 1, bytes
, trx
) != bytes
) {
354 fprintf(stderr
, "Failed to write %zu B to %s\n", bytes
, pathname
);
358 *crc32
= bcm4908img_crc32(*crc32
, buf
, bytes
);
367 static ssize_t
bcm4908img_create_append_zeros(FILE *trx
, size_t length
) {
370 buf
= malloc(length
);
373 memset(buf
, 0, length
);
375 if (fwrite(buf
, 1, length
, trx
) != length
) {
376 fprintf(stderr
, "Failed to write %zu B to %s\n", length
, pathname
);
386 static ssize_t
bcm4908img_create_align(FILE *trx
, size_t cur_offset
, size_t alignment
) {
387 if (cur_offset
& (alignment
- 1)) {
388 size_t length
= alignment
- (cur_offset
% alignment
);
389 return bcm4908img_create_append_zeros(trx
, length
);
395 static int bcm4908img_create(int argc
, char **argv
) {
396 struct bcm4908img_tail tail
= {
397 .version
= cpu_to_le32(WFI_VERSION
),
398 .chip_id
= cpu_to_le32(0x4908),
399 .flash_type
= cpu_to_le32(WFI_NAND128_FLASH
),
400 .flags
= cpu_to_le32(WFI_FLAG_SUPPORTS_BTRM
),
402 uint32_t crc32
= 0xffffffff;
403 size_t cur_offset
= 0;
410 fprintf(stderr
, "No BCM4908 image pathname passed\n");
416 fp
= fopen(pathname
, "w+");
418 fprintf(stderr
, "Failed to open %s\n", pathname
);
424 while ((c
= getopt(argc
, argv
, "f:a:A:")) != -1) {
427 bytes
= bcm4908img_create_append_file(fp
, optarg
, &crc32
);
429 fprintf(stderr
, "Failed to append file %s\n", optarg
);
435 bytes
= bcm4908img_create_align(fp
, cur_offset
, strtol(optarg
, NULL
, 0));
437 fprintf(stderr
, "Failed to append zeros\n");
442 bytes
= strtol(optarg
, NULL
, 0) - cur_offset
;
444 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));
446 bytes
= bcm4908img_create_append_zeros(fp
, bytes
);
448 fprintf(stderr
, "Failed to append zeros\n");
458 tail
.crc32
= cpu_to_le32(crc32
);
460 bytes
= fwrite(&tail
, 1, sizeof(tail
), fp
);
461 if (bytes
!= sizeof(tail
)) {
462 fprintf(stderr
, "Failed to write BCM4908 image tail to %s\n", pathname
);
472 /**************************************************
474 **************************************************/
476 static void usage() {
479 printf("Info about a BCM4908 image:\n");
480 printf("\tbcm4908img info <options>\n");
481 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
483 printf("Creating a new BCM4908 image:\n");
484 printf("\tbcm4908img create <file> [options]\n");
485 printf("\t-f file\t\t\t\tadd data from specified file\n");
486 printf("\t-a alignment\t\t\tpad image with zeros to specified alignment\n");
487 printf("\t-A offset\t\t\t\tappend zeros until reaching specified offset\n");
490 int main(int argc
, char **argv
) {
493 if (!strcmp(argv
[1], "info"))
494 return bcm4908img_info(argc
, argv
);
495 else if (!strcmp(argv
[1], "create"))
496 return bcm4908img_create(argc
, argv
);