ramips: fix USW-Flex reversed switch-port order
[openwrt/staging/hauke.git] / tools / 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 }