pc1crypt: make decrypt/encrypt functions take void * as argument
[project/firmware-utils.git] / src / bcmclm.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 BCMCLM_MAGIC "CLM DATA"
39
40 /* Raw data */
41
42 struct bcmclm_header {
43 char magic[8];
44 uint32_t unk0;
45 uint8_t unk1[2];
46 char api[20];
47 char compiler[10];
48 uint32_t virtual_header_address;
49 uint32_t lookup_table_address;
50 char clm_import_ver[30];
51 char manufacturer[22];
52 };
53
54 struct bcmclm_lookup_table {
55 uint32_t offset0;
56 uint32_t offset1;
57 uint32_t offset2;
58 uint32_t offset3;
59 uint32_t offset4;
60 uint32_t offset5;
61 uint32_t offset6;
62 uint32_t offset7;
63 uint32_t offset8;
64 uint32_t offset9;
65 uint32_t offset10;
66 uint32_t offset11;
67 uint32_t offset12;
68 uint32_t offset13;
69 uint32_t offset14;
70 uint32_t offset15;
71 uint32_t offset16;
72 uint32_t offset17;
73 uint32_t offset18;
74 uint32_t offset19;
75 uint32_t offset20;
76 uint32_t offset21;
77 uint32_t offset22;
78 uint32_t offset23;
79 uint32_t offset_creation_date;
80 uint32_t offset25;
81 uint32_t offset26;
82 uint32_t offset27;
83 uint32_t offset28;
84 uint32_t offset29;
85 uint32_t offset30;
86 uint32_t offset31;
87 uint32_t offset32;
88 uint32_t offset33;
89 uint32_t offset34;
90 uint32_t offset35;
91 uint32_t offset36;
92 uint32_t offset37;
93 uint32_t offset38;
94 uint32_t offset39;
95 uint32_t offset40;
96 uint32_t offset41;
97 uint32_t offset42;
98 uint32_t offset43;
99 uint32_t offset44;
100 uint32_t offset45;
101 uint32_t offset46;
102 uint32_t offset47;
103 };
104
105 /* Parsed info */
106
107 struct bcmclm_info {
108 struct bcmclm_header header;
109 struct bcmclm_lookup_table lookup_table;
110 size_t file_size;
111 size_t clm_offset;
112 size_t offsets_fixup;
113 };
114
115 static inline size_t bcmclm_min(size_t x, size_t y)
116 {
117 return x < y ? x : y;
118 }
119
120 /**************************************************
121 * Helpers
122 **************************************************/
123
124 static FILE *bcmclm_open(const char *pathname, const char *mode)
125 {
126 struct stat st;
127
128 if (pathname)
129 return fopen(pathname, mode);
130
131 if (isatty(fileno(stdin))) {
132 fprintf(stderr, "Reading from TTY stdin is unsupported\n");
133 return NULL;
134 }
135
136 if (fstat(fileno(stdin), &st)) {
137 fprintf(stderr, "Failed to fstat stdin: %d\n", -errno);
138 return NULL;
139 }
140
141 if (S_ISFIFO(st.st_mode)) {
142 fprintf(stderr, "Reading from pipe stdin is unsupported\n");
143 return NULL;
144 }
145
146 return stdin;
147 }
148
149 static void bcmclm_close(FILE *fp)
150 {
151 if (fp != stdin)
152 fclose(fp);
153 }
154
155 /**************************************************
156 * Existing CLM parser
157 **************************************************/
158
159 static int bcmclm_search(FILE *fp, struct bcmclm_info *info)
160 {
161 uint8_t buf[1024];
162 size_t offset = 0;
163 size_t bytes;
164 int i;
165
166 while ((bytes = fread(buf, 1, sizeof(buf), fp)) == sizeof(buf)) {
167 for (i = 0; i < bytes - 12; i += 4) {
168 uint32_t unk = le32_to_cpu(*(uint32_t *)(&buf[i + 8]));
169
170 if (!memcmp(&buf[i], BCMCLM_MAGIC, 8) && !(unk & 0xff00ffff)) {
171 info->clm_offset = offset + i;
172
173 printf("Found CLM at offset 0x%zx\n", info->clm_offset);
174 printf("\n");
175
176 return 0;
177 }
178 }
179
180 offset += bytes;
181 }
182
183 return -ENOENT;
184 }
185
186 static int bcmclm_parse(FILE *fp, struct bcmclm_info *info)
187 {
188 struct bcmclm_header *header = &info->header;
189 struct bcmclm_lookup_table *lookup_table = &info->lookup_table;
190 struct stat st;
191 int err = 0;
192
193 /* File size */
194
195 if (fstat(fileno(fp), &st)) {
196 err = -errno;
197 fprintf(stderr, "Failed to fstat: %d\n", err);
198 return err;
199 }
200 info->file_size = st.st_size;
201
202 /* Header */
203
204 fseek(fp, info->clm_offset, SEEK_SET);
205
206 if (fread(header, 1, sizeof(*header), fp) != sizeof(*header)) {
207 fprintf(stderr, "Failed to read CLM header\n");
208 return -EIO;
209 }
210
211 if (strncmp(header->magic, BCMCLM_MAGIC, 8)) {
212 fprintf(stderr, "Invalid CLM header magic\n");
213 return -EPROTO;
214 }
215
216 info->offsets_fixup = info->clm_offset - le32_to_cpu(header->virtual_header_address);
217
218 /* Lookup table */
219
220 fseek(fp, le32_to_cpu(info->header.lookup_table_address) + info->offsets_fixup, SEEK_SET);
221
222 if (fread(lookup_table, 1, sizeof(*lookup_table), fp) != sizeof(*lookup_table)) {
223 fprintf(stderr, "Failed to read lookup table\n");
224 return -EIO;
225 }
226
227 return 0;
228 }
229
230 /**************************************************
231 * Info
232 **************************************************/
233
234 static void bcmclm_print_lookup_data(FILE *fp, struct bcmclm_info *info)
235 {
236 uint8_t buf[64];
237 size_t bytes;
238
239 if (info->lookup_table.offset_creation_date) {
240 printf("\n");
241
242 fseek(fp, le32_to_cpu(info->lookup_table.offset_creation_date) + info->offsets_fixup, SEEK_SET);
243
244 bytes = fread(buf, 1, sizeof(buf), fp);
245 if (bytes) {
246 printf("Creation date: %s\n", buf);
247 }
248 }
249 }
250
251 static int bcmclm_info(int argc, char **argv)
252 {
253 struct bcmclm_info info = {};
254 const char *pathname = NULL;
255 int search = 0;
256 FILE *fp;
257 int c;
258 int err = 0;
259
260 while ((c = getopt(argc, argv, "i:s")) != -1) {
261 switch (c) {
262 case 'i':
263 pathname = optarg;
264 break;
265 case 's':
266 search = 1;
267 break;
268 }
269 }
270
271 fp = bcmclm_open(pathname, "r");
272 if (!fp) {
273 fprintf(stderr, "Failed to open CLM\n");
274 err = -EACCES;
275 goto out;
276 }
277
278 if (search) {
279 err = bcmclm_search(fp, &info);
280 if (err) {
281 fprintf(stderr, "Failed to find CLM in input file\n");
282 goto err_close;
283 }
284 }
285
286 err = bcmclm_parse(fp, &info);
287 if (err) {
288 fprintf(stderr, "Failed to parse CLM\n");
289 goto err_close;
290 }
291
292 printf("API: %s\n", info.header.api);
293 printf("Compiler: %s\n", info.header.compiler);
294 printf("clm_import_ver: %s\n", info.header.clm_import_ver);
295 printf("Manufacturer: %s\n", info.header.manufacturer);
296 printf("\n");
297 printf("Virtual header address: 0x%08x (real: 0x%zx)\n", le32_to_cpu(info.header.virtual_header_address), le32_to_cpu(info.header.virtual_header_address) + info.offsets_fixup);
298 printf("Virtual lookup table address: 0x%08x (real: 0x%zx)\n", le32_to_cpu(info.header.lookup_table_address), le32_to_cpu(info.header.lookup_table_address) + info.offsets_fixup);
299
300 bcmclm_print_lookup_data(fp, &info);
301
302 err_close:
303 bcmclm_close(fp);
304 out:
305 return err;
306 }
307
308 /**************************************************
309 * Start
310 **************************************************/
311
312 static void usage()
313 {
314 printf("Usage:\n");
315 printf("\n");
316 printf("Info about CLM:\n");
317 printf("\tbcmclm info <options>\n");
318 printf("\t-i <file>\t\t\t\t\tinput CLM\n");
319 printf("\t-s\t\t\t\t\tsearch for CLM data in bigger file\n");
320 printf("\n");
321 printf("Examples:\n");
322 printf("\tbcmclm info -i x.clm\n");
323 printf("\tbcmclm info -s -i brcmfmac4366c-pcie.bin\n");
324 }
325
326 int main(int argc, char **argv)
327 {
328 if (argc > 1) {
329 optind++;
330 if (!strcmp(argv[1], "info"))
331 return bcmclm_info(argc, argv);
332 }
333
334 usage();
335
336 return 0;
337 }