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 #elif __BYTE_ORDER == __LITTLE_ENDIAN
26 #define cpu_to_le32(x) (x)
27 #define le32_to_cpu(x) (x)
28 #define cpu_to_be32(x) bswap_32(x)
29 #define be32_to_cpu(x) bswap_32(x)
31 #error "Unsupported endianness"
34 #define WFI_VERSION 0x00005732
35 #define WFI_VERSION_NAND_1MB_DATA 0x00005731
37 #define WFI_NOR_FLASH 1
38 #define WFI_NAND16_FLASH 2
39 #define WFI_NAND128_FLASH 3
40 #define WFI_NAND256_FLASH 4
41 #define WFI_NAND512_FLASH 5
42 #define WFI_NAND1024_FLASH 6
43 #define WFI_NAND2048_FLASH 7
45 #define WFI_FLAG_HAS_PMC 0x1
46 #define WFI_FLAG_SUPPORTS_BTRM 0x2
48 struct bcm4908img_tail
{
56 /* Info about BCM4908 image */
57 struct bcm4908img_info
{
59 size_t vendor_header_size
; /* Vendor header size */
60 uint32_t crc32
; /* Calculated checksum */
61 struct bcm4908img_tail tail
;
66 static inline size_t bcm4908img_min(size_t x
, size_t y
) {
70 /**************************************************
72 **************************************************/
74 static const uint32_t crc32_tbl
[] = {
75 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
76 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
77 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
78 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
79 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
80 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
81 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
82 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
83 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
84 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
85 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
86 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
87 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
88 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
89 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
90 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
91 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
92 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
93 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
94 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
95 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
96 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
97 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
98 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
99 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
100 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
101 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
102 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
103 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
104 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
105 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
106 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
107 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
108 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
109 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
110 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
111 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
112 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
113 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
114 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
115 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
116 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
117 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
118 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
119 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
120 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
121 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
122 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
123 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
124 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
125 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
126 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
127 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
128 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
129 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
130 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
131 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
132 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
133 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
134 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
135 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
136 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
137 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
138 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
141 uint32_t bcm4908img_crc32(uint32_t crc
, uint8_t *buf
, size_t len
) {
143 crc
= crc32_tbl
[(crc
^ *buf
) & 0xff] ^ (crc
>> 8);
151 /**************************************************
153 **************************************************/
155 static FILE *bcm4908img_open(const char *pathname
, const char *mode
) {
159 return fopen(pathname
, mode
);
161 if (isatty(fileno(stdin
))) {
162 fprintf(stderr
, "Reading from TTY stdin is unsupported\n");
166 if (fstat(fileno(stdin
), &st
)) {
167 fprintf(stderr
, "Failed to fstat stdin: %d\n", -errno
);
171 if (S_ISFIFO(st
.st_mode
)) {
172 fprintf(stderr
, "Reading from pipe stdin is unsupported\n");
179 static void bcm4908img_close(FILE *fp
) {
184 /**************************************************
185 * Existing firmware parser
186 **************************************************/
192 uint32_t kernel_chksum
;
193 uint32_t rootfs_chksum
;
196 uint32_t image_chksum
;
197 uint32_t header_chksum
;
201 static int bcm4908img_parse(FILE *fp
, struct bcm4908img_info
*info
) {
202 struct bcm4908img_tail
*tail
= &info
->tail
;
203 struct chk_header
*chk
;
210 memset(info
, 0, sizeof(*info
));
214 if (fstat(fileno(fp
), &st
)) {
216 fprintf(stderr
, "Failed to fstat: %d\n", err
);
219 info
->file_size
= st
.st_size
;
224 if (fread(buf
, 1, sizeof(buf
), fp
) != sizeof(buf
)) {
225 fprintf(stderr
, "Failed to read file header\n");
229 if (be32_to_cpu(chk
->magic
) == 0x2a23245e)
230 info
->vendor_header_size
= be32_to_cpu(chk
->header_len
);
234 fseek(fp
, info
->vendor_header_size
, SEEK_SET
);
236 info
->crc32
= 0xffffffff;
237 length
= info
->file_size
- info
->vendor_header_size
- sizeof(*tail
);
238 while (length
&& (bytes
= fread(buf
, 1, bcm4908img_min(sizeof(buf
), length
), fp
)) > 0) {
239 info
->crc32
= bcm4908img_crc32(info
->crc32
, buf
, bytes
);
243 fprintf(stderr
, "Failed to read last %zd B of data\n", length
);
249 if (fread(tail
, 1, sizeof(*tail
), fp
) != sizeof(*tail
)) {
250 fprintf(stderr
, "Failed to read BCM4908 image tail\n");
254 /* Standard validation */
256 if (info
->crc32
!= le32_to_cpu(tail
->crc32
)) {
257 fprintf(stderr
, "Invalid data crc32: 0x%08x instead of 0x%08x\n", info
->crc32
, le32_to_cpu(tail
->crc32
));
264 /**************************************************
266 **************************************************/
268 static int bcm4908img_info(int argc
, char **argv
) {
269 struct bcm4908img_info info
;
270 const char *pathname
= NULL
;
275 while ((c
= getopt(argc
, argv
, "i:")) != -1) {
283 fp
= bcm4908img_open(pathname
, "r");
285 fprintf(stderr
, "Failed to open BCM4908 image\n");
290 err
= bcm4908img_parse(fp
, &info
);
292 fprintf(stderr
, "Failed to parse BCM4908 image\n");
296 printf("Vendor header length:\t%zu\n", info
.vendor_header_size
);
297 printf("Checksum:\t0x%08x\n", info
.crc32
);
300 bcm4908img_close(fp
);
305 /**************************************************
307 **************************************************/
309 static ssize_t
bcm4908img_create_append_file(FILE *trx
, const char *in_path
, uint32_t *crc32
) {
315 in
= fopen(in_path
, "r");
317 fprintf(stderr
, "Failed to open %s\n", in_path
);
321 while ((bytes
= fread(buf
, 1, sizeof(buf
), in
)) > 0) {
322 if (fwrite(buf
, 1, bytes
, trx
) != bytes
) {
323 fprintf(stderr
, "Failed to write %zu B to %s\n", bytes
, pathname
);
327 *crc32
= bcm4908img_crc32(*crc32
, buf
, bytes
);
336 static ssize_t
bcm4908img_create_append_zeros(FILE *trx
, size_t length
) {
339 buf
= malloc(length
);
342 memset(buf
, 0, length
);
344 if (fwrite(buf
, 1, length
, trx
) != length
) {
345 fprintf(stderr
, "Failed to write %zu B to %s\n", length
, pathname
);
355 static ssize_t
bcm4908img_create_align(FILE *trx
, size_t cur_offset
, size_t alignment
) {
356 if (cur_offset
& (alignment
- 1)) {
357 size_t length
= alignment
- (cur_offset
% alignment
);
358 return bcm4908img_create_append_zeros(trx
, length
);
364 static int bcm4908img_create(int argc
, char **argv
) {
365 struct bcm4908img_tail tail
= {
366 .version
= cpu_to_le32(WFI_VERSION
),
367 .chip_id
= cpu_to_le32(0x4908),
368 .flash_type
= cpu_to_le32(WFI_NAND128_FLASH
),
369 .flags
= cpu_to_le32(WFI_FLAG_SUPPORTS_BTRM
),
371 uint32_t crc32
= 0xffffffff;
372 size_t cur_offset
= 0;
379 fprintf(stderr
, "No BCM4908 image pathname passed\n");
385 fp
= fopen(pathname
, "w+");
387 fprintf(stderr
, "Failed to open %s\n", pathname
);
393 while ((c
= getopt(argc
, argv
, "f:a:A:")) != -1) {
396 bytes
= bcm4908img_create_append_file(fp
, optarg
, &crc32
);
398 fprintf(stderr
, "Failed to append file %s\n", optarg
);
404 bytes
= bcm4908img_create_align(fp
, cur_offset
, strtol(optarg
, NULL
, 0));
406 fprintf(stderr
, "Failed to append zeros\n");
411 bytes
= strtol(optarg
, NULL
, 0) - cur_offset
;
413 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));
415 bytes
= bcm4908img_create_append_zeros(fp
, bytes
);
417 fprintf(stderr
, "Failed to append zeros\n");
427 tail
.crc32
= cpu_to_le32(crc32
);
429 bytes
= fwrite(&tail
, 1, sizeof(tail
), fp
);
430 if (bytes
!= sizeof(tail
)) {
431 fprintf(stderr
, "Failed to write BCM4908 image tail to %s\n", pathname
);
441 /**************************************************
443 **************************************************/
445 static void usage() {
448 printf("Info about a BCM4908 image:\n");
449 printf("\tbcm4908img info <options>\n");
450 printf("\t-i <file>\t\t\t\tinput BCM490 image\n");
452 printf("Creating a new BCM4908 image:\n");
453 printf("\tbcm4908img create <file> [options]\n");
454 printf("\t-f file\t\t\t\tadd data from specified file\n");
455 printf("\t-a alignment\t\t\tpad image with zeros to specified alignment\n");
456 printf("\t-A offset\t\t\t\tappend zeros until reaching specified offset\n");
459 int main(int argc
, char **argv
) {
462 if (!strcmp(argv
[1], "info"))
463 return bcm4908img_info(argc
, argv
);
464 else if (!strcmp(argv
[1], "create"))
465 return bcm4908img_create(argc
, argv
);