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 } 338
This page was automatically generated by LXR 0.3.1. • OpenWrt