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