pc1crypt: make decrypt/encrypt functions take void * as argument
[project/firmware-utils.git] / 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 }