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

Sources/firmware-utils/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 channels_2ghz;
 56         uint32_t channels_5ghz;
 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 struct bcmclm_channels_lookup {
106         uint32_t num;
107         uint32_t offset;
108 };
109 
110 struct bcmclm_channels_range {
111         uint8_t first;
112         uint8_t last;
113         uint8_t unknown;
114 };
115 
116 /* Parsed info */
117 
118 struct bcmclm_info {
119         struct bcmclm_header header;
120         struct bcmclm_lookup_table lookup_table;
121         size_t file_size;
122         size_t clm_offset;
123         size_t offsets_fixup;
124 };
125 
126 static inline size_t bcmclm_min(size_t x, size_t y)
127 {
128         return x < y ? x : y;
129 }
130 
131 /**************************************************
132  * Helpers
133  **************************************************/
134 
135 static FILE *bcmclm_open(const char *pathname, const char *mode)
136 {
137         struct stat st;
138 
139         if (pathname)
140                 return fopen(pathname, mode);
141 
142         if (isatty(fileno(stdin))) {
143                 fprintf(stderr, "Reading from TTY stdin is unsupported\n");
144                 return NULL;
145         }
146 
147         if (fstat(fileno(stdin), &st)) {
148                 fprintf(stderr, "Failed to fstat stdin: %d\n", -errno);
149                 return NULL;
150         }
151 
152         if (S_ISFIFO(st.st_mode)) {
153                 fprintf(stderr, "Reading from pipe stdin is unsupported\n");
154                 return NULL;
155         }
156 
157         return stdin;
158 }
159 
160 static void bcmclm_close(FILE *fp)
161 {
162         if (fp != stdin)
163                 fclose(fp);
164 }
165 
166 /**************************************************
167  * Existing CLM parser
168  **************************************************/
169 
170 static int bcmclm_search(FILE *fp, struct bcmclm_info *info)
171 {
172         uint8_t buf[1024];
173         size_t offset = 0;
174         size_t bytes;
175         int i;
176 
177         while ((bytes = fread(buf, 1, sizeof(buf), fp)) == sizeof(buf)) {
178                 for (i = 0; i < bytes - 12; i += 4) {
179                         uint32_t unk = le32_to_cpu(*(uint32_t *)(&buf[i + 8]));
180 
181                         if (!memcmp(&buf[i], BCMCLM_MAGIC, 8) && !(unk & 0xff00ffff)) {
182                                 info->clm_offset = offset + i;
183 
184                                 printf("Found CLM at offset 0x%zx\n", info->clm_offset);
185                                 printf("\n");
186 
187                                 return 0;
188                         }
189                 }
190 
191                 offset += bytes;
192         }
193 
194         return -ENOENT;
195 }
196 
197 static int bcmclm_parse(FILE *fp, struct bcmclm_info *info)
198 {
199         struct bcmclm_header *header = &info->header;
200         struct bcmclm_lookup_table *lookup_table = &info->lookup_table;
201         struct stat st;
202         int err = 0;
203 
204         /* File size */
205 
206         if (fstat(fileno(fp), &st)) {
207                 err = -errno;
208                 fprintf(stderr, "Failed to fstat: %d\n", err);
209                 return err;
210         }
211         info->file_size = st.st_size;
212 
213         /* Header */
214 
215         fseek(fp, info->clm_offset, SEEK_SET);
216 
217         if (fread(header, 1, sizeof(*header), fp) != sizeof(*header)) {
218                 fprintf(stderr, "Failed to read CLM header\n");
219                 return -EIO;
220         }
221 
222         if (strncmp(header->magic, BCMCLM_MAGIC, 8)) {
223                 fprintf(stderr, "Invalid CLM header magic\n");
224                 return -EPROTO;
225         }
226 
227         info->offsets_fixup = info->clm_offset - le32_to_cpu(header->virtual_header_address);
228 
229         /* Lookup table */
230 
231         fseek(fp, le32_to_cpu(info->header.lookup_table_address) + info->offsets_fixup, SEEK_SET);
232 
233         if (fread(lookup_table, 1, sizeof(*lookup_table), fp) != sizeof(*lookup_table)) {
234                 fprintf(stderr, "Failed to read lookup table\n");
235                 return -EIO;
236         }
237 
238         return 0;
239 }
240 
241 /**************************************************
242  * Info
243  **************************************************/
244 
245 static void bcmclm_print_channels(FILE *fp, struct bcmclm_info *info, const char *band, size_t num, size_t offset)
246 {
247         struct bcmclm_channels_range *ranges;
248         size_t read;
249         int i;
250 
251         ranges = calloc(num, sizeof(struct bcmclm_channels_range));
252         if (!ranges) {
253                 return;
254         }
255 
256         fseek(fp, offset + info->offsets_fixup, SEEK_SET);
257 
258         read = fread(ranges, sizeof(struct bcmclm_channels_range), num, fp);
259         if (read != num) {
260                 goto out;
261         }
262 
263         for (i = 0; i < num; i++) {
264                 printf("%3s GHz band channels: %d - %d\n", band, le32_to_cpu(ranges[i].first), le32_to_cpu(ranges[i].last));
265         }
266 
267 out:
268         free(ranges);
269 }
270 
271 static void bcmclm_print_lookup_data(FILE *fp, struct bcmclm_info *info)
272 {
273         uint8_t buf[64];
274         size_t bytes;
275 
276         if (info->lookup_table.channels_2ghz) {
277                 struct bcmclm_channels_lookup channels;
278 
279                 fseek(fp, le32_to_cpu(info->lookup_table.channels_2ghz) + info->offsets_fixup, SEEK_SET);
280 
281                 bytes = fread(&channels, 1, sizeof(channels), fp);
282                 if (bytes == sizeof(channels)) {
283                         bcmclm_print_channels(fp, info, "2.4", le32_to_cpu(channels.num), le32_to_cpu(channels.offset));
284                 }
285         }
286 
287         if (info->lookup_table.channels_5ghz) {
288                 struct bcmclm_channels_lookup channels;
289 
290                 fseek(fp, le32_to_cpu(info->lookup_table.channels_5ghz) + info->offsets_fixup, SEEK_SET);
291 
292                 bytes = fread(&channels, 1, sizeof(channels), fp);
293                 if (bytes == sizeof(channels)) {
294                         bcmclm_print_channels(fp, info, "5", le32_to_cpu(channels.num), le32_to_cpu(channels.offset));
295                 }
296         }
297 
298         if (info->lookup_table.offset_creation_date) {
299                 fseek(fp, le32_to_cpu(info->lookup_table.offset_creation_date) + info->offsets_fixup, SEEK_SET);
300 
301                 bytes = fread(buf, 1, sizeof(buf), fp);
302                 if (bytes) {
303                         printf("Creation date: %s\n", buf);
304                 }
305         }
306 }
307 
308 static int bcmclm_info(int argc, char **argv)
309 {
310         struct bcmclm_info info = {};
311         const char *pathname = NULL;
312         int search = 0;
313         FILE *fp;
314         int c;
315         int err = 0;
316 
317         while ((c = getopt(argc, argv, "i:s")) != -1) {
318                 switch (c) {
319                 case 'i':
320                         pathname = optarg;
321                         break;
322                 case 's':
323                         search = 1;
324                         break;
325                 }
326         }
327 
328         fp = bcmclm_open(pathname, "r");
329         if (!fp) {
330                 fprintf(stderr, "Failed to open CLM\n");
331                 err = -EACCES;
332                 goto out;
333         }
334 
335         if (search) {
336                 err = bcmclm_search(fp, &info);
337                 if (err) {
338                         fprintf(stderr, "Failed to find CLM in input file\n");
339                         goto err_close;
340                 }
341         }
342 
343         err = bcmclm_parse(fp, &info);
344         if (err) {
345                 fprintf(stderr, "Failed to parse CLM\n");
346                 goto err_close;
347         }
348 
349         printf("API: %s\n", info.header.api);
350         printf("Compiler: %s\n", info.header.compiler);
351         printf("clm_import_ver: %s\n", info.header.clm_import_ver);
352         printf("Manufacturer: %s\n", info.header.manufacturer);
353         printf("\n");
354         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);
355         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);
356         printf("\n");
357 
358         bcmclm_print_lookup_data(fp, &info);
359 
360 err_close:
361         bcmclm_close(fp);
362 out:
363         return err;
364 }
365 
366 /**************************************************
367  * Start
368  **************************************************/
369 
370 static void usage()
371 {
372         printf("Usage:\n");
373         printf("\n");
374         printf("Info about CLM:\n");
375         printf("\tbcmclm info <options>\n");
376         printf("\t-i <file>\t\t\t\t\tinput CLM\n");
377         printf("\t-s\t\t\t\t\tsearch for CLM data in bigger file\n");
378         printf("\n");
379         printf("Examples:\n");
380         printf("\tbcmclm info -i x.clm\n");
381         printf("\tbcmclm info -s -i brcmfmac4366c-pcie.bin\n");
382 }
383 
384 int main(int argc, char **argv)
385 {
386         if (argc > 1) {
387                 optind++;
388                 if (!strcmp(argv[1], "info"))
389                         return bcmclm_info(argc, argv);
390         }
391 
392         usage();
393 
394         return 0;
395 }
396 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt