• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/firmware-utils/src/bcmblob.c

  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