1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
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)
27 #error "Unsupported endianness"
30 /* BCM4908 specific tail - appended to the firmware image */
31 struct bcm4908img_tail
{
39 /* Asus BCM4908 tail - placed at the end of BCM4908 firmware, right before BCM4908 tail */
40 struct bcm4908asus_tail
{
43 uint16_t extend_no_u16
;
44 char productid
[12]; /* The longest seen was 9 (e.g. GT-AC5300) */
46 uint32_t extend_no_u32
;
48 uint8_t ver_flags
; /* Version or flags (only seen values: 0x00 and 0x01) */
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 |....|
64 char *out_path
= NULL
;
66 static inline size_t bcm4908asus_min(size_t x
, size_t y
) {
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,
137 uint32_t bcm4908img_crc32(uint32_t crc
, uint8_t *buf
, size_t len
) {
139 crc
= crc32_tbl
[(crc
^ *buf
) & 0xff] ^ (crc
>> 8);
147 /**************************************************
149 **************************************************/
151 static int bcm4908asus_info(int argc
, char **argv
)
153 struct bcm4908asus_tail asus_tail
;
154 struct bcm4908img_tail img_tail
;
156 const char *pathname
;
157 size_t bytes
, length
;
166 fprintf(stderr
, "No BCM4908 Asus image pathname passed\n");
172 if (stat(pathname
, &st
)) {
173 fprintf(stderr
, "Failed to stat %s\n", pathname
);
178 fp
= fopen(pathname
, "r");
180 fprintf(stderr
, "Failed to open %s\n", pathname
);
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
);
193 fprintf(stderr
, "Failed to read from %s\n", pathname
);
198 if (fread(&asus_tail
, 1, sizeof(asus_tail
), fp
) != sizeof(asus_tail
)) {
199 fprintf(stderr
, "Failed to read BCM4908 Asus image tail\n");
203 crc32
= bcm4908img_crc32(crc32
, (uint8_t *)&asus_tail
, sizeof(asus_tail
));
205 if (fread(&img_tail
, 1, sizeof(img_tail
), fp
) != sizeof(img_tail
)) {
206 fprintf(stderr
, "Failed to read BCM4908 Asus image tail\n");
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
));
218 for (i
= 0; i
< sizeof(asus_tail
); i
++) {
219 if (((uint8_t *)&asus_tail
)[i
] != 0xff) {
225 fprintf(stderr
, "BCM4908 image doesn't contain Asus tail\n");
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
);
241 /**************************************************
243 **************************************************/
245 static void bcm4908asus_create_parse_options(int argc
, char **argv
, struct bcm4908asus_tail
*tail
)
251 while ((c
= getopt(argc
, argv
, "i:o:p:f:b:e:")) != -1) {
260 strncpy(tail
->productid
, optarg
, sizeof(tail
->productid
));
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
);
267 tmp16
= strtol(optarg
, NULL
, 0);
268 tail
->build_no
= cpu_to_le16(tmp16
);
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
);
280 static int bcm4908asus_create(int argc
, char **argv
)
282 struct bcm4908asus_tail asus_tail
= {};
283 struct bcm4908img_tail img_tail
= {};
296 /* Parse & validate arguments */
297 bcm4908asus_create_parse_options(argc
, argv
, &asus_tail
);
299 fprintf(stderr
, "No BCM4908 Asus image pathname passed\n");
304 /* Check input file: size, access, empty space for Asus tail */
306 if (stat(in_path
, &st
)) {
307 fprintf(stderr
, "Failed to stat %s\n", in_path
);
312 in
= fopen(in_path
, "r+");
314 fprintf(stderr
, "Failed to open %s\n", in_path
);
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
);
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");
335 /* Create new BCM4908 Asus image file if requested (otherwise input file will get modified) */
337 if (out_path
&& !(out
= fopen(out_path
, "w+"))) {
338 fprintf(stderr
, "Failed to open %s\n", out_path
);
343 /* Calculate CRC for data that doesn't get modified. Optionally copy input file if requested */
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
);
353 crc32_old
= bcm4908img_crc32(crc32_old
, buf
, bytes
);
358 fprintf(stderr
, "Failed to read from %s\n", in_path
);
363 crc32_new
= crc32_old
;
365 /* Finish calculating old checksum & verify it */
367 for (i
= 0; i
< sizeof(asus_tail
); i
++) {
370 crc32_old
= bcm4908img_crc32(crc32_old
, &val
, 1);
372 fseek(in
, sizeof(asus_tail
), SEEK_CUR
);
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
);
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
));
386 /* Write Asus tail & updated BCM4908 tail */
392 fseek(in
, -sizeof(asus_tail
) - sizeof(img_tail
), SEEK_CUR
);
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
);
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
);
417 static void usage() {
420 printf("Info about BCM4908 Asus image:\n");
421 printf("\tbcm4908asus info <file>\tget info about BCM4908 Asus image\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");
433 int main(int argc
, char **argv
) {
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
);