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

Sources/firmware-utils/src/bcm4908asus.c

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
  4  */
  5 
  6 #include <byteswap.h>
  7 #include <errno.h>
  8 #include <stdbool.h>
  9 #include <stdint.h>
 10 #include <stdio.h>
 11 #include <stdlib.h>
 12 #include <string.h>
 13 #include <sys/stat.h>
 14 #include <unistd.h>
 15 
 16 #if __BYTE_ORDER == __BIG_ENDIAN
 17 #define cpu_to_le32(x)  bswap_32(x)
 18 #define le32_to_cpu(x)  bswap_32(x)
 19 #define cpu_to_le16(x)  bswap_16(x)
 20 #define le16_to_cpu(x)  bswap_16(x)
 21 #elif __BYTE_ORDER == __LITTLE_ENDIAN
 22 #define cpu_to_le32(x)  (x)
 23 #define le32_to_cpu(x)  (x)
 24 #define cpu_to_le16(x)  (x)
 25 #define le16_to_cpu(x)  (x)
 26 #else
 27 #error "Unsupported endianness"
 28 #endif
 29 
 30 /* BCM4908 specific tail - appended to the firmware image */
 31 struct bcm4908img_tail {
 32         uint32_t crc32;
 33         uint32_t unk1;
 34         uint32_t family;
 35         uint32_t unk2;
 36         uint32_t unk3;
 37 };
 38 
 39 /* Asus BCM4908 tail - placed at the end of BCM4908 firmware, right before BCM4908 tail */
 40 struct bcm4908asus_tail {
 41         uint8_t fw_ver[4];
 42         uint16_t build_no;
 43         uint16_t extend_no_u16;
 44         char productid[12];     /* The longest seen was 9 (e.g. GT-AC5300) */
 45         uint8_t unused1[8];
 46         uint32_t extend_no_u32;
 47         uint8_t unused2[31];
 48         uint8_t ver_flags;      /* Version or flags (only seen values: 0x00 and 0x01) */
 49 };
 50 
 51 /*
 52  * Example:
 53  *
 54  * 0053ffb0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
 55  * 0053ffc0  03 00 00 04 80 01 94 52  47 54 2d 41 43 35 33 30  |.......RGT-AC530|
 56  * 0053ffd0  30 00 00 00 00 00 00 00  00 00 00 00 94 52 00 00  |0............R..|
 57  * 0053ffe0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 58  * 0053fff0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 01  |................|
 59  * 00540000  c4 20 e6 72 32 57 00 00  08 49 00 00 03 00 00 00  |. .r2W...I......|
 60  * 00540010  02 00 00 00                                       |....|
 61  */
 62 
 63 char *in_path = NULL;
 64 char *out_path = NULL;
 65 
 66 static inline size_t bcm4908asus_min(size_t x, size_t y) {
 67         return x < y ? x : y;
 68 }
 69 
 70 static const uint32_t crc32_tbl[] = {
 71         0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
 72         0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
 73         0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
 74         0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
 75         0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
 76         0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
 77         0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
 78         0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
 79         0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
 80         0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
 81         0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
 82         0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
 83         0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
 84         0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
 85         0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
 86         0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
 87         0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
 88         0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
 89         0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
 90         0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
 91         0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
 92         0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
 93         0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
 94         0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
 95         0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
 96         0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
 97         0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
 98         0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
 99         0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
100         0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
101         0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
102         0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
103         0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
104         0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
105         0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
106         0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
107         0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
108         0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
109         0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
110         0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
111         0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
112         0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
113         0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
114         0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
115         0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
116         0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
117         0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
118         0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
119         0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
120         0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
121         0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
122         0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
123         0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
124         0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
125         0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
126         0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
127         0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
128         0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
129         0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
130         0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
131         0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
132         0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
133         0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
134         0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
135 };
136 
137 uint32_t bcm4908img_crc32(uint32_t crc, uint8_t *buf, size_t len) {
138         while (len) {
139                 crc = crc32_tbl[(crc ^ *buf) & 0xff] ^ (crc >> 8);
140                 buf++;
141                 len--;
142         }
143 
144         return crc;
145 }
146 
147 /**************************************************
148  * Info
149  **************************************************/
150 
151 static int bcm4908asus_info(int argc, char **argv)
152 {
153         struct bcm4908asus_tail asus_tail;
154         struct bcm4908img_tail img_tail;
155         struct stat st;
156         const char *pathname;
157         size_t bytes, length;
158         uint8_t buf[1024];
159         uint32_t crc32;
160         bool empty;
161         FILE *fp;
162         int i;
163         int err = 0;
164 
165         if (argc < 3) {
166                 fprintf(stderr, "No BCM4908 Asus image pathname passed\n");
167                 err = -EINVAL;
168                 goto out;
169         }
170         pathname = argv[2];
171 
172         if (stat(pathname, &st)) {
173                 fprintf(stderr, "Failed to stat %s\n", pathname);
174                 err = -EIO;
175                 goto out;
176         }
177 
178         fp = fopen(pathname, "r");
179         if (!fp) {
180                 fprintf(stderr, "Failed to open %s\n", pathname);
181                 err = -EACCES;
182                 goto out;
183         }
184 
185         crc32 = 0xffffffff;
186         length = st.st_size - sizeof(asus_tail) - sizeof(img_tail);
187         while (length && (bytes = fread(buf, 1, bcm4908asus_min(sizeof(buf), length), fp)) > 0) {
188                 crc32 = bcm4908img_crc32(crc32, buf, bytes);
189                 length -= bytes;
190         }
191 
192         if (length) {
193                 fprintf(stderr, "Failed to read from %s\n", pathname);
194                 err = -EIO;
195                 goto err_close;
196         }
197 
198         if (fread(&asus_tail, 1, sizeof(asus_tail), fp) != sizeof(asus_tail)) {
199                 fprintf(stderr, "Failed to read BCM4908 Asus image tail\n");
200                 err = -EIO;
201                 goto err_close;
202         }
203         crc32 = bcm4908img_crc32(crc32, (uint8_t *)&asus_tail, sizeof(asus_tail));
204 
205         if (fread(&img_tail, 1, sizeof(img_tail), fp) != sizeof(img_tail)) {
206                 fprintf(stderr, "Failed to read BCM4908 Asus image tail\n");
207                 err = -EIO;
208                 goto err_close;
209         }
210 
211         if (crc32 != le32_to_cpu(img_tail.crc32)) {
212                 fprintf(stderr, "Invalid crc32 (calculated 0x%08x expected 0x%08x)\n", crc32, le32_to_cpu(img_tail.crc32));
213                 err =  -EINVAL;
214                 goto err_close;
215         }
216 
217         empty = true;
218         for (i = 0; i < sizeof(asus_tail); i++) {
219                 if (((uint8_t *)&asus_tail)[i] != 0xff) {
220                         empty = false;
221                         break;
222                 }
223         }
224         if (empty) {
225                 fprintf(stderr, "BCM4908 image doesn't contain Asus tail\n");
226                 err =  -EINVAL;
227                 goto err_close;
228         }
229 
230         printf("Firmware version:\t%u.%u.%u.%u\n", asus_tail.fw_ver[0], asus_tail.fw_ver[1], asus_tail.fw_ver[2], asus_tail.fw_ver[3]);
231         printf("Build number:\t\t%u\n", le16_to_cpu(asus_tail.build_no));
232         printf("Extended number:\t%u\n", asus_tail.ver_flags & 0x1 ? le32_to_cpu(asus_tail.extend_no_u32) : le16_to_cpu(asus_tail.extend_no_u16));
233         printf("Product ID:\t\t%s\n", asus_tail.productid);
234 
235 err_close:
236         fclose(fp);
237 out:
238         return err;
239 }
240 
241 /**************************************************
242  * Create
243  **************************************************/
244 
245 static void bcm4908asus_create_parse_options(int argc, char **argv, struct bcm4908asus_tail *tail)
246 {
247         uint32_t tmp32;
248         uint16_t tmp16;
249         int c;
250 
251         while ((c = getopt(argc, argv, "i:o:p:f:b:e:")) != -1) {
252                 switch (c) {
253                 case 'i':
254                         in_path = optarg;
255                         break;
256                 case 'o':
257                         out_path = optarg;
258                         break;
259                 case 'p':
260                         strncpy(tail->productid, optarg, sizeof(tail->productid));
261                         break;
262                 case 'f':
263                         if (sscanf(optarg, "%hhu.%hhu.%hhu.%hhu", &(tail->fw_ver[0]), &tail->fw_ver[1], &tail->fw_ver[2], &tail->fw_ver[3]) != 4)
264                                 fprintf(stderr, "Version %s doesn't match suppored 4-digits format\n", optarg);
265                         break;
266                 case 'b':
267                         tmp16 = strtol(optarg, NULL, 0);
268                         tail->build_no = cpu_to_le16(tmp16);
269                         break;
270                 case 'e':
271                         tmp32 = strtol(optarg, NULL, 0);
272                         tail->ver_flags = 0x01;
273                         tail->extend_no_u32 = cpu_to_le32(tmp32);
274                         tail->extend_no_u16 = cpu_to_le16((uint16_t)tmp32);
275                         break;
276                 }
277         }
278 }
279 
280 static int bcm4908asus_create(int argc, char **argv)
281 {
282         struct bcm4908asus_tail asus_tail = {};
283         struct bcm4908img_tail img_tail = {};
284         struct stat st;
285         uint32_t crc32_old;
286         uint32_t crc32_new;
287         uint8_t buf[1024];
288         FILE *out = NULL;
289         FILE *in = NULL;
290         size_t length;
291         size_t bytes;
292         FILE *fp;
293         int i;
294         int err = 0;
295 
296         /* Parse & validate arguments */
297         bcm4908asus_create_parse_options(argc, argv, &asus_tail);
298         if (!in_path) {
299                 fprintf(stderr, "No BCM4908 Asus image pathname passed\n");
300                 err = -EINVAL;
301                 goto err;
302         }
303 
304         /* Check input file: size, access, empty space for Asus tail */
305 
306         if (stat(in_path, &st)) {
307                 fprintf(stderr, "Failed to stat %s\n", in_path);
308                 err = -EIO;
309                 goto err;
310         }
311 
312         in = fopen(in_path, "r+");
313         if (!in) {
314                 fprintf(stderr, "Failed to open %s\n", in_path);
315                 err = -EIO;
316                 goto err;
317         }
318 
319         length = st.st_size - sizeof(asus_tail) - sizeof(img_tail);
320         fseek(in, length, SEEK_SET);
321         if (fread(buf, 1, sizeof(asus_tail), in) != sizeof(asus_tail)) {
322                 fprintf(stderr, "Failed to read BCM4908 image from %s\n", in_path);
323                 err = -EIO;
324                 goto err;
325         }
326         for (i = 0; i < sizeof(asus_tail); i++) {
327                 if (buf[i] != 0xff) {
328                         fprintf(stderr, "Input BCM4908 image doesn't have empty 64 B tail\n");
329                         err = -ENOSPC;
330                         goto err;
331                 }
332         }
333         rewind(in);
334 
335         /* Create new BCM4908 Asus image file if requested (otherwise input file will get modified) */
336 
337         if (out_path && !(out = fopen(out_path, "w+"))) {
338                 fprintf(stderr, "Failed to open %s\n", out_path);
339                 err = -EIO;
340                 goto err;
341         }
342 
343         /* Calculate CRC for data that doesn't get modified. Optionally copy input file if requested */
344 
345         crc32_old = 0xffffffff;
346         length = st.st_size - sizeof(asus_tail) - sizeof(img_tail);
347         while (length && (bytes = fread(buf, 1, bcm4908asus_min(sizeof(buf), length), in)) > 0) {
348                 if (out && fwrite(buf, 1, bytes, out) != bytes) {
349                         fprintf(stderr, "Failed to write %zu B to %s\n", bytes, out_path);
350                         err = -EIO;
351                         goto err;
352                 }
353                 crc32_old = bcm4908img_crc32(crc32_old, buf, bytes);
354                 length -= bytes;
355         }
356 
357         if (length) {
358                 fprintf(stderr, "Failed to read from %s\n", in_path);
359                 err = -EIO;
360                 goto err;
361         }
362 
363         crc32_new = crc32_old;
364 
365         /* Finish calculating old checksum & verify it */
366 
367         for (i = 0; i < sizeof(asus_tail); i++) {
368                 uint8_t val = 0xff;
369 
370                 crc32_old = bcm4908img_crc32(crc32_old, &val, 1);
371         }
372         fseek(in, sizeof(asus_tail), SEEK_CUR);
373 
374         if (fread(&img_tail, 1, sizeof(img_tail), in) != sizeof(img_tail)) {
375                 fprintf(stderr, "Failed to read BCM4908 image tail from %s\n", in_path);
376                 err = -EIO;
377                 goto err;
378         }
379 
380         if (crc32_old != le32_to_cpu(img_tail.crc32)) {
381                 fprintf(stderr, "Invalid data crc32: calculated 0x%08x instead of 0x%08x\n", crc32_old, le32_to_cpu(img_tail.crc32));
382                 err =  -EPROTO;
383                 goto err;
384         }
385 
386         /* Write Asus tail & updated BCM4908 tail */
387 
388         if (out) {
389                 fp = out;
390         } else {
391                 fp = in;
392                 fseek(in, -sizeof(asus_tail) - sizeof(img_tail), SEEK_CUR);
393         }
394 
395         if (fwrite(&asus_tail, 1, sizeof(asus_tail), fp) != sizeof(asus_tail)) {
396                 fprintf(stderr, "Failed to write BCM4908 image Asus tail to %s\n", out_path);
397                 err = -EIO;
398                 goto err;
399         }
400 
401         crc32_new = bcm4908img_crc32(crc32_new, (uint8_t *)&asus_tail, sizeof(asus_tail));
402         img_tail.crc32 = cpu_to_le32(crc32_new);
403         if (fwrite(&img_tail, 1, sizeof(img_tail), fp) != sizeof(img_tail)) {
404                 fprintf(stderr, "Failed to write BCM4908 image tail to %s\n", out_path);
405                 err = -EIO;
406                 goto err;
407         }
408 
409 err:
410         if (out)
411                 fclose(out);
412         if (in)
413                 fclose(in);
414         return err;
415 }
416 
417 static void usage() {
418         printf("Usage:\n");
419         printf("\n");
420         printf("Info about BCM4908 Asus image:\n");
421         printf("\tbcm4908asus info <file>\tget info about BCM4908 Asus image\n");
422         printf("\n");
423         printf("Create a BCM4908 Asus image:\n");
424         printf("\tbcm4908asus create\tinsert Asus info into BCM4908 image\n");
425         printf("\t-i file\t\t\t\tinput BCM4908 image file (required)\n");
426         printf("\t-o file\t\t\t\toutput BCM4908 Asus image file\n");
427         printf("\t-p productid\t\t\tproduct (device) ID\n");
428         printf("\t-f firmware version\t\tfirmware version formatted with 4 digits like: 1.2.3.4\n");
429         printf("\t-b build number\t\tbuild number (e.g. 380, 382, 384)\n");
430         printf("\t-e extend number\t\textended number (e.g. 21140, 81622, 81695, 82037)\n");
431 }
432 
433 int main(int argc, char **argv) {
434         if (argc > 1) {
435                 if (!strcmp(argv[1], "info"))
436                         return bcm4908asus_info(argc, argv);
437                 else if (!strcmp(argv[1], "create"))
438                         return bcm4908asus_create(argc, argv);
439         }
440 
441         usage();
442 
443         return 0;
444 }
445 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt