1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2023 Rafał Miłecki <rafal@milecki.pl>
18 #if !defined(__BYTE_ORDER)
19 #error "Unknown byte order"
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 #elif __BYTE_ORDER == __LITTLE_ENDIAN
28 #define cpu_to_le32(x) (x)
29 #define le32_to_cpu(x) (x)
30 #define cpu_to_be32(x) bswap_32(x)
31 #define be32_to_cpu(x) bswap_32(x)
33 #error "Unsupported endianness"
36 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
38 #define BCMBLOB_MAGIC "BLOB"
42 struct bcmblob_entry
{
50 struct bcmblob_header
{
56 struct bcmblob_entry entries
[2];
61 struct bcmblob_entry_info
{
62 struct bcmblob_entry header
;
69 struct bcmblob_header header
;
70 struct bcmblob_entry_info entries
[2];
75 static inline size_t bcmblob_min(size_t x
, size_t y
)
80 /**************************************************
82 **************************************************/
84 static const uint32_t crc32_tbl
[] = {
85 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
86 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
87 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
88 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
89 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
90 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
91 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
92 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
93 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
94 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
95 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
96 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
97 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
98 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
99 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
100 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
101 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
102 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
103 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
104 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
105 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
106 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
107 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
108 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
109 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
110 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
111 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
112 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
113 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
114 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
115 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
116 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
117 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
118 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
119 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
120 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
121 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
122 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
123 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
124 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
125 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
126 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
127 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
128 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
129 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
130 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
131 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
132 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
133 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
134 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
135 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
136 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
137 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
138 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
139 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
140 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
141 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
142 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
143 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
144 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
145 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
146 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
147 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
148 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
151 uint32_t bcmblob_crc32(uint32_t crc
, const void *buf
, size_t len
)
153 const uint8_t *in
= buf
;
156 crc
= crc32_tbl
[(crc
^ *in
) & 0xff] ^ (crc
>> 8);
164 /**************************************************
166 **************************************************/
168 static FILE *bcmblob_open(const char *pathname
, const char *mode
)
173 return fopen(pathname
, mode
);
175 if (isatty(fileno(stdin
))) {
176 fprintf(stderr
, "Reading from TTY stdin is unsupported\n");
180 if (fstat(fileno(stdin
), &st
)) {
181 fprintf(stderr
, "Failed to fstat stdin: %d\n", -errno
);
185 if (S_ISFIFO(st
.st_mode
)) {
186 fprintf(stderr
, "Reading from pipe stdin is unsupported\n");
193 static void bcmblob_close(FILE *fp
)
199 /**************************************************
200 * Existing BLOB parser
201 **************************************************/
203 static int bcmblob_parse(FILE *fp
, struct bcmblob_info
*info
)
205 struct bcmblob_header
*header
= &info
->header
;
213 memset(info
, 0, sizeof(*info
));
217 if (fstat(fileno(fp
), &st
)) {
219 fprintf(stderr
, "Failed to fstat: %d\n", err
);
222 info
->file_size
= st
.st_size
;
226 if (fread(header
, 1, sizeof(*header
), fp
) != sizeof(*header
)) {
227 fprintf(stderr
, "Failed to read BLOB header\n");
231 if (strncmp(header
->magic
, BCMBLOB_MAGIC
, 4)) {
232 fprintf(stderr
, "Invalid BLOB header magic\n");
238 fseek(fp
, 12, SEEK_SET
);
240 info
->crc32
= 0xffffffff;
241 length
= sizeof(struct bcmblob_header
) - 12;
242 while (length
&& (bytes
= fread(buf
, 1, bcmblob_min(sizeof(buf
), length
), fp
)) > 0) {
243 info
->crc32
= bcmblob_crc32(info
->crc32
, buf
, bytes
);
247 fprintf(stderr
, "Failed to read last %zd B of data\n", length
);
252 if (info
->crc32
!= le32_to_cpu(header
->crc32
)) {
253 fprintf(stderr
, "Invalid data crc32: 0x%08x instead of 0x%08x\n", info
->crc32
, le32_to_cpu(header
->crc32
));
259 for (i
= 0; i
< ARRAY_SIZE(header
->entries
); i
++) {
260 struct bcmblob_entry_info
*entry_info
= &info
->entries
[i
];
262 entry_info
->offset
= le32_to_cpu(header
->entries
[i
].offset
);
263 entry_info
->size
= le32_to_cpu(header
->entries
[i
].size
);
265 fseek(fp
, entry_info
->offset
, SEEK_SET
);
267 entry_info
->crc32
= 0xffffffff;
268 length
= entry_info
->size
;
269 while (length
&& (bytes
= fread(buf
, 1, bcmblob_min(sizeof(buf
), length
), fp
)) > 0) {
270 entry_info
->crc32
= bcmblob_crc32(entry_info
->crc32
, buf
, bytes
);
274 fprintf(stderr
, "Failed to read last %zd B of data\n", length
);
277 entry_info
->crc32
^= ~0U;
279 if (entry_info
->crc32
!= le32_to_cpu(header
->entries
[i
].crc32
)) {
280 fprintf(stderr
, "Invalid entry's crc32: 0x%08x instead of 0x%08x\n", entry_info
->crc32
, le32_to_cpu(header
->entries
[i
].crc32
));
284 if (header
->entries
[i
].unk0
) {
285 fprintf(stderr
, "Unexpected entry's unk0 value: 0x%08x\n", le32_to_cpu(header
->entries
[i
].unk0
));
288 if (header
->entries
[i
].unk1
) {
289 fprintf(stderr
, "Unexpected entry's unk1 value: 0x%08x\n", le32_to_cpu(header
->entries
[i
].unk1
));
295 if (le32_to_cpu(header
->unk0
) != 1) {
296 fprintf(stderr
, "Unexpected unk0 value: 0x%08x\n", le32_to_cpu(header
->unk0
));
299 if (le32_to_cpu(header
->unk1
) != 2) {
300 fprintf(stderr
, "Unexpected unk1 value: 0x%08x\n", le32_to_cpu(header
->unk1
));
306 /**************************************************
308 **************************************************/
310 static int bcmblob_info(int argc
, char **argv
)
312 struct bcmblob_info info
;
313 const char *pathname
= NULL
;
319 while ((c
= getopt(argc
, argv
, "i:")) != -1) {
327 fp
= bcmblob_open(pathname
, "r");
329 fprintf(stderr
, "Failed to open BLOB\n");
334 err
= bcmblob_parse(fp
, &info
);
336 fprintf(stderr
, "Failed to parse BLOB\n");
340 printf("CRC32: 0x%08x\n", info
.crc32
);
341 for (i
= 0; i
< ARRAY_SIZE(info
.entries
); i
++) {
342 struct bcmblob_entry_info
*entry_info
= &info
.entries
[i
];
344 printf("[Entry %d] offset:0x%08zx size:0x%08zx crc32:0x%08x\n", i
, entry_info
->offset
, entry_info
->size
, entry_info
->crc32
);
353 /**************************************************
355 **************************************************/
357 static int bcmblob_extract(int argc
, char **argv
)
359 struct bcmblob_entry_info
*entry_info
;
360 struct bcmblob_info info
;
361 const char *pathname
= NULL
;
370 while ((c
= getopt(argc
, argv
, "i:n:")) != -1) {
376 index
= strtoul(optarg
, NULL
, 0);
381 if (index
< 0 || index
>= ARRAY_SIZE(info
.entries
)) {
383 fprintf(stderr
, "No valid entry index specified\n");
387 fp
= bcmblob_open(pathname
, "r");
389 fprintf(stderr
, "Failed to open BLOB\n");
394 err
= bcmblob_parse(fp
, &info
);
396 fprintf(stderr
, "Failed to parse BLOB\n");
400 entry_info
= &info
.entries
[index
];
402 fseek(fp
, entry_info
->offset
, SEEK_SET
);
403 for (size
= entry_info
->size
;
404 size
&& (bytes
= fread(buf
, 1, bcmblob_min(sizeof(buf
), size
), fp
)) > 0;
406 fwrite(buf
, bytes
, 1, stdout
);
410 fprintf(stderr
, "Failed to read last %zd B of data\n", size
);
420 /**************************************************
422 **************************************************/
428 printf("Info about a BLOB:\n");
429 printf("\tbcmblob info <options>\n");
430 printf("\t-i <file>\t\t\t\t\tinput BLOB\n");
432 printf("Extracting from a BLOB:\n");
433 printf("\tbcmblob extract <options>\n");
434 printf("\t-i <file>\t\t\t\t\tinput BLOB\n");
435 printf("\t-n <index>\t\t\t\t\tindex of entry to extract\n");
437 printf("Examples:\n");
438 printf("\tbcmblob info -i cyfmac4354-sdio.clm_blob\n");
439 printf("\tbcmblob extract -i cyfmac4354-sdio.clm_blob -n 1 | hexdump -C\n");
442 int main(int argc
, char **argv
)
446 if (!strcmp(argv
[1], "info"))
447 return bcmblob_info(argc
, argv
);
448 else if (!strcmp(argv
[1], "extract"))
449 return bcmblob_extract(argc
, argv
);