firmware-utils: bcm4908img: detect Netgear vendor firmware
[openwrt/staging/wigyori.git] / tools / firmware-utils / src / bcm4908img.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 <endian.h>
8 #include <errno.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 !defined(__BYTE_ORDER)
17 #error "Unknown byte order"
18 #endif
19
20 #if __BYTE_ORDER == __BIG_ENDIAN
21 #define cpu_to_le32(x) bswap_32(x)
22 #define le32_to_cpu(x) bswap_32(x)
23 #define cpu_to_be32(x) (x)
24 #define be32_to_cpu(x) (x)
25 #elif __BYTE_ORDER == __LITTLE_ENDIAN
26 #define cpu_to_le32(x) (x)
27 #define le32_to_cpu(x) (x)
28 #define cpu_to_be32(x) bswap_32(x)
29 #define be32_to_cpu(x) bswap_32(x)
30 #else
31 #error "Unsupported endianness"
32 #endif
33
34 #define WFI_VERSION 0x00005732
35 #define WFI_VERSION_NAND_1MB_DATA 0x00005731
36
37 #define WFI_NOR_FLASH 1
38 #define WFI_NAND16_FLASH 2
39 #define WFI_NAND128_FLASH 3
40 #define WFI_NAND256_FLASH 4
41 #define WFI_NAND512_FLASH 5
42 #define WFI_NAND1024_FLASH 6
43 #define WFI_NAND2048_FLASH 7
44
45 #define WFI_FLAG_HAS_PMC 0x1
46 #define WFI_FLAG_SUPPORTS_BTRM 0x2
47
48 struct bcm4908img_tail {
49 uint32_t crc32;
50 uint32_t version;
51 uint32_t chip_id;
52 uint32_t flash_type;
53 uint32_t flags;
54 };
55
56 /* Info about BCM4908 image */
57 struct bcm4908img_info {
58 size_t file_size;
59 size_t vendor_header_size; /* Vendor header size */
60 uint32_t crc32; /* Calculated checksum */
61 struct bcm4908img_tail tail;
62 };
63
64 char *pathname;
65
66 static inline size_t bcm4908img_min(size_t x, size_t y) {
67 return x < y ? x : y;
68 }
69
70 /**************************************************
71 * CRC32
72 **************************************************/
73
74 static const uint32_t crc32_tbl[] = {
75 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
76 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
77 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
78 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
79 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
80 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
81 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
82 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
83 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
84 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
85 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
86 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
87 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
88 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
89 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
90 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
91 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
92 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
93 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
94 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
95 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
96 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
97 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
98 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
99 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
100 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
101 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
102 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
103 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
104 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
105 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
106 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
107 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
108 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
109 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
110 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
111 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
112 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
113 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
114 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
115 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
116 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
117 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
118 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
119 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
120 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
121 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
122 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
123 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
124 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
125 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
126 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
127 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
128 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
129 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
130 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
131 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
132 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
133 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
134 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
135 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
136 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
137 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
138 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
139 };
140
141 uint32_t bcm4908img_crc32(uint32_t crc, uint8_t *buf, size_t len) {
142 while (len) {
143 crc = crc32_tbl[(crc ^ *buf) & 0xff] ^ (crc >> 8);
144 buf++;
145 len--;
146 }
147
148 return crc;
149 }
150
151 /**************************************************
152 * Existing firmware parser
153 **************************************************/
154
155 struct chk_header {
156 uint32_t magic;
157 uint32_t header_len;
158 uint8_t reserved[8];
159 uint32_t kernel_chksum;
160 uint32_t rootfs_chksum;
161 uint32_t kernel_len;
162 uint32_t rootfs_len;
163 uint32_t image_chksum;
164 uint32_t header_chksum;
165 char board_id[0];
166 };
167
168 static int bcm4908img_parse(FILE *fp, struct bcm4908img_info *info) {
169 struct bcm4908img_tail *tail = &info->tail;
170 struct chk_header *chk;
171 struct stat st;
172 uint8_t buf[1024];
173 size_t length;
174 size_t bytes;
175 int err = 0;
176
177 memset(info, 0, sizeof(*info));
178
179 /* File size */
180
181 if (fstat(fileno(fp), &st)) {
182 err = -errno;
183 fprintf(stderr, "Failed to fstat: %d\n", err);
184 return err;
185 }
186 info->file_size = st.st_size;
187
188 /* Vendor formats */
189
190 rewind(fp);
191 if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) {
192 fprintf(stderr, "Failed to read file header\n");
193 return -EIO;
194 }
195 chk = (void *)buf;
196 if (be32_to_cpu(chk->magic) == 0x2a23245e)
197 info->vendor_header_size = be32_to_cpu(chk->header_len);
198
199 /* CRC32 */
200
201 fseek(fp, info->vendor_header_size, SEEK_SET);
202
203 info->crc32 = 0xffffffff;
204 length = info->file_size - info->vendor_header_size - sizeof(*tail);
205 while (length && (bytes = fread(buf, 1, bcm4908img_min(sizeof(buf), length), fp)) > 0) {
206 info->crc32 = bcm4908img_crc32(info->crc32, buf, bytes);
207 length -= bytes;
208 }
209 if (length) {
210 fprintf(stderr, "Failed to read last %zd B of data\n", length);
211 return -EIO;
212 }
213
214 /* Tail */
215
216 if (fread(tail, 1, sizeof(*tail), fp) != sizeof(*tail)) {
217 fprintf(stderr, "Failed to read BCM4908 image tail\n");
218 return -EIO;
219 }
220
221 /* Standard validation */
222
223 if (info->crc32 != le32_to_cpu(tail->crc32)) {
224 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", info->crc32, le32_to_cpu(tail->crc32));
225 return -EPROTO;
226 }
227
228 return 0;
229 }
230
231 /**************************************************
232 * Check
233 **************************************************/
234
235 static int bcm4908img_check(int argc, char **argv) {
236 struct bcm4908img_info info;
237 FILE *fp;
238 int err = 0;
239
240 if (argc < 3) {
241 fprintf(stderr, "No BCM4908 image pathname passed\n");
242 err = -EINVAL;
243 goto out;
244 }
245 pathname = argv[2];
246
247 fp = fopen(pathname, "r");
248 if (!fp) {
249 fprintf(stderr, "Failed to open %s\n", pathname);
250 err = -EACCES;
251 goto out;
252 }
253
254 err = bcm4908img_parse(fp, &info);
255 if (err) {
256 fprintf(stderr, "Failed to parse %s\n", pathname);
257 goto err_close;
258 }
259
260 printf("Found a valid BCM4908 image (crc: 0x%08x)\n", info.crc32);
261
262 err_close:
263 fclose(fp);
264 out:
265 return err;
266 }
267
268 /**************************************************
269 * Create
270 **************************************************/
271
272 static ssize_t bcm4908img_create_append_file(FILE *trx, const char *in_path, uint32_t *crc32) {
273 FILE *in;
274 size_t bytes;
275 ssize_t length = 0;
276 uint8_t buf[1024];
277
278 in = fopen(in_path, "r");
279 if (!in) {
280 fprintf(stderr, "Failed to open %s\n", in_path);
281 return -EACCES;
282 }
283
284 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
285 if (fwrite(buf, 1, bytes, trx) != bytes) {
286 fprintf(stderr, "Failed to write %zu B to %s\n", bytes, pathname);
287 length = -EIO;
288 break;
289 }
290 *crc32 = bcm4908img_crc32(*crc32, buf, bytes);
291 length += bytes;
292 }
293
294 fclose(in);
295
296 return length;
297 }
298
299 static ssize_t bcm4908img_create_append_zeros(FILE *trx, size_t length) {
300 uint8_t *buf;
301
302 buf = malloc(length);
303 if (!buf)
304 return -ENOMEM;
305 memset(buf, 0, length);
306
307 if (fwrite(buf, 1, length, trx) != length) {
308 fprintf(stderr, "Failed to write %zu B to %s\n", length, pathname);
309 free(buf);
310 return -EIO;
311 }
312
313 free(buf);
314
315 return length;
316 }
317
318 static ssize_t bcm4908img_create_align(FILE *trx, size_t cur_offset, size_t alignment) {
319 if (cur_offset & (alignment - 1)) {
320 size_t length = alignment - (cur_offset % alignment);
321 return bcm4908img_create_append_zeros(trx, length);
322 }
323
324 return 0;
325 }
326
327 static int bcm4908img_create(int argc, char **argv) {
328 struct bcm4908img_tail tail = {
329 .version = cpu_to_le32(WFI_VERSION),
330 .chip_id = cpu_to_le32(0x4908),
331 .flash_type = cpu_to_le32(WFI_NAND128_FLASH),
332 .flags = cpu_to_le32(WFI_FLAG_SUPPORTS_BTRM),
333 };
334 uint32_t crc32 = 0xffffffff;
335 size_t cur_offset = 0;
336 ssize_t bytes;
337 FILE *fp;
338 int c;
339 int err = 0;
340
341 if (argc < 3) {
342 fprintf(stderr, "No BCM4908 image pathname passed\n");
343 err = -EINVAL;
344 goto out;
345 }
346 pathname = argv[2];
347
348 fp = fopen(pathname, "w+");
349 if (!fp) {
350 fprintf(stderr, "Failed to open %s\n", pathname);
351 err = -EACCES;
352 goto out;
353 }
354
355 optind = 3;
356 while ((c = getopt(argc, argv, "f:a:A:")) != -1) {
357 switch (c) {
358 case 'f':
359 bytes = bcm4908img_create_append_file(fp, optarg, &crc32);
360 if (bytes < 0) {
361 fprintf(stderr, "Failed to append file %s\n", optarg);
362 } else {
363 cur_offset += bytes;
364 }
365 break;
366 case 'a':
367 bytes = bcm4908img_create_align(fp, cur_offset, strtol(optarg, NULL, 0));
368 if (bytes < 0)
369 fprintf(stderr, "Failed to append zeros\n");
370 else
371 cur_offset += bytes;
372 break;
373 case 'A':
374 bytes = strtol(optarg, NULL, 0) - cur_offset;
375 if (bytes < 0) {
376 fprintf(stderr, "Current BCM4908 image length is 0x%zx, can't pad it with zeros to 0x%lx\n", cur_offset, strtol(optarg, NULL, 0));
377 } else {
378 bytes = bcm4908img_create_append_zeros(fp, bytes);
379 if (bytes < 0)
380 fprintf(stderr, "Failed to append zeros\n");
381 else
382 cur_offset += bytes;
383 }
384 break;
385 }
386 if (err)
387 goto err_close;
388 }
389
390 tail.crc32 = cpu_to_le32(crc32);
391
392 bytes = fwrite(&tail, 1, sizeof(tail), fp);
393 if (bytes != sizeof(tail)) {
394 fprintf(stderr, "Failed to write BCM4908 image tail to %s\n", pathname);
395 return -EIO;
396 }
397
398 err_close:
399 fclose(fp);
400 out:
401 return err;
402 }
403
404 /**************************************************
405 * Start
406 **************************************************/
407
408 static void usage() {
409 printf("Usage:\n");
410 printf("\n");
411 printf("Checking a BCM4908 image:\n");
412 printf("\tbcm4908img check <file>\t\t\tcheck if images is valid\n");
413 printf("\n");
414 printf("Creating a new BCM4908 image:\n");
415 printf("\tbcm4908img create <file> [options]\n");
416 printf("\t-f file\t\t\t\tadd data from specified file\n");
417 printf("\t-a alignment\t\t\tpad image with zeros to specified alignment\n");
418 printf("\t-A offset\t\t\t\tappend zeros until reaching specified offset\n");
419 }
420
421 int main(int argc, char **argv) {
422 if (argc > 1) {
423 if (!strcmp(argv[1], "check"))
424 return bcm4908img_check(argc, argv);
425 else if (!strcmp(argv[1], "create"))
426 return bcm4908img_create(argc, argv);
427 }
428
429 usage();
430 return 0;
431 }