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_check(int argc
, char **argv
) {
269 struct bcm4908img_info info
;
270 const char *pathname
= NULL
;
277 fp
= bcm4908img_open(pathname
, "r");
279 fprintf(stderr
, "Failed to open %s\n", pathname
);
284 err
= bcm4908img_parse(fp
, &info
);
286 fprintf(stderr
, "Failed to parse %s\n", pathname
);
290 printf("Found a valid BCM4908 image (crc: 0x%08x)\n", info
.crc32
);
293 bcm4908img_close(fp
);
298 /**************************************************
300 **************************************************/
302 static ssize_t
bcm4908img_create_append_file(FILE *trx
, const char *in_path
, uint32_t *crc32
) {
308 in
= fopen(in_path
, "r");
310 fprintf(stderr
, "Failed to open %s\n", in_path
);
314 while ((bytes
= fread(buf
, 1, sizeof(buf
), in
)) > 0) {
315 if (fwrite(buf
, 1, bytes
, trx
) != bytes
) {
316 fprintf(stderr
, "Failed to write %zu B to %s\n", bytes
, pathname
);
320 *crc32
= bcm4908img_crc32(*crc32
, buf
, bytes
);
329 static ssize_t
bcm4908img_create_append_zeros(FILE *trx
, size_t length
) {
332 buf
= malloc(length
);
335 memset(buf
, 0, length
);
337 if (fwrite(buf
, 1, length
, trx
) != length
) {
338 fprintf(stderr
, "Failed to write %zu B to %s\n", length
, pathname
);
348 static ssize_t
bcm4908img_create_align(FILE *trx
, size_t cur_offset
, size_t alignment
) {
349 if (cur_offset
& (alignment
- 1)) {
350 size_t length
= alignment
- (cur_offset
% alignment
);
351 return bcm4908img_create_append_zeros(trx
, length
);
357 static int bcm4908img_create(int argc
, char **argv
) {
358 struct bcm4908img_tail tail
= {
359 .version
= cpu_to_le32(WFI_VERSION
),
360 .chip_id
= cpu_to_le32(0x4908),
361 .flash_type
= cpu_to_le32(WFI_NAND128_FLASH
),
362 .flags
= cpu_to_le32(WFI_FLAG_SUPPORTS_BTRM
),
364 uint32_t crc32
= 0xffffffff;
365 size_t cur_offset
= 0;
372 fprintf(stderr
, "No BCM4908 image pathname passed\n");
378 fp
= fopen(pathname
, "w+");
380 fprintf(stderr
, "Failed to open %s\n", pathname
);
386 while ((c
= getopt(argc
, argv
, "f:a:A:")) != -1) {
389 bytes
= bcm4908img_create_append_file(fp
, optarg
, &crc32
);
391 fprintf(stderr
, "Failed to append file %s\n", optarg
);
397 bytes
= bcm4908img_create_align(fp
, cur_offset
, strtol(optarg
, NULL
, 0));
399 fprintf(stderr
, "Failed to append zeros\n");
404 bytes
= strtol(optarg
, NULL
, 0) - cur_offset
;
406 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));
408 bytes
= bcm4908img_create_append_zeros(fp
, bytes
);
410 fprintf(stderr
, "Failed to append zeros\n");
420 tail
.crc32
= cpu_to_le32(crc32
);
422 bytes
= fwrite(&tail
, 1, sizeof(tail
), fp
);
423 if (bytes
!= sizeof(tail
)) {
424 fprintf(stderr
, "Failed to write BCM4908 image tail to %s\n", pathname
);
434 /**************************************************
436 **************************************************/
438 static void usage() {
441 printf("Checking a BCM4908 image:\n");
442 printf("\tbcm4908img check <file>\t\t\tcheck if images is valid\n");
444 printf("Creating a new BCM4908 image:\n");
445 printf("\tbcm4908img create <file> [options]\n");
446 printf("\t-f file\t\t\t\tadd data from specified file\n");
447 printf("\t-a alignment\t\t\tpad image with zeros to specified alignment\n");
448 printf("\t-A offset\t\t\t\tappend zeros until reaching specified offset\n");
451 int main(int argc
, char **argv
) {
453 if (!strcmp(argv
[1], "check"))
454 return bcm4908img_check(argc
, argv
);
455 else if (!strcmp(argv
[1], "create"))
456 return bcm4908img_create(argc
, argv
);