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 /**************************************************
152 * Existing firmware parser
153 **************************************************/
159 uint32_t kernel_chksum
;
160 uint32_t rootfs_chksum
;
163 uint32_t image_chksum
;
164 uint32_t header_chksum
;
168 static int bcm4908img_parse(FILE *fp
, struct bcm4908img_info
*info
) {
169 struct bcm4908img_tail
*tail
= &info
->tail
;
170 struct chk_header
*chk
;
177 memset(info
, 0, sizeof(*info
));
181 if (fstat(fileno(fp
), &st
)) {
183 fprintf(stderr
, "Failed to fstat: %d\n", err
);
186 info
->file_size
= st
.st_size
;
191 if (fread(buf
, 1, sizeof(buf
), fp
) != sizeof(buf
)) {
192 fprintf(stderr
, "Failed to read file header\n");
196 if (be32_to_cpu(chk
->magic
) == 0x2a23245e)
197 info
->vendor_header_size
= be32_to_cpu(chk
->header_len
);
201 fseek(fp
, info
->vendor_header_size
, SEEK_SET
);
203 info
->crc32
= 0xffffffff;
204 length
= info
->file_size
- info
->vendor_header_size
- sizeof(*tail
);
205 while (length
&& (bytes
= fread(buf
, 1, bcm4908img_min(sizeof(buf
), length
), fp
)) > 0) {
206 info
->crc32
= bcm4908img_crc32(info
->crc32
, buf
, bytes
);
210 fprintf(stderr
, "Failed to read last %zd B of data\n", length
);
216 if (fread(tail
, 1, sizeof(*tail
), fp
) != sizeof(*tail
)) {
217 fprintf(stderr
, "Failed to read BCM4908 image tail\n");
221 /* Standard validation */
223 if (info
->crc32
!= le32_to_cpu(tail
->crc32
)) {
224 fprintf(stderr
, "Invalid data crc32: 0x%08x instead of 0x%08x\n", info
->crc32
, le32_to_cpu(tail
->crc32
));
231 /**************************************************
233 **************************************************/
235 static int bcm4908img_check(int argc
, char **argv
) {
236 struct bcm4908img_info info
;
241 fprintf(stderr
, "No BCM4908 image pathname passed\n");
247 fp
= fopen(pathname
, "r");
249 fprintf(stderr
, "Failed to open %s\n", pathname
);
254 err
= bcm4908img_parse(fp
, &info
);
256 fprintf(stderr
, "Failed to parse %s\n", pathname
);
260 printf("Found a valid BCM4908 image (crc: 0x%08x)\n", info
.crc32
);
268 /**************************************************
270 **************************************************/
272 static ssize_t
bcm4908img_create_append_file(FILE *trx
, const char *in_path
, uint32_t *crc32
) {
278 in
= fopen(in_path
, "r");
280 fprintf(stderr
, "Failed to open %s\n", in_path
);
284 while ((bytes
= fread(buf
, 1, sizeof(buf
), in
)) > 0) {
285 if (fwrite(buf
, 1, bytes
, trx
) != bytes
) {
286 fprintf(stderr
, "Failed to write %zu B to %s\n", bytes
, pathname
);
290 *crc32
= bcm4908img_crc32(*crc32
, buf
, bytes
);
299 static ssize_t
bcm4908img_create_append_zeros(FILE *trx
, size_t length
) {
302 buf
= malloc(length
);
305 memset(buf
, 0, length
);
307 if (fwrite(buf
, 1, length
, trx
) != length
) {
308 fprintf(stderr
, "Failed to write %zu B to %s\n", length
, pathname
);
318 static ssize_t
bcm4908img_create_align(FILE *trx
, size_t cur_offset
, size_t alignment
) {
319 if (cur_offset
& (alignment
- 1)) {
320 size_t length
= alignment
- (cur_offset
% alignment
);
321 return bcm4908img_create_append_zeros(trx
, length
);
327 static int bcm4908img_create(int argc
, char **argv
) {
328 struct bcm4908img_tail tail
= {
329 .version
= cpu_to_le32(WFI_VERSION
),
330 .chip_id
= cpu_to_le32(0x4908),
331 .flash_type
= cpu_to_le32(WFI_NAND128_FLASH
),
332 .flags
= cpu_to_le32(WFI_FLAG_SUPPORTS_BTRM
),
334 uint32_t crc32
= 0xffffffff;
335 size_t cur_offset
= 0;
342 fprintf(stderr
, "No BCM4908 image pathname passed\n");
348 fp
= fopen(pathname
, "w+");
350 fprintf(stderr
, "Failed to open %s\n", pathname
);
356 while ((c
= getopt(argc
, argv
, "f:a:A:")) != -1) {
359 bytes
= bcm4908img_create_append_file(fp
, optarg
, &crc32
);
361 fprintf(stderr
, "Failed to append file %s\n", optarg
);
367 bytes
= bcm4908img_create_align(fp
, cur_offset
, strtol(optarg
, NULL
, 0));
369 fprintf(stderr
, "Failed to append zeros\n");
374 bytes
= strtol(optarg
, NULL
, 0) - cur_offset
;
376 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));
378 bytes
= bcm4908img_create_append_zeros(fp
, bytes
);
380 fprintf(stderr
, "Failed to append zeros\n");
390 tail
.crc32
= cpu_to_le32(crc32
);
392 bytes
= fwrite(&tail
, 1, sizeof(tail
), fp
);
393 if (bytes
!= sizeof(tail
)) {
394 fprintf(stderr
, "Failed to write BCM4908 image tail to %s\n", pathname
);
404 /**************************************************
406 **************************************************/
408 static void usage() {
411 printf("Checking a BCM4908 image:\n");
412 printf("\tbcm4908img check <file>\t\t\tcheck if images is valid\n");
414 printf("Creating a new BCM4908 image:\n");
415 printf("\tbcm4908img create <file> [options]\n");
416 printf("\t-f file\t\t\t\tadd data from specified file\n");
417 printf("\t-a alignment\t\t\tpad image with zeros to specified alignment\n");
418 printf("\t-A offset\t\t\t\tappend zeros until reaching specified offset\n");
421 int main(int argc
, char **argv
) {
423 if (!strcmp(argv
[1], "check"))
424 return bcm4908img_check(argc
, argv
);
425 else if (!strcmp(argv
[1], "create"))
426 return bcm4908img_create(argc
, argv
);