1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2023 Rafał Miłecki <rafal@milecki.pl> 4 */ 5 6 #include <byteswap.h> 7 #include <endian.h> 8 #include <errno.h> 9 #include <stdbool.h> 10 #include <stddef.h> 11 #include <stdint.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <sys/stat.h> 16 #include <unistd.h> 17 18 #if !defined(__BYTE_ORDER) 19 #error "Unknown byte order" 20 #endif 21 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) 32 #else 33 #error "Unsupported endianness" 34 #endif 35 36 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 37 38 #define BCMBLOB_MAGIC "BLOB" 39 40 /* Raw data */ 41 42 struct bcmblob_entry { 43 uint32_t unk0; 44 uint32_t offset; 45 uint32_t size; 46 uint32_t crc32; 47 uint32_t unk1; 48 }; 49 50 struct bcmblob_header { 51 char magic[4]; 52 uint32_t hdr_len; 53 uint32_t crc32; 54 uint32_t unk0; 55 uint32_t unk1; 56 struct bcmblob_entry entries[2]; 57 }; 58 59 /* Parsed info */ 60 61 struct bcmblob_entry_info { 62 struct bcmblob_entry header; 63 size_t offset; 64 size_t size; 65 uint32_t crc32; 66 }; 67 68 struct bcmblob_info { 69 struct bcmblob_header header; 70 struct bcmblob_entry_info entries[2]; 71 size_t file_size; 72 uint32_t crc32; 73 }; 74 75 static inline size_t bcmblob_min(size_t x, size_t y) 76 { 77 return x < y ? x : y; 78 } 79 80 /************************************************** 81 * CRC32 82 **************************************************/ 83 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, 149 }; 150 151 uint32_t bcmblob_crc32(uint32_t crc, const void *buf, size_t len) 152 { 153 const uint8_t *in = buf; 154 155 while (len) { 156 crc = crc32_tbl[(crc ^ *in) & 0xff] ^ (crc >> 8); 157 in++; 158 len--; 159 } 160 161 return crc; 162 } 163 164 /************************************************** 165 * Helpers 166 **************************************************/ 167 168 static FILE *bcmblob_open(const char *pathname, const char *mode) 169 { 170 struct stat st; 171 172 if (pathname) 173 return fopen(pathname, mode); 174 175 if (isatty(fileno(stdin))) { 176 fprintf(stderr, "Reading from TTY stdin is unsupported\n"); 177 return NULL; 178 } 179 180 if (fstat(fileno(stdin), &st)) { 181 fprintf(stderr, "Failed to fstat stdin: %d\n", -errno); 182 return NULL; 183 } 184 185 if (S_ISFIFO(st.st_mode)) { 186 fprintf(stderr, "Reading from pipe stdin is unsupported\n"); 187 return NULL; 188 } 189 190 return stdin; 191 } 192 193 static void bcmblob_close(FILE *fp) 194 { 195 if (fp != stdin) 196 fclose(fp); 197 } 198 199 /************************************************** 200 * Existing BLOB parser 201 **************************************************/ 202 203 static int bcmblob_parse(FILE *fp, struct bcmblob_info *info) 204 { 205 struct bcmblob_header *header = &info->header; 206 struct stat st; 207 uint8_t buf[1024]; 208 size_t length; 209 size_t bytes; 210 int i; 211 int err = 0; 212 213 memset(info, 0, sizeof(*info)); 214 215 /* File size */ 216 217 if (fstat(fileno(fp), &st)) { 218 err = -errno; 219 fprintf(stderr, "Failed to fstat: %d\n", err); 220 return err; 221 } 222 info->file_size = st.st_size; 223 224 /* Header */ 225 226 if (fread(header, 1, sizeof(*header), fp) != sizeof(*header)) { 227 fprintf(stderr, "Failed to read BLOB header\n"); 228 return -EIO; 229 } 230 231 if (strncmp(header->magic, BCMBLOB_MAGIC, 4)) { 232 fprintf(stderr, "Invalid BLOB header magic\n"); 233 return -EPROTO; 234 } 235 236 /* CRC32 */ 237 238 fseek(fp, 12, SEEK_SET); 239 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); 244 length -= bytes; 245 } 246 if (length) { 247 fprintf(stderr, "Failed to read last %zd B of data\n", length); 248 return -EIO; 249 } 250 info->crc32 ^= ~0U; 251 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)); 254 return -EPROTO; 255 } 256 257 /* Entries */ 258 259 for (i = 0; i < ARRAY_SIZE(header->entries); i++) { 260 struct bcmblob_entry_info *entry_info = &info->entries[i]; 261 262 entry_info->offset = le32_to_cpu(header->entries[i].offset); 263 entry_info->size = le32_to_cpu(header->entries[i].size); 264 265 fseek(fp, entry_info->offset, SEEK_SET); 266 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); 271 length -= bytes; 272 } 273 if (length) { 274 fprintf(stderr, "Failed to read last %zd B of data\n", length); 275 return -EIO; 276 } 277 entry_info->crc32 ^= ~0U; 278 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)); 281 return -EPROTO; 282 } 283 284 if (header->entries[i].unk0) { 285 fprintf(stderr, "Unexpected entry's unk0 value: 0x%08x\n", le32_to_cpu(header->entries[i].unk0)); 286 } 287 288 if (header->entries[i].unk1) { 289 fprintf(stderr, "Unexpected entry's unk1 value: 0x%08x\n", le32_to_cpu(header->entries[i].unk1)); 290 } 291 } 292 293 /* Verify */ 294 295 if (le32_to_cpu(header->unk0) != 1) { 296 fprintf(stderr, "Unexpected unk0 value: 0x%08x\n", le32_to_cpu(header->unk0)); 297 } 298 299 if (le32_to_cpu(header->unk1) != 2) { 300 fprintf(stderr, "Unexpected unk1 value: 0x%08x\n", le32_to_cpu(header->unk1)); 301 } 302 303 return 0; 304 } 305 306 /************************************************** 307 * Info 308 **************************************************/ 309 310 static int bcmblob_info(int argc, char **argv) 311 { 312 struct bcmblob_info info; 313 const char *pathname = NULL; 314 FILE *fp; 315 int i; 316 int c; 317 int err = 0; 318 319 while ((c = getopt(argc, argv, "i:")) != -1) { 320 switch (c) { 321 case 'i': 322 pathname = optarg; 323 break; 324 } 325 } 326 327 fp = bcmblob_open(pathname, "r"); 328 if (!fp) { 329 fprintf(stderr, "Failed to open BLOB\n"); 330 err = -EACCES; 331 goto out; 332 } 333 334 err = bcmblob_parse(fp, &info); 335 if (err) { 336 fprintf(stderr, "Failed to parse BLOB\n"); 337 goto err_close; 338 } 339 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]; 343 344 printf("[Entry %d] offset:0x%08zx size:0x%08zx crc32:0x%08x\n", i, entry_info->offset, entry_info->size, entry_info->crc32); 345 } 346 347 err_close: 348 bcmblob_close(fp); 349 out: 350 return err; 351 } 352 353 /************************************************** 354 * Extract 355 **************************************************/ 356 357 static int bcmblob_extract(int argc, char **argv) 358 { 359 struct bcmblob_entry_info *entry_info; 360 struct bcmblob_info info; 361 const char *pathname = NULL; 362 uint8_t buf[1024]; 363 size_t size = 0; 364 int index = -1; 365 size_t bytes; 366 FILE *fp; 367 int c; 368 int err = 0; 369 370 while ((c = getopt(argc, argv, "i:n:")) != -1) { 371 switch (c) { 372 case 'i': 373 pathname = optarg; 374 break; 375 case 'n': 376 index = strtoul(optarg, NULL, 0); 377 break; 378 } 379 } 380 381 if (index < 0 || index >= ARRAY_SIZE(info.entries)) { 382 err = -EINVAL; 383 fprintf(stderr, "No valid entry index specified\n"); 384 goto err_out; 385 } 386 387 fp = bcmblob_open(pathname, "r"); 388 if (!fp) { 389 fprintf(stderr, "Failed to open BLOB\n"); 390 err = -EACCES; 391 goto err_out; 392 } 393 394 err = bcmblob_parse(fp, &info); 395 if (err) { 396 fprintf(stderr, "Failed to parse BLOB\n"); 397 goto err_close; 398 } 399 400 entry_info = &info.entries[index]; 401 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; 405 size -= bytes) { 406 fwrite(buf, bytes, 1, stdout); 407 } 408 if (size) { 409 err = -EIO; 410 fprintf(stderr, "Failed to read last %zd B of data\n", size); 411 goto err_close; 412 } 413 414 err_close: 415 bcmblob_close(fp); 416 err_out: 417 return err; 418 } 419 420 /************************************************** 421 * Start 422 **************************************************/ 423 424 static void usage() 425 { 426 printf("Usage:\n"); 427 printf("\n"); 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"); 431 printf("\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"); 436 printf("\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"); 440 } 441 442 int main(int argc, char **argv) 443 { 444 if (argc > 1) { 445 optind++; 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); 450 } 451 452 usage(); 453 454 return 0; 455 } 456
This page was automatically generated by LXR 0.3.1. • OpenWrt